From 21f05284c79c3e823169c62d189826f735006d43 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Fri, 29 Apr 2022 14:45:46 -0400 Subject: [PATCH 001/113] cmd/go: index standard library packages Change-Id: I07594303a1e9833723522d5ff94577a5510ca6f0 Reviewed-on: https://go-review.googlesource.com/c/go/+/404714 Run-TryBot: Michael Matloob Reviewed-by: Bryan Mills TryBot-Result: Gopher Robot Reviewed-by: Michael Matloob --- src/cmd/go/internal/load/pkg.go | 3 +- src/cmd/go/internal/modindex/read.go | 148 ++++++++++++++++++++------ src/cmd/go/internal/modload/build.go | 4 +- src/cmd/go/internal/modload/import.go | 3 +- 4 files changed, 120 insertions(+), 38 deletions(-) diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index 8ceacec326..4c7833b4d2 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -14,7 +14,6 @@ import ( "go/build" "go/scanner" "go/token" - "internal/goroot" "io/fs" "os" "os/exec" @@ -3072,7 +3071,7 @@ func PackagesAndErrorsOutsideModule(ctx context.Context, opts PackageOpts, args return nil, fmt.Errorf("%s: argument must be a package path, not a meta-package", arg) case path.Clean(p) != p: return nil, fmt.Errorf("%s: argument must be a clean package path", arg) - case !strings.Contains(p, "...") && search.IsStandardImportPath(p) && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, p): + case !strings.Contains(p, "...") && search.IsStandardImportPath(p) && modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, p): return nil, fmt.Errorf("%s: argument must not be a package in the standard library", arg) default: patterns[i] = p diff --git a/src/cmd/go/internal/modindex/read.go b/src/cmd/go/internal/modindex/read.go index e180ca5450..4f02ca5d10 100644 --- a/src/cmd/go/internal/modindex/read.go +++ b/src/cmd/go/internal/modindex/read.go @@ -1,7 +1,32 @@ +// 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 modindex import ( "bytes" + "encoding/binary" + "errors" + "fmt" + "go/build" + "go/build/constraint" + "go/token" + "internal/goroot" + "internal/unsafeheader" + "io/fs" + "math" + "os" + "path" + "path/filepath" + "runtime" + "runtime/debug" + "sort" + "strconv" + "strings" + "sync" + "unsafe" + "cmd/go/internal/base" "cmd/go/internal/cache" "cmd/go/internal/cfg" @@ -9,23 +34,6 @@ import ( "cmd/go/internal/imports" "cmd/go/internal/par" "cmd/go/internal/str" - "encoding/binary" - "errors" - "fmt" - "go/build" - "go/build/constraint" - "go/token" - "internal/unsafeheader" - "io/fs" - "math" - "os" - "path/filepath" - "runtime/debug" - "sort" - "strconv" - "strings" - "sync" - "unsafe" ) // enabled is used to flag off the behavior of the module index on tip. @@ -48,8 +56,8 @@ var fcache par.Cache func moduleHash(modroot string, ismodcache bool) (cache.ActionID, error) { h := cache.NewHash("moduleIndex") - fmt.Fprintf(h, "module index %s %v", indexVersion, modroot) - if ismodcache { + fmt.Fprintf(h, "module index %s %s %v\n", runtime.Version(), indexVersion, modroot) + if ismodcache || str.HasFilePathPrefix(modroot, cfg.GOROOT) { return h.Sum(), nil } // walkdir happens in deterministic order. @@ -97,10 +105,6 @@ func Get(modroot string) (*ModuleIndex, error) { if modroot == "" { panic("modindex.Get called with empty modroot") } - if str.HasFilePathPrefix(modroot, cfg.GOROOT) { - // TODO(matloob): add a case for stdlib here. - return nil, ErrNotIndexed - } isModCache := str.HasFilePathPrefix(modroot, cfg.GOMODCACHE) return openIndex(modroot, isModCache) } @@ -225,9 +229,6 @@ func (mi *ModuleIndex) Import(bctxt build.Context, relpath string, mode build.Im p.ImportPath = "." p.Dir = filepath.Join(mi.modroot, rp.dir) - if rp.error != "" { - return p, errors.New(rp.error) - } var pkgerr error switch ctxt.Compiler { @@ -241,6 +242,62 @@ func (mi *ModuleIndex) Import(bctxt build.Context, relpath string, mode build.Im return p, fmt.Errorf("import %q: import of unknown directory", p.Dir) } + // goroot + inTestdata := func(sub string) bool { + return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || str.HasPathPrefix(sub, "testdata") + } + if ctxt.GOROOT != "" && str.HasFilePathPrefix(mi.modroot, cfg.GOROOTsrc) && !inTestdata(relpath) { + modprefix := str.TrimFilePathPrefix(mi.modroot, cfg.GOROOTsrc) + p.Goroot = true + p.ImportPath = relpath + if modprefix != "" { + p.ImportPath = filepath.Join(modprefix, p.ImportPath) + } + // In build.go, p.Root should only be set in the non-local-import case, or in + // GOROOT or GOPATH. Since module mode only calls Import with path set to "." + // and the module index doesn't apply outside modules, the GOROOT case is + // the only case where GOROOT needs to be set. + // TODO(#37015): p.Root actually might be set in the local-import case outside + // GOROOT, if the directory is contained in GOPATH/src, even in module + // mode, but that's a bug. + p.Root = ctxt.GOROOT + + // Set GOROOT-specific fields + // The fields set below (SrcRoot, PkgRoot, BinDir, PkgTargetRoot, and PkgObj) + // are only set in build.Import if p.Root != "". As noted in the comment + // on setting p.Root above, p.Root should only be set in the GOROOT case for the + // set of packages we care about. + var pkgtargetroot string + var pkga string + suffix := "" + if ctxt.InstallSuffix != "" { + suffix = "_" + ctxt.InstallSuffix + } + switch ctxt.Compiler { + case "gccgo": + pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix + dir, elem := path.Split(p.ImportPath) + pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a" + case "gc": + pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix + pkga = pkgtargetroot + "/" + p.ImportPath + ".a" + } + p.SrcRoot = ctxt.joinPath(p.Root, "src") + p.PkgRoot = ctxt.joinPath(p.Root, "pkg") + p.BinDir = ctxt.joinPath(p.Root, "bin") + if pkga != "" { + p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot) + p.PkgObj = ctxt.joinPath(p.Root, pkga) + } + } + + if rp.error != nil { + if errors.Is(rp.error, errCannotFindPackage) && ctxt.Compiler == "gccgo" && p.Goroot { + return p, nil + } + return p, rp.error + } + if mode&build.FindOnly != 0 { return p, pkgerr } @@ -444,8 +501,31 @@ func (mi *ModuleIndex) Import(bctxt build.Context, relpath string, mode build.Im return p, pkgerr } -// IsDirWithGoFiles is the equivalent of fsys.IsDirWithGoFiles using the information in the -// RawPackage. +// IsStandardPackage reports whether path is a standard package +// for the goroot and compiler using the module index if possible, +// and otherwise falling back to internal/goroot.IsStandardPackage +func IsStandardPackage(goroot_, compiler, path string) bool { + if !enabled || compiler != "gc" { + return goroot.IsStandardPackage(goroot_, compiler, path) + } + + reldir := filepath.FromSlash(path) // relative dir path in module index for package + modroot := filepath.Join(goroot_, "src") + if str.HasFilePathPrefix(reldir, "cmd") { + reldir = str.TrimFilePathPrefix(reldir, "cmd") + modroot = filepath.Join(modroot, "cmd") + } + mod, err := Get(modroot) + if err != nil { + return goroot.IsStandardPackage(goroot_, compiler, path) + } + + pkgs := mod.Packages() + i := sort.SearchStrings(pkgs, reldir) + return i != len(pkgs) && pkgs[i] == reldir +} + +// IsDirWithGoFiles is the equivalent of fsys.IsDirWithGoFiles using the information in the index. func (mi *ModuleIndex) IsDirWithGoFiles(relpath string) (_ bool, err error) { rp := mi.indexPackage(relpath) @@ -462,7 +542,7 @@ func (mi *ModuleIndex) IsDirWithGoFiles(relpath string) (_ bool, err error) { return false, nil } -// ScanDir implements imports.ScanDir using the information in the RawPackage. +// ScanDir implements imports.ScanDir using the information in the index. func (mi *ModuleIndex) ScanDir(path string, tags map[string]bool) (sortedImports []string, sortedTestImports []string, err error) { rp := mi.indexPackage(path) @@ -556,13 +636,15 @@ func shouldBuild(sf *sourceFile, tags map[string]bool) bool { // index package holds the information needed to access information in the // index about a package. type indexPackage struct { - error string + error error dir string // directory of the package relative to the modroot // Source files sourceFiles []*sourceFile } +var errCannotFindPackage = errors.New("cannot find package") + // indexPackage returns an indexPackage constructed using the information in the ModuleIndex. func (mi *ModuleIndex) indexPackage(path string) *indexPackage { defer func() { @@ -572,13 +654,15 @@ func (mi *ModuleIndex) indexPackage(path string) *indexPackage { }() offset, ok := mi.packages[path] if !ok { - return &indexPackage{error: fmt.Sprintf("cannot find package %q in:\n\t%s", path, filepath.Join(mi.modroot, path))} + return &indexPackage{error: fmt.Errorf("%w %q in:\n\t%s", errCannotFindPackage, path, filepath.Join(mi.modroot, path))} } // TODO(matloob): do we want to lock on the module index? d := mi.od.decoderAt(offset) rp := new(indexPackage) - rp.error = d.string() + if errstr := d.string(); errstr != "" { + rp.error = errors.New(errstr) + } rp.dir = d.string() numSourceFiles := d.uint32() rp.sourceFiles = make([]*sourceFile, numSourceFiles) diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go index 7b1bc732fc..0799fec35c 100644 --- a/src/cmd/go/internal/modload/build.go +++ b/src/cmd/go/internal/modload/build.go @@ -9,7 +9,6 @@ import ( "encoding/hex" "errors" "fmt" - "internal/goroot" "io/fs" "os" "path/filepath" @@ -18,6 +17,7 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/modfetch" + "cmd/go/internal/modindex" "cmd/go/internal/modinfo" "cmd/go/internal/search" @@ -39,7 +39,7 @@ func findStandardImportPath(path string) string { panic("findStandardImportPath called with empty path") } if search.IsStandardImportPath(path) { - if goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { + if modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { return filepath.Join(cfg.GOROOT, "src", path) } } diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go index 22286e5e2d..f7810ca5c6 100644 --- a/src/cmd/go/internal/modload/import.go +++ b/src/cmd/go/internal/modload/import.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "go/build" - "internal/goroot" "io/fs" "os" pathpkg "path" @@ -281,7 +280,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M // Is the package in the standard library? pathIsStd := search.IsStandardImportPath(path) - if pathIsStd && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { + if pathIsStd && modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { for _, mainModule := range MainModules.Versions() { if MainModules.InGorootSrc(mainModule) { if dir, ok, err := dirInModule(path, MainModules.PathPrefix(mainModule), MainModules.ModRoot(mainModule), true); err != nil { From f8a53df314e4af8cd350eedb0dae77d4c4fc30d0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 3 Jun 2022 16:00:16 -0400 Subject: [PATCH 002/113] io: revert: add an Err field to LimitedReader We are having a hard time deciding the exact semantics of the Err field, and we need to ship the beta. So revert the Err field change; it can wait for Go 1.20. For #51115. This reverts CL 396215. Change-Id: I7719386567d3da10a614058a11f19dbccf304b4d Reviewed-on: https://go-review.googlesource.com/c/go/+/410133 TryBot-Result: Gopher Robot Reviewed-by: Ian Lance Taylor Reviewed-by: Dmitri Shuralyov Reviewed-by: Dmitri Shuralyov Run-TryBot: Russ Cox Reviewed-by: Bryan Mills Auto-Submit: Russ Cox --- api/next/51115.txt | 1 - src/io/example_test.go | 14 -------------- src/io/io.go | 16 +++++----------- 3 files changed, 5 insertions(+), 26 deletions(-) delete mode 100644 api/next/51115.txt diff --git a/api/next/51115.txt b/api/next/51115.txt deleted file mode 100644 index 0ce24b4ed0..0000000000 --- a/api/next/51115.txt +++ /dev/null @@ -1 +0,0 @@ -pkg io, type LimitedReader struct, Err error #51115 diff --git a/src/io/example_test.go b/src/io/example_test.go index e4b20bd981..419e449982 100644 --- a/src/io/example_test.go +++ b/src/io/example_test.go @@ -6,7 +6,6 @@ package io_test import ( "bytes" - "errors" "fmt" "io" "log" @@ -284,16 +283,3 @@ func ExampleReadAll() { // Output: // Go is a general-purpose language designed with systems programming in mind. } - -func ExampleLimitedReader() { - r := strings.NewReader("some io.Reader stream to be read\n") - sentinel := errors.New("reached read limit") - lr := &io.LimitedReader{R: r, N: 4, Err: sentinel} - - if _, err := io.Copy(os.Stdout, lr); err != sentinel { - log.Fatal(err) - } - - // Output: - // some -} diff --git a/src/io/io.go b/src/io/io.go index 830779e79d..db88125f50 100644 --- a/src/io/io.go +++ b/src/io/io.go @@ -455,26 +455,20 @@ func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) { // LimitReader returns a Reader that reads from r // but stops with EOF after n bytes. // The underlying implementation is a *LimitedReader. -func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n, nil} } +func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n} } // A LimitedReader reads from R but limits the amount of // data returned to just N bytes. Each call to Read // updates N to reflect the new amount remaining. -// Read returns Err when N <= 0. -// If Err is nil, it returns EOF instead. +// Read returns EOF when N <= 0 or when the underlying R returns EOF. type LimitedReader struct { - R Reader // underlying reader - N int64 // max bytes remaining - Err error // error to return on reaching the limit + R Reader // underlying reader + N int64 // max bytes remaining } func (l *LimitedReader) Read(p []byte) (n int, err error) { if l.N <= 0 { - err := l.Err - if err == nil { - err = EOF - } - return 0, err + return 0, EOF } if int64(len(p)) > l.N { p = p[0:l.N] From fc66cae490a0cd8b8cefefbc0ace7c3fb030f779 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 4 Jun 2022 09:44:11 -0400 Subject: [PATCH 003/113] doc/go1.19: remove TODO about LimitedReader Rolled back in CL 410133. For #51115. Change-Id: I009c557acf98a98a9e5648fa82d998d41974ae60 Reviewed-on: https://go-review.googlesource.com/c/go/+/410357 Run-TryBot: Russ Cox Reviewed-by: Ian Lance Taylor Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot --- doc/go1.19.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 4de93d1565..6d49cedfd4 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -282,10 +282,6 @@ Do not send CLs removing the interior tags from such phrases.
io
-

- TODO: https://go.dev/cl/396215: add an Err field to LimitedReader -

-

TODO: https://go.dev/cl/400236: NopCloser forward WriterTo implementations if the reader supports it

From 865911424d509184d95d3f9fc6a8301927117fdc Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 26 Jan 2022 16:42:05 -0500 Subject: [PATCH 004/113] doc: update Go memory model Following discussion on #47141, make the following changes: - Document Go's overall approach. - Document that multiword races can cause crashes. - Document happens-before for runtime.SetFinalizer. - Document (or link to) happens-before for more sync types. - Document happens-before for sync/atomic. - Document disallowed compiler optimizations. See also https://research.swtch.com/gomm for background. Fixes #50859. Change-Id: I17d837756a77f4d8569f263489c2c45de20a8778 Reviewed-on: https://go-review.googlesource.com/c/go/+/381315 Reviewed-by: Ian Lance Taylor --- doc/go_mem.html | 587 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 492 insertions(+), 95 deletions(-) diff --git a/doc/go_mem.html b/doc/go_mem.html index 5f1eb68af3..9bcf06707a 100644 --- a/doc/go_mem.html +++ b/doc/go_mem.html @@ -8,12 +8,9 @@ p.rule { font-style: italic; } -span.event { - font-style: italic; -} -

Introduction

+

Introduction

The Go memory model specifies the conditions under which @@ -22,7 +19,7 @@ observe values produced by writes to the same variable in a different goroutine.

-

Advice

+

Advice

Programs that modify data being simultaneously accessed by multiple goroutines @@ -44,90 +41,237 @@ you are being too clever. Don't be clever.

-

Happens Before

+

Informal Overview

-Within a single goroutine, reads and writes must behave -as if they executed in the order specified by the program. -That is, compilers and processors may reorder the reads and writes -executed within a single goroutine only when the reordering -does not change the behavior within that goroutine -as defined by the language specification. -Because of this reordering, the execution order observed -by one goroutine may differ from the order perceived -by another. For example, if one goroutine -executes a = 1; b = 2;, another might observe -the updated value of b before the updated value of a. +Go approaches its memory model in much the same way as the rest of the language, +aiming to keep the semantics simple, understandable, and useful. +This section gives a general overview of the approach and should suffice for most programmers. +The memory model is specified more formally in the next section.

-To specify the requirements of reads and writes, we define -happens before, a partial order on the execution -of memory operations in a Go program. If event e1 happens -before event e2, then we say that e2 happens after e1. -Also, if e1 does not happen before e2 and does not happen -after e2, then we say that e1 and e2 happen concurrently. -

- -

-Within a single goroutine, the happens-before order is the -order expressed by the program. +A data race is defined as +a write to a memory location happening concurrently with another read or write to that same location, +unless all the accesses involved are atomic data accesses as provided by the sync/atomic package. +As noted already, programmers are strongly encouraged to use appropriate synchronization +to avoid data races. +In the absence of data races, Go programs behave as if all the goroutines +were multiplexed onto a single processor. +This property is sometimes referred to as DRF-SC: data-race-free programs +execute in a sequentially consistent manner.

-A read r of a variable v is allowed to observe a write w to v -if both of the following hold: +While programmers should write Go programs without data races, +there are limitations to what a Go implementation can do in response to a data race. +An implementation may always react to a data race by reporting the race and terminating the program. +Otherwise, each read of a single-word-sized or sub-word-sized memory location +must observe a value actually written to that location (perhaps by a concurrent executing goroutine) +and not yet overwritten. +These implementation constraints make Go more like Java or JavaScript, +in that most races have a limited number of outcomes, +and less like C and C++, where the meaning of any program with a race +is entirely undefined, and the compiler may do anything at all. +Go's approach aims to make errant programs more reliable and easier to debug, +while still insisting that races are errors and that tools can diagnose and report them.

+

Memory Model

+ +

+The following formal definition of Go's memory model closely follows +the approach presented by Hans-J. Boehm and Sarita V. Adve in +“Foundations of the C++ Concurrency Memory Model”, +published in PLDI 2008. +The definition of data-race-free programs and the guarantee of sequential consistency +for race-free progams are equivalent to the ones in that work. +

+ +

+The memory model describes the requirements on program executions, +which are made up of goroutine executions, +which in turn are made up of memory operations. +

+ +

+A memory operation is modeled by four details: +

+
    +
  • its kind, indicating whether it is an ordinary data read, an ordinary data write, +or a synchronizing operation such as an atomic data access, +a mutex operation, or a channel operation, +
  • its location in the program, +
  • the memory location or variable being accessed, and +
  • the values read or written by the operation. +
+

+Some memory operations are read-like, including read, atomic read, mutex lock, and channel receive. +Other memory operations are write-like, including write, atomic write, mutex unlock, channel send, and channel close. +Some, such as atomic compare-and-swap, are both read-like and write-like. +

+ +

+A goroutine execution is modeled as a set of memory operations executed by a single goroutine. +

+ +

+Requirement 1: +The memory operations in each goroutine must correspond to a correct sequential execution of that goroutine, +given the values read from and written to memory. +That execution must be consistent with the sequenced before relation, +defined as the partial order requirements set out by the Go language specification +for Go's control flow constructs as well as the order of evaluation for expressions. +

+ +

+A Go program execution is modeled as a set of goroutine executions, +together with a mapping W that specifies the write-like operation that each read-like operation reads from. +(Multiple executions of the same program can have different program executions.) +

+ +

+Requirement 2: +For a given program execution, the mapping W, when limited to synchronizing operations, +must be explainable by some implicit total order of the synchronizing operations +that is consistent with sequencing and the values read and written by those operations. +

+ +

+The synchronized before relation is a partial order on synchronizing memory operations, +derived from W. +If a synchronizing read-like memory operation r +observes a synchronizing write-like memory operation w +(that is, if W(r) = w), +then w is synchronized before r. +Informally, the synchronized before relation is a subset of the implied total order +mentioned in the previous paragraph, +limited to the information that W directly observes. +

+ +

+The happens before relation is defined as the transitive closure of the +union of the sequenced before and synchronized before relations. +

+ +

+Requirement 3: +For an ordinary (non-synchronizing) data read r on a memory location x, +W(r) must be a write w that is visible to r, +where visible means that both of the following hold: +

    -
  1. r does not happen before w.
  2. -
  3. There is no other write w' to v that happens - after w but before r.
  4. +
  5. w happens before r. +
  6. w does not happen before any other write w' (to x) that happens before r.

-To guarantee that a read r of a variable v observes a -particular write w to v, ensure that w is the only -write r is allowed to observe. -That is, r is guaranteed to observe w if both of the following hold: -

- -
    -
  1. w happens before r.
  2. -
  3. Any other write to the shared variable v -either happens before w or after r.
  4. -
- -

-This pair of conditions is stronger than the first pair; -it requires that there are no other writes happening -concurrently with w or r. +A read-write data race on memory location x +consists of a read-like memory operation r on x +and a write-like memory operation w on x, +at least one of which is non-synchronizing, +which are unordered by happens before +(that is, neither r happens before w +nor w happens before r).

-Within a single goroutine, -there is no concurrency, so the two definitions are equivalent: -a read r observes the value written by the most recent write w to v. -When multiple goroutines access a shared variable v, -they must use synchronization events to establish -happens-before conditions that ensure reads observe the -desired writes. +A write-write data race on memory location x +consists of two write-like memory operations w and w' on x, +at least one of which is non-synchronizing, +which are unordered by happens before.

-The initialization of variable v with the zero value -for v's type behaves as a write in the memory model. +Note that if there are no read-write or write-write data races on memory location x, +then any read r on x has only one possible W(r): +the single w that immediately precedes it in the happens before order.

-Reads and writes of values larger than a single machine word -behave as multiple machine-word-sized operations in an -unspecified order. +More generally, it can be shown that any Go program that is data-race-free, +meaning it has no program executions with read-write or write-write data races, +can only have outcomes explained by some sequentially consistent interleaving +of the goroutine executions. +(The proof is the same as Section 7 of Boehm and Adve's paper cited above.) +This property is called DRF-SC.

-

Synchronization

+

+The intent of the formal definition is to match +the DRF-SC guarantee provided to race-free programs +by other languages, including C, C++, Java, JavaScript, Rust, and Swift. +

-

Initialization

+

+Certain Go language operations such as goroutine creation and memory allocation +act as synchronization opeartions. +The effect of these operations on the synchronized-before partial order +is documented in the “Synchronization” section below. +Individual packages are responsible for providing similar documentation +for their own operations. +

+ +

Implementation Restrictions for Programs Containing Data Races

+ +

+The preceding section gave a formal definition of data-race-free program execution. +This section informally describes the semantics that implementations must provide +for programs that do contain races. +

+ +

+First, any implementation can, upon detecting a data race, +report the race and halt execution of the program. +Implementations using ThreadSanitizer +(accessed with “go build -race”) +do exactly this. +

+ +

+Otherwise, a read r of a memory location x +that is not larger than a machine word must observe +some write w such that r does not happen before w +and there is no write w' such that w happens before w' +and w' happens before r. +That is, each read must observe a value written by a preceding or concurrent write. +

+ +

+Additionally, observation of acausal and “out of thin air” writes is disallowed. +

+ +

+Reads of memory locations larger than a single machine word +are encouraged but not required to meet the same semantics +as word-sized memory locations, +observing a single allowed write w. +For performance reasons, +implementations may instead treat larger operations +as a set of individual machine-word-sized operations +in an unspecified order. +This means that races on multiword data structures +can lead to inconsistent values not corresponding to a single write. +When the values depend on the consistency +of internal (pointer, length) or (pointer, type) pairs, +as can be the case for interface values, maps, +slices, and strings in most Go implementations, +such races can in turn lead to arbitrary memory corruption. +

+ +

+Examples of incorrect synchronization are given in the +“Incorrect synchronization” section below. +

+ +

+Examples of the limitations on implementations are given in the +“Incorrect compilation” section below. +

+ +

Synchronization

+ +

Initialization

Program initialization runs in a single goroutine, @@ -141,15 +285,15 @@ If a package p imports package q, the completion of

-The start of the function main.main happens after -all init functions have finished. +The completion of all init functions is synchronized before +the start of the function main.main.

-

Goroutine creation

+

Goroutine creation

The go statement that starts a new goroutine -happens before the goroutine's execution begins. +is synchronized before the start of the goroutine's execution.

@@ -174,11 +318,12 @@ calling hello will print "hello, world" at some point in the future (perhaps after hello has returned).

-

Goroutine destruction

+

Goroutine destruction

-The exit of a goroutine is not guaranteed to happen before -any event in the program. For example, in this program: +The exit of a goroutine is not guaranteed to be synchronized before +any event in the program. +For example, in this program:

@@ -203,7 +348,7 @@ use a synchronization mechanism such as a lock or channel
 communication to establish a relative ordering.
 

-

Channel communication

+

Channel communication

Channel communication is the main method of synchronization @@ -213,8 +358,8 @@ usually in a different goroutine.

-A send on a channel happens before the corresponding -receive from that channel completes. +A send on a channel is synchronized before the completion of the +corresponding receive from that channel.

@@ -239,13 +384,13 @@ func main() {

is guaranteed to print "hello, world". The write to a -happens before the send on c, which happens before -the corresponding receive on c completes, which happens before +is sequenced before the send on c, which is synchronized before +the corresponding receive on c completes, which is sequenced before the print.

-The closing of a channel happens before a receive that returns a zero value +The closing of a channel is synchronized before a receive that returns a zero value because the channel is closed.

@@ -256,8 +401,8 @@ yields a program with the same guaranteed behavior.

-A receive from an unbuffered channel happens before -the send on that channel completes. +A receive from an unbuffered channel is synchronized before the completion of +the corresponding send on that channel.

@@ -283,8 +428,8 @@ func main() {

is also guaranteed to print "hello, world". The write to a -happens before the receive on c, which happens before -the corresponding send on c completes, which happens +is sequenced before the receive on c, which is synchronized before +the corresponding send on c completes, which is sequenced before the print.

@@ -296,7 +441,7 @@ crash, or do something else.)

-The kth receive on a channel with capacity C happens before the k+Cth send from that channel completes. +The kth receive on a channel with capacity C is synchronized before the completion of the k+Cth send from that channel completes.

@@ -330,7 +475,7 @@ func main() { }

-

Locks

+

Locks

The sync package implements two lock data types, @@ -339,7 +484,7 @@ The sync package implements two lock data types,

For any sync.Mutex or sync.RWMutex variable l and n < m, -call n of l.Unlock() happens before call m of l.Lock() returns. +call n of l.Unlock() is synchronized before call m of l.Lock() returns.

@@ -365,19 +510,29 @@ func main() {

is guaranteed to print "hello, world". -The first call to l.Unlock() (in f) happens +The first call to l.Unlock() (in f) is synchronized before the second call to l.Lock() (in main) returns, -which happens before the print. +which is sequenced before the print.

For any call to l.RLock on a sync.RWMutex variable l, -there is an n such that the l.RLock happens (returns) after call n to -l.Unlock and the matching l.RUnlock happens -before call n+1 to l.Lock. +there is an n such that the nth call to l.Unlock +is synchronized before the return from l.RLock, +and the matching call to l.RUnlock is synchronized before the return from call n+1 to l.Lock.

-

Once

+

+A successful call to l.TryLock (or l.TryRLock) +is equivalent to a call to l.Lock (or l.RLock). +An unsuccessful call has no synchronizing effect at all. +As far as the memory model is concerned, +l.TryLock (or l.TryRLock) +may be considered to be able to return false +even when the mutex l is unlocked. +

+ +

Once

The sync package provides a safe mechanism for @@ -389,7 +544,8 @@ until f() has returned.

-A single call of f() from once.Do(f) happens (returns) before any call of once.Do(f) returns. +The completion of a single call of f() from once.Do(f) +is synchronized before the return of any call of once.Do(f).

@@ -424,13 +580,60 @@ The result will be that "hello, world" will be printed twice.

-

Incorrect synchronization

+

Atomic Values

-Note that a read r may observe the value written by a write w -that happens concurrently with r. -Even if this occurs, it does not imply that reads happening after r -will observe writes that happened before w. +The APIs in the sync/atomic +package are collectively “atomic operations” +that can be used to synchronize the execution of different goroutines. +If the effect of an atomic operation A is observed by atomic operation B, +then A is synchronized before B. +All the atomic operations executed in a program behave as though executed +in some sequentially consistent order. +

+ +

+The preceding definition has the same semantics as C++’s sequentially consistent atomics +and Java’s volatile variables. +

+ +

Finalizers

+ +

+The runtime package provides +a SetFinalizer function that adds a finalizer to be called when +a particular object is no longer reachable by the program. +A call to SetFinalizer(x, f) is synchronized before the finalization call f(x). +

+ +

Additional Mechanisms

+ +

+The sync package provides additional synchronization abstractions, +including condition variables, +lock-free maps, +allocation pools, +and +wait groups. +The documentation for each of these specifies the guarantees it +makes concerning synchronization. +

+ +

+Other packages that provide synchronization abstractions +should document the guarantees they make too. +

+ + +

Incorrect synchronization

+ +

+Programs with races are incorrect and +can exhibit non-sequentially consistent executions. +In particular, note that a read r may observe the value written by any write w +that executes concurrently with r. +Even if this occurs, it does not imply that reads happening after r +will observe writes that happened before w.

@@ -566,3 +769,197 @@ value for g.msg. In all these examples, the solution is the same: use explicit synchronization.

+ +

Incorrect compilation

+ +

+The Go memory model restricts compiler optimizations as much as it does Go programs. +Some compiler optimizations that would be valid in single-threaded programs are not valid in all Go programs. +In particular, a compiler must not introduce writes that do not exist in the original program, +it must not allow a single read to observe multiple values, +and it must not allow a single write to write multiple values. +

+ +

+All the following examples assume that `*p` and `*q` refer to +memory locations accessible to multiple goroutines. +

+ +

+Not introducing data races into race-free programs means not moving +writes out of conditional statements in which they appear. +For example, a compiler must not invert the conditional in this program: +

+ +
+*p = 1
+if cond {
+	*p = 2
+}
+
+ +

+That is, the compiler must not rewrite the program into this one: +

+ +
+*p = 2
+if !cond {
+	*p = 1
+}
+
+ +

+If cond is false and another goroutine is reading *p, +then in the original program, the other goroutine can only observe any prior value of *p and 1. +In the rewritten program, the other goroutine can observe 2, which was previously impossible. +

+ +

+Not introducing data races also means not assuming that loops terminate. +For example, a compiler must in general not move the accesses to *p or *q +ahead of the loop in this program: +

+ +
+n := 0
+for e := list; e != nil; e = e.next {
+	n++
+}
+i := *p
+*q = 1
+
+ +

+If list pointed to a cyclic list, +then the original program would never access *p or *q, +but the rewritten program would. +(Moving `*p` ahead would be safe if the compiler can prove `*p` will not panic; +moving `*q` ahead would also require the compiler proving that no other +goroutine can access `*q`.) +

+ +

+Not introducing data races also means not assuming that called functions +always return or are free of synchronization operations. +For example, a compiler must not move the accesses to *p or *q +ahead of the function call in this program +(at least not without direct knowledge of the precise behavior of f): +

+ +
+f()
+i := *p
+*q = 1
+
+ +

+If the call never returned, then once again the original program +would never access *p or *q, but the rewritten program would. +And if the call contained synchronizing operations, then the original program +could establish happens before edges preceding the accesses +to *p and *q, but the rewritten program would not. +

+ +

+Not allowing a single read to observe multiple values means +not reloading local variables from shared memory. +For example, a compiler must not discard i and reload it +a second time from *p in this program: +

+ +
+i := *p
+if i < 0 || i >= len(funcs) {
+	panic("invalid function index")
+}
+... complex code ...
+// compiler must NOT reload i = *p here
+funcs[i]()
+
+ +

+If the complex code needs many registers, a compiler for single-threaded programs +could discard i without saving a copy and then reload +i = *p just before +funcs[i](). +A Go compiler must not, because the value of *p may have changed. +(Instead, the compiler could spill i to the stack.) +

+ +

+Not allowing a single write to write multiple values also means not using +the memory where a local variable will be written as temporary storage before the write. +For example, a compiler must not use *p as temporary storage in this program: +

+ +
+*p = i + *p/2
+
+ +

+That is, it must not rewrite the program into this one: +

+ +
+*p /= 2
+*p += i
+
+ +

+If i and *p start equal to 2, +the original code does *p = 3, +so a racing thread can read only 2 or 3 from *p. +The rewritten code does *p = 1 and then *p = 3, +allowing a racing thread to read 1 as well. +

+ +

+Note that all these optimizations are permitted in C/C++ compilers: +a Go compiler sharing a back end with a C/C++ compiler must take care +to disable optimizations that are invalid for Go. +

+ +

+Note that the prohibition on introducing data races +does not apply if the compiler can prove that the races +do not affect correct execution on the target platform. +For example, on essentially all CPUs, it is valid to rewrite +

+ +
+n := 0
+for i := 0; i < m; i++ {
+	n += *shared
+}
+
+ +into: + +
+n := 0
+local := *shared
+for i := 0; i < m; i++ {
+	n += local
+}
+
+ +

+provided it can be proved that *shared will not fault on access, +because the potential added read will not affect any existing concurrent reads or writes. +On the other hand, the rewrite would not be valid in a source-to-source translator. +

+ +

Conclusion

+ +

+Go programmers writing data-race-free programs can rely on +sequentially consistent execution of those programs, +just as in essentially all other modern programming languages. +

+ +

+When it comes to programs with races, +both programmers and compilers should remember the advice: +don't be clever. +

From 0293c51bc5d8ca0728913c4b7f9f92339f8fd9a6 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 13 Oct 2021 22:40:22 +0100 Subject: [PATCH 005/113] regexp: avoid copying each instruction executed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inst is a 40-byte struct, so avoiding the copy gives a decent speedup: name old time/op new time/op delta Find-8 160ns ± 4% 109ns ± 4% -32.22% (p=0.008 n=5+5) FindAllNoMatches-8 70.4ns ± 4% 53.8ns ± 0% -23.58% (p=0.016 n=5+4) FindString-8 154ns ± 6% 107ns ± 0% -30.37% (p=0.016 n=5+4) FindSubmatch-8 194ns ± 1% 135ns ± 1% -30.56% (p=0.008 n=5+5) FindStringSubmatch-8 193ns ± 8% 131ns ± 0% -31.82% (p=0.008 n=5+5) Literal-8 42.8ns ± 2% 34.8ns ± 0% -18.67% (p=0.008 n=5+5) NotLiteral-8 917ns ± 2% 636ns ± 0% -30.68% (p=0.008 n=5+5) MatchClass-8 1.18µs ± 3% 0.91µs ± 1% -22.27% (p=0.016 n=5+4) MatchClass_InRange-8 1.11µs ± 1% 0.87µs ± 2% -21.38% (p=0.008 n=5+5) ReplaceAll-8 659ns ± 6% 596ns ± 3% -9.60% (p=0.008 n=5+5) AnchoredLiteralShortNonMatch-8 34.2ns ± 0% 30.4ns ± 1% -11.20% (p=0.016 n=4+5) AnchoredLiteralLongNonMatch-8 38.7ns ± 0% 38.7ns ± 0% ~ (p=0.579 n=5+5) AnchoredShortMatch-8 67.0ns ± 1% 52.7ns ± 0% -21.31% (p=0.016 n=5+4) AnchoredLongMatch-8 121ns ± 0% 124ns ±10% ~ (p=0.730 n=5+5) OnePassShortA-8 392ns ± 0% 231ns ± 3% -41.10% (p=0.008 n=5+5) NotOnePassShortA-8 370ns ± 0% 282ns ± 1% -23.81% (p=0.008 n=5+5) OnePassShortB-8 280ns ± 0% 179ns ± 1% -36.05% (p=0.008 n=5+5) NotOnePassShortB-8 226ns ± 0% 185ns ± 3% -18.26% (p=0.008 n=5+5) OnePassLongPrefix-8 51.7ns ± 0% 39.1ns ± 1% -24.28% (p=0.016 n=4+5) OnePassLongNotPrefix-8 213ns ± 2% 132ns ± 1% -37.86% (p=0.008 n=5+5) MatchParallelShared-8 25.3ns ± 3% 23.4ns ± 7% -7.50% (p=0.016 n=5+5) MatchParallelCopied-8 26.5ns ± 7% 22.3ns ± 7% -16.06% (p=0.008 n=5+5) QuoteMetaAll-8 45.8ns ± 1% 45.8ns ± 1% ~ (p=1.000 n=5+5) QuoteMetaNone-8 24.3ns ± 0% 24.3ns ± 0% ~ (p=0.325 n=5+5) Compile/Onepass-8 1.98µs ± 0% 1.97µs ± 0% -0.22% (p=0.016 n=5+4) Compile/Medium-8 4.56µs ± 0% 4.55µs ± 1% ~ (p=0.595 n=5+5) Compile/Hard-8 35.7µs ± 0% 35.3µs ± 3% ~ (p=0.151 n=5+5) Match/Easy0/16-8 2.18ns ± 2% 2.19ns ± 5% ~ (p=0.690 n=5+5) Match/Easy0/32-8 27.4ns ± 2% 27.6ns ± 4% ~ (p=1.000 n=5+5) Match/Easy0/1K-8 246ns ± 0% 252ns ± 7% ~ (p=0.238 n=5+5) Match/Easy0/32K-8 4.58µs ± 7% 4.64µs ± 5% ~ (p=1.000 n=5+5) Match/Easy0/1M-8 235µs ± 0% 235µs ± 0% ~ (p=0.886 n=4+4) Match/Easy0/32M-8 7.86ms ± 0% 7.86ms ± 1% ~ (p=0.730 n=4+5) Match/Easy0i/16-8 2.15ns ± 0% 2.15ns ± 0% ~ (p=0.246 n=5+5) Match/Easy0i/32-8 507ns ± 2% 466ns ± 4% -8.03% (p=0.008 n=5+5) Match/Easy0i/1K-8 14.7µs ± 0% 13.6µs ± 2% -7.63% (p=0.008 n=5+5) Match/Easy0i/32K-8 571µs ± 1% 570µs ± 1% ~ (p=0.556 n=4+5) Match/Easy0i/1M-8 18.2ms ± 0% 18.8ms ±11% ~ (p=0.548 n=5+5) Match/Easy0i/32M-8 581ms ± 0% 590ms ± 1% +1.52% (p=0.016 n=4+5) Match/Easy1/16-8 2.17ns ± 0% 2.15ns ± 0% -0.90% (p=0.000 n=5+4) Match/Easy1/32-8 25.1ns ± 0% 25.4ns ± 4% ~ (p=0.651 n=5+5) Match/Easy1/1K-8 462ns ± 1% 431ns ± 4% -6.56% (p=0.008 n=5+5) Match/Easy1/32K-8 18.8µs ± 0% 18.8µs ± 1% ~ (p=1.000 n=5+5) Match/Easy1/1M-8 658µs ± 0% 658µs ± 1% ~ (p=0.841 n=5+5) Match/Easy1/32M-8 21.0ms ± 1% 21.0ms ± 2% ~ (p=0.841 n=5+5) Match/Medium/16-8 2.15ns ± 0% 2.16ns ± 0% ~ (p=0.714 n=4+5) Match/Medium/32-8 561ns ± 1% 512ns ± 5% -8.69% (p=0.008 n=5+5) Match/Medium/1K-8 16.9µs ± 0% 15.2µs ± 1% -10.40% (p=0.008 n=5+5) Match/Medium/32K-8 632µs ± 0% 631µs ± 1% ~ (p=0.421 n=5+5) Match/Medium/1M-8 20.3ms ± 1% 20.1ms ± 0% ~ (p=0.190 n=5+4) Match/Medium/32M-8 650ms ± 1% 646ms ± 0% -0.58% (p=0.032 n=5+4) Match/Hard/16-8 2.15ns ± 0% 2.15ns ± 1% ~ (p=0.111 n=5+5) Match/Hard/32-8 870ns ± 2% 667ns ± 1% -23.28% (p=0.008 n=5+5) Match/Hard/1K-8 26.9µs ± 0% 21.0µs ± 2% -21.83% (p=0.008 n=5+5) Match/Hard/32K-8 833µs ± 0% 833µs ± 1% ~ (p=0.548 n=5+5) Match/Hard/1M-8 26.6ms ± 0% 26.8ms ± 1% ~ (p=0.905 n=4+5) Match/Hard/32M-8 856ms ± 0% 851ms ± 0% -0.65% (p=0.016 n=5+4) Match/Hard1/16-8 2.96µs ±12% 1.81µs ± 3% -38.68% (p=0.008 n=5+5) Match/Hard1/32-8 5.62µs ± 3% 3.48µs ± 0% -38.07% (p=0.016 n=5+4) Match/Hard1/1K-8 175µs ± 5% 108µs ± 0% -37.85% (p=0.016 n=5+4) Match/Hard1/32K-8 4.09ms ± 2% 4.05ms ± 0% -0.85% (p=0.016 n=5+4) Match/Hard1/1M-8 131ms ± 0% 131ms ± 3% ~ (p=0.151 n=5+5) Match/Hard1/32M-8 4.19s ± 0% 4.20s ± 1% ~ (p=1.000 n=5+5) Match_onepass_regex/16-8 262ns ± 2% 170ns ± 2% -35.13% (p=0.008 n=5+5) Match_onepass_regex/32-8 463ns ± 0% 306ns ± 0% -33.90% (p=0.008 n=5+5) Match_onepass_regex/1K-8 13.3µs ± 2% 8.8µs ± 0% -33.84% (p=0.008 n=5+5) Match_onepass_regex/32K-8 424µs ± 3% 280µs ± 1% -33.93% (p=0.008 n=5+5) Match_onepass_regex/1M-8 13.4ms ± 0% 9.0ms ± 1% -32.80% (p=0.016 n=4+5) Match_onepass_regex/32M-8 427ms ± 0% 288ms ± 1% -32.60% (p=0.008 n=5+5) Change-Id: I02c54176ed5c9f5b5fc99524a2d5eb1c490f0ebf Reviewed-on: https://go-review.googlesource.com/c/go/+/355789 Reviewed-by: Peter Weinberger Reviewed-by: Russ Cox TryBot-Result: Gopher Robot Run-TryBot: Russ Cox Auto-Submit: Russ Cox --- src/regexp/backtrack.go | 2 +- src/regexp/exec.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/regexp/backtrack.go b/src/regexp/backtrack.go index 41ae59bcaa..0739f5ff58 100644 --- a/src/regexp/backtrack.go +++ b/src/regexp/backtrack.go @@ -163,7 +163,7 @@ func (re *Regexp) tryBacktrack(b *bitState, i input, pc uint32, pos int) bool { } Skip: - inst := re.prog.Inst[pc] + inst := &re.prog.Inst[pc] switch inst.Op { default: diff --git a/src/regexp/exec.go b/src/regexp/exec.go index 4411e4c3e6..3fc4b684fe 100644 --- a/src/regexp/exec.go +++ b/src/regexp/exec.go @@ -427,7 +427,7 @@ func (re *Regexp) doOnePass(ir io.RuneReader, ib []byte, is string, pos, ncap in flag = i.context(pos) } pc := re.onepass.Start - inst := re.onepass.Inst[pc] + inst := &re.onepass.Inst[pc] // If there is a simple literal prefix, skip over it. if pos == 0 && flag.match(syntax.EmptyOp(inst.Arg)) && len(re.prefix) > 0 && i.canCheckPrefix() { @@ -442,7 +442,7 @@ func (re *Regexp) doOnePass(ir io.RuneReader, ib []byte, is string, pos, ncap in pc = int(re.prefixEnd) } for { - inst = re.onepass.Inst[pc] + inst = &re.onepass.Inst[pc] pc = int(inst.Out) switch inst.Op { default: @@ -470,7 +470,7 @@ func (re *Regexp) doOnePass(ir io.RuneReader, ib []byte, is string, pos, ncap in } // peek at the input rune to see which branch of the Alt to take case syntax.InstAlt, syntax.InstAltMatch: - pc = int(onePassNext(&inst, r)) + pc = int(onePassNext(inst, r)) continue case syntax.InstFail: goto Return From a32a592c8c14927c20ac42808e1fb2e55b2e9470 Mon Sep 17 00:00:00 2001 From: mstmdev Date: Sat, 4 Jun 2022 19:32:04 +0000 Subject: [PATCH 006/113] database/sql/driver: fix typo in driver.go ExecerContext -> ExecContext QueryerContext -> QueryContext Change-Id: Id3b1f44de5aa47372d59696523b4379e1fbfc11c GitHub-Last-Rev: 571d01f8052cef27bd5f91a5e95c0735ddf2e327 GitHub-Pull-Request: golang/go#53235 Reviewed-on: https://go-review.googlesource.com/c/go/+/410415 Reviewed-by: Dmitri Shuralyov Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor Auto-Submit: Ian Lance Taylor TryBot-Result: Gopher Robot --- src/database/sql/driver/driver.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/database/sql/driver/driver.go b/src/database/sql/driver/driver.go index 43fa579bda..daf282bf74 100644 --- a/src/database/sql/driver/driver.go +++ b/src/database/sql/driver/driver.go @@ -192,9 +192,9 @@ type Execer interface { // DB.Exec will first prepare a query, execute the statement, and then // close the statement. // -// ExecerContext may return ErrSkip. +// ExecContext may return ErrSkip. // -// ExecerContext must honor the context timeout and return when the context is canceled. +// ExecContext must honor the context timeout and return when the context is canceled. type ExecerContext interface { ExecContext(ctx context.Context, query string, args []NamedValue) (Result, error) } @@ -219,9 +219,9 @@ type Queryer interface { // DB.Query will first prepare a query, execute the statement, and then // close the statement. // -// QueryerContext may return ErrSkip. +// QueryContext may return ErrSkip. // -// QueryerContext must honor the context timeout and return when the context is canceled. +// QueryContext must honor the context timeout and return when the context is canceled. type QueryerContext interface { QueryContext(ctx context.Context, query string, args []NamedValue) (Rows, error) } From 2730c6af9fb8a7dea9bf610699be0d543aed4da1 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Sat, 4 Jun 2022 03:41:06 +0000 Subject: [PATCH 007/113] runtime: fix typo in libfuzzer_arm64.s statment -> statement Change-Id: Ia93a466fdc20157a7d6048903e359fe8717ecb8f GitHub-Last-Rev: 0a9bc5cab0ec2ac8d76ede3722c8813372ac771e GitHub-Pull-Request: golang/go#53231 Reviewed-on: https://go-review.googlesource.com/c/go/+/410374 Auto-Submit: Ian Lance Taylor Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Dmitri Shuralyov --- src/runtime/libfuzzer_arm64.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/libfuzzer_arm64.s b/src/runtime/libfuzzer_arm64.s index 0729077759..9da94be03e 100644 --- a/src/runtime/libfuzzer_arm64.s +++ b/src/runtime/libfuzzer_arm64.s @@ -22,7 +22,7 @@ // Calls C function fn from libFuzzer and passes 2 arguments to it after // manipulating the return address so that libfuzzer's integer compare hooks // work. -// The problem statment and solution are documented in detail in libfuzzer_amd64.s. +// The problem statement and solution are documented in detail in libfuzzer_amd64.s. // See commentary there. TEXT runtime·libfuzzerCallTraceIntCmp(SB), NOSPLIT, $8-32 MOVD fn+0(FP), R9 From 47f806ce81aac555946144f112b9f8733e2ed871 Mon Sep 17 00:00:00 2001 From: Ben Hoyt Date: Sat, 4 Jun 2022 18:31:40 +1200 Subject: [PATCH 008/113] strconv: clarify ParseFloat accepts Go syntax for float literals The documentation for strconv.ParseFloat mentions that it "accepts decimal and hexadecimal floating-point number syntax", but it doesn't specify what those formats entail. For example, "0x10" is not allowed; you need an explicit exponent, as in "0x10p0". This clarifies that ParseFloat accepts the Go syntax for floating-point literals, and links to that spec section. I've also linked to the relevant spec section for ParseInt's doc comment, which already said "as defined by the Go syntax for integer literals". Change-Id: Ib5d2b408bdd01ea0b9f69381a9dbe858f6d1d424 Reviewed-on: https://go-review.googlesource.com/c/go/+/410335 Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot Run-TryBot: Ian Lance Taylor Reviewed-by: Dmitri Shuralyov Auto-Submit: Ian Lance Taylor --- src/strconv/atof.go | 5 ++++- src/strconv/atoi.go | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/strconv/atof.go b/src/strconv/atof.go index 60098efed0..c26c34208c 100644 --- a/src/strconv/atof.go +++ b/src/strconv/atof.go @@ -670,7 +670,8 @@ func atof64(s string) (f float64, n int, err error) { // When bitSize=32, the result still has type float64, but it will be // convertible to float32 without changing its value. // -// ParseFloat accepts decimal and hexadecimal floating-point number syntax. +// ParseFloat accepts decimal and hexadecimal floating-point numbers +// as defined by the Go syntax for [floating-point literals]. // If s is well-formed and near a valid floating-point number, // ParseFloat returns the nearest floating-point number rounded // using IEEE754 unbiased rounding. @@ -689,6 +690,8 @@ func atof64(s string) (f float64, n int, err error) { // // ParseFloat recognizes the strings "NaN", and the (possibly signed) strings "Inf" and "Infinity" // as their respective special floating point values. It ignores case when matching. +// +// [floating-point literals]: https://go.dev/ref/spec#Floating-point_literals func ParseFloat(s string, bitSize int) (float64, error) { f, n, err := parseFloatPrefix(s, bitSize) if n != len(s) && (err == nil || err.(*NumError).Err != ErrSyntax) { diff --git a/src/strconv/atoi.go b/src/strconv/atoi.go index 631b487d97..be08f93356 100644 --- a/src/strconv/atoi.go +++ b/src/strconv/atoi.go @@ -167,7 +167,7 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) { // prefix following the sign (if present): 2 for "0b", 8 for "0" or "0o", // 16 for "0x", and 10 otherwise. Also, for argument base 0 only, // underscore characters are permitted as defined by the Go syntax for -// integer literals. +// [integer literals]. // // The bitSize argument specifies the integer type // that the result must fit into. Bit sizes 0, 8, 16, 32, and 64 @@ -181,6 +181,8 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) { // signed integer of the given size, err.Err = ErrRange and the // returned value is the maximum magnitude integer of the // appropriate bitSize and sign. +// +// [integer literals]: https://go.dev/ref/spec#Integer_literals func ParseInt(s string, base int, bitSize int) (i int64, err error) { const fnParseInt = "ParseInt" From 66cbf67345b1631adbee2109f5bb78fb4e321144 Mon Sep 17 00:00:00 2001 From: Stephen Eckels Date: Sat, 4 Jun 2022 20:39:36 +0000 Subject: [PATCH 009/113] cmd/buildid: reject rewriting legacy buildids This resolves legacy go binaries crashing the buildid tool when the -w flag is specified. Fixes #50809 Change-Id: I55a866f285a3c2cebcf2cdbb9cc30e5078e1d18f GitHub-Last-Rev: 7169a58fd7ba17fdeb8037cef3f50080169dc137 GitHub-Pull-Request: golang/go#53163 Reviewed-on: https://go-review.googlesource.com/c/go/+/409535 Run-TryBot: Ian Lance Taylor Auto-Submit: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Ian Lance Taylor Reviewed-by: Dmitri Shuralyov --- src/cmd/buildid/buildid.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cmd/buildid/buildid.go b/src/cmd/buildid/buildid.go index 8e02a7ae10..72ad80dbbb 100644 --- a/src/cmd/buildid/buildid.go +++ b/src/cmd/buildid/buildid.go @@ -53,6 +53,11 @@ func main() { log.Fatal(err) } + // <= go 1.7 doesn't embed the contentID or actionID, so no slash is present + if !strings.Contains(id, "/") { + log.Fatalf("%s: build ID is a legacy format...binary too old for this tool", file) + } + newID := id[:strings.LastIndex(id, "/")] + "/" + buildid.HashToString(hash) if len(newID) != len(id) { log.Fatalf("%s: build ID length mismatch %q vs %q", file, id, newID) From 846f971daa03fda149a3d66a3fda6eb6a2a7484e Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Sun, 15 May 2022 19:31:42 -0400 Subject: [PATCH 010/113] go/types, types2: remove Named.once in favor of monotonic state Introduce a monotonic state variable to track the lifecycle of a named type, replacing the existing sync.Once. Having a single guard for the state of underlying and methods will allow for cleaning-up when the type is fully expanded. In the future, this state may also be used for detecting access to information such as underlying or methods before the type is fully set-up, though that will require rethinking our type-checking of invalid cyclic types. Also remove support for type-type inference. If we ever support this feature in the future (inference of missing type arguments for named type instances), it will likely involve additional machinery that does not yet exist. Remove the current partial support to simplify our internal APIs. In particular, this means that Named.resolver is only used for lazy loading. As a result, we can revert the lazy loader signature to its previous form. A lot of exposition is added for how Named types work. Along the way, the terminology we use to describe them is refined. Some microbenchmarks are added that were useful in evaluating the tradeoffs between synchronization mechanisms. Updates #52728 Change-Id: I4e147360bc6e5d8cd4f37e32e86fece0530a6480 Reviewed-on: https://go-review.googlesource.com/c/go/+/404875 Run-TryBot: Robert Findley Reviewed-by: Robert Griesemer TryBot-Result: Gopher Robot --- src/cmd/compile/internal/types2/api_test.go | 2 +- src/cmd/compile/internal/types2/decl.go | 4 +- .../compile/internal/types2/instantiate.go | 3 - src/cmd/compile/internal/types2/named.go | 291 +++++++++++++----- src/cmd/compile/internal/types2/named_test.go | 75 +++++ src/cmd/compile/internal/types2/object.go | 14 +- src/cmd/compile/internal/types2/typexpr.go | 48 +-- src/go/types/api_test.go | 2 +- src/go/types/decl.go | 4 +- src/go/types/instantiate.go | 3 - src/go/types/named.go | 291 +++++++++++++----- src/go/types/named_test.go | 88 ++++++ src/go/types/object.go | 14 +- src/go/types/typexpr.go | 53 +--- 14 files changed, 611 insertions(+), 281 deletions(-) create mode 100644 src/cmd/compile/internal/types2/named_test.go create mode 100644 src/go/types/named_test.go diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go index e6de955a6e..f5526bb25a 100644 --- a/src/cmd/compile/internal/types2/api_test.go +++ b/src/cmd/compile/internal/types2/api_test.go @@ -38,7 +38,7 @@ func pkgFor(path, source string, info *Info) (*Package, error) { return conf.Check(f.PkgName.Value, []*syntax.File{f}, info) } -func mustTypecheck(t *testing.T, path, source string, info *Info) string { +func mustTypecheck(t testing.TB, path, source string, info *Info) string { pkg, err := pkgFor(path, source, info) if err != nil { name := path diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index b6f81aa8a5..008d3698b7 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -522,8 +522,8 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named assert(rhs != nil) named.fromRHS = rhs - // If the underlying was not set while type-checking the right-hand side, it - // is invalid and an error should have been reported elsewhere. + // If the underlying type was not set while type-checking the right-hand + // side, it is invalid and an error should have been reported elsewhere. if named.underlying == nil { named.underlying = Typ[Invalid] } diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index bb90ab3736..44ed0319c8 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -79,9 +79,6 @@ func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, ctxt *Co tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) named := check.newNamed(tname, orig, nil, nil) // underlying, tparams, and methods are set when named is resolved named.targs = newTypeList(targs) - named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) { - return expandNamed(ctxt, n, pos) - } res = named case *Signature: diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index 849398a6f4..0a2b2aa6b1 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -5,32 +5,116 @@ package types2 import ( - "cmd/compile/internal/syntax" "sync" + "sync/atomic" ) +// Type-checking Named types is subtle, because they may be recursively +// defined, and because their full details may be spread across multiple +// declarations (via methods). For this reason they are type-checked lazily, +// to avoid information being accessed before it is complete. +// +// Conceptually, it is helpful to think of named types as having two distinct +// sets of information: +// - "LHS" information, defining their identity: Obj() and TypeArgs() +// - "RHS" information, defining their details: TypeParams(), Underlying(), +// and methods. +// +// In this taxonomy, LHS information is available immediately, but RHS +// information is lazy. Specifically, a named type N may be constructed in any +// of the following ways: +// 1. type-checked from the source +// 2. loaded eagerly from export data +// 3. loaded lazily from export data (when using unified IR) +// 4. instantiated from a generic type +// +// In cases 1, 3, and 4, it is possible that the underlying type or methods of +// N may not be immediately available. +// - During type-checking, we allocate N before type-checking its underlying +// type or methods, so that we may resolve recursive references. +// - When loading from export data, we may load its methods and underlying +// type lazily using a provided load function. +// - After instantiating, we lazily expand the underlying type and methods +// (note that instances may be created while still in the process of +// type-checking the original type declaration). +// +// In cases 3 and 4 this lazy construction may also occur concurrently, due to +// concurrent use of the type checker API (after type checking or importing has +// finished). It is critical that we keep track of state, so that Named types +// are constructed exactly once and so that we do not access their details too +// soon. +// +// We achieve this by tracking state with an atomic state variable, and +// guarding potentially concurrent calculations with a mutex. At any point in +// time this state variable determines which data on N may be accessed. As +// state monotonically progresses, any data available at state M may be +// accessed without acquiring the mutex at state N, provided N >= M. +// +// GLOSSARY: Here are a few terms used in this file to describe Named types: +// - We say that a Named type is "instantiated" if it has been constructed by +// instantiating a generic named type with type arguments. +// - We say that a Named type is "declared" if it corresponds to a type +// declaration in the source. Instantiated named types correspond to a type +// instantiation in the source, not a declaration. But their Origin type is +// a declared type. +// - We say that a Named type is "resolved" if its RHS information has been +// loaded or fully type-checked. For Named types constructed from export +// data, this may involve invoking a loader function to extract information +// from export data. For instantiated named types this involves reading +// information from their origin. +// - We say that a Named type is "expanded" if it is an instantiated type and +// type parameters in its underlying type and methods have been substituted +// with the type arguments from the instantiation. A type may be partially +// expanded if some but not all of these details have been substituted. +// Similarly, we refer to these individual details (underlying type or +// method) as being "expanded". +// - When all information is known for a named type, we say it is "complete". +// +// Some invariants to keep in mind: each declared Named type has a single +// corresponding object, and that object's type is the (possibly generic) Named +// type. Declared Named types are identical if and only if their pointers are +// identical. On the other hand, multiple instantiated Named types may be +// identical even though their pointers are not identical. One has to use +// Identical to compare them. For instantiated named types, their obj is a +// synthetic placeholder that records their position of the corresponding +// instantiation in the source (if they were constructed during type checking). + // A Named represents a named (defined) type. type Named struct { - check *Checker - obj *TypeName // corresponding declared object for declared types; placeholder for instantiated types - orig *Named // original, uninstantiated type - fromRHS Type // type (on RHS of declaration) this *Named type is derived from (for cycle reporting) + check *Checker // non-nil during type-checking; nil otherwise + obj *TypeName // corresponding declared object for declared types; see above for instantiated types + orig *Named // origin type for instantiated types, this type for declared types + targs *TypeList // type arguments (after instantiation), or nil + + // fromRHS holds the type (on RHS of declaration) this *Named type is derived + // from (for cycle reporting). Only used by validType, and therefore does not + // require synchronization. + fromRHS Type + + mu sync.Mutex // guards all fields below + state_ uint32 // the current state of this type; must only be accessed atomically underlying Type // possibly a *Named during setup; never a *Named once set up completely tparams *TypeParamList // type parameters, or nil - targs *TypeList // type arguments (after instantiation), or nil // methods declared for this type (not the method set of this type). // Signatures are type-checked lazily. // For non-instantiated types, this is a fully populated list of methods. For - // instantiated types, this is a 'lazy' list, and methods are instantiated - // when they are first accessed. + // instantiated types, this is a 'lazy' list, and methods are individually + // expanded when they are first accessed. methods *methodList - // resolver may be provided to lazily resolve type parameters, underlying, and methods. - resolver func(*Context, *Named) (tparams *TypeParamList, underlying Type, methods *methodList) - once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing + // loader may be provided to lazily load type parameters, underlying, and methods. + loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func) } +// namedState represents the possible states that a named type may assume. +type namedState uint32 + +const ( + unresolved namedState = iota // tparams, underlying type and methods might be unavailable + resolved +) + // NewNamed returns a new named type for the given type name, underlying type, and associated methods. // If the given type name obj doesn't have a type yet, its type is set to the returned named type. // The underlying type must not be a *Named. @@ -41,24 +125,74 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { return (*Checker)(nil).newNamed(obj, nil, underlying, newMethodList(methods)) } -func (t *Named) resolve(ctxt *Context) *Named { - if t.resolver == nil { - return t +// resolve resolves the type parameters, methods, and underlying type of n. +// This information may be loaded from a provided loader function, or computed +// from an origin type (in the case of instances). +// +// After resolution, the type parameters, methods, and underlying type of n are +// accessible; but if n is an instantiated type, its methods may still be +// unexpanded. +func (n *Named) resolve(ctxt *Context) *Named { + if n.state() >= resolved { // avoid locking below + return n } - t.once.Do(func() { - // TODO(mdempsky): Since we're passing t to the resolver anyway - // (necessary because types2 expects the receiver type for methods - // on defined interface types to be the Named rather than the - // underlying Interface), maybe it should just handle calling - // SetTypeParams, SetUnderlying, and AddMethod instead? Those - // methods would need to support reentrant calls though. It would - // also make the API more future-proof towards further extensions - // (like SetTypeParams). - t.tparams, t.underlying, t.methods = t.resolver(ctxt, t) - t.fromRHS = t.underlying // for cycle detection - }) - return t + // TODO(rfindley): if n.check is non-nil we can avoid locking here, since + // type-checking is not concurrent. Evaluate if this is worth doing. + n.mu.Lock() + defer n.mu.Unlock() + + if n.state() >= resolved { + return n + } + + if n.TypeArgs().Len() > 0 { + assert(n.underlying == nil) // n is an unresolved instance + assert(n.loader == nil) // instances are created by instantiation, in which case n.loader is nil + n.orig.resolve(ctxt) + + underlying := n.expandUnderlying(ctxt) + + n.tparams = n.orig.tparams + n.underlying = underlying + n.fromRHS = n.orig.fromRHS // for cycle detection + n.methods = newLazyMethodList(n.orig.methods.Len()) + n.setState(resolved) + return n + } + + // TODO(mdempsky): Since we're passing n to the loader anyway + // (necessary because types2 expects the receiver type for methods + // on defined interface types to be the Named rather than the + // underlying Interface), maybe it should just handle calling + // SetTypeParams, SetUnderlying, and AddMethod instead? Those + // methods would need to support reentrant calls though. It would + // also make the API more future-proof towards further extensions. + if n.loader != nil { + assert(n.underlying == nil) + + tparams, underlying, methods := n.loader(n) + + n.tparams = bindTParams(tparams) + n.underlying = underlying + n.fromRHS = underlying // for cycle detection + n.methods = newMethodList(methods) + n.loader = nil + } + + n.setState(resolved) + return n +} + +// state atomically accesses the current state of the receiver. +func (n *Named) state() namedState { + return namedState(atomic.LoadUint32(&n.state_)) +} + +// setState atomically stores the given state for n. +// Must only be called while holding n.mu. +func (n *Named) setState(state namedState) { + atomic.StoreUint32(&n.state_, uint32(state)) } // newNamed is like NewNamed but with a *Checker receiver and additional orig argument. @@ -80,16 +214,16 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, meth func (t *Named) cleanup() { assert(t.orig.orig == t.orig) // Ensure that every defined type created in the course of type-checking has - // either non-*Named underlying, or is unresolved. + // either non-*Named underlying type, or is unexpanded. // - // This guarantees that we don't leak any types whose underlying is *Named, - // because any unresolved instances will lazily compute their underlying by - // substituting in the underlying of their origin. The origin must have - // either been imported or type-checked and expanded here, and in either case - // its underlying will be fully expanded. + // This guarantees that we don't leak any types whose underlying type is + // *Named, because any unexpanded instances will lazily compute their + // underlying type by substituting in the underlying type of their origin. + // The origin must have either been imported or type-checked and expanded + // here, and in either case its underlying type will be fully expanded. switch t.underlying.(type) { case nil: - if t.resolver == nil { + if t.TypeArgs().Len() == 0 { panic("nil underlying") } case *Named: @@ -134,12 +268,13 @@ func (t *Named) NumMethods() int { return t.resolve(nil).methods.Len() } func (t *Named) Method(i int) *Func { t.resolve(nil) return t.methods.At(i, func() *Func { - return t.instantiateMethod(i) + return t.expandMethod(i) }) } -// instantiateMethod instantiates the i'th method for an instantiated receiver. -func (t *Named) instantiateMethod(i int) *Func { +// expandMethod substitutes type arguments in the i'th method for an +// instantiated receiver. +func (t *Named) expandMethod(i int) *Func { assert(t.TypeArgs().Len() > 0) // t must be an instance // t.orig.methods is not lazy. origm is the method instantiated with its @@ -275,7 +410,7 @@ func (n0 *Named) under() Type { check := n0.check n := n0 - seen := make(map[*Named]int) // types that need their underlying resolved + seen := make(map[*Named]int) // types that need their underlying type resolved var path []Object // objects encountered, for cycle reporting loop: @@ -352,66 +487,64 @@ func (check *Checker) bestContext(ctxt *Context) *Context { return NewContext() } -// expandNamed ensures that the underlying type of n is instantiated. -// The underlying type will be Typ[Invalid] if there was an error. -func expandNamed(ctxt *Context, n *Named, instPos syntax.Pos) (tparams *TypeParamList, underlying Type, methods *methodList) { +// expandUnderlying substitutes type arguments in the underlying type n.orig, +// returning the result. Returns Typ[Invalid] if there was an error. +func (n *Named) expandUnderlying(ctxt *Context) Type { check := n.check if check != nil && check.conf.Trace { - check.trace(instPos, "-- expandNamed %s", n) + check.trace(n.obj.pos, "-- Named.expandUnderlying %s", n) check.indent++ defer func() { check.indent-- - check.trace(instPos, "=> %s (tparams = %s, under = %s)", n, tparams.list(), underlying) + check.trace(n.obj.pos, "=> %s (tparams = %s, under = %s)", n, n.tparams.list(), n.underlying) }() } - n.orig.resolve(ctxt) assert(n.orig.underlying != nil) if _, unexpanded := n.orig.underlying.(*Named); unexpanded { - // We should only get an unexpanded underlying here during type checking + // We should only get a Named underlying type here during type checking // (for example, in recursive type declarations). assert(check != nil) } - // Mismatching arg and tparam length may be checked elsewhere. - if n.orig.tparams.Len() == n.targs.Len() { - // We must always have a context, to avoid infinite recursion. - ctxt = check.bestContext(ctxt) - h := ctxt.instanceHash(n.orig, n.targs.list()) - // ensure that an instance is recorded for h to avoid infinite recursion. - ctxt.update(h, n.orig, n.TypeArgs().list(), n) - - smap := makeSubstMap(n.orig.tparams.list(), n.targs.list()) - underlying = n.check.subst(instPos, n.orig.underlying, smap, ctxt) - // If the underlying of n is an interface, we need to set the receiver of - // its methods accurately -- we set the receiver of interface methods on - // the RHS of a type declaration to the defined type. - if iface, _ := underlying.(*Interface); iface != nil { - if methods, copied := replaceRecvType(iface.methods, n.orig, n); copied { - // If the underlying doesn't actually use type parameters, it's possible - // that it wasn't substituted. In this case we need to create a new - // *Interface before modifying receivers. - if iface == n.orig.underlying { - old := iface - iface = check.newInterface() - iface.embeddeds = old.embeddeds - iface.complete = old.complete - iface.implicit = old.implicit // should be false but be conservative - underlying = iface - } - iface.methods = methods - } - } - } else { - underlying = Typ[Invalid] + if n.orig.tparams.Len() != n.targs.Len() { + // Mismatching arg and tparam length may be checked elsewhere. + return Typ[Invalid] } - return n.orig.tparams, underlying, newLazyMethodList(n.orig.methods.Len()) + // We must always have a context, to avoid infinite recursion. + ctxt = check.bestContext(ctxt) + h := ctxt.instanceHash(n.orig, n.targs.list()) + // ensure that an instance is recorded for h to avoid infinite recursion. + ctxt.update(h, n.orig, n.TypeArgs().list(), n) + + smap := makeSubstMap(n.orig.tparams.list(), n.targs.list()) + underlying := n.check.subst(n.obj.pos, n.orig.underlying, smap, ctxt) + // If the underlying type of n is an interface, we need to set the receiver + // of its methods accurately -- we set the receiver of interface methods on + // the RHS of a type declaration to the defined type. + if iface, _ := underlying.(*Interface); iface != nil { + if methods, copied := replaceRecvType(iface.methods, n.orig, n); copied { + // If the underlying type doesn't actually use type parameters, it's + // possible that it wasn't substituted. In this case we need to create + // a new *Interface before modifying receivers. + if iface == n.orig.underlying { + old := iface + iface = check.newInterface() + iface.embeddeds = old.embeddeds + iface.complete = old.complete + iface.implicit = old.implicit // should be false but be conservative + underlying = iface + } + iface.methods = methods + } + } + return underlying } -// safeUnderlying returns the underlying of typ without expanding instances, to -// avoid infinite recursion. +// safeUnderlying returns the underlying type of typ without expanding +// instances, to avoid infinite recursion. // // TODO(rfindley): eliminate this function or give it a better name. func safeUnderlying(typ Type) Type { diff --git a/src/cmd/compile/internal/types2/named_test.go b/src/cmd/compile/internal/types2/named_test.go new file mode 100644 index 0000000000..14a982048a --- /dev/null +++ b/src/cmd/compile/internal/types2/named_test.go @@ -0,0 +1,75 @@ +// 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 types2_test + +import ( + "testing" + + . "cmd/compile/internal/types2" +) + +func BenchmarkNamed(b *testing.B) { + const src = ` +package p + +type T struct { + P int +} + +func (T) M(int) {} +func (T) N() (i int) { return } + +type G[P any] struct { + F P +} + +func (G[P]) M(P) {} +func (G[P]) N() (p P) { return } + +type Inst = G[int] + ` + pkg, err := pkgFor("p", src, nil) + if err != nil { + b.Fatal(err) + } + + var ( + T = pkg.Scope().Lookup("T").Type() + G = pkg.Scope().Lookup("G").Type() + SrcInst = pkg.Scope().Lookup("Inst").Type() + UserInst = mustInstantiate(b, G, Typ[Int]) + ) + + tests := []struct { + name string + typ Type + }{ + {"nongeneric", T}, + {"generic", G}, + {"src instance", SrcInst}, + {"user instance", UserInst}, + } + + b.Run("Underlying", func(b *testing.B) { + for _, test := range tests { + b.Run(test.name, func(b *testing.B) { + // Access underlying once, to trigger any lazy calculation. + _ = test.typ.Underlying() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = test.typ.Underlying() + } + }) + } + }) +} + +func mustInstantiate(tb testing.TB, orig Type, targs ...Type) Type { + inst, err := Instantiate(nil, orig, targs, true) + if err != nil { + tb.Fatal(err) + } + return inst +} diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go index 75f7ea5b12..df080f071c 100644 --- a/src/cmd/compile/internal/types2/object.go +++ b/src/cmd/compile/internal/types2/object.go @@ -279,19 +279,7 @@ func NewTypeName(pos syntax.Pos, pkg *Package, name string, typ Type) *TypeName // lazily calls resolve to finish constructing the Named object. func NewTypeNameLazy(pos syntax.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName { obj := NewTypeName(pos, pkg, name, nil) - - resolve := func(_ *Context, t *Named) (*TypeParamList, Type, *methodList) { - tparams, underlying, methods := load(t) - - switch underlying.(type) { - case nil, *Named: - panic(fmt.Sprintf("invalid underlying type %T", t.underlying)) - } - - return bindTParams(tparams), underlying, newMethodList(methods) - } - - NewNamed(obj, nil, nil).resolver = resolve + NewNamed(obj, nil, nil).loader = load return obj } diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index 1f8b40dba6..020653332d 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -432,57 +432,20 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def * return Typ[Invalid] } - // enableTypeTypeInference controls whether to infer missing type arguments - // using constraint type inference. See issue #51527. - const enableTypeTypeInference = false - // create the instance ctxt := check.bestContext(nil) - h := ctxt.instanceHash(orig, targs) - // targs may be incomplete, and require inference. In any case we should de-duplicate. - inst, _ := ctxt.lookup(h, orig, targs).(*Named) - // If inst is non-nil, we can't just return here. Inst may have been - // constructed via recursive substitution, in which case we wouldn't do the - // validation below. Ensure that the validation (and resulting errors) runs - // for each instantiated type in the source. - if inst == nil { - // x may be a selector for an imported type; use its start pos rather than x.Pos(). - tname := NewTypeName(syntax.StartPos(x), orig.obj.pkg, orig.obj.name, nil) - inst = check.newNamed(tname, orig, nil, nil) // underlying, methods and tparams are set when named is resolved - inst.targs = newTypeList(targs) - inst = ctxt.update(h, orig, targs, inst).(*Named) - } + inst := check.instance(x.Pos(), orig, targs, ctxt).(*Named) def.setUnderlying(inst) - inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) { - tparams := n.orig.TypeParams().list() - - targs := n.targs.list() - if enableTypeTypeInference && len(targs) < len(tparams) { - // If inference fails, len(inferred) will be 0, and inst.underlying will - // be set to Typ[Invalid] in expandNamed. - inferred := check.infer(x.Pos(), tparams, targs, nil, nil) - if len(inferred) > len(targs) { - n.targs = newTypeList(inferred) - } - } - - return expandNamed(ctxt, n, x.Pos()) - } - // orig.tparams may not be set up, so we need to do expansion later. check.later(func() { // This is an instance from the source, not from recursive substitution, // and so it must be resolved during type-checking so that we can report // errors. - inst.resolve(ctxt) - // Since check is non-nil, we can still mutate inst. Unpinning the resolver - // frees some memory. - inst.resolver = nil check.recordInstance(x, inst.TypeArgs().list(), inst) - if check.validateTArgLen(x.Pos(), inst.tparams.Len(), inst.targs.Len()) { - if i, err := check.verify(x.Pos(), inst.tparams.list(), inst.targs.list()); err != nil { + if check.validateTArgLen(x.Pos(), inst.TypeParams().Len(), inst.targs.Len()) { + if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.targs.list()); err != nil { // best position for error reporting pos := x.Pos() if i < len(xlist) { @@ -490,10 +453,13 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def * } check.softErrorf(pos, "%s", err) } else { - check.mono.recordInstance(check.pkg, x.Pos(), inst.tparams.list(), inst.targs.list(), xlist) + check.mono.recordInstance(check.pkg, x.Pos(), inst.TypeParams().list(), inst.targs.list(), xlist) } } + // TODO(rfindley): remove this call: we don't need to call validType here, + // as cycles can only occur for types used inside a Named type declaration, + // and so it suffices to call validType from declared types. check.validType(inst) }).describef(x, "resolve instance %s", inst) diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index eb17f9280d..db2ace5feb 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -43,7 +43,7 @@ func pkgForMode(path, source string, info *Info, mode parser.Mode) (*Package, er return conf.Check(f.Name.Name, fset, []*ast.File{f}, info) } -func mustTypecheck(t *testing.T, path, source string, info *Info) string { +func mustTypecheck(t testing.TB, path, source string, info *Info) string { pkg, err := pkgFor(path, source, info) if err != nil { name := path diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 123d296791..61c9696948 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -579,8 +579,8 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) { assert(rhs != nil) named.fromRHS = rhs - // If the underlying was not set while type-checking the right-hand side, it - // is invalid and an error should have been reported elsewhere. + // If the underlying type was not set while type-checking the right-hand + // side, it is invalid and an error should have been reported elsewhere. if named.underlying == nil { named.underlying = Typ[Invalid] } diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index 964a4f907c..8be0eab407 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -79,9 +79,6 @@ func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, ctxt *Con tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) named := check.newNamed(tname, orig, nil, nil) // underlying, tparams, and methods are set when named is resolved named.targs = newTypeList(targs) - named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) { - return expandNamed(ctxt, n, pos) - } res = named case *Signature: diff --git a/src/go/types/named.go b/src/go/types/named.go index a82679eb10..bfb4a11da7 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -5,32 +5,116 @@ package types import ( - "go/token" "sync" + "sync/atomic" ) +// Type-checking Named types is subtle, because they may be recursively +// defined, and because their full details may be spread across multiple +// declarations (via methods). For this reason they are type-checked lazily, +// to avoid information being accessed before it is complete. +// +// Conceptually, it is helpful to think of named types as having two distinct +// sets of information: +// - "LHS" information, defining their identity: Obj() and TypeArgs() +// - "RHS" information, defining their details: TypeParams(), Underlying(), +// and methods. +// +// In this taxonomy, LHS information is available immediately, but RHS +// information is lazy. Specifically, a named type N may be constructed in any +// of the following ways: +// 1. type-checked from the source +// 2. loaded eagerly from export data +// 3. loaded lazily from export data (when using unified IR) +// 4. instantiated from a generic type +// +// In cases 1, 3, and 4, it is possible that the underlying type or methods of +// N may not be immediately available. +// - During type-checking, we allocate N before type-checking its underlying +// type or methods, so that we may resolve recursive references. +// - When loading from export data, we may load its methods and underlying +// type lazily using a provided load function. +// - After instantiating, we lazily expand the underlying type and methods +// (note that instances may be created while still in the process of +// type-checking the original type declaration). +// +// In cases 3 and 4 this lazy construction may also occur concurrently, due to +// concurrent use of the type checker API (after type checking or importing has +// finished). It is critical that we keep track of state, so that Named types +// are constructed exactly once and so that we do not access their details too +// soon. +// +// We achieve this by tracking state with an atomic state variable, and +// guarding potentially concurrent calculations with a mutex. At any point in +// time this state variable determines which data on N may be accessed. As +// state monotonically progresses, any data available at state M may be +// accessed without acquiring the mutex at state N, provided N >= M. +// +// GLOSSARY: Here are a few terms used in this file to describe Named types: +// - We say that a Named type is "instantiated" if it has been constructed by +// instantiating a generic named type with type arguments. +// - We say that a Named type is "declared" if it corresponds to a type +// declaration in the source. Instantiated named types correspond to a type +// instantiation in the source, not a declaration. But their Origin type is +// a declared type. +// - We say that a Named type is "resolved" if its RHS information has been +// loaded or fully type-checked. For Named types constructed from export +// data, this may involve invoking a loader function to extract information +// from export data. For instantiated named types this involves reading +// information from their origin. +// - We say that a Named type is "expanded" if it is an instantiated type and +// type parameters in its underlying type and methods have been substituted +// with the type arguments from the instantiation. A type may be partially +// expanded if some but not all of these details have been substituted. +// Similarly, we refer to these individual details (underlying type or +// method) as being "expanded". +// - When all information is known for a named type, we say it is "complete". +// +// Some invariants to keep in mind: each declared Named type has a single +// corresponding object, and that object's type is the (possibly generic) Named +// type. Declared Named types are identical if and only if their pointers are +// identical. On the other hand, multiple instantiated Named types may be +// identical even though their pointers are not identical. One has to use +// Identical to compare them. For instantiated named types, their obj is a +// synthetic placeholder that records their position of the corresponding +// instantiation in the source (if they were constructed during type checking). + // A Named represents a named (defined) type. type Named struct { - check *Checker - obj *TypeName // corresponding declared object for declared types; placeholder for instantiated types - orig *Named // original, uninstantiated type - fromRHS Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting) + check *Checker // non-nil during type-checking; nil otherwise + obj *TypeName // corresponding declared object for declared types; see above for instantiated types + orig *Named // origin type for instantiated types, this type for declared types + targs *TypeList // type arguments (after instantiation), or nil + + // fromRHS holds the type (on RHS of declaration) this *Named type is derived + // from (for cycle reporting). Only used by validType, and therefore does not + // require synchronization. + fromRHS Type + + mu sync.Mutex // guards all fields below + state_ uint32 // the current state of this type; must only be accessed atomically underlying Type // possibly a *Named during setup; never a *Named once set up completely tparams *TypeParamList // type parameters, or nil - targs *TypeList // type arguments (after instantiation), or nil // methods declared for this type (not the method set of this type). // Signatures are type-checked lazily. // For non-instantiated types, this is a fully populated list of methods. For - // instantiated types, this is a 'lazy' list, and methods are instantiated - // when they are first accessed. + // instantiated types, this is a 'lazy' list, and methods are individually + // expanded when they are first accessed. methods *methodList - // resolver may be provided to lazily resolve type parameters, underlying, and methods. - resolver func(*Context, *Named) (tparams *TypeParamList, underlying Type, methods *methodList) - once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing + // loader may be provided to lazily load type parameters, underlying, and methods. + loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func) } +// namedState represents the possible states that a named type may assume. +type namedState uint32 + +const ( + unresolved namedState = iota // tparams, underlying type and methods might be unavailable + resolved +) + // NewNamed returns a new named type for the given type name, underlying type, and associated methods. // If the given type name obj doesn't have a type yet, its type is set to the returned named type. // The underlying type must not be a *Named. @@ -41,24 +125,74 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { return (*Checker)(nil).newNamed(obj, nil, underlying, newMethodList(methods)) } -func (t *Named) resolve(ctxt *Context) *Named { - if t.resolver == nil { - return t +// resolve resolves the type parameters, methods, and underlying type of n. +// This information may be loaded from a provided loader function, or computed +// from an origin type (in the case of instances). +// +// After resolution, the type parameters, methods, and underlying type of n are +// accessible; but if n is an instantiated type, its methods may still be +// unexpanded. +func (n *Named) resolve(ctxt *Context) *Named { + if n.state() >= resolved { // avoid locking below + return n } - t.once.Do(func() { - // TODO(mdempsky): Since we're passing t to the resolver anyway - // (necessary because types2 expects the receiver type for methods - // on defined interface types to be the Named rather than the - // underlying Interface), maybe it should just handle calling - // SetTypeParams, SetUnderlying, and AddMethod instead? Those - // methods would need to support reentrant calls though. It would - // also make the API more future-proof towards further extensions - // (like SetTypeParams). - t.tparams, t.underlying, t.methods = t.resolver(ctxt, t) - t.fromRHS = t.underlying // for cycle detection - }) - return t + // TODO(rfindley): if n.check is non-nil we can avoid locking here, since + // type-checking is not concurrent. Evaluate if this is worth doing. + n.mu.Lock() + defer n.mu.Unlock() + + if n.state() >= resolved { + return n + } + + if n.TypeArgs().Len() > 0 { + assert(n.underlying == nil) // n is an unresolved instance + assert(n.loader == nil) // instances are created by instantiation, in which case n.loader is nil + n.orig.resolve(ctxt) + + underlying := n.expandUnderlying(ctxt) + + n.tparams = n.orig.tparams + n.underlying = underlying + n.fromRHS = n.orig.fromRHS // for cycle detection + n.methods = newLazyMethodList(n.orig.methods.Len()) + n.setState(resolved) + return n + } + + // TODO(mdempsky): Since we're passing n to the loader anyway + // (necessary because types2 expects the receiver type for methods + // on defined interface types to be the Named rather than the + // underlying Interface), maybe it should just handle calling + // SetTypeParams, SetUnderlying, and AddMethod instead? Those + // methods would need to support reentrant calls though. It would + // also make the API more future-proof towards further extensions. + if n.loader != nil { + assert(n.underlying == nil) + + tparams, underlying, methods := n.loader(n) + + n.tparams = bindTParams(tparams) + n.underlying = underlying + n.fromRHS = underlying // for cycle detection + n.methods = newMethodList(methods) + n.loader = nil + } + + n.setState(resolved) + return n +} + +// state atomically accesses the current state of the receiver. +func (n *Named) state() namedState { + return namedState(atomic.LoadUint32(&n.state_)) +} + +// setState atomically stores the given state for n. +// Must only be called while holding n.mu. +func (n *Named) setState(state namedState) { + atomic.StoreUint32(&n.state_, uint32(state)) } // newNamed is like NewNamed but with a *Checker receiver and additional orig argument. @@ -80,16 +214,16 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, meth func (t *Named) cleanup() { assert(t.orig.orig == t.orig) // Ensure that every defined type created in the course of type-checking has - // either non-*Named underlying, or is unresolved. + // either non-*Named underlying type, or is unexpanded. // - // This guarantees that we don't leak any types whose underlying is *Named, - // because any unresolved instances will lazily compute their underlying by - // substituting in the underlying of their origin. The origin must have - // either been imported or type-checked and expanded here, and in either case - // its underlying will be fully expanded. + // This guarantees that we don't leak any types whose underlying type is + // *Named, because any unexpanded instances will lazily compute their + // underlying type by substituting in the underlying type of their origin. + // The origin must have either been imported or type-checked and expanded + // here, and in either case its underlying type will be fully expanded. switch t.underlying.(type) { case nil: - if t.resolver == nil { + if t.TypeArgs().Len() == 0 { panic("nil underlying") } case *Named: @@ -136,12 +270,13 @@ func (t *Named) NumMethods() int { return t.resolve(nil).methods.Len() } func (t *Named) Method(i int) *Func { t.resolve(nil) return t.methods.At(i, func() *Func { - return t.instantiateMethod(i) + return t.expandMethod(i) }) } -// instantiateMethod instantiates the i'th method for an instantiated receiver. -func (t *Named) instantiateMethod(i int) *Func { +// expandMethod substitutes type arguments in the i'th method for an +// instantiated receiver. +func (t *Named) expandMethod(i int) *Func { assert(t.TypeArgs().Len() > 0) // t must be an instance // t.orig.methods is not lazy. origm is the method instantiated with its @@ -277,7 +412,7 @@ func (n0 *Named) under() Type { check := n0.check n := n0 - seen := make(map[*Named]int) // types that need their underlying resolved + seen := make(map[*Named]int) // types that need their underlying type resolved var path []Object // objects encountered, for cycle reporting loop: @@ -354,66 +489,64 @@ func (check *Checker) bestContext(ctxt *Context) *Context { return NewContext() } -// expandNamed ensures that the underlying type of n is instantiated. -// The underlying type will be Typ[Invalid] if there was an error. -func expandNamed(ctxt *Context, n *Named, instPos token.Pos) (tparams *TypeParamList, underlying Type, methods *methodList) { +// expandUnderlying substitutes type arguments in the underlying type n.orig, +// returning the result. Returns Typ[Invalid] if there was an error. +func (n *Named) expandUnderlying(ctxt *Context) Type { check := n.check if check != nil && trace { - check.trace(instPos, "-- expandNamed %s", n) + check.trace(n.obj.pos, "-- Named.expandUnderlying %s", n) check.indent++ defer func() { check.indent-- - check.trace(instPos, "=> %s (tparams = %s, under = %s)", n, tparams.list(), underlying) + check.trace(n.obj.pos, "=> %s (tparams = %s, under = %s)", n, n.tparams.list(), n.underlying) }() } - n.orig.resolve(ctxt) assert(n.orig.underlying != nil) if _, unexpanded := n.orig.underlying.(*Named); unexpanded { - // We should only get an unexpanded underlying here during type checking + // We should only get a Named underlying type here during type checking // (for example, in recursive type declarations). assert(check != nil) } - // Mismatching arg and tparam length may be checked elsewhere. - if n.orig.tparams.Len() == n.targs.Len() { - // We must always have a context, to avoid infinite recursion. - ctxt = check.bestContext(ctxt) - h := ctxt.instanceHash(n.orig, n.targs.list()) - // ensure that an instance is recorded for h to avoid infinite recursion. - ctxt.update(h, n.orig, n.TypeArgs().list(), n) - - smap := makeSubstMap(n.orig.tparams.list(), n.targs.list()) - underlying = n.check.subst(instPos, n.orig.underlying, smap, ctxt) - // If the underlying of n is an interface, we need to set the receiver of - // its methods accurately -- we set the receiver of interface methods on - // the RHS of a type declaration to the defined type. - if iface, _ := underlying.(*Interface); iface != nil { - if methods, copied := replaceRecvType(iface.methods, n.orig, n); copied { - // If the underlying doesn't actually use type parameters, it's possible - // that it wasn't substituted. In this case we need to create a new - // *Interface before modifying receivers. - if iface == n.orig.underlying { - old := iface - iface = check.newInterface() - iface.embeddeds = old.embeddeds - iface.complete = old.complete - iface.implicit = old.implicit // should be false but be conservative - underlying = iface - } - iface.methods = methods - } - } - } else { - underlying = Typ[Invalid] + if n.orig.tparams.Len() != n.targs.Len() { + // Mismatching arg and tparam length may be checked elsewhere. + return Typ[Invalid] } - return n.orig.tparams, underlying, newLazyMethodList(n.orig.methods.Len()) + // We must always have a context, to avoid infinite recursion. + ctxt = check.bestContext(ctxt) + h := ctxt.instanceHash(n.orig, n.targs.list()) + // ensure that an instance is recorded for h to avoid infinite recursion. + ctxt.update(h, n.orig, n.TypeArgs().list(), n) + + smap := makeSubstMap(n.orig.tparams.list(), n.targs.list()) + underlying := n.check.subst(n.obj.pos, n.orig.underlying, smap, ctxt) + // If the underlying type of n is an interface, we need to set the receiver + // of its methods accurately -- we set the receiver of interface methods on + // the RHS of a type declaration to the defined type. + if iface, _ := underlying.(*Interface); iface != nil { + if methods, copied := replaceRecvType(iface.methods, n.orig, n); copied { + // If the underlying type doesn't actually use type parameters, it's + // possible that it wasn't substituted. In this case we need to create + // a new *Interface before modifying receivers. + if iface == n.orig.underlying { + old := iface + iface = check.newInterface() + iface.embeddeds = old.embeddeds + iface.complete = old.complete + iface.implicit = old.implicit // should be false but be conservative + underlying = iface + } + iface.methods = methods + } + } + return underlying } -// safeUnderlying returns the underlying of typ without expanding instances, to -// avoid infinite recursion. +// safeUnderlying returns the underlying type of typ without expanding +// instances, to avoid infinite recursion. // // TODO(rfindley): eliminate this function or give it a better name. func safeUnderlying(typ Type) Type { diff --git a/src/go/types/named_test.go b/src/go/types/named_test.go new file mode 100644 index 0000000000..74cdb48889 --- /dev/null +++ b/src/go/types/named_test.go @@ -0,0 +1,88 @@ +// 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 types_test + +import ( + "testing" + + . "go/types" +) + +func BenchmarkNamed(b *testing.B) { + const src = ` +package p + +type T struct { + P int +} + +func (T) M(int) {} +func (T) N() (i int) { return } + +type G[P any] struct { + F P +} + +func (G[P]) M(P) {} +func (G[P]) N() (p P) { return } + +type Inst = G[int] + ` + pkg, err := pkgForMode("p", src, nil, 0) + if err != nil { + b.Fatal(err) + } + + var ( + T = pkg.Scope().Lookup("T").Type() + G = pkg.Scope().Lookup("G").Type() + SrcInst = pkg.Scope().Lookup("Inst").Type() + UserInst = mustInstantiate(b, G, Typ[Int]) + ) + + tests := []struct { + name string + typ Type + }{ + {"nongeneric", T}, + {"generic", G}, + {"src instance", SrcInst}, + {"user instance", UserInst}, + } + + b.Run("Underlying", func(b *testing.B) { + for _, test := range tests { + b.Run(test.name, func(b *testing.B) { + // Access underlying once, to trigger any lazy calculation. + _ = test.typ.Underlying() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = test.typ.Underlying() + } + }) + } + }) + + b.Run("NewMethodSet", func(b *testing.B) { + for _, test := range tests { + b.Run(test.name, func(b *testing.B) { + // Access underlying once, to trigger any lazy calculation. + _ = NewMethodSet(test.typ) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = NewMethodSet(test.typ) + } + }) + } + }) +} + +func mustInstantiate(tb testing.TB, orig Type, targs ...Type) Type { + inst, err := Instantiate(nil, orig, targs, true) + if err != nil { + tb.Fatal(err) + } + return inst +} diff --git a/src/go/types/object.go b/src/go/types/object.go index ae138a5879..f203b0113d 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -233,19 +233,7 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { // lazily calls resolve to finish constructing the Named object. func _NewTypeNameLazy(pos token.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName { obj := NewTypeName(pos, pkg, name, nil) - - resolve := func(_ *Context, t *Named) (*TypeParamList, Type, *methodList) { - tparams, underlying, methods := load(t) - - switch underlying.(type) { - case nil, *Named: - panic(fmt.Sprintf("invalid underlying type %T", t.underlying)) - } - - return bindTParams(tparams), underlying, newMethodList(methods) - } - - NewNamed(obj, nil, nil).resolver = resolve + NewNamed(obj, nil, nil).loader = load return obj } diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 7afc66a925..c7161e00a5 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -385,14 +385,13 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { } func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (res Type) { - pos := ix.X.Pos() if trace { - check.trace(pos, "-- instantiating type %s with %s", ix.X, ix.Indices) + check.trace(ix.Pos(), "-- instantiating type %s with %s", ix.X, ix.Indices) check.indent++ defer func() { check.indent-- // Don't format the underlying here. It will always be nil. - check.trace(pos, "=> %s", res) + check.trace(ix.Pos(), "=> %s", res) }() } @@ -417,57 +416,20 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re return Typ[Invalid] } - // enableTypeTypeInference controls whether to infer missing type arguments - // using constraint type inference. See issue #51527. - const enableTypeTypeInference = false - // create the instance ctxt := check.bestContext(nil) - h := ctxt.instanceHash(orig, targs) - // targs may be incomplete, and require inference. In any case we should de-duplicate. - inst, _ := ctxt.lookup(h, orig, targs).(*Named) - // If inst is non-nil, we can't just return here. Inst may have been - // constructed via recursive substitution, in which case we wouldn't do the - // validation below. Ensure that the validation (and resulting errors) runs - // for each instantiated type in the source. - if inst == nil { - // x may be a selector for an imported type; use its start pos rather than x.Pos(). - tname := NewTypeName(ix.Pos(), orig.obj.pkg, orig.obj.name, nil) - inst = check.newNamed(tname, orig, nil, nil) // underlying, methods and tparams are set when named is resolved - inst.targs = newTypeList(targs) - inst = ctxt.update(h, orig, targs, inst).(*Named) - } + inst := check.instance(ix.Pos(), orig, targs, ctxt).(*Named) def.setUnderlying(inst) - inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) { - tparams := n.orig.TypeParams().list() - - targs := n.targs.list() - if enableTypeTypeInference && len(targs) < len(tparams) { - // If inference fails, len(inferred) will be 0, and inst.underlying will - // be set to Typ[Invalid] in expandNamed. - inferred := check.infer(ix.Orig, tparams, targs, nil, nil) - if len(inferred) > len(targs) { - n.targs = newTypeList(inferred) - } - } - - return expandNamed(ctxt, n, pos) - } - // orig.tparams may not be set up, so we need to do expansion later. check.later(func() { // This is an instance from the source, not from recursive substitution, // and so it must be resolved during type-checking so that we can report // errors. - inst.resolve(ctxt) - // Since check is non-nil, we can still mutate inst. Unpinning the resolver - // frees some memory. - inst.resolver = nil check.recordInstance(ix.Orig, inst.TypeArgs().list(), inst) - if check.validateTArgLen(pos, inst.tparams.Len(), inst.targs.Len()) { - if i, err := check.verify(pos, inst.tparams.list(), inst.targs.list()); err != nil { + if check.validateTArgLen(ix.Pos(), inst.TypeParams().Len(), inst.targs.Len()) { + if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.targs.list()); err != nil { // best position for error reporting pos := ix.Pos() if i < len(ix.Indices) { @@ -475,10 +437,13 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re } check.softErrorf(atPos(pos), _InvalidTypeArg, err.Error()) } else { - check.mono.recordInstance(check.pkg, pos, inst.tparams.list(), inst.targs.list(), ix.Indices) + check.mono.recordInstance(check.pkg, ix.Pos(), inst.TypeParams().list(), inst.targs.list(), ix.Indices) } } + // TODO(rfindley): remove this call: we don't need to call validType here, + // as cycles can only occur for types used inside a Named type declaration, + // and so it suffices to call validType from declared types. check.validType(inst) }).describef(ix, "resolve instance %s", inst) From 1323b0e8f0c5afb72afe51d8ee3bd5f66c23e353 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Sat, 7 May 2022 18:59:14 -0400 Subject: [PATCH 011/113] go/types, types2: eliminate methodList in favor of just using Named.mu In order to clean up context after fully expanding a type (in subsequent CLs), we must use a common mutex. Eliminate the lazy methodList type, which keeps a sync.Once per method, in favor of Named.mu. Updates #52728 Change-Id: I2d13319276df1330ee53046ef1823b0167a258d8 Reviewed-on: https://go-review.googlesource.com/c/go/+/404883 TryBot-Result: Gopher Robot Run-TryBot: Robert Findley Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/types2/decl.go | 8 +- src/cmd/compile/internal/types2/methodlist.go | 79 ------------------- .../internal/types2/methodlist_test.go | 40 ---------- src/cmd/compile/internal/types2/named.go | 71 ++++++++++++----- .../compile/internal/types2/sizeof_test.go | 2 +- src/go/types/decl.go | 8 +- src/go/types/methodlist.go | 79 ------------------- src/go/types/methodlist_test.go | 41 ---------- src/go/types/named.go | 71 ++++++++++++----- src/go/types/sizeof_test.go | 2 +- 10 files changed, 112 insertions(+), 289 deletions(-) delete mode 100644 src/cmd/compile/internal/types2/methodlist.go delete mode 100644 src/cmd/compile/internal/types2/methodlist_test.go delete mode 100644 src/go/types/methodlist.go delete mode 100644 src/go/types/methodlist_test.go diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index 008d3698b7..a5d29765c6 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -646,8 +646,8 @@ func (check *Checker) collectMethods(obj *TypeName) { // Checker.Files may be called multiple times; additional package files // may add methods to already type-checked types. Add pre-existing methods // so that we can detect redeclarations. - for i := 0; i < base.methods.Len(); i++ { - m := base.methods.At(i, nil) + for i := 0; i < base.NumMethods(); i++ { + m := base.Method(i) assert(m.name != "_") assert(mset.insert(m) == nil) } @@ -679,8 +679,8 @@ func (check *Checker) collectMethods(obj *TypeName) { func (check *Checker) checkFieldUniqueness(base *Named) { if t, _ := base.under().(*Struct); t != nil { var mset objset - for i := 0; i < base.methods.Len(); i++ { - m := base.methods.At(i, nil) + for i := 0; i < base.NumMethods(); i++ { + m := base.Method(i) assert(m.name != "_") assert(mset.insert(m) == nil) } diff --git a/src/cmd/compile/internal/types2/methodlist.go b/src/cmd/compile/internal/types2/methodlist.go deleted file mode 100644 index cd6c06c5fb..0000000000 --- a/src/cmd/compile/internal/types2/methodlist.go +++ /dev/null @@ -1,79 +0,0 @@ -// 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 types2 - -import "sync" - -// methodList holds a list of methods that may be lazily resolved by a provided -// resolution method. -type methodList struct { - methods []*Func - - // guards synchronizes the instantiation of lazy methods. For lazy method - // lists, guards is non-nil and of the length passed to newLazyMethodList. - // For non-lazy method lists, guards is nil. - guards *[]sync.Once -} - -// newMethodList creates a non-lazy method list holding the given methods. -func newMethodList(methods []*Func) *methodList { - return &methodList{methods: methods} -} - -// newLazyMethodList creates a lazy method list of the given length. Methods -// may be resolved lazily for a given index by providing a resolver function. -func newLazyMethodList(length int) *methodList { - guards := make([]sync.Once, length) - return &methodList{ - methods: make([]*Func, length), - guards: &guards, - } -} - -// isLazy reports whether the receiver is a lazy method list. -func (l *methodList) isLazy() bool { - return l != nil && l.guards != nil -} - -// Add appends a method to the method list if not not already present. Add -// panics if the receiver is lazy. -func (l *methodList) Add(m *Func) { - assert(!l.isLazy()) - if i, _ := lookupMethod(l.methods, m.pkg, m.name, false); i < 0 { - l.methods = append(l.methods, m) - } -} - -// Lookup looks up the method identified by pkg and name in the receiver. -// Lookup panics if the receiver is lazy. If foldCase is true, method names -// are considered equal if they are equal with case folding. -func (l *methodList) Lookup(pkg *Package, name string, foldCase bool) (int, *Func) { - assert(!l.isLazy()) - if l == nil { - return -1, nil - } - return lookupMethod(l.methods, pkg, name, foldCase) -} - -// Len returns the length of the method list. -func (l *methodList) Len() int { - if l == nil { - return 0 - } - return len(l.methods) -} - -// At returns the i'th method of the method list. At panics if i is out of -// bounds, or if the receiver is lazy and resolve is nil. -func (l *methodList) At(i int, resolve func() *Func) *Func { - if !l.isLazy() { - return l.methods[i] - } - assert(resolve != nil) - (*l.guards)[i].Do(func() { - l.methods[i] = resolve() - }) - return l.methods[i] -} diff --git a/src/cmd/compile/internal/types2/methodlist_test.go b/src/cmd/compile/internal/types2/methodlist_test.go deleted file mode 100644 index 7a183ac7f9..0000000000 --- a/src/cmd/compile/internal/types2/methodlist_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// 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 types2 - -import ( - "testing" -) - -func TestLazyMethodList(t *testing.T) { - l := newLazyMethodList(2) - - if got := l.Len(); got != 2 { - t.Fatalf("Len() = %d, want 2", got) - } - - f0 := NewFunc(nopos, nil, "f0", nil) - f1 := NewFunc(nopos, nil, "f1", nil) - - // Verify that methodList.At is idempotent, by calling it repeatedly with a - // resolve func that returns different pointer values (f0 or f1). - steps := []struct { - index int - resolve *Func // the *Func returned by the resolver - want *Func // the actual *Func returned by methodList.At - }{ - {0, f0, f0}, - {0, f1, f0}, - {1, f1, f1}, - {1, f0, f1}, - } - - for i, step := range steps { - got := l.At(step.index, func() *Func { return step.resolve }) - if got != step.want { - t.Errorf("step %d: At(%d, ...) = %s, want %s", i, step.index, got.Name(), step.want.Name()) - } - } -} diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index 0a2b2aa6b1..77655bc821 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -96,14 +96,16 @@ type Named struct { underlying Type // possibly a *Named during setup; never a *Named once set up completely tparams *TypeParamList // type parameters, or nil - // methods declared for this type (not the method set of this type). + // methods declared for this type (not the method set of this type) // Signatures are type-checked lazily. // For non-instantiated types, this is a fully populated list of methods. For - // instantiated types, this is a 'lazy' list, and methods are individually - // expanded when they are first accessed. - methods *methodList + // instantiated types, methods are individually expanded when they are first + // accessed. + methods []*Func + // number of expanded methods (only valid for instantiated named types) + expandedMethods int // expandedMethods <= len(orig.methods) - // loader may be provided to lazily load type parameters, underlying, and methods. + // loader may be provided to lazily load type parameters, underlying type, and methods. loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func) } @@ -112,7 +114,8 @@ type namedState uint32 const ( unresolved namedState = iota // tparams, underlying type and methods might be unavailable - resolved + resolved // resolve has run; methods might be incomplete (for instances) + complete // all data is known ) // NewNamed returns a new named type for the given type name, underlying type, and associated methods. @@ -122,7 +125,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { if _, ok := underlying.(*Named); ok { panic("underlying type must not be *Named") } - return (*Checker)(nil).newNamed(obj, nil, underlying, newMethodList(methods)) + return (*Checker)(nil).newNamed(obj, nil, underlying, methods) } // resolve resolves the type parameters, methods, and underlying type of n. @@ -156,8 +159,12 @@ func (n *Named) resolve(ctxt *Context) *Named { n.tparams = n.orig.tparams n.underlying = underlying n.fromRHS = n.orig.fromRHS // for cycle detection - n.methods = newLazyMethodList(n.orig.methods.Len()) - n.setState(resolved) + + if len(n.orig.methods) == 0 { + n.setState(complete) + } else { + n.setState(resolved) + } return n } @@ -170,17 +177,18 @@ func (n *Named) resolve(ctxt *Context) *Named { // also make the API more future-proof towards further extensions. if n.loader != nil { assert(n.underlying == nil) + assert(n.TypeArgs().Len() == 0) // instances are created by instantiation, in which case n.loader is nil tparams, underlying, methods := n.loader(n) n.tparams = bindTParams(tparams) n.underlying = underlying n.fromRHS = underlying // for cycle detection - n.methods = newMethodList(methods) + n.methods = methods n.loader = nil } - n.setState(resolved) + n.setState(complete) return n } @@ -196,7 +204,7 @@ func (n *Named) setState(state namedState) { } // newNamed is like NewNamed but with a *Checker receiver and additional orig argument. -func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods *methodList) *Named { +func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods []*Func) *Named { typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods} if typ.orig == nil { typ.orig = typ @@ -262,14 +270,38 @@ func (t *Named) TypeArgs() *TypeList { return t.targs } // For an ordinary or instantiated type t, the receiver base type of these // methods will be the named type t. For an uninstantiated generic type t, each // method receiver will be instantiated with its receiver type parameters. -func (t *Named) NumMethods() int { return t.resolve(nil).methods.Len() } +func (t *Named) NumMethods() int { return len(t.orig.resolve(nil).methods) } // Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). func (t *Named) Method(i int) *Func { t.resolve(nil) - return t.methods.At(i, func() *Func { - return t.expandMethod(i) - }) + + if t.state() >= complete { + return t.methods[i] + } + + assert(t.TypeArgs().Len() > 0) // only instances should have incomplete methods + + t.mu.Lock() + defer t.mu.Unlock() + + if len(t.methods) != len(t.orig.methods) { + assert(len(t.methods) == 0) + t.methods = make([]*Func, len(t.orig.methods)) + } + + if t.methods[i] == nil { + t.methods[i] = t.expandMethod(i) + t.expandedMethods++ + + // Check if we've created all methods at this point. If we have, mark the + // type as fully expanded. + if t.expandedMethods == len(t.orig.methods) { + t.setState(complete) + } + } + + return t.methods[i] } // expandMethod substitutes type arguments in the i'th method for an @@ -351,10 +383,9 @@ func (t *Named) SetUnderlying(underlying Type) { func (t *Named) AddMethod(m *Func) { assert(t.targs.Len() == 0) t.resolve(nil) - if t.methods == nil { - t.methods = newMethodList(nil) + if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 { + t.methods = append(t.methods, m) } - t.methods.Add(m) } func (t *Named) Underlying() Type { return t.resolve(nil).underlying } @@ -462,7 +493,7 @@ func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Fu // If n is an instance, we may not have yet instantiated all of its methods. // Look up the method index in orig, and only instantiate method at the // matching index (if any). - i, _ := n.orig.methods.Lookup(pkg, name, foldCase) + i, _ := lookupMethod(n.orig.methods, pkg, name, foldCase) if i < 0 { return -1, nil } diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go index 7ab7abb317..3f0bf8f3c5 100644 --- a/src/cmd/compile/internal/types2/sizeof_test.go +++ b/src/cmd/compile/internal/types2/sizeof_test.go @@ -31,7 +31,7 @@ func TestSizeof(t *testing.T) { {Interface{}, 40, 80}, {Map{}, 16, 32}, {Chan{}, 12, 24}, - {Named{}, 56, 104}, + {Named{}, 68, 128}, {TypeParam{}, 28, 48}, {term{}, 12, 24}, diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 61c9696948..b5ff1214dd 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -722,8 +722,8 @@ func (check *Checker) collectMethods(obj *TypeName) { // Checker.Files may be called multiple times; additional package files // may add methods to already type-checked types. Add pre-existing methods // so that we can detect redeclarations. - for i := 0; i < base.methods.Len(); i++ { - m := base.methods.At(i, nil) + for i := 0; i < base.NumMethods(); i++ { + m := base.Method(i) assert(m.name != "_") assert(mset.insert(m) == nil) } @@ -749,8 +749,8 @@ func (check *Checker) collectMethods(obj *TypeName) { func (check *Checker) checkFieldUniqueness(base *Named) { if t, _ := base.under().(*Struct); t != nil { var mset objset - for i := 0; i < base.methods.Len(); i++ { - m := base.methods.At(i, nil) + for i := 0; i < base.NumMethods(); i++ { + m := base.Method(i) assert(m.name != "_") assert(mset.insert(m) == nil) } diff --git a/src/go/types/methodlist.go b/src/go/types/methodlist.go deleted file mode 100644 index afe919013d..0000000000 --- a/src/go/types/methodlist.go +++ /dev/null @@ -1,79 +0,0 @@ -// 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 types - -import "sync" - -// methodList holds a list of methods that may be lazily resolved by a provided -// resolution method. -type methodList struct { - methods []*Func - - // guards synchronizes the instantiation of lazy methods. For lazy method - // lists, guards is non-nil and of the length passed to newLazyMethodList. - // For non-lazy method lists, guards is nil. - guards *[]sync.Once -} - -// newMethodList creates a non-lazy method list holding the given methods. -func newMethodList(methods []*Func) *methodList { - return &methodList{methods: methods} -} - -// newLazyMethodList creates a lazy method list of the given length. Methods -// may be resolved lazily for a given index by providing a resolver function. -func newLazyMethodList(length int) *methodList { - guards := make([]sync.Once, length) - return &methodList{ - methods: make([]*Func, length), - guards: &guards, - } -} - -// isLazy reports whether the receiver is a lazy method list. -func (l *methodList) isLazy() bool { - return l != nil && l.guards != nil -} - -// Add appends a method to the method list if not not already present. Add -// panics if the receiver is lazy. -func (l *methodList) Add(m *Func) { - assert(!l.isLazy()) - if i, _ := lookupMethod(l.methods, m.pkg, m.name, false); i < 0 { - l.methods = append(l.methods, m) - } -} - -// Lookup looks up the method identified by pkg and name in the receiver. -// Lookup panics if the receiver is lazy. If foldCase is true, method names -// are considered equal if they are equal with case folding. -func (l *methodList) Lookup(pkg *Package, name string, foldCase bool) (int, *Func) { - assert(!l.isLazy()) - if l == nil { - return -1, nil - } - return lookupMethod(l.methods, pkg, name, foldCase) -} - -// Len returns the length of the method list. -func (l *methodList) Len() int { - if l == nil { - return 0 - } - return len(l.methods) -} - -// At returns the i'th method of the method list. At panics if i is out of -// bounds, or if the receiver is lazy and resolve is nil. -func (l *methodList) At(i int, resolve func() *Func) *Func { - if !l.isLazy() { - return l.methods[i] - } - assert(resolve != nil) - (*l.guards)[i].Do(func() { - l.methods[i] = resolve() - }) - return l.methods[i] -} diff --git a/src/go/types/methodlist_test.go b/src/go/types/methodlist_test.go deleted file mode 100644 index e628bce767..0000000000 --- a/src/go/types/methodlist_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// 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 types - -import ( - "go/token" - "testing" -) - -func TestLazyMethodList(t *testing.T) { - l := newLazyMethodList(2) - - if got := l.Len(); got != 2 { - t.Fatalf("Len() = %d, want 2", got) - } - - f0 := NewFunc(token.NoPos, nil, "f0", nil) - f1 := NewFunc(token.NoPos, nil, "f1", nil) - - // Verify that methodList.At is idempotent, by calling it repeatedly with a - // resolve func that returns different pointer values (f0 or f1). - steps := []struct { - index int - resolve *Func // the *Func returned by the resolver - want *Func // the actual *Func returned by methodList.At - }{ - {0, f0, f0}, - {0, f1, f0}, - {1, f1, f1}, - {1, f0, f1}, - } - - for i, step := range steps { - got := l.At(step.index, func() *Func { return step.resolve }) - if got != step.want { - t.Errorf("step %d: At(%d, ...) = %s, want %s", i, step.index, got.Name(), step.want.Name()) - } - } -} diff --git a/src/go/types/named.go b/src/go/types/named.go index bfb4a11da7..f1c5dd4f81 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -96,14 +96,16 @@ type Named struct { underlying Type // possibly a *Named during setup; never a *Named once set up completely tparams *TypeParamList // type parameters, or nil - // methods declared for this type (not the method set of this type). + // methods declared for this type (not the method set of this type) // Signatures are type-checked lazily. // For non-instantiated types, this is a fully populated list of methods. For - // instantiated types, this is a 'lazy' list, and methods are individually - // expanded when they are first accessed. - methods *methodList + // instantiated types, methods are individually expanded when they are first + // accessed. + methods []*Func + // number of expanded methods (only valid for instantiated named types) + expandedMethods int // expandedMethods <= len(orig.methods) - // loader may be provided to lazily load type parameters, underlying, and methods. + // loader may be provided to lazily load type parameters, underlying type, and methods. loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func) } @@ -112,7 +114,8 @@ type namedState uint32 const ( unresolved namedState = iota // tparams, underlying type and methods might be unavailable - resolved + resolved // resolve has run; methods might be incomplete (for instances) + complete // all data is known ) // NewNamed returns a new named type for the given type name, underlying type, and associated methods. @@ -122,7 +125,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { if _, ok := underlying.(*Named); ok { panic("underlying type must not be *Named") } - return (*Checker)(nil).newNamed(obj, nil, underlying, newMethodList(methods)) + return (*Checker)(nil).newNamed(obj, nil, underlying, methods) } // resolve resolves the type parameters, methods, and underlying type of n. @@ -156,8 +159,12 @@ func (n *Named) resolve(ctxt *Context) *Named { n.tparams = n.orig.tparams n.underlying = underlying n.fromRHS = n.orig.fromRHS // for cycle detection - n.methods = newLazyMethodList(n.orig.methods.Len()) - n.setState(resolved) + + if len(n.orig.methods) == 0 { + n.setState(complete) + } else { + n.setState(resolved) + } return n } @@ -170,17 +177,18 @@ func (n *Named) resolve(ctxt *Context) *Named { // also make the API more future-proof towards further extensions. if n.loader != nil { assert(n.underlying == nil) + assert(n.TypeArgs().Len() == 0) // instances are created by instantiation, in which case n.loader is nil tparams, underlying, methods := n.loader(n) n.tparams = bindTParams(tparams) n.underlying = underlying n.fromRHS = underlying // for cycle detection - n.methods = newMethodList(methods) + n.methods = methods n.loader = nil } - n.setState(resolved) + n.setState(complete) return n } @@ -196,7 +204,7 @@ func (n *Named) setState(state namedState) { } // newNamed is like NewNamed but with a *Checker receiver and additional orig argument. -func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods *methodList) *Named { +func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods []*Func) *Named { typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods} if typ.orig == nil { typ.orig = typ @@ -264,14 +272,38 @@ func (t *Named) TypeArgs() *TypeList { return t.targs } // For an ordinary or instantiated type t, the receiver base type of these // methods will be the named type t. For an uninstantiated generic type t, each // method receiver will be instantiated with its receiver type parameters. -func (t *Named) NumMethods() int { return t.resolve(nil).methods.Len() } +func (t *Named) NumMethods() int { return len(t.orig.resolve(nil).methods) } // Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). func (t *Named) Method(i int) *Func { t.resolve(nil) - return t.methods.At(i, func() *Func { - return t.expandMethod(i) - }) + + if t.state() >= complete { + return t.methods[i] + } + + assert(t.TypeArgs().Len() > 0) // only instances should have incomplete methods + + t.mu.Lock() + defer t.mu.Unlock() + + if len(t.methods) != len(t.orig.methods) { + assert(len(t.methods) == 0) + t.methods = make([]*Func, len(t.orig.methods)) + } + + if t.methods[i] == nil { + t.methods[i] = t.expandMethod(i) + t.expandedMethods++ + + // Check if we've created all methods at this point. If we have, mark the + // type as fully expanded. + if t.expandedMethods == len(t.orig.methods) { + t.setState(complete) + } + } + + return t.methods[i] } // expandMethod substitutes type arguments in the i'th method for an @@ -353,10 +385,9 @@ func (t *Named) SetUnderlying(underlying Type) { func (t *Named) AddMethod(m *Func) { assert(t.targs.Len() == 0) t.resolve(nil) - if t.methods == nil { - t.methods = newMethodList(nil) + if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 { + t.methods = append(t.methods, m) } - t.methods.Add(m) } func (t *Named) Underlying() Type { return t.resolve(nil).underlying } @@ -464,7 +495,7 @@ func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Fu // If n is an instance, we may not have yet instantiated all of its methods. // Look up the method index in orig, and only instantiate method at the // matching index (if any). - i, _ := n.orig.methods.Lookup(pkg, name, foldCase) + i, _ := lookupMethod(n.orig.methods, pkg, name, foldCase) if i < 0 { return -1, nil } diff --git a/src/go/types/sizeof_test.go b/src/go/types/sizeof_test.go index 3428eb9191..66a69521d2 100644 --- a/src/go/types/sizeof_test.go +++ b/src/go/types/sizeof_test.go @@ -30,7 +30,7 @@ func TestSizeof(t *testing.T) { {Interface{}, 40, 80}, {Map{}, 16, 32}, {Chan{}, 12, 24}, - {Named{}, 56, 104}, + {Named{}, 68, 128}, {TypeParam{}, 28, 48}, {term{}, 12, 24}, From 02e69cfa9695f17902ff1806205c26a0d02a684f Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Sat, 7 May 2022 19:26:35 -0400 Subject: [PATCH 012/113] go/types, types2: store Named instance information separately Separate instance information into an instance struct, to reduce memory footprint for non-instance Named types. This may induce a sense of deja-vu: we had a similar construct in the past that was removed as unnecessary. With additional new fields being added that only apply to instances, having a separate struct makes sense again. Updates #52728 Change-Id: I0bb5982d71c27e6b574bfb4f7b886a6aeb9c5390 Reviewed-on: https://go-review.googlesource.com/c/go/+/404884 Reviewed-by: Robert Griesemer Run-TryBot: Robert Findley TryBot-Result: Gopher Robot --- src/cmd/compile/internal/types2/decl.go | 4 +- src/cmd/compile/internal/types2/infer.go | 2 +- .../compile/internal/types2/instantiate.go | 5 +- src/cmd/compile/internal/types2/named.go | 131 +++++++++++------- src/cmd/compile/internal/types2/predicates.go | 4 +- .../compile/internal/types2/sizeof_test.go | 2 +- src/cmd/compile/internal/types2/subst.go | 25 ++-- src/cmd/compile/internal/types2/typestring.go | 4 +- src/cmd/compile/internal/types2/typexpr.go | 6 +- src/cmd/compile/internal/types2/unify.go | 4 +- src/cmd/compile/internal/types2/validtype.go | 2 +- src/go/types/decl.go | 4 +- src/go/types/infer.go | 2 +- src/go/types/instantiate.go | 5 +- src/go/types/named.go | 129 ++++++++++------- src/go/types/predicates.go | 4 +- src/go/types/sizeof_test.go | 2 +- src/go/types/subst.go | 25 ++-- src/go/types/typestring.go | 4 +- src/go/types/typexpr.go | 6 +- src/go/types/unify.go | 4 +- src/go/types/validtype.go | 2 +- 22 files changed, 222 insertions(+), 154 deletions(-) diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index a5d29765c6..0bc5f9f3e1 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -508,7 +508,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named } // type definition or generic type declaration - named := check.newNamed(obj, nil, nil, nil) + named := check.newNamed(obj, nil, nil) def.setUnderlying(named) if tdecl.TParamList != nil { @@ -635,7 +635,7 @@ func (check *Checker) collectMethods(obj *TypeName) { // and field names must be distinct." base, _ := obj.typ.(*Named) // shouldn't fail but be conservative if base != nil { - assert(base.targs.Len() == 0) // collectMethods should not be called on an instantiated type + assert(base.TypeArgs().Len() == 0) // collectMethods should not be called on an instantiated type // See issue #52529: we must delay the expansion of underlying here, as // base may not be fully set-up. diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index 9e77d67a7d..8ab568be59 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go @@ -434,7 +434,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { return w.isParameterized(t.elem) case *Named: - return w.isParameterizedTypeList(t.targs.list()) + return w.isParameterizedTypeList(t.TypeArgs().list()) case *TypeParam: // t must be one of w.tparams diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index 44ed0319c8..ed6206e150 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -76,10 +76,7 @@ func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, ctxt *Co switch orig := orig.(type) { case *Named: - tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) - named := check.newNamed(tname, orig, nil, nil) // underlying, tparams, and methods are set when named is resolved - named.targs = newTypeList(targs) - res = named + res = check.newNamedInstance(pos, orig, targs) case *Signature: tparams := orig.TypeParams() diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index 77655bc821..133fb6fa88 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -5,6 +5,7 @@ package types2 import ( + "cmd/compile/internal/syntax" "sync" "sync/atomic" ) @@ -83,14 +84,15 @@ import ( type Named struct { check *Checker // non-nil during type-checking; nil otherwise obj *TypeName // corresponding declared object for declared types; see above for instantiated types - orig *Named // origin type for instantiated types, this type for declared types - targs *TypeList // type arguments (after instantiation), or nil // fromRHS holds the type (on RHS of declaration) this *Named type is derived // from (for cycle reporting). Only used by validType, and therefore does not // require synchronization. fromRHS Type + // information for instantiated types; nil otherwise + inst *instance + mu sync.Mutex // guards all fields below state_ uint32 // the current state of this type; must only be accessed atomically underlying Type // possibly a *Named during setup; never a *Named once set up completely @@ -102,13 +104,19 @@ type Named struct { // instantiated types, methods are individually expanded when they are first // accessed. methods []*Func - // number of expanded methods (only valid for instantiated named types) - expandedMethods int // expandedMethods <= len(orig.methods) // loader may be provided to lazily load type parameters, underlying type, and methods. loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func) } +// instance holds information that is only necessary for instantiated named +// types. +type instance struct { + orig *Named // original, uninstantiated type + targs *TypeList // type arguments + expandedMethods int // number of expanded methods; expandedMethods <= len(orig.methods) +} + // namedState represents the possible states that a named type may assume. type namedState uint32 @@ -125,7 +133,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { if _, ok := underlying.(*Named); ok { panic("underlying type must not be *Named") } - return (*Checker)(nil).newNamed(obj, nil, underlying, methods) + return (*Checker)(nil).newNamed(obj, underlying, methods) } // resolve resolves the type parameters, methods, and underlying type of n. @@ -149,19 +157,20 @@ func (n *Named) resolve(ctxt *Context) *Named { return n } - if n.TypeArgs().Len() > 0 { + if n.inst != nil { assert(n.underlying == nil) // n is an unresolved instance assert(n.loader == nil) // instances are created by instantiation, in which case n.loader is nil - n.orig.resolve(ctxt) + orig := n.inst.orig + orig.resolve(ctxt) underlying := n.expandUnderlying(ctxt) - n.tparams = n.orig.tparams + n.tparams = orig.tparams n.underlying = underlying - n.fromRHS = n.orig.fromRHS // for cycle detection + n.fromRHS = orig.fromRHS // for cycle detection - if len(n.orig.methods) == 0 { - n.setState(complete) + if len(orig.methods) == 0 { + n.setState(complete) // nothing further to do } else { n.setState(resolved) } @@ -204,11 +213,8 @@ func (n *Named) setState(state namedState) { } // newNamed is like NewNamed but with a *Checker receiver and additional orig argument. -func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods []*Func) *Named { - typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods} - if typ.orig == nil { - typ.orig = typ - } +func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) *Named { + typ := &Named{check: check, obj: obj, fromRHS: underlying, underlying: underlying, methods: methods} if obj.typ == nil { obj.typ = typ } @@ -219,8 +225,22 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, meth return typ } +func (check *Checker) newNamedInstance(pos syntax.Pos, orig *Named, targs []Type) *Named { + assert(len(targs) > 0) + + obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) + inst := &instance{orig: orig, targs: newTypeList(targs)} + typ := &Named{check: check, obj: obj, inst: inst} + obj.typ = typ + // Ensure that typ is always expanded and sanity-checked. + if check != nil { + check.needsCleanup(typ) + } + return typ +} + func (t *Named) cleanup() { - assert(t.orig.orig == t.orig) + assert(t.inst == nil || t.inst.orig.inst == nil) // Ensure that every defined type created in the course of type-checking has // either non-*Named underlying type, or is unexpanded. // @@ -242,14 +262,21 @@ func (t *Named) cleanup() { // Obj returns the type name for the declaration defining the named type t. For // instantiated types, this is same as the type name of the origin type. -func (t *Named) Obj() *TypeName { return t.orig.obj } // for non-instances this is the same as t.obj +func (t *Named) Obj() *TypeName { + if t.inst == nil { + return t.obj + } + return t.inst.orig.obj +} // Origin returns the generic type from which the named type t is // instantiated. If t is not an instantiated type, the result is t. -func (t *Named) Origin() *Named { return t.orig } - -// TODO(gri) Come up with a better representation and API to distinguish -// between parameterized instantiated and non-instantiated types. +func (t *Named) Origin() *Named { + if t.inst == nil { + return t + } + return t.inst.orig +} // TypeParams returns the type parameters of the named type t, or nil. // The result is non-nil for an (originally) generic type even if it is instantiated. @@ -258,19 +285,26 @@ func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams } // SetTypeParams sets the type parameters of the named type t. // t must not have type arguments. func (t *Named) SetTypeParams(tparams []*TypeParam) { - assert(t.targs.Len() == 0) + assert(t.inst == nil) t.resolve(nil).tparams = bindTParams(tparams) } // TypeArgs returns the type arguments used to instantiate the named type t. -func (t *Named) TypeArgs() *TypeList { return t.targs } +func (t *Named) TypeArgs() *TypeList { + if t.inst == nil { + return nil + } + return t.inst.targs +} // NumMethods returns the number of explicit methods defined for t. // // For an ordinary or instantiated type t, the receiver base type of these // methods will be the named type t. For an uninstantiated generic type t, each // method receiver will be instantiated with its receiver type parameters. -func (t *Named) NumMethods() int { return len(t.orig.resolve(nil).methods) } +func (t *Named) NumMethods() int { + return len(t.Origin().resolve(nil).methods) +} // Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). func (t *Named) Method(i int) *Func { @@ -280,23 +314,24 @@ func (t *Named) Method(i int) *Func { return t.methods[i] } - assert(t.TypeArgs().Len() > 0) // only instances should have incomplete methods + assert(t.inst != nil) // only instances should have incomplete methods + orig := t.inst.orig t.mu.Lock() defer t.mu.Unlock() - if len(t.methods) != len(t.orig.methods) { + if len(t.methods) != len(orig.methods) { assert(len(t.methods) == 0) - t.methods = make([]*Func, len(t.orig.methods)) + t.methods = make([]*Func, len(orig.methods)) } if t.methods[i] == nil { t.methods[i] = t.expandMethod(i) - t.expandedMethods++ + t.inst.expandedMethods++ // Check if we've created all methods at this point. If we have, mark the // type as fully expanded. - if t.expandedMethods == len(t.orig.methods) { + if t.inst.expandedMethods == len(orig.methods) { t.setState(complete) } } @@ -307,11 +342,9 @@ func (t *Named) Method(i int) *Func { // expandMethod substitutes type arguments in the i'th method for an // instantiated receiver. func (t *Named) expandMethod(i int) *Func { - assert(t.TypeArgs().Len() > 0) // t must be an instance - // t.orig.methods is not lazy. origm is the method instantiated with its // receiver type parameters (the "origin" method). - origm := t.orig.Method(i) + origm := t.inst.orig.Method(i) assert(origm != nil) check := t.check @@ -338,9 +371,9 @@ func (t *Named) expandMethod(i int) *Func { // We can only substitute if we have a correspondence between type arguments // and type parameters. This check is necessary in the presence of invalid // code. - if origSig.RecvTypeParams().Len() == t.targs.Len() { + if origSig.RecvTypeParams().Len() == t.inst.targs.Len() { ctxt := check.bestContext(nil) - smap := makeSubstMap(origSig.RecvTypeParams().list(), t.targs.list()) + smap := makeSubstMap(origSig.RecvTypeParams().list(), t.inst.targs.list()) sig = check.subst(origm.pos, origSig, smap, ctxt).(*Signature) } @@ -365,7 +398,7 @@ func (t *Named) expandMethod(i int) *Func { // SetUnderlying sets the underlying type and marks t as complete. // t must not have type arguments. func (t *Named) SetUnderlying(underlying Type) { - assert(t.targs.Len() == 0) + assert(t.inst == nil) if underlying == nil { panic("underlying type must not be nil") } @@ -381,7 +414,7 @@ func (t *Named) SetUnderlying(underlying Type) { // AddMethod adds method m unless it is already in the method list. // t must not have type arguments. func (t *Named) AddMethod(m *Func) { - assert(t.targs.Len() == 0) + assert(t.inst == nil) t.resolve(nil) if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 { t.methods = append(t.methods, m) @@ -493,7 +526,7 @@ func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Fu // If n is an instance, we may not have yet instantiated all of its methods. // Look up the method index in orig, and only instantiate method at the // matching index (if any). - i, _ := lookupMethod(n.orig.methods, pkg, name, foldCase) + i, _ := lookupMethod(n.Origin().methods, pkg, name, foldCase) if i < 0 { return -1, nil } @@ -531,36 +564,39 @@ func (n *Named) expandUnderlying(ctxt *Context) Type { }() } - assert(n.orig.underlying != nil) + assert(n.inst.orig.underlying != nil) - if _, unexpanded := n.orig.underlying.(*Named); unexpanded { + orig := n.inst.orig + targs := n.inst.targs + + if _, unexpanded := orig.underlying.(*Named); unexpanded { // We should only get a Named underlying type here during type checking // (for example, in recursive type declarations). assert(check != nil) } - if n.orig.tparams.Len() != n.targs.Len() { + if orig.tparams.Len() != targs.Len() { // Mismatching arg and tparam length may be checked elsewhere. return Typ[Invalid] } // We must always have a context, to avoid infinite recursion. ctxt = check.bestContext(ctxt) - h := ctxt.instanceHash(n.orig, n.targs.list()) + h := ctxt.instanceHash(orig, targs.list()) // ensure that an instance is recorded for h to avoid infinite recursion. - ctxt.update(h, n.orig, n.TypeArgs().list(), n) + ctxt.update(h, orig, targs.list(), n) - smap := makeSubstMap(n.orig.tparams.list(), n.targs.list()) - underlying := n.check.subst(n.obj.pos, n.orig.underlying, smap, ctxt) + smap := makeSubstMap(orig.tparams.list(), targs.list()) + underlying := n.check.subst(n.obj.pos, orig.underlying, smap, ctxt) // If the underlying type of n is an interface, we need to set the receiver // of its methods accurately -- we set the receiver of interface methods on // the RHS of a type declaration to the defined type. if iface, _ := underlying.(*Interface); iface != nil { - if methods, copied := replaceRecvType(iface.methods, n.orig, n); copied { + if methods, copied := replaceRecvType(iface.methods, orig, n); copied { // If the underlying type doesn't actually use type parameters, it's // possible that it wasn't substituted. In this case we need to create // a new *Interface before modifying receivers. - if iface == n.orig.underlying { + if iface == orig.underlying { old := iface iface = check.newInterface() iface.embeddeds = old.embeddeds @@ -571,6 +607,7 @@ func (n *Named) expandUnderlying(ctxt *Context) Type { iface.methods = methods } } + return underlying } diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go index 6bce26137e..6b6c21c780 100644 --- a/src/cmd/compile/internal/types2/predicates.go +++ b/src/cmd/compile/internal/types2/predicates.go @@ -102,7 +102,7 @@ func isTypeParam(t Type) bool { func isGeneric(t Type) bool { // A parameterized type is only generic if it doesn't have an instantiation already. named, _ := t.(*Named) - return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil + return named != nil && named.obj != nil && named.inst == nil && named.TypeParams().Len() > 0 } // Comparable reports whether values of type T are comparable. @@ -401,7 +401,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { if len(xargs) > 0 { // Instances are identical if their original type and type arguments // are identical. - if !Identical(x.orig, y.orig) { + if !Identical(x.Origin(), y.Origin()) { return false } for i, xa := range xargs { diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go index 3f0bf8f3c5..17876d1f3c 100644 --- a/src/cmd/compile/internal/types2/sizeof_test.go +++ b/src/cmd/compile/internal/types2/sizeof_test.go @@ -31,7 +31,7 @@ func TestSizeof(t *testing.T) { {Interface{}, 40, 80}, {Map{}, 16, 32}, {Chan{}, 12, 24}, - {Named{}, 68, 128}, + {Named{}, 60, 112}, {TypeParam{}, 28, 48}, {term{}, 12, 24}, diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index 6e41ebdf53..9af1a71cfe 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -176,7 +176,7 @@ func (subst *subster) typ(typ Type) Type { // In this case the interface will not be substituted here, because its // method signatures do not depend on the type parameter P, but we still // need to create new interface methods to hold the instantiated - // receiver. This is handled by expandNamed. + // receiver. This is handled by Named.expandUnderlying. iface.methods, _ = replaceRecvType(methods, t, iface) return iface } @@ -207,19 +207,20 @@ func (subst *subster) typ(typ Type) Type { } } - // subst is called by expandNamed, so in this function we need to be + // subst is called during expansion, so in this function we need to be // careful not to call any methods that would cause t to be expanded: doing // so would result in deadlock. // - // So we call t.orig.TypeParams() rather than t.TypeParams() here and - // below. - if t.orig.TypeParams().Len() == 0 { + // So we call t.Origin().TypeParams() rather than t.TypeParams(). + orig := t.Origin() + n := orig.TypeParams().Len() + if n == 0 { dump(">>> %s is not parameterized", t) return t // type is not parameterized } var newTArgs []Type - if t.targs.Len() != t.orig.TypeParams().Len() { + if t.TypeArgs().Len() != n { return Typ[Invalid] // error reported elsewhere } @@ -228,14 +229,14 @@ func (subst *subster) typ(typ Type) Type { // For each (existing) type argument targ, determine if it needs // to be substituted; i.e., if it is or contains a type parameter // that has a type argument for it. - for i, targ := range t.targs.list() { + for i, targ := range t.TypeArgs().list() { dump(">>> %d targ = %s", i, targ) new_targ := subst.typ(targ) if new_targ != targ { dump(">>> substituted %d targ %s => %s", i, targ, new_targ) if newTArgs == nil { - newTArgs = make([]Type, t.orig.TypeParams().Len()) - copy(newTArgs, t.targs.list()) + newTArgs = make([]Type, n) + copy(newTArgs, t.TypeArgs().list()) } newTArgs[i] = new_targ } @@ -247,9 +248,9 @@ func (subst *subster) typ(typ Type) Type { } // before creating a new named type, check if we have this one already - h := subst.ctxt.instanceHash(t.orig, newTArgs) + h := subst.ctxt.instanceHash(orig, newTArgs) dump(">>> new type hash: %s", h) - if named := subst.ctxt.lookup(h, t.orig, newTArgs); named != nil { + if named := subst.ctxt.lookup(h, orig, newTArgs); named != nil { dump(">>> found %s", named) return named } @@ -258,7 +259,7 @@ func (subst *subster) typ(typ Type) Type { // recursion. The position used here is irrelevant because validation only // occurs on t (we don't call validType on named), but we use subst.pos to // help with debugging. - return subst.check.instance(subst.pos, t.orig, newTArgs, subst.ctxt) + return subst.check.instance(subst.pos, orig, newTArgs, subst.ctxt) // Note that if we were to expose substitution more generally (not just in // the context of a declaration), we'd have to substitute in diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go index e0f36fcec4..c10c2d8973 100644 --- a/src/cmd/compile/internal/types2/typestring.go +++ b/src/cmd/compile/internal/types2/typestring.go @@ -284,9 +284,9 @@ func (w *typeWriter) typ(typ Type) { w.string(strconv.Itoa(w.ctxt.getID(t))) } w.typeName(t.obj) // when hashing written for readability of the hash only - if t.targs != nil { + if t.inst != nil { // instantiated type - w.typeList(t.targs.list()) + w.typeList(t.inst.targs.list()) } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams // parameterized type w.tParamList(t.TypeParams().list()) diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index 020653332d..ea13eb622d 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -444,8 +444,8 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def * // errors. check.recordInstance(x, inst.TypeArgs().list(), inst) - if check.validateTArgLen(x.Pos(), inst.TypeParams().Len(), inst.targs.Len()) { - if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.targs.list()); err != nil { + if check.validateTArgLen(x.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) { + if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list()); err != nil { // best position for error reporting pos := x.Pos() if i < len(xlist) { @@ -453,7 +453,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def * } check.softErrorf(pos, "%s", err) } else { - check.mono.recordInstance(check.pkg, x.Pos(), inst.TypeParams().list(), inst.targs.list(), xlist) + check.mono.recordInstance(check.pkg, x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), xlist) } } diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go index a7f68a05b1..7063789f3f 100644 --- a/src/cmd/compile/internal/types2/unify.go +++ b/src/cmd/compile/internal/types2/unify.go @@ -546,8 +546,8 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) { case *Named: // TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate. if y, ok := y.(*Named); ok { - xargs := x.targs.list() - yargs := y.targs.list() + xargs := x.TypeArgs().list() + yargs := y.TypeArgs().list() if len(xargs) != len(yargs) { return false diff --git a/src/cmd/compile/internal/types2/validtype.go b/src/cmd/compile/internal/types2/validtype.go index d495c6788e..b69120481b 100644 --- a/src/cmd/compile/internal/types2/validtype.go +++ b/src/cmd/compile/internal/types2/validtype.go @@ -72,7 +72,7 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeIn switch check.infoMap[t] { case unknown: check.infoMap[t] = marked - check.infoMap[t] = check.validType0(t.orig.fromRHS, env.push(t), append(path, t.obj)) + check.infoMap[t] = check.validType0(t.Origin().fromRHS, env.push(t), append(path, t.obj)) case marked: // We have seen type t before and thus must have a cycle. check.infoMap[t] = invalid diff --git a/src/go/types/decl.go b/src/go/types/decl.go index b5ff1214dd..c176042852 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -565,7 +565,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) { } // type definition or generic type declaration - named := check.newNamed(obj, nil, nil, nil) + named := check.newNamed(obj, nil, nil) def.setUnderlying(named) if tdecl.TypeParams != nil { @@ -711,7 +711,7 @@ func (check *Checker) collectMethods(obj *TypeName) { // and field names must be distinct." base, _ := obj.typ.(*Named) // shouldn't fail but be conservative if base != nil { - assert(base.targs.Len() == 0) // collectMethods should not be called on an instantiated type + assert(base.TypeArgs().Len() == 0) // collectMethods should not be called on an instantiated type // See issue #52529: we must delay the expansion of underlying here, as // base may not be fully set-up. diff --git a/src/go/types/infer.go b/src/go/types/infer.go index 031850b8da..ebe6d8ced7 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -434,7 +434,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { return w.isParameterized(t.elem) case *Named: - return w.isParameterizedTypeList(t.targs.list()) + return w.isParameterizedTypeList(t.TypeArgs().list()) case *TypeParam: // t must be one of w.tparams diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index 8be0eab407..d420a61572 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -76,10 +76,7 @@ func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, ctxt *Con switch orig := orig.(type) { case *Named: - tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) - named := check.newNamed(tname, orig, nil, nil) // underlying, tparams, and methods are set when named is resolved - named.targs = newTypeList(targs) - res = named + res = check.newNamedInstance(pos, orig, targs) case *Signature: tparams := orig.TypeParams() diff --git a/src/go/types/named.go b/src/go/types/named.go index f1c5dd4f81..71a26f96a1 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -5,6 +5,7 @@ package types import ( + "go/token" "sync" "sync/atomic" ) @@ -83,14 +84,15 @@ import ( type Named struct { check *Checker // non-nil during type-checking; nil otherwise obj *TypeName // corresponding declared object for declared types; see above for instantiated types - orig *Named // origin type for instantiated types, this type for declared types - targs *TypeList // type arguments (after instantiation), or nil // fromRHS holds the type (on RHS of declaration) this *Named type is derived // from (for cycle reporting). Only used by validType, and therefore does not // require synchronization. fromRHS Type + // information for instantiated types; nil otherwise + inst *instance + mu sync.Mutex // guards all fields below state_ uint32 // the current state of this type; must only be accessed atomically underlying Type // possibly a *Named during setup; never a *Named once set up completely @@ -102,13 +104,19 @@ type Named struct { // instantiated types, methods are individually expanded when they are first // accessed. methods []*Func - // number of expanded methods (only valid for instantiated named types) - expandedMethods int // expandedMethods <= len(orig.methods) // loader may be provided to lazily load type parameters, underlying type, and methods. loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func) } +// instance holds information that is only necessary for instantiated named +// types. +type instance struct { + orig *Named // original, uninstantiated type + targs *TypeList // type arguments + expandedMethods int // number of expanded methods; expandedMethods <= len(orig.methods) +} + // namedState represents the possible states that a named type may assume. type namedState uint32 @@ -125,7 +133,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { if _, ok := underlying.(*Named); ok { panic("underlying type must not be *Named") } - return (*Checker)(nil).newNamed(obj, nil, underlying, methods) + return (*Checker)(nil).newNamed(obj, underlying, methods) } // resolve resolves the type parameters, methods, and underlying type of n. @@ -149,19 +157,20 @@ func (n *Named) resolve(ctxt *Context) *Named { return n } - if n.TypeArgs().Len() > 0 { + if n.inst != nil { assert(n.underlying == nil) // n is an unresolved instance assert(n.loader == nil) // instances are created by instantiation, in which case n.loader is nil - n.orig.resolve(ctxt) + orig := n.inst.orig + orig.resolve(ctxt) underlying := n.expandUnderlying(ctxt) - n.tparams = n.orig.tparams + n.tparams = orig.tparams n.underlying = underlying - n.fromRHS = n.orig.fromRHS // for cycle detection + n.fromRHS = orig.fromRHS // for cycle detection - if len(n.orig.methods) == 0 { - n.setState(complete) + if len(orig.methods) == 0 { + n.setState(complete) // nothing further to do } else { n.setState(resolved) } @@ -204,11 +213,8 @@ func (n *Named) setState(state namedState) { } // newNamed is like NewNamed but with a *Checker receiver and additional orig argument. -func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods []*Func) *Named { - typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods} - if typ.orig == nil { - typ.orig = typ - } +func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) *Named { + typ := &Named{check: check, obj: obj, fromRHS: underlying, underlying: underlying, methods: methods} if obj.typ == nil { obj.typ = typ } @@ -219,8 +225,22 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, meth return typ } +func (check *Checker) newNamedInstance(pos token.Pos, orig *Named, targs []Type) *Named { + assert(len(targs) > 0) + + obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) + inst := &instance{orig: orig, targs: newTypeList(targs)} + typ := &Named{check: check, obj: obj, inst: inst} + obj.typ = typ + // Ensure that typ is always expanded and sanity-checked. + if check != nil { + check.needsCleanup(typ) + } + return typ +} + func (t *Named) cleanup() { - assert(t.orig.orig == t.orig) + assert(t.inst == nil || t.inst.orig.inst == nil) // Ensure that every defined type created in the course of type-checking has // either non-*Named underlying type, or is unexpanded. // @@ -243,15 +263,20 @@ func (t *Named) cleanup() { // Obj returns the type name for the declaration defining the named type t. For // instantiated types, this is same as the type name of the origin type. func (t *Named) Obj() *TypeName { - return t.orig.obj // for non-instances this is the same as t.obj + if t.inst == nil { + return t.obj + } + return t.inst.orig.obj } // Origin returns the generic type from which the named type t is // instantiated. If t is not an instantiated type, the result is t. -func (t *Named) Origin() *Named { return t.orig } - -// TODO(gri) Come up with a better representation and API to distinguish -// between parameterized instantiated and non-instantiated types. +func (t *Named) Origin() *Named { + if t.inst == nil { + return t + } + return t.inst.orig +} // TypeParams returns the type parameters of the named type t, or nil. // The result is non-nil for an (originally) generic type even if it is instantiated. @@ -260,19 +285,26 @@ func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams } // SetTypeParams sets the type parameters of the named type t. // t must not have type arguments. func (t *Named) SetTypeParams(tparams []*TypeParam) { - assert(t.targs.Len() == 0) + assert(t.inst == nil) t.resolve(nil).tparams = bindTParams(tparams) } // TypeArgs returns the type arguments used to instantiate the named type t. -func (t *Named) TypeArgs() *TypeList { return t.targs } +func (t *Named) TypeArgs() *TypeList { + if t.inst == nil { + return nil + } + return t.inst.targs +} // NumMethods returns the number of explicit methods defined for t. // // For an ordinary or instantiated type t, the receiver base type of these // methods will be the named type t. For an uninstantiated generic type t, each // method receiver will be instantiated with its receiver type parameters. -func (t *Named) NumMethods() int { return len(t.orig.resolve(nil).methods) } +func (t *Named) NumMethods() int { + return len(t.Origin().resolve(nil).methods) +} // Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). func (t *Named) Method(i int) *Func { @@ -282,23 +314,24 @@ func (t *Named) Method(i int) *Func { return t.methods[i] } - assert(t.TypeArgs().Len() > 0) // only instances should have incomplete methods + assert(t.inst != nil) // only instances should have incomplete methods + orig := t.inst.orig t.mu.Lock() defer t.mu.Unlock() - if len(t.methods) != len(t.orig.methods) { + if len(t.methods) != len(orig.methods) { assert(len(t.methods) == 0) - t.methods = make([]*Func, len(t.orig.methods)) + t.methods = make([]*Func, len(orig.methods)) } if t.methods[i] == nil { t.methods[i] = t.expandMethod(i) - t.expandedMethods++ + t.inst.expandedMethods++ // Check if we've created all methods at this point. If we have, mark the // type as fully expanded. - if t.expandedMethods == len(t.orig.methods) { + if t.inst.expandedMethods == len(orig.methods) { t.setState(complete) } } @@ -309,11 +342,9 @@ func (t *Named) Method(i int) *Func { // expandMethod substitutes type arguments in the i'th method for an // instantiated receiver. func (t *Named) expandMethod(i int) *Func { - assert(t.TypeArgs().Len() > 0) // t must be an instance - // t.orig.methods is not lazy. origm is the method instantiated with its // receiver type parameters (the "origin" method). - origm := t.orig.Method(i) + origm := t.inst.orig.Method(i) assert(origm != nil) check := t.check @@ -340,9 +371,9 @@ func (t *Named) expandMethod(i int) *Func { // We can only substitute if we have a correspondence between type arguments // and type parameters. This check is necessary in the presence of invalid // code. - if origSig.RecvTypeParams().Len() == t.targs.Len() { + if origSig.RecvTypeParams().Len() == t.inst.targs.Len() { ctxt := check.bestContext(nil) - smap := makeSubstMap(origSig.RecvTypeParams().list(), t.targs.list()) + smap := makeSubstMap(origSig.RecvTypeParams().list(), t.inst.targs.list()) sig = check.subst(origm.pos, origSig, smap, ctxt).(*Signature) } @@ -367,7 +398,7 @@ func (t *Named) expandMethod(i int) *Func { // SetUnderlying sets the underlying type and marks t as complete. // t must not have type arguments. func (t *Named) SetUnderlying(underlying Type) { - assert(t.targs.Len() == 0) + assert(t.inst == nil) if underlying == nil { panic("underlying type must not be nil") } @@ -383,7 +414,7 @@ func (t *Named) SetUnderlying(underlying Type) { // AddMethod adds method m unless it is already in the method list. // t must not have type arguments. func (t *Named) AddMethod(m *Func) { - assert(t.targs.Len() == 0) + assert(t.inst == nil) t.resolve(nil) if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 { t.methods = append(t.methods, m) @@ -495,7 +526,7 @@ func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Fu // If n is an instance, we may not have yet instantiated all of its methods. // Look up the method index in orig, and only instantiate method at the // matching index (if any). - i, _ := lookupMethod(n.orig.methods, pkg, name, foldCase) + i, _ := lookupMethod(n.Origin().methods, pkg, name, foldCase) if i < 0 { return -1, nil } @@ -533,36 +564,39 @@ func (n *Named) expandUnderlying(ctxt *Context) Type { }() } - assert(n.orig.underlying != nil) + assert(n.inst.orig.underlying != nil) - if _, unexpanded := n.orig.underlying.(*Named); unexpanded { + orig := n.inst.orig + targs := n.inst.targs + + if _, unexpanded := orig.underlying.(*Named); unexpanded { // We should only get a Named underlying type here during type checking // (for example, in recursive type declarations). assert(check != nil) } - if n.orig.tparams.Len() != n.targs.Len() { + if orig.tparams.Len() != targs.Len() { // Mismatching arg and tparam length may be checked elsewhere. return Typ[Invalid] } // We must always have a context, to avoid infinite recursion. ctxt = check.bestContext(ctxt) - h := ctxt.instanceHash(n.orig, n.targs.list()) + h := ctxt.instanceHash(orig, targs.list()) // ensure that an instance is recorded for h to avoid infinite recursion. - ctxt.update(h, n.orig, n.TypeArgs().list(), n) + ctxt.update(h, orig, targs.list(), n) - smap := makeSubstMap(n.orig.tparams.list(), n.targs.list()) - underlying := n.check.subst(n.obj.pos, n.orig.underlying, smap, ctxt) + smap := makeSubstMap(orig.tparams.list(), targs.list()) + underlying := n.check.subst(n.obj.pos, orig.underlying, smap, ctxt) // If the underlying type of n is an interface, we need to set the receiver // of its methods accurately -- we set the receiver of interface methods on // the RHS of a type declaration to the defined type. if iface, _ := underlying.(*Interface); iface != nil { - if methods, copied := replaceRecvType(iface.methods, n.orig, n); copied { + if methods, copied := replaceRecvType(iface.methods, orig, n); copied { // If the underlying type doesn't actually use type parameters, it's // possible that it wasn't substituted. In this case we need to create // a new *Interface before modifying receivers. - if iface == n.orig.underlying { + if iface == orig.underlying { old := iface iface = check.newInterface() iface.embeddeds = old.embeddeds @@ -573,6 +607,7 @@ func (n *Named) expandUnderlying(ctxt *Context) Type { iface.methods = methods } } + return underlying } diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 51d056f355..6e08b76e40 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -104,7 +104,7 @@ func isTypeParam(t Type) bool { func isGeneric(t Type) bool { // A parameterized type is only generic if it doesn't have an instantiation already. named, _ := t.(*Named) - return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil + return named != nil && named.obj != nil && named.inst == nil && named.TypeParams().Len() > 0 } // Comparable reports whether values of type T are comparable. @@ -403,7 +403,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { if len(xargs) > 0 { // Instances are identical if their original type and type arguments // are identical. - if !Identical(x.orig, y.orig) { + if !Identical(x.Origin(), y.Origin()) { return false } for i, xa := range xargs { diff --git a/src/go/types/sizeof_test.go b/src/go/types/sizeof_test.go index 66a69521d2..d4ce0a7fcd 100644 --- a/src/go/types/sizeof_test.go +++ b/src/go/types/sizeof_test.go @@ -30,7 +30,7 @@ func TestSizeof(t *testing.T) { {Interface{}, 40, 80}, {Map{}, 16, 32}, {Chan{}, 12, 24}, - {Named{}, 68, 128}, + {Named{}, 60, 112}, {TypeParam{}, 28, 48}, {term{}, 12, 24}, diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 63849b9212..110298cbae 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -176,7 +176,7 @@ func (subst *subster) typ(typ Type) Type { // In this case the interface will not be substituted here, because its // method signatures do not depend on the type parameter P, but we still // need to create new interface methods to hold the instantiated - // receiver. This is handled by expandNamed. + // receiver. This is handled by Named.expandUnderlying. iface.methods, _ = replaceRecvType(methods, t, iface) return iface } @@ -207,19 +207,20 @@ func (subst *subster) typ(typ Type) Type { } } - // subst is called by expandNamed, so in this function we need to be + // subst is called during expansion, so in this function we need to be // careful not to call any methods that would cause t to be expanded: doing // so would result in deadlock. // - // So we call t.orig.TypeParams() rather than t.TypeParams() here and - // below. - if t.orig.TypeParams().Len() == 0 { + // So we call t.Origin().TypeParams() rather than t.TypeParams(). + orig := t.Origin() + n := orig.TypeParams().Len() + if n == 0 { dump(">>> %s is not parameterized", t) return t // type is not parameterized } var newTArgs []Type - if t.targs.Len() != t.orig.TypeParams().Len() { + if t.TypeArgs().Len() != n { return Typ[Invalid] // error reported elsewhere } @@ -228,14 +229,14 @@ func (subst *subster) typ(typ Type) Type { // For each (existing) type argument targ, determine if it needs // to be substituted; i.e., if it is or contains a type parameter // that has a type argument for it. - for i, targ := range t.targs.list() { + for i, targ := range t.TypeArgs().list() { dump(">>> %d targ = %s", i, targ) new_targ := subst.typ(targ) if new_targ != targ { dump(">>> substituted %d targ %s => %s", i, targ, new_targ) if newTArgs == nil { - newTArgs = make([]Type, t.orig.TypeParams().Len()) - copy(newTArgs, t.targs.list()) + newTArgs = make([]Type, n) + copy(newTArgs, t.TypeArgs().list()) } newTArgs[i] = new_targ } @@ -247,9 +248,9 @@ func (subst *subster) typ(typ Type) Type { } // before creating a new named type, check if we have this one already - h := subst.ctxt.instanceHash(t.orig, newTArgs) + h := subst.ctxt.instanceHash(orig, newTArgs) dump(">>> new type hash: %s", h) - if named := subst.ctxt.lookup(h, t.orig, newTArgs); named != nil { + if named := subst.ctxt.lookup(h, orig, newTArgs); named != nil { dump(">>> found %s", named) return named } @@ -258,7 +259,7 @@ func (subst *subster) typ(typ Type) Type { // recursion. The position used here is irrelevant because validation only // occurs on t (we don't call validType on named), but we use subst.pos to // help with debugging. - return subst.check.instance(subst.pos, t.orig, newTArgs, subst.ctxt) + return subst.check.instance(subst.pos, orig, newTArgs, subst.ctxt) // Note that if we were to expose substitution more generally (not just in // the context of a declaration), we'd have to substitute in diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go index 0325d4a77f..5a2e2c171a 100644 --- a/src/go/types/typestring.go +++ b/src/go/types/typestring.go @@ -285,9 +285,9 @@ func (w *typeWriter) typ(typ Type) { w.string(strconv.Itoa(w.ctxt.getID(t))) } w.typeName(t.obj) // when hashing written for readability of the hash only - if t.targs != nil { + if t.inst != nil { // instantiated type - w.typeList(t.targs.list()) + w.typeList(t.inst.targs.list()) } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams // parameterized type w.tParamList(t.TypeParams().list()) diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index c7161e00a5..05bd51a82b 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -428,8 +428,8 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re // errors. check.recordInstance(ix.Orig, inst.TypeArgs().list(), inst) - if check.validateTArgLen(ix.Pos(), inst.TypeParams().Len(), inst.targs.Len()) { - if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.targs.list()); err != nil { + if check.validateTArgLen(ix.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) { + if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list()); err != nil { // best position for error reporting pos := ix.Pos() if i < len(ix.Indices) { @@ -437,7 +437,7 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re } check.softErrorf(atPos(pos), _InvalidTypeArg, err.Error()) } else { - check.mono.recordInstance(check.pkg, ix.Pos(), inst.TypeParams().list(), inst.targs.list(), ix.Indices) + check.mono.recordInstance(check.pkg, ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), ix.Indices) } } diff --git a/src/go/types/unify.go b/src/go/types/unify.go index 0742e40d8b..602e304b4a 100644 --- a/src/go/types/unify.go +++ b/src/go/types/unify.go @@ -546,8 +546,8 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) { case *Named: // TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate. if y, ok := y.(*Named); ok { - xargs := x.targs.list() - yargs := y.targs.list() + xargs := x.TypeArgs().list() + yargs := y.TypeArgs().list() if len(xargs) != len(yargs) { return false diff --git a/src/go/types/validtype.go b/src/go/types/validtype.go index edb4c02ecd..0d7a0f308c 100644 --- a/src/go/types/validtype.go +++ b/src/go/types/validtype.go @@ -71,7 +71,7 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeIn switch check.infoMap[t] { case unknown: check.infoMap[t] = marked - check.infoMap[t] = check.validType0(t.orig.fromRHS, env.push(t), append(path, t.obj)) + check.infoMap[t] = check.validType0(t.Origin().fromRHS, env.push(t), append(path, t.obj)) case marked: // We have seen type t before and thus must have a cycle. check.infoMap[t] = invalid From 47e34ca533b118bd47061b15fc7918563f4837a7 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Sat, 7 May 2022 21:22:17 -0400 Subject: [PATCH 013/113] go/types, types2: ensure that named types never expand infinitely During type-checking, newly created instances share a type checking Context which de-duplicates identical instances. However, when unexpanded types escape the type-checking pass or are created via calls to Instantiate, they lack this shared context. As reported in #52728, this may lead to infinitely many identical but distinct types that are reachable via the API. This CL introduces a new invariant that ensures we don't create such infinitely expanding chains: instances created during expansion share a context with the type that led to their creation. During expansion, the expanding type passes its Context to any newly created instances. This ensures that cycles will eventually terminate with a previously seen instance. For example, if we have an instantiation chain T1[P]->T2[P]->T3[P]->T1[P], by virtue of this Context passing the expansion of T3[P] will find the instantiation T1[P]. In general, storing a Context in a Named type could lead to pinning types in memory unnecessarily, but in this case the Context pins only those types that are reachable from the original instance. This seems like a reasonable compromise between lazy and eager expansion. Our treatment of Context was a little haphazard: Checker.bestContext made it easy to get a context at any point, but made it harder to reason about which context is being used. To fix this, replace bestContext with Checker.context, which returns the type-checking context and panics on a nil receiver. Update all call-sites to verify that the Checker is non-nil when context is called. Also make it a panic to call subst with a nil context. Instead, update subst to explicitly accept a local (=instance) context along with a global context, and require that one of them is non-nil. Thread this through to the call to Checker.instance, and handle context updating there. Fixes #52728 Change-Id: Ib7f26eb8c406290325bc3212fda25421a37a1e8e Reviewed-on: https://go-review.googlesource.com/c/go/+/404885 Reviewed-by: Robert Griesemer TryBot-Result: Gopher Robot Run-TryBot: Robert Findley --- src/cmd/compile/internal/types2/call.go | 6 +- src/cmd/compile/internal/types2/infer.go | 10 +- .../compile/internal/types2/instantiate.go | 83 +++++++++----- src/cmd/compile/internal/types2/named.go | 102 ++++++++++-------- src/cmd/compile/internal/types2/named_test.go | 45 ++++++++ src/cmd/compile/internal/types2/predicates.go | 9 +- src/cmd/compile/internal/types2/signature.go | 2 +- src/cmd/compile/internal/types2/subst.go | 37 +++---- src/cmd/compile/internal/types2/typexpr.go | 5 +- src/go/types/call.go | 6 +- src/go/types/infer.go | 10 +- src/go/types/instantiate.go | 83 +++++++++----- src/go/types/named.go | 102 ++++++++++-------- src/go/types/named_test.go | 48 +++++++++ src/go/types/predicates.go | 9 +- src/go/types/signature.go | 2 +- src/go/types/subst.go | 37 +++---- src/go/types/typexpr.go | 5 +- 18 files changed, 389 insertions(+), 212 deletions(-) diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index 3ade147dfe..b1ea6917fb 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -72,13 +72,13 @@ func (check *Checker) instantiateSignature(pos syntax.Pos, typ *Signature, targs }() } - inst := check.instance(pos, typ, targs, check.bestContext(nil)).(*Signature) + inst := check.instance(pos, typ, targs, nil, check.context()).(*Signature) assert(len(xlist) <= len(targs)) // verify instantiation lazily (was issue #50450) check.later(func() { tparams := typ.TypeParams().list() - if i, err := check.verify(pos, tparams, targs); err != nil { + if i, err := check.verify(pos, tparams, targs, check.context()); err != nil { // best position for error reporting pos := pos if i < len(xlist) { @@ -395,7 +395,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T // need to compute it from the adjusted list; otherwise we can // simply use the result signature's parameter list. if adjusted { - sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TypeParams().list(), targs), nil).(*Tuple) + sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TypeParams().list(), targs), nil, check.context()).(*Tuple) } else { sigParams = rsig.params } diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index 8ab568be59..b0c6a4fcea 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go @@ -110,11 +110,11 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type, renameMap := makeRenameMap(tparams, tparams2) for i, tparam := range tparams { - tparams2[i].bound = check.subst(pos, tparam.bound, renameMap, nil) + tparams2[i].bound = check.subst(pos, tparam.bound, renameMap, nil, check.context()) } tparams = tparams2 - params = check.subst(pos, params, renameMap, nil).(*Tuple) + params = check.subst(pos, params, renameMap, nil, check.context()).(*Tuple) } } @@ -188,7 +188,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type, // but that doesn't impact the isParameterized check for now). if params.Len() > 0 { smap := makeSubstMap(tparams, targs) - params = check.subst(nopos, params, smap, nil).(*Tuple) + params = check.subst(nopos, params, smap, nil, check.context()).(*Tuple) } // Unify parameter and argument types for generic parameters with typed arguments @@ -224,7 +224,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type, } } smap := makeSubstMap(tparams, targs) - inferred := check.subst(arg.Pos(), tpar, smap, nil) + inferred := check.subst(arg.Pos(), tpar, smap, nil, check.context()) if inferred != tpar { check.errorf(arg, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar) } else { @@ -626,7 +626,7 @@ func (check *Checker) inferB(pos syntax.Pos, tparams []*TypeParam, targs []Type) n := 0 for _, index := range dirty { t0 := types[index] - if t1 := check.subst(nopos, t0, smap, nil); t1 != t0 { + if t1 := check.subst(nopos, t0, smap, nil, check.context()); t1 != t0 { types[index] = t1 dirty[n] = index n++ diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index ed6206e150..f338e28d2e 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -40,6 +40,9 @@ import ( // count is incorrect; for *Named types, a panic may occur later inside the // *Named API. func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) { + if ctxt == nil { + ctxt = NewContext() + } if validate { var tparams []*TypeParam switch t := orig.(type) { @@ -51,34 +54,71 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e if len(targs) != len(tparams) { return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams)) } - if i, err := (*Checker)(nil).verify(nopos, tparams, targs); err != nil { + if i, err := (*Checker)(nil).verify(nopos, tparams, targs, ctxt); err != nil { return nil, &ArgumentError{i, err} } } - inst := (*Checker)(nil).instance(nopos, orig, targs, ctxt) + inst := (*Checker)(nil).instance(nopos, orig, targs, nil, ctxt) return inst, nil } -// instance creates a type or function instance using the given original type -// typ and arguments targs. For Named types the resulting instance will be -// unexpanded. check may be nil. -func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, ctxt *Context) (res Type) { - var h string - if ctxt != nil { - h = ctxt.instanceHash(orig, targs) - // typ may already have been instantiated with identical type arguments. In - // that case, re-use the existing instance. - if inst := ctxt.lookup(h, orig, targs); inst != nil { - return inst +// instance resolves a type or function instance for the given original type +// and type arguments. It looks for an existing identical instance in the given +// contexts, creating a new instance if none is found. +// +// If local is non-nil, it is the context associated with a Named instance +// type currently being expanded. If global is non-nil, it is the context +// associated with the current type-checking pass or call to Instantiate. At +// least one of local or global must be non-nil. +// +// For Named types the resulting instance may be unexpanded. +func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, local, global *Context) (res Type) { + // The order of the contexts below matters: we always prefer instances in + // local in order to preserve reference cycles. + // + // Invariant: if local != nil, the returned instance will be the instance + // recorded in local. + var ctxts []*Context + if local != nil { + ctxts = append(ctxts, local) + } + if global != nil { + ctxts = append(ctxts, global) + } + assert(len(ctxts) > 0) + + // Compute all hashes; hashes may differ across contexts due to different + // unique IDs for Named types within the hasher. + hashes := make([]string, len(ctxts)) + for i, ctxt := range ctxts { + hashes[i] = ctxt.instanceHash(orig, targs) + } + + // If local is non-nil, updateContexts return the type recorded in + // local. + updateContexts := func(res Type) Type { + for i := len(ctxts) - 1; i >= 0; i-- { + res = ctxts[i].update(hashes[i], orig, targs, res) + } + return res + } + + // typ may already have been instantiated with identical type arguments. In + // that case, re-use the existing instance. + for i, ctxt := range ctxts { + if inst := ctxt.lookup(hashes[i], orig, targs); inst != nil { + return updateContexts(inst) } } switch orig := orig.(type) { case *Named: - res = check.newNamedInstance(pos, orig, targs) + res = check.newNamedInstance(pos, orig, targs, local) // substituted lazily case *Signature: + assert(local == nil) // function instances cannot be reached from Named types + tparams := orig.TypeParams() if !check.validateTArgLen(pos, tparams.Len(), len(targs)) { return Typ[Invalid] @@ -86,7 +126,7 @@ func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, ctxt *Co if tparams.Len() == 0 { return orig // nothing to do (minor optimization) } - sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), ctxt).(*Signature) + sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, global).(*Signature) // If the signature doesn't use its type parameters, subst // will not make a copy. In that case, make a copy now (so // we can set tparams to nil w/o causing side-effects). @@ -104,13 +144,8 @@ func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, ctxt *Co panic(fmt.Sprintf("%v: cannot instantiate %v", pos, orig)) } - if ctxt != nil { - // It's possible that we've lost a race to add named to the context. - // In this case, use whichever instance is recorded in the context. - res = ctxt.update(h, orig, targs, res) - } - - return res + // Update all contexts; it's possible that we've lost a race. + return updateContexts(res) } // validateTArgLen verifies that the length of targs and tparams matches, @@ -128,7 +163,7 @@ func (check *Checker) validateTArgLen(pos syntax.Pos, ntparams, ntargs int) bool return true } -func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type) (int, error) { +func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type, ctxt *Context) (int, error) { smap := makeSubstMap(tparams, targs) for i, tpar := range tparams { // Ensure that we have a (possibly implicit) interface as type bound (issue #51048). @@ -137,7 +172,7 @@ func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type) // as the instantiated type; before we can use it for bounds checking we // need to instantiate it with the type arguments with which we instantiated // the parameterized type. - bound := check.subst(pos, tpar.bound, smap, nil) + bound := check.subst(pos, tpar.bound, smap, nil, ctxt) if err := check.implements(targs[i], bound); err != nil { return i, err } diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index 133fb6fa88..720e500cd5 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -79,6 +79,16 @@ import ( // Identical to compare them. For instantiated named types, their obj is a // synthetic placeholder that records their position of the corresponding // instantiation in the source (if they were constructed during type checking). +// +// To prevent infinite expansion of named instances that are created outside of +// type-checking, instances share a Context with other instances created during +// their expansion. Via the pidgeonhole principle, this guarantees that in the +// presence of a cycle of named types, expansion will eventually find an +// existing instance in the Context and short-circuit the expansion. +// +// Once an instance is complete, we can nil out this shared Context to unpin +// memory, though this Context may still be held by other incomplete instances +// in its "lineage". // A Named represents a named (defined) type. type Named struct { @@ -115,6 +125,7 @@ type instance struct { orig *Named // original, uninstantiated type targs *TypeList // type arguments expandedMethods int // number of expanded methods; expandedMethods <= len(orig.methods) + ctxt *Context // local Context; set to nil after full expansion } // namedState represents the possible states that a named type may assume. @@ -143,7 +154,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { // After resolution, the type parameters, methods, and underlying type of n are // accessible; but if n is an instantiated type, its methods may still be // unexpanded. -func (n *Named) resolve(ctxt *Context) *Named { +func (n *Named) resolve() *Named { if n.state() >= resolved { // avoid locking below return n } @@ -162,8 +173,8 @@ func (n *Named) resolve(ctxt *Context) *Named { assert(n.loader == nil) // instances are created by instantiation, in which case n.loader is nil orig := n.inst.orig - orig.resolve(ctxt) - underlying := n.expandUnderlying(ctxt) + orig.resolve() + underlying := n.expandUnderlying() n.tparams = orig.tparams n.underlying = underlying @@ -171,6 +182,7 @@ func (n *Named) resolve(ctxt *Context) *Named { if len(orig.methods) == 0 { n.setState(complete) // nothing further to do + n.inst.ctxt = nil } else { n.setState(resolved) } @@ -225,11 +237,11 @@ func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) return typ } -func (check *Checker) newNamedInstance(pos syntax.Pos, orig *Named, targs []Type) *Named { +func (check *Checker) newNamedInstance(pos syntax.Pos, orig *Named, targs []Type, local *Context) *Named { assert(len(targs) > 0) obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) - inst := &instance{orig: orig, targs: newTypeList(targs)} + inst := &instance{orig: orig, targs: newTypeList(targs), ctxt: local} typ := &Named{check: check, obj: obj, inst: inst} obj.typ = typ // Ensure that typ is always expanded and sanity-checked. @@ -280,13 +292,13 @@ func (t *Named) Origin() *Named { // TypeParams returns the type parameters of the named type t, or nil. // The result is non-nil for an (originally) generic type even if it is instantiated. -func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams } +func (t *Named) TypeParams() *TypeParamList { return t.resolve().tparams } // SetTypeParams sets the type parameters of the named type t. // t must not have type arguments. func (t *Named) SetTypeParams(tparams []*TypeParam) { assert(t.inst == nil) - t.resolve(nil).tparams = bindTParams(tparams) + t.resolve().tparams = bindTParams(tparams) } // TypeArgs returns the type arguments used to instantiate the named type t. @@ -298,17 +310,17 @@ func (t *Named) TypeArgs() *TypeList { } // NumMethods returns the number of explicit methods defined for t. -// -// For an ordinary or instantiated type t, the receiver base type of these -// methods will be the named type t. For an uninstantiated generic type t, each -// method receiver will be instantiated with its receiver type parameters. func (t *Named) NumMethods() int { - return len(t.Origin().resolve(nil).methods) + return len(t.Origin().resolve().methods) } // Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). +// +// For an ordinary or instantiated type t, the receiver base type of this +// method is the named type t. For an uninstantiated generic type t, each +// method receiver is instantiated with its receiver type parameters. func (t *Named) Method(i int) *Func { - t.resolve(nil) + t.resolve() if t.state() >= complete { return t.methods[i] @@ -326,6 +338,7 @@ func (t *Named) Method(i int) *Func { } if t.methods[i] == nil { + assert(t.inst.ctxt != nil) // we should still have a context remaining from the resolution phase t.methods[i] = t.expandMethod(i) t.inst.expandedMethods++ @@ -333,6 +346,7 @@ func (t *Named) Method(i int) *Func { // type as fully expanded. if t.inst.expandedMethods == len(orig.methods) { t.setState(complete) + t.inst.ctxt = nil // no need for a context anymore } } @@ -372,9 +386,12 @@ func (t *Named) expandMethod(i int) *Func { // and type parameters. This check is necessary in the presence of invalid // code. if origSig.RecvTypeParams().Len() == t.inst.targs.Len() { - ctxt := check.bestContext(nil) smap := makeSubstMap(origSig.RecvTypeParams().list(), t.inst.targs.list()) - sig = check.subst(origm.pos, origSig, smap, ctxt).(*Signature) + var global *Context + if check != nil { + global = check.context() + } + sig = check.subst(origm.pos, origSig, smap, t.inst.ctxt, global).(*Signature) } if sig == origSig { @@ -405,7 +422,7 @@ func (t *Named) SetUnderlying(underlying Type) { if _, ok := underlying.(*Named); ok { panic("underlying type must not be *Named") } - t.resolve(nil).underlying = underlying + t.resolve().underlying = underlying if t.fromRHS == nil { t.fromRHS = underlying // for cycle detection } @@ -415,17 +432,20 @@ func (t *Named) SetUnderlying(underlying Type) { // t must not have type arguments. func (t *Named) AddMethod(m *Func) { assert(t.inst == nil) - t.resolve(nil) + t.resolve() if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 { t.methods = append(t.methods, m) } } -func (t *Named) Underlying() Type { return t.resolve(nil).underlying } +func (t *Named) Underlying() Type { return t.resolve().underlying } func (t *Named) String() string { return TypeString(t, nil) } // ---------------------------------------------------------------------------- // Implementation +// +// TODO(rfindley): reorganize the loading and expansion methods under this +// heading. // under returns the expanded underlying type of n0; possibly by following // forward chains of named types. If an underlying type is found, resolve @@ -522,7 +542,7 @@ func (n *Named) setUnderlying(typ Type) { } func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Func) { - n.resolve(nil) + n.resolve() // If n is an instance, we may not have yet instantiated all of its methods. // Look up the method index in orig, and only instantiate method at the // matching index (if any). @@ -534,26 +554,17 @@ func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Fu return i, n.Method(i) } -// bestContext returns the best available context. In order of preference: -// - the given ctxt, if non-nil -// - check.ctxt, if check is non-nil -// - a new Context -func (check *Checker) bestContext(ctxt *Context) *Context { - if ctxt != nil { - return ctxt +// context returns the type-checker context. +func (check *Checker) context() *Context { + if check.ctxt == nil { + check.ctxt = NewContext() } - if check != nil { - if check.ctxt == nil { - check.ctxt = NewContext() - } - return check.ctxt - } - return NewContext() + return check.ctxt } // expandUnderlying substitutes type arguments in the underlying type n.orig, // returning the result. Returns Typ[Invalid] if there was an error. -func (n *Named) expandUnderlying(ctxt *Context) Type { +func (n *Named) expandUnderlying() Type { check := n.check if check != nil && check.conf.Trace { check.trace(n.obj.pos, "-- Named.expandUnderlying %s", n) @@ -565,6 +576,9 @@ func (n *Named) expandUnderlying(ctxt *Context) Type { } assert(n.inst.orig.underlying != nil) + if n.inst.ctxt == nil { + n.inst.ctxt = NewContext() + } orig := n.inst.orig targs := n.inst.targs @@ -580,16 +594,20 @@ func (n *Named) expandUnderlying(ctxt *Context) Type { return Typ[Invalid] } - // We must always have a context, to avoid infinite recursion. - ctxt = check.bestContext(ctxt) - h := ctxt.instanceHash(orig, targs.list()) - // ensure that an instance is recorded for h to avoid infinite recursion. - ctxt.update(h, orig, targs.list(), n) + // Ensure that an instance is recorded before substituting, so that we + // resolve n for any recursive references. + h := n.inst.ctxt.instanceHash(orig, targs.list()) + n2 := n.inst.ctxt.update(h, orig, n.TypeArgs().list(), n) + assert(n == n2) smap := makeSubstMap(orig.tparams.list(), targs.list()) - underlying := n.check.subst(n.obj.pos, orig.underlying, smap, ctxt) - // If the underlying type of n is an interface, we need to set the receiver - // of its methods accurately -- we set the receiver of interface methods on + var global *Context + if check != nil { + global = check.context() + } + underlying := n.check.subst(n.obj.pos, orig.underlying, smap, n.inst.ctxt, global) + // If the underlying type of n is an interface, we need to set the receiver of + // its methods accurately -- we set the receiver of interface methods on // the RHS of a type declaration to the defined type. if iface, _ := underlying.(*Interface); iface != nil { if methods, copied := replaceRecvType(iface.methods, orig, n); copied { diff --git a/src/cmd/compile/internal/types2/named_test.go b/src/cmd/compile/internal/types2/named_test.go index 14a982048a..e5e8eddb05 100644 --- a/src/cmd/compile/internal/types2/named_test.go +++ b/src/cmd/compile/internal/types2/named_test.go @@ -7,6 +7,7 @@ package types2_test import ( "testing" + "cmd/compile/internal/syntax" . "cmd/compile/internal/types2" ) @@ -73,3 +74,47 @@ func mustInstantiate(tb testing.TB, orig Type, targs ...Type) Type { } return inst } + +// Test that types do not expand infinitely, as in golang/go#52715. +func TestFiniteTypeExpansion(t *testing.T) { + const src = ` +package p + +type Tree[T any] struct { + *Node[T] +} + +func (*Tree[R]) N(r R) R { return r } + +type Node[T any] struct { + *Tree[T] +} + +func (Node[Q]) M(Q) {} + +type Inst = *Tree[int] +` + + f, err := parseSrc("foo.go", src) + if err != nil { + t.Fatal(err) + } + pkg := NewPackage("p", f.PkgName.Value) + if err := NewChecker(nil, pkg, nil).Files([]*syntax.File{f}); err != nil { + t.Fatal(err) + } + + firstFieldType := func(n *Named) *Named { + return n.Underlying().(*Struct).Field(0).Type().(*Pointer).Elem().(*Named) + } + + Inst := pkg.Scope().Lookup("Inst").Type().(*Pointer).Elem().(*Named) + Node := firstFieldType(Inst) + Tree := firstFieldType(Node) + if !Identical(Inst, Tree) { + t.Fatalf("Not a cycle: got %v, want %v", Tree, Inst) + } + if Inst != Tree { + t.Errorf("Duplicate instances in cycle: %s (%p) -> %s (%p) -> %s (%p)", Inst, Inst, Node, Node, Tree, Tree) + } +} diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go index 6b6c21c780..f7b5b16204 100644 --- a/src/cmd/compile/internal/types2/predicates.go +++ b/src/cmd/compile/internal/types2/predicates.go @@ -283,18 +283,19 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { } smap := makeSubstMap(ytparams, targs) - var check *Checker // ok to call subst on a nil *Checker + var check *Checker // ok to call subst on a nil *Checker + ctxt := NewContext() // need a non-nil Context for the substitution below // Constraints must be pair-wise identical, after substitution. for i, xtparam := range xtparams { - ybound := check.subst(nopos, ytparams[i].bound, smap, nil) + ybound := check.subst(nopos, ytparams[i].bound, smap, nil, ctxt) if !identical(xtparam.bound, ybound, cmpTags, p) { return false } } - yparams = check.subst(nopos, y.params, smap, nil).(*Tuple) - yresults = check.subst(nopos, y.results, smap, nil).(*Tuple) + yparams = check.subst(nopos, y.params, smap, nil, ctxt).(*Tuple) + yresults = check.subst(nopos, y.results, smap, nil, ctxt).(*Tuple) } return x.variadic == y.variadic && diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index 92d3aadf88..1b61b368d2 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -143,7 +143,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] // recvTPar.bound is (possibly) parameterized in the context of the // receiver type declaration. Substitute parameters for the current // context. - tpar.bound = check.subst(tpar.obj.pos, recvTPar.bound, smap, nil) + tpar.bound = check.subst(tpar.obj.pos, recvTPar.bound, smap, nil, check.context()) } } else if len(tparams) < len(recvTParams) { // Reporting an error here is a stop-gap measure to avoid crashes in the diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index 9af1a71cfe..4a4c8f960a 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -49,7 +49,9 @@ func (m substMap) lookup(tpar *TypeParam) Type { // from the incoming type. // // If the given context is non-nil, it is used in lieu of check.Config.Context. -func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, ctxt *Context) Type { +func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, local, global *Context) Type { + assert(local != nil || global != nil) + if smap.empty() { return typ } @@ -64,19 +66,20 @@ func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, ctxt *Conte // general case subst := subster{ - pos: pos, - smap: smap, - check: check, - ctxt: check.bestContext(ctxt), + pos: pos, + smap: smap, + check: check, + local: local, + global: global, } return subst.typ(typ) } type subster struct { - pos syntax.Pos - smap substMap - check *Checker // nil if called via Instantiate - ctxt *Context + pos syntax.Pos + smap substMap + check *Checker // nil if called via Instantiate + local, global *Context } func (subst *subster) typ(typ Type) Type { @@ -247,25 +250,11 @@ func (subst *subster) typ(typ Type) Type { return t // nothing to substitute } - // before creating a new named type, check if we have this one already - h := subst.ctxt.instanceHash(orig, newTArgs) - dump(">>> new type hash: %s", h) - if named := subst.ctxt.lookup(h, orig, newTArgs); named != nil { - dump(">>> found %s", named) - return named - } - // Create a new instance and populate the context to avoid endless // recursion. The position used here is irrelevant because validation only // occurs on t (we don't call validType on named), but we use subst.pos to // help with debugging. - return subst.check.instance(subst.pos, orig, newTArgs, subst.ctxt) - - // Note that if we were to expose substitution more generally (not just in - // the context of a declaration), we'd have to substitute in - // named.underlying as well. - // - // But this is unnecessary for now. + return subst.check.instance(subst.pos, orig, newTArgs, subst.local, subst.global) case *TypeParam: return subst.smap.lookup(t) diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index ea13eb622d..f0cd236050 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -433,8 +433,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def * } // create the instance - ctxt := check.bestContext(nil) - inst := check.instance(x.Pos(), orig, targs, ctxt).(*Named) + inst := check.instance(x.Pos(), orig, targs, nil, check.context()).(*Named) def.setUnderlying(inst) // orig.tparams may not be set up, so we need to do expansion later. @@ -445,7 +444,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def * check.recordInstance(x, inst.TypeArgs().list(), inst) if check.validateTArgLen(x.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) { - if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list()); err != nil { + if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), check.context()); err != nil { // best position for error reporting pos := x.Pos() if i < len(xlist) { diff --git a/src/go/types/call.go b/src/go/types/call.go index 3c7c3226f6..c580885a5a 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -73,13 +73,13 @@ func (check *Checker) instantiateSignature(pos token.Pos, typ *Signature, targs }() } - inst := check.instance(pos, typ, targs, check.bestContext(nil)).(*Signature) + inst := check.instance(pos, typ, targs, nil, check.context()).(*Signature) assert(len(xlist) <= len(targs)) // verify instantiation lazily (was issue #50450) check.later(func() { tparams := typ.TypeParams().list() - if i, err := check.verify(pos, tparams, targs); err != nil { + if i, err := check.verify(pos, tparams, targs, check.context()); err != nil { // best position for error reporting pos := pos if i < len(xlist) { @@ -400,7 +400,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type // need to compute it from the adjusted list; otherwise we can // simply use the result signature's parameter list. if adjusted { - sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TypeParams().list(), targs), nil).(*Tuple) + sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TypeParams().list(), targs), nil, check.context()).(*Tuple) } else { sigParams = rsig.params } diff --git a/src/go/types/infer.go b/src/go/types/infer.go index ebe6d8ced7..1aa2612638 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -110,11 +110,11 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, renameMap := makeRenameMap(tparams, tparams2) for i, tparam := range tparams { - tparams2[i].bound = check.subst(posn.Pos(), tparam.bound, renameMap, nil) + tparams2[i].bound = check.subst(posn.Pos(), tparam.bound, renameMap, nil, check.context()) } tparams = tparams2 - params = check.subst(posn.Pos(), params, renameMap, nil).(*Tuple) + params = check.subst(posn.Pos(), params, renameMap, nil, check.context()).(*Tuple) } } @@ -188,7 +188,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, // but that doesn't impact the isParameterized check for now). if params.Len() > 0 { smap := makeSubstMap(tparams, targs) - params = check.subst(token.NoPos, params, smap, nil).(*Tuple) + params = check.subst(token.NoPos, params, smap, nil, check.context()).(*Tuple) } // Unify parameter and argument types for generic parameters with typed arguments @@ -225,7 +225,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, } smap := makeSubstMap(tparams, targs) // TODO(rFindley): pass a positioner here, rather than arg.Pos(). - inferred := check.subst(arg.Pos(), tpar, smap, nil) + inferred := check.subst(arg.Pos(), tpar, smap, nil, check.context()) // _CannotInferTypeArgs indicates a failure of inference, though the actual // error may be better attributed to a user-provided type argument (hence // _InvalidTypeArg). We can't differentiate these cases, so fall back on @@ -626,7 +626,7 @@ func (check *Checker) inferB(posn positioner, tparams []*TypeParam, targs []Type n := 0 for _, index := range dirty { t0 := types[index] - if t1 := check.subst(token.NoPos, t0, smap, nil); t1 != t0 { + if t1 := check.subst(token.NoPos, t0, smap, nil, check.context()); t1 != t0 { types[index] = t1 dirty[n] = index n++ diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index d420a61572..6091b0b381 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -40,6 +40,9 @@ import ( // count is incorrect; for *Named types, a panic may occur later inside the // *Named API. func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) { + if ctxt == nil { + ctxt = NewContext() + } if validate { var tparams []*TypeParam switch t := orig.(type) { @@ -51,34 +54,71 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e if len(targs) != len(tparams) { return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams)) } - if i, err := (*Checker)(nil).verify(token.NoPos, tparams, targs); err != nil { + if i, err := (*Checker)(nil).verify(token.NoPos, tparams, targs, ctxt); err != nil { return nil, &ArgumentError{i, err} } } - inst := (*Checker)(nil).instance(token.NoPos, orig, targs, ctxt) + inst := (*Checker)(nil).instance(token.NoPos, orig, targs, nil, ctxt) return inst, nil } -// instance creates a type or function instance using the given original type -// typ and arguments targs. For Named types the resulting instance will be -// unexpanded. check may be nil. -func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, ctxt *Context) (res Type) { - var h string - if ctxt != nil { - h = ctxt.instanceHash(orig, targs) - // typ may already have been instantiated with identical type arguments. In - // that case, re-use the existing instance. - if inst := ctxt.lookup(h, orig, targs); inst != nil { - return inst +// instance resolves a type or function instance for the given original type +// and type arguments. It looks for an existing identical instance in the given +// contexts, creating a new instance if none is found. +// +// If local is non-nil, it is the context associated with a Named instance +// type currently being expanded. If global is non-nil, it is the context +// associated with the current type-checking pass or call to Instantiate. At +// least one of local or global must be non-nil. +// +// For Named types the resulting instance may be unexpanded. +func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, local, global *Context) (res Type) { + // The order of the contexts below matters: we always prefer instances in + // local in order to preserve reference cycles. + // + // Invariant: if local != nil, the returned instance will be the instance + // recorded in local. + var ctxts []*Context + if local != nil { + ctxts = append(ctxts, local) + } + if global != nil { + ctxts = append(ctxts, global) + } + assert(len(ctxts) > 0) + + // Compute all hashes; hashes may differ across contexts due to different + // unique IDs for Named types within the hasher. + hashes := make([]string, len(ctxts)) + for i, ctxt := range ctxts { + hashes[i] = ctxt.instanceHash(orig, targs) + } + + // If local is non-nil, updateContexts return the type recorded in + // local. + updateContexts := func(res Type) Type { + for i := len(ctxts) - 1; i >= 0; i-- { + res = ctxts[i].update(hashes[i], orig, targs, res) + } + return res + } + + // typ may already have been instantiated with identical type arguments. In + // that case, re-use the existing instance. + for i, ctxt := range ctxts { + if inst := ctxt.lookup(hashes[i], orig, targs); inst != nil { + return updateContexts(inst) } } switch orig := orig.(type) { case *Named: - res = check.newNamedInstance(pos, orig, targs) + res = check.newNamedInstance(pos, orig, targs, local) // substituted lazily case *Signature: + assert(local == nil) // function instances cannot be reached from Named types + tparams := orig.TypeParams() if !check.validateTArgLen(pos, tparams.Len(), len(targs)) { return Typ[Invalid] @@ -86,7 +126,7 @@ func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, ctxt *Con if tparams.Len() == 0 { return orig // nothing to do (minor optimization) } - sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), ctxt).(*Signature) + sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, global).(*Signature) // If the signature doesn't use its type parameters, subst // will not make a copy. In that case, make a copy now (so // we can set tparams to nil w/o causing side-effects). @@ -104,13 +144,8 @@ func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, ctxt *Con panic(fmt.Sprintf("%v: cannot instantiate %v", pos, orig)) } - if ctxt != nil { - // It's possible that we've lost a race to add named to the context. - // In this case, use whichever instance is recorded in the context. - res = ctxt.update(h, orig, targs, res) - } - - return res + // Update all contexts; it's possible that we've lost a race. + return updateContexts(res) } // validateTArgLen verifies that the length of targs and tparams matches, @@ -128,7 +163,7 @@ func (check *Checker) validateTArgLen(pos token.Pos, ntparams, ntargs int) bool return true } -func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type) (int, error) { +func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type, ctxt *Context) (int, error) { smap := makeSubstMap(tparams, targs) for i, tpar := range tparams { // Ensure that we have a (possibly implicit) interface as type bound (issue #51048). @@ -137,7 +172,7 @@ func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type) // as the instantiated type; before we can use it for bounds checking we // need to instantiate it with the type arguments with which we instantiated // the parameterized type. - bound := check.subst(pos, tpar.bound, smap, nil) + bound := check.subst(pos, tpar.bound, smap, nil, ctxt) if err := check.implements(targs[i], bound); err != nil { return i, err } diff --git a/src/go/types/named.go b/src/go/types/named.go index 71a26f96a1..63f0a22323 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -79,6 +79,16 @@ import ( // Identical to compare them. For instantiated named types, their obj is a // synthetic placeholder that records their position of the corresponding // instantiation in the source (if they were constructed during type checking). +// +// To prevent infinite expansion of named instances that are created outside of +// type-checking, instances share a Context with other instances created during +// their expansion. Via the pidgeonhole principle, this guarantees that in the +// presence of a cycle of named types, expansion will eventually find an +// existing instance in the Context and short-circuit the expansion. +// +// Once an instance is complete, we can nil out this shared Context to unpin +// memory, though this Context may still be held by other incomplete instances +// in its "lineage". // A Named represents a named (defined) type. type Named struct { @@ -115,6 +125,7 @@ type instance struct { orig *Named // original, uninstantiated type targs *TypeList // type arguments expandedMethods int // number of expanded methods; expandedMethods <= len(orig.methods) + ctxt *Context // local Context; set to nil after full expansion } // namedState represents the possible states that a named type may assume. @@ -143,7 +154,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { // After resolution, the type parameters, methods, and underlying type of n are // accessible; but if n is an instantiated type, its methods may still be // unexpanded. -func (n *Named) resolve(ctxt *Context) *Named { +func (n *Named) resolve() *Named { if n.state() >= resolved { // avoid locking below return n } @@ -162,8 +173,8 @@ func (n *Named) resolve(ctxt *Context) *Named { assert(n.loader == nil) // instances are created by instantiation, in which case n.loader is nil orig := n.inst.orig - orig.resolve(ctxt) - underlying := n.expandUnderlying(ctxt) + orig.resolve() + underlying := n.expandUnderlying() n.tparams = orig.tparams n.underlying = underlying @@ -171,6 +182,7 @@ func (n *Named) resolve(ctxt *Context) *Named { if len(orig.methods) == 0 { n.setState(complete) // nothing further to do + n.inst.ctxt = nil } else { n.setState(resolved) } @@ -225,11 +237,11 @@ func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) return typ } -func (check *Checker) newNamedInstance(pos token.Pos, orig *Named, targs []Type) *Named { +func (check *Checker) newNamedInstance(pos token.Pos, orig *Named, targs []Type, local *Context) *Named { assert(len(targs) > 0) obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) - inst := &instance{orig: orig, targs: newTypeList(targs)} + inst := &instance{orig: orig, targs: newTypeList(targs), ctxt: local} typ := &Named{check: check, obj: obj, inst: inst} obj.typ = typ // Ensure that typ is always expanded and sanity-checked. @@ -280,13 +292,13 @@ func (t *Named) Origin() *Named { // TypeParams returns the type parameters of the named type t, or nil. // The result is non-nil for an (originally) generic type even if it is instantiated. -func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams } +func (t *Named) TypeParams() *TypeParamList { return t.resolve().tparams } // SetTypeParams sets the type parameters of the named type t. // t must not have type arguments. func (t *Named) SetTypeParams(tparams []*TypeParam) { assert(t.inst == nil) - t.resolve(nil).tparams = bindTParams(tparams) + t.resolve().tparams = bindTParams(tparams) } // TypeArgs returns the type arguments used to instantiate the named type t. @@ -298,17 +310,17 @@ func (t *Named) TypeArgs() *TypeList { } // NumMethods returns the number of explicit methods defined for t. -// -// For an ordinary or instantiated type t, the receiver base type of these -// methods will be the named type t. For an uninstantiated generic type t, each -// method receiver will be instantiated with its receiver type parameters. func (t *Named) NumMethods() int { - return len(t.Origin().resolve(nil).methods) + return len(t.Origin().resolve().methods) } // Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). +// +// For an ordinary or instantiated type t, the receiver base type of this +// method is the named type t. For an uninstantiated generic type t, each +// method receiver is instantiated with its receiver type parameters. func (t *Named) Method(i int) *Func { - t.resolve(nil) + t.resolve() if t.state() >= complete { return t.methods[i] @@ -326,6 +338,7 @@ func (t *Named) Method(i int) *Func { } if t.methods[i] == nil { + assert(t.inst.ctxt != nil) // we should still have a context remaining from the resolution phase t.methods[i] = t.expandMethod(i) t.inst.expandedMethods++ @@ -333,6 +346,7 @@ func (t *Named) Method(i int) *Func { // type as fully expanded. if t.inst.expandedMethods == len(orig.methods) { t.setState(complete) + t.inst.ctxt = nil // no need for a context anymore } } @@ -372,9 +386,12 @@ func (t *Named) expandMethod(i int) *Func { // and type parameters. This check is necessary in the presence of invalid // code. if origSig.RecvTypeParams().Len() == t.inst.targs.Len() { - ctxt := check.bestContext(nil) smap := makeSubstMap(origSig.RecvTypeParams().list(), t.inst.targs.list()) - sig = check.subst(origm.pos, origSig, smap, ctxt).(*Signature) + var global *Context + if check != nil { + global = check.context() + } + sig = check.subst(origm.pos, origSig, smap, t.inst.ctxt, global).(*Signature) } if sig == origSig { @@ -405,7 +422,7 @@ func (t *Named) SetUnderlying(underlying Type) { if _, ok := underlying.(*Named); ok { panic("underlying type must not be *Named") } - t.resolve(nil).underlying = underlying + t.resolve().underlying = underlying if t.fromRHS == nil { t.fromRHS = underlying // for cycle detection } @@ -415,17 +432,20 @@ func (t *Named) SetUnderlying(underlying Type) { // t must not have type arguments. func (t *Named) AddMethod(m *Func) { assert(t.inst == nil) - t.resolve(nil) + t.resolve() if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 { t.methods = append(t.methods, m) } } -func (t *Named) Underlying() Type { return t.resolve(nil).underlying } +func (t *Named) Underlying() Type { return t.resolve().underlying } func (t *Named) String() string { return TypeString(t, nil) } // ---------------------------------------------------------------------------- // Implementation +// +// TODO(rfindley): reorganize the loading and expansion methods under this +// heading. // under returns the expanded underlying type of n0; possibly by following // forward chains of named types. If an underlying type is found, resolve @@ -522,7 +542,7 @@ func (n *Named) setUnderlying(typ Type) { } func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Func) { - n.resolve(nil) + n.resolve() // If n is an instance, we may not have yet instantiated all of its methods. // Look up the method index in orig, and only instantiate method at the // matching index (if any). @@ -534,26 +554,17 @@ func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Fu return i, n.Method(i) } -// bestContext returns the best available context. In order of preference: -// - the given ctxt, if non-nil -// - check.ctxt, if check is non-nil -// - a new Context -func (check *Checker) bestContext(ctxt *Context) *Context { - if ctxt != nil { - return ctxt +// context returns the type-checker context. +func (check *Checker) context() *Context { + if check.ctxt == nil { + check.ctxt = NewContext() } - if check != nil { - if check.ctxt == nil { - check.ctxt = NewContext() - } - return check.ctxt - } - return NewContext() + return check.ctxt } // expandUnderlying substitutes type arguments in the underlying type n.orig, // returning the result. Returns Typ[Invalid] if there was an error. -func (n *Named) expandUnderlying(ctxt *Context) Type { +func (n *Named) expandUnderlying() Type { check := n.check if check != nil && trace { check.trace(n.obj.pos, "-- Named.expandUnderlying %s", n) @@ -565,6 +576,9 @@ func (n *Named) expandUnderlying(ctxt *Context) Type { } assert(n.inst.orig.underlying != nil) + if n.inst.ctxt == nil { + n.inst.ctxt = NewContext() + } orig := n.inst.orig targs := n.inst.targs @@ -580,16 +594,20 @@ func (n *Named) expandUnderlying(ctxt *Context) Type { return Typ[Invalid] } - // We must always have a context, to avoid infinite recursion. - ctxt = check.bestContext(ctxt) - h := ctxt.instanceHash(orig, targs.list()) - // ensure that an instance is recorded for h to avoid infinite recursion. - ctxt.update(h, orig, targs.list(), n) + // Ensure that an instance is recorded before substituting, so that we + // resolve n for any recursive references. + h := n.inst.ctxt.instanceHash(orig, targs.list()) + n2 := n.inst.ctxt.update(h, orig, n.TypeArgs().list(), n) + assert(n == n2) smap := makeSubstMap(orig.tparams.list(), targs.list()) - underlying := n.check.subst(n.obj.pos, orig.underlying, smap, ctxt) - // If the underlying type of n is an interface, we need to set the receiver - // of its methods accurately -- we set the receiver of interface methods on + var global *Context + if check != nil { + global = check.context() + } + underlying := n.check.subst(n.obj.pos, orig.underlying, smap, n.inst.ctxt, global) + // If the underlying type of n is an interface, we need to set the receiver of + // its methods accurately -- we set the receiver of interface methods on // the RHS of a type declaration to the defined type. if iface, _ := underlying.(*Interface); iface != nil { if methods, copied := replaceRecvType(iface.methods, orig, n); copied { diff --git a/src/go/types/named_test.go b/src/go/types/named_test.go index 74cdb48889..0fe17418f4 100644 --- a/src/go/types/named_test.go +++ b/src/go/types/named_test.go @@ -5,6 +5,9 @@ package types_test import ( + "go/ast" + "go/parser" + "go/token" "testing" . "go/types" @@ -86,3 +89,48 @@ func mustInstantiate(tb testing.TB, orig Type, targs ...Type) Type { } return inst } + +// Test that types do not expand infinitely, as in golang/go#52715. +func TestFiniteTypeExpansion(t *testing.T) { + const src = ` +package p + +type Tree[T any] struct { + *Node[T] +} + +func (*Tree[R]) N(r R) R { return r } + +type Node[T any] struct { + *Tree[T] +} + +func (Node[Q]) M(Q) {} + +type Inst = *Tree[int] +` + + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "foo.go", src, 0) + if err != nil { + t.Fatal(err) + } + pkg := NewPackage("p", f.Name.Name) + if err := NewChecker(nil, fset, pkg, nil).Files([]*ast.File{f}); err != nil { + t.Fatal(err) + } + + firstFieldType := func(n *Named) *Named { + return n.Underlying().(*Struct).Field(0).Type().(*Pointer).Elem().(*Named) + } + + Inst := pkg.Scope().Lookup("Inst").Type().(*Pointer).Elem().(*Named) + Node := firstFieldType(Inst) + Tree := firstFieldType(Node) + if !Identical(Inst, Tree) { + t.Fatalf("Not a cycle: got %v, want %v", Tree, Inst) + } + if Inst != Tree { + t.Errorf("Duplicate instances in cycle: %s (%p) -> %s (%p) -> %s (%p)", Inst, Inst, Node, Node, Tree, Tree) + } +} diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 6e08b76e40..25db4acf4a 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -285,18 +285,19 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { } smap := makeSubstMap(ytparams, targs) - var check *Checker // ok to call subst on a nil *Checker + var check *Checker // ok to call subst on a nil *Checker + ctxt := NewContext() // need a non-nil Context for the substitution below // Constraints must be pair-wise identical, after substitution. for i, xtparam := range xtparams { - ybound := check.subst(token.NoPos, ytparams[i].bound, smap, nil) + ybound := check.subst(token.NoPos, ytparams[i].bound, smap, nil, ctxt) if !identical(xtparam.bound, ybound, cmpTags, p) { return false } } - yparams = check.subst(token.NoPos, y.params, smap, nil).(*Tuple) - yresults = check.subst(token.NoPos, y.results, smap, nil).(*Tuple) + yparams = check.subst(token.NoPos, y.params, smap, nil, ctxt).(*Tuple) + yresults = check.subst(token.NoPos, y.results, smap, nil, ctxt).(*Tuple) } return x.variadic == y.variadic && diff --git a/src/go/types/signature.go b/src/go/types/signature.go index 4b63f0e6f0..82177a1c58 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -150,7 +150,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast // recvTPar.bound is (possibly) parameterized in the context of the // receiver type declaration. Substitute parameters for the current // context. - tpar.bound = check.subst(tpar.obj.pos, recvTPar.bound, smap, nil) + tpar.bound = check.subst(tpar.obj.pos, recvTPar.bound, smap, nil, check.context()) } } else if len(tparams) < len(recvTParams) { // Reporting an error here is a stop-gap measure to avoid crashes in the diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 110298cbae..36987a4c95 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -49,7 +49,9 @@ func (m substMap) lookup(tpar *TypeParam) Type { // result type is different from the incoming type. // // If the given context is non-nil, it is used in lieu of check.Config.Context -func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, ctxt *Context) Type { +func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, local, global *Context) Type { + assert(local != nil || global != nil) + if smap.empty() { return typ } @@ -64,19 +66,20 @@ func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, ctxt *Contex // general case subst := subster{ - pos: pos, - smap: smap, - check: check, - ctxt: check.bestContext(ctxt), + pos: pos, + smap: smap, + check: check, + local: local, + global: global, } return subst.typ(typ) } type subster struct { - pos token.Pos - smap substMap - check *Checker // nil if called via Instantiate - ctxt *Context + pos token.Pos + smap substMap + check *Checker // nil if called via Instantiate + local, global *Context } func (subst *subster) typ(typ Type) Type { @@ -247,25 +250,11 @@ func (subst *subster) typ(typ Type) Type { return t // nothing to substitute } - // before creating a new named type, check if we have this one already - h := subst.ctxt.instanceHash(orig, newTArgs) - dump(">>> new type hash: %s", h) - if named := subst.ctxt.lookup(h, orig, newTArgs); named != nil { - dump(">>> found %s", named) - return named - } - // Create a new instance and populate the context to avoid endless // recursion. The position used here is irrelevant because validation only // occurs on t (we don't call validType on named), but we use subst.pos to // help with debugging. - return subst.check.instance(subst.pos, orig, newTArgs, subst.ctxt) - - // Note that if we were to expose substitution more generally (not just in - // the context of a declaration), we'd have to substitute in - // named.underlying as well. - // - // But this is unnecessary for now. + return subst.check.instance(subst.pos, orig, newTArgs, subst.local, subst.global) case *TypeParam: return subst.smap.lookup(t) diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 05bd51a82b..a881d33654 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -417,8 +417,7 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re } // create the instance - ctxt := check.bestContext(nil) - inst := check.instance(ix.Pos(), orig, targs, ctxt).(*Named) + inst := check.instance(ix.Pos(), orig, targs, nil, check.context()).(*Named) def.setUnderlying(inst) // orig.tparams may not be set up, so we need to do expansion later. @@ -429,7 +428,7 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re check.recordInstance(ix.Orig, inst.TypeArgs().list(), inst) if check.validateTArgLen(ix.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) { - if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list()); err != nil { + if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), check.context()); err != nil { // best position for error reporting pos := ix.Pos() if i < len(ix.Indices) { From 9ce28b518d9a792d2e3e741bcb38fa046891906e Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Sat, 4 Jun 2022 09:43:40 -0700 Subject: [PATCH 014/113] text/template/parse: fix data race on lexer initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this change, `startParse` would write `lex.breakOK` and `lex.continueOK` when the lexer goroutine is already running, which is a potential race condition. Makes `breakOK` and `continueOK` configuration flags passed when `lexer` is created, similarly to how `emitComment` works. Fixes #53234 Change-Id: Ia65f6135509a758cd4c5a453b249a174f4fb3e21 Reviewed-on: https://go-review.googlesource.com/c/go/+/410414 Reviewed-by: Eli Bendersky Reviewed-by: Daniel Martí TryBot-Result: Gopher Robot Reviewed-by: Rob Pike Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor --- src/text/template/parse/lex.go | 4 +++- src/text/template/parse/lex_test.go | 4 ++-- src/text/template/parse/parse.go | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go index 4c32d261f2..29403dd947 100644 --- a/src/text/template/parse/lex.go +++ b/src/text/template/parse/lex.go @@ -211,7 +211,7 @@ func (l *lexer) drain() { } // lex creates a new scanner for the input string. -func lex(name, input, left, right string, emitComment bool) *lexer { +func lex(name, input, left, right string, emitComment, breakOK, continueOK bool) *lexer { if left == "" { left = leftDelim } @@ -224,6 +224,8 @@ func lex(name, input, left, right string, emitComment bool) *lexer { leftDelim: left, rightDelim: right, emitComment: emitComment, + breakOK: breakOK, + continueOK: continueOK, items: make(chan item), line: 1, startLine: 1, diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go index fcb7e8eacd..c5f429667c 100644 --- a/src/text/template/parse/lex_test.go +++ b/src/text/template/parse/lex_test.go @@ -394,7 +394,7 @@ var lexTests = []lexTest{ // collect gathers the emitted items into a slice. func collect(t *lexTest, left, right string) (items []item) { - l := lex(t.name, t.input, left, right, true) + l := lex(t.name, t.input, left, right, true, true, true) for { item := l.nextItem() items = append(items, item) @@ -550,7 +550,7 @@ func TestPos(t *testing.T) { func TestShutdown(t *testing.T) { // We need to duplicate template.Parse here to hold on to the lexer. const text = "erroneous{{define}}{{else}}1234" - lexer := lex("foo", text, "{{", "}}", false) + lexer := lex("foo", text, "{{", "}}", false, true, true) _, err := New("root").parseLexer(lexer) if err == nil { t.Fatalf("expected error") diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go index 67e2f5b2f4..00c258ad5d 100644 --- a/src/text/template/parse/parse.go +++ b/src/text/template/parse/parse.go @@ -224,8 +224,6 @@ func (t *Tree) startParse(funcs []map[string]any, lex *lexer, treeSet map[string t.vars = []string{"$"} t.funcs = funcs t.treeSet = treeSet - lex.breakOK = !t.hasFunction("break") - lex.continueOK = !t.hasFunction("continue") } // stopParse terminates parsing. @@ -244,7 +242,10 @@ func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tre defer t.recover(&err) t.ParseName = t.Name emitComment := t.Mode&ParseComments != 0 - t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim, emitComment), treeSet) + breakOK := !t.hasFunction("break") + continueOK := !t.hasFunction("continue") + lexer := lex(t.Name, text, leftDelim, rightDelim, emitComment, breakOK, continueOK) + t.startParse(funcs, lexer, treeSet) t.text = text t.parse() t.add() From 1b8ca75eaaff2985709ad61cc33c77a673c75a8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20S=C3=A1ez?= Date: Mon, 23 May 2022 12:07:47 +0000 Subject: [PATCH 015/113] runtime: fix breakpoint in ppc64x Currently runtime.Breakpoint generates a SIGSEGV in ppc64. The solution is an unconditional trap similar to what clang and gcc do. It is documented in the section C.6 of the ABI Book 3. Fixes #52101 Change-Id: I071d2f2679b695ef268445b04c9222bd74e1f9af GitHub-Last-Rev: fff4e5e8ffe23bf0cef135b22abd2cc0a3838613 GitHub-Pull-Request: golang/go#52102 Reviewed-on: https://go-review.googlesource.com/c/go/+/397554 Reviewed-by: Dmitri Shuralyov TryBot-Result: Gopher Robot Reviewed-by: Paul Murphy Run-TryBot: Paul Murphy Reviewed-by: Cherry Mui --- src/runtime/asm_ppc64x.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s index ec17dc9ce3..c6bcf82ec0 100644 --- a/src/runtime/asm_ppc64x.s +++ b/src/runtime/asm_ppc64x.s @@ -106,7 +106,7 @@ DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0 - MOVD R0, 0(R0) // TODO: TD + TW $31, R0, R0 RET TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0 From 770146d5a857e8606222276040c6712b48e27e0e Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Mon, 6 Jun 2022 11:48:29 -0400 Subject: [PATCH 016/113] doc/go1.19: add TODOs for changes to go/types Add TODO items for significant changes to go/types: the inclusion of Origin methods for Var and Func, and a re-working of Named types to ensure finiteness of reachable types via their API. Updates #51400 Change-Id: I0f2a972023a5d5f995de3c33e9e2b0a4213e900a Reviewed-on: https://go-review.googlesource.com/c/go/+/410614 Reviewed-by: Robert Griesemer Run-TryBot: Robert Findley TryBot-Result: Gopher Robot --- doc/go1.19.html | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/go1.19.html b/doc/go1.19.html index 6d49cedfd4..06bd7bcff1 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -268,6 +268,17 @@ Do not send CLs removing the interior tags from such phrases.
+
go/types
+
+

+ TODO: https://go.dev/cl/395535: add Var.Origin and Func.Origin +

+

+ TODO: https://go.dev/cl/404885: a finite number of types are reachable via Named.Underlying, Named.Method +

+
+
+
image/draw

From 07eca49055f7ef0d73be2ca28dcc5d489db129b9 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 31 May 2022 20:59:55 -0700 Subject: [PATCH 017/113] go/types, types2: use type nest to detect type cycles (fix validType) validType was using a global type info map to detect invalid recursive types, which was incorrect. Instead, change the algorithm as follows: - Rather than using a "seen" (or typeInfo) map which is cumbersome to update correctly, use the stack of embedding types (the type nest) to check whether a type is embedded within itself, directly or indirectly. - Use Identical for type comparisons which correctly considers identity of instantiated generic types. - As before, maintain the full path of types leading to a cycle. But unlike before, track the named types rather than their objects, for a smaller slice ([]*Named rather than []Object), and convert to an object list only when needed for error reporting. - As an optimization, keep track of valid *Named types (Checker.valids). This prevents pathological cases from consuming excessive computation time. - Add clarifying comments and document invariants. Based on earlier insights by David Chase (see also CL 408818). Fixes #52698. Change-Id: I5e4598c58afcf4ab987a426c5c4b7b28bdfcf5ea Reviewed-on: https://go-review.googlesource.com/c/go/+/409694 Reviewed-by: Robert Findley Run-TryBot: Robert Griesemer Reviewed-by: David Chase TryBot-Result: Gopher Robot --- src/cmd/compile/internal/types2/check.go | 3 +- .../types2/testdata/fixedbugs/issue52698.go | 62 +++++ src/cmd/compile/internal/types2/validtype.go | 210 +++++++++++++---- src/go/types/check.go | 3 +- src/go/types/testdata/fixedbugs/issue52698.go | 50 ++++ src/go/types/validtype.go | 221 ++++++++++++++---- 6 files changed, 450 insertions(+), 99 deletions(-) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue52698.go create mode 100644 src/go/types/testdata/fixedbugs/issue52698.go diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go index 5cf8454aa4..ff8ae3bc7e 100644 --- a/src/cmd/compile/internal/types2/check.go +++ b/src/cmd/compile/internal/types2/check.go @@ -98,7 +98,7 @@ type Checker struct { nextID uint64 // unique Id for type parameters (first valid Id is 1) objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package - infoMap map[*Named]typeInfo // maps named types to their associated type info (for cycle detection) + valids instanceLookup // valid *Named (incl. instantiated) types per the validType check // pkgPathMap maps package names to the set of distinct import paths we've // seen for that name, anywhere in the import graph. It is used for @@ -241,7 +241,6 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker { version: version, objMap: make(map[Object]*declInfo), impMap: make(map[importKey]*Package), - infoMap: make(map[*Named]typeInfo), } } diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue52698.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue52698.go new file mode 100644 index 0000000000..d1b06a210d --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue52698.go @@ -0,0 +1,62 @@ +// 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 + +// correctness check: ensure that cycles through generic instantiations are detected +type T[P any] struct { + _ P +} + +type S /* ERROR illegal cycle */ struct { + _ T[S] +} + +// simplified test 1 + +var _ A1[A1[string]] + +type A1[P any] struct { + _ B1[P] +} + +type B1[P any] struct { + _ P +} + +// simplified test 2 +var _ B2[A2] + +type A2 struct { + _ B2[string] +} + +type B2[P any] struct { + _ C2[P] +} + +type C2[P any] struct { + _ P +} + +// test case from issue +type T23 interface { + ~struct { + Field0 T13[T15] + } +} + +type T1[P1 interface { +}] struct { + Field2 P1 +} + +type T13[P2 interface { +}] struct { + Field2 T1[P2] +} + +type T15 struct { + Field0 T13[string] +} diff --git a/src/cmd/compile/internal/types2/validtype.go b/src/cmd/compile/internal/types2/validtype.go index b69120481b..4ea29551ab 100644 --- a/src/cmd/compile/internal/types2/validtype.go +++ b/src/cmd/compile/internal/types2/validtype.go @@ -5,29 +5,24 @@ package types2 // validType verifies that the given type does not "expand" indefinitely -// producing a cycle in the type graph. Cycles are detected by marking -// defined types. +// producing a cycle in the type graph. // (Cycles involving alias types, as in "type A = [10]A" are detected // earlier, via the objDecl cycle detection mechanism.) func (check *Checker) validType(typ *Named) { - check.validType0(typ, nil, nil) + check.validType0(typ, nil, nil, nil) } -type typeInfo uint - // validType0 checks if the given type is valid. If typ is a type parameter // its value is looked up in the provided environment. The environment is // nil if typ is not part of (the RHS of) an instantiated type, in that case // any type parameter encountered must be from an enclosing function and can -// be ignored. The path is the list of type names that lead to the current typ. -func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeInfo { - const ( - unknown typeInfo = iota - marked - valid - invalid - ) - +// be ignored. The nest list describes the stack (the "nest in memory") of +// types which contain (or embed in the case of interfaces) other types. For +// instance, a struct named S which contains a field of named type F contains +// (the memory of) F in S, leading to the nest S->F. If a type appears in its +// own nest (say S->F->S) we have an invalid recursive type. The path list is +// the full path of named types in a cycle, it is only needed for error reporting. +func (check *Checker) validType0(typ Type, env *tparamEnv, nest, path []*Named) bool { switch t := typ.(type) { case nil: // We should never see a nil type but be conservative and panic @@ -37,60 +32,79 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeIn } case *Array: - return check.validType0(t.elem, env, path) + return check.validType0(t.elem, env, nest, path) case *Struct: for _, f := range t.fields { - if check.validType0(f.typ, env, path) == invalid { - return invalid + if !check.validType0(f.typ, env, nest, path) { + return false } } case *Union: for _, t := range t.terms { - if check.validType0(t.typ, env, path) == invalid { - return invalid + if !check.validType0(t.typ, env, nest, path) { + return false } } case *Interface: for _, etyp := range t.embeddeds { - if check.validType0(etyp, env, path) == invalid { - return invalid + if !check.validType0(etyp, env, nest, path) { + return false } } case *Named: + // Exit early if we already know t is valid. + // This is purely an optimization but it prevents excessive computation + // times in pathological cases such as testdata/fixedbugs/issue6977.go. + // (Note: The valids map could also be allocated locally, once for each + // validType call.) + if check.valids.lookup(t) != nil { + break + } + // Don't report a 2nd error if we already know the type is invalid // (e.g., if a cycle was detected earlier, via under). // Note: ensure that t.orig is fully resolved by calling Underlying(). if t.Underlying() == Typ[Invalid] { - check.infoMap[t] = invalid - return invalid + return false } - switch check.infoMap[t] { - case unknown: - check.infoMap[t] = marked - check.infoMap[t] = check.validType0(t.Origin().fromRHS, env.push(t), append(path, t.obj)) - case marked: - // We have seen type t before and thus must have a cycle. - check.infoMap[t] = invalid - // t cannot be in an imported package otherwise that package - // would have reported a type cycle and couldn't have been - // imported in the first place. - assert(t.obj.pkg == check.pkg) - t.underlying = Typ[Invalid] // t is in the current package (no race possibility) - // Find the starting point of the cycle and report it. - for i, tn := range path { - if tn == t.obj { - check.cycleError(path[i:]) - return invalid + // If the current type t is also found in nest, (the memory of) t is + // embedded in itself, indicating an invalid recursive type. + for _, e := range nest { + if Identical(e, t) { + // t cannot be in an imported package otherwise that package + // would have reported a type cycle and couldn't have been + // imported in the first place. + assert(t.obj.pkg == check.pkg) + t.underlying = Typ[Invalid] // t is in the current package (no race possibility) + // Find the starting point of the cycle and report it. + // Because each type in nest must also appear in path (see invariant below), + // type t must be in path since it was found in nest. But not every type in path + // is in nest. Specifically t may appear in path with an earlier index than the + // index of t in nest. Search again. + for start, p := range path { + if Identical(p, t) { + check.cycleError(makeObjList(path[start:])) + return false + } } + panic("cycle start not found") } - panic("cycle start not found") } - return check.infoMap[t] + + // No cycle was found. Check the RHS of t. + // Every type added to nest is also added to path; thus every type that is in nest + // must also be in path (invariant). But not every type in path is in nest, since + // nest may be pruned (see below, *TypeParam case). + if !check.validType0(t.Origin().fromRHS, env.push(t), append(nest, t), append(path, t)) { + return false + } + + check.valids.add(t) // t is valid case *TypeParam: // A type parameter stands for the type (argument) it was instantiated with. @@ -98,13 +112,29 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeIn if env != nil { if targ := env.tmap[t]; targ != nil { // Type arguments found in targ must be looked - // up in the enclosing environment env.link. - return check.validType0(targ, env.link, path) + // up in the enclosing environment env.link. The + // type argument must be valid in the enclosing + // type (where the current type was instantiated), + // hence we must check targ's validity in the type + // nest excluding the current (instantiated) type + // (see the example at the end of this file). + // For error reporting we keep the full path. + return check.validType0(targ, env.link, nest[:len(nest)-1], path) } } } - return valid + return true +} + +// makeObjList returns the list of type name objects for the given +// list of named types. +func makeObjList(tlist []*Named) []Object { + olist := make([]Object, len(tlist)) + for i, t := range tlist { + olist[i] = t.obj + } + return olist } // A tparamEnv provides the environment for looking up the type arguments @@ -146,3 +176,93 @@ func (env *tparamEnv) push(typ *Named) *tparamEnv { // same information should be available via the path: // We should be able to just walk the path backwards // and find the type arguments in the instance objects. + +// Here is an example illustrating why we need to exclude the +// instantiated type from nest when evaluating the validity of +// a type parameter. Given the declarations +// +// var _ A[A[string]] +// +// type A[P any] struct { _ B[P] } +// type B[P any] struct { _ P } +// +// we want to determine if the type A[A[string]] is valid. +// We start evaluating A[A[string]] outside any type nest: +// +// A[A[string]] +// nest = +// path = +// +// The RHS of A is now evaluated in the A[A[string]] nest: +// +// struct{_ B[P₁]} +// nest = A[A[string]] +// path = A[A[string]] +// +// The struct has a single field of type B[P₁] with which +// we continue: +// +// B[P₁] +// nest = A[A[string]] +// path = A[A[string]] +// +// struct{_ P₂} +// nest = A[A[string]]->B[P] +// path = A[A[string]]->B[P] +// +// Eventutally we reach the type parameter P of type B (P₂): +// +// P₂ +// nest = A[A[string]]->B[P] +// path = A[A[string]]->B[P] +// +// The type argument for P of B is the type parameter P of A (P₁). +// It must be evaluated in the type nest that existed when B was +// instantiated: +// +// P₁ +// nest = A[A[string]] <== type nest at B's instantiation time +// path = A[A[string]]->B[P] +// +// If we'd use the current nest it would correspond to the path +// which will be wrong as we will see shortly. P's type argument +// is A[string], which again must be evaluated in the type nest +// that existed when A was instantiated with A[string]. That type +// nest is empty: +// +// A[string] +// nest = <== type nest at A's instantiation time +// path = A[A[string]]->B[P] +// +// Evaluation then proceeds as before for A[string]: +// +// struct{_ B[P₁]} +// nest = A[string] +// path = A[A[string]]->B[P]->A[string] +// +// Now we reach B[P] again. If we had not adjusted nest, it would +// correspond to path, and we would find B[P] in nest, indicating +// a cycle, which would clearly be wrong since there's no cycle in +// A[string]: +// +// B[P₁] +// nest = A[string] +// path = A[A[string]]->B[P]->A[string] <== path contains B[P]! +// +// But because we use the correct type nest, evaluation proceeds without +// errors and we get the evaluation sequence: +// +// struct{_ P₂} +// nest = A[string]->B[P] +// path = A[A[string]]->B[P]->A[string]->B[P] +// P₂ +// nest = A[string]->B[P] +// path = A[A[string]]->B[P]->A[string]->B[P] +// P₁ +// nest = A[string] +// path = A[A[string]]->B[P]->A[string]->B[P] +// string +// nest = +// path = A[A[string]]->B[P]->A[string]->B[P] +// +// At this point we're done and A[A[string]] and is valid. diff --git a/src/go/types/check.go b/src/go/types/check.go index d920d9c080..b787c5c38b 100644 --- a/src/go/types/check.go +++ b/src/go/types/check.go @@ -105,7 +105,7 @@ type Checker struct { nextID uint64 // unique Id for type parameters (first valid Id is 1) objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package - infoMap map[*Named]typeInfo // maps named types to their associated type info (for cycle detection) + valids instanceLookup // valid *Named (incl. instantiated) types per the validType check // pkgPathMap maps package names to the set of distinct import paths we've // seen for that name, anywhere in the import graph. It is used for @@ -249,7 +249,6 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch version: version, objMap: make(map[Object]*declInfo), impMap: make(map[importKey]*Package), - infoMap: make(map[*Named]typeInfo), } } diff --git a/src/go/types/testdata/fixedbugs/issue52698.go b/src/go/types/testdata/fixedbugs/issue52698.go new file mode 100644 index 0000000000..3babc21d92 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue52698.go @@ -0,0 +1,50 @@ +// 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 + +// sanity check +type T[P any] struct { + _ P +} + +type S /* ERROR illegal cycle */ struct { + _ T[S] +} + +// simplified test +var _ B[A] + +type A struct { + _ B[string] +} + +type B[P any] struct { + _ C[P] +} + +type C[P any] struct { + _ P +} + +// test case from issue +type T23 interface { + ~struct { + Field0 T13[T15] + } +} + +type T1[P1 interface { +}] struct { + Field2 P1 +} + +type T13[P2 interface { +}] struct { + Field2 T1[P2] +} + +type T15 struct { + Field0 T13[string] +} diff --git a/src/go/types/validtype.go b/src/go/types/validtype.go index 0d7a0f308c..712508670f 100644 --- a/src/go/types/validtype.go +++ b/src/go/types/validtype.go @@ -5,29 +5,24 @@ package types // validType verifies that the given type does not "expand" indefinitely -// producing a cycle in the type graph. Cycles are detected by marking -// defined types. +// producing a cycle in the type graph. // (Cycles involving alias types, as in "type A = [10]A" are detected // earlier, via the objDecl cycle detection mechanism.) func (check *Checker) validType(typ *Named) { - check.validType0(typ, nil, nil) + check.validType0(typ, nil, nil, nil) } -type typeInfo uint - // validType0 checks if the given type is valid. If typ is a type parameter // its value is looked up in the provided environment. The environment is // nil if typ is not part of (the RHS of) an instantiated type, in that case // any type parameter encountered must be from an enclosing function and can -// be ignored. The path is the list of type names that lead to the current typ. -func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeInfo { - const ( - unknown typeInfo = iota - marked - valid - invalid - ) - +// be ignored. The nest list describes the stack (the "nest in memory") of +// types which contain (or embed in the case of interfaces) other types. For +// instance, a struct named S which contains a field of named type F contains +// (the memory of) F in S, leading to the nest S->F. If a type appears in its +// own nest (say S->F->S) we have an invalid recursive type. The path list is +// the full path of named types in a cycle, it is only needed for error reporting. +func (check *Checker) validType0(typ Type, env *tparamEnv, nest, path []*Named) bool { switch t := typ.(type) { case nil: // We should never see a nil type but be conservative and panic @@ -37,59 +32,79 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeIn } case *Array: - return check.validType0(t.elem, env, path) + return check.validType0(t.elem, env, nest, path) case *Struct: for _, f := range t.fields { - if check.validType0(f.typ, env, path) == invalid { - return invalid + if !check.validType0(f.typ, env, nest, path) { + return false } } case *Union: for _, t := range t.terms { - if check.validType0(t.typ, env, path) == invalid { - return invalid + if !check.validType0(t.typ, env, nest, path) { + return false } } case *Interface: for _, etyp := range t.embeddeds { - if check.validType0(etyp, env, path) == invalid { - return invalid + if !check.validType0(etyp, env, nest, path) { + return false } } case *Named: - // Don't report a 2nd error if we already know the type is invalid - // Note: ensure that t.orig is fully resolved by calling Underlying(). - if t.Underlying() == Typ[Invalid] { - check.infoMap[t] = invalid - return invalid + // Exit early if we already know t is valid. + // This is purely an optimization but it prevents excessive computation + // times in pathological cases such as testdata/fixedbugs/issue6977.go. + // (Note: The valids map could also be allocated locally, once for each + // validType call.) + if check.valids.lookup(t) != nil { + break } - switch check.infoMap[t] { - case unknown: - check.infoMap[t] = marked - check.infoMap[t] = check.validType0(t.Origin().fromRHS, env.push(t), append(path, t.obj)) - case marked: - // We have seen type t before and thus must have a cycle. - check.infoMap[t] = invalid - // t cannot be in an imported package otherwise that package - // would have reported a type cycle and couldn't have been - // imported in the first place. - assert(t.obj.pkg == check.pkg) - t.underlying = Typ[Invalid] // t is in the current package (no race possibility) - // Find the starting point of the cycle and report it. - for i, tn := range path { - if tn == t.obj { - check.cycleError(path[i:]) - return invalid - } - } - panic("cycle start not found") + // Don't report a 2nd error if we already know the type is invalid + // (e.g., if a cycle was detected earlier, via under). + // Note: ensure that t.orig is fully resolved by calling Underlying(). + if t.Underlying() == Typ[Invalid] { + return false } - return check.infoMap[t] + + // If the current type t is also found in nest, (the memory of) t is + // embedded in itself, indicating an invalid recursive type. + for _, e := range nest { + if Identical(e, t) { + // t cannot be in an imported package otherwise that package + // would have reported a type cycle and couldn't have been + // imported in the first place. + assert(t.obj.pkg == check.pkg) + t.underlying = Typ[Invalid] // t is in the current package (no race possibility) + // Find the starting point of the cycle and report it. + // Because each type in nest must also appear in path (see invariant below), + // type t must be in path since it was found in nest. But not every type in path + // is in nest. Specifically t may appear in path with an earlier index than the + // index of t in nest. Search again. + for start, p := range path { + if Identical(p, t) { + check.cycleError(makeObjList(path[start:])) + return false + } + } + panic("cycle start not found") + } + } + + // No cycle was found. Check the RHS of t. + // Every type added to nest is also added to path; thus every type that is in nest + // must also be in path (invariant). But not every type in path is in nest, since + // nest may be pruned (see below, *TypeParam case). + if !check.validType0(t.Origin().fromRHS, env.push(t), append(nest, t), append(path, t)) { + return false + } + + check.valids.add(t) // t is valid case *TypeParam: // A type parameter stands for the type (argument) it was instantiated with. @@ -97,13 +112,29 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeIn if env != nil { if targ := env.tmap[t]; targ != nil { // Type arguments found in targ must be looked - // up in the enclosing environment env.link. - return check.validType0(targ, env.link, path) + // up in the enclosing environment env.link. The + // type argument must be valid in the enclosing + // type (where the current type was instantiated), + // hence we must check targ's validity in the type + // nest excluding the current (instantiated) type + // (see the example at the end of this file). + // For error reporting we keep the full path. + return check.validType0(targ, env.link, nest[:len(nest)-1], path) } } } - return valid + return true +} + +// makeObjList returns the list of type name objects for the given +// list of named types. +func makeObjList(tlist []*Named) []Object { + olist := make([]Object, len(tlist)) + for i, t := range tlist { + olist[i] = t.obj + } + return olist } // A tparamEnv provides the environment for looking up the type arguments @@ -145,3 +176,93 @@ func (env *tparamEnv) push(typ *Named) *tparamEnv { // same information should be available via the path: // We should be able to just walk the path backwards // and find the type arguments in the instance objects. + +// Here is an example illustrating why we need to exclude the +// instantiated type from nest when evaluating the validity of +// a type parameter. Given the declarations +// +// var _ A[A[string]] +// +// type A[P any] struct { _ B[P] } +// type B[P any] struct { _ P } +// +// we want to determine if the type A[A[string]] is valid. +// We start evaluating A[A[string]] outside any type nest: +// +// A[A[string]] +// nest = +// path = +// +// The RHS of A is now evaluated in the A[A[string]] nest: +// +// struct{_ B[P₁]} +// nest = A[A[string]] +// path = A[A[string]] +// +// The struct has a single field of type B[P₁] with which +// we continue: +// +// B[P₁] +// nest = A[A[string]] +// path = A[A[string]] +// +// struct{_ P₂} +// nest = A[A[string]]->B[P] +// path = A[A[string]]->B[P] +// +// Eventutally we reach the type parameter P of type B (P₂): +// +// P₂ +// nest = A[A[string]]->B[P] +// path = A[A[string]]->B[P] +// +// The type argument for P of B is the type parameter P of A (P₁). +// It must be evaluated in the type nest that existed when B was +// instantiated: +// +// P₁ +// nest = A[A[string]] <== type nest at B's instantiation time +// path = A[A[string]]->B[P] +// +// If we'd use the current nest it would correspond to the path +// which will be wrong as we will see shortly. P's type argument +// is A[string], which again must be evaluated in the type nest +// that existed when A was instantiated with A[string]. That type +// nest is empty: +// +// A[string] +// nest = <== type nest at A's instantiation time +// path = A[A[string]]->B[P] +// +// Evaluation then proceeds as before for A[string]: +// +// struct{_ B[P₁]} +// nest = A[string] +// path = A[A[string]]->B[P]->A[string] +// +// Now we reach B[P] again. If we had not adjusted nest, it would +// correspond to path, and we would find B[P] in nest, indicating +// a cycle, which would clearly be wrong since there's no cycle in +// A[string]: +// +// B[P₁] +// nest = A[string] +// path = A[A[string]]->B[P]->A[string] <== path contains B[P]! +// +// But because we use the correct type nest, evaluation proceeds without +// errors and we get the evaluation sequence: +// +// struct{_ P₂} +// nest = A[string]->B[P] +// path = A[A[string]]->B[P]->A[string]->B[P] +// P₂ +// nest = A[string]->B[P] +// path = A[A[string]]->B[P]->A[string]->B[P] +// P₁ +// nest = A[string] +// path = A[A[string]]->B[P]->A[string]->B[P] +// string +// nest = +// path = A[A[string]]->B[P]->A[string]->B[P] +// +// At this point we're done and A[A[string]] and is valid. From fc9707594910452cce3fba794fa9ffe541e8cefa Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 2 Jun 2022 20:40:17 -0700 Subject: [PATCH 018/113] go/types, types2: simplify implementation of validType (fix TODO) Now that validType is using the correct type nest (CL 409694), the top entry of the type nest corresponds to the instantiated type. Thus we can use that type instance to look up the value of type parameters, there's no need anymore to create an environment to look up type arguments. Remove the need to pass around the environment and remove all associated types and functions. Updates #52698. Change-Id: Ie37eace88896386e667ef93c77a4fc3cd0be6eb9 Reviewed-on: https://go-review.googlesource.com/c/go/+/410294 Reviewed-by: Robert Findley TryBot-Result: Gopher Robot Reviewed-by: Robert Griesemer Run-TryBot: Robert Griesemer --- src/cmd/compile/internal/types2/validtype.go | 103 +++++++------------ src/go/types/validtype.go | 103 +++++++------------ 2 files changed, 70 insertions(+), 136 deletions(-) diff --git a/src/cmd/compile/internal/types2/validtype.go b/src/cmd/compile/internal/types2/validtype.go index 4ea29551ab..99fdebc978 100644 --- a/src/cmd/compile/internal/types2/validtype.go +++ b/src/cmd/compile/internal/types2/validtype.go @@ -9,20 +9,20 @@ package types2 // (Cycles involving alias types, as in "type A = [10]A" are detected // earlier, via the objDecl cycle detection mechanism.) func (check *Checker) validType(typ *Named) { - check.validType0(typ, nil, nil, nil) + check.validType0(typ, nil, nil) } // validType0 checks if the given type is valid. If typ is a type parameter -// its value is looked up in the provided environment. The environment is -// nil if typ is not part of (the RHS of) an instantiated type, in that case -// any type parameter encountered must be from an enclosing function and can -// be ignored. The nest list describes the stack (the "nest in memory") of -// types which contain (or embed in the case of interfaces) other types. For -// instance, a struct named S which contains a field of named type F contains -// (the memory of) F in S, leading to the nest S->F. If a type appears in its -// own nest (say S->F->S) we have an invalid recursive type. The path list is -// the full path of named types in a cycle, it is only needed for error reporting. -func (check *Checker) validType0(typ Type, env *tparamEnv, nest, path []*Named) bool { +// its value is looked up in the type argument list of the instantiated +// (enclosing) type, if it exists. Otherwise the type parameter must be from +// an enclosing function and can be ignored. +// The nest list describes the stack (the "nest in memory") of types which +// contain (or embed in the case of interfaces) other types. For instance, a +// struct named S which contains a field of named type F contains (the memory +// of) F in S, leading to the nest S->F. If a type appears in its own nest +// (say S->F->S) we have an invalid recursive type. The path list is the full +// path of named types in a cycle, it is only needed for error reporting. +func (check *Checker) validType0(typ Type, nest, path []*Named) bool { switch t := typ.(type) { case nil: // We should never see a nil type but be conservative and panic @@ -32,25 +32,25 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, nest, path []*Named) } case *Array: - return check.validType0(t.elem, env, nest, path) + return check.validType0(t.elem, nest, path) case *Struct: for _, f := range t.fields { - if !check.validType0(f.typ, env, nest, path) { + if !check.validType0(f.typ, nest, path) { return false } } case *Union: for _, t := range t.terms { - if !check.validType0(t.typ, env, nest, path) { + if !check.validType0(t.typ, nest, path) { return false } } case *Interface: for _, etyp := range t.embeddeds { - if !check.validType0(etyp, env, nest, path) { + if !check.validType0(etyp, nest, path) { return false } } @@ -100,7 +100,7 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, nest, path []*Named) // Every type added to nest is also added to path; thus every type that is in nest // must also be in path (invariant). But not every type in path is in nest, since // nest may be pruned (see below, *TypeParam case). - if !check.validType0(t.Origin().fromRHS, env.push(t), append(nest, t), append(path, t)) { + if !check.validType0(t.Origin().fromRHS, append(nest, t), append(path, t)) { return false } @@ -108,18 +108,25 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, nest, path []*Named) case *TypeParam: // A type parameter stands for the type (argument) it was instantiated with. - // Check the corresponding type argument for validity if we have one. - if env != nil { - if targ := env.tmap[t]; targ != nil { - // Type arguments found in targ must be looked - // up in the enclosing environment env.link. The - // type argument must be valid in the enclosing - // type (where the current type was instantiated), - // hence we must check targ's validity in the type - // nest excluding the current (instantiated) type - // (see the example at the end of this file). - // For error reporting we keep the full path. - return check.validType0(targ, env.link, nest[:len(nest)-1], path) + // Check the corresponding type argument for validity if we are in an + // instantiated type. + if len(nest) > 0 { + inst := nest[len(nest)-1] // the type instance + // Find the corresponding type argument for the type parameter + // and proceed with checking that type argument. + for i, tparam := range inst.TypeParams().list() { + // The type parameter and type argument lists should + // match in length but be careful in case of errors. + if t == tparam && i < inst.TypeArgs().Len() { + targ := inst.TypeArgs().At(i) + // The type argument must be valid in the enclosing + // type (where inst was instantiated), hence we must + // check targ's validity in the type nest excluding + // the current (instantiated) type (see the example + // at the end of this file). + // For error reporting we keep the full path. + return check.validType0(targ, nest[:len(nest)-1], path) + } } } } @@ -137,46 +144,6 @@ func makeObjList(tlist []*Named) []Object { return olist } -// A tparamEnv provides the environment for looking up the type arguments -// with which type parameters for a given instance were instantiated. -// If we don't have an instance, the corresponding tparamEnv is nil. -type tparamEnv struct { - tmap substMap - link *tparamEnv -} - -func (env *tparamEnv) push(typ *Named) *tparamEnv { - // If typ is not an instantiated type there are no typ-specific - // type parameters to look up and we don't need an environment. - targs := typ.TypeArgs() - if targs == nil { - return nil // no instance => nil environment - } - - // Populate tmap: remember the type argument for each type parameter. - // We cannot use makeSubstMap because the number of type parameters - // and arguments may not match due to errors in the source (too many - // or too few type arguments). Populate tmap "manually". - tparams := typ.TypeParams() - n, m := targs.Len(), tparams.Len() - if n > m { - n = m // too many targs - } - tmap := make(substMap, n) - for i := 0; i < n; i++ { - tmap[tparams.At(i)] = targs.At(i) - } - - return &tparamEnv{tmap: tmap, link: env} -} - -// TODO(gri) Alternative implementation: -// We may not need to build a stack of environments to -// look up the type arguments for type parameters. The -// same information should be available via the path: -// We should be able to just walk the path backwards -// and find the type arguments in the instance objects. - // Here is an example illustrating why we need to exclude the // instantiated type from nest when evaluating the validity of // a type parameter. Given the declarations diff --git a/src/go/types/validtype.go b/src/go/types/validtype.go index 712508670f..34c9533a05 100644 --- a/src/go/types/validtype.go +++ b/src/go/types/validtype.go @@ -9,20 +9,20 @@ package types // (Cycles involving alias types, as in "type A = [10]A" are detected // earlier, via the objDecl cycle detection mechanism.) func (check *Checker) validType(typ *Named) { - check.validType0(typ, nil, nil, nil) + check.validType0(typ, nil, nil) } // validType0 checks if the given type is valid. If typ is a type parameter -// its value is looked up in the provided environment. The environment is -// nil if typ is not part of (the RHS of) an instantiated type, in that case -// any type parameter encountered must be from an enclosing function and can -// be ignored. The nest list describes the stack (the "nest in memory") of -// types which contain (or embed in the case of interfaces) other types. For -// instance, a struct named S which contains a field of named type F contains -// (the memory of) F in S, leading to the nest S->F. If a type appears in its -// own nest (say S->F->S) we have an invalid recursive type. The path list is -// the full path of named types in a cycle, it is only needed for error reporting. -func (check *Checker) validType0(typ Type, env *tparamEnv, nest, path []*Named) bool { +// its value is looked up in the type argument list of the instantiated +// (enclosing) type, if it exists. Otherwise the type parameter must be from +// an enclosing function and can be ignored. +// The nest list describes the stack (the "nest in memory") of types which +// contain (or embed in the case of interfaces) other types. For instance, a +// struct named S which contains a field of named type F contains (the memory +// of) F in S, leading to the nest S->F. If a type appears in its own nest +// (say S->F->S) we have an invalid recursive type. The path list is the full +// path of named types in a cycle, it is only needed for error reporting. +func (check *Checker) validType0(typ Type, nest, path []*Named) bool { switch t := typ.(type) { case nil: // We should never see a nil type but be conservative and panic @@ -32,25 +32,25 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, nest, path []*Named) } case *Array: - return check.validType0(t.elem, env, nest, path) + return check.validType0(t.elem, nest, path) case *Struct: for _, f := range t.fields { - if !check.validType0(f.typ, env, nest, path) { + if !check.validType0(f.typ, nest, path) { return false } } case *Union: for _, t := range t.terms { - if !check.validType0(t.typ, env, nest, path) { + if !check.validType0(t.typ, nest, path) { return false } } case *Interface: for _, etyp := range t.embeddeds { - if !check.validType0(etyp, env, nest, path) { + if !check.validType0(etyp, nest, path) { return false } } @@ -100,7 +100,7 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, nest, path []*Named) // Every type added to nest is also added to path; thus every type that is in nest // must also be in path (invariant). But not every type in path is in nest, since // nest may be pruned (see below, *TypeParam case). - if !check.validType0(t.Origin().fromRHS, env.push(t), append(nest, t), append(path, t)) { + if !check.validType0(t.Origin().fromRHS, append(nest, t), append(path, t)) { return false } @@ -108,18 +108,25 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, nest, path []*Named) case *TypeParam: // A type parameter stands for the type (argument) it was instantiated with. - // Check the corresponding type argument for validity if we have one. - if env != nil { - if targ := env.tmap[t]; targ != nil { - // Type arguments found in targ must be looked - // up in the enclosing environment env.link. The - // type argument must be valid in the enclosing - // type (where the current type was instantiated), - // hence we must check targ's validity in the type - // nest excluding the current (instantiated) type - // (see the example at the end of this file). - // For error reporting we keep the full path. - return check.validType0(targ, env.link, nest[:len(nest)-1], path) + // Check the corresponding type argument for validity if we are in an + // instantiated type. + if len(nest) > 0 { + inst := nest[len(nest)-1] // the type instance + // Find the corresponding type argument for the type parameter + // and proceed with checking that type argument. + for i, tparam := range inst.TypeParams().list() { + // The type parameter and type argument lists should + // match in length but be careful in case of errors. + if t == tparam && i < inst.TypeArgs().Len() { + targ := inst.TypeArgs().At(i) + // The type argument must be valid in the enclosing + // type (where inst was instantiated), hence we must + // check targ's validity in the type nest excluding + // the current (instantiated) type (see the example + // at the end of this file). + // For error reporting we keep the full path. + return check.validType0(targ, nest[:len(nest)-1], path) + } } } } @@ -137,46 +144,6 @@ func makeObjList(tlist []*Named) []Object { return olist } -// A tparamEnv provides the environment for looking up the type arguments -// with which type parameters for a given instance were instantiated. -// If we don't have an instance, the corresponding tparamEnv is nil. -type tparamEnv struct { - tmap substMap - link *tparamEnv -} - -func (env *tparamEnv) push(typ *Named) *tparamEnv { - // If typ is not an instantiated type there are no typ-specific - // type parameters to look up and we don't need an environment. - targs := typ.TypeArgs() - if targs == nil { - return nil // no instance => nil environment - } - - // Populate tmap: remember the type argument for each type parameter. - // We cannot use makeSubstMap because the number of type parameters - // and arguments may not match due to errors in the source (too many - // or too few type arguments). Populate tmap "manually". - tparams := typ.TypeParams() - n, m := targs.Len(), tparams.Len() - if n > m { - n = m // too many targs - } - tmap := make(substMap, n) - for i := 0; i < n; i++ { - tmap[tparams.At(i)] = targs.At(i) - } - - return &tparamEnv{tmap: tmap, link: env} -} - -// TODO(gri) Alternative implementation: -// We may not need to build a stack of environments to -// look up the type arguments for type parameters. The -// same information should be available via the path: -// We should be able to just walk the path backwards -// and find the type arguments in the instance objects. - // Here is an example illustrating why we need to exclude the // instantiated type from nest when evaluating the validity of // a type parameter. Given the declarations From 2fa45a4fcd57070967f081f3a5c33014c3f29fea Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Fri, 3 Jun 2022 07:50:58 -0400 Subject: [PATCH 019/113] cmd/link/internal/loadpe: handle _main reference properly When building CGO internal linking on windows 386, make sure to avoid rewriting references to "_main" to "main" when reading symbols during host object loading; the main routine defined by the Go runtime is still named "_main" (not "main"). If we don't do this, we wind up with an SXREF symbol named "main", which can then cause the loader to pull an actual "main" symbol out of a host archive, which is undesirable. Updates #35006. Change-Id: I3768e3617b560552f4522e9e72af879c6adf7705 Reviewed-on: https://go-review.googlesource.com/c/go/+/410124 TryBot-Result: Gopher Robot Reviewed-by: Alex Brainman Auto-Submit: Than McIntosh Run-TryBot: Than McIntosh Reviewed-by: Cherry Mui --- src/cmd/link/internal/loadpe/ldpe.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go index bfe2e837c9..bc66252cfa 100644 --- a/src/cmd/link/internal/loadpe/ldpe.go +++ b/src/cmd/link/internal/loadpe/ldpe.go @@ -551,7 +551,13 @@ func (state *peLoaderState) readpesym(pesym *pe.COFFSymbol) (*loader.SymbolBuild name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name } } - if state.arch.Family == sys.I386 && name[0] == '_' { + // A note on the "_main" exclusion below: the main routine + // defined by the Go runtime is named "_main", not "main", so + // when reading references to _main from a host object we want + // to avoid rewriting "_main" to "main" in this specific + // instance. See #issuecomment-1143698749 on #35006 for more + // details on this problem. + if state.arch.Family == sys.I386 && name[0] == '_' && name != "_main" { name = name[1:] // _Name => Name } } From d43ddc1f3fb25b4338433435caae7e6f4b3138a9 Mon Sep 17 00:00:00 2001 From: "90364136+butterfly1924@users.noreply.github.com" <90364136+butterfly1924@users.noreply.github.com> Date: Mon, 6 Jun 2022 05:10:38 +0000 Subject: [PATCH 020/113] strconv: fix typo in atof.go strings for 'NaN' -> string for 'NaN' Change-Id: Ia415644a1b651e6ef9996ad24dd9708a60e57dfc GitHub-Last-Rev: 877f1c3eb1dc885915ae75385c5d38ee6f5fd9b1 GitHub-Pull-Request: golang/go#53246 Reviewed-on: https://go-review.googlesource.com/c/go/+/410494 Auto-Submit: Ian Lance Taylor TryBot-Result: Gopher Robot Run-TryBot: Ian Lance Taylor Reviewed-by: Ian Lance Taylor Reviewed-by: Dmitri Shuralyov Reviewed-by: Cherry Mui --- src/strconv/atof.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strconv/atof.go b/src/strconv/atof.go index c26c34208c..8fc90425f6 100644 --- a/src/strconv/atof.go +++ b/src/strconv/atof.go @@ -688,7 +688,7 @@ func atof64(s string) (f float64, n int, err error) { // away from the largest floating point number of the given size, // ParseFloat returns f = ±Inf, err.Err = ErrRange. // -// ParseFloat recognizes the strings "NaN", and the (possibly signed) strings "Inf" and "Infinity" +// ParseFloat recognizes the string "NaN", and the (possibly signed) strings "Inf" and "Infinity" // as their respective special floating point values. It ignores case when matching. // // [floating-point literals]: https://go.dev/ref/spec#Floating-point_literals From 95547aee8c6377e73919d6f0b99484152fb3de04 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Thu, 2 Jun 2022 05:09:09 +1000 Subject: [PATCH 021/113] cmd/compile: cast riscv64 rewrite shifts to unsigned int This appeases Go 1.4, making it possible to bootstrap GOARCH=riscv64 with a Go 1.4 compiler. Fixes #52583 Change-Id: Ib13c2afeb095b2bb1464dcd7f1502574209bc7ab Reviewed-on: https://go-review.googlesource.com/c/go/+/409974 TryBot-Result: Gopher Robot Run-TryBot: Joel Sing Reviewed-by: Bryan Mills Reviewed-by: Cherry Mui --- src/cmd/compile/internal/ssa/gen/RISCV64.rules | 6 +++--- src/cmd/compile/internal/ssa/rewriteRISCV64.go | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64.rules b/src/cmd/compile/internal/ssa/gen/RISCV64.rules index 7aea622c5e..dd20be2aeb 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64.rules +++ b/src/cmd/compile/internal/ssa/gen/RISCV64.rules @@ -735,9 +735,9 @@ (NEGW (MOVDconst [x])) => (MOVDconst [int64(int32(-x))]) // Shift of a constant. -(SLLI [x] (MOVDconst [y])) && is32Bit(y << x) => (MOVDconst [y << x]) -(SRLI [x] (MOVDconst [y])) => (MOVDconst [int64(uint64(y) >> x)]) -(SRAI [x] (MOVDconst [y])) => (MOVDconst [int64(y) >> x]) +(SLLI [x] (MOVDconst [y])) && is32Bit(y << uint32(x)) => (MOVDconst [y << uint32(x)]) +(SRLI [x] (MOVDconst [y])) => (MOVDconst [int64(uint64(y) >> uint32(x))]) +(SRAI [x] (MOVDconst [y])) => (MOVDconst [int64(y) >> uint32(x)]) // SLTI/SLTIU with constants. (SLTI [x] (MOVDconst [y])) => (MOVDconst [b2i(int64(y) < int64(x))]) diff --git a/src/cmd/compile/internal/ssa/rewriteRISCV64.go b/src/cmd/compile/internal/ssa/rewriteRISCV64.go index 6828d97ff8..2677e99dc0 100644 --- a/src/cmd/compile/internal/ssa/rewriteRISCV64.go +++ b/src/cmd/compile/internal/ssa/rewriteRISCV64.go @@ -4843,19 +4843,19 @@ func rewriteValueRISCV64_OpRISCV64SLL(v *Value) bool { func rewriteValueRISCV64_OpRISCV64SLLI(v *Value) bool { v_0 := v.Args[0] // match: (SLLI [x] (MOVDconst [y])) - // cond: is32Bit(y << x) - // result: (MOVDconst [y << x]) + // cond: is32Bit(y << uint32(x)) + // result: (MOVDconst [y << uint32(x)]) for { x := auxIntToInt64(v.AuxInt) if v_0.Op != OpRISCV64MOVDconst { break } y := auxIntToInt64(v_0.AuxInt) - if !(is32Bit(y << x)) { + if !(is32Bit(y << uint32(x))) { break } v.reset(OpRISCV64MOVDconst) - v.AuxInt = int64ToAuxInt(y << x) + v.AuxInt = int64ToAuxInt(y << uint32(x)) return true } return false @@ -4913,7 +4913,7 @@ func rewriteValueRISCV64_OpRISCV64SRA(v *Value) bool { func rewriteValueRISCV64_OpRISCV64SRAI(v *Value) bool { v_0 := v.Args[0] // match: (SRAI [x] (MOVDconst [y])) - // result: (MOVDconst [int64(y) >> x]) + // result: (MOVDconst [int64(y) >> uint32(x)]) for { x := auxIntToInt64(v.AuxInt) if v_0.Op != OpRISCV64MOVDconst { @@ -4921,7 +4921,7 @@ func rewriteValueRISCV64_OpRISCV64SRAI(v *Value) bool { } y := auxIntToInt64(v_0.AuxInt) v.reset(OpRISCV64MOVDconst) - v.AuxInt = int64ToAuxInt(int64(y) >> x) + v.AuxInt = int64ToAuxInt(int64(y) >> uint32(x)) return true } return false @@ -4947,7 +4947,7 @@ func rewriteValueRISCV64_OpRISCV64SRL(v *Value) bool { func rewriteValueRISCV64_OpRISCV64SRLI(v *Value) bool { v_0 := v.Args[0] // match: (SRLI [x] (MOVDconst [y])) - // result: (MOVDconst [int64(uint64(y) >> x)]) + // result: (MOVDconst [int64(uint64(y) >> uint32(x))]) for { x := auxIntToInt64(v.AuxInt) if v_0.Op != OpRISCV64MOVDconst { @@ -4955,7 +4955,7 @@ func rewriteValueRISCV64_OpRISCV64SRLI(v *Value) bool { } y := auxIntToInt64(v_0.AuxInt) v.reset(OpRISCV64MOVDconst) - v.AuxInt = int64ToAuxInt(int64(uint64(y) >> x)) + v.AuxInt = int64ToAuxInt(int64(uint64(y) >> uint32(x))) return true } return false From ce757e94e028a3c4b490a460b7b8b8266ae38fe4 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 1 Jun 2022 22:09:18 -0400 Subject: [PATCH 022/113] go/doc/comment: add doc comment A CL in the website repo will add go.dev/doc/comment. One of the final steps for #51082. Change-Id: I419b4f6dbb424a8a93a8d09db30f7321af9ae976 Reviewed-on: https://go-review.googlesource.com/c/go/+/410358 TryBot-Result: Gopher Robot Run-TryBot: Russ Cox Reviewed-by: Ian Lance Taylor --- src/go/doc/comment/doc.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/go/doc/comment/doc.go diff --git a/src/go/doc/comment/doc.go b/src/go/doc/comment/doc.go new file mode 100644 index 0000000000..45a476aa9a --- /dev/null +++ b/src/go/doc/comment/doc.go @@ -0,0 +1,36 @@ +// 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 comment implements parsing and reformatting of Go doc comments, +(documentation comments), which are comments that immediately precede +a top-level declaration of a package, const, func, type, or var. + +Go doc comment syntax is a simplified subset of Markdown that supports +links, headings, paragraphs, lists (without nesting), and preformatted text blocks. +The details of the syntax are documented at https://go.dev/doc/comment. + +To parse the text associated with a doc comment (after removing comment markers), +use a [Parser]: + + var p comment.Parser + doc := p.Parse(text) + +The result is a [*Doc]. +To reformat it as a doc comment, HTML, Markdown, or plain text, +use a [Printer]: + + var pr comment.Printer + os.Stdout.Write(pr.Text(doc)) + +The [Parser] and [Printer] types are structs whose fields can be +modified to customize the operations. +For details, see the documentation for those types. + +Use cases that need additional control over reformatting can +implement their own logic by inspecting the parsed syntax itself. +See the documentation for [Doc], [Block], [Text] for an overview +and links to additional types. +*/ +package comment From 6c7b223c2bfa700d9e1dc53d58c1c998493126e0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 3 Jun 2022 09:18:57 -0400 Subject: [PATCH 023/113] =?UTF-8?q?go/doc/comment:=20do=20not=20turn=20```?= =?UTF-8?q?=20into=20=E2=80=9C`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` is Markdown, not Go doc comment, but some small fraction of users get confused. In a set of 55M Go doc comments drawn from the latest version of all public Go modules known to the module proxy in spring 2020, the current Go 1.19 gofmt reformats about 1.57M of them. Out of those 1.57M comments, 8k of them (about 0.5%) contain ```. Instead of rewriting ``` to “`, leave it alone. For #51082. Change-Id: I1c8c88aac7ef75ec03e1a396b84ffe711c46f941 Reviewed-on: https://go-review.googlesource.com/c/go/+/410359 Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot Run-TryBot: Russ Cox --- src/go/doc/comment/parse.go | 8 ++++++++ src/go/doc/comment/testdata/quote.txt | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/go/doc/comment/parse.go b/src/go/doc/comment/parse.go index 83b37c32c5..8a311ff817 100644 --- a/src/go/doc/comment/parse.go +++ b/src/go/doc/comment/parse.go @@ -840,6 +840,14 @@ func (d *parseDoc) parseText(out []Text, s string, autoLink bool) []Text { } switch { case strings.HasPrefix(t, "``"): + if len(t) >= 3 && t[2] == '`' { + // Do not convert `` inside ```, in case people are mistakenly writing Markdown. + i += 3 + for i < len(t) && t[i] == '`' { + i++ + } + break + } writeUntil(i) w.WriteRune('“') i += 2 diff --git a/src/go/doc/comment/testdata/quote.txt b/src/go/doc/comment/testdata/quote.txt index 799663af80..b64adae0b3 100644 --- a/src/go/doc/comment/testdata/quote.txt +++ b/src/go/doc/comment/testdata/quote.txt @@ -1,12 +1,15 @@ -- input -- Doubled single quotes like `` and '' turn into Unicode double quotes, but single quotes ` and ' do not. +Misplaced markdown fences ``` do not either. -- gofmt -- Doubled single quotes like “ and ” turn into Unicode double quotes, but single quotes ` and ' do not. +Misplaced markdown fences ``` do not either. -- text -- Doubled single quotes like “ and ” turn into Unicode double quotes, but single -quotes ` and ' do not. +quotes ` and ' do not. Misplaced markdown fences ``` do not either. -- html --

Doubled single quotes like “ and ” turn into Unicode double quotes, but single quotes ` and ' do not. +Misplaced markdown fences ``` do not either. From ea5d7cbc2644643331bd675b1ebdf0aaac7419f1 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 4 May 2022 08:58:02 -0400 Subject: [PATCH 024/113] all: boringcrypto post-merge cleanup This CL addresses the comments on CL 403154. For #51940. Change-Id: I99bb3530916d469077bfbd53095bfcd1d2aa82ef Reviewed-on: https://go-review.googlesource.com/c/go/+/403976 Reviewed-by: Roland Shoemaker TryBot-Result: Gopher Robot Run-TryBot: Russ Cox Reviewed-by: Ian Lance Taylor --- src/cmd/go/internal/modindex/build.go | 4 +-- src/cmd/internal/notsha256/example_test.go | 41 ---------------------- src/crypto/aes/cipher.go | 3 +- src/crypto/aes/cipher_asm.go | 3 +- src/crypto/ecdsa/ecdsa.go | 3 +- src/crypto/hmac/hmac.go | 3 +- src/crypto/internal/boring/aes.go | 7 ++-- src/crypto/internal/boring/notboring.go | 1 + src/crypto/rand/rand_unix.go | 3 +- src/crypto/rsa/pkcs1v15.go | 34 +++++++++--------- src/crypto/rsa/pss.go | 4 +-- src/crypto/tls/cipher_suites.go | 10 ++---- src/go/build/build.go | 6 ++-- 13 files changed, 35 insertions(+), 87 deletions(-) delete mode 100644 src/cmd/internal/notsha256/example_test.go diff --git a/src/cmd/go/internal/modindex/build.go b/src/cmd/go/internal/modindex/build.go index 78bd12636d..9d52be851b 100644 --- a/src/cmd/go/internal/modindex/build.go +++ b/src/cmd/go/internal/modindex/build.go @@ -887,8 +887,8 @@ func (ctxt *Context) eval(x constraint.Expr, allTags map[string]bool) bool { // $GOARCH // boringcrypto // ctxt.Compiler -// linux (if GOOS = android) -// solaris (if GOOS = illumos) +// linux (if GOOS == android) +// solaris (if GOOS == illumos) // tag (if tag is listed in ctxt.BuildTags or ctxt.ReleaseTags) // // It records all consulted tags in allTags. diff --git a/src/cmd/internal/notsha256/example_test.go b/src/cmd/internal/notsha256/example_test.go deleted file mode 100644 index 06e9c379c9..0000000000 --- a/src/cmd/internal/notsha256/example_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package notsha256_test - -import ( - "crypto/sha256" - "fmt" - "io" - "log" - "os" -) - -func ExampleSum256() { - sum := sha256.Sum256([]byte("hello world\n")) - fmt.Printf("%x", sum) - // Output: a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447 -} - -func ExampleNew() { - h := sha256.New() - h.Write([]byte("hello world\n")) - fmt.Printf("%x", h.Sum(nil)) - // Output: a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447 -} - -func ExampleNew_file() { - f, err := os.Open("file.txt") - if err != nil { - log.Fatal(err) - } - defer f.Close() - - h := sha256.New() - if _, err := io.Copy(h, f); err != nil { - log.Fatal(err) - } - - fmt.Printf("%x", h.Sum(nil)) -} diff --git a/src/crypto/aes/cipher.go b/src/crypto/aes/cipher.go index 29d01796eb..db0ee38b78 100644 --- a/src/crypto/aes/cipher.go +++ b/src/crypto/aes/cipher.go @@ -6,12 +6,11 @@ package aes import ( "crypto/cipher" + "crypto/internal/boring" "crypto/internal/subtle" "strconv" ) -import "crypto/internal/boring" - // The AES block size in bytes. const BlockSize = 16 diff --git a/src/crypto/aes/cipher_asm.go b/src/crypto/aes/cipher_asm.go index b7e59d7edb..1482b22d08 100644 --- a/src/crypto/aes/cipher_asm.go +++ b/src/crypto/aes/cipher_asm.go @@ -8,13 +8,12 @@ package aes import ( "crypto/cipher" + "crypto/internal/boring" "crypto/internal/subtle" "internal/cpu" "internal/goarch" ) -import "crypto/internal/boring" - // defined in asm_*.s //go:noescape diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go index 7ce7542872..d0e52ad864 100644 --- a/src/crypto/ecdsa/ecdsa.go +++ b/src/crypto/ecdsa/ecdsa.go @@ -24,6 +24,7 @@ import ( "crypto/aes" "crypto/cipher" "crypto/elliptic" + "crypto/internal/boring" "crypto/internal/boring/bbig" "crypto/internal/randutil" "crypto/sha512" @@ -31,8 +32,6 @@ import ( "io" "math/big" - "crypto/internal/boring" - "golang.org/x/crypto/cryptobyte" "golang.org/x/crypto/cryptobyte/asn1" ) diff --git a/src/crypto/hmac/hmac.go b/src/crypto/hmac/hmac.go index 34805765d5..ed3ebc0602 100644 --- a/src/crypto/hmac/hmac.go +++ b/src/crypto/hmac/hmac.go @@ -22,12 +22,11 @@ timing side-channels: package hmac import ( + "crypto/internal/boring" "crypto/subtle" "hash" ) -import "crypto/internal/boring" - // FIPS 198-1: // https://csrc.nist.gov/publications/fips/fips198-1/FIPS-198-1_final.pdf diff --git a/src/crypto/internal/boring/aes.go b/src/crypto/internal/boring/aes.go index 515b60bb8a..eaa1adc892 100644 --- a/src/crypto/internal/boring/aes.go +++ b/src/crypto/internal/boring/aes.go @@ -72,9 +72,6 @@ type extraModes interface { NewCBCDecrypter(iv []byte) cipher.BlockMode NewCTR(iv []byte) cipher.Stream NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) - - // Invented for BoringCrypto. - NewGCMTLS() (cipher.AEAD, error) } var _ extraModes = (*aesCipher)(nil) @@ -235,8 +232,8 @@ func (c *aesCipher) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) { return c.newGCM(false) } -func (c *aesCipher) NewGCMTLS() (cipher.AEAD, error) { - return c.newGCM(true) +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { + return c.(*aesCipher).newGCM(true) } func (c *aesCipher) newGCM(tls bool) (cipher.AEAD, error) { diff --git a/src/crypto/internal/boring/notboring.go b/src/crypto/internal/boring/notboring.go index bb88fb0004..53096a68d1 100644 --- a/src/crypto/internal/boring/notboring.go +++ b/src/crypto/internal/boring/notboring.go @@ -50,6 +50,7 @@ func SHA512([]byte) [64]byte { panic("boringcrypto: not available") } func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("boringcrypto: not available") } func NewAESCipher(key []byte) (cipher.Block, error) { panic("boringcrypto: not available") } +func NewGCMTLS(cipher.Block) (cipher.AEAD, error) { panic("boringcrypto: not available") } type PublicKeyECDSA struct{ _ int } type PrivateKeyECDSA struct{ _ int } diff --git a/src/crypto/rand/rand_unix.go b/src/crypto/rand/rand_unix.go index 830983c74a..746e90cc91 100644 --- a/src/crypto/rand/rand_unix.go +++ b/src/crypto/rand/rand_unix.go @@ -10,6 +10,7 @@ package rand import ( + "crypto/internal/boring" "errors" "io" "os" @@ -19,8 +20,6 @@ import ( "time" ) -import "crypto/internal/boring" - const urandomDevice = "/dev/urandom" func init() { diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go index 8cf3b6e255..ab19229a6c 100644 --- a/src/crypto/rsa/pkcs1v15.go +++ b/src/crypto/rsa/pkcs1v15.go @@ -6,16 +6,14 @@ package rsa import ( "crypto" + "crypto/internal/boring" + "crypto/internal/randutil" "crypto/subtle" "errors" "io" "math/big" - - "crypto/internal/randutil" ) -import "crypto/internal/boring" - // This file implements encryption and decryption using PKCS #1 v1.5 padding. // PKCS1v15DecrypterOpts is for passing options to PKCS #1 v1.5 decryption using @@ -32,7 +30,7 @@ type PKCS1v15DecryptOptions struct { // scheme from PKCS #1 v1.5. The message must be no longer than the // length of the public modulus minus 11 bytes. // -// The rand parameter is used as a source of entropy to ensure that +// The random parameter is used as a source of entropy to ensure that // encrypting the same message twice doesn't result in the same // ciphertext. // @@ -84,14 +82,14 @@ func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, erro } // DecryptPKCS1v15 decrypts a plaintext using RSA and the padding scheme from PKCS #1 v1.5. -// If rand != nil, it uses RSA blinding to avoid timing side-channel attacks. +// If random != nil, it uses RSA blinding to avoid timing side-channel attacks. // // Note that whether this function returns an error or not discloses secret // information. If an attacker can cause this function to run repeatedly and // learn whether each instance returned an error then they can decrypt and // forge signatures as if they had the private key. See // DecryptPKCS1v15SessionKey for a way of solving this problem. -func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) ([]byte, error) { +func DecryptPKCS1v15(random io.Reader, priv *PrivateKey, ciphertext []byte) ([]byte, error) { if err := checkPub(&priv.PublicKey); err != nil { return nil, err } @@ -108,7 +106,7 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) ([]byt return out, nil } - valid, out, index, err := decryptPKCS1v15(rand, priv, ciphertext) + valid, out, index, err := decryptPKCS1v15(random, priv, ciphertext) if err != nil { return nil, err } @@ -119,7 +117,7 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) ([]byt } // DecryptPKCS1v15SessionKey decrypts a session key using RSA and the padding scheme from PKCS #1 v1.5. -// If rand != nil, it uses RSA blinding to avoid timing side-channel attacks. +// If random != nil, it uses RSA blinding to avoid timing side-channel attacks. // It returns an error if the ciphertext is the wrong length or if the // ciphertext is greater than the public modulus. Otherwise, no error is // returned. If the padding is valid, the resulting plaintext message is copied @@ -137,7 +135,7 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) ([]byt // a random value was used (because it'll be different for the same ciphertext) // and thus whether the padding was correct. This defeats the point of this // function. Using at least a 16-byte key will protect against this attack. -func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) error { +func DecryptPKCS1v15SessionKey(random io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) error { if err := checkPub(&priv.PublicKey); err != nil { return err } @@ -146,7 +144,7 @@ func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []by return ErrDecryption } - valid, em, index, err := decryptPKCS1v15(rand, priv, ciphertext) + valid, em, index, err := decryptPKCS1v15(random, priv, ciphertext) if err != nil { return err } @@ -163,12 +161,12 @@ func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []by } // decryptPKCS1v15 decrypts ciphertext using priv and blinds the operation if -// rand is not nil. It returns one or zero in valid that indicates whether the +// random is not nil. It returns one or zero in valid that indicates whether the // plaintext was correctly structured. In either case, the plaintext is // returned in em so that it may be read independently of whether it was valid // in order to maintain constant memory access patterns. If the plaintext was // valid then index contains the index of the original message in em. -func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, em []byte, index int, err error) { +func decryptPKCS1v15(random io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, em []byte, index int, err error) { k := priv.Size() if k < 11 { err = ErrDecryption @@ -188,7 +186,7 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid } else { c := new(big.Int).SetBytes(ciphertext) var m *big.Int - m, err = decrypt(rand, priv, c) + m, err = decrypt(random, priv, c) if err != nil { return } @@ -220,15 +218,15 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid } // nonZeroRandomBytes fills the given slice with non-zero random octets. -func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) { - _, err = io.ReadFull(rand, s) +func nonZeroRandomBytes(s []byte, random io.Reader) (err error) { + _, err = io.ReadFull(random, s) if err != nil { return } for i := 0; i < len(s); i++ { for s[i] == 0 { - _, err = io.ReadFull(rand, s[i:i+1]) + _, err = io.ReadFull(random, s[i:i+1]) if err != nil { return } @@ -268,7 +266,7 @@ var hashPrefixes = map[crypto.Hash][]byte{ // function. If hash is zero, hashed is signed directly. This isn't // advisable except for interoperability. // -// If rand is not nil then RSA blinding will be used to avoid timing +// If random is not nil then RSA blinding will be used to avoid timing // side-channel attacks. // // This function is deterministic. Thus, if the set of possible diff --git a/src/crypto/rsa/pss.go b/src/crypto/rsa/pss.go index 16ebc0e6a7..29e79bd342 100644 --- a/src/crypto/rsa/pss.go +++ b/src/crypto/rsa/pss.go @@ -9,14 +9,13 @@ package rsa import ( "bytes" "crypto" + "crypto/internal/boring" "errors" "hash" "io" "math/big" ) -import "crypto/internal/boring" - // Per RFC 8017, Section 9.1 // // EM = MGF1 xor DB || H( 8*0x00 || mHash || salt ) || 0xbc @@ -298,6 +297,7 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, } return boring.SignRSAPSS(bkey, hash, digest, saltLength) } + boring.UnreachableExceptTests() salt := make([]byte, saltLength) if _, err := io.ReadFull(rand, salt); err != nil { diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go index 3004b31698..9a1fa3104b 100644 --- a/src/crypto/tls/cipher_suites.go +++ b/src/crypto/tls/cipher_suites.go @@ -4,14 +4,13 @@ package tls -import "crypto/internal/boring" - import ( "crypto" "crypto/aes" "crypto/cipher" "crypto/des" "crypto/hmac" + "crypto/internal/boring" "crypto/rc4" "crypto/sha1" "crypto/sha256" @@ -517,12 +516,9 @@ func aeadAESGCM(key, noncePrefix []byte) aead { if err != nil { panic(err) } - type gcmtls interface { - NewGCMTLS() (cipher.AEAD, error) - } var aead cipher.AEAD - if aesTLS, ok := aes.(gcmtls); ok { - aead, err = aesTLS.NewGCMTLS() + if boring.Enabled { + aead, err = boring.NewGCMTLS(aes) } else { boring.Unreachable() aead, err = cipher.NewGCM(aes) diff --git a/src/go/build/build.go b/src/go/build/build.go index 039b422dab..bfe3f444ca 100644 --- a/src/go/build/build.go +++ b/src/go/build/build.go @@ -1883,11 +1883,13 @@ func (ctxt *Context) eval(x constraint.Expr, allTags map[string]bool) bool { // cgo (if cgo is enabled) // $GOOS // $GOARCH -// boringcrypto // ctxt.Compiler // linux (if GOOS = android) // solaris (if GOOS = illumos) -// tag (if tag is listed in ctxt.BuildTags or ctxt.ReleaseTags) +// darwin (if GOOS = ios) +// unix (if this is a Unix GOOS) +// boringcrypto (if GOEXPERIMENT=boringcrypto is enabled) +// tag (if tag is listed in ctxt.BuildTags, ctxt.ToolTags, or ctxt.ReleaseTags) // // It records all consulted tags in allTags. func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool { From 11195c60e6197016c0d5d32b04d4cb0ca7594014 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Tue, 3 May 2022 13:17:08 -0400 Subject: [PATCH 025/113] cmd/go: use index to match packages in dependency modules If we're trying to search in a module in the module cache, instead iterate over the packages in the index. Change-Id: Ia94cbe6e9690110c28b93dbb33810680e3010381 Reviewed-on: https://go-review.googlesource.com/c/go/+/403756 Reviewed-by: Michael Matloob Reviewed-by: Peter Weinberger Run-TryBot: Michael Matloob TryBot-Result: Gopher Robot --- src/cmd/go/internal/modindex/read.go | 2 +- src/cmd/go/internal/modindex/scan.go | 7 +-- src/cmd/go/internal/modload/search.go | 63 +++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/cmd/go/internal/modindex/read.go b/src/cmd/go/internal/modindex/read.go index 4f02ca5d10..f259a8dbe3 100644 --- a/src/cmd/go/internal/modindex/read.go +++ b/src/cmd/go/internal/modindex/read.go @@ -210,7 +210,7 @@ func (mi *ModuleIndex) Packages() []string { // RelPath returns the path relative to the module's root. func (mi *ModuleIndex) RelPath(path string) string { - return filepath.Clean(str.TrimFilePathPrefix(path, mi.modroot)) + return str.TrimFilePathPrefix(path, mi.modroot) } // ImportPackage is the equivalent of build.Import given the information in ModuleIndex. diff --git a/src/cmd/go/internal/modindex/scan.go b/src/cmd/go/internal/modindex/scan.go index e40d3e0f53..d1f73dbb53 100644 --- a/src/cmd/go/internal/modindex/scan.go +++ b/src/cmd/go/internal/modindex/scan.go @@ -4,6 +4,7 @@ import ( "cmd/go/internal/base" "cmd/go/internal/fsys" "cmd/go/internal/par" + "cmd/go/internal/str" "encoding/json" "errors" "fmt" @@ -54,10 +55,10 @@ func indexModule(modroot string) ([]byte, error) { if !info.IsDir() { return nil } - rel, err := filepath.Rel(modroot, path) - if err != nil { - panic(err) + if !str.HasFilePathPrefix(path, modroot) { + panic(fmt.Errorf("path %v in walk doesn't have modroot %v as prefix:", path, modroot)) } + rel := str.TrimFilePathPrefix(path, modroot) packages = append(packages, importRaw(modroot, rel)) return nil }) diff --git a/src/cmd/go/internal/modload/search.go b/src/cmd/go/internal/modload/search.go index cddb9f8067..60c68860ed 100644 --- a/src/cmd/go/internal/modload/search.go +++ b/src/cmd/go/internal/modload/search.go @@ -6,15 +6,18 @@ package modload import ( "context" + "errors" "fmt" "io/fs" "os" + "path" "path/filepath" "strings" "cmd/go/internal/cfg" "cmd/go/internal/fsys" "cmd/go/internal/imports" + "cmd/go/internal/modindex" "cmd/go/internal/search" "golang.org/x/mod/module" @@ -165,6 +168,12 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f } modPrefix = mod.Path } + if mi, err := modindex.Get(root); err == nil { + walkFromIndex(ctx, m, tags, root, mi, have, modPrefix) + continue + } else if !errors.Is(err, modindex.ErrNotIndexed) { + m.AddError(err) + } prune := pruneVendor if isLocal { @@ -176,6 +185,60 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f return } +// walkFromIndex matches packages in a module using the module index. modroot +// is the module's root directory on disk, index is the ModuleIndex for the +// module, and importPathRoot is the module's path prefix. +func walkFromIndex(ctx context.Context, m *search.Match, tags map[string]bool, modroot string, index *modindex.ModuleIndex, have map[string]bool, importPathRoot string) { + isMatch := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } + if !m.IsMeta() { + isMatch = search.MatchPattern(m.Pattern()) + treeCanMatch = search.TreeCanMatchPattern(m.Pattern()) + } +loopPackages: + for _, reldir := range index.Packages() { + // Avoid .foo, _foo, and testdata subdirectory trees. + p := reldir + for { + elem, rest, found := strings.Cut(p, string(filepath.Separator)) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + continue loopPackages + } + if found && elem == "vendor" { + // Ignore this path if it contains the element "vendor" anywhere + // except for the last element (packages named vendor are allowed + // for historical reasons). Note that found is true when this + // isn't the last path element. + continue loopPackages + } + if !found { + // Didn't find the separator, so we're considering the last element. + break + } + p = rest + } + + // Don't use GOROOT/src. + if reldir == "" && importPathRoot == "" { + continue + } + + name := path.Join(importPathRoot, filepath.ToSlash(reldir)) + if !treeCanMatch(name) { + continue + } + + if !have[name] { + have[name] = true + if isMatch(name) { + if _, _, err := index.ScanDir(reldir, tags); err != imports.ErrNoGo { + m.Pkgs = append(m.Pkgs, name) + } + } + } + } +} + // MatchInModule identifies the packages matching the given pattern within the // given module version, which does not need to be in the build list or module // requirement graph. From c1e2ecbaf93b360f3384a23c77a01c2280b9e58e Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 3 Jun 2022 21:18:14 -0700 Subject: [PATCH 026/113] doc/go1.19: document Resolver.PreferGo Updates #51400 Change-Id: I61733574362d4cf3cb65122bd13361e5c0f6728c Reviewed-on: https://go-review.googlesource.com/c/go/+/410375 Run-TryBot: Heschi Kreinick TryBot-Result: Gopher Robot Reviewed-by: Michael Pratt Run-TryBot: Michael Pratt Reviewed-by: Heschi Kreinick Auto-Submit: Michael Pratt --- doc/go1.19.html | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 06bd7bcff1..4e6699c289 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -343,7 +343,20 @@ Do not send CLs removing the interior tags from such phrases.

- TODO: https://go.dev/cl/400654: permit use of Resolver.PreferGo, netgo on Windows and Plan 9 + Resolver.PreferGo + is now implemented on Windows and Plan 9. It previously only worked on Unix + platforms. Combined with + Dialer.Resolver and + Resolver.Dial, it's now + possible to write portable programs and be in control of all DNS name lookups + when dialing. +

+

+ The net package now has initial support for the netgo + build tag on Windows. When used, the package uses the Go DNS client (as used + by Resolver.PreferGo) instead of asking Windows for + DNS results. The upstream DNS server it discovers from Windows + may not yet be correct with complex system network configurations, however.

From 7271a0a287fdd765f29c193163b0f1305edb6205 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 6 Jun 2022 15:45:00 -0400 Subject: [PATCH 027/113] doc/go1.19: gc requires -p=importpath For #51400 Change-Id: I07a805147a6aa0923331f3f940a9e6e5553cbea9 Reviewed-on: https://go-review.googlesource.com/c/go/+/410676 Reviewed-by: Matthew Dempsky --- doc/go1.19.html | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 4e6699c289..9a689d1980 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -134,8 +134,12 @@ Do not send CLs removing the interior tags from such phrases.

TODO: https://go.dev/cl/402374: enable regabi on riscv64 by default

-

- TODO: https://go.dev/cl/391014: The Go compiler now requires the -p=importpath flag, which is already supplied by the go command and by Bazel. Any other build systems that invoke the Go compiler directly will need to make sure they pass this flag as well in order to use Go 1.19.: cmd/compile: require -p flag +

+ The Go compiler now requires the -p=importpath flag to + build a linkable object file. This is already supplied by + the go command and by Bazel. Any other build systems + that invoke the Go compiler directly will need to make sure they + pass this flag as well.

TODO: complete this section, or delete if not needed From 4c08260c51c6ebe405a78a30f970014af763ca38 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 6 Jun 2022 15:20:51 -0400 Subject: [PATCH 028/113] doc/go_mem: update revision date CL 381315 added major revisions but neglected to update the date. For #50859. Change-Id: I086a55f0c80579c479bca5268109c9f3ae680adf Reviewed-on: https://go-review.googlesource.com/c/go/+/410675 Reviewed-by: Roland Shoemaker Run-TryBot: Russ Cox Reviewed-by: Ian Lance Taylor Auto-Submit: Russ Cox Reviewed-by: Alan Donovan --- doc/go_mem.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/go_mem.html b/doc/go_mem.html index 9bcf06707a..59f9ab880d 100644 --- a/doc/go_mem.html +++ b/doc/go_mem.html @@ -1,6 +1,6 @@ From 3651a6117e9a88576615c29c4faf7eeec55d7691 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 3 Jun 2022 12:44:58 -0400 Subject: [PATCH 029/113] go/doc/comment: add heuristics for common badly formatted comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a set of 55M Go doc comments drawn from the latest version of all public Go modules known to the module proxy in spring 2020, the current Go 1.19 gofmt reformats about 1.57M of them. Out of those 1.57M comments, inspection of random samples shows that around 5% of the changed comments contain unindented code snippets, multiline shell commands, or lists. For example: // Here is a greeting: // // func main() { // fmt.Println("hello, world") // } // Run this command: // // path/to/your/program -flag1=longargument1 \ // -flag2=longargument2 \ // -flag3 // There are three possibilities: // // - Unindented code snippets (or JSON objects) // in which the first and last line are unindented // but end in { and start with }, respectively. // - Unindented multiline shell commands // in which the lines end in \ // - Unindented lists, in which wrapped lines are indented. All three of these cases involve unindented lines next to indented lines that would according to the usual rules begin a pre block. Before this CL, they'd be reformatted to: // Here is a greeting: // // func main() { // // fmt.Println("hello, world") // // } // Run this command: // // path/to/your/program -flag1=longargument1 \ // // -flag2=longargument2 \ // -flag3 // There are three possibilities: // // - Unindented code snippets (or JSON objects) // // in which the first and last line are unindented // but end in { and start with }, respectively. // // - Unindented multiline shell commands // // in which the lines end in \ // // - Unindented lists, in which wrapped lines are indented. The fact that they are not already in canonical format gives us a signal that they might not mean what the usual rules would say. This CL takes advantage of that opening to apply a few heuristics to better handle these cases: 1. If an indented code block immediately follows (without a blank line) an unindented line ending in { or \, include the unindented line in the code block. 2. If an indented code block immediately precedes (without a blank line) an unindented line beginning with }, include the unindented line in the code block. 3. If an indented line immediately follows (without a blank line) an unindented line that starts with a list marker, assume this is an unindented list with a wrapped indented line, and treat all adjacent unindented lines starting with list markers as part of the list, stopping at any surrounding blank lines. This raises the fraction of “correctly” reformatted doc comments in the corpus from approximately 87% to approximately 93%. Change-Id: I7ac542eb085032d607a7caf3ba9020787b2978b5 Reviewed-on: https://go-review.googlesource.com/c/go/+/410360 Auto-Submit: Russ Cox TryBot-Result: Gopher Robot Reviewed-by: Roland Shoemaker Reviewed-by: Alan Donovan Run-TryBot: Russ Cox Reviewed-by: Ian Lance Taylor --- src/go/doc/comment/parse.go | 294 ++++++++++++++++--------- src/go/doc/comment/testdata/code4.txt | 38 ++++ src/go/doc/comment/testdata/code5.txt | 21 ++ src/go/doc/comment/testdata/code6.txt | 24 ++ src/go/doc/comment/testdata/list10.txt | 13 ++ src/go/doc/comment/testdata/list9.txt | 30 +++ src/go/doc/comment/text.go | 1 - 7 files changed, 322 insertions(+), 99 deletions(-) create mode 100644 src/go/doc/comment/testdata/code4.txt create mode 100644 src/go/doc/comment/testdata/code5.txt create mode 100644 src/go/doc/comment/testdata/code6.txt create mode 100644 src/go/doc/comment/testdata/list10.txt create mode 100644 src/go/doc/comment/testdata/list9.txt diff --git a/src/go/doc/comment/parse.go b/src/go/doc/comment/parse.go index 8a311ff817..4de8ce710d 100644 --- a/src/go/doc/comment/parse.go +++ b/src/go/doc/comment/parse.go @@ -260,7 +260,8 @@ func (d *parseDoc) lookupPkg(pkg string) (importPath string, ok bool) { } func isStdPkg(path string) bool { - // TODO(rsc): Use sort.Find. + // TODO(rsc): Use sort.Find once we don't have to worry about + // copying this code into older Go environments. i := sort.Search(len(stdPkgs), func(i int) bool { return stdPkgs[i] >= path }) return i < len(stdPkgs) && stdPkgs[i] == path } @@ -297,44 +298,27 @@ func (p *Parser) Parse(text string) *Doc { // First pass: break into block structure and collect known links. // The text is all recorded as Plain for now. - // TODO: Break into actual block structure. - didHeading := false - all := lines - for len(lines) > 0 { - line := lines[0] - n := len(lines) + var prev span + for _, s := range parseSpans(lines) { var b Block - - switch { - case line == "": - // emit nothing - - case isList(line): - prevWasBlank := len(lines) < len(all) && all[len(all)-len(lines)-1] == "" - b, lines = d.list(lines, prevWasBlank) - - case isIndented(line): - b, lines = d.code(lines) - - case (len(lines) == 1 || lines[1] == "") && !didHeading && isOldHeading(line, all, len(all)-n): - b = d.oldHeading(line) - didHeading = true - - case (len(lines) == 1 || lines[1] == "") && isHeading(line): - b = d.heading(line) - didHeading = true - + switch s.kind { default: - b, lines = d.paragraph(lines) - didHeading = false + panic("go/doc/comment: internal error: unknown span kind") + case spanList: + b = d.list(lines[s.start:s.end], prev.end < s.start) + case spanCode: + b = d.code(lines[s.start:s.end]) + case spanOldHeading: + b = d.oldHeading(lines[s.start]) + case spanHeading: + b = d.heading(lines[s.start]) + case spanPara: + b = d.paragraph(lines[s.start:s.end]) } - if b != nil { d.Content = append(d.Content, b) } - if len(lines) == n { - lines = lines[1:] - } + prev = s } // Second pass: interpret all the Plain text now that we know the links. @@ -348,9 +332,172 @@ func (p *Parser) Parse(text string) *Doc { return d.Doc } +// A span represents a single span of comment lines (lines[start:end]) +// of an identified kind (code, heading, paragraph, and so on). +type span struct { + start int + end int + kind spanKind +} + +// A spanKind describes the kind of span. +type spanKind int + +const ( + _ spanKind = iota + spanCode + spanHeading + spanList + spanOldHeading + spanPara +) + +func parseSpans(lines []string) []span { + var spans []span + + // The loop may process a line twice: once as unindented + // and again forced indented. So the maximum expected + // number of iterations is 2*len(lines). The repeating logic + // can be subtle, though, and to protect against introduction + // of infinite loops in future changes, we watch to see that + // we are not looping too much. A panic is better than a + // quiet infinite loop. + watchdog := 2 * len(lines) + + i := 0 + forceIndent := 0 +Spans: + for { + // Skip blank lines. + for i < len(lines) && lines[i] == "" { + i++ + } + if i >= len(lines) { + break + } + if watchdog--; watchdog < 0 { + panic("go/doc/comment: internal error: not making progress") + } + + var kind spanKind + start := i + end := i + if i < forceIndent || indented(lines[i]) { + // Indented (or force indented). + // Ends before next unindented. (Blank lines are OK.) + // If this is an unindented list that we are heuristically treating as indented, + // then accept unindented list item lines up to the first blank lines. + // The heuristic is disabled at blank lines to contain its effect + // to non-gofmt'ed sections of the comment. + unindentedListOK := isList(lines[i]) && i < forceIndent + i++ + for i < len(lines) && (lines[i] == "" || i < forceIndent || indented(lines[i]) || (unindentedListOK && isList(lines[i]))) { + if lines[i] == "" { + unindentedListOK = false + } + i++ + } + + // Drop trailing blank lines. + end = i + for end > start && lines[end-1] == "" { + end-- + } + + // If indented lines are followed (without a blank line) + // by an unindented line ending in a brace, + // take that one line too. This fixes the common mistake + // of pasting in something like + // + // func main() { + // fmt.Println("hello, world") + // } + // + // and forgetting to indent it. + // The heuristic will never trigger on a gofmt'ed comment, + // because any gofmt'ed code block or list would be + // followed by a blank line or end of comment. + if end < len(lines) && strings.HasPrefix(lines[end], "}") { + end++ + } + + if isList(lines[start]) { + kind = spanList + } else { + kind = spanCode + } + } else { + // Unindented. Ends at next blank or indented line. + i++ + for i < len(lines) && lines[i] != "" && !indented(lines[i]) { + i++ + } + end = i + + // If unindented lines are followed (without a blank line) + // by an indented line that would start a code block, + // check whether the final unindented lines + // should be left for the indented section. + // This can happen for the common mistakes of + // unindented code or unindented lists. + // The heuristic will never trigger on a gofmt'ed comment, + // because any gofmt'ed code block would have a blank line + // preceding it after the unindented lines. + if i < len(lines) && lines[i] != "" && !isList(lines[i]) { + switch { + case isList(lines[i-1]): + // If the final unindented line looks like a list item, + // this may be the first indented line wrap of + // a mistakenly unindented list. + // Leave all the unindented list items. + forceIndent = end + end-- + for end > start && isList(lines[end-1]) { + end-- + } + + case strings.HasSuffix(lines[i-1], "{") || strings.HasSuffix(lines[i-1], `\`): + // If the final unindented line ended in { or \ + // it is probably the start of a misindented code block. + // Give the user a single line fix. + // Often that's enough; if not, the user can fix the others themselves. + forceIndent = end + end-- + } + + if start == end && forceIndent > start { + i = start + continue Spans + } + } + + // Span is either paragraph or heading. + if end-start == 1 && isHeading(lines[start]) { + kind = spanHeading + } else if end-start == 1 && isOldHeading(lines[start], lines, start) { + kind = spanOldHeading + } else { + kind = spanPara + } + } + + spans = append(spans, span{start, end, kind}) + i = end + } + + return spans +} + +// indented reports whether line is indented +// (starts with a leading space or tab). +func indented(line string) bool { + return line != "" && (line[0] == ' ' || line[0] == '\t') +} + // unindent removes any common space/tab prefix // from each line in lines, returning a copy of lines in which // those prefixes have been trimmed from each line. +// It also replaces any lines containing only spaces with blank lines (empty strings). func unindent(lines []string) []string { // Trim leading and trailing blank lines. for len(lines) > 0 && isBlank(lines[0]) { @@ -480,58 +627,16 @@ func (d *parseDoc) heading(line string) Block { return &Heading{Text: []Text{Plain(strings.TrimSpace(line[1:]))}} } -// code returns a code block built from the indented text -// at the start of lines, along with the remainder of the lines. -// If there is no indented text at the start, or if the indented -// text consists only of empty lines, code returns a nil Block. -func (d *parseDoc) code(lines []string) (b Block, rest []string) { - lines, rest = indented(lines) +// code returns a code block built from the lines. +func (d *parseDoc) code(lines []string) *Code { body := unindent(lines) - if len(body) == 0 { - return nil, rest - } body = append(body, "") // to get final \n from Join - return &Code{Text: strings.Join(body, "\n")}, rest + return &Code{Text: strings.Join(body, "\n")} } -// isIndented reports whether the line is indented, -// meaning it starts with a space or tab. -func isIndented(line string) bool { - return line != "" && (line[0] == ' ' || line[0] == '\t') -} - -// indented splits lines into an initial indented section -// and the remaining lines, returning the two halves. -func indented(lines []string) (indented, rest []string) { - // Blank lines mid-run are OK, but not at the end. - i := 0 - for i < len(lines) && (isIndented(lines[i]) || lines[i] == "") { - i++ - } - for i > 0 && lines[i-1] == "" { - i-- - } - return lines[:i], lines[i:] -} - -// paragraph returns a paragraph block built from the -// unindented text at the start of lines, along with the remainder of the lines. -// If there is no unindented text at the start of lines, -// then paragraph returns a nil Block. -func (d *parseDoc) paragraph(lines []string) (b Block, rest []string) { - // Paragraph is interrupted by any indented line, - // which is either a list or a code block, - // and of course by a blank line. - // It is not interrupted by a # line - headings must stand alone. - i := 0 - for i < len(lines) && lines[i] != "" && !isIndented(lines[i]) { - i++ - } - lines, rest = lines[:i], lines[i:] - if len(lines) == 0 { - return nil, rest - } - +// paragraph returns a paragraph block built from the lines. +// If the lines are link definitions, paragraph adds them to d and returns nil. +func (d *parseDoc) paragraph(lines []string) Block { // Is this a block of known links? Handle. var defs []*LinkDef for _, line := range lines { @@ -547,10 +652,10 @@ func (d *parseDoc) paragraph(lines []string) (b Block, rest []string) { d.links[def.Text] = def } } - return nil, rest + return nil NoDefs: - return &Paragraph{Text: []Text{Plain(strings.Join(lines, "\n"))}}, rest + return &Paragraph{Text: []Text{Plain(strings.Join(lines, "\n"))}} } // parseLink parses a single link definition line: @@ -581,14 +686,9 @@ func parseLink(line string) (*LinkDef, bool) { return &LinkDef{Text: text, URL: url}, true } -// list returns a list built from the indented text at the start of lines, +// list returns a list built from the indented lines, // using forceBlankBefore as the value of the List's ForceBlankBefore field. -// The caller is responsible for ensuring that the first line of lines -// satisfies isList. -// list returns the *List as a Block along with the remaining lines. -func (d *parseDoc) list(lines []string, forceBlankBefore bool) (b Block, rest []string) { - lines, rest = indented(lines) - +func (d *parseDoc) list(lines []string, forceBlankBefore bool) *List { num, _, _ := listMarker(lines[0]) var ( list *List = &List{ForceBlankBefore: forceBlankBefore} @@ -597,7 +697,7 @@ func (d *parseDoc) list(lines []string, forceBlankBefore bool) (b Block, rest [] ) flush := func() { if item != nil { - if para, _ := d.paragraph(text); para != nil { + if para := d.paragraph(text); para != nil { item.Content = append(item.Content, para) } } @@ -622,17 +722,14 @@ func (d *parseDoc) list(lines []string, forceBlankBefore bool) (b Block, rest [] text = append(text, strings.TrimSpace(line)) } flush() - return list, rest + return list } -// listMarker parses the line as an indented line beginning with a list marker. +// listMarker parses the line as beginning with a list marker. // If it can do that, it returns the numeric marker ("" for a bullet list), // the rest of the line, and ok == true. // Otherwise, it returns "", "", false. func listMarker(line string) (num, rest string, ok bool) { - if !isIndented(line) { - return "", "", false - } line = strings.TrimSpace(line) if line == "" { return "", "", false @@ -654,7 +751,7 @@ func listMarker(line string) (num, rest string, ok bool) { return "", "", false } - if !isIndented(rest) || strings.TrimSpace(rest) == "" { + if !indented(rest) || strings.TrimSpace(rest) == "" { return "", "", false } @@ -662,7 +759,8 @@ func listMarker(line string) (num, rest string, ok bool) { } // isList reports whether the line is the first line of a list, -// meaning is indented and starts with a list marker. +// meaning starts with a list marker after any indentation. +// (The caller is responsible for checking the line is indented, as appropriate.) func isList(line string) bool { _, _, ok := listMarker(line) return ok diff --git a/src/go/doc/comment/testdata/code4.txt b/src/go/doc/comment/testdata/code4.txt new file mode 100644 index 0000000000..f128c9aeff --- /dev/null +++ b/src/go/doc/comment/testdata/code4.txt @@ -0,0 +1,38 @@ +-- input -- +To test, run this command: + go test -more + +Or, to test specific things, run this command: + +go test -more \ + -pkg first/package \ + -pkg second/package \ + -pkg third/package + +Happy testing! +-- gofmt -- +To test, run this command: + + go test -more + +Or, to test specific things, run this command: + + go test -more \ + -pkg first/package \ + -pkg second/package \ + -pkg third/package + +Happy testing! +-- markdown -- +To test, run this command: + + go test -more + +Or, to test specific things, run this command: + + go test -more \ + -pkg first/package \ + -pkg second/package \ + -pkg third/package + +Happy testing! diff --git a/src/go/doc/comment/testdata/code5.txt b/src/go/doc/comment/testdata/code5.txt new file mode 100644 index 0000000000..0e340dd129 --- /dev/null +++ b/src/go/doc/comment/testdata/code5.txt @@ -0,0 +1,21 @@ +-- input -- +L1 +L2 +L3 +L4 +L5 +- L6 { + L7 +} +L8 +-- gofmt -- +L1 +L2 +L3 +L4 +L5 + - L6 { + L7 + } + +L8 diff --git a/src/go/doc/comment/testdata/code6.txt b/src/go/doc/comment/testdata/code6.txt new file mode 100644 index 0000000000..d2915d1068 --- /dev/null +++ b/src/go/doc/comment/testdata/code6.txt @@ -0,0 +1,24 @@ +-- input -- +Run this program: + +func main() { + fmt.Println("hello, world") +} + +Or this: + +go func() { + fmt.Println("hello, world") +}() +-- gofmt -- +Run this program: + + func main() { + fmt.Println("hello, world") + } + +Or this: + + go func() { + fmt.Println("hello, world") + }() diff --git a/src/go/doc/comment/testdata/list10.txt b/src/go/doc/comment/testdata/list10.txt new file mode 100644 index 0000000000..9c49083456 --- /dev/null +++ b/src/go/doc/comment/testdata/list10.txt @@ -0,0 +1,13 @@ +-- input -- + + 1. This list + 2. Starts the comment + 3. And also has a blank line before it. + +All of which is a little weird. +-- gofmt -- + 1. This list + 2. Starts the comment + 3. And also has a blank line before it. + +All of which is a little weird. diff --git a/src/go/doc/comment/testdata/list9.txt b/src/go/doc/comment/testdata/list9.txt new file mode 100644 index 0000000000..48e4673d54 --- /dev/null +++ b/src/go/doc/comment/testdata/list9.txt @@ -0,0 +1,30 @@ +-- input -- +Text. + +1. Not a list +2. because it is +3. unindented. + +4. This one + is a list + because of the indented text. +5. More wrapped + items. +6. And unwrapped. + +7. The blank line stops the heuristic. +-- gofmt -- +Text. + +1. Not a list +2. because it is +3. unindented. + + 4. This one + is a list + because of the indented text. + 5. More wrapped + items. + 6. And unwrapped. + +7. The blank line stops the heuristic. diff --git a/src/go/doc/comment/text.go b/src/go/doc/comment/text.go index 86e5eebe9a..6f9c2e201d 100644 --- a/src/go/doc/comment/text.go +++ b/src/go/doc/comment/text.go @@ -134,7 +134,6 @@ func (p *textPrinter) block(out *bytes.Buffer, x Block) { } // text prints the text sequence x to out. -// TODO: Wrap lines. func (p *textPrinter) text(out *bytes.Buffer, indent string, x []Text) { p.oneLongLine(&p.long, x) words := strings.Fields(p.long.String()) From a71ca3dfbd32faf351ff68bcc26a4d5abd9b06d7 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 26 Jan 2022 16:53:50 -0500 Subject: [PATCH 030/113] runtime, sync, sync/atomic: document happens-before guarantees A few of these are copied from the memory model doc. Many are entirely new, following discussion on #47141. See https://research.swtch.com/gomm for background. The rule we are establishing is that each type that is meant to help synchronize a Go program should document its happens-before guarantees. For #50859. Change-Id: I947c40639b263abe67499fa74f68711a97873a39 Reviewed-on: https://go-review.googlesource.com/c/go/+/381316 Auto-Submit: Russ Cox Run-TryBot: Russ Cox Reviewed-by: Ian Lance Taylor Reviewed-by: Alan Donovan TryBot-Result: Gopher Robot Reviewed-by: Roland Shoemaker --- src/runtime/mfinal.go | 14 +++++++++++++- src/sync/atomic/doc.go | 8 ++++++++ src/sync/cond.go | 8 +++++++- src/sync/map.go | 7 +++++++ src/sync/mutex.go | 7 +++++++ src/sync/once.go | 4 ++++ src/sync/pool.go | 5 +++++ src/sync/rwmutex.go | 8 ++++++++ src/sync/waitgroup.go | 3 +++ 9 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index 44174913de..f3f3a79fa5 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -321,11 +321,23 @@ func runfinq() { // closing p.d, causing syscall.Write to fail because it is writing to // a closed file descriptor (or, worse, to an entirely different // file descriptor opened by a different goroutine). To avoid this problem, -// call runtime.KeepAlive(p) after the call to syscall.Write. +// call KeepAlive(p) after the call to syscall.Write. // // A single goroutine runs all finalizers for a program, sequentially. // If a finalizer must run for a long time, it should do so by starting // a new goroutine. +// +// In the terminology of the Go memory model, a call +// SetFinalizer(x, f) “synchronizes before” the finalization call f(x). +// However, there is no guarantee that KeepAlive(x) or any other use of x +// “synchronizes before” f(x), so in general a finalizer should use a mutex +// or other synchronization mechanism if it needs to access mutable state in x. +// For example, consider a finalizer that inspects a mutable field in x +// that is modified from time to time in the main program before x +// becomes unreachable and the finalizer is invoked. +// The modifications in the main program and the inspection in the finalizer +// need to use appropriate synchronization, such as mutexes or atomic updates, +// to avoid read-write races. func SetFinalizer(obj any, finalizer any) { if debug.sbrk != 0 { // debug.sbrk never frees memory, so no finalizers run diff --git a/src/sync/atomic/doc.go b/src/sync/atomic/doc.go index bb3b8f673e..4d426826da 100644 --- a/src/sync/atomic/doc.go +++ b/src/sync/atomic/doc.go @@ -36,6 +36,14 @@ // The load and store operations, implemented by the LoadT and StoreT // functions, are the atomic equivalents of "return *addr" and // "*addr = val". +// +// In the terminology of the Go memory model, if the effect of +// an atomic operation A is observed by atomic operation B, +// then A “synchronizes before” B. +// Additionally, all the atomic operations executed in a program +// behave as though executed in some sequentially consistent order. +// This definition provides the same semantics as +// C++'s sequentially consistent atomics and Java's volatile variables. package atomic import ( diff --git a/src/sync/cond.go b/src/sync/cond.go index 841be96896..19f986e478 100644 --- a/src/sync/cond.go +++ b/src/sync/cond.go @@ -18,6 +18,10 @@ import ( // when calling the Wait method. // // A Cond must not be copied after first use. +// +// In the terminology of the Go memory model, Cond arranges that +// a call to Broadcast or Signal “synchronizes before” any Wait call +// that it unblocks. type Cond struct { noCopy noCopy @@ -85,11 +89,13 @@ func (c *copyChecker) check() { } } -// noCopy may be embedded into structs which must not be copied +// noCopy may be added to structs which must not be copied // after the first use. // // See https://golang.org/issues/8005#issuecomment-190753527 // for details. +// +// Note that it must not be embedded, due to the Lock and Unlock methods. type noCopy struct{} // Lock is a no-op used by -copylocks checker from `go vet`. diff --git a/src/sync/map.go b/src/sync/map.go index 2fa3253429..ec529e056b 100644 --- a/src/sync/map.go +++ b/src/sync/map.go @@ -24,6 +24,13 @@ import ( // contention compared to a Go map paired with a separate Mutex or RWMutex. // // The zero Map is empty and ready for use. A Map must not be copied after first use. +// +// In the terminology of the Go memory model, Map arranges that a write operation +// “synchronizes before” any read operation that observes the effect of the write, where +// read and write operations are defined as follows. +// Load, LoadAndDelete, LoadOrStore are read operations; +// Delete, LoadAndDelete, and Store are write operations; +// and LoadOrStore is a write operation when it returns loaded set to false. type Map struct { mu Mutex diff --git a/src/sync/mutex.go b/src/sync/mutex.go index 80bb827054..2ea024e585 100644 --- a/src/sync/mutex.go +++ b/src/sync/mutex.go @@ -24,6 +24,13 @@ func fatal(string) // The zero value for a Mutex is an unlocked mutex. // // A Mutex must not be copied after first use. +// +// In the terminology of the Go memory model, +// the n'th call to Unlock “synchronizes before” the m'th call to Lock +// for any n < m. +// A successful call to TryLock is equivalent to a call to Lock. +// A failed call to TryLock does not establish any “synchronizes before” +// relation at all. type Mutex struct { state int32 sema uint32 diff --git a/src/sync/once.go b/src/sync/once.go index 38373160b9..b6399cfc3d 100644 --- a/src/sync/once.go +++ b/src/sync/once.go @@ -11,6 +11,10 @@ import ( // Once is an object that will perform exactly one action. // // A Once must not be copied after first use. +// +// In the terminology of the Go memory model, +// the return from f “synchronizes before” +// the return from any call of once.Do(f). type Once struct { // done indicates whether the action has been performed. // It is first in the struct because it is used in the hot path. diff --git a/src/sync/pool.go b/src/sync/pool.go index ea142bb181..cf01e2e189 100644 --- a/src/sync/pool.go +++ b/src/sync/pool.go @@ -41,6 +41,11 @@ import ( // free list. // // A Pool must not be copied after first use. +// +// In the terminology of the Go memory model, a call to Put(x) “synchronizes before” +// a call to Get returning that same value x. +// Similarly, a call to New returning x “synchronizes before” +// a call to Get returning that same value x. type Pool struct { noCopy noCopy diff --git a/src/sync/rwmutex.go b/src/sync/rwmutex.go index 7b10808ec4..e914f3eba0 100644 --- a/src/sync/rwmutex.go +++ b/src/sync/rwmutex.go @@ -25,6 +25,14 @@ import ( // recursive read locking. This is to ensure that the lock eventually becomes // available; a blocked Lock call excludes new readers from acquiring the // lock. +// +// In the terminology of the Go memory model, +// the n'th call to Unlock “synchronizes before” the m'th call to Lock +// for any n < m, just as for Mutex. +// For any call to RLock, there exists an n such that +// the n'th call to Unlock “synchronizes before” that call to RLock, +// and the corresponding call to RUnlock “synchronizes before” +// the n+1'th call to Lock. type RWMutex struct { w Mutex // held if there are pending writers writerSem uint32 // semaphore for writers to wait for completing readers diff --git a/src/sync/waitgroup.go b/src/sync/waitgroup.go index 9c6662d04b..9f26ae106c 100644 --- a/src/sync/waitgroup.go +++ b/src/sync/waitgroup.go @@ -17,6 +17,9 @@ import ( // Wait can be used to block until all goroutines have finished. // // A WaitGroup must not be copied after first use. +// +// In the terminology of the Go memory model, a call to Done +// “synchronizes before” the return of any Wait call that it unblocks. type WaitGroup struct { noCopy noCopy From acfff428029130654d44b56e80a5e9c7e825e951 Mon Sep 17 00:00:00 2001 From: Michael Anthony Knyszek Date: Sat, 4 Jun 2022 05:42:30 +0000 Subject: [PATCH 031/113] doc/go1.19: add release notes for the soft memory limit and idle GC This change resolves some TODOs in the release notes, and while we're here, also clarifies how CPU profile samples are represented in runtime traces. Change-Id: Idaa36ccf65b03fd5463b2d5da682d3fa578d2f46 Reviewed-on: https://go-review.googlesource.com/c/go/+/410356 Reviewed-by: Michael Pratt Reviewed-by: Austin Clements --- doc/go1.19.html | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 9a689d1980..a932a717ba 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -82,12 +82,37 @@ Do not send CLs removing the interior tags from such phrases.

Runtime

-

- TODO: soft memory limit +

+ The runtime now includes support for a soft memory limit. This memory limit + includes all memory mapped and managed by the runtime, and excludes external + memory sources such as binary size, memory managed in other languages, and + memory held by the operating system on behalf of the Go program. This limit + may be managed via the GOMEMLIMIT environment variable or the + SetMemoryLimit function in the runtime/debug package. The limit + works in conjunction with GOGC and SetGCPercent, + and will be respected even if GOGC=off, allowing Go programs to + always make maximal use of their memory limit, improving resource efficiency + in some cases. Please note that small memory limits, on the order of tens of + megabytes or less, are less likely to be adhered to due to external latency + factors, such as OS scheduling. See https://go.dev/issue/52433 for more + details. Larger memory limits, on the order of hundreds of megabytes or more, + are stable and production-ready. +

+ +

+ In order to limit the effects of GC thrashing when the program's live heap + size approaches the soft memory limit, the Go runtime also attempts to limit + total GC CPU utilization to 50%, excluding idle time, choosing to use more + memory over preventing application progress. In practice, we expect this limit + to only play a role in exceptional cases, and the new runtime/metrics metric + /gc/limiter/last-enabled:gc-cycle reports when this last + occurred.

- TODO: idle mark workers + The runtime now schedules many fewer GC worker goroutines on idle operating + system threads when the application is idle enough to force a periodic GC + cycle.

@@ -494,7 +519,7 @@ Do not send CLs removing the interior tags from such phrases.

When used together with the CPU profiler, the - execution trace includes CPU profile samples. + execution trace includes CPU profile samples as instantaneous events.

From a79623b019b1ea3f4afb7e274c76c1e9936f0aa6 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 4 Jun 2022 14:33:41 -0400 Subject: [PATCH 032/113] doc/go1.19: add more TODOs from updated relnote CL 410244 changes relnote to look for api file changes as well as references to proposal issues, finding various things that were missing from the release notes. This CL adds the TODOs that the updated relnote found. For #51400. Change-Id: I512a9b8f1349a6c68c8a6979f55a07964d630175 Reviewed-on: https://go-review.googlesource.com/c/go/+/410361 Run-TryBot: Russ Cox Reviewed-by: Carlos Amedee Reviewed-by: Heschi Kreinick --- doc/go1.19.html | 224 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 201 insertions(+), 23 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index a932a717ba..db2bb91ae8 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -18,18 +18,34 @@ Do not send CLs removing the interior tags from such phrases. release notes. Go 1.19 is expected to be released in August 2022.

+

Changes to the language

TODO: complete this section

+ +

+ TODO: https://go.dev/issue/52038: adjust scope of type parameters declared by method receivers +

+

Ports

TODO: complete this section, or delete if not needed

+

Tools

TODO: complete this section, or delete if not needed

+

: + TODO: https://go.dev/issue/47528 warn when errors.As target has type *error +

+ +

Doc Comments

+

+ TODO: complete this section. +

+

Go command

TODO: complete this section. @@ -66,7 +82,7 @@ Do not send CLs removing the interior tags from such phrases.

New unix build constraint

-

+

The build constraint unix is now recognized in //go:build lines. The constraint is satisfied if the target operating system, also known as GOOS, is @@ -146,6 +162,10 @@ Do not send CLs removing the interior tags from such phrases. functionality.

+

+ TODO: https://go.dev/issue/44853: enable address sanitizer in Go +

+

Compiler

@@ -156,7 +176,7 @@ Do not send CLs removing the interior tags from such phrases. on the order of 20% faster. (GOARCH=amd64 and GOARCH=arm64 only)

-

+

TODO: https://go.dev/cl/402374: enable regabi on riscv64 by default

@@ -165,7 +185,7 @@ Do not send CLs removing the interior tags from such phrases. the go command and by Bazel. Any other build systems that invoke the Go compiler directly will need to make sure they pass this flag as well. -

+

TODO: complete this section, or delete if not needed

@@ -176,6 +196,14 @@ Do not send CLs removing the interior tags from such phrases.

Core library

+

+ TODO: https://go.dev/issue/51940: all: move dev.boringcrypto into main branch behind GOEXPERIMENT +

+ +

+ TODO: complete this section +

+

New atomic types

The sync/atomic package defines new atomic types @@ -197,13 +225,26 @@ Do not send CLs removing the interior tags from such phrases. atomics on these systems.

-

- TODO: https://go.dev/issue/51940: all: move dev.boringcrypto into main branch behind GOEXPERIMENT +

Doc comment parsing

+ +

+ TODO: https://go.dev/cl/384265: go/doc: use go/doc/comment; modified api/next/51082.txt + TODO: https://go.dev/cl/397276: go/doc/comment: add data structures; modified api/next/51082.txt + TODO: https://go.dev/cl/397278: go/doc/comment: add paragraph parsing and test framework; modified api/next/51082.txt + TODO: https://go.dev/cl/397279: go/doc/comment: add Printer and basic comment printing; modified api/next/51082.txt + TODO: https://go.dev/cl/397281: go/doc/comment: parse and print doc links; modified api/next/51082.txt + TODO: https://go.dev/cl/397284: go/doc/comment: parse and print headings; modified api/next/51082.txt

-

- TODO: complete this section +

PATH lookups

+ +

+ TODO: https://go.dev/issue/43724: return error when PATH lookup would use current directory

+

+ TODO: https://go.dev/issue/43947: on Windows use NeedCurrentDirectoryForExePathW for LookPath behavior +

+

Minor changes to the library

@@ -241,7 +282,7 @@ Do not send CLs removing the interior tags from such phrases.

crypto/tls
-

+

The tls10default GODEBUG option has been removed. It is still possible to enable TLS 1.0 client-side by setting Config.MinVersion. @@ -254,28 +295,78 @@ Do not send CLs removing the interior tags from such phrases.

TODO: https://go.dev/cl/285872: disable signing with MD5WithRSA

+ +

+ TODO: https://go.dev/issue/46057: add CertPool.Equal +

+ +

+ TODO: https://go.dev/issue/50674: add ParseRevocationList, deprecate ParseCRL & ParseDERCRL +

+ +

+ TODO: https://go.dev/cl/390834: crypto/x509: add new CRL parser, deprecate old one; modified api/next/50674.txt +

+ +

+ TODO: https://go.dev/cl/400175: crypto/x509: add CertPool.Clone; modified api/next/35044.txt + TODO: https://go.dev/issue/35044: add CertPool.Clone +

+
debug
+
+

+ TODO: https://go.dev/cl/396735: debug: define ELF relocation for loong64; modified api/next/46229.txt +

+
+
+ +
debug/pe
+
+

+ TODO: https://go.dev/issue/51868: add APIs to support reading COMDAT info for sections +

+ +

+ TODO: https://go.dev/cl/394534: debug/pe: add APIs for reading section def aux info; modified api/next/51868.txt +

+
+
+
encoding/binary
-

+

TODO: https://go.dev/cl/386017: add AppendByteOrder

+

+ TODO: https://go.dev/issue/51644: add AppendUvarint and AppendVarint +

+
encoding/csv
-

+

TODO: https://go.dev/cl/405675: add Reader.InputOffset method

+
encoding/xml
+
+

+ TODO: https://go.dev/issue/45628: add Decoder.InputPos + TODO: https://go.dev/cl/311270: encoding/xml: expose decoder line and column; modified api/next/45628.txt +

+
+
+
flag
-

+

TODO: https://go.dev/cl/313329: add TextVar function

@@ -283,7 +374,7 @@ Do not send CLs removing the interior tags from such phrases.
fmt
-

+

TODO: https://go.dev/cl/406177: add Append, Appendln, Appendf

@@ -299,7 +390,7 @@ Do not send CLs removing the interior tags from such phrases.
go/types
-

+

TODO: https://go.dev/cl/395535: add Var.Origin and Func.Origin

@@ -308,6 +399,25 @@ Do not send CLs removing the interior tags from such phrases.

+ +
hash/maphash
+
+

+ TODO: https://go.dev/cl/392494: hash/maphash: add Bytes and String; modified api/next/42710.txt + TODO: https://go.dev/issue/42710: add Bytes and String +

+
+
+ +
html/template
+
+

+ TODO: https://go.dev/issue/46121: make FuncMap an alias for text/template.FuncMap + TODO: https://go.dev/cl/389156: html/template: make FuncMap a type alias of text/template.FuncMap; modified api/except.txt, api/next/46121.txt +

+
+
+
image/draw

@@ -322,9 +432,13 @@ Do not send CLs removing the interior tags from such phrases.

io
-

+

TODO: https://go.dev/cl/400236: NopCloser forward WriterTo implementations if the reader supports it

+ +

+ TODO: https://go.dev/issue/50842: implement WriterTo on result of MultiReader +

@@ -358,7 +472,7 @@ Do not send CLs removing the interior tags from such phrases. issue tracker.

-

+

When a net package function or method returns an "I/O timeout" error, the error will now satisfy errors.Is(err, context.DeadlineExceeded). When a net package function @@ -369,9 +483,7 @@ Do not send CLs removing the interior tags from such phrases. package function or method to return an error, while preserving backward compatibility for error messages.

-
-
-

+

Resolver.PreferGo is now implemented on Windows and Plan 9. It previously only worked on Unix platforms. Combined with @@ -395,14 +507,22 @@ Do not send CLs removing the interior tags from such phrases.

TODO: https://go.dev/cl/269997: allow sending 1xx responses

+

+ TODO: https://go.dev/cl/361397: net/http: add MaxBytesError; modified api/next/30715.txt + TODO: https://go.dev/issue/30715: add MaxBytesError +

net/url
-

+

TODO: https://go.dev/cl/374654: add JoinPath, URL.JoinPath

+

+ TODO: https://go.dev/issue/46059: add OmitHost bool to URL +

+
@@ -416,8 +536,8 @@ 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. @@ -432,7 +552,7 @@ Do not send CLs removing the interior tags from such phrases.

reflect
-

+

The method Value.Bytes now accepts addressable arrays in addition to slices.

@@ -441,6 +561,25 @@ Do not send CLs removing the interior tags from such phrases.

+
regexp
+
+

+ TODO: https://go.dev/issue/51684: add ErrNestingDepth error + TODO: https://go.dev/cl/401076: regexp: change ErrInvalidDepth message to match proposal; modified api/next/51684.txt, api/next/regexpdepth.txt +

+
+
+ +
regexp/syntax
+
+

+ TODO: https://go.dev/cl/384617: regexp/syntax: add and use ErrInvalidDepth; modified api/next/regexpdepth.txt + TODO: https://go.dev/cl/401854: regexp/syntax: rename ErrInvalidDepth to ErrNestingDepth; modified api/next/51684.txt +

+
+
+ +
runtime

@@ -452,6 +591,14 @@ Do not send CLs removing the interior tags from such phrases.

+
runtime/debug
+
+

+ TODO: https://go.dev/cl/397018: runtime/debug: export SetMemoryLimit; modified api/next/48409.txt +

+
+
+
runtime/metrics

@@ -531,6 +678,11 @@ Do not send CLs removing the interior tags from such phrases. pattern-defeating quicksort, which is faster for several common scenarios.

+

+ TODO: https://go.dev/issue/50340: add Find + TODO: https://go.dev/cl/396514: sort: add Find function; modified api/next/50340.txt +

+
@@ -564,8 +716,34 @@ Do not send CLs removing the interior tags from such phrases.
time
-

+

TODO: https://go.dev/cl/393515: add Duration.Abs + TODO: https://go.dev/issue/51414: add Duration.Abs +

+

+ TODO: https://go.dev/issue/50062: add Time.ZoneBounds + TODO: https://go.dev/cl/405374: time: add Time.ZoneBounds; modified api/next/50062.txt

+ + + + + + + + + + + + + + + + + + + + + From 95b68e1e02fa713719f02f6c59fb1532bd05e824 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 4 Jun 2022 14:46:42 -0400 Subject: [PATCH 033/113] doc/go1.19: delete boringcrypto TODO Boringcrypto has never been officially supported and it remains unsupported. It need not be mentioned in the release notes. Change-Id: I24a08d424982615244d51c1d250035d85a602023 Reviewed-on: https://go-review.googlesource.com/c/go/+/410362 Run-TryBot: Russ Cox Reviewed-by: Carlos Amedee Reviewed-by: Heschi Kreinick --- doc/go1.19.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index db2bb91ae8..512db3981f 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -196,10 +196,6 @@ Do not send CLs removing the interior tags from such phrases.

Core library

-

- TODO: https://go.dev/issue/51940: all: move dev.boringcrypto into main branch behind GOEXPERIMENT -

-

TODO: complete this section

From 38607c553878da21b5042e63997ecb3b7201e684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Tue, 7 Jun 2022 05:55:40 +0000 Subject: [PATCH 034/113] cmd/link: specify -Wl,-z params as documented Both GNU and LLVM linkers de facto accept `-zPARAM`, and Go sometimes does it. Inconsistently: there are more uses of `-z PARAM` than `-zPARAM`: $ git grep -E -- '-Wl,-z[^,]' master | wc -l 4 $ git grep -E -- '-Wl,-z,' master | wc -l 7 However, not adding a space between `-z` and the param is not documented: llvm-13: $ man ld.lld-13 | grep -E -A1 -w -- "^ +-z" -z option Linker option extensions. gnu ld: $ man ld | grep -E -A1 -w -- "^ +-z" -z keyword The recognized keywords are: -- -z defs Report unresolved symbol references from regular object files. This is done even if the linker is creating a non-symbolic -- -z muldefs Normally when a symbol is defined multiple times, the linker will report a fatal error. These options allow multiple definitions -- -z --imagic ... and thus should be avoided. `zig cc`, when used as the C compiler (`CC="zig cc" go build ...`), will bark, because `zig cc` accepts only `-z PARAM`, as documented. Closes ziglang/zig#11669 Change-Id: I758054ecaa3ce01a72600bf65d7f7b5c3ec46d09 GitHub-Last-Rev: e068e007da9f2b0441ee0aa8b198a7ba3cd93ed3 GitHub-Pull-Request: golang/go#53030 Reviewed-on: https://go-review.googlesource.com/c/go/+/407834 TryBot-Result: Gopher Robot Reviewed-by: Alan Donovan Reviewed-by: Cherry Mui Run-TryBot: Cherry Mui --- src/cmd/link/internal/ld/lib.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 19678adbd5..9a5d89a6f7 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1463,12 +1463,12 @@ func (ctxt *Link) hostlink() { // We force all symbol resolution to be done at program startup // because lazy PLT resolution can use large amounts of stack at // times we cannot allow it to do so. - argv = append(argv, "-Wl,-znow") + argv = append(argv, "-Wl,-z,now") // Do not let the host linker generate COPY relocations. These // can move symbols out of sections that rely on stable offsets // from the beginning of the section (like sym.STYPE). - argv = append(argv, "-Wl,-znocopyreloc") + argv = append(argv, "-Wl,-z,nocopyreloc") if buildcfg.GOOS == "android" { // Use lld to avoid errors from default linker (issue #38838) From 77d9252ddfc6b3e2e48916240340ea5470b005a6 Mon Sep 17 00:00:00 2001 From: Khaled Yakdan Date: Mon, 23 May 2022 23:20:00 +0000 Subject: [PATCH 035/113] runtime: fix inline assembly trampoline for arm64 Use the program counter to compute the address of the first instruction of the ret sled. The ret sled is located after 5 instructions from the MOVD instruction saving the value of the program counter. Change-Id: Ie7ae7a0807785d6fea035cf7a770dba7f37de0ec GitHub-Last-Rev: 2719208c6a3b049e0f394e5311ce3282b58f8516 GitHub-Pull-Request: golang/go#53039 Reviewed-on: https://go-review.googlesource.com/c/go/+/407895 Reviewed-by: Cherry Mui Reviewed-by: Alan Donovan --- src/runtime/libfuzzer_arm64.s | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/runtime/libfuzzer_arm64.s b/src/runtime/libfuzzer_arm64.s index 9da94be03e..37b35173c3 100644 --- a/src/runtime/libfuzzer_arm64.s +++ b/src/runtime/libfuzzer_arm64.s @@ -43,8 +43,8 @@ TEXT runtime·libfuzzerCallTraceIntCmp(SB), NOSPLIT, $8-32 MOVD R12, RSP call: // Load address of the ret sled into the default register for the return - // address (offset of four instructions, which means 16 bytes). - ADR $16, R30 + // address. + ADR ret_sled, R30 // Clear the lowest 2 bits of fakePC. All ARM64 instructions are four // bytes long, so we cannot get better return address granularity than // multiples of 4. @@ -60,8 +60,9 @@ call: // has the same byte length of 4 * 128 = 512 as the x86_64 sled, but // coarser granularity. #define RET_SLED \ - JMP end_of_function; + JMP end_of_function; +ret_sled: REPEAT_128(RET_SLED); end_of_function: From d2630aa4b20b6ebd2a5a37628c2f1b9d7158621f Mon Sep 17 00:00:00 2001 From: Roland Shoemaker Date: Fri, 3 Jun 2022 11:48:43 -0700 Subject: [PATCH 036/113] doc/go1.19: add various crypto release notes For #51400 Change-Id: I908f53a54c6603e1bf2c9238cd51cf5c4a24407b Reviewed-on: https://go-review.googlesource.com/c/go/+/410295 Reviewed-by: Russ Cox Auto-Submit: Roland Shoemaker Run-TryBot: Roland Shoemaker TryBot-Result: Gopher Robot Reviewed-by: Tatiana Bradley --- doc/go1.19.html | 77 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 512db3981f..db2b1e1ab8 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -262,16 +262,14 @@ Do not send CLs removing the interior tags from such phrases.
crypto/rand
-

- TODO: https://go.dev/cl/370894: batch and buffer calls to getrandom/getentropy +

+ Read no longer buffers + random data obtained from the operating system between calls.

- TODO: https://go.dev/cl/375215: use fast key erasure RNG on plan9 instead of ANSI X9.31 -

- -

- TODO: https://go.dev/cl/390038: remove all buffering + On Plan 9, Read has been reimplemented, replacing the ANSI + X9.31 algorithm with fast key erasure.

@@ -281,7 +279,13 @@ Do not send CLs removing the interior tags from such phrases.

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

+ +

+ The TLS server and client now reject duplicate extensions in TLS + handshakes, as required by RFC 5246, Section 7.4.1.4 and RFC 8446, Section + 4.2.

@@ -289,7 +293,51 @@ Do not send CLs removing the interior tags from such phrases.
crypto/x509

- TODO: https://go.dev/cl/285872: disable signing with MD5WithRSA + CreateCertificate + no longer supports creating certificates with SignatureAlgorithm + set to MD5WithRSA. +

+ +

+ CreateCertificate no longer accepts negative serial numbers. +

+ +

+ ParseCertificate + and ParseCertificateRequest + now reject certificates and CSRs which contain duplicate extensions. +

+ +

+ The new CertPool.Clone + and CertPool.Equal + methods allow cloning a CertPool and checking the equality of two + CertPools respectively. +

+ +

+ The new function ParseRevocationList + provides a faster, safer to use CRL parser which returns a + RevocationList. + To support this addition, RevocationList adds new fields + RawIssuer, Signature, + AuthorityKeyId, and Extensions. + + The new method RevocationList.CheckSignatureFrom + checks that the signature on a CRL is a valid signature from a + Certificate. + + With the new CRL functionality, the existing functions + ParseCRL and + ParseDERCRL are deprecated. + Additionally the method Certificate.CheckCRLSignature + is deprecated. +

+ +

+ When building paths, Certificate.Verify + now considers certificates to be equal when the subjects, public keys, and SANs + are all equal. Before, it required byte-for-byte equality.

@@ -311,6 +359,17 @@ Do not send CLs removing the interior tags from such phrases.

+
crypto/x509/pkix
+
+

+ The types CertificateList and + TBSCertificateList + have been deprecated. The new crypto/x509 CRL functionality + should be used instead. +

+
+
+
debug

From 429a4041eb2657fad8870cad5662202f3bd0eeb6 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Tue, 7 Jun 2022 09:42:03 -0400 Subject: [PATCH 037/113] doc/go1.19: complete TODOs for go/types Fill in the details of outstanding TODO items for go/types changes. For #51400 Change-Id: Ib40d75fa1018aa164022cb49b293795dd597d49d Reviewed-on: https://go-review.googlesource.com/c/go/+/410815 Run-TryBot: Robert Findley Reviewed-by: Russ Cox TryBot-Result: Gopher Robot Reviewed-by: Robert Griesemer --- doc/go1.19.html | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index db2b1e1ab8..42494c203e 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -446,10 +446,19 @@ Do not send CLs removing the interior tags from such phrases.

go/types

- TODO: https://go.dev/cl/395535: add Var.Origin and Func.Origin + The new methods Func.Origin + and Var.Origin return the + corresponding Object of the + generic type for synthetic Func + and Var objects created during type + instantiation.

-

- TODO: https://go.dev/cl/404885: a finite number of types are reachable via Named.Underlying, Named.Method +

+ It is no longer possible to produce an infinite number of distinct-but-identical + Named type instantiations via + recursive calls to + Named.Underlying or + Named.Method.

From 835a94613703bb856ea4d16f05c116399c4b2fc9 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 6 Jun 2022 15:07:16 -0400 Subject: [PATCH 038/113] doc/go1.19: minor edits For #51400 Change-Id: I57565c1d79e0c5487d39d46f556b247d35f05d3c Reviewed-on: https://go-review.googlesource.com/c/go/+/410674 Reviewed-by: Cherry Mui Reviewed-by: Keith Randall --- doc/go1.19.html | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 42494c203e..62129160c5 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -705,22 +705,13 @@ Do not send CLs removing the interior tags from such phrases.

The race detector has been upgraded to use thread sanitizer - version v3. -

    -
  • - Faster (typically 1.5 to 2 times faster) -
  • -
  • - Uses less memory (typically 1/2 as much) -
  • -
  • - Supports unlimited numbers of goroutines -
  • -
+ version v3. Compared to v2, it is now typically 1.5x to 2x + faster, uses half as much memory, and it supports an unlimited + number of goroutines.

- The race detector is now supported on S390. + The race detector is now supported on GOARCH=s390x.

@@ -728,9 +719,10 @@ Do not send CLs removing the interior tags from such phrases.
runtime/trace

- When used together with the - CPU profiler, the - execution trace includes CPU profile samples as instantaneous events. + When tracing and the + CPU profiler are + enabled simultaneously, the execution trace includes CPU profile + samples as instantaneous events.

From 0c3a0543c2e4322ff3dccf6e2b82a7fbafaaabea Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 6 Jun 2022 17:05:49 -0400 Subject: [PATCH 039/113] doc/go1.19: compiler section is complete, modulo TODOs For #51400 Change-Id: I964e52e0a36e7bbe77175670e93ce8c99e7dab6d Reviewed-on: https://go-review.googlesource.com/c/go/+/410367 Reviewed-by: Matthew Dempsky Reviewed-by: Cherry Mui --- doc/go1.19.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 62129160c5..a39eaf79d5 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -185,10 +185,8 @@ Do not send CLs removing the interior tags from such phrases. the go command and by Bazel. Any other build systems that invoke the Go compiler directly will need to make sure they pass this flag as well. - -

- TODO: complete this section, or delete if not needed

+

Linker

TODO: complete this section, or delete if not needed From 81033fbd8e414447049e356af382fa6ecca072ea Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 7 Jun 2022 13:10:32 -0400 Subject: [PATCH 040/113] doc/go1.19: some platforms are still on TSAN v2 For #51400 Change-Id: Ie6d6ac773aa81b105e15ef7399374f574197d775 Reviewed-on: https://go-review.googlesource.com/c/go/+/410817 Reviewed-by: Cherry Mui --- doc/go1.19.html | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index a39eaf79d5..37983f93b6 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -703,14 +703,23 @@ Do not send CLs removing the interior tags from such phrases.

The race detector has been upgraded to use thread sanitizer - version v3. Compared to v2, it is now typically 1.5x to 2x - faster, uses half as much memory, and it supports an unlimited - number of goroutines. + version v3 on all supported platforms + except windows/amd64 + and openbsd/amd64, which remain on v2. + Compared to v2, it is now typically 1.5x to 2x faster, uses half + as much memory, and it supports an unlimited number of + goroutines.

The race detector is now supported on GOARCH=s390x.

+ +

+ Race detector support for openbsd/amd64 has been + removed from thread sanitizer upstream, so it is unlikely to + ever be updated from v2. +

From 69bb7c6ef540caa422e5d7fd1127dd71e3ca90fe Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Sat, 4 Jun 2022 03:44:18 -0400 Subject: [PATCH 041/113] sync/atomic: clarify that 8-byte alignment of variables is due to escape For #53223. Change-Id: I79e9b920488581a4d850e4051ee0dd600b5bbcb1 Reviewed-on: https://go-review.googlesource.com/c/go/+/410102 Reviewed-by: Michael Pratt Reviewed-by: Austin Clements --- src/sync/atomic/doc.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sync/atomic/doc.go b/src/sync/atomic/doc.go index 4d426826da..7977d13168 100644 --- a/src/sync/atomic/doc.go +++ b/src/sync/atomic/doc.go @@ -57,8 +57,9 @@ import ( // On ARM, 386, and 32-bit MIPS, it is the caller's responsibility to arrange // for 64-bit alignment of 64-bit words accessed atomically via the primitive // atomic functions (types Int64 and Uint64 are automatically aligned). -// The first word in a variable or in an allocated struct, array, or slice can -// be relied upon to be 64-bit aligned. +// The first word in an allocated struct, array, or slice; in a global +// variable; or in a local variable (because the subject of all atomic operations +// will escape to the heap) can be relied upon to be 64-bit aligned. // SwapInt32 atomically stores new into *addr and returns the previous *addr value. func SwapInt32(addr *int32, new int32) (old int32) From ef2567c7dd040f2cee87b8fd52885c6481deee35 Mon Sep 17 00:00:00 2001 From: David Chase Date: Tue, 7 Jun 2022 12:28:54 -0400 Subject: [PATCH 042/113] doc/go1.19: document loong64 port Updates #46229 For #51400 Change-Id: Iedd5d3c4cd656b59ba2e1fe813851830849a8614 Reviewed-on: https://go-review.googlesource.com/c/go/+/410816 TryBot-Result: Gopher Robot Reviewed-by: Cherry Mui Run-TryBot: David Chase Reviewed-by: Austin Clements --- doc/go1.19.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/go1.19.html b/doc/go1.19.html index 37983f93b6..037ea4fec6 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -32,6 +32,9 @@ Do not send CLs removing the interior tags from such phrases.

TODO: complete this section, or delete if not needed

+

+ Go 1.19 supports the Loongson 64-bit architecture LoongArch on Linux (GOOS=linux, GOARCH=loong64). +

Tools

From f3e051a184ddd060f1e17200b0081648411fa073 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Mon, 6 Jun 2022 17:20:44 -0400 Subject: [PATCH 043/113] runtime: document GOMEMLIMIT in environment variables section For #48409. Change-Id: Ia6616a377bc4c871b7ffba6f5a59792a09b64809 Reviewed-on: https://go-review.googlesource.com/c/go/+/410734 Run-TryBot: Michael Pratt TryBot-Result: Gopher Robot Reviewed-by: Chris Hines Reviewed-by: Russ Cox --- src/runtime/extern.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/runtime/extern.go b/src/runtime/extern.go index 54378885dc..15c519d233 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -18,8 +18,19 @@ The GOGC variable sets the initial garbage collection target percentage. A collection is triggered when the ratio of freshly allocated data to live data remaining after the previous collection reaches this percentage. The default is GOGC=100. Setting GOGC=off disables the garbage collector entirely. -The runtime/debug package's SetGCPercent function allows changing this -percentage at run time. See https://golang.org/pkg/runtime/debug/#SetGCPercent. +[runtime/debug.SetGCPercent] allows changing this percentage at run time. + +The GOMEMLIMIT variable sets a soft memory limit for the runtime. This memory limit +includes the Go heap and all other memory managed by the runtime, and excludes +external memory sources such as mappings of the binary itself, memory managed in +other languages, and memory held by the operating system on behalf of the Go +program. GOMEMLIMIT is a numeric value in bytes with an optional unit suffix. +The supported suffixes include B, KiB, MiB, GiB, and TiB. These suffixes +represent quantities of bytes as defined by the IEC 80000-13 standard. That is, +they are based on powers of two: KiB means 2^10 bytes, MiB means 2^20 bytes, +and so on. The default setting is math.MaxInt64, which effectively disables the +memory limit. [runtime/debug.SetMemoryLimit] allows changing this limit at run +time. The GODEBUG variable controls debugging variables within the runtime. It is a comma-separated list of name=val pairs setting these named variables: From 7a82c6859f14d97b741073083849bace7693f7e2 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Mon, 6 Jun 2022 17:32:03 -0400 Subject: [PATCH 044/113] doc/go1.19: adjust runtime release notes This addresses comments from CL 410356. For #48409. For #51400. Change-Id: I03560e820a06c0745700ac997b02d13bc03adfc6 Reviewed-on: https://go-review.googlesource.com/c/go/+/410735 Run-TryBot: Michael Pratt TryBot-Result: Gopher Robot Reviewed-by: Chris Hines Reviewed-by: Russ Cox --- doc/go1.19.html | 53 +++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 037ea4fec6..2674ad6972 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -103,19 +103,26 @@ Do not send CLs removing the interior tags from such phrases.

The runtime now includes support for a soft memory limit. This memory limit - includes all memory mapped and managed by the runtime, and excludes external - memory sources such as binary size, memory managed in other languages, and - memory held by the operating system on behalf of the Go program. This limit - may be managed via the GOMEMLIMIT environment variable or the - SetMemoryLimit function in the runtime/debug package. The limit - works in conjunction with GOGC and SetGCPercent, + includes the Go heap and all other memory managed by the runtime, and + excludes external memory sources such as mappings of the binary itself, + memory managed in other languages, and memory held by the operating system on + behalf of the Go program. This limit may be managed via + runtime/debug.SetMemoryLimit + or the equivalent + GOMEMLIMIT + environment variable. The limit works in conjunction with + runtime/debug.SetGCPercent + / GOGC, and will be respected even if GOGC=off, allowing Go programs to always make maximal use of their memory limit, improving resource efficiency - in some cases. Please note that small memory limits, on the order of tens of - megabytes or less, are less likely to be adhered to due to external latency - factors, such as OS scheduling. See https://go.dev/issue/52433 for more - details. Larger memory limits, on the order of hundreds of megabytes or more, - are stable and production-ready. + in some cases. See the GC guide for + a detailed guide explaining the soft memory limit in more detail, as well as + a variety of common use-cases and scenarios. Please note that small memory + limits, on the order of tens of megabytes or less, are less likely to be + respected due to external latency factors, such as OS scheduling. See + issue 52433 for more details. Larger + memory limits, on the order of hundreds of megabytes or more, are stable and + production-ready.

@@ -123,7 +130,8 @@ Do not send CLs removing the interior tags from such phrases. size approaches the soft memory limit, the Go runtime also attempts to limit total GC CPU utilization to 50%, excluding idle time, choosing to use more memory over preventing application progress. In practice, we expect this limit - to only play a role in exceptional cases, and the new runtime/metrics metric + to only play a role in exceptional cases, and the new + runtime metric /gc/limiter/last-enabled:gc-cycle reports when this last occurred.

@@ -667,20 +675,27 @@ Do not send CLs removing the interior tags from such phrases.
runtime/metrics

- The new /sched/gomaxprocs:threads metric reports the current - runtime.GOMAXPROCS value. + The new /sched/gomaxprocs:threads + metric reports + the current + runtime.GOMAXPROCS + value.

- The new /cgo/go-to-c-calls:calls metric reports the total - number of calls made from Go to C. This metric is identical to the runtime.NumCgoCall + The new /cgo/go-to-c-calls:calls + metric + reports the total number of calls made from Go to C. This metric is + identical to the + runtime.NumCgoCall function.

- The new /gc/limiter/last-enabled:gc-cycle metric reports the - last GC cycle when the GC CPU limiter was enabled. + The new /gc/limiter/last-enabled:gc-cycle + metric + reports the last GC cycle when the GC CPU limiter was enabled. See the + runtime notes for details about the GC CPU limiter.

From 346698eea71139280e3b3380554371b5d332ce02 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Thu, 26 May 2022 15:09:34 -0700 Subject: [PATCH 045/113] doc/go1.19: add release notes for net/http and net/url For #51400 Change-Id: I6412132db79074eef7d2cb3d66456c48b0d745a3 Reviewed-on: https://go-review.googlesource.com/c/go/+/408877 TryBot-Result: Gopher Robot Run-TryBot: Damien Neil Reviewed-by: Russ Cox Reviewed-by: Carlos Amedee --- doc/go1.19.html | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 2674ad6972..b8d372224a 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -578,22 +578,46 @@ Do not send CLs removing the interior tags from such phrases.
net/http

- TODO: https://go.dev/cl/269997: allow sending 1xx responses + ResponseWriter.WriteHeader + now supports sending user-defined 1xx informational headers.

-

- TODO: https://go.dev/cl/361397: net/http: add MaxBytesError; modified api/next/30715.txt - TODO: https://go.dev/issue/30715: add MaxBytesError + +

+ The io.ReadCloser returned by + MaxBytesReader + will now return the defined error type + MaxBytesError + when its read limit is exceeded. +

+ +

+ The HTTP client will handle a 3xx response without a + Location header by returning it to the caller, + rather than treating it as an error.

net/url
-

- TODO: https://go.dev/cl/374654: add JoinPath, URL.JoinPath +

+ The new + JoinPath + function and + URL.JoinPath + method create a new URL by joining a list of path + elements.

- TODO: https://go.dev/issue/46059: add OmitHost bool to URL + The URL type now distinguishes between URLs with no + authority and URLs with an empty authority. For example, + http:///path has an empty authority (host), + while http:/path has none. +

+

+ The new URL field + OmitHost is set to true when a + URL has an empty authority.

From d4fb93be87c38aaf0f68ad91852f9f83be726262 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 7 Jun 2022 10:33:01 -0700 Subject: [PATCH 046/113] =?UTF-8?q?go/types,=20types2:=20use=20|=20rather?= =?UTF-8?q?=20than=20=E2=88=AA=20when=20printing=20term=20lists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change, the termlist String() function prints termlists in the usual Go notation and thus we can use it in error reporting. Preparation for fixing #40350. For #40350. Change-Id: Ia28318841305de234a71af3146ce0c59f5e601a5 Reviewed-on: https://go-review.googlesource.com/c/go/+/410894 Reviewed-by: Robert Griesemer Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/termlist.go | 2 +- .../compile/internal/types2/termlist_test.go | 140 +++++++++--------- .../compile/internal/types2/typeset_test.go | 6 +- src/go/types/termlist.go | 2 +- src/go/types/termlist_test.go | 140 +++++++++--------- src/go/types/typeset_test.go | 6 +- 6 files changed, 148 insertions(+), 148 deletions(-) diff --git a/src/cmd/compile/internal/types2/termlist.go b/src/cmd/compile/internal/types2/termlist.go index a0108c4638..43e43ce87c 100644 --- a/src/cmd/compile/internal/types2/termlist.go +++ b/src/cmd/compile/internal/types2/termlist.go @@ -25,7 +25,7 @@ func (xl termlist) String() string { var buf bytes.Buffer for i, x := range xl { if i > 0 { - buf.WriteString(" ∪ ") + buf.WriteString(" | ") } buf.WriteString(x.String()) } diff --git a/src/cmd/compile/internal/types2/termlist_test.go b/src/cmd/compile/internal/types2/termlist_test.go index d1e3bdf88e..3005d0edea 100644 --- a/src/cmd/compile/internal/types2/termlist_test.go +++ b/src/cmd/compile/internal/types2/termlist_test.go @@ -12,7 +12,7 @@ import ( // maketl makes a term list from a string of the term list. func maketl(s string) termlist { s = strings.ReplaceAll(s, " ", "") - names := strings.Split(s, "∪") + names := strings.Split(s, "|") r := make(termlist, len(names)) for i, n := range names { r[i] = testTerm(n) @@ -33,10 +33,10 @@ func TestTermlistString(t *testing.T) { "int", "~int", "myInt", - "∅ ∪ ∅", - "𝓤 ∪ 𝓤", - "∅ ∪ 𝓤 ∪ int", - "∅ ∪ 𝓤 ∪ int ∪ myInt", + "∅ | ∅", + "𝓤 | 𝓤", + "∅ | 𝓤 | int", + "∅ | 𝓤 | int | myInt", } { if got := maketl(want).String(); got != want { t.Errorf("(%v).String() == %v", want, got) @@ -47,12 +47,12 @@ func TestTermlistString(t *testing.T) { func TestTermlistIsEmpty(t *testing.T) { for test, want := range map[string]bool{ "∅": true, - "∅ ∪ ∅": true, - "∅ ∪ ∅ ∪ 𝓤": false, - "∅ ∪ ∅ ∪ myInt": false, + "∅ | ∅": true, + "∅ | ∅ | 𝓤": false, + "∅ | ∅ | myInt": false, "𝓤": false, - "𝓤 ∪ int": false, - "𝓤 ∪ myInt ∪ ∅": false, + "𝓤 | int": false, + "𝓤 | myInt | ∅": false, } { xl := maketl(test) got := xl.isEmpty() @@ -65,13 +65,13 @@ func TestTermlistIsEmpty(t *testing.T) { func TestTermlistIsAll(t *testing.T) { for test, want := range map[string]bool{ "∅": false, - "∅ ∪ ∅": false, - "int ∪ ~string": false, - "~int ∪ myInt": false, - "∅ ∪ ∅ ∪ 𝓤": true, + "∅ | ∅": false, + "int | ~string": false, + "~int | myInt": false, + "∅ | ∅ | 𝓤": true, "𝓤": true, - "𝓤 ∪ int": true, - "myInt ∪ 𝓤": true, + "𝓤 | int": true, + "myInt | 𝓤": true, } { xl := maketl(test) got := xl.isAll() @@ -86,17 +86,17 @@ func TestTermlistNorm(t *testing.T) { xl, want string }{ {"∅", "∅"}, - {"∅ ∪ ∅", "∅"}, - {"∅ ∪ int", "int"}, - {"∅ ∪ myInt", "myInt"}, - {"𝓤 ∪ int", "𝓤"}, - {"𝓤 ∪ myInt", "𝓤"}, - {"int ∪ myInt", "int ∪ myInt"}, - {"~int ∪ int", "~int"}, - {"~int ∪ myInt", "~int"}, - {"int ∪ ~string ∪ int", "int ∪ ~string"}, - {"~int ∪ string ∪ 𝓤 ∪ ~string ∪ int", "𝓤"}, - {"~int ∪ string ∪ myInt ∪ ~string ∪ int", "~int ∪ ~string"}, + {"∅ | ∅", "∅"}, + {"∅ | int", "int"}, + {"∅ | myInt", "myInt"}, + {"𝓤 | int", "𝓤"}, + {"𝓤 | myInt", "𝓤"}, + {"int | myInt", "int | myInt"}, + {"~int | int", "~int"}, + {"~int | myInt", "~int"}, + {"int | ~string | int", "int | ~string"}, + {"~int | string | 𝓤 | ~string | int", "𝓤"}, + {"~int | string | myInt | ~string | int", "~int | ~string"}, } { xl := maketl(test.xl) got := maketl(test.xl).norm() @@ -116,15 +116,15 @@ func TestTermlistUnion(t *testing.T) { {"∅", "int", "int"}, {"𝓤", "~int", "𝓤"}, {"int", "~int", "~int"}, - {"int", "string", "int ∪ string"}, - {"int", "myInt", "int ∪ myInt"}, + {"int", "string", "int | string"}, + {"int", "myInt", "int | myInt"}, {"~int", "myInt", "~int"}, - {"int ∪ string", "~string", "int ∪ ~string"}, - {"~int ∪ string", "~string ∪ int", "~int ∪ ~string"}, - {"~int ∪ string ∪ ∅", "~string ∪ int", "~int ∪ ~string"}, - {"~int ∪ myInt ∪ ∅", "~string ∪ int", "~int ∪ ~string"}, - {"~int ∪ string ∪ 𝓤", "~string ∪ int", "𝓤"}, - {"~int ∪ string ∪ myInt", "~string ∪ int", "~int ∪ ~string"}, + {"int | string", "~string", "int | ~string"}, + {"~int | string", "~string | int", "~int | ~string"}, + {"~int | string | ∅", "~string | int", "~int | ~string"}, + {"~int | myInt | ∅", "~string | int", "~int | ~string"}, + {"~int | string | 𝓤", "~string | int", "𝓤"}, + {"~int | string | myInt", "~string | int", "~int | ~string"}, } { xl := maketl(test.xl) yl := maketl(test.yl) @@ -150,12 +150,12 @@ func TestTermlistIntersect(t *testing.T) { {"int", "string", "∅"}, {"int", "myInt", "∅"}, {"~int", "myInt", "myInt"}, - {"int ∪ string", "~string", "string"}, - {"~int ∪ string", "~string ∪ int", "int ∪ string"}, - {"~int ∪ string ∪ ∅", "~string ∪ int", "int ∪ string"}, - {"~int ∪ myInt ∪ ∅", "~string ∪ int", "int"}, - {"~int ∪ string ∪ 𝓤", "~string ∪ int", "int ∪ ~string"}, - {"~int ∪ string ∪ myInt", "~string ∪ int", "int ∪ string"}, + {"int | string", "~string", "string"}, + {"~int | string", "~string | int", "int | string"}, + {"~int | string | ∅", "~string | int", "int | string"}, + {"~int | myInt | ∅", "~string | int", "int"}, + {"~int | string | 𝓤", "~string | int", "int | ~string"}, + {"~int | string | myInt", "~string | int", "int | string"}, } { xl := maketl(test.xl) yl := maketl(test.yl) @@ -174,12 +174,12 @@ func TestTermlistEqual(t *testing.T) { {"∅", "∅", true}, {"∅", "𝓤", false}, {"𝓤", "𝓤", true}, - {"𝓤 ∪ int", "𝓤", true}, - {"𝓤 ∪ int", "string ∪ 𝓤", true}, - {"𝓤 ∪ myInt", "string ∪ 𝓤", true}, - {"int ∪ ~string", "string ∪ int", false}, - {"~int ∪ string", "string ∪ myInt", false}, - {"int ∪ ~string ∪ ∅", "string ∪ int ∪ ~string", true}, + {"𝓤 | int", "𝓤", true}, + {"𝓤 | int", "string | 𝓤", true}, + {"𝓤 | myInt", "string | 𝓤", true}, + {"int | ~string", "string | int", false}, + {"~int | string", "string | myInt", false}, + {"int | ~string | ∅", "string | int | ~string", true}, } { xl := maketl(test.xl) yl := maketl(test.yl) @@ -201,11 +201,11 @@ func TestTermlistIncludes(t *testing.T) { {"int", "string", false}, {"~int", "string", false}, {"~int", "myInt", true}, - {"int ∪ string", "string", true}, - {"~int ∪ string", "int", true}, - {"~int ∪ string", "myInt", true}, - {"~int ∪ myInt ∪ ∅", "myInt", true}, - {"myInt ∪ ∅ ∪ 𝓤", "int", true}, + {"int | string", "string", true}, + {"~int | string", "int", true}, + {"~int | string", "myInt", true}, + {"~int | myInt | ∅", "myInt", true}, + {"myInt | ∅ | 𝓤", "int", true}, } { xl := maketl(test.xl) yl := testTerm(test.typ).typ @@ -236,12 +236,12 @@ func TestTermlistSupersetOf(t *testing.T) { {"myInt", "~int", false}, {"int", "string", false}, {"~int", "string", false}, - {"int ∪ string", "string", true}, - {"int ∪ string", "~string", false}, - {"~int ∪ string", "int", true}, - {"~int ∪ string", "myInt", true}, - {"~int ∪ string ∪ ∅", "string", true}, - {"~string ∪ ∅ ∪ 𝓤", "myInt", true}, + {"int | string", "string", true}, + {"int | string", "~string", false}, + {"~int | string", "int", true}, + {"~int | string", "myInt", true}, + {"~int | string | ∅", "string", true}, + {"~string | ∅ | 𝓤", "myInt", true}, } { xl := maketl(test.xl) y := testTerm(test.typ) @@ -261,18 +261,18 @@ func TestTermlistSubsetOf(t *testing.T) { {"∅", "𝓤", true}, {"𝓤", "∅", false}, {"𝓤", "𝓤", true}, - {"int", "int ∪ string", true}, - {"~int", "int ∪ string", false}, - {"~int", "myInt ∪ string", false}, - {"myInt", "~int ∪ string", true}, - {"~int", "string ∪ string ∪ int ∪ ~int", true}, - {"myInt", "string ∪ string ∪ ~int", true}, - {"int ∪ string", "string", false}, - {"int ∪ string", "string ∪ int", true}, - {"int ∪ ~string", "string ∪ int", false}, - {"myInt ∪ ~string", "string ∪ int ∪ 𝓤", true}, - {"int ∪ ~string", "string ∪ int ∪ ∅ ∪ string", false}, - {"int ∪ myInt", "string ∪ ~int ∪ ∅ ∪ string", true}, + {"int", "int | string", true}, + {"~int", "int | string", false}, + {"~int", "myInt | string", false}, + {"myInt", "~int | string", true}, + {"~int", "string | string | int | ~int", true}, + {"myInt", "string | string | ~int", true}, + {"int | string", "string", false}, + {"int | string", "string | int", true}, + {"int | ~string", "string | int", false}, + {"myInt | ~string", "string | int | 𝓤", true}, + {"int | ~string", "string | int | ∅ | string", false}, + {"int | myInt", "string | ~int | ∅ | string", true}, } { xl := maketl(test.xl) yl := maketl(test.yl) diff --git a/src/cmd/compile/internal/types2/typeset_test.go b/src/cmd/compile/internal/types2/typeset_test.go index 69eaff741f..40ca28e525 100644 --- a/src/cmd/compile/internal/types2/typeset_test.go +++ b/src/cmd/compile/internal/types2/typeset_test.go @@ -21,13 +21,13 @@ func TestTypeSetString(t *testing.T) { "{}": "𝓤", "{int}": "{int}", "{~int}": "{~int}", - "{int|string}": "{int ∪ string}", + "{int|string}": "{int | string}", "{int; string}": "∅", "{comparable}": "{comparable}", "{comparable; int}": "{int}", "{~int; comparable}": "{~int}", - "{int|string; comparable}": "{int ∪ string}", + "{int|string; comparable}": "{int | string}", "{comparable; int; string}": "∅", "{m()}": "{func (p.T).m()}", @@ -37,7 +37,7 @@ func TestTypeSetString(t *testing.T) { "{m1(); comparable; m2() int }": "{comparable; func (p.T).m1(); func (p.T).m2() int}", "{comparable; error}": "{comparable; func (error).Error() string}", - "{m(); comparable; int|float32|string}": "{func (p.T).m(); int ∪ float32 ∪ string}", + "{m(); comparable; int|float32|string}": "{func (p.T).m(); int | float32 | string}", "{m1(); int; m2(); comparable }": "{func (p.T).m1(); func (p.T).m2(); int}", "{E}; type E interface{}": "𝓤", diff --git a/src/go/types/termlist.go b/src/go/types/termlist.go index 94e49caee0..6d08ddb397 100644 --- a/src/go/types/termlist.go +++ b/src/go/types/termlist.go @@ -25,7 +25,7 @@ func (xl termlist) String() string { var buf bytes.Buffer for i, x := range xl { if i > 0 { - buf.WriteString(" ∪ ") + buf.WriteString(" | ") } buf.WriteString(x.String()) } diff --git a/src/go/types/termlist_test.go b/src/go/types/termlist_test.go index f0d58ac1bc..0ff687ebda 100644 --- a/src/go/types/termlist_test.go +++ b/src/go/types/termlist_test.go @@ -12,7 +12,7 @@ import ( // maketl makes a term list from a string of the term list. func maketl(s string) termlist { s = strings.ReplaceAll(s, " ", "") - names := strings.Split(s, "∪") + names := strings.Split(s, "|") r := make(termlist, len(names)) for i, n := range names { r[i] = testTerm(n) @@ -33,10 +33,10 @@ func TestTermlistString(t *testing.T) { "int", "~int", "myInt", - "∅ ∪ ∅", - "𝓤 ∪ 𝓤", - "∅ ∪ 𝓤 ∪ int", - "∅ ∪ 𝓤 ∪ int ∪ myInt", + "∅ | ∅", + "𝓤 | 𝓤", + "∅ | 𝓤 | int", + "∅ | 𝓤 | int | myInt", } { if got := maketl(want).String(); got != want { t.Errorf("(%v).String() == %v", want, got) @@ -47,12 +47,12 @@ func TestTermlistString(t *testing.T) { func TestTermlistIsEmpty(t *testing.T) { for test, want := range map[string]bool{ "∅": true, - "∅ ∪ ∅": true, - "∅ ∪ ∅ ∪ 𝓤": false, - "∅ ∪ ∅ ∪ myInt": false, + "∅ | ∅": true, + "∅ | ∅ | 𝓤": false, + "∅ | ∅ | myInt": false, "𝓤": false, - "𝓤 ∪ int": false, - "𝓤 ∪ myInt ∪ ∅": false, + "𝓤 | int": false, + "𝓤 | myInt | ∅": false, } { xl := maketl(test) got := xl.isEmpty() @@ -65,13 +65,13 @@ func TestTermlistIsEmpty(t *testing.T) { func TestTermlistIsAll(t *testing.T) { for test, want := range map[string]bool{ "∅": false, - "∅ ∪ ∅": false, - "int ∪ ~string": false, - "~int ∪ myInt": false, - "∅ ∪ ∅ ∪ 𝓤": true, + "∅ | ∅": false, + "int | ~string": false, + "~int | myInt": false, + "∅ | ∅ | 𝓤": true, "𝓤": true, - "𝓤 ∪ int": true, - "myInt ∪ 𝓤": true, + "𝓤 | int": true, + "myInt | 𝓤": true, } { xl := maketl(test) got := xl.isAll() @@ -86,17 +86,17 @@ func TestTermlistNorm(t *testing.T) { xl, want string }{ {"∅", "∅"}, - {"∅ ∪ ∅", "∅"}, - {"∅ ∪ int", "int"}, - {"∅ ∪ myInt", "myInt"}, - {"𝓤 ∪ int", "𝓤"}, - {"𝓤 ∪ myInt", "𝓤"}, - {"int ∪ myInt", "int ∪ myInt"}, - {"~int ∪ int", "~int"}, - {"~int ∪ myInt", "~int"}, - {"int ∪ ~string ∪ int", "int ∪ ~string"}, - {"~int ∪ string ∪ 𝓤 ∪ ~string ∪ int", "𝓤"}, - {"~int ∪ string ∪ myInt ∪ ~string ∪ int", "~int ∪ ~string"}, + {"∅ | ∅", "∅"}, + {"∅ | int", "int"}, + {"∅ | myInt", "myInt"}, + {"𝓤 | int", "𝓤"}, + {"𝓤 | myInt", "𝓤"}, + {"int | myInt", "int | myInt"}, + {"~int | int", "~int"}, + {"~int | myInt", "~int"}, + {"int | ~string | int", "int | ~string"}, + {"~int | string | 𝓤 | ~string | int", "𝓤"}, + {"~int | string | myInt | ~string | int", "~int | ~string"}, } { xl := maketl(test.xl) got := maketl(test.xl).norm() @@ -116,15 +116,15 @@ func TestTermlistUnion(t *testing.T) { {"∅", "int", "int"}, {"𝓤", "~int", "𝓤"}, {"int", "~int", "~int"}, - {"int", "string", "int ∪ string"}, - {"int", "myInt", "int ∪ myInt"}, + {"int", "string", "int | string"}, + {"int", "myInt", "int | myInt"}, {"~int", "myInt", "~int"}, - {"int ∪ string", "~string", "int ∪ ~string"}, - {"~int ∪ string", "~string ∪ int", "~int ∪ ~string"}, - {"~int ∪ string ∪ ∅", "~string ∪ int", "~int ∪ ~string"}, - {"~int ∪ myInt ∪ ∅", "~string ∪ int", "~int ∪ ~string"}, - {"~int ∪ string ∪ 𝓤", "~string ∪ int", "𝓤"}, - {"~int ∪ string ∪ myInt", "~string ∪ int", "~int ∪ ~string"}, + {"int | string", "~string", "int | ~string"}, + {"~int | string", "~string | int", "~int | ~string"}, + {"~int | string | ∅", "~string | int", "~int | ~string"}, + {"~int | myInt | ∅", "~string | int", "~int | ~string"}, + {"~int | string | 𝓤", "~string | int", "𝓤"}, + {"~int | string | myInt", "~string | int", "~int | ~string"}, } { xl := maketl(test.xl) yl := maketl(test.yl) @@ -150,12 +150,12 @@ func TestTermlistIntersect(t *testing.T) { {"int", "string", "∅"}, {"int", "myInt", "∅"}, {"~int", "myInt", "myInt"}, - {"int ∪ string", "~string", "string"}, - {"~int ∪ string", "~string ∪ int", "int ∪ string"}, - {"~int ∪ string ∪ ∅", "~string ∪ int", "int ∪ string"}, - {"~int ∪ myInt ∪ ∅", "~string ∪ int", "int"}, - {"~int ∪ string ∪ 𝓤", "~string ∪ int", "int ∪ ~string"}, - {"~int ∪ string ∪ myInt", "~string ∪ int", "int ∪ string"}, + {"int | string", "~string", "string"}, + {"~int | string", "~string | int", "int | string"}, + {"~int | string | ∅", "~string | int", "int | string"}, + {"~int | myInt | ∅", "~string | int", "int"}, + {"~int | string | 𝓤", "~string | int", "int | ~string"}, + {"~int | string | myInt", "~string | int", "int | string"}, } { xl := maketl(test.xl) yl := maketl(test.yl) @@ -174,12 +174,12 @@ func TestTermlistEqual(t *testing.T) { {"∅", "∅", true}, {"∅", "𝓤", false}, {"𝓤", "𝓤", true}, - {"𝓤 ∪ int", "𝓤", true}, - {"𝓤 ∪ int", "string ∪ 𝓤", true}, - {"𝓤 ∪ myInt", "string ∪ 𝓤", true}, - {"int ∪ ~string", "string ∪ int", false}, - {"~int ∪ string", "string ∪ myInt", false}, - {"int ∪ ~string ∪ ∅", "string ∪ int ∪ ~string", true}, + {"𝓤 | int", "𝓤", true}, + {"𝓤 | int", "string | 𝓤", true}, + {"𝓤 | myInt", "string | 𝓤", true}, + {"int | ~string", "string | int", false}, + {"~int | string", "string | myInt", false}, + {"int | ~string | ∅", "string | int | ~string", true}, } { xl := maketl(test.xl) yl := maketl(test.yl) @@ -201,11 +201,11 @@ func TestTermlistIncludes(t *testing.T) { {"int", "string", false}, {"~int", "string", false}, {"~int", "myInt", true}, - {"int ∪ string", "string", true}, - {"~int ∪ string", "int", true}, - {"~int ∪ string", "myInt", true}, - {"~int ∪ myInt ∪ ∅", "myInt", true}, - {"myInt ∪ ∅ ∪ 𝓤", "int", true}, + {"int | string", "string", true}, + {"~int | string", "int", true}, + {"~int | string", "myInt", true}, + {"~int | myInt | ∅", "myInt", true}, + {"myInt | ∅ | 𝓤", "int", true}, } { xl := maketl(test.xl) yl := testTerm(test.typ).typ @@ -236,12 +236,12 @@ func TestTermlistSupersetOf(t *testing.T) { {"myInt", "~int", false}, {"int", "string", false}, {"~int", "string", false}, - {"int ∪ string", "string", true}, - {"int ∪ string", "~string", false}, - {"~int ∪ string", "int", true}, - {"~int ∪ string", "myInt", true}, - {"~int ∪ string ∪ ∅", "string", true}, - {"~string ∪ ∅ ∪ 𝓤", "myInt", true}, + {"int | string", "string", true}, + {"int | string", "~string", false}, + {"~int | string", "int", true}, + {"~int | string", "myInt", true}, + {"~int | string | ∅", "string", true}, + {"~string | ∅ | 𝓤", "myInt", true}, } { xl := maketl(test.xl) y := testTerm(test.typ) @@ -261,18 +261,18 @@ func TestTermlistSubsetOf(t *testing.T) { {"∅", "𝓤", true}, {"𝓤", "∅", false}, {"𝓤", "𝓤", true}, - {"int", "int ∪ string", true}, - {"~int", "int ∪ string", false}, - {"~int", "myInt ∪ string", false}, - {"myInt", "~int ∪ string", true}, - {"~int", "string ∪ string ∪ int ∪ ~int", true}, - {"myInt", "string ∪ string ∪ ~int", true}, - {"int ∪ string", "string", false}, - {"int ∪ string", "string ∪ int", true}, - {"int ∪ ~string", "string ∪ int", false}, - {"myInt ∪ ~string", "string ∪ int ∪ 𝓤", true}, - {"int ∪ ~string", "string ∪ int ∪ ∅ ∪ string", false}, - {"int ∪ myInt", "string ∪ ~int ∪ ∅ ∪ string", true}, + {"int", "int | string", true}, + {"~int", "int | string", false}, + {"~int", "myInt | string", false}, + {"myInt", "~int | string", true}, + {"~int", "string | string | int | ~int", true}, + {"myInt", "string | string | ~int", true}, + {"int | string", "string", false}, + {"int | string", "string | int", true}, + {"int | ~string", "string | int", false}, + {"myInt | ~string", "string | int | 𝓤", true}, + {"int | ~string", "string | int | ∅ | string", false}, + {"int | myInt", "string | ~int | ∅ | string", true}, } { xl := maketl(test.xl) yl := maketl(test.yl) diff --git a/src/go/types/typeset_test.go b/src/go/types/typeset_test.go index 2bbe611376..5156092483 100644 --- a/src/go/types/typeset_test.go +++ b/src/go/types/typeset_test.go @@ -22,13 +22,13 @@ func TestTypeSetString(t *testing.T) { "{}": "𝓤", "{int}": "{int}", "{~int}": "{~int}", - "{int|string}": "{int ∪ string}", + "{int|string}": "{int | string}", "{int; string}": "∅", "{comparable}": "{comparable}", "{comparable; int}": "{int}", "{~int; comparable}": "{~int}", - "{int|string; comparable}": "{int ∪ string}", + "{int|string; comparable}": "{int | string}", "{comparable; int; string}": "∅", "{m()}": "{func (p.T).m()}", @@ -38,7 +38,7 @@ func TestTypeSetString(t *testing.T) { "{m1(); comparable; m2() int }": "{comparable; func (p.T).m1(); func (p.T).m2() int}", "{comparable; error}": "{comparable; func (error).Error() string}", - "{m(); comparable; int|float32|string}": "{func (p.T).m(); int ∪ float32 ∪ string}", + "{m(); comparable; int|float32|string}": "{func (p.T).m(); int | float32 | string}", "{m1(); int; m2(); comparable }": "{func (p.T).m1(); func (p.T).m2(); int}", "{E}; type E interface{}": "𝓤", From 269bf7e855da04e664fe8d7ffb654c4d0b1439f5 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 7 Jun 2022 10:43:51 -0700 Subject: [PATCH 047/113] go/types, types2: better error message if type is not in type set Fixes #40350. Change-Id: Ia654d6b854971700ca618692a864265557122b23 Reviewed-on: https://go-review.googlesource.com/c/go/+/410876 Reviewed-by: Robert Findley Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/types2/instantiate.go | 2 +- .../types2/testdata/fixedbugs/issue40350.go | 16 ++++++++++++++++ src/go/types/instantiate.go | 2 +- src/go/types/testdata/fixedbugs/issue40350.go | 16 ++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue40350.go create mode 100644 src/go/types/testdata/fixedbugs/issue40350.go diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index f338e28d2e..45f7e43ccf 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -277,7 +277,7 @@ func (check *Checker) implements(V, T Type) error { if alt != nil { return errorf("%s does not implement %s (possibly missing ~ for %s in constraint %s)", V, T, alt, T) } else { - return errorf("%s does not implement %s", V, T) + return errorf("%s does not implement %s (%s missing in %s)", V, T, V, Ti.typeSet().terms) } } diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40350.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40350.go new file mode 100644 index 0000000000..7ffd551c2e --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40350.go @@ -0,0 +1,16 @@ +// 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 number interface { + ~float64 | ~int | ~int32 + float64 | ~int32 +} + +func f[T number]() {} + +func _() { + _ = f[int /* ERROR int does not implement number \(int missing in float64 | ~int32\)*/] +} diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index 6091b0b381..e6b731f241 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -277,7 +277,7 @@ func (check *Checker) implements(V, T Type) error { if alt != nil { return errorf("%s does not implement %s (possibly missing ~ for %s in constraint %s)", V, T, alt, T) } else { - return errorf("%s does not implement %s", V, T) + return errorf("%s does not implement %s (%s missing in %s)", V, T, V, Ti.typeSet().terms) } } diff --git a/src/go/types/testdata/fixedbugs/issue40350.go b/src/go/types/testdata/fixedbugs/issue40350.go new file mode 100644 index 0000000000..7ffd551c2e --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue40350.go @@ -0,0 +1,16 @@ +// 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 number interface { + ~float64 | ~int | ~int32 + float64 | ~int32 +} + +func f[T number]() {} + +func _() { + _ = f[int /* ERROR int does not implement number \(int missing in float64 | ~int32\)*/] +} From 3507805bcdcd6674c842e25fdb5f07f5ce47ba87 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 7 Jun 2022 13:39:56 -0700 Subject: [PATCH 048/113] go/types, types2: better error message for invalid use of constraint type Fixes #42881. Change-Id: If800c5f90c0034d192bf8b6649e5cfda96df48cb Reviewed-on: https://go-review.googlesource.com/c/go/+/410954 Reviewed-by: Robert Griesemer Reviewed-by: Robert Findley --- .../types2/testdata/fixedbugs/issue42881.go | 16 ++++++++++++++++ src/cmd/compile/internal/types2/typexpr.go | 4 ++-- src/go/types/testdata/fixedbugs/issue42881.go | 16 ++++++++++++++++ src/go/types/typexpr.go | 4 ++-- 4 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue42881.go create mode 100644 src/go/types/testdata/fixedbugs/issue42881.go diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42881.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42881.go new file mode 100644 index 0000000000..7122d1c787 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42881.go @@ -0,0 +1,16 @@ +// 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 ( + T1 interface{ comparable } + T2 interface{ int } +) + +var ( + _ comparable // ERROR cannot use type comparable outside a type constraint: interface is \(or embeds\) comparable + _ T1 // ERROR cannot use type T1 outside a type constraint: interface is \(or embeds\) comparable + _ T2 // ERROR cannot use type T2 outside a type constraint: interface contains type constraints +) diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index f0cd236050..692feb9751 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -167,9 +167,9 @@ func (check *Checker) validVarType(e syntax.Expr, typ Type) { tset := computeInterfaceTypeSet(check, pos, t) // TODO(gri) is this the correct position? if !tset.IsMethodSet() { if tset.comparable { - check.softErrorf(pos, "interface is (or embeds) comparable") + check.softErrorf(pos, "cannot use type %s outside a type constraint: interface is (or embeds) comparable", typ) } else { - check.softErrorf(pos, "interface contains type constraints") + check.softErrorf(pos, "cannot use type %s outside a type constraint: interface contains type constraints", typ) } } } diff --git a/src/go/types/testdata/fixedbugs/issue42881.go b/src/go/types/testdata/fixedbugs/issue42881.go new file mode 100644 index 0000000000..7122d1c787 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue42881.go @@ -0,0 +1,16 @@ +// 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 ( + T1 interface{ comparable } + T2 interface{ int } +) + +var ( + _ comparable // ERROR cannot use type comparable outside a type constraint: interface is \(or embeds\) comparable + _ T1 // ERROR cannot use type T1 outside a type constraint: interface is \(or embeds\) comparable + _ T2 // ERROR cannot use type T2 outside a type constraint: interface contains type constraints +) diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index a881d33654..b02929df22 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -163,9 +163,9 @@ func (check *Checker) validVarType(e ast.Expr, typ Type) { tset := computeInterfaceTypeSet(check, e.Pos(), t) // TODO(gri) is this the correct position? if !tset.IsMethodSet() { if tset.comparable { - check.softErrorf(e, _MisplacedConstraintIface, "interface is (or embeds) comparable") + check.softErrorf(e, _MisplacedConstraintIface, "cannot use type %s outside a type constraint: interface is (or embeds) comparable", typ) } else { - check.softErrorf(e, _MisplacedConstraintIface, "interface contains type constraints") + check.softErrorf(e, _MisplacedConstraintIface, "cannot use type %s outside a type constraint: interface contains type constraints", typ) } } } From d151134851554aa0a3f05206019b9b8c1e1ad70f Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Tue, 7 Jun 2022 16:30:37 -0400 Subject: [PATCH 049/113] doc/go1.19: document linker CL that switches DWARF compressed section format For #51400. Updates #50796. Change-Id: Ica6c700a5b54e4712b09c43d1d7a9c3bba408b8b Reviewed-on: https://go-review.googlesource.com/c/go/+/410823 Reviewed-by: Ian Lance Taylor Reviewed-by: Fangrui Song --- doc/go1.19.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index b8d372224a..155e300d5a 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -199,8 +199,10 @@ Do not send CLs removing the interior tags from such phrases.

Linker

-

- TODO: complete this section, or delete if not needed +

+ On ELF platforms, the linker now emits compressed DWARF sections in + the standard gABI format (SHF_COMPRESSED), instead of + the legacy .zdebug format.

Core library

From 19d71acd978891b201bc5ce79bdcd20b36d04a2e Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Tue, 7 Jun 2022 16:35:13 -0400 Subject: [PATCH 050/113] doc/go1.19: document that the assembler requires -p For #51400. Change-Id: I50fb4313105ae6dbbbe2c98cbe4a8f8e2563eba9 Reviewed-on: https://go-review.googlesource.com/c/go/+/410824 Reviewed-by: Ian Lance Taylor --- doc/go1.19.html | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/go1.19.html b/doc/go1.19.html index 155e300d5a..b9dfa59297 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -198,6 +198,15 @@ Do not send CLs removing the interior tags from such phrases. pass this flag as well.

+

Assembler

+

+ Like the compiler, the assembler now requires the + -p=importpath flag to build a linkable object file. + This is already supplied by the go command. Any other + build systems that invoke the Go assembler directly will need to + make sure they pass this flag as well. +

+

Linker

On ELF platforms, the linker now emits compressed DWARF sections in From a7551fe24524fb960fbe4cd74dae13afe9ca6a5c Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Mon, 6 Jun 2022 15:52:19 -0700 Subject: [PATCH 051/113] net: use synthetic network in TestDialParallel TestDialParallel is testing the Happy Eyeballs algorithm implementation, which dials IPv4 and IPv6 addresses in parallel with the preferred address family getting a head start. This test doesn't care about the actual network operations, just the handling of the parallel connections. Use testHookDialTCP to replace socket creation with a function that returns successfully, with an error, or after context cancellation as required. Limit tests of elapsed times to a check that the fallback deadline has been exceeded in cases where this is expected. This should fix persistent test flakiness. Fixes #52173. Change-Id: Ic93f270fccb63b24a91105a4d541479fc33a2de4 Reviewed-on: https://go-review.googlesource.com/c/go/+/410754 Auto-Submit: Damien Neil Reviewed-by: Ian Lance Taylor Run-TryBot: Damien Neil TryBot-Result: Gopher Robot --- src/net/dial_test.go | 174 ++++++++++++++++++------------------------- 1 file changed, 71 insertions(+), 103 deletions(-) diff --git a/src/net/dial_test.go b/src/net/dial_test.go index afec31f636..0550acb01d 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -9,6 +9,8 @@ package net import ( "bufio" "context" + "errors" + "fmt" "internal/testenv" "io" "os" @@ -175,31 +177,9 @@ func dialClosedPort(t *testing.T) (dialLatency time.Duration) { } func TestDialParallel(t *testing.T) { - testenv.MustHaveExternalNetwork(t) - - if !supportsIPv4() || !supportsIPv6() { - t.Skip("both IPv4 and IPv6 are required") - } - - closedPortDelay := dialClosedPort(t) - const instant time.Duration = 0 const fallbackDelay = 200 * time.Millisecond - // Some cases will run quickly when "connection refused" is fast, - // or trigger the fallbackDelay on Windows. This value holds the - // lesser of the two delays. - var closedPortOrFallbackDelay time.Duration - if closedPortDelay < fallbackDelay { - closedPortOrFallbackDelay = closedPortDelay - } else { - closedPortOrFallbackDelay = fallbackDelay - } - - origTestHookDialTCP := testHookDialTCP - defer func() { testHookDialTCP = origTestHookDialTCP }() - testHookDialTCP = slowDialTCP - nCopies := func(s string, n int) []string { out := make([]string, n) for i := 0; i < n; i++ { @@ -223,31 +203,21 @@ func TestDialParallel(t *testing.T) { // Primary is slow; fallback should kick in. {[]string{slowDst4}, []string{"::1"}, "", true, fallbackDelay}, // Skip a "connection refused" in the primary thread. - {[]string{"127.0.0.1", "::1"}, []string{}, "tcp4", true, closedPortDelay}, - {[]string{"::1", "127.0.0.1"}, []string{}, "tcp6", true, closedPortDelay}, + {[]string{"127.0.0.1", "::1"}, []string{}, "tcp4", true, instant}, + {[]string{"::1", "127.0.0.1"}, []string{}, "tcp6", true, instant}, // Skip a "connection refused" in the fallback thread. - {[]string{slowDst4, slowDst6}, []string{"::1", "127.0.0.1"}, "tcp6", true, fallbackDelay + closedPortDelay}, + {[]string{slowDst4, slowDst6}, []string{"::1", "127.0.0.1"}, "tcp6", true, fallbackDelay}, // Primary refused, fallback without delay. - {[]string{"127.0.0.1"}, []string{"::1"}, "tcp4", true, closedPortOrFallbackDelay}, - {[]string{"::1"}, []string{"127.0.0.1"}, "tcp6", true, closedPortOrFallbackDelay}, + {[]string{"127.0.0.1"}, []string{"::1"}, "tcp4", true, instant}, + {[]string{"::1"}, []string{"127.0.0.1"}, "tcp6", true, instant}, // Everything is refused. - {[]string{"127.0.0.1"}, []string{}, "tcp4", false, closedPortDelay}, + {[]string{"127.0.0.1"}, []string{}, "tcp4", false, instant}, // Nothing to do; fail instantly. {[]string{}, []string{}, "", false, instant}, // Connecting to tons of addresses should not trip the deadline. {nCopies("::1", 1000), []string{}, "", true, instant}, } - handler := func(dss *dualStackServer, ln Listener) { - for { - c, err := ln.Accept() - if err != nil { - return - } - c.Close() - } - } - // Convert a list of IP strings into TCPAddrs. makeAddrs := func(ips []string, port string) addrList { var out addrList @@ -262,76 +232,74 @@ func TestDialParallel(t *testing.T) { } for i, tt := range testCases { - dss, err := newDualStackServer() - if err != nil { - t.Fatal(err) - } - defer dss.teardown() - if err := dss.buildup(handler); err != nil { - t.Fatal(err) - } - if tt.teardownNetwork != "" { - // Destroy one of the listening sockets, creating an unreachable port. - dss.teardownNetwork(tt.teardownNetwork) - } + i, tt := i, tt + t.Run(fmt.Sprint(i), func(t *testing.T) { + origTestHookDialTCP := testHookDialTCP + defer func() { testHookDialTCP = origTestHookDialTCP }() + testHookDialTCP = func(ctx context.Context, network string, laddr, raddr *TCPAddr) (*TCPConn, error) { + n := "tcp6" + if raddr.IP.To4() != nil { + n = "tcp4" + } + if n == tt.teardownNetwork { + return nil, errors.New("unreachable") + } + if r := raddr.IP.String(); r == slowDst4 || r == slowDst6 { + <-ctx.Done() + return nil, ctx.Err() + } + return &TCPConn{}, nil + } - primaries := makeAddrs(tt.primaries, dss.port) - fallbacks := makeAddrs(tt.fallbacks, dss.port) - d := Dialer{ - FallbackDelay: fallbackDelay, - } - startTime := time.Now() - sd := &sysDialer{ - Dialer: d, - network: "tcp", - address: "?", - } - c, err := sd.dialParallel(context.Background(), primaries, fallbacks) - elapsed := time.Since(startTime) + primaries := makeAddrs(tt.primaries, "80") + fallbacks := makeAddrs(tt.fallbacks, "80") + d := Dialer{ + FallbackDelay: fallbackDelay, + } + const forever = 60 * time.Minute + if tt.expectElapsed == instant { + d.FallbackDelay = forever + } + startTime := time.Now() + sd := &sysDialer{ + Dialer: d, + network: "tcp", + address: "?", + } + c, err := sd.dialParallel(context.Background(), primaries, fallbacks) + elapsed := time.Since(startTime) - if c != nil { - c.Close() - } + if c != nil { + c.Close() + } - if tt.expectOk && err != nil { - t.Errorf("#%d: got %v; want nil", i, err) - } else if !tt.expectOk && err == nil { - t.Errorf("#%d: got nil; want non-nil", i) - } + if tt.expectOk && err != nil { + t.Errorf("#%d: got %v; want nil", i, err) + } else if !tt.expectOk && err == nil { + t.Errorf("#%d: got nil; want non-nil", i) + } - // We used to always use 95 milliseconds as the slop, - // but that was flaky on Windows. See issue 35616. - slop := 95 * time.Millisecond - if half := tt.expectElapsed / 2; half > slop { - slop = half - } - expectElapsedMin := tt.expectElapsed - slop - expectElapsedMax := tt.expectElapsed + slop - if elapsed < expectElapsedMin { - t.Errorf("#%d: got %v; want >= %v", i, elapsed, expectElapsedMin) - } else if elapsed > expectElapsedMax { - t.Errorf("#%d: got %v; want <= %v", i, elapsed, expectElapsedMax) - } + if elapsed < tt.expectElapsed || elapsed >= forever { + t.Errorf("#%d: got %v; want >= %v, < forever", i, elapsed, tt.expectElapsed) + } - // Repeat each case, ensuring that it can be canceled quickly. - ctx, cancel := context.WithCancel(context.Background()) - var wg sync.WaitGroup - wg.Add(1) - go func() { - time.Sleep(5 * time.Millisecond) - cancel() - wg.Done() - }() - startTime = time.Now() - c, err = sd.dialParallel(ctx, primaries, fallbacks) - if c != nil { - c.Close() - } - elapsed = time.Now().Sub(startTime) - if elapsed > 100*time.Millisecond { - t.Errorf("#%d (cancel): got %v; want <= 100ms", i, elapsed) - } - wg.Wait() + // Repeat each case, ensuring that it can be canceled. + ctx, cancel := context.WithCancel(context.Background()) + var wg sync.WaitGroup + wg.Add(1) + go func() { + time.Sleep(5 * time.Millisecond) + cancel() + wg.Done() + }() + // Ignore errors, since all we care about is that the + // call can be canceled. + c, _ = sd.dialParallel(ctx, primaries, fallbacks) + if c != nil { + c.Close() + } + wg.Wait() + }) } } From 30b929b1efb470cb9b434cd47d6cbaa74c2baedf Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 7 Jun 2022 07:25:36 +0200 Subject: [PATCH 052/113] syscall: remove unused accept on linux/loong64 accept is no longer used on Linux since CL 346849 changed Accept to use accept4 only. This follows CL 386415 which already removed accept on all other Linux platforms before the linux/loong64 port was submitted. For #45964 Change-Id: I26945ff780e71174a0b0c2f5313c4bc1e1cbf786 Reviewed-on: https://go-review.googlesource.com/c/go/+/410737 Reviewed-by: Cherry Mui Auto-Submit: Tobias Klauser Auto-Submit: Ian Lance Taylor Run-TryBot: Tobias Klauser TryBot-Result: Gopher Robot Run-TryBot: Ian Lance Taylor Reviewed-by: Ian Lance Taylor --- src/syscall/syscall_linux_loong64.go | 1 - src/syscall/zsyscall_linux_loong64.go | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/src/syscall/syscall_linux_loong64.go b/src/syscall/syscall_linux_loong64.go index 91cbf24bbb..72d42ddeb7 100644 --- a/src/syscall/syscall_linux_loong64.go +++ b/src/syscall/syscall_linux_loong64.go @@ -101,7 +101,6 @@ func Lstat(path string, stat *Stat_t) (err error) { //sys Statfs(path string, buf *Statfs_t) (err error) //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) //sys Truncate(path string, length int64) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) diff --git a/src/syscall/zsyscall_linux_loong64.go b/src/syscall/zsyscall_linux_loong64.go index ad3d84b7c2..5ceebc8800 100644 --- a/src/syscall/zsyscall_linux_loong64.go +++ b/src/syscall/zsyscall_linux_loong64.go @@ -1318,17 +1318,6 @@ func Truncate(path string, length int64) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) From b72a6a7b868deb5b671020c08fbf8d61ad8803d4 Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Fri, 3 Jun 2022 20:51:26 +0930 Subject: [PATCH 053/113] os: document that Chdir affects fs.FS returned by DirFS with a relative path Fixes #47214. Change-Id: I6fdc1c4340c0943b825ac22e311179ad1cf30915 Reviewed-on: https://go-review.googlesource.com/c/go/+/410334 Reviewed-by: Ian Lance Taylor Auto-Submit: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Cherry Mui --- src/os/file.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/os/file.go b/src/os/file.go index ab017d4af7..9f388921ae 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -620,8 +620,10 @@ func isWindowsNulName(name string) bool { // operating system will begin with "/prefix": DirFS("/prefix").Open("file") is the // same as os.Open("/prefix/file"). So if /prefix/file is a symbolic link pointing outside // the /prefix tree, then using DirFS does not stop the access any more than using -// os.Open does. DirFS is therefore not a general substitute for a chroot-style security -// mechanism when the directory tree contains arbitrary content. +// os.Open does. Additionally, the root of the fs.FS returned for a relative path, +// DirFS("prefix"), will be affected by later calls to Chdir. DirFS is therefore not +// a general substitute for a chroot-style security mechanism when the directory tree +// contains arbitrary content. // // The result implements fs.StatFS. func DirFS(dir string) fs.FS { From decdd87bea5ab380b6c2a656735db9b2eb08a202 Mon Sep 17 00:00:00 2001 From: Meng Zhuo Date: Sat, 28 May 2022 12:39:35 +0800 Subject: [PATCH 054/113] doc/go1.19: mention riscv64 supported regabi Change-Id: I715e53e4baf67f896fa9c240f7668ce11f7b33c3 Reviewed-on: https://go-review.googlesource.com/c/go/+/409195 Reviewed-by: Austin Clements Reviewed-by: Russ Cox --- doc/go1.19.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index b9dfa59297..d73f635344 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -188,7 +188,9 @@ Do not send CLs removing the interior tags from such phrases. (GOARCH=amd64 and GOARCH=arm64 only)

- TODO: https://go.dev/cl/402374: enable regabi on riscv64 by default + The riscv64 port now supports passing function arguments + and result using registers. Benchmarking shows typical performance + improvements of 10% or more on riscv64.

The Go compiler now requires the -p=importpath flag to From 2882786bf4cd779f166e9ced82a4da2ea0f8b1f9 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 7 Jun 2022 07:25:10 +0200 Subject: [PATCH 055/113] runtime: remove unused pipe and setNonblock on linux/loong64 CL 389354 removed the fallback to pipe on all platforms with pipe2. This is the case for linux. Thus, pipe and setNonblock are no longer needed on linux/loong64 too. Change-Id: I089adf918d0fd8de5d4d61a893707a2660f89183 Reviewed-on: https://go-review.googlesource.com/c/go/+/410736 Reviewed-by: Ian Lance Taylor Auto-Submit: Tobias Klauser Auto-Submit: Ian Lance Taylor Run-TryBot: Ian Lance Taylor Reviewed-by: Dmitri Shuralyov TryBot-Result: Gopher Robot --- src/runtime/defs_linux_loong64.go | 1 - src/runtime/sys_linux_loong64.s | 24 ------------------------ 2 files changed, 25 deletions(-) diff --git a/src/runtime/defs_linux_loong64.go b/src/runtime/defs_linux_loong64.go index 3e0fac0298..dda4009fb0 100644 --- a/src/runtime/defs_linux_loong64.go +++ b/src/runtime/defs_linux_loong64.go @@ -10,7 +10,6 @@ const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc - _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 diff --git a/src/runtime/sys_linux_loong64.s b/src/runtime/sys_linux_loong64.s index 07628ba499..36a92df87c 100644 --- a/src/runtime/sys_linux_loong64.s +++ b/src/runtime/sys_linux_loong64.s @@ -107,15 +107,6 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28 MOVW R4, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12 - MOVV $r+0(FP), R4 - MOVV R0, R5 - MOVV $SYS_pipe2, R11 - SYSCALL - MOVW R4, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 MOVV $r+8(FP), R4 @@ -591,21 +582,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0 SYSCALL RET -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4 - MOVW fd+0(FP), R4 // fd - MOVV $3, R5 // F_GETFL - MOVV $0, R6 - MOVV $SYS_fcntl, R11 - SYSCALL - MOVW $0x800, R6 // O_NONBLOCK - OR R4, R6 - MOVW fd+0(FP), R4 // fd - MOVV $4, R5 // F_SETFL - MOVV $SYS_fcntl, R11 - SYSCALL - RET - // func sbrk0() uintptr TEXT runtime·sbrk0(SB),NOSPLIT|NOFRAME,$0-8 // Implemented as brk(NULL). From f330a3a987aa980d6c1adfb4ed509b461d5c13cf Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 7 Jun 2022 00:02:29 -0400 Subject: [PATCH 056/113] doc/go1.19: complete most remaining TODOs The ones I left behind are almost entirely ones that I see pending CLs for. Also make various fixes to existing text. For #51400. Change-Id: I555e0074c9df82b5bdb345e21a08c8757ca147b4 Reviewed-on: https://go-review.googlesource.com/c/go/+/410814 Reviewed-by: Ian Lance Taylor Reviewed-by: Michael Pratt --- doc/go1.19.html | 327 +++++++++++++++++++++++++++++------------------- 1 file changed, 200 insertions(+), 127 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index d73f635344..4276e9ffea 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -20,12 +20,26 @@ Do not send CLs removing the interior tags from such phrases.

Changes to the language

-

- TODO: complete this section -

- TODO: https://go.dev/issue/52038: adjust scope of type parameters declared by method receivers + There is only one small change to the language, + a very small correction + to the scope of type parameters in method declarations. + Existing programs are unaffected. +

+ +

Memory Model

+ +

+ The Go memory model has been revised to align Go with + the memory model used by C, C++, Java, JavaScript, Rust, and Swift. + Go only provides sequentially consistent atomics, not any of the more relaxed forms found in other languages. + Along with the memory model update, + Go 1.19 introduces new types in the sync/atomic package + that make it easier to use atomic values, such as + atomic.Int64 + and + atomic.Pointer[T].

Ports

@@ -37,16 +51,34 @@ Do not send CLs removing the interior tags from such phrases.

Tools

-

- TODO: complete this section, or delete if not needed -

-

: - TODO: https://go.dev/issue/47528 warn when errors.As target has type *error -

Doc Comments

-

- TODO: complete this section. + +

+Go 1.19 adds support for links, lists, and clearer headings in doc comments. +As part of this change, gofmt +now reformats doc comments to make their rendered meaning clearer. +See “Go Doc Comments” +for syntax details and descriptions of common mistakes now highlighted by gofmt. +As another part of this change, the new package go/doc/comment +provides parsing and reformatting of doc comments +as well as support for rendering them to HTML, Markdown, and text. +

+ +

New unix build constraint

+ +

+ The build constraint unix is now recognized + in //go:build lines. The constraint is satisfied + if the target operating system, also known as GOOS, is + a Unix or Unix-like system. For the 1.19 release it is satisfied + if GOOS is one of + aix, android, darwin, + dragonfly, freebsd, hurd, + illumos, ios, linux, + netbsd, openbsd, or solaris. + In future releases the unix constraint may match + additional newly supported operating systems.

Go command

@@ -82,21 +114,13 @@ Do not send CLs removing the interior tags from such phrases. and GOGCCFLAGS variables it reports.

+

Vet

-

New unix build constraint

- -

- The build constraint unix is now recognized - in //go:build lines. The constraint is satisfied - if the target operating system, also known as GOOS, is - a Unix or Unix-like system. For the 1.19 release it is satisfied - if GOOS is one of - aix, android, darwin, - dragonfly, freebsd, hurd, - illumos, ios, linux, - netbsd, openbsd, or solaris. - In future releases the unix constraint may match - additional newly supported operating systems. +

: + The vet checker “errorsas” now reports when + errors.As is called + with a second argument of type *error, + a common mistake.

Runtime

@@ -149,13 +173,18 @@ Do not send CLs removing the interior tags from such phrases. space on below-average goroutines.

-

+

On Unix operating systems, Go programs that import package os now automatically increase the open file limit - (RLIMIT_NOFILE) to the maximum allowed value. Programs that need - a lower limit (for compatibility with select, for example) can - set the limit back as needed, or lower the hard limit prior to starting the - Go program. + (RLIMIT_NOFILE) to the maximum allowed value; + that is, they change the soft limit to match the hard limit. + This corrects artificially low limits set on some systems for compatibility with very old C programs using the + select system call. + Go programs are not helped by that limit, and instead even simple programs like gofmt + often ran out of file descriptors on such systems when processing many files in parallel. + One impact of this change is that Go programs that in turn execute very old C programs in child processes + may run those programs with too high a limit. + This can be corrected by setting the hard limit before invoking the Go program.

@@ -174,7 +203,8 @@ Do not send CLs removing the interior tags from such phrases.

- TODO: https://go.dev/issue/44853: enable address sanitizer in Go + The address sanitizer support added in Go 1.18 + now handles function arguments and global variables more precisely.

Compiler

@@ -218,11 +248,8 @@ Do not send CLs removing the interior tags from such phrases.

Core library

-

- TODO: complete this section -

-

New atomic types

+

The sync/atomic package defines new atomic types Bool, @@ -238,46 +265,46 @@ Do not send CLs removing the interior tags from such phrases. the need to convert to unsafe.Pointer at call sites. Int64 and - Uint64 automatically - receive 64-bit alignment on ARM, 386, and 32-bit MIPS required for 64-bit - atomics on these systems. -

- -

Doc comment parsing

- -

- TODO: https://go.dev/cl/384265: go/doc: use go/doc/comment; modified api/next/51082.txt - TODO: https://go.dev/cl/397276: go/doc/comment: add data structures; modified api/next/51082.txt - TODO: https://go.dev/cl/397278: go/doc/comment: add paragraph parsing and test framework; modified api/next/51082.txt - TODO: https://go.dev/cl/397279: go/doc/comment: add Printer and basic comment printing; modified api/next/51082.txt - TODO: https://go.dev/cl/397281: go/doc/comment: parse and print doc links; modified api/next/51082.txt - TODO: https://go.dev/cl/397284: go/doc/comment: parse and print headings; modified api/next/51082.txt + Uint64 are + automatically aligned to 64-bit boundaries in structs and allocated data, + even on 32-bit systems.

PATH lookups

-

- TODO: https://go.dev/issue/43724: return error when PATH lookup would use current directory -

-

- TODO: https://go.dev/issue/43947: on Windows use NeedCurrentDirectoryForExePathW for LookPath behavior +

+ + Command and + LookPath no longer + allow results from a PATH search to be found relative to the current directory. + This removes a common source of security problems + but may also break existing programs that depend on using, say, exec.Command("prog") + to run a binary named prog (or, on Windows, prog.exe) in the current directory. + See the os/exec package documentation for + information about how best to update such programs.

+

+ On Windows, Command and LookPath now respect the + NoDefaultCurrentDirectoryInExePath + environment variable, making it possible to disable + the default implicit search of “.” in PATH lookups on Windows systems. +

Minor changes to the library

As always, there are various minor changes and updates to the library, made with the Go 1 promise of compatibility in mind. -

-

- TODO: complete this section + There are also various performance improvements, not enumerated here.

archive/zip

- TODO: https://go.dev/cl/387976: permit zip files to have prefixes + Reader + now ignores non-ZIP data at the start of a ZIP file, matching most other implementations. + This is necessary to read some Java JAR files, among other uses.

@@ -315,8 +342,8 @@ Do not send CLs removing the interior tags from such phrases.
crypto/x509

- CreateCertificate - no longer supports creating certificates with SignatureAlgorithm + CreateCertificate + no longer supports creating certificates with SignatureAlgorithm set to MD5WithRSA.

@@ -344,8 +371,8 @@ Do not send CLs removing the interior tags from such phrases. To support this addition, RevocationList adds new fields RawIssuer, Signature, AuthorityKeyId, and Extensions. - - The new method RevocationList.CheckSignatureFrom + + The new method RevocationList.CheckSignatureFrom checks that the signature on a CRL is a valid signature from a Certificate. @@ -358,7 +385,7 @@ Do not send CLs removing the interior tags from such phrases.

When building paths, Certificate.Verify - now considers certificates to be equal when the subjects, public keys, and SANs + now considers certificates to be equal when the subjects, public keys, and SANs are all equal. Before, it required byte-for-byte equality.

@@ -395,19 +422,19 @@ Do not send CLs removing the interior tags from such phrases.
debug

- TODO: https://go.dev/cl/396735: debug: define ELF relocation for loong64; modified api/next/46229.txt + The new EM_LONGARCH and R_LARCH_* constants + support the loong64 port.

debug/pe
-

- TODO: https://go.dev/issue/51868: add APIs to support reading COMDAT info for sections -

- -

- TODO: https://go.dev/cl/394534: debug/pe: add APIs for reading section def aux info; modified api/next/51868.txt +

+ The new File.COFFSymbolReadSectionDefAux + method, which returns a COFFSymbolAuxFormat5, + provides access to COMDAT information in PE file sections. + These are supported by new IMAGE_COMDAT_* and IMAGE_SCN_* constants.

@@ -415,19 +442,32 @@ Do not send CLs removing the interior tags from such phrases.
encoding/binary

- TODO: https://go.dev/cl/386017: add AppendByteOrder + The new interface + AppendByteOrder + provides efficient methods for appending a uint16, uint32, or uint64 + to a byte slice. + BigEndian and + LittleEndian now implement this interface.

- TODO: https://go.dev/issue/51644: add AppendUvarint and AppendVarint + Similarly, the new functions + AppendUvarint and + AppendVarint + are efficient appending versions of + PutUvarint and + PutVarint.

-
encoding/csv

- TODO: https://go.dev/cl/405675: add Reader.InputOffset method + The new method + Reader.InputOffset + reports the reader's current input position as a byte offset, + analogous to encoding/json's + Decoder.InputOffset.

@@ -435,8 +475,11 @@ Do not send CLs removing the interior tags from such phrases.
encoding/xml

- TODO: https://go.dev/issue/45628: add Decoder.InputPos - TODO: https://go.dev/cl/311270: encoding/xml: expose decoder line and column; modified api/next/45628.txt + The new method + Decoder.InputPos + reports the reader's current input position as a line and column, + analogous to encoding/csv's + Decoder.FieldPos.

@@ -444,7 +487,14 @@ Do not send CLs removing the interior tags from such phrases.
flag

- TODO: https://go.dev/cl/313329: add TextVar function + The new function + TextVar + defines a flag with a value implementing + encoding.TextUnmarshaler, + allowing command-line flag variables to have types such as + big.Int, + netip.Addr, and + time.Time.

@@ -452,7 +502,11 @@ Do not send CLs removing the interior tags from such phrases.
fmt

- TODO: https://go.dev/cl/406177: add Append, Appendln, Appendf + The new functions + Append, + Appendf, and + Appendln + append formatted data to byte slices.

@@ -460,7 +514,9 @@ Do not send CLs removing the interior tags from such phrases.
go/parser

- TODO: https://go.dev/cl/403696: parser to accept ~x as unary expression + The parser now recognizes ~x as a unary expression with operator + token.TILDE, + allowing better error recovery when a type constraint such as ~int is used in an incorrect context.

@@ -489,8 +545,14 @@ Do not send CLs removing the interior tags from such phrases.
hash/maphash

- TODO: https://go.dev/cl/392494: hash/maphash: add Bytes and String; modified api/next/42710.txt - TODO: https://go.dev/issue/42710: add Bytes and String + The new functions + Bytes + and + String + provide an efficient way hash a single byte slice or string. + They are equivalent to using the more general + Hash + with a single write, but they avoid setup overhead for small inputs.

@@ -498,8 +560,11 @@ Do not send CLs removing the interior tags from such phrases.
html/template

- TODO: https://go.dev/issue/46121: make FuncMap an alias for text/template.FuncMap - TODO: https://go.dev/cl/389156: html/template: make FuncMap a type alias of text/template.FuncMap; modified api/except.txt, api/next/46121.txt + The type FuncMap + is now an alias for + text/template's FuncMap + instead of its own named type. + This allows writing code that operates on a FuncMap from either setting.

@@ -507,11 +572,13 @@ Do not send CLs removing the interior tags from such phrases.
image/draw

- Draw with the Src operator preserves + Draw with the + Src operator preserves non-premultiplied-alpha colors when destination and source images are - both *image.NRGBA (or both *image.NRGBA64). + both image.NRGBA + or both image.NRGBA64. This reverts a behavior change accidentally introduced by a Go 1.18 - library optimization, to match the behavior in Go 1.17 and earlier. + library optimization; the code now matches the behavior in Go 1.17 and earlier.

@@ -519,11 +586,16 @@ Do not send CLs removing the interior tags from such phrases.
io

- TODO: https://go.dev/cl/400236: NopCloser forward WriterTo implementations if the reader supports it + NopCloser's result now implements + WriterTo + whenever its input does.

- TODO: https://go.dev/issue/50842: implement WriterTo on result of MultiReader + MultiReader's result now implements + WriterTo unconditionally. + If any underlying reader does not implement WriterTo, + it is simulated appropriately.

@@ -539,7 +611,7 @@ Do not send CLs removing the interior tags from such phrases. type text/javascript; charset=utf-8. Applications that expect text/plain on Windows must now explicitly call - AddExtensionType. + AddExtensionType.

@@ -569,6 +641,7 @@ Do not send CLs removing the interior tags from such phrases. package function or method to return an error, while preserving backward compatibility for error messages.

+

Resolver.PreferGo is now implemented on Windows and Plan 9. It previously only worked on Unix @@ -578,6 +651,7 @@ Do not send CLs removing the interior tags from such phrases. possible to write portable programs and be in control of all DNS name lookups when dialing.

+

The net package now has initial support for the netgo build tag on Windows. When used, the package uses the Go DNS client (as used @@ -636,26 +710,17 @@ Do not send CLs removing the interior tags from such phrases.

-
os
-
-

- TODO: https://go.dev/cl/392415: raise open file rlimit at startup -

-
-
- -
os/exec

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

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

@@ -663,37 +728,36 @@ Do not send CLs removing the interior tags from such phrases.
reflect

- The method Value.Bytes now accepts addressable arrays in addition to slices. + The method Value.Bytes + now accepts addressable arrays in addition to slices.

- The methods Value.Len and Value.Cap now successfully operate on a pointer to an array and return the length of that array, to match what the builtin len and cap functions do. + The methods Value.Len + and Value.Cap + now successfully operate on a pointer to an array and return the length of that array, + to match what the builtin + len and cap functions do.

-
regexp
+
regexp/syntax

- TODO: https://go.dev/issue/51684: add ErrNestingDepth error - TODO: https://go.dev/cl/401076: regexp: change ErrInvalidDepth message to match proposal; modified api/next/51684.txt, api/next/regexpdepth.txt + Go 1.18 release candidate 1, Go 1.17.8, and Go 1.16.15 included a security fix + to the regular expression parser, making it reject very deeply nested expressions. + Because Go patch releases do not introduce new API, + the parser returned syntax.ErrInternalError in this case. + Go 1.19 adds a more specific error, syntax.ErrNestingDepth, + which the parser now returns instead.

-
regexp/syntax
-
-

- TODO: https://go.dev/cl/384617: regexp/syntax: add and use ErrInvalidDepth; modified api/next/regexpdepth.txt - TODO: https://go.dev/cl/401854: regexp/syntax: rename ErrInvalidDepth to ErrNestingDepth; modified api/next/51684.txt -

-
-
- -
runtime

- The GOROOT function now returns the empty string + The GOROOT function now returns the empty string (instead of "go") when the binary was built with the -trimpath flag set and the GOROOT variable is not set in the process environment. @@ -797,8 +861,11 @@ Do not send CLs removing the interior tags from such phrases. is faster for several common scenarios.

- TODO: https://go.dev/issue/50340: add Find - TODO: https://go.dev/cl/396514: sort: add Find function; modified api/next/50340.txt + The new function + Find + is like + Search + but often easier to use: it returns an additional boolean reporting whether an equal value was found.

@@ -808,8 +875,9 @@ Do not send CLs removing the interior tags from such phrases.

Quote - and related functions now quote the rune 007F as \x7f, - not \u007f. + and related functions now quote the rune U+007F as \x7f, + not \u007f, + for consistency with other ASCII values.

@@ -835,12 +903,17 @@ Do not send CLs removing the interior tags from such phrases.
time

- TODO: https://go.dev/cl/393515: add Duration.Abs - TODO: https://go.dev/issue/51414: add Duration.Abs + The new method + Duration.Abs + provides a convenient and safe way to take the absolute value of a duration, + converting −2⁶³ to 2⁶³−1. + (This boundary case can happen as the result of subtracting a recent time from the zero time.)

- TODO: https://go.dev/issue/50062: add Time.ZoneBounds - TODO: https://go.dev/cl/405374: time: add Time.ZoneBounds; modified api/next/50062.txt + The new method + Time.ZoneBounds + returns the start and end times of the time zone in effect at a given time. + It can be used in a loop to enumerate all the known time zone transitions at a given location.

From 3426b7201da8140bb0fb433facd9e1fd3f267dfb Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Wed, 8 Jun 2022 10:46:42 -0400 Subject: [PATCH 057/113] runtime: gofmt libfuzzerHookStrCmp is manually reformatted into a proper go doc list. We don't always format testdata, but these test programs are standard Go programs that can be formatted. Change-Id: I4dde398bca225ae8c72e787e4d43fd0ccfd0a90b Reviewed-on: https://go-review.googlesource.com/c/go/+/411114 Auto-Submit: Michael Pratt TryBot-Result: Gopher Robot Run-TryBot: Michael Pratt Reviewed-by: Cherry Mui --- src/runtime/libfuzzer.go | 20 +++++++++---------- src/runtime/testdata/testprog/crash.go | 6 +++--- .../testdata/testprogcgo/pprof_callback.go | 2 +- src/runtime/testdata/testwinlib/main.go | 2 ++ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/runtime/libfuzzer.go b/src/runtime/libfuzzer.go index 09e84d7394..8c6642443c 100644 --- a/src/runtime/libfuzzer.go +++ b/src/runtime/libfuzzer.go @@ -11,10 +11,10 @@ import "unsafe" func libfuzzerCallWithTwoByteBuffers(fn, start, end *byte) func libfuzzerCallTraceIntCmp(fn *byte, arg0, arg1, fakePC uintptr) func libfuzzerCall4(fn *byte, fakePC uintptr, s1, s2 unsafe.Pointer, result uintptr) + // Keep in sync with the definition of ret_sled in src/runtime/libfuzzer_amd64.s const retSledSize = 512 - func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC int) { fakePC = fakePC % retSledSize libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp1, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) @@ -71,15 +71,15 @@ func init() { libfuzzerCallWithTwoByteBuffers(&__sanitizer_cov_pcs_init, &pcTables[0], &pcTables[size-1]) } -// We call libFuzzer's __sanitizer_weak_hook_strcmp function -// which takes the following four arguments: -// 1- caller_pc: location of string comparison call site -// 2- s1: first string used in the comparison -// 3- s2: second string used in the comparison -// 4- result: an integer representing the comparison result. Libfuzzer only distinguishes between two cases: -// - 0 means that the strings are equal and the comparison will be ignored by libfuzzer. -// - Any other value means that strings are not equal and libfuzzer takes the comparison into consideration. -// Here, we pass 1 when the strings are not equal. +// We call libFuzzer's __sanitizer_weak_hook_strcmp function which takes the +// following four arguments: +// +// 1. caller_pc: location of string comparison call site +// 2. s1: first string used in the comparison +// 3. s2: second string used in the comparison +// 4. result: an integer representing the comparison result. 0 indicates +// equality (comparison will ignored by libfuzzer), non-zero indicates a +// difference (comparison will be taken into consideration). func libfuzzerHookStrCmp(s1, s2 string, fakePC int) { if s1 != s2 { libfuzzerCall4(&__sanitizer_weak_hook_strcmp, uintptr(fakePC), cstring(s1), cstring(s2), uintptr(1)) diff --git a/src/runtime/testdata/testprog/crash.go b/src/runtime/testdata/testprog/crash.go index a2294ba149..38c8f6a2fa 100644 --- a/src/runtime/testdata/testprog/crash.go +++ b/src/runtime/testdata/testprog/crash.go @@ -122,13 +122,13 @@ func NilPanic() { panic(nil) } -type exampleCircleStartError struct {} +type exampleCircleStartError struct{} func (e exampleCircleStartError) Error() string { panic(exampleCircleEndError{}) } -type exampleCircleEndError struct {} +type exampleCircleEndError struct{} func (e exampleCircleEndError) Error() string { panic(exampleCircleStartError{}) @@ -136,4 +136,4 @@ func (e exampleCircleEndError) Error() string { func CircularPanic() { panic(exampleCircleStartError{}) -} \ No newline at end of file +} diff --git a/src/runtime/testdata/testprogcgo/pprof_callback.go b/src/runtime/testdata/testprogcgo/pprof_callback.go index e34564395e..fd87eb87dd 100644 --- a/src/runtime/testdata/testprogcgo/pprof_callback.go +++ b/src/runtime/testdata/testprogcgo/pprof_callback.go @@ -27,8 +27,8 @@ import "C" import ( "fmt" "os" - "runtime/pprof" "runtime" + "runtime/pprof" "time" ) diff --git a/src/runtime/testdata/testwinlib/main.go b/src/runtime/testdata/testwinlib/main.go index 025ef913e5..407331bb83 100644 --- a/src/runtime/testdata/testwinlib/main.go +++ b/src/runtime/testdata/testwinlib/main.go @@ -11,6 +11,7 @@ package main import "C" // CallMeBack call backs C code. +// //export CallMeBack func CallMeBack(callback C.callmeBackFunc) { C.bridgeCallback(callback) @@ -21,6 +22,7 @@ func CallMeBack(callback C.callmeBackFunc) { // validate that it does not crash the program before another handler could take an action. // The idea here is to reproduce what happens when you attach a debugger to a running program. // It also simulate the behavior of the .Net debugger, which register its exception/continue handlers lazily. +// //export Dummy func Dummy() int { return 42 From 4afb0b9e533767f788252816c4b79ee29a1952a7 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 8 Jun 2022 11:33:53 -0400 Subject: [PATCH 058/113] doc/go1.19: delete remaining TODOs The crypto ones were done in a separate CL and didn't merge well. Same for runtime/debug. The others are stale. For #51400. Change-Id: Iadb4de94d21cd6a20f52277a1c3d7800a729b81e Reviewed-on: https://go-review.googlesource.com/c/go/+/411115 Run-TryBot: Russ Cox Reviewed-by: Cherry Mui Auto-Submit: Russ Cox TryBot-Result: Gopher Robot Reviewed-by: David Chase --- doc/go1.19.html | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 4276e9ffea..37f562a9df 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -43,9 +43,7 @@ Do not send CLs removing the interior tags from such phrases.

Ports

-

- TODO: complete this section, or delete if not needed -

+

Go 1.19 supports the Loongson 64-bit architecture LoongArch on Linux (GOOS=linux, GOARCH=loong64).

@@ -82,9 +80,6 @@ as well as support for rendering them to HTML, Markdown, and text.

Go command

-

- TODO: complete this section. -

@@ -357,14 +352,14 @@ as well as support for rendering them to HTML, Markdown, and text. now reject certificates and CSRs which contain duplicate extensions.

-

+

The new CertPool.Clone and CertPool.Equal methods allow cloning a CertPool and checking the equality of two CertPools respectively.

-

+

The new function ParseRevocationList provides a faster, safer to use CRL parser which returns a RevocationList. @@ -388,23 +383,6 @@ as well as support for rendering them to HTML, Markdown, and text. now considers certificates to be equal when the subjects, public keys, and SANs are all equal. Before, it required byte-for-byte equality.

- -

- TODO: https://go.dev/issue/46057: add CertPool.Equal -

- -

- TODO: https://go.dev/issue/50674: add ParseRevocationList, deprecate ParseCRL & ParseDERCRL -

- -

- TODO: https://go.dev/cl/390834: crypto/x509: add new CRL parser, deprecate old one; modified api/next/50674.txt -

- -

- TODO: https://go.dev/cl/400175: crypto/x509: add CertPool.Clone; modified api/next/35044.txt - TODO: https://go.dev/issue/35044: add CertPool.Clone -

@@ -765,14 +743,6 @@ as well as support for rendering them to HTML, Markdown, and text. -
runtime/debug
-
-

- TODO: https://go.dev/cl/397018: runtime/debug: export SetMemoryLimit; modified api/next/48409.txt -

-
-
-
runtime/metrics

From d65166024f3969289be5c74fd8be7d06a93264f1 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Tue, 7 Jun 2022 15:31:20 -0400 Subject: [PATCH 059/113] cmd/go: set Root and target fields for packages in GOPATH This change replicates the behavior filed in issue #37015 for packages imported from the module index. That behavior is that packages that happen to exist in a GOPATH src directory have p.Root and p.Target set even when the packages are loaded from modules. This is likely unintentional behavior because in module mode, packages shouldn't behave differently depending on whether their directories exist in GOPATH. But for uniformity, (and because two of our tests depend on this behavior), this CL will implement this behavior. We can remove it from the module index when we remove it from the go/build logic. Change-Id: I3f501c92fbb76eaf86b6b9275539f2129b67f884 Reviewed-on: https://go-review.googlesource.com/c/go/+/410822 Reviewed-by: Michael Matloob Run-TryBot: Michael Matloob TryBot-Result: Gopher Robot Reviewed-by: Russ Cox --- src/cmd/go/internal/modindex/build.go | 31 +++++++++++++++++++ src/cmd/go/internal/modindex/read.go | 44 ++++++++++++++++++--------- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/cmd/go/internal/modindex/build.go b/src/cmd/go/internal/modindex/build.go index 9d52be851b..d6d4ea371a 100644 --- a/src/cmd/go/internal/modindex/build.go +++ b/src/cmd/go/internal/modindex/build.go @@ -177,6 +177,37 @@ func hasSubdir(root, dir string) (rel string, ok bool) { return filepath.ToSlash(dir[len(root):]), true } +// gopath returns the list of Go path directories. +func (ctxt *Context) gopath() []string { + var all []string + for _, p := range ctxt.splitPathList(ctxt.GOPATH) { + if p == "" || p == ctxt.GOROOT { + // Empty paths are uninteresting. + // If the path is the GOROOT, ignore it. + // People sometimes set GOPATH=$GOROOT. + // Do not get confused by this common mistake. + continue + } + if strings.HasPrefix(p, "~") { + // Path segments starting with ~ on Unix are almost always + // users who have incorrectly quoted ~ while setting GOPATH, + // preventing it from expanding to $HOME. + // The situation is made more confusing by the fact that + // bash allows quoted ~ in $PATH (most shells do not). + // Do not get confused by this, and do not try to use the path. + // It does not exist, and printing errors about it confuses + // those users even more, because they think "sure ~ exists!". + // The go command diagnoses this situation and prints a + // useful error. + // On Windows, ~ is used in short names, such as c:\progra~1 + // for c:\program files. + continue + } + all = append(all, p) + } + return all +} + var defaultToolTags, defaultReleaseTags []string // A Package describes the Go package found in a directory. diff --git a/src/cmd/go/internal/modindex/read.go b/src/cmd/go/internal/modindex/read.go index f259a8dbe3..0ed480fbd0 100644 --- a/src/cmd/go/internal/modindex/read.go +++ b/src/cmd/go/internal/modindex/read.go @@ -242,31 +242,47 @@ func (mi *ModuleIndex) Import(bctxt build.Context, relpath string, mode build.Im return p, fmt.Errorf("import %q: import of unknown directory", p.Dir) } - // goroot + // goroot and gopath inTestdata := func(sub string) bool { return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || str.HasPathPrefix(sub, "testdata") } - if ctxt.GOROOT != "" && str.HasFilePathPrefix(mi.modroot, cfg.GOROOTsrc) && !inTestdata(relpath) { - modprefix := str.TrimFilePathPrefix(mi.modroot, cfg.GOROOTsrc) - p.Goroot = true - p.ImportPath = relpath - if modprefix != "" { - p.ImportPath = filepath.Join(modprefix, p.ImportPath) - } + if !inTestdata(relpath) { // In build.go, p.Root should only be set in the non-local-import case, or in // GOROOT or GOPATH. Since module mode only calls Import with path set to "." // and the module index doesn't apply outside modules, the GOROOT case is // the only case where GOROOT needs to be set. - // TODO(#37015): p.Root actually might be set in the local-import case outside - // GOROOT, if the directory is contained in GOPATH/src, even in module - // mode, but that's a bug. - p.Root = ctxt.GOROOT + // But: p.Root is actually set in the local-import case outside GOROOT, if + // the directory is contained in GOPATH/src + // TODO(#37015): fix that behavior in go/build and remove the gopath case + // below. + if ctxt.GOROOT != "" && str.HasFilePathPrefix(p.Dir, cfg.GOROOTsrc) && p.Dir != cfg.GOROOTsrc { + p.Root = ctxt.GOROOT + p.Goroot = true + modprefix := str.TrimFilePathPrefix(mi.modroot, cfg.GOROOTsrc) + p.ImportPath = relpath + if modprefix != "" { + p.ImportPath = filepath.Join(modprefix, p.ImportPath) + } + } + for _, root := range ctxt.gopath() { + // TODO(matloob): do we need to reimplement the conflictdir logic? - // Set GOROOT-specific fields + // TODO(matloob): ctxt.hasSubdir evaluates symlinks, so it + // can be slower than we'd like. Find out if we can drop this + // logic before the release. + if sub, ok := ctxt.hasSubdir(filepath.Join(root, "src"), p.Dir); ok { + p.ImportPath = sub + p.Root = root + } + } + } + if p.Root != "" { + // Set GOROOT-specific fields (sometimes for modules in a GOPATH directory). // The fields set below (SrcRoot, PkgRoot, BinDir, PkgTargetRoot, and PkgObj) // are only set in build.Import if p.Root != "". As noted in the comment // on setting p.Root above, p.Root should only be set in the GOROOT case for the - // set of packages we care about. + // set of packages we care about, but is also set for modules in a GOPATH src + // directory. var pkgtargetroot string var pkga string suffix := "" From f862280e30300017292b24a0fca088628d7b8065 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Tue, 7 Jun 2022 13:54:53 -0400 Subject: [PATCH 060/113] cmd/go: properly call PackageModuleRoot to get modroot for index PackageModuleRoot needs to be called with the package's path, not its directory on disk. Change-Id: I080fe8ce2aeb72e1466624db81595a00915606bb Reviewed-on: https://go-review.googlesource.com/c/go/+/410820 Reviewed-by: Russ Cox Reviewed-by: Michael Matloob TryBot-Result: Gopher Robot Run-TryBot: Michael Matloob --- src/cmd/go/internal/load/pkg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index 4c7833b4d2..394a4a4383 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -871,7 +871,7 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo if !cfg.ModulesEnabled { buildMode = build.ImportComment } - if modroot := modload.PackageModRoot(ctx, r.dir); modroot != "" { + if modroot := modload.PackageModRoot(ctx, r.path); modroot != "" { if mi, err := modindex.Get(modroot); err == nil { data.p, data.err = mi.Import(cfg.BuildContext, mi.RelPath(r.dir), buildMode) goto Happy From 899f0a29c7be2bba3f8f0bc2987f7c2d70a6c4ec Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Tue, 7 Jun 2022 13:53:40 -0400 Subject: [PATCH 061/113] cmd/go: enable module index by default This changes the module index to be enabled by default, rather than disabled by default. The index can still be disabled by setting GODEBUG=index=0. Fixes #53290. Change-Id: Ic3728fc69d96bb6ef56b56e8c9f2dce35f2923cc Reviewed-on: https://go-review.googlesource.com/c/go/+/410821 Reviewed-by: Dmitri Shuralyov Reviewed-by: Russ Cox Reviewed-by: Michael Matloob Reviewed-by: Dmitri Shuralyov --- src/cmd/go/internal/modindex/read.go | 13 ++++++++++--- src/cmd/go/script_test.go | 1 - 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cmd/go/internal/modindex/read.go b/src/cmd/go/internal/modindex/read.go index 0ed480fbd0..daa85762be 100644 --- a/src/cmd/go/internal/modindex/read.go +++ b/src/cmd/go/internal/modindex/read.go @@ -22,7 +22,6 @@ import ( "runtime" "runtime/debug" "sort" - "strconv" "strings" "sync" "unsafe" @@ -40,7 +39,15 @@ import ( // It will be removed before the release. // TODO(matloob): Remove enabled once we have more confidence on the // module index. -var enabled, _ = strconv.ParseBool(os.Getenv("GOINDEX")) +var enabled = func() bool { + debug := strings.Split(os.Getenv("GODEBUG"), ",") + for _, f := range debug { + if f == "goindex=0" { + return false + } + } + return true +}() // ModuleIndex represents and encoded module index file. It is used to // do the equivalent of build.Import of packages in the module and answer other @@ -125,7 +132,7 @@ func openIndex(modroot string, ismodcache bool) (*ModuleIndex, error) { data, _, err := cache.Default().GetMmap(id) if err != nil { // Couldn't read from modindex. Assume we couldn't read from - // the index because the module has't been indexed yet. + // the index because the module hasn't been indexed yet. data, err = indexModule(modroot) if err != nil { return result{nil, err} diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go index d1fe36ec21..04bc8d581a 100644 --- a/src/cmd/go/script_test.go +++ b/src/cmd/go/script_test.go @@ -170,7 +170,6 @@ func (ts *testScript) setup() { "GOCACHE=" + testGOCACHE, "GODEBUG=" + os.Getenv("GODEBUG"), "GOEXE=" + cfg.ExeSuffix, - "GOINDEX=true", "GOOS=" + runtime.GOOS, "GOPATH=" + filepath.Join(ts.workdir, "gopath"), "GOPROXY=" + proxyURL, From 432158b69a50e292b625d08dcfacd0604acbabd3 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Tue, 7 Jun 2022 16:53:53 -0700 Subject: [PATCH 062/113] net: fix testHookDialTCP race CL 410754 introduces a race accessing the global testHookDialTCP hook. Avoiding this race is difficult, since Dial can return while goroutines it starts are still running. Add a version of this hook to sysDialer, so it can be set on a per-test basis. (Perhaps other uses of this hook should be moved to use the sysDialer-local hook, but this change fixes the immediate data race.) For #52173. Change-Id: I8fb9be13957e91f92919cae7be213c38ad2af75a Reviewed-on: https://go-review.googlesource.com/c/go/+/410957 Run-TryBot: Damien Neil Reviewed-by: Ian Lance Taylor Reviewed-by: Cherry Mui TryBot-Result: Gopher Robot --- src/net/dial.go | 1 + src/net/dial_test.go | 11 +++++------ src/net/tcpsock_plan9.go | 7 +++++-- src/net/tcpsock_posix.go | 7 +++++-- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/net/dial.go b/src/net/dial.go index b24bd2f5f4..c538342566 100644 --- a/src/net/dial.go +++ b/src/net/dial.go @@ -341,6 +341,7 @@ func DialTimeout(network, address string, timeout time.Duration) (Conn, error) { type sysDialer struct { Dialer network, address string + testHookDialTCP func(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) } // Dial connects to the address on the named network. diff --git a/src/net/dial_test.go b/src/net/dial_test.go index 0550acb01d..e49b4a61d6 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -234,9 +234,7 @@ func TestDialParallel(t *testing.T) { for i, tt := range testCases { i, tt := i, tt t.Run(fmt.Sprint(i), func(t *testing.T) { - origTestHookDialTCP := testHookDialTCP - defer func() { testHookDialTCP = origTestHookDialTCP }() - testHookDialTCP = func(ctx context.Context, network string, laddr, raddr *TCPAddr) (*TCPConn, error) { + dialTCP := func(ctx context.Context, network string, laddr, raddr *TCPAddr) (*TCPConn, error) { n := "tcp6" if raddr.IP.To4() != nil { n = "tcp4" @@ -262,9 +260,10 @@ func TestDialParallel(t *testing.T) { } startTime := time.Now() sd := &sysDialer{ - Dialer: d, - network: "tcp", - address: "?", + Dialer: d, + network: "tcp", + address: "?", + testHookDialTCP: dialTCP, } c, err := sd.dialParallel(context.Background(), primaries, fallbacks) elapsed := time.Since(startTime) diff --git a/src/net/tcpsock_plan9.go b/src/net/tcpsock_plan9.go index 768d03b06c..435335e92e 100644 --- a/src/net/tcpsock_plan9.go +++ b/src/net/tcpsock_plan9.go @@ -15,8 +15,11 @@ func (c *TCPConn) readFrom(r io.Reader) (int64, error) { } func (sd *sysDialer) dialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { - if testHookDialTCP != nil { - return testHookDialTCP(ctx, sd.network, laddr, raddr) + if h := sd.testHookDialTCP; h != nil { + return h(ctx, sd.network, laddr, raddr) + } + if h := testHookDialTCP; h != nil { + return h(ctx, sd.network, laddr, raddr) } return sd.doDialTCP(ctx, laddr, raddr) } diff --git a/src/net/tcpsock_posix.go b/src/net/tcpsock_posix.go index bc3d324e6b..1c91170c50 100644 --- a/src/net/tcpsock_posix.go +++ b/src/net/tcpsock_posix.go @@ -55,8 +55,11 @@ func (c *TCPConn) readFrom(r io.Reader) (int64, error) { } func (sd *sysDialer) dialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { - if testHookDialTCP != nil { - return testHookDialTCP(ctx, sd.network, laddr, raddr) + if h := sd.testHookDialTCP; h != nil { + return h(ctx, sd.network, laddr, raddr) + } + if h := testHookDialTCP; h != nil { + return h(ctx, sd.network, laddr, raddr) } return sd.doDialTCP(ctx, laddr, raddr) } From bdde41e3ba4926b9c1143502f299286d5eca6490 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 8 Jun 2022 12:24:55 -0400 Subject: [PATCH 063/113] runtime: skip TestGdbBacktrace on gdb bug Very rarely, GDB will successfully run the whole test and the inferior will exit successfully, and then GDB itself hangs and never exits. Detect this and skip the test as flaky. We could just continue the test since all of the output we need is there, but by skipping it we're less likely to notice serious regressions in this test. Fixes #37405. Change-Id: I016cbb06f48673f064733da3e3f1ddcbefd58159 Reviewed-on: https://go-review.googlesource.com/c/go/+/411117 Reviewed-by: Cherry Mui --- src/runtime/runtime-gdb_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 063b9a7d45..d97c2a2524 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -435,6 +435,11 @@ func TestGdbBacktrace(t *testing.T) { // GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=9086 testenv.SkipFlaky(t, 50838) } + if bytes.Contains(got, []byte(" exited normally]\n")) { + // GDB bug: Sometimes the inferior exits fine, + // but then GDB hangs. + testenv.SkipFlaky(t, 37405) + } t.Fatalf("gdb exited with error: %v", err) } From 1858ea5d857f3a874bef131b7e1bc162d05b3366 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 8 Jun 2022 19:47:31 +0200 Subject: [PATCH 064/113] syscall: remove unused setgroups on linux/loong64 Setgroups in syscall_linux.go already wraps the setgroups(2) syscall with correct POSIX semantics (ref. CL 210639). Change-Id: I961cd7c7fce1d70d23bf13cc82cad17854bbf40e Reviewed-on: https://go-review.googlesource.com/c/go/+/411214 Reviewed-by: Cherry Mui Run-TryBot: Ian Lance Taylor Run-TryBot: Tobias Klauser TryBot-Result: Gopher Robot Auto-Submit: Tobias Klauser Reviewed-by: Ian Lance Taylor Auto-Submit: Ian Lance Taylor --- src/syscall/syscall_linux_loong64.go | 1 - src/syscall/zsyscall_linux_loong64.go | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/src/syscall/syscall_linux_loong64.go b/src/syscall/syscall_linux_loong64.go index 72d42ddeb7..99674b4a8b 100644 --- a/src/syscall/syscall_linux_loong64.go +++ b/src/syscall/syscall_linux_loong64.go @@ -105,7 +105,6 @@ func Lstat(path string, stat *Stat_t) (err error) { //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sysnb getgroups(n int, list *_Gid_t) (nn int, err error) -//sysnb setgroups(n int, list *_Gid_t) (err error) //sys getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) //sys setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) //sysnb socket(domain int, typ int, proto int) (fd int, err error) diff --git a/src/syscall/zsyscall_linux_loong64.go b/src/syscall/zsyscall_linux_loong64.go index 5ceebc8800..b3d703d86b 100644 --- a/src/syscall/zsyscall_linux_loong64.go +++ b/src/syscall/zsyscall_linux_loong64.go @@ -1360,16 +1360,6 @@ func getgroups(n int, list *_Gid_t) (nn int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func setgroups(n int, list *_Gid_t) (err error) { - _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) { _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0) if e1 != 0 { From 1292176bc98be4b7b9d24abec05e88b3dbd89e21 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Wed, 8 Jun 2022 14:26:44 -0400 Subject: [PATCH 065/113] cmd/go: clean paths before using them form index functions We use str.TrimFilePathPrefix to trim the module root prefix and get the relative path of each package in the module when scanning the module and in the RelPath function. Make sure to clean the path before indexing and in RelPath to ensure that each path starts with that prefix, because walk will clean the root path before joining each subdirectory path to it. Change-Id: I1dc1eddbd42030eb6d5d8e76a8675f94216447c3 Reviewed-on: https://go-review.googlesource.com/c/go/+/411118 Run-TryBot: Michael Matloob TryBot-Result: Gopher Robot Reviewed-by: Russ Cox Reviewed-by: Michael Matloob --- src/cmd/go/internal/modindex/read.go | 3 +- .../script/list_replace_absolute_windows.txt | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/cmd/go/testdata/script/list_replace_absolute_windows.txt diff --git a/src/cmd/go/internal/modindex/read.go b/src/cmd/go/internal/modindex/read.go index daa85762be..6ec3a6b3af 100644 --- a/src/cmd/go/internal/modindex/read.go +++ b/src/cmd/go/internal/modindex/read.go @@ -112,6 +112,7 @@ func Get(modroot string) (*ModuleIndex, error) { if modroot == "" { panic("modindex.Get called with empty modroot") } + modroot = filepath.Clean(modroot) isModCache := str.HasFilePathPrefix(modroot, cfg.GOMODCACHE) return openIndex(modroot, isModCache) } @@ -217,7 +218,7 @@ func (mi *ModuleIndex) Packages() []string { // RelPath returns the path relative to the module's root. func (mi *ModuleIndex) RelPath(path string) string { - return str.TrimFilePathPrefix(path, mi.modroot) + return str.TrimFilePathPrefix(filepath.Clean(path), mi.modroot) // mi.modroot is already clean } // ImportPackage is the equivalent of build.Import given the information in ModuleIndex. diff --git a/src/cmd/go/testdata/script/list_replace_absolute_windows.txt b/src/cmd/go/testdata/script/list_replace_absolute_windows.txt new file mode 100644 index 0000000000..6f5d737ade --- /dev/null +++ b/src/cmd/go/testdata/script/list_replace_absolute_windows.txt @@ -0,0 +1,37 @@ +# Test a replacement with an absolute path (so the path isn't +# cleaned by having filepath.Abs called on it). This checks +# whether the modindex logic cleans the modroot path before using +# it. + +[!windows] [short] skip + +go run print_go_mod.go # use this program to write a go.mod with an absolute path +cp stdout go.mod + +go list -modfile=go.mod all +-- print_go_mod.go -- +//go:build ignore +package main + +import ( + "fmt" + "os" +) + +func main() { + work := os.Getenv("WORK") +fmt.Printf(`module example.com/mod + +require b.com v0.0.0 + +replace b.com => %s\gopath\src/modb +`, work) +} +-- a.go -- +package a + +import _ "b.com/b" +-- modb/go.mod -- +module b.com +-- modb/b/b.go -- +package b From 13f6be28338c43d3aa22a4467b34a45c40f83593 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Wed, 8 Jun 2022 15:59:37 -0400 Subject: [PATCH 066/113] runtime: use pidleget for faketime jump In faketime mode, checkdead is responsible for jumping time forward to the next timer expiration, and waking an M to handle the newly ready timer. Currently it pulls the exact P that owns the next timer off of the pidle list. In theory this is efficient because that P is immediately eligible to run the timer without stealing. Unfortunately it is also fraught with peril because we are skipping all of the bookkeeping in pidleget: * Skipped updates to timerpMask mean that our timers may not be eligible for stealing, as they should be. * Skipped updates to idlepMask mean that our runq may not be eligible for stealing, as they should be. * Skipped updates to sched.npidle may break tracking of spinning Ms, potentially resulting in lost work. * Finally, as of CL 410122, skipped updates to p.limiterEvent may affect the GC limiter, or cause a fatal throw when another event occurs. The last case has finally undercovered this issue since it quickly results in a hard crash. We could add all of these updates into checkdead, but it is much more maintainable to keep this logic in one place and use pidleget here like everywhere else in the runtime. This means we probably won't wake the P owning the timer, meaning that the P will need to steal the timer, which is less efficient, but faketime is not a performance-sensitive build mode. Note that the M will automatically make itself a spinning M to make it eligible to steal since it is the only one running. Fixes #53294 For #52890 Change-Id: I4acc3d259b9b4d7dc02608581c8b4fd259f272e9 Reviewed-on: https://go-review.googlesource.com/c/go/+/411119 Run-TryBot: Michael Pratt Auto-Submit: Michael Pratt TryBot-Result: Gopher Robot Reviewed-by: Michael Knyszek --- src/runtime/proc.go | 26 ++++++++++++++++---------- src/runtime/time.go | 11 ++++------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/runtime/proc.go b/src/runtime/proc.go index dc2957b939..3991a48b10 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -5071,14 +5071,15 @@ func checkdead() { // Maybe jump time forward for playground. if faketime != 0 { - when, _p_ := timeSleepUntil() - if _p_ != nil { + if when := timeSleepUntil(); when < maxWhen { faketime = when - for pp := &sched.pidle; *pp != 0; pp = &(*pp).ptr().link { - if (*pp).ptr() == _p_ { - *pp = _p_.link - break - } + + // Start an M to steal the timer. + pp, _ := pidleget(faketime) + if pp == nil { + // There should always be a free P since + // nothing is running. + throw("checkdead: no p for timer") } mp := mget() if mp == nil { @@ -5086,7 +5087,12 @@ func checkdead() { // nothing is running. throw("checkdead: no m for timer") } - mp.nextp.set(_p_) + // M must be spinning to steal. We set this to be + // explicit, but since this is the only M it would + // become spinning on its own anyways. + atomic.Xadd(&sched.nmspinning, 1) + mp.spinning = true + mp.nextp.set(pp) notewakeup(&mp.park) return } @@ -5158,7 +5164,7 @@ func sysmon() { lock(&sched.lock) if atomic.Load(&sched.gcwaiting) != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs) { syscallWake := false - next, _ := timeSleepUntil() + next := timeSleepUntil() if next > now { atomic.Store(&sched.sysmonwait, 1) unlock(&sched.lock) @@ -5231,7 +5237,7 @@ func sysmon() { // // See issue 42515 and // https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50094. - if next, _ := timeSleepUntil(); next < now { + if next := timeSleepUntil(); next < now { startm(nil, false) } } diff --git a/src/runtime/time.go b/src/runtime/time.go index e4d8269987..aec39083b4 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -1016,12 +1016,11 @@ func updateTimerModifiedEarliest(pp *p, nextwhen int64) { } } -// timeSleepUntil returns the time when the next timer should fire, -// and the P that holds the timer heap that that timer is on. +// timeSleepUntil returns the time when the next timer should fire. Returns +// maxWhen if there are no timers. // This is only called by sysmon and checkdead. -func timeSleepUntil() (int64, *p) { +func timeSleepUntil() int64 { next := int64(maxWhen) - var pret *p // Prevent allp slice changes. This is like retake. lock(&allpLock) @@ -1035,18 +1034,16 @@ func timeSleepUntil() (int64, *p) { w := int64(atomic.Load64(&pp.timer0When)) if w != 0 && w < next { next = w - pret = pp } w = int64(atomic.Load64(&pp.timerModifiedEarliest)) if w != 0 && w < next { next = w - pret = pp } } unlock(&allpLock) - return next, pret + return next } // Heap maintenance algorithms. From 80f86f706deff532cf3ee94a75dd1dc0db6795b2 Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Wed, 8 Jun 2022 18:08:30 -0400 Subject: [PATCH 067/113] api/next: minor reformat Add newline endings to files without them. Delete empty lines. So it is consistent and easier to put them together. Change-Id: I84e6b7a1fe59e9f4d7f00f61539f6449f19a5d40 Reviewed-on: https://go-review.googlesource.com/c/go/+/411121 Run-TryBot: Cherry Mui TryBot-Result: Gopher Robot Reviewed-by: Ian Lance Taylor --- api/next/35044.txt | 2 +- api/next/46059.txt | 1 - api/next/50062.txt | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/api/next/35044.txt b/api/next/35044.txt index 0ed6f2e4d0..5eb6381f92 100644 --- a/api/next/35044.txt +++ b/api/next/35044.txt @@ -1 +1 @@ -pkg crypto/x509, method (*CertPool) Clone() *CertPool #35044 \ No newline at end of file +pkg crypto/x509, method (*CertPool) Clone() *CertPool #35044 diff --git a/api/next/46059.txt b/api/next/46059.txt index 3cc44966a2..4c82f79f23 100644 --- a/api/next/46059.txt +++ b/api/next/46059.txt @@ -1,2 +1 @@ pkg net/url, type URL struct, OmitHost bool #46059 - diff --git a/api/next/50062.txt b/api/next/50062.txt index 0a5efcc70b..31d7d2c641 100644 --- a/api/next/50062.txt +++ b/api/next/50062.txt @@ -1 +1 @@ -pkg time, method (Time) ZoneBounds() (Time, Time) #50062 \ No newline at end of file +pkg time, method (Time) ZoneBounds() (Time, Time) #50062 From b51d44c6dd9d6f3ac3e1d275bc118aae23a5a482 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Wed, 8 Jun 2022 19:14:11 -0400 Subject: [PATCH 068/113] cmd/go/testdata/script: fix skip on list_replace_absolute_windows The test should skip if it's not on windows *or* it's a short test, but instead is now skipping if it's not on windows *and* it's a short test, causing it to be run on non-windows longtest builders. Change-Id: Ica011bab632b713b0564fefabd5b42878d401844 Reviewed-on: https://go-review.googlesource.com/c/go/+/411122 Reviewed-by: Heschi Kreinick TryBot-Result: Gopher Robot Reviewed-by: Cherry Mui Run-TryBot: Michael Matloob Auto-Submit: Michael Matloob --- src/cmd/go/testdata/script/list_replace_absolute_windows.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cmd/go/testdata/script/list_replace_absolute_windows.txt b/src/cmd/go/testdata/script/list_replace_absolute_windows.txt index 6f5d737ade..b3ff2a7c2d 100644 --- a/src/cmd/go/testdata/script/list_replace_absolute_windows.txt +++ b/src/cmd/go/testdata/script/list_replace_absolute_windows.txt @@ -3,7 +3,8 @@ # whether the modindex logic cleans the modroot path before using # it. -[!windows] [short] skip +[!windows] skip +[short] skip go run print_go_mod.go # use this program to write a go.mod with an absolute path cp stdout go.mod From 1a2ca95ad2d0e6599ab8b9772c30afbb743abc89 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Sat, 4 Jun 2022 17:01:47 -0400 Subject: [PATCH 069/113] go/types, types2: only set instance context if packages match In CL 404885, we avoid infinite expansion of type instances by sharing a context between the expanding type and new instances created during expansion. This ensures that we do not create an infinite number of identical but distinct instances in the presence of reference cycles. This pins additional memory to the new instance, but no more (approximately) than would be pinned by the original expanding instance. However, we can do better: since type cycles are only possible within a single package, we only need to share the local context if the two types are in the same package. This reduces the scope of the shared local context, and in particular can avoid pinning the package of the expanding type to the package of the newly created instance. Updates #52728 Change-Id: Iad2c85f4ecf60125f1da0ba22a7fdec7423e0338 Reviewed-on: https://go-review.googlesource.com/c/go/+/410416 Run-TryBot: Robert Findley Reviewed-by: Robert Griesemer TryBot-Result: Gopher Robot --- .../compile/internal/types2/instantiate.go | 39 ++++++++++--------- src/cmd/compile/internal/types2/named.go | 35 ++++++++++++----- src/cmd/compile/internal/types2/subst.go | 28 ++++++------- src/go/types/instantiate.go | 39 ++++++++++--------- src/go/types/named.go | 35 ++++++++++++----- src/go/types/subst.go | 28 ++++++------- 6 files changed, 120 insertions(+), 84 deletions(-) diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index 45f7e43ccf..5833f8db7e 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -63,28 +63,29 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e return inst, nil } -// instance resolves a type or function instance for the given original type -// and type arguments. It looks for an existing identical instance in the given -// contexts, creating a new instance if none is found. +// instance instantiates the given original (generic) function or type with the +// provided type arguments and returns the resulting instance. If an identical +// instance exists already in the given contexts, it returns that instance, +// otherwise it creates a new one. // -// If local is non-nil, it is the context associated with a Named instance -// type currently being expanded. If global is non-nil, it is the context -// associated with the current type-checking pass or call to Instantiate. At -// least one of local or global must be non-nil. +// If expanding is non-nil, it is the Named instance type currently being +// expanded. If ctxt is non-nil, it is the context associated with the current +// type-checking pass or call to Instantiate. At least one of expanding or ctxt +// must be non-nil. // // For Named types the resulting instance may be unexpanded. -func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, local, global *Context) (res Type) { - // The order of the contexts below matters: we always prefer instances in - // local in order to preserve reference cycles. +func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, expanding *Named, ctxt *Context) (res Type) { + // The order of the contexts below matters: we always prefer instances in the + // expanding instance context in order to preserve reference cycles. // - // Invariant: if local != nil, the returned instance will be the instance - // recorded in local. + // Invariant: if expanding != nil, the returned instance will be the instance + // recorded in expanding.inst.ctxt. var ctxts []*Context - if local != nil { - ctxts = append(ctxts, local) + if expanding != nil { + ctxts = append(ctxts, expanding.inst.ctxt) } - if global != nil { - ctxts = append(ctxts, global) + if ctxt != nil { + ctxts = append(ctxts, ctxt) } assert(len(ctxts) > 0) @@ -114,10 +115,10 @@ func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, local, g switch orig := orig.(type) { case *Named: - res = check.newNamedInstance(pos, orig, targs, local) // substituted lazily + res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily case *Signature: - assert(local == nil) // function instances cannot be reached from Named types + assert(expanding == nil) // function instances cannot be reached from Named types tparams := orig.TypeParams() if !check.validateTArgLen(pos, tparams.Len(), len(targs)) { @@ -126,7 +127,7 @@ func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, local, g if tparams.Len() == 0 { return orig // nothing to do (minor optimization) } - sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, global).(*Signature) + sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, ctxt).(*Signature) // If the signature doesn't use its type parameters, subst // will not make a copy. In that case, make a copy now (so // we can set tparams to nil w/o causing side-effects). diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index 720e500cd5..2cf6d3871f 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -230,21 +230,36 @@ func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) if obj.typ == nil { obj.typ = typ } - // Ensure that typ is always expanded and sanity-checked. + // Ensure that typ is always sanity-checked. if check != nil { check.needsCleanup(typ) } return typ } -func (check *Checker) newNamedInstance(pos syntax.Pos, orig *Named, targs []Type, local *Context) *Named { +// newNamedInstance creates a new named instance for the given origin and type +// arguments, recording pos as the position of its synthetic object (for error +// reporting). +// +// If set, expanding is the named type instance currently being expanded, that +// led to the creation of this instance. +func (check *Checker) newNamedInstance(pos syntax.Pos, orig *Named, targs []Type, expanding *Named) *Named { assert(len(targs) > 0) obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) - inst := &instance{orig: orig, targs: newTypeList(targs), ctxt: local} + inst := &instance{orig: orig, targs: newTypeList(targs)} + + // Only pass the expanding context to the new instance if their packages + // match. Since type reference cycles are only possible within a single + // package, this is sufficient for the purposes of short-circuiting cycles. + // Avoiding passing the context in other cases prevents unnecessary coupling + // of types across packages. + if expanding != nil && expanding.Obj().pkg == obj.pkg { + inst.ctxt = expanding.inst.ctxt + } typ := &Named{check: check, obj: obj, inst: inst} obj.typ = typ - // Ensure that typ is always expanded and sanity-checked. + // Ensure that typ is always sanity-checked. if check != nil { check.needsCleanup(typ) } @@ -387,11 +402,11 @@ func (t *Named) expandMethod(i int) *Func { // code. if origSig.RecvTypeParams().Len() == t.inst.targs.Len() { smap := makeSubstMap(origSig.RecvTypeParams().list(), t.inst.targs.list()) - var global *Context + var ctxt *Context if check != nil { - global = check.context() + ctxt = check.context() } - sig = check.subst(origm.pos, origSig, smap, t.inst.ctxt, global).(*Signature) + sig = check.subst(origm.pos, origSig, smap, t, ctxt).(*Signature) } if sig == origSig { @@ -601,11 +616,11 @@ func (n *Named) expandUnderlying() Type { assert(n == n2) smap := makeSubstMap(orig.tparams.list(), targs.list()) - var global *Context + var ctxt *Context if check != nil { - global = check.context() + ctxt = check.context() } - underlying := n.check.subst(n.obj.pos, orig.underlying, smap, n.inst.ctxt, global) + underlying := n.check.subst(n.obj.pos, orig.underlying, smap, n, ctxt) // If the underlying type of n is an interface, we need to set the receiver of // its methods accurately -- we set the receiver of interface methods on // the RHS of a type declaration to the defined type. diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index 4a4c8f960a..d5a48c6995 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -48,9 +48,10 @@ func (m substMap) lookup(tpar *TypeParam) Type { // incoming type. If a substitution took place, the result type is different // from the incoming type. // -// If the given context is non-nil, it is used in lieu of check.Config.Context. -func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, local, global *Context) Type { - assert(local != nil || global != nil) +// If expanding is non-nil, it is the instance type currently being expanded. +// One of expanding or ctxt must be non-nil. +func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, expanding *Named, ctxt *Context) Type { + assert(expanding != nil || ctxt != nil) if smap.empty() { return typ @@ -66,20 +67,21 @@ func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, local, glob // general case subst := subster{ - pos: pos, - smap: smap, - check: check, - local: local, - global: global, + pos: pos, + smap: smap, + check: check, + expanding: expanding, + ctxt: ctxt, } return subst.typ(typ) } type subster struct { - pos syntax.Pos - smap substMap - check *Checker // nil if called via Instantiate - local, global *Context + pos syntax.Pos + smap substMap + check *Checker // nil if called via Instantiate + expanding *Named // if non-nil, the instance that is being expanded + ctxt *Context } func (subst *subster) typ(typ Type) Type { @@ -254,7 +256,7 @@ func (subst *subster) typ(typ Type) Type { // recursion. The position used here is irrelevant because validation only // occurs on t (we don't call validType on named), but we use subst.pos to // help with debugging. - return subst.check.instance(subst.pos, orig, newTArgs, subst.local, subst.global) + return subst.check.instance(subst.pos, orig, newTArgs, subst.expanding, subst.ctxt) case *TypeParam: return subst.smap.lookup(t) diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index e6b731f241..f7505854d1 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -63,28 +63,29 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e return inst, nil } -// instance resolves a type or function instance for the given original type -// and type arguments. It looks for an existing identical instance in the given -// contexts, creating a new instance if none is found. +// instance instantiates the given original (generic) function or type with the +// provided type arguments and returns the resulting instance. If an identical +// instance exists already in the given contexts, it returns that instance, +// otherwise it creates a new one. // -// If local is non-nil, it is the context associated with a Named instance -// type currently being expanded. If global is non-nil, it is the context -// associated with the current type-checking pass or call to Instantiate. At -// least one of local or global must be non-nil. +// If expanding is non-nil, it is the Named instance type currently being +// expanded. If ctxt is non-nil, it is the context associated with the current +// type-checking pass or call to Instantiate. At least one of expanding or ctxt +// must be non-nil. // // For Named types the resulting instance may be unexpanded. -func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, local, global *Context) (res Type) { - // The order of the contexts below matters: we always prefer instances in - // local in order to preserve reference cycles. +func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, expanding *Named, ctxt *Context) (res Type) { + // The order of the contexts below matters: we always prefer instances in the + // expanding instance context in order to preserve reference cycles. // - // Invariant: if local != nil, the returned instance will be the instance - // recorded in local. + // Invariant: if expanding != nil, the returned instance will be the instance + // recorded in expanding.inst.ctxt. var ctxts []*Context - if local != nil { - ctxts = append(ctxts, local) + if expanding != nil { + ctxts = append(ctxts, expanding.inst.ctxt) } - if global != nil { - ctxts = append(ctxts, global) + if ctxt != nil { + ctxts = append(ctxts, ctxt) } assert(len(ctxts) > 0) @@ -114,10 +115,10 @@ func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, local, gl switch orig := orig.(type) { case *Named: - res = check.newNamedInstance(pos, orig, targs, local) // substituted lazily + res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily case *Signature: - assert(local == nil) // function instances cannot be reached from Named types + assert(expanding == nil) // function instances cannot be reached from Named types tparams := orig.TypeParams() if !check.validateTArgLen(pos, tparams.Len(), len(targs)) { @@ -126,7 +127,7 @@ func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, local, gl if tparams.Len() == 0 { return orig // nothing to do (minor optimization) } - sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, global).(*Signature) + sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, ctxt).(*Signature) // If the signature doesn't use its type parameters, subst // will not make a copy. In that case, make a copy now (so // we can set tparams to nil w/o causing side-effects). diff --git a/src/go/types/named.go b/src/go/types/named.go index 63f0a22323..c08997aa77 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -230,21 +230,36 @@ func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) if obj.typ == nil { obj.typ = typ } - // Ensure that typ is always expanded and sanity-checked. + // Ensure that typ is always sanity-checked. if check != nil { check.needsCleanup(typ) } return typ } -func (check *Checker) newNamedInstance(pos token.Pos, orig *Named, targs []Type, local *Context) *Named { +// newNamedInstance creates a new named instance for the given origin and type +// arguments, recording pos as the position of its synthetic object (for error +// reporting). +// +// If set, expanding is the named type instance currently being expanded, that +// led to the creation of this instance. +func (check *Checker) newNamedInstance(pos token.Pos, orig *Named, targs []Type, expanding *Named) *Named { assert(len(targs) > 0) obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) - inst := &instance{orig: orig, targs: newTypeList(targs), ctxt: local} + inst := &instance{orig: orig, targs: newTypeList(targs)} + + // Only pass the expanding context to the new instance if their packages + // match. Since type reference cycles are only possible within a single + // package, this is sufficient for the purposes of short-circuiting cycles. + // Avoiding passing the context in other cases prevents unnecessary coupling + // of types across packages. + if expanding != nil && expanding.Obj().pkg == obj.pkg { + inst.ctxt = expanding.inst.ctxt + } typ := &Named{check: check, obj: obj, inst: inst} obj.typ = typ - // Ensure that typ is always expanded and sanity-checked. + // Ensure that typ is always sanity-checked. if check != nil { check.needsCleanup(typ) } @@ -387,11 +402,11 @@ func (t *Named) expandMethod(i int) *Func { // code. if origSig.RecvTypeParams().Len() == t.inst.targs.Len() { smap := makeSubstMap(origSig.RecvTypeParams().list(), t.inst.targs.list()) - var global *Context + var ctxt *Context if check != nil { - global = check.context() + ctxt = check.context() } - sig = check.subst(origm.pos, origSig, smap, t.inst.ctxt, global).(*Signature) + sig = check.subst(origm.pos, origSig, smap, t, ctxt).(*Signature) } if sig == origSig { @@ -601,11 +616,11 @@ func (n *Named) expandUnderlying() Type { assert(n == n2) smap := makeSubstMap(orig.tparams.list(), targs.list()) - var global *Context + var ctxt *Context if check != nil { - global = check.context() + ctxt = check.context() } - underlying := n.check.subst(n.obj.pos, orig.underlying, smap, n.inst.ctxt, global) + underlying := n.check.subst(n.obj.pos, orig.underlying, smap, n, ctxt) // If the underlying type of n is an interface, we need to set the receiver of // its methods accurately -- we set the receiver of interface methods on // the RHS of a type declaration to the defined type. diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 36987a4c95..42f3619f88 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -48,9 +48,10 @@ func (m substMap) lookup(tpar *TypeParam) Type { // that it doesn't modify the incoming type. If a substitution took place, the // result type is different from the incoming type. // -// If the given context is non-nil, it is used in lieu of check.Config.Context -func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, local, global *Context) Type { - assert(local != nil || global != nil) +// If expanding is non-nil, it is the instance type currently being expanded. +// One of expanding or ctxt must be non-nil. +func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, expanding *Named, ctxt *Context) Type { + assert(expanding != nil || ctxt != nil) if smap.empty() { return typ @@ -66,20 +67,21 @@ func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, local, globa // general case subst := subster{ - pos: pos, - smap: smap, - check: check, - local: local, - global: global, + pos: pos, + smap: smap, + check: check, + expanding: expanding, + ctxt: ctxt, } return subst.typ(typ) } type subster struct { - pos token.Pos - smap substMap - check *Checker // nil if called via Instantiate - local, global *Context + pos token.Pos + smap substMap + check *Checker // nil if called via Instantiate + expanding *Named // if non-nil, the instance that is being expanded + ctxt *Context } func (subst *subster) typ(typ Type) Type { @@ -254,7 +256,7 @@ func (subst *subster) typ(typ Type) Type { // recursion. The position used here is irrelevant because validation only // occurs on t (we don't call validType on named), but we use subst.pos to // help with debugging. - return subst.check.instance(subst.pos, orig, newTArgs, subst.local, subst.global) + return subst.check.instance(subst.pos, orig, newTArgs, subst.expanding, subst.ctxt) case *TypeParam: return subst.smap.lookup(t) From 840e99ed742e55ddd00a57210a706e14234c8bc5 Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Thu, 9 Jun 2022 10:59:05 -0400 Subject: [PATCH 070/113] api: promote next to go1.19 Change-Id: I3d80f0691b399fe4ee4a0d161b5cee907ae6b94f Reviewed-on: https://go-review.googlesource.com/c/go/+/411394 Reviewed-by: Heschi Kreinick Reviewed-by: Dmitri Shuralyov Run-TryBot: Cherry Mui TryBot-Result: Gopher Robot --- api/go1.19.txt | 288 +++++++++++++++++++++++++++++++++++++++++++++ api/next/30715.txt | 3 - api/next/35044.txt | 1 - api/next/42710.txt | 2 - api/next/43401.txt | 1 - api/next/43724.txt | 2 - api/next/45628.txt | 1 - api/next/45754.txt | 2 - api/next/46057.txt | 1 - api/next/46059.txt | 1 - api/next/46121.txt | 2 - api/next/46229.txt | 105 ----------------- api/next/47005.txt | 2 - api/next/47579.txt | 3 - api/next/48409.txt | 1 - api/next/50062.txt | 1 - api/next/50340.txt | 1 - api/next/50599.txt | 1 - api/next/50601.txt | 5 - api/next/50674.txt | 9 -- api/next/50860.txt | 40 ------- api/next/51082.txt | 61 ---------- api/next/51414.txt | 1 - api/next/51644.txt | 2 - api/next/51682.txt | 2 - api/next/51684.txt | 2 - api/next/51868.txt | 36 ------ 27 files changed, 288 insertions(+), 288 deletions(-) create mode 100644 api/go1.19.txt delete mode 100644 api/next/30715.txt delete mode 100644 api/next/35044.txt delete mode 100644 api/next/42710.txt delete mode 100644 api/next/43401.txt delete mode 100644 api/next/43724.txt delete mode 100644 api/next/45628.txt delete mode 100644 api/next/45754.txt delete mode 100644 api/next/46057.txt delete mode 100644 api/next/46059.txt delete mode 100644 api/next/46121.txt delete mode 100644 api/next/46229.txt delete mode 100644 api/next/47005.txt delete mode 100644 api/next/47579.txt delete mode 100644 api/next/48409.txt delete mode 100644 api/next/50062.txt delete mode 100644 api/next/50340.txt delete mode 100644 api/next/50599.txt delete mode 100644 api/next/50601.txt delete mode 100644 api/next/50674.txt delete mode 100644 api/next/50860.txt delete mode 100644 api/next/51082.txt delete mode 100644 api/next/51414.txt delete mode 100644 api/next/51644.txt delete mode 100644 api/next/51682.txt delete mode 100644 api/next/51684.txt delete mode 100644 api/next/51868.txt diff --git a/api/go1.19.txt b/api/go1.19.txt new file mode 100644 index 0000000000..98f252281f --- /dev/null +++ b/api/go1.19.txt @@ -0,0 +1,288 @@ +pkg crypto/x509, func ParseRevocationList([]uint8) (*RevocationList, error) #50674 +pkg crypto/x509, method (*CertPool) Clone() *CertPool #35044 +pkg crypto/x509, method (*CertPool) Equal(*CertPool) bool #46057 +pkg crypto/x509, method (*RevocationList) CheckSignatureFrom(*Certificate) error #50674 +pkg crypto/x509, type RevocationList struct, AuthorityKeyId []uint8 #50674 +pkg crypto/x509, type RevocationList struct, Extensions []pkix.Extension #50674 +pkg crypto/x509, type RevocationList struct, Issuer pkix.Name #50674 +pkg crypto/x509, type RevocationList struct, Raw []uint8 #50674 +pkg crypto/x509, type RevocationList struct, RawIssuer []uint8 #50674 +pkg crypto/x509, type RevocationList struct, RawTBSRevocationList []uint8 #50674 +pkg crypto/x509, type RevocationList struct, Signature []uint8 #50674 +pkg debug/elf, const EM_LOONGARCH = 258 #46229 +pkg debug/elf, const EM_LOONGARCH Machine #46229 +pkg debug/elf, const R_LARCH_32 = 1 #46229 +pkg debug/elf, const R_LARCH_32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_64 = 2 #46229 +pkg debug/elf, const R_LARCH_64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD16 = 48 #46229 +pkg debug/elf, const R_LARCH_ADD16 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD24 = 49 #46229 +pkg debug/elf, const R_LARCH_ADD24 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD32 = 50 #46229 +pkg debug/elf, const R_LARCH_ADD32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD64 = 51 #46229 +pkg debug/elf, const R_LARCH_ADD64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD8 = 47 #46229 +pkg debug/elf, const R_LARCH_ADD8 R_LARCH #46229 +pkg debug/elf, const R_LARCH_COPY = 4 #46229 +pkg debug/elf, const R_LARCH_COPY R_LARCH #46229 +pkg debug/elf, const R_LARCH_IRELATIVE = 12 #46229 +pkg debug/elf, const R_LARCH_IRELATIVE R_LARCH #46229 +pkg debug/elf, const R_LARCH_JUMP_SLOT = 5 #46229 +pkg debug/elf, const R_LARCH_JUMP_SLOT R_LARCH #46229 +pkg debug/elf, const R_LARCH_MARK_LA = 20 #46229 +pkg debug/elf, const R_LARCH_MARK_LA R_LARCH #46229 +pkg debug/elf, const R_LARCH_MARK_PCREL = 21 #46229 +pkg debug/elf, const R_LARCH_MARK_PCREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_NONE = 0 #46229 +pkg debug/elf, const R_LARCH_NONE R_LARCH #46229 +pkg debug/elf, const R_LARCH_RELATIVE = 3 #46229 +pkg debug/elf, const R_LARCH_RELATIVE R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_ADD = 35 #46229 +pkg debug/elf, const R_LARCH_SOP_ADD R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_AND = 36 #46229 +pkg debug/elf, const R_LARCH_SOP_AND R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_ASSERT = 30 #46229 +pkg debug/elf, const R_LARCH_SOP_ASSERT R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_IF_ELSE = 37 #46229 +pkg debug/elf, const R_LARCH_SOP_IF_ELSE R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_NOT = 31 #46229 +pkg debug/elf, const R_LARCH_SOP_NOT R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_10_10_16_S2 = 45 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_10_10_16_S2 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_5_10_16_S2 = 44 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_5_10_16_S2 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_12 = 40 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_12 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16 = 41 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16_S2 = 42 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16_S2 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_5 = 38 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_5 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_5_20 = 43 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_5_20 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U = 46 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U_10_12 = 39 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U_10_12 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_ABSOLUTE = 23 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_ABSOLUTE R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_DUP = 24 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_DUP R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_GPREL = 25 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_GPREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PCREL = 22 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PCREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PLT_PCREL = 29 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PLT_PCREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GD = 28 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GD R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GOT = 27 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GOT R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_TPREL = 26 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_TPREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_SL = 33 #46229 +pkg debug/elf, const R_LARCH_SOP_SL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_SR = 34 #46229 +pkg debug/elf, const R_LARCH_SOP_SR R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_SUB = 32 #46229 +pkg debug/elf, const R_LARCH_SOP_SUB R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB16 = 53 #46229 +pkg debug/elf, const R_LARCH_SUB16 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB24 = 54 #46229 +pkg debug/elf, const R_LARCH_SUB24 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB32 = 55 #46229 +pkg debug/elf, const R_LARCH_SUB32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB64 = 56 #46229 +pkg debug/elf, const R_LARCH_SUB64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB8 = 52 #46229 +pkg debug/elf, const R_LARCH_SUB8 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD32 = 6 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD64 = 7 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL32 = 8 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL64 = 9 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL32 = 10 #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL64 = 11 #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL64 R_LARCH #46229 +pkg debug/elf, method (R_LARCH) GoString() string #46229 +pkg debug/elf, method (R_LARCH) String() string #46229 +pkg debug/elf, type R_LARCH int #46229 +pkg debug/pe, const IMAGE_COMDAT_SELECT_ANY = 2 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_ANY ideal-int #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_ASSOCIATIVE = 5 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_ASSOCIATIVE ideal-int #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_EXACT_MATCH = 4 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_EXACT_MATCH ideal-int #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_LARGEST = 6 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_LARGEST ideal-int #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_NODUPLICATES = 1 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_NODUPLICATES ideal-int #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_SAME_SIZE = 3 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_SAME_SIZE ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_CNT_CODE = 32 #51686 +pkg debug/pe, const IMAGE_SCN_CNT_CODE ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_CNT_INITIALIZED_DATA = 64 #51686 +pkg debug/pe, const IMAGE_SCN_CNT_INITIALIZED_DATA ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_CNT_UNINITIALIZED_DATA = 128 #51686 +pkg debug/pe, const IMAGE_SCN_CNT_UNINITIALIZED_DATA ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_LNK_COMDAT = 4096 #51686 +pkg debug/pe, const IMAGE_SCN_LNK_COMDAT ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_MEM_DISCARDABLE = 33554432 #51686 +pkg debug/pe, const IMAGE_SCN_MEM_DISCARDABLE ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_MEM_EXECUTE = 536870912 #51686 +pkg debug/pe, const IMAGE_SCN_MEM_EXECUTE ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_MEM_READ = 1073741824 #51686 +pkg debug/pe, const IMAGE_SCN_MEM_READ ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_MEM_WRITE = 2147483648 #51686 +pkg debug/pe, const IMAGE_SCN_MEM_WRITE ideal-int #51686 +pkg debug/pe, method (*File) COFFSymbolReadSectionDefAux(int) (*COFFSymbolAuxFormat5, error) #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, Checksum uint32 #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, NumLineNumbers uint16 #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, NumRelocs uint16 #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, SecNum uint16 #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, Selection uint8 #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, Size uint32 #51686 +pkg encoding/binary, func AppendUvarint([]uint8, uint64) []uint8 #51644 +pkg encoding/binary, func AppendVarint([]uint8, int64) []uint8 #51644 +pkg encoding/binary, type AppendByteOrder interface { AppendUint16, AppendUint32, AppendUint64, String } #50601 +pkg encoding/binary, type AppendByteOrder interface, AppendUint16([]uint8, uint16) []uint8 #50601 +pkg encoding/binary, type AppendByteOrder interface, AppendUint32([]uint8, uint32) []uint8 #50601 +pkg encoding/binary, type AppendByteOrder interface, AppendUint64([]uint8, uint64) []uint8 #50601 +pkg encoding/binary, type AppendByteOrder interface, String() string #50601 +pkg encoding/csv, method (*Reader) InputOffset() int64 #43401 +pkg encoding/xml, method (*Decoder) InputPos() (int, int) #45628 +pkg flag, func TextVar(encoding.TextUnmarshaler, string, encoding.TextMarshaler, string) #45754 +pkg flag, method (*FlagSet) TextVar(encoding.TextUnmarshaler, string, encoding.TextMarshaler, string) #45754 +pkg fmt, func Append([]uint8, ...interface{}) []uint8 #47579 +pkg fmt, func Appendf([]uint8, string, ...interface{}) []uint8 #47579 +pkg fmt, func Appendln([]uint8, ...interface{}) []uint8 #47579 +pkg go/doc, method (*Package) HTML(string) []uint8 #51082 +pkg go/doc, method (*Package) Markdown(string) []uint8 #51082 +pkg go/doc, method (*Package) Parser() *comment.Parser #51082 +pkg go/doc, method (*Package) Printer() *comment.Printer #51082 +pkg go/doc, method (*Package) Synopsis(string) string #51082 +pkg go/doc, method (*Package) Text(string) []uint8 #51082 +pkg go/doc/comment, func DefaultLookupPackage(string) (string, bool) #51082 +pkg go/doc/comment, method (*DocLink) DefaultURL(string) string #51082 +pkg go/doc/comment, method (*Heading) DefaultID() string #51082 +pkg go/doc/comment, method (*List) BlankBefore() bool #51082 +pkg go/doc/comment, method (*List) BlankBetween() bool #51082 +pkg go/doc/comment, method (*Parser) Parse(string) *Doc #51082 +pkg go/doc/comment, method (*Printer) Comment(*Doc) []uint8 #51082 +pkg go/doc/comment, method (*Printer) HTML(*Doc) []uint8 #51082 +pkg go/doc/comment, method (*Printer) Markdown(*Doc) []uint8 #51082 +pkg go/doc/comment, method (*Printer) Text(*Doc) []uint8 #51082 +pkg go/doc/comment, type Block interface, unexported methods #51082 +pkg go/doc/comment, type Code struct #51082 +pkg go/doc/comment, type Code struct, Text string #51082 +pkg go/doc/comment, type Doc struct #51082 +pkg go/doc/comment, type Doc struct, Content []Block #51082 +pkg go/doc/comment, type Doc struct, Links []*LinkDef #51082 +pkg go/doc/comment, type DocLink struct #51082 +pkg go/doc/comment, type DocLink struct, ImportPath string #51082 +pkg go/doc/comment, type DocLink struct, Name string #51082 +pkg go/doc/comment, type DocLink struct, Recv string #51082 +pkg go/doc/comment, type DocLink struct, Text []Text #51082 +pkg go/doc/comment, type Heading struct #51082 +pkg go/doc/comment, type Heading struct, Text []Text #51082 +pkg go/doc/comment, type Italic string #51082 +pkg go/doc/comment, type Link struct #51082 +pkg go/doc/comment, type Link struct, Auto bool #51082 +pkg go/doc/comment, type Link struct, Text []Text #51082 +pkg go/doc/comment, type Link struct, URL string #51082 +pkg go/doc/comment, type LinkDef struct #51082 +pkg go/doc/comment, type LinkDef struct, Text string #51082 +pkg go/doc/comment, type LinkDef struct, URL string #51082 +pkg go/doc/comment, type LinkDef struct, Used bool #51082 +pkg go/doc/comment, type List struct #51082 +pkg go/doc/comment, type List struct, ForceBlankBefore bool #51082 +pkg go/doc/comment, type List struct, ForceBlankBetween bool #51082 +pkg go/doc/comment, type List struct, Items []*ListItem #51082 +pkg go/doc/comment, type ListItem struct #51082 +pkg go/doc/comment, type ListItem struct, Content []Block #51082 +pkg go/doc/comment, type ListItem struct, Number string #51082 +pkg go/doc/comment, type Paragraph struct #51082 +pkg go/doc/comment, type Paragraph struct, Text []Text #51082 +pkg go/doc/comment, type Parser struct #51082 +pkg go/doc/comment, type Parser struct, LookupPackage func(string) (string, bool) #51082 +pkg go/doc/comment, type Parser struct, LookupSym func(string, string) bool #51082 +pkg go/doc/comment, type Parser struct, Words map[string]string #51082 +pkg go/doc/comment, type Plain string #51082 +pkg go/doc/comment, type Printer struct #51082 +pkg go/doc/comment, type Printer struct, DocLinkBaseURL string #51082 +pkg go/doc/comment, type Printer struct, DocLinkURL func(*DocLink) string #51082 +pkg go/doc/comment, type Printer struct, HeadingID func(*Heading) string #51082 +pkg go/doc/comment, type Printer struct, HeadingLevel int #51082 +pkg go/doc/comment, type Printer struct, TextCodePrefix string #51082 +pkg go/doc/comment, type Printer struct, TextPrefix string #51082 +pkg go/doc/comment, type Printer struct, TextWidth int #51082 +pkg go/doc/comment, type Text interface, unexported methods #51082 +pkg go/types, method (*Func) Origin() *Func #51682 +pkg go/types, method (*Var) Origin() *Var #51682 +pkg hash/maphash, func Bytes(Seed, []uint8) uint64 #42710 +pkg hash/maphash, func String(Seed, string) uint64 #42710 +pkg html/template, method (*Template) Funcs(template.FuncMap) *Template #46121 +pkg html/template, type FuncMap = template.FuncMap #46121 +pkg net/http, method (*MaxBytesError) Error() string #30715 +pkg net/http, type MaxBytesError struct #30715 +pkg net/http, type MaxBytesError struct, Limit int64 #30715 +pkg net/url, func JoinPath(string, ...string) (string, error) #47005 +pkg net/url, method (*URL) JoinPath(...string) *URL #47005 +pkg net/url, type URL struct, OmitHost bool #46059 +pkg os/exec, method (*Cmd) Environ() []string #50599 +pkg os/exec, type Cmd struct, Err error #43724 +pkg os/exec, var ErrDot error #43724 +pkg regexp/syntax, const ErrNestingDepth = "expression nests too deeply" #51684 +pkg regexp/syntax, const ErrNestingDepth ErrorCode #51684 +pkg runtime/debug, func SetMemoryLimit(int64) int64 #48409 +pkg sort, func Find(int, func(int) int) (int, bool) #50340 +pkg sync/atomic, method (*Bool) CompareAndSwap(bool, bool) bool #50860 +pkg sync/atomic, method (*Bool) Load() bool #50860 +pkg sync/atomic, method (*Bool) Store(bool) #50860 +pkg sync/atomic, method (*Bool) Swap(bool) bool #50860 +pkg sync/atomic, method (*Int32) Add(int32) int32 #50860 +pkg sync/atomic, method (*Int32) CompareAndSwap(int32, int32) bool #50860 +pkg sync/atomic, method (*Int32) Load() int32 #50860 +pkg sync/atomic, method (*Int32) Store(int32) #50860 +pkg sync/atomic, method (*Int32) Swap(int32) int32 #50860 +pkg sync/atomic, method (*Int64) Add(int64) int64 #50860 +pkg sync/atomic, method (*Int64) CompareAndSwap(int64, int64) bool #50860 +pkg sync/atomic, method (*Int64) Load() int64 #50860 +pkg sync/atomic, method (*Int64) Store(int64) #50860 +pkg sync/atomic, method (*Int64) Swap(int64) int64 #50860 +pkg sync/atomic, method (*Pointer[$0]) CompareAndSwap(*$0, *$0) bool #50860 +pkg sync/atomic, method (*Pointer[$0]) Load() *$0 #50860 +pkg sync/atomic, method (*Pointer[$0]) Store(*$0) #50860 +pkg sync/atomic, method (*Pointer[$0]) Swap(*$0) *$0 #50860 +pkg sync/atomic, method (*Uint32) Add(uint32) uint32 #50860 +pkg sync/atomic, method (*Uint32) CompareAndSwap(uint32, uint32) bool #50860 +pkg sync/atomic, method (*Uint32) Load() uint32 #50860 +pkg sync/atomic, method (*Uint32) Store(uint32) #50860 +pkg sync/atomic, method (*Uint32) Swap(uint32) uint32 #50860 +pkg sync/atomic, method (*Uint64) Add(uint64) uint64 #50860 +pkg sync/atomic, method (*Uint64) CompareAndSwap(uint64, uint64) bool #50860 +pkg sync/atomic, method (*Uint64) Load() uint64 #50860 +pkg sync/atomic, method (*Uint64) Store(uint64) #50860 +pkg sync/atomic, method (*Uint64) Swap(uint64) uint64 #50860 +pkg sync/atomic, method (*Uintptr) Add(uintptr) uintptr #50860 +pkg sync/atomic, method (*Uintptr) CompareAndSwap(uintptr, uintptr) bool #50860 +pkg sync/atomic, method (*Uintptr) Load() uintptr #50860 +pkg sync/atomic, method (*Uintptr) Store(uintptr) #50860 +pkg sync/atomic, method (*Uintptr) Swap(uintptr) uintptr #50860 +pkg sync/atomic, type Bool struct #50860 +pkg sync/atomic, type Int32 struct #50860 +pkg sync/atomic, type Int64 struct #50860 +pkg sync/atomic, type Pointer[$0 interface{}] struct #50860 +pkg sync/atomic, type Uint32 struct #50860 +pkg sync/atomic, type Uint64 struct #50860 +pkg sync/atomic, type Uintptr struct #50860 +pkg time, method (Duration) Abs() Duration #51414 +pkg time, method (Time) ZoneBounds() (Time, Time) #50062 diff --git a/api/next/30715.txt b/api/next/30715.txt deleted file mode 100644 index 077a8d136f..0000000000 --- a/api/next/30715.txt +++ /dev/null @@ -1,3 +0,0 @@ -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/35044.txt b/api/next/35044.txt deleted file mode 100644 index 5eb6381f92..0000000000 --- a/api/next/35044.txt +++ /dev/null @@ -1 +0,0 @@ -pkg crypto/x509, method (*CertPool) Clone() *CertPool #35044 diff --git a/api/next/42710.txt b/api/next/42710.txt deleted file mode 100644 index 7879758d16..0000000000 --- a/api/next/42710.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg hash/maphash, func Bytes(Seed, []uint8) uint64 #42710 -pkg hash/maphash, func String(Seed, string) uint64 #42710 diff --git a/api/next/43401.txt b/api/next/43401.txt deleted file mode 100644 index 832e60173b..0000000000 --- a/api/next/43401.txt +++ /dev/null @@ -1 +0,0 @@ -pkg encoding/csv, method (*Reader) InputOffset() int64 #43401 diff --git a/api/next/43724.txt b/api/next/43724.txt deleted file mode 100644 index 1030a80585..0000000000 --- a/api/next/43724.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg os/exec, type Cmd struct, Err error #43724 -pkg os/exec, var ErrDot error #43724 diff --git a/api/next/45628.txt b/api/next/45628.txt deleted file mode 100644 index 5065ae4a60..0000000000 --- a/api/next/45628.txt +++ /dev/null @@ -1 +0,0 @@ -pkg encoding/xml, method (*Decoder) InputPos() (int, int) #45628 diff --git a/api/next/45754.txt b/api/next/45754.txt deleted file mode 100644 index e980342c04..0000000000 --- a/api/next/45754.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg flag, func TextVar(encoding.TextUnmarshaler, string, encoding.TextMarshaler, string) #45754 -pkg flag, method (*FlagSet) TextVar(encoding.TextUnmarshaler, string, encoding.TextMarshaler, string) #45754 diff --git a/api/next/46057.txt b/api/next/46057.txt deleted file mode 100644 index d971aa7ffd..0000000000 --- a/api/next/46057.txt +++ /dev/null @@ -1 +0,0 @@ -pkg crypto/x509, method (*CertPool) Equal(*CertPool) bool #46057 diff --git a/api/next/46059.txt b/api/next/46059.txt deleted file mode 100644 index 4c82f79f23..0000000000 --- a/api/next/46059.txt +++ /dev/null @@ -1 +0,0 @@ -pkg net/url, type URL struct, OmitHost bool #46059 diff --git a/api/next/46121.txt b/api/next/46121.txt deleted file mode 100644 index a50d6456c8..0000000000 --- a/api/next/46121.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg html/template, method (*Template) Funcs(template.FuncMap) *Template #46121 -pkg html/template, type FuncMap = template.FuncMap #46121 diff --git a/api/next/46229.txt b/api/next/46229.txt deleted file mode 100644 index ebaaefda55..0000000000 --- a/api/next/46229.txt +++ /dev/null @@ -1,105 +0,0 @@ -pkg debug/elf, const EM_LOONGARCH = 258 #46229 -pkg debug/elf, const EM_LOONGARCH Machine #46229 -pkg debug/elf, const R_LARCH_32 = 1 #46229 -pkg debug/elf, const R_LARCH_32 R_LARCH #46229 -pkg debug/elf, const R_LARCH_64 = 2 #46229 -pkg debug/elf, const R_LARCH_64 R_LARCH #46229 -pkg debug/elf, const R_LARCH_ADD16 = 48 #46229 -pkg debug/elf, const R_LARCH_ADD16 R_LARCH #46229 -pkg debug/elf, const R_LARCH_ADD24 = 49 #46229 -pkg debug/elf, const R_LARCH_ADD24 R_LARCH #46229 -pkg debug/elf, const R_LARCH_ADD32 = 50 #46229 -pkg debug/elf, const R_LARCH_ADD32 R_LARCH #46229 -pkg debug/elf, const R_LARCH_ADD64 = 51 #46229 -pkg debug/elf, const R_LARCH_ADD64 R_LARCH #46229 -pkg debug/elf, const R_LARCH_ADD8 = 47 #46229 -pkg debug/elf, const R_LARCH_ADD8 R_LARCH #46229 -pkg debug/elf, const R_LARCH_COPY = 4 #46229 -pkg debug/elf, const R_LARCH_COPY R_LARCH #46229 -pkg debug/elf, const R_LARCH_IRELATIVE = 12 #46229 -pkg debug/elf, const R_LARCH_IRELATIVE R_LARCH #46229 -pkg debug/elf, const R_LARCH_JUMP_SLOT = 5 #46229 -pkg debug/elf, const R_LARCH_JUMP_SLOT R_LARCH #46229 -pkg debug/elf, const R_LARCH_MARK_LA = 20 #46229 -pkg debug/elf, const R_LARCH_MARK_LA R_LARCH #46229 -pkg debug/elf, const R_LARCH_MARK_PCREL = 21 #46229 -pkg debug/elf, const R_LARCH_MARK_PCREL R_LARCH #46229 -pkg debug/elf, const R_LARCH_NONE = 0 #46229 -pkg debug/elf, const R_LARCH_NONE R_LARCH #46229 -pkg debug/elf, const R_LARCH_RELATIVE = 3 #46229 -pkg debug/elf, const R_LARCH_RELATIVE R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_ADD = 35 #46229 -pkg debug/elf, const R_LARCH_SOP_ADD R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_AND = 36 #46229 -pkg debug/elf, const R_LARCH_SOP_AND R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_ASSERT = 30 #46229 -pkg debug/elf, const R_LARCH_SOP_ASSERT R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_IF_ELSE = 37 #46229 -pkg debug/elf, const R_LARCH_SOP_IF_ELSE R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_NOT = 31 #46229 -pkg debug/elf, const R_LARCH_SOP_NOT R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_10_10_16_S2 = 45 #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_10_10_16_S2 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_5_10_16_S2 = 44 #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_5_10_16_S2 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_12 = 40 #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_12 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16 = 41 #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16_S2 = 42 #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16_S2 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_5 = 38 #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_5 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_5_20 = 43 #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_S_5_20 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_U = 46 #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_U R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_U_10_12 = 39 #46229 -pkg debug/elf, const R_LARCH_SOP_POP_32_U_10_12 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_ABSOLUTE = 23 #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_ABSOLUTE R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_DUP = 24 #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_DUP R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_GPREL = 25 #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_GPREL R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_PCREL = 22 #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_PCREL R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_PLT_PCREL = 29 #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_PLT_PCREL R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GD = 28 #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GD R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GOT = 27 #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GOT R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_TPREL = 26 #46229 -pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_TPREL R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_SL = 33 #46229 -pkg debug/elf, const R_LARCH_SOP_SL R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_SR = 34 #46229 -pkg debug/elf, const R_LARCH_SOP_SR R_LARCH #46229 -pkg debug/elf, const R_LARCH_SOP_SUB = 32 #46229 -pkg debug/elf, const R_LARCH_SOP_SUB R_LARCH #46229 -pkg debug/elf, const R_LARCH_SUB16 = 53 #46229 -pkg debug/elf, const R_LARCH_SUB16 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SUB24 = 54 #46229 -pkg debug/elf, const R_LARCH_SUB24 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SUB32 = 55 #46229 -pkg debug/elf, const R_LARCH_SUB32 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SUB64 = 56 #46229 -pkg debug/elf, const R_LARCH_SUB64 R_LARCH #46229 -pkg debug/elf, const R_LARCH_SUB8 = 52 #46229 -pkg debug/elf, const R_LARCH_SUB8 R_LARCH #46229 -pkg debug/elf, const R_LARCH_TLS_DTPMOD32 = 6 #46229 -pkg debug/elf, const R_LARCH_TLS_DTPMOD32 R_LARCH #46229 -pkg debug/elf, const R_LARCH_TLS_DTPMOD64 = 7 #46229 -pkg debug/elf, const R_LARCH_TLS_DTPMOD64 R_LARCH #46229 -pkg debug/elf, const R_LARCH_TLS_DTPREL32 = 8 #46229 -pkg debug/elf, const R_LARCH_TLS_DTPREL32 R_LARCH #46229 -pkg debug/elf, const R_LARCH_TLS_DTPREL64 = 9 #46229 -pkg debug/elf, const R_LARCH_TLS_DTPREL64 R_LARCH #46229 -pkg debug/elf, const R_LARCH_TLS_TPREL32 = 10 #46229 -pkg debug/elf, const R_LARCH_TLS_TPREL32 R_LARCH #46229 -pkg debug/elf, const R_LARCH_TLS_TPREL64 = 11 #46229 -pkg debug/elf, const R_LARCH_TLS_TPREL64 R_LARCH #46229 -pkg debug/elf, method (R_LARCH) GoString() string #46229 -pkg debug/elf, method (R_LARCH) String() string #46229 -pkg debug/elf, type R_LARCH int #46229 diff --git a/api/next/47005.txt b/api/next/47005.txt deleted file mode 100644 index 0d7695e45c..0000000000 --- a/api/next/47005.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg net/url, func JoinPath(string, ...string) (string, error) #47005 -pkg net/url, method (*URL) JoinPath(...string) *URL #47005 diff --git a/api/next/47579.txt b/api/next/47579.txt deleted file mode 100644 index a5d4d9f59c..0000000000 --- a/api/next/47579.txt +++ /dev/null @@ -1,3 +0,0 @@ -pkg fmt, func Append([]uint8, ...interface{}) []uint8 #47579 -pkg fmt, func Appendf([]uint8, string, ...interface{}) []uint8 #47579 -pkg fmt, func Appendln([]uint8, ...interface{}) []uint8 #47579 diff --git a/api/next/48409.txt b/api/next/48409.txt deleted file mode 100644 index 1acd9024b0..0000000000 --- a/api/next/48409.txt +++ /dev/null @@ -1 +0,0 @@ -pkg runtime/debug, func SetMemoryLimit(int64) int64 #48409 diff --git a/api/next/50062.txt b/api/next/50062.txt deleted file mode 100644 index 31d7d2c641..0000000000 --- a/api/next/50062.txt +++ /dev/null @@ -1 +0,0 @@ -pkg time, method (Time) ZoneBounds() (Time, Time) #50062 diff --git a/api/next/50340.txt b/api/next/50340.txt deleted file mode 100644 index 211392cd25..0000000000 --- a/api/next/50340.txt +++ /dev/null @@ -1 +0,0 @@ -pkg sort, func Find(int, func(int) int) (int, bool) #50340 diff --git a/api/next/50599.txt b/api/next/50599.txt deleted file mode 100644 index be271ea5e4..0000000000 --- a/api/next/50599.txt +++ /dev/null @@ -1 +0,0 @@ -pkg os/exec, method (*Cmd) Environ() []string #50599 diff --git a/api/next/50601.txt b/api/next/50601.txt deleted file mode 100644 index 261dce375d..0000000000 --- a/api/next/50601.txt +++ /dev/null @@ -1,5 +0,0 @@ -pkg encoding/binary, type AppendByteOrder interface { AppendUint16, AppendUint32, AppendUint64, String } #50601 -pkg encoding/binary, type AppendByteOrder interface, AppendUint16([]uint8, uint16) []uint8 #50601 -pkg encoding/binary, type AppendByteOrder interface, AppendUint32([]uint8, uint32) []uint8 #50601 -pkg encoding/binary, type AppendByteOrder interface, AppendUint64([]uint8, uint64) []uint8 #50601 -pkg encoding/binary, type AppendByteOrder interface, String() string #50601 diff --git a/api/next/50674.txt b/api/next/50674.txt deleted file mode 100644 index 6b5bca3a9d..0000000000 --- a/api/next/50674.txt +++ /dev/null @@ -1,9 +0,0 @@ -pkg crypto/x509, func ParseRevocationList([]uint8) (*RevocationList, error) #50674 -pkg crypto/x509, method (*RevocationList) CheckSignatureFrom(*Certificate) error #50674 -pkg crypto/x509, type RevocationList struct, AuthorityKeyId []uint8 #50674 -pkg crypto/x509, type RevocationList struct, Extensions []pkix.Extension #50674 -pkg crypto/x509, type RevocationList struct, Issuer pkix.Name #50674 -pkg crypto/x509, type RevocationList struct, Raw []uint8 #50674 -pkg crypto/x509, type RevocationList struct, RawIssuer []uint8 #50674 -pkg crypto/x509, type RevocationList struct, RawTBSRevocationList []uint8 #50674 -pkg crypto/x509, type RevocationList struct, Signature []uint8 #50674 diff --git a/api/next/50860.txt b/api/next/50860.txt deleted file mode 100644 index 9ff0feca24..0000000000 --- a/api/next/50860.txt +++ /dev/null @@ -1,40 +0,0 @@ -pkg sync/atomic, method (*Bool) CompareAndSwap(bool, bool) bool #50860 -pkg sync/atomic, method (*Bool) Load() bool #50860 -pkg sync/atomic, method (*Bool) Store(bool) #50860 -pkg sync/atomic, method (*Bool) Swap(bool) bool #50860 -pkg sync/atomic, method (*Int32) Add(int32) int32 #50860 -pkg sync/atomic, method (*Int32) CompareAndSwap(int32, int32) bool #50860 -pkg sync/atomic, method (*Int32) Load() int32 #50860 -pkg sync/atomic, method (*Int32) Store(int32) #50860 -pkg sync/atomic, method (*Int32) Swap(int32) int32 #50860 -pkg sync/atomic, method (*Int64) Add(int64) int64 #50860 -pkg sync/atomic, method (*Int64) CompareAndSwap(int64, int64) bool #50860 -pkg sync/atomic, method (*Int64) Load() int64 #50860 -pkg sync/atomic, method (*Int64) Store(int64) #50860 -pkg sync/atomic, method (*Int64) Swap(int64) int64 #50860 -pkg sync/atomic, method (*Pointer[$0]) CompareAndSwap(*$0, *$0) bool #50860 -pkg sync/atomic, method (*Pointer[$0]) Load() *$0 #50860 -pkg sync/atomic, method (*Pointer[$0]) Store(*$0) #50860 -pkg sync/atomic, method (*Pointer[$0]) Swap(*$0) *$0 #50860 -pkg sync/atomic, method (*Uint32) Add(uint32) uint32 #50860 -pkg sync/atomic, method (*Uint32) CompareAndSwap(uint32, uint32) bool #50860 -pkg sync/atomic, method (*Uint32) Load() uint32 #50860 -pkg sync/atomic, method (*Uint32) Store(uint32) #50860 -pkg sync/atomic, method (*Uint32) Swap(uint32) uint32 #50860 -pkg sync/atomic, method (*Uint64) Add(uint64) uint64 #50860 -pkg sync/atomic, method (*Uint64) CompareAndSwap(uint64, uint64) bool #50860 -pkg sync/atomic, method (*Uint64) Load() uint64 #50860 -pkg sync/atomic, method (*Uint64) Store(uint64) #50860 -pkg sync/atomic, method (*Uint64) Swap(uint64) uint64 #50860 -pkg sync/atomic, method (*Uintptr) Add(uintptr) uintptr #50860 -pkg sync/atomic, method (*Uintptr) CompareAndSwap(uintptr, uintptr) bool #50860 -pkg sync/atomic, method (*Uintptr) Load() uintptr #50860 -pkg sync/atomic, method (*Uintptr) Store(uintptr) #50860 -pkg sync/atomic, method (*Uintptr) Swap(uintptr) uintptr #50860 -pkg sync/atomic, type Bool struct #50860 -pkg sync/atomic, type Int32 struct #50860 -pkg sync/atomic, type Int64 struct #50860 -pkg sync/atomic, type Pointer[$0 interface{}] struct #50860 -pkg sync/atomic, type Uint32 struct #50860 -pkg sync/atomic, type Uint64 struct #50860 -pkg sync/atomic, type Uintptr struct #50860 diff --git a/api/next/51082.txt b/api/next/51082.txt deleted file mode 100644 index b05997f985..0000000000 --- a/api/next/51082.txt +++ /dev/null @@ -1,61 +0,0 @@ -pkg go/doc, method (*Package) HTML(string) []uint8 #51082 -pkg go/doc, method (*Package) Markdown(string) []uint8 #51082 -pkg go/doc, method (*Package) Parser() *comment.Parser #51082 -pkg go/doc, method (*Package) Printer() *comment.Printer #51082 -pkg go/doc, method (*Package) Synopsis(string) string #51082 -pkg go/doc, method (*Package) Text(string) []uint8 #51082 -pkg go/doc/comment, func DefaultLookupPackage(string) (string, bool) #51082 -pkg go/doc/comment, method (*DocLink) DefaultURL(string) string #51082 -pkg go/doc/comment, method (*Heading) DefaultID() string #51082 -pkg go/doc/comment, method (*List) BlankBefore() bool #51082 -pkg go/doc/comment, method (*List) BlankBetween() bool #51082 -pkg go/doc/comment, method (*Parser) Parse(string) *Doc #51082 -pkg go/doc/comment, method (*Printer) Comment(*Doc) []uint8 #51082 -pkg go/doc/comment, method (*Printer) HTML(*Doc) []uint8 #51082 -pkg go/doc/comment, method (*Printer) Markdown(*Doc) []uint8 #51082 -pkg go/doc/comment, method (*Printer) Text(*Doc) []uint8 #51082 -pkg go/doc/comment, type Block interface, unexported methods #51082 -pkg go/doc/comment, type Code struct #51082 -pkg go/doc/comment, type Code struct, Text string #51082 -pkg go/doc/comment, type Doc struct #51082 -pkg go/doc/comment, type Doc struct, Content []Block #51082 -pkg go/doc/comment, type Doc struct, Links []*LinkDef #51082 -pkg go/doc/comment, type DocLink struct #51082 -pkg go/doc/comment, type DocLink struct, ImportPath string #51082 -pkg go/doc/comment, type DocLink struct, Name string #51082 -pkg go/doc/comment, type DocLink struct, Recv string #51082 -pkg go/doc/comment, type DocLink struct, Text []Text #51082 -pkg go/doc/comment, type Heading struct #51082 -pkg go/doc/comment, type Heading struct, Text []Text #51082 -pkg go/doc/comment, type Italic string #51082 -pkg go/doc/comment, type Link struct #51082 -pkg go/doc/comment, type Link struct, Auto bool #51082 -pkg go/doc/comment, type Link struct, Text []Text #51082 -pkg go/doc/comment, type Link struct, URL string #51082 -pkg go/doc/comment, type LinkDef struct #51082 -pkg go/doc/comment, type LinkDef struct, Text string #51082 -pkg go/doc/comment, type LinkDef struct, URL string #51082 -pkg go/doc/comment, type LinkDef struct, Used bool #51082 -pkg go/doc/comment, type List struct #51082 -pkg go/doc/comment, type List struct, ForceBlankBefore bool #51082 -pkg go/doc/comment, type List struct, ForceBlankBetween bool #51082 -pkg go/doc/comment, type List struct, Items []*ListItem #51082 -pkg go/doc/comment, type ListItem struct #51082 -pkg go/doc/comment, type ListItem struct, Content []Block #51082 -pkg go/doc/comment, type ListItem struct, Number string #51082 -pkg go/doc/comment, type Paragraph struct #51082 -pkg go/doc/comment, type Paragraph struct, Text []Text #51082 -pkg go/doc/comment, type Parser struct #51082 -pkg go/doc/comment, type Parser struct, LookupPackage func(string) (string, bool) #51082 -pkg go/doc/comment, type Parser struct, LookupSym func(string, string) bool #51082 -pkg go/doc/comment, type Parser struct, Words map[string]string #51082 -pkg go/doc/comment, type Plain string #51082 -pkg go/doc/comment, type Printer struct #51082 -pkg go/doc/comment, type Printer struct, DocLinkBaseURL string #51082 -pkg go/doc/comment, type Printer struct, DocLinkURL func(*DocLink) string #51082 -pkg go/doc/comment, type Printer struct, HeadingID func(*Heading) string #51082 -pkg go/doc/comment, type Printer struct, HeadingLevel int #51082 -pkg go/doc/comment, type Printer struct, TextCodePrefix string #51082 -pkg go/doc/comment, type Printer struct, TextPrefix string #51082 -pkg go/doc/comment, type Printer struct, TextWidth int #51082 -pkg go/doc/comment, type Text interface, unexported methods #51082 diff --git a/api/next/51414.txt b/api/next/51414.txt deleted file mode 100644 index 7491285bb8..0000000000 --- a/api/next/51414.txt +++ /dev/null @@ -1 +0,0 @@ -pkg time, method (Duration) Abs() Duration #51414 diff --git a/api/next/51644.txt b/api/next/51644.txt deleted file mode 100644 index d93dbbf184..0000000000 --- a/api/next/51644.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg encoding/binary, func AppendUvarint([]uint8, uint64) []uint8 #51644 -pkg encoding/binary, func AppendVarint([]uint8, int64) []uint8 #51644 diff --git a/api/next/51682.txt b/api/next/51682.txt deleted file mode 100644 index 35e471d50f..0000000000 --- a/api/next/51682.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg go/types, method (*Func) Origin() *Func #51682 -pkg go/types, method (*Var) Origin() *Var #51682 diff --git a/api/next/51684.txt b/api/next/51684.txt deleted file mode 100644 index b8a0645256..0000000000 --- a/api/next/51684.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg regexp/syntax, const ErrNestingDepth = "expression nests too deeply" #51684 -pkg regexp/syntax, const ErrNestingDepth ErrorCode #51684 diff --git a/api/next/51868.txt b/api/next/51868.txt deleted file mode 100644 index cbf0324d5f..0000000000 --- a/api/next/51868.txt +++ /dev/null @@ -1,36 +0,0 @@ -pkg debug/pe, const IMAGE_COMDAT_SELECT_ANY = 2 #51686 -pkg debug/pe, const IMAGE_COMDAT_SELECT_ANY ideal-int #51686 -pkg debug/pe, const IMAGE_COMDAT_SELECT_ASSOCIATIVE = 5 #51686 -pkg debug/pe, const IMAGE_COMDAT_SELECT_ASSOCIATIVE ideal-int #51686 -pkg debug/pe, const IMAGE_COMDAT_SELECT_EXACT_MATCH = 4 #51686 -pkg debug/pe, const IMAGE_COMDAT_SELECT_EXACT_MATCH ideal-int #51686 -pkg debug/pe, const IMAGE_COMDAT_SELECT_LARGEST = 6 #51686 -pkg debug/pe, const IMAGE_COMDAT_SELECT_LARGEST ideal-int #51686 -pkg debug/pe, const IMAGE_COMDAT_SELECT_NODUPLICATES = 1 #51686 -pkg debug/pe, const IMAGE_COMDAT_SELECT_NODUPLICATES ideal-int #51686 -pkg debug/pe, const IMAGE_COMDAT_SELECT_SAME_SIZE = 3 #51686 -pkg debug/pe, const IMAGE_COMDAT_SELECT_SAME_SIZE ideal-int #51686 -pkg debug/pe, const IMAGE_SCN_CNT_CODE = 32 #51686 -pkg debug/pe, const IMAGE_SCN_CNT_CODE ideal-int #51686 -pkg debug/pe, const IMAGE_SCN_CNT_INITIALIZED_DATA = 64 #51686 -pkg debug/pe, const IMAGE_SCN_CNT_INITIALIZED_DATA ideal-int #51686 -pkg debug/pe, const IMAGE_SCN_CNT_UNINITIALIZED_DATA = 128 #51686 -pkg debug/pe, const IMAGE_SCN_CNT_UNINITIALIZED_DATA ideal-int #51686 -pkg debug/pe, const IMAGE_SCN_LNK_COMDAT = 4096 #51686 -pkg debug/pe, const IMAGE_SCN_LNK_COMDAT ideal-int #51686 -pkg debug/pe, const IMAGE_SCN_MEM_DISCARDABLE = 33554432 #51686 -pkg debug/pe, const IMAGE_SCN_MEM_DISCARDABLE ideal-int #51686 -pkg debug/pe, const IMAGE_SCN_MEM_EXECUTE = 536870912 #51686 -pkg debug/pe, const IMAGE_SCN_MEM_EXECUTE ideal-int #51686 -pkg debug/pe, const IMAGE_SCN_MEM_READ = 1073741824 #51686 -pkg debug/pe, const IMAGE_SCN_MEM_READ ideal-int #51686 -pkg debug/pe, const IMAGE_SCN_MEM_WRITE = 2147483648 #51686 -pkg debug/pe, const IMAGE_SCN_MEM_WRITE ideal-int #51686 -pkg debug/pe, method (*File) COFFSymbolReadSectionDefAux(int) (*COFFSymbolAuxFormat5, error) #51686 -pkg debug/pe, type COFFSymbolAuxFormat5 struct #51686 -pkg debug/pe, type COFFSymbolAuxFormat5 struct, Checksum uint32 #51686 -pkg debug/pe, type COFFSymbolAuxFormat5 struct, NumLineNumbers uint16 #51686 -pkg debug/pe, type COFFSymbolAuxFormat5 struct, NumRelocs uint16 #51686 -pkg debug/pe, type COFFSymbolAuxFormat5 struct, SecNum uint16 #51686 -pkg debug/pe, type COFFSymbolAuxFormat5 struct, Selection uint8 #51686 -pkg debug/pe, type COFFSymbolAuxFormat5 struct, Size uint32 #51686 From 91019cc13d9de72d5e43a0068311dc9e6012777a Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Fri, 3 Jun 2022 16:03:55 -0400 Subject: [PATCH 071/113] runtime/cgo: merge bodies of cgo_sys_thread_start on windows The bodies of cgo_sys_thread_start (and x_cgo_sys_thread_create) are nearly identical on all of the windows ports. Create a single _cgo_beginthread implementation that contains the body and is used on all ports. This will reduce churn in an upcoming CL to add retry logic. We could theoretically have a single implementation of _cgo_sys_thread_start shared by all ports, but I keep them separate for ease of searching. Right now every single port implements this function in their gcc_GOOS_GOARCH.c file, so it is nice to keep this symmetry. _cgo_dummy_export must move out of libcgo_windows.h because it is a definition and the inclusion of libcgo_windows.h in multiple files creates duplicate definitions. For #52572. Change-Id: I9fa22009389349c754210274c7db2631b061f9c7 Reviewed-on: https://go-review.googlesource.com/c/go/+/410354 Run-TryBot: Michael Pratt Reviewed-by: Michael Knyszek Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot --- src/runtime/cgo/gcc_libinit_windows.c | 28 ++++++++++++++++++++------- src/runtime/cgo/gcc_windows_386.c | 10 ++-------- src/runtime/cgo/gcc_windows_amd64.c | 8 +------- src/runtime/cgo/gcc_windows_arm64.c | 8 +------- src/runtime/cgo/libcgo_windows.h | 10 ++-------- 5 files changed, 27 insertions(+), 37 deletions(-) diff --git a/src/runtime/cgo/gcc_libinit_windows.c b/src/runtime/cgo/gcc_libinit_windows.c index ad5038667a..a9b94c3713 100644 --- a/src/runtime/cgo/gcc_libinit_windows.c +++ b/src/runtime/cgo/gcc_libinit_windows.c @@ -13,6 +13,16 @@ #include #include "libcgo.h" +#include "libcgo_windows.h" + +// Ensure there's one symbol marked __declspec(dllexport). +// If there are no exported symbols, the unfortunate behavior of +// the binutils linker is to also strip the relocations table, +// resulting in non-PIE binary. The other option is the +// --export-all-symbols flag, but we don't need to export all symbols +// and this may overflow the export table (#40795). +// See https://sourceware.org/bugzilla/show_bug.cgi?id=19011 +__declspec(dllexport) int _cgo_dummy_export; static volatile LONG runtime_init_once_gate = 0; static volatile LONG runtime_init_once_done = 0; @@ -53,13 +63,7 @@ _cgo_maybe_run_preinit() { void x_cgo_sys_thread_create(void (*func)(void*), void* arg) { - uintptr_t thandle; - - thandle = _beginthread(func, 0, arg); - if(thandle == -1) { - fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); - abort(); - } + _cgo_beginthread(func, arg); } int @@ -123,3 +127,13 @@ void (*(_cgo_get_context_function(void)))(struct context_arg*) { LeaveCriticalSection(&runtime_init_cs); return ret; } + +void _cgo_beginthread(void (*func)(void*), void* arg) { + uintptr_t thandle; + + thandle = _beginthread(func, 0, arg); + if (thandle == -1) { + fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); + abort(); + } +} diff --git a/src/runtime/cgo/gcc_windows_386.c b/src/runtime/cgo/gcc_windows_386.c index 60cb011bf2..56fbaac9b8 100644 --- a/src/runtime/cgo/gcc_windows_386.c +++ b/src/runtime/cgo/gcc_windows_386.c @@ -22,13 +22,7 @@ x_cgo_init(G *g) void _cgo_sys_thread_start(ThreadStart *ts) { - uintptr_t thandle; - - thandle = _beginthread(threadentry, 0, ts); - if(thandle == -1) { - fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); - abort(); - } + _cgo_beginthread(threadentry, ts); } static void @@ -50,6 +44,6 @@ threadentry(void *v) "movl %1, 0(%%eax)\n" // MOVL g, 0(FS) :: "r"(ts.tls), "r"(ts.g) : "%eax" ); - + crosscall_386(ts.fn); } diff --git a/src/runtime/cgo/gcc_windows_amd64.c b/src/runtime/cgo/gcc_windows_amd64.c index 9df9b9b1e4..996947eccf 100644 --- a/src/runtime/cgo/gcc_windows_amd64.c +++ b/src/runtime/cgo/gcc_windows_amd64.c @@ -24,13 +24,7 @@ x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) void _cgo_sys_thread_start(ThreadStart *ts) { - uintptr_t thandle; - - thandle = _beginthread(threadentry, 0, ts); - if(thandle == -1) { - fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); - abort(); - } + _cgo_beginthread(threadentry, ts); } static void diff --git a/src/runtime/cgo/gcc_windows_arm64.c b/src/runtime/cgo/gcc_windows_arm64.c index 61ef094866..8f113cc3b1 100644 --- a/src/runtime/cgo/gcc_windows_arm64.c +++ b/src/runtime/cgo/gcc_windows_arm64.c @@ -23,13 +23,7 @@ x_cgo_init(G *g, void (*setg)(void*)) void _cgo_sys_thread_start(ThreadStart *ts) { - uintptr_t thandle; - - thandle = _beginthread(threadentry, 0, ts); - if(thandle == -1) { - fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); - abort(); - } + _cgo_beginthread(threadentry, ts); } extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); diff --git a/src/runtime/cgo/libcgo_windows.h b/src/runtime/cgo/libcgo_windows.h index 0013f06bae..33d7637fec 100644 --- a/src/runtime/cgo/libcgo_windows.h +++ b/src/runtime/cgo/libcgo_windows.h @@ -2,11 +2,5 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Ensure there's one symbol marked __declspec(dllexport). -// If there are no exported symbols, the unfortunate behavior of -// the binutils linker is to also strip the relocations table, -// resulting in non-PIE binary. The other option is the -// --export-all-symbols flag, but we don't need to export all symbols -// and this may overflow the export table (#40795). -// See https://sourceware.org/bugzilla/show_bug.cgi?id=19011 -__declspec(dllexport) int _cgo_dummy_export; +// Call _beginthread, aborting on failure. +void _cgo_beginthread(void (*func)(void*), void* arg); From c7ccabf3fea67f002bef190a5ffc7417f4371a23 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Fri, 3 Jun 2022 16:22:58 -0400 Subject: [PATCH 072/113] runtime/cgo: retry _beginthread on EACCES We occassionally see _beginthread failing with EACCES, meaning "insufficient resources" according to the Microsoft documentation. Exactly which resources is unclear. Similar to pthread_create on unix systems, we can wait a bit and retry to try to get success. The alternative is to abort, so we may as well give it a try. Fixes #52572. Change-Id: I6e05add53b4ae36c61e53b1ee3fed6bc74e17dfa Reviewed-on: https://go-review.googlesource.com/c/go/+/410355 Reviewed-by: Michael Knyszek Reviewed-by: Ian Lance Taylor Run-TryBot: Michael Pratt TryBot-Result: Gopher Robot --- src/runtime/cgo/gcc_libinit_windows.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/runtime/cgo/gcc_libinit_windows.c b/src/runtime/cgo/gcc_libinit_windows.c index a9b94c3713..2b5896bb22 100644 --- a/src/runtime/cgo/gcc_libinit_windows.c +++ b/src/runtime/cgo/gcc_libinit_windows.c @@ -129,11 +129,23 @@ void (*(_cgo_get_context_function(void)))(struct context_arg*) { } void _cgo_beginthread(void (*func)(void*), void* arg) { + int tries; uintptr_t thandle; - thandle = _beginthread(func, 0, arg); - if (thandle == -1) { - fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); - abort(); + for (tries = 0; tries < 20; tries++) { + thandle = _beginthread(func, 0, arg); + if (thandle == -1 && errno == EACCES) { + // "Insufficient resources", try again in a bit. + // + // Note that the first Sleep(0) is a yield. + Sleep(tries); // milliseconds + continue; + } else if (thandle == -1) { + break; + } + return; // Success! } + + fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); + abort(); } From 2cfbef438049fd4c3f73d1562773ad1f93900897 Mon Sep 17 00:00:00 2001 From: Dmitri Goutnik Date: Fri, 20 May 2022 08:07:03 -0500 Subject: [PATCH 073/113] cmd/cgo: recognize clang 14 DWARF type names Fixes #53013 Change-Id: I169d4eb2420a6da52cc9abe17da98c3092a91be6 Reviewed-on: https://go-review.googlesource.com/c/go/+/407514 Auto-Submit: Ian Lance Taylor Reviewed-by: Cherry Mui Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gopher Robot --- src/cmd/cgo/gcc.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 4d1a5bd8de..8ce5d4de73 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -2242,6 +2242,8 @@ var dwarfToName = map[string]string{ "long long unsigned int": "ulonglong", "signed char": "schar", "unsigned char": "uchar", + "unsigned long": "ulong", // Used by Clang 14; issue 53013. + "unsigned long long": "ulonglong", // Used by Clang 14; issue 53013. } const signedDelta = 64 From 386245b68ef4a24450a12d4f85d1835779dfef86 Mon Sep 17 00:00:00 2001 From: Khaled Yakdan Date: Thu, 9 Jun 2022 07:29:14 +0000 Subject: [PATCH 074/113] runtime: fix stack split at bad time when fuzzing Fix #53190 Change-Id: I6c1f9c3ab58818d3a9f05ddaa02fc247e53677d3 GitHub-Last-Rev: 13b0749c135598c97063b07e2b0266125f5da83e GitHub-Pull-Request: golang/go#53191 Reviewed-on: https://go-review.googlesource.com/c/go/+/410034 Reviewed-by: Michael Knyszek Reviewed-by: Dmitri Shuralyov --- src/runtime/libfuzzer.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/runtime/libfuzzer.go b/src/runtime/libfuzzer.go index 8c6642443c..02dcc18e7a 100644 --- a/src/runtime/libfuzzer.go +++ b/src/runtime/libfuzzer.go @@ -15,41 +15,53 @@ func libfuzzerCall4(fn *byte, fakePC uintptr, s1, s2 unsafe.Pointer, result uint // Keep in sync with the definition of ret_sled in src/runtime/libfuzzer_amd64.s const retSledSize = 512 +// In libFuzzer mode, the compiler inserts calls to libfuzzerTraceCmpN and libfuzzerTraceConstCmpN +// (where N can be 1, 2, 4, or 8) for encountered integer comparisons in the code to be instrumented. +// This may result in these functions having callers that are nosplit. That is why they must be nosplit. +// +//go:nosplit func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC int) { fakePC = fakePC % retSledSize libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp1, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } +//go:nosplit func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC int) { fakePC = fakePC % retSledSize libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp2, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } +//go:nosplit func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC int) { fakePC = fakePC % retSledSize libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp4, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } +//go:nosplit func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC int) { fakePC = fakePC % retSledSize libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp8, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } +//go:nosplit func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC int) { fakePC = fakePC % retSledSize libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp1, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } +//go:nosplit func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC int) { fakePC = fakePC % retSledSize libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp2, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } +//go:nosplit func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC int) { fakePC = fakePC % retSledSize libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp4, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } +//go:nosplit func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC int) { fakePC = fakePC % retSledSize libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp8, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) From fb75c2da91b9cccf05ec6baad2636325c5d96751 Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Thu, 9 Jun 2022 17:30:05 -0400 Subject: [PATCH 075/113] cmd/dist, cmd/internal/metadata: don't install metadata binary It is only needed by cmd/dist, no need to build and install the binary. Change-Id: I6aba6b81496406077a0efba255c35020cff9d351 Reviewed-on: https://go-review.googlesource.com/c/go/+/411534 TryBot-Result: Gopher Robot Reviewed-by: Michael Pratt Run-TryBot: Cherry Mui --- src/cmd/dist/test.go | 2 +- src/cmd/internal/metadata/main.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 677be336ac..26d7fe0f73 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -290,7 +290,7 @@ func (t *tester) maybeLogMetadata() error { // // TODO(prattmic): If we split dist bootstrap and dist test then this // could be simplified to directly use internal/sysinfo here. - return t.dirCmd(filepath.Join(goroot, "src/cmd/internal/metadata"), "go", []string{"run", "."}).Run() + return t.dirCmd(filepath.Join(goroot, "src/cmd/internal/metadata"), "go", []string{"run", "main.go"}).Run() } // short returns a -short flag value to use with 'go test' diff --git a/src/cmd/internal/metadata/main.go b/src/cmd/internal/metadata/main.go index 157226e890..7478eec1c9 100644 --- a/src/cmd/internal/metadata/main.go +++ b/src/cmd/internal/metadata/main.go @@ -5,6 +5,12 @@ // Metadata prints basic system metadata to include in test logs. This is // separate from cmd/dist so it does not need to build with the bootstrap // toolchain. + +// This program is only used by cmd/dist. Add an "ignore" build tag so it +// is not installed. cmd/dist does "go run main.go" directly. + +//go:build ignore + package main import ( From ff3db8d12d8e2eacde7eccd47ea4f3e3dd5a6807 Mon Sep 17 00:00:00 2001 From: Dominik Honnef Date: Fri, 10 Jun 2022 23:13:41 +0200 Subject: [PATCH 076/113] doc: fix typos in Go memory model Change-Id: I8e94215d9bee0ea3ad378870fe565e961a9d80c9 Reviewed-on: https://go-review.googlesource.com/c/go/+/411595 Reviewed-by: Ian Lance Taylor Auto-Submit: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Dmitri Shuralyov --- doc/go_mem.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/go_mem.html b/doc/go_mem.html index 59f9ab880d..661e1e781c 100644 --- a/doc/go_mem.html +++ b/doc/go_mem.html @@ -85,7 +85,7 @@ the approach presented by Hans-J. Boehm and Sarita V. Adve in “Foundations of the C++ Concurrency Memory Model”, published in PLDI 2008. The definition of data-race-free programs and the guarantee of sequential consistency -for race-free progams are equivalent to the ones in that work. +for race-free programs are equivalent to the ones in that work.

@@ -205,7 +205,7 @@ by other languages, including C, C++, Java, JavaScript, Rust, and Swift.

Certain Go language operations such as goroutine creation and memory allocation -act as synchronization opeartions. +act as synchronization operations. The effect of these operations on the synchronized-before partial order is documented in the “Synchronization” section below. Individual packages are responsible for providing similar documentation From 55590f3a2b89f001bcadf0df6eb2dde62618302b Mon Sep 17 00:00:00 2001 From: Louis PORTAY Date: Thu, 9 Jun 2022 23:29:35 +0000 Subject: [PATCH 077/113] net/http: doc: update RFC reference for appropriate HTTP codes This documentation commit points to the latest RFC documenting HTTP codes Change-Id: Ia7640664637f0c7846e2182353b787474bac7b4f GitHub-Last-Rev: 5fb544ee32e1f16c871b08d9ff0a3cc6819418cd GitHub-Pull-Request: golang/go#53318 Reviewed-on: https://go-review.googlesource.com/c/go/+/411475 Auto-Submit: Ian Lance Taylor Reviewed-by: Ian Lance Taylor Reviewed-by: Damien Neil Run-TryBot: Ian Lance Taylor TryBot-Result: Gopher Robot --- src/net/http/status.go | 92 +++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/net/http/status.go b/src/net/http/status.go index 75fea0ca35..cd90877ef0 100644 --- a/src/net/http/status.go +++ b/src/net/http/status.go @@ -7,68 +7,68 @@ package http // HTTP status codes as registered with IANA. // See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml const ( - StatusContinue = 100 // RFC 7231, 6.2.1 - StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2 + StatusContinue = 100 // RFC 9110, 15.2.1 + StatusSwitchingProtocols = 101 // RFC 9110, 15.2.2 StatusProcessing = 102 // RFC 2518, 10.1 StatusEarlyHints = 103 // RFC 8297 - StatusOK = 200 // RFC 7231, 6.3.1 - StatusCreated = 201 // RFC 7231, 6.3.2 - StatusAccepted = 202 // RFC 7231, 6.3.3 - StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4 - StatusNoContent = 204 // RFC 7231, 6.3.5 - StatusResetContent = 205 // RFC 7231, 6.3.6 - StatusPartialContent = 206 // RFC 7233, 4.1 + StatusOK = 200 // RFC 9110, 15.3.1 + StatusCreated = 201 // RFC 9110, 15.3.2 + StatusAccepted = 202 // RFC 9110, 15.3.3 + StatusNonAuthoritativeInfo = 203 // RFC 9110, 15.3.4 + StatusNoContent = 204 // RFC 9110, 15.3.5 + StatusResetContent = 205 // RFC 9110, 15.3.6 + StatusPartialContent = 206 // RFC 9110, 15.3.7 StatusMultiStatus = 207 // RFC 4918, 11.1 StatusAlreadyReported = 208 // RFC 5842, 7.1 StatusIMUsed = 226 // RFC 3229, 10.4.1 - StatusMultipleChoices = 300 // RFC 7231, 6.4.1 - StatusMovedPermanently = 301 // RFC 7231, 6.4.2 - StatusFound = 302 // RFC 7231, 6.4.3 - StatusSeeOther = 303 // RFC 7231, 6.4.4 - StatusNotModified = 304 // RFC 7232, 4.1 - StatusUseProxy = 305 // RFC 7231, 6.4.5 - _ = 306 // RFC 7231, 6.4.6 (Unused) - StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7 - StatusPermanentRedirect = 308 // RFC 7538, 3 + StatusMultipleChoices = 300 // RFC 9110, 15.4.1 + StatusMovedPermanently = 301 // RFC 9110, 15.4.2 + StatusFound = 302 // RFC 9110, 15.4.3 + StatusSeeOther = 303 // RFC 9110, 15.4.4 + StatusNotModified = 304 // RFC 9110, 15.4.5 + StatusUseProxy = 305 // RFC 9110, 15.4.6 + _ = 306 // RFC 9110, 15.4.7 (Unused) + StatusTemporaryRedirect = 307 // RFC 9110, 15.4.8 + StatusPermanentRedirect = 308 // RFC 9110, 15.4.9 - StatusBadRequest = 400 // RFC 7231, 6.5.1 - StatusUnauthorized = 401 // RFC 7235, 3.1 - StatusPaymentRequired = 402 // RFC 7231, 6.5.2 - StatusForbidden = 403 // RFC 7231, 6.5.3 - StatusNotFound = 404 // RFC 7231, 6.5.4 - StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5 - StatusNotAcceptable = 406 // RFC 7231, 6.5.6 - StatusProxyAuthRequired = 407 // RFC 7235, 3.2 - StatusRequestTimeout = 408 // RFC 7231, 6.5.7 - StatusConflict = 409 // RFC 7231, 6.5.8 - StatusGone = 410 // RFC 7231, 6.5.9 - StatusLengthRequired = 411 // RFC 7231, 6.5.10 - StatusPreconditionFailed = 412 // RFC 7232, 4.2 - StatusRequestEntityTooLarge = 413 // RFC 7231, 6.5.11 - StatusRequestURITooLong = 414 // RFC 7231, 6.5.12 - StatusUnsupportedMediaType = 415 // RFC 7231, 6.5.13 - StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4 - StatusExpectationFailed = 417 // RFC 7231, 6.5.14 - StatusTeapot = 418 // RFC 7168, 2.3.3 - StatusMisdirectedRequest = 421 // RFC 7540, 9.1.2 - StatusUnprocessableEntity = 422 // RFC 4918, 11.2 + StatusBadRequest = 400 // RFC 9110, 15.5.1 + StatusUnauthorized = 401 // RFC 9110, 15.5.2 + StatusPaymentRequired = 402 // RFC 9110, 15.5.3 + StatusForbidden = 403 // RFC 9110, 15.5.4 + StatusNotFound = 404 // RFC 9110, 15.5.5 + StatusMethodNotAllowed = 405 // RFC 9110, 15.5.6 + StatusNotAcceptable = 406 // RFC 9110, 15.5.7 + StatusProxyAuthRequired = 407 // RFC 9110, 15.5.8 + StatusRequestTimeout = 408 // RFC 9110, 15.5.9 + StatusConflict = 409 // RFC 9110, 15.5.10 + StatusGone = 410 // RFC 9110, 15.5.11 + StatusLengthRequired = 411 // RFC 9110, 15.5.12 + StatusPreconditionFailed = 412 // RFC 9110, 15.5.13 + StatusRequestEntityTooLarge = 413 // RFC 9110, 15.5.14 + StatusRequestURITooLong = 414 // RFC 9110, 15.5.15 + StatusUnsupportedMediaType = 415 // RFC 9110, 15.5.16 + StatusRequestedRangeNotSatisfiable = 416 // RFC 9110, 15.5.17 + StatusExpectationFailed = 417 // RFC 9110, 15.5.18 + StatusTeapot = 418 // RFC 9110, 15.5.19 (Unused) + StatusMisdirectedRequest = 421 // RFC 9110, 15.5.20 + StatusUnprocessableEntity = 422 // RFC 9110, 15.5.21 StatusLocked = 423 // RFC 4918, 11.3 StatusFailedDependency = 424 // RFC 4918, 11.4 StatusTooEarly = 425 // RFC 8470, 5.2. - StatusUpgradeRequired = 426 // RFC 7231, 6.5.15 + StatusUpgradeRequired = 426 // RFC 9110, 15.5.22 StatusPreconditionRequired = 428 // RFC 6585, 3 StatusTooManyRequests = 429 // RFC 6585, 4 StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5 StatusUnavailableForLegalReasons = 451 // RFC 7725, 3 - StatusInternalServerError = 500 // RFC 7231, 6.6.1 - StatusNotImplemented = 501 // RFC 7231, 6.6.2 - StatusBadGateway = 502 // RFC 7231, 6.6.3 - StatusServiceUnavailable = 503 // RFC 7231, 6.6.4 - StatusGatewayTimeout = 504 // RFC 7231, 6.6.5 - StatusHTTPVersionNotSupported = 505 // RFC 7231, 6.6.6 + StatusInternalServerError = 500 // RFC 9110, 15.6.1 + StatusNotImplemented = 501 // RFC 9110, 15.6.2 + StatusBadGateway = 502 // RFC 9110, 15.6.3 + StatusServiceUnavailable = 503 // RFC 9110, 15.6.4 + StatusGatewayTimeout = 504 // RFC 9110, 15.6.5 + StatusHTTPVersionNotSupported = 505 // RFC 9110, 15.6.6 StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1 StatusInsufficientStorage = 507 // RFC 4918, 11.5 StatusLoopDetected = 508 // RFC 5842, 7.2 From d27128b065010ac6bae8dd648f85302240417294 Mon Sep 17 00:00:00 2001 From: Roland Shoemaker Date: Fri, 10 Jun 2022 13:21:14 -0700 Subject: [PATCH 078/113] doc/go1.19: fix crypto tags Closes an unclosed tag, and tags a type that was untagged. Change-Id: I9a1efda07f783f0ca7a93ffefbda4e29f5fc8d41 Reviewed-on: https://go-review.googlesource.com/c/go/+/411694 Auto-Submit: Roland Shoemaker TryBot-Result: Gopher Robot Reviewed-by: Julie Qiu Run-TryBot: Roland Shoemaker --- doc/go1.19.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 37f562a9df..00fd38e2d6 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -367,9 +367,9 @@ as well as support for rendering them to HTML, Markdown, and text. RawIssuer, Signature, AuthorityKeyId, and Extensions. - The new method RevocationList.CheckSignatureFrom + The new method RevocationList.CheckSignatureFrom checks that the signature on a CRL is a valid signature from a - Certificate. + Certificate. With the new CRL functionality, the existing functions ParseCRL and @@ -391,7 +391,7 @@ as well as support for rendering them to HTML, Markdown, and text.

The types CertificateList and TBSCertificateList - have been deprecated. The new crypto/x509 CRL functionality + have been deprecated. The new crypto/x509 CRL functionality should be used instead.

From 7eeec1f6e4b9359381e9aeffdb87c59308ecbb7e Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Sat, 11 Jun 2022 01:33:11 +0700 Subject: [PATCH 079/113] cmd/compile: fix missing dict pass for type assertions For type assertions, if either src or dst type has shape, we must convert them to dynamic type assertions. Fixes #53309 Change-Id: Ia3362fa67c011febcbdb5b26f856d081b5c366de Reviewed-on: https://go-review.googlesource.com/c/go/+/411617 Run-TryBot: Cuong Manh Le TryBot-Result: Gopher Robot Reviewed-by: Matthew Dempsky Reviewed-by: Cherry Mui Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/stencil.go | 4 +-- test/fixedbugs/issue53309.go | 42 +++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 test/fixedbugs/issue53309.go diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index 88e4961666..3f12aa3cbd 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -1330,10 +1330,10 @@ func (g *genInst) dictPass(info *instInfo) { m = convertUsingDictionary(info, info.dictParam, m.Pos(), mce.X, m, m.Type()) } case ir.ODOTTYPE, ir.ODOTTYPE2: - if !m.Type().HasShape() { + dt := m.(*ir.TypeAssertExpr) + if !dt.Type().HasShape() && !dt.X.Type().HasShape() { break } - dt := m.(*ir.TypeAssertExpr) var rtype, itab ir.Node if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() { // TODO(mdempsky): Investigate executing this block unconditionally. diff --git a/test/fixedbugs/issue53309.go b/test/fixedbugs/issue53309.go new file mode 100644 index 0000000000..2b752fe161 --- /dev/null +++ b/test/fixedbugs/issue53309.go @@ -0,0 +1,42 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type TaskInput interface { + deps() []*taskDefinition +} + +type Value[T any] interface { + metaValue +} + +type metaValue interface { + TaskInput +} + +type taskDefinition struct { +} + +type taskResult struct { + task *taskDefinition +} + +func (tr *taskResult) deps() []*taskDefinition { + return nil +} + +func use[T any](v Value[T]) { + _, ok := v.(*taskResult) + if !ok { + panic("output must be *taskResult") + } +} + +func main() { + tr := &taskResult{&taskDefinition{}} + use(Value[string](tr)) +} From 9228d7d7d523e6831933b79f768dfbb51f8ffb5b Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Thu, 9 Jun 2022 15:11:57 -0400 Subject: [PATCH 080/113] doc/go1.19: add a release note for module indexing Change-Id: I264499d955049c5b7c4bdda7ce23cf7fe7031402 Reviewed-on: https://go-review.googlesource.com/c/go/+/411497 Reviewed-by: Michael Matloob Run-TryBot: Michael Matloob TryBot-Result: Gopher Robot Reviewed-by: Bryan Mills --- doc/go1.19.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/go1.19.html b/doc/go1.19.html index 00fd38e2d6..4d7552276f 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -109,6 +109,11 @@ as well as support for rendering them to HTML, Markdown, and text. and GOGCCFLAGS variables it reports.

+

+ The go command now caches information necessary to load some modules, + which should result in a speed-up of some go list invocations. +

+

Vet

: From 2c52465cb3d327590755cfb9ef1ef0f7a167c4eb Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Wed, 8 Jun 2022 11:53:22 -0700 Subject: [PATCH 081/113] net: avoid darwin_arm64 bug in TestDialParallelSpuriousConnection On darwin_arm64, reading from a socket at the same time as the other end is closing it will occasionally hang for 60 seconds before returning ECONNRESET. (This is a macOS issue, not a Go issue.) Work around this condition by adding a brief sleep before the read. Fixes #37795. Change-Id: I63f92b91fb297cd66f89cdab707583afd50ab9c5 Reviewed-on: https://go-review.googlesource.com/c/go/+/411155 TryBot-Result: Gopher Robot Reviewed-by: Ian Lance Taylor Run-TryBot: Damien Neil --- src/net/dial_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/net/dial_test.go b/src/net/dial_test.go index e49b4a61d6..1256867da8 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -405,6 +405,16 @@ func TestDialParallelSpuriousConnection(t *testing.T) { t.Fatal(err) } + // Workaround for https://go.dev/issue/37795. + // On arm64 macOS (current as of macOS 12.4), + // reading from a socket at the same time as the client + // is closing it occasionally hangs for 60 seconds before + // returning ECONNRESET. Sleep for a bit to give the + // socket time to close before trying to read from it. + if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { + time.Sleep(10 * time.Millisecond) + } + // The client should close itself, without sending data. c.SetReadDeadline(readDeadline) var b [1]byte From 4703546a294b28e4e23043fe9cb745f27e55edab Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 13 Jun 2022 10:18:38 -0700 Subject: [PATCH 082/113] spec: add missing optional type arguments after TypeName in syntax Types may be generic, so each occurrence of a TypeName may be followed by optional type arguments. Add the missing syntactic (EBNF) factor. The syntax of type names followed by type arguments matches the syntax of operand names followed by type arguments (operands may also be types, or generic functions, among other things). This opens the door to factoring out this shared syntax, but it will also require some adjustments to prose to make it work well. Leaving for another change. Fixes #53240. Change-Id: I15212225c28b27f7621e3ca80dfbd131f6b7eada Reviewed-on: https://go-review.googlesource.com/c/go/+/411918 Reviewed-by: Ian Lance Taylor Reviewed-by: Robert Griesemer --- doc/go_spec.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/go_spec.html b/doc/go_spec.html index 37580a83b7..170c359c87 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -1025,7 +1025,7 @@ be unique.

 StructType    = "struct" "{" { FieldDecl ";" } "}" .
 FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
-EmbeddedField = [ "*" ] TypeName .
+EmbeddedField = [ "*" ] TypeName [ TypeArgs ] .
 Tag           = string_lit .
 
@@ -3029,7 +3029,7 @@ Each element may optionally be preceded by a corresponding key.
 CompositeLit  = LiteralType LiteralValue .
 LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
-                SliceType | MapType | TypeName .
+                SliceType | MapType | TypeName [ TypeArgs ] .
 LiteralValue  = "{" [ ElementList [ "," ] ] "}" .
 ElementList   = KeyedElement { "," KeyedElement } .
 KeyedElement  = [ Key ":" ] Element .

From 5ee939b8199266446d7ccc563751a9d3db26bf8b Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Mon, 13 Jun 2022 11:06:55 -0700
Subject: [PATCH 083/113] spec: clarify behavior of map size hint for make
 built-in

The spec already states that the precise behavior of the map size
hint provided to the make built-in is implementation-dependent.

Exclude requiring specific run-time behavior for maps.
(The current Go compiler does not panic if the size hint is negative
at run-time.)

Fixes #53219.

Change-Id: I2f3618bf9ba4ed921e18dc4f2273eaa770805bd7
Reviewed-on: https://go-review.googlesource.com/c/go/+/411919
Reviewed-by: Keith Randall 
Reviewed-by: Ian Lance Taylor 
Reviewed-by: Keith Randall 
---
 doc/go_spec.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/go_spec.html b/doc/go_spec.html
index 170c359c87..b5f6c5fd65 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -7141,7 +7141,7 @@ A constant size argument must be non-negative and re
 by a value of type int; if it is an untyped constant it is given type int.
 If both n and m are provided and are constant, then
 n must be no larger than m.
-If n is negative or larger than m at run time,
+For slices and channels, if n is negative or larger than m at run time,
 a run-time panic occurs.
 

From fbc75dff2fa5bac474936e611ff1b7e778617be3 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 10 Jun 2022 16:49:51 -0700 Subject: [PATCH 084/113] cmd/cgo: remove -fsanitize=hwaddress hardware tags No test because this isn't support on any of the builders. Fixes #53285 Change-Id: If8d17bdcdac81a6ce404a35a289bf83f07f02855 Reviewed-on: https://go-review.googlesource.com/c/go/+/411698 Auto-Submit: Ian Lance Taylor Reviewed-by: Ian Lance Taylor Reviewed-by: Cherry Mui Run-TryBot: Ian Lance Taylor TryBot-Result: Gopher Robot --- src/cmd/cgo/gcc.go | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 8ce5d4de73..4dff5e2b1c 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -1831,6 +1831,23 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 bo := f.ByteOrder symtab, err := f.Symbols() if err == nil { + // Check for use of -fsanitize=hwaddress (issue 53285). + removeTag := func(v uint64) uint64 { return v } + if goarch == "arm64" { + for i := range symtab { + if symtab[i].Name == "__hwasan_init" { + // -fsanitize=hwaddress on ARM + // uses the upper byte of a + // memory address as a hardware + // tag. Remove it so that + // we can find the associated + // data. + removeTag = func(v uint64) uint64 { return v &^ (0xff << (64 - 8)) } + break + } + } + } + for i := range symtab { s := &symtab[i] switch { @@ -1838,9 +1855,10 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] - if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { + val := removeTag(s.Value) + if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { - data := sdat[s.Value-sect.Addr:] + data := sdat[val-sect.Addr:] ints = make([]int64, len(data)/8) for i := range ints { ints[i] = int64(bo.Uint64(data[i*8:])) @@ -1852,9 +1870,10 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] - if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { + val := removeTag(s.Value) + if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { - data := sdat[s.Value-sect.Addr:] + data := sdat[val-sect.Addr:] floats = make([]float64, len(data)/8) for i := range floats { floats[i] = math.Float64frombits(bo.Uint64(data[i*8:])) @@ -1867,9 +1886,10 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] - if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { + val := removeTag(s.Value) + if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { - data := sdat[s.Value-sect.Addr:] + data := sdat[val-sect.Addr:] strdata[n] = string(data) } } @@ -1880,9 +1900,10 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] - if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { + val := removeTag(s.Value) + if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { - data := sdat[s.Value-sect.Addr:] + data := sdat[val-sect.Addr:] strlen := bo.Uint64(data[:8]) if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt? fatalf("string literal too big") From 24b90391495094f1059cb7b09cf6bcfc790f7fe5 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Mon, 13 Jun 2022 15:39:08 -0400 Subject: [PATCH 085/113] doc/go1.19: prefer relative links to other parts of the Go website The Go website can be served on more than one domain (for example, go.dev, golang.google.cn, tip.golang.org, localhost:6060, and so on). Use relative links which work in all contexts. For #51400. Updates #53337. Change-Id: I100938981447537ac242b4045929f6db8a2674c2 Reviewed-on: https://go-review.googlesource.com/c/go/+/411974 TryBot-Result: Gopher Robot Reviewed-by: Dmitri Shuralyov Run-TryBot: Dmitri Shuralyov Auto-Submit: Dmitri Shuralyov Reviewed-by: Michael Knyszek --- doc/go1.19.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 4d7552276f..df42a427ff 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -139,7 +139,7 @@ as well as support for rendering them to HTML, Markdown, and text. / GOGC, and will be respected even if GOGC=off, allowing Go programs to always make maximal use of their memory limit, improving resource efficiency - in some cases. See the GC guide for + in some cases. See the GC guide for a detailed guide explaining the soft memory limit in more detail, as well as a variety of common use-cases and scenarios. Please note that small memory limits, on the order of tens of megabytes or less, are less likely to be @@ -277,7 +277,7 @@ as well as support for rendering them to HTML, Markdown, and text. Command and LookPath no longer allow results from a PATH search to be found relative to the current directory. - This removes a common source of security problems + This removes a common source of security problems but may also break existing programs that depend on using, say, exec.Command("prog") to run a binary named prog (or, on Windows, prog.exe) in the current directory. See the os/exec package documentation for @@ -718,7 +718,7 @@ as well as support for rendering them to HTML, Markdown, and text. The methods Value.Len and Value.Cap now successfully operate on a pointer to an array and return the length of that array, - to match what the builtin + to match what the builtin len and cap functions do.

From 6130461149020d2b4b91fb183afa388a211cadc5 Mon Sep 17 00:00:00 2001 From: Michael Anthony Knyszek Date: Thu, 9 Jun 2022 20:41:18 +0000 Subject: [PATCH 086/113] internal/testmath: add two-sample Welch's t-test for performance tests This CL copies code from github.com/aclements/go-moremath/stats and github.com/aclements/go-moremath/mathx for Welch's t-test. Several existing tests in the Go repository check performance and scalability, and this import is part of a move toward a more rigorous measurement of both. Note that the copied code is already licensed to Go Authors, so there's no need to worry about additional licensing considerations. For #32986. Change-Id: I058630fab7216d1a589bb182b69fa2231e6f5475 Reviewed-on: https://go-review.googlesource.com/c/go/+/411395 Reviewed-by: Michael Pratt --- src/go/build/deps_test.go | 5 +- src/internal/testmath/ttest.go | 213 +++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 src/internal/testmath/ttest.go diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 5b971b93e2..1ddf8f69be 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -543,7 +543,10 @@ var depsRules = ` internal/fuzz, internal/testlog, runtime/pprof, regexp < testing/internal/testdeps; - OS, flag, testing, internal/cfg + MATH, errors, testing + < internal/testmath; + + OS, flag, testing, internal/cfg, internal/testmath < internal/testenv; OS, encoding/base64 diff --git a/src/internal/testmath/ttest.go b/src/internal/testmath/ttest.go new file mode 100644 index 0000000000..d15d2deebb --- /dev/null +++ b/src/internal/testmath/ttest.go @@ -0,0 +1,213 @@ +// 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 testmath + +import ( + "errors" + "math" +) + +// A TTestSample is a sample that can be used for a one or two sample +// t-test. +type TTestSample interface { + Weight() float64 + Mean() float64 + Variance() float64 +} + +var ( + ErrSampleSize = errors.New("sample is too small") + ErrZeroVariance = errors.New("sample has zero variance") + ErrMismatchedSamples = errors.New("samples have different lengths") +) + +// TwoSampleWelchTTest performs a two-sample (unpaired) Welch's t-test +// on samples x1 and x2. This t-test does not assume the distributions +// have equal variance. +func TwoSampleWelchTTest(x1, x2 TTestSample, alt LocationHypothesis) (*TTestResult, error) { + n1, n2 := x1.Weight(), x2.Weight() + if n1 <= 1 || n2 <= 1 { + // TODO: Can we still do this with n == 1? + return nil, ErrSampleSize + } + v1, v2 := x1.Variance(), x2.Variance() + if v1 == 0 && v2 == 0 { + return nil, ErrZeroVariance + } + + dof := math.Pow(v1/n1+v2/n2, 2) / + (math.Pow(v1/n1, 2)/(n1-1) + math.Pow(v2/n2, 2)/(n2-1)) + s := math.Sqrt(v1/n1 + v2/n2) + t := (x1.Mean() - x2.Mean()) / s + return newTTestResult(int(n1), int(n2), t, dof, alt), nil +} + +// A TTestResult is the result of a t-test. +type TTestResult struct { + // N1 and N2 are the sizes of the input samples. For a + // one-sample t-test, N2 is 0. + N1, N2 int + + // T is the value of the t-statistic for this t-test. + T float64 + + // DoF is the degrees of freedom for this t-test. + DoF float64 + + // AltHypothesis specifies the alternative hypothesis tested + // by this test against the null hypothesis that there is no + // difference in the means of the samples. + AltHypothesis LocationHypothesis + + // P is p-value for this t-test for the given null hypothesis. + P float64 +} + +func newTTestResult(n1, n2 int, t, dof float64, alt LocationHypothesis) *TTestResult { + dist := TDist{dof} + var p float64 + switch alt { + case LocationDiffers: + p = 2 * (1 - dist.CDF(math.Abs(t))) + case LocationLess: + p = dist.CDF(t) + case LocationGreater: + p = 1 - dist.CDF(t) + } + return &TTestResult{N1: n1, N2: n2, T: t, DoF: dof, AltHypothesis: alt, P: p} +} + +// A LocationHypothesis specifies the alternative hypothesis of a +// location test such as a t-test or a Mann-Whitney U-test. The +// default (zero) value is to test against the alternative hypothesis +// that they differ. +type LocationHypothesis int + +const ( + // LocationLess specifies the alternative hypothesis that the + // location of the first sample is less than the second. This + // is a one-tailed test. + LocationLess LocationHypothesis = -1 + + // LocationDiffers specifies the alternative hypothesis that + // the locations of the two samples are not equal. This is a + // two-tailed test. + LocationDiffers LocationHypothesis = 0 + + // LocationGreater specifies the alternative hypothesis that + // the location of the first sample is greater than the + // second. This is a one-tailed test. + LocationGreater LocationHypothesis = 1 +) + +// A TDist is a Student's t-distribution with V degrees of freedom. +type TDist struct { + V float64 +} + +// PDF returns the value at x of the probability distribution function for the +// distribution. +func (t TDist) PDF(x float64) float64 { + return math.Exp(lgamma((t.V+1)/2)-lgamma(t.V/2)) / + math.Sqrt(t.V*math.Pi) * math.Pow(1+(x*x)/t.V, -(t.V+1)/2) +} + +// CDF returns the value at x of the cumulative distribution function for the +// distribution. +func (t TDist) CDF(x float64) float64 { + if x == 0 { + return 0.5 + } else if x > 0 { + return 1 - 0.5*betaInc(t.V/(t.V+x*x), t.V/2, 0.5) + } else if x < 0 { + return 1 - t.CDF(-x) + } else { + return math.NaN() + } +} + +func (t TDist) Bounds() (float64, float64) { + return -4, 4 +} + +func lgamma(x float64) float64 { + y, _ := math.Lgamma(x) + return y +} + +// betaInc returns the value of the regularized incomplete beta +// function Iₓ(a, b) = 1 / B(a, b) * ∫₀ˣ tᵃ⁻¹ (1-t)ᵇ⁻¹ dt. +// +// This is not to be confused with the "incomplete beta function", +// which can be computed as BetaInc(x, a, b)*Beta(a, b). +// +// If x < 0 or x > 1, returns NaN. +func betaInc(x, a, b float64) float64 { + // Based on Numerical Recipes in C, section 6.4. This uses the + // continued fraction definition of I: + // + // (xᵃ*(1-x)ᵇ)/(a*B(a,b)) * (1/(1+(d₁/(1+(d₂/(1+...)))))) + // + // where B(a,b) is the beta function and + // + // d_{2m+1} = -(a+m)(a+b+m)x/((a+2m)(a+2m+1)) + // d_{2m} = m(b-m)x/((a+2m-1)(a+2m)) + if x < 0 || x > 1 { + return math.NaN() + } + bt := 0.0 + if 0 < x && x < 1 { + // Compute the coefficient before the continued + // fraction. + bt = math.Exp(lgamma(a+b) - lgamma(a) - lgamma(b) + + a*math.Log(x) + b*math.Log(1-x)) + } + if x < (a+1)/(a+b+2) { + // Compute continued fraction directly. + return bt * betacf(x, a, b) / a + } else { + // Compute continued fraction after symmetry transform. + return 1 - bt*betacf(1-x, b, a)/b + } +} + +// betacf is the continued fraction component of the regularized +// incomplete beta function Iₓ(a, b). +func betacf(x, a, b float64) float64 { + const maxIterations = 200 + const epsilon = 3e-14 + + raiseZero := func(z float64) float64 { + if math.Abs(z) < math.SmallestNonzeroFloat64 { + return math.SmallestNonzeroFloat64 + } + return z + } + + c := 1.0 + d := 1 / raiseZero(1-(a+b)*x/(a+1)) + h := d + for m := 1; m <= maxIterations; m++ { + mf := float64(m) + + // Even step of the recurrence. + numer := mf * (b - mf) * x / ((a + 2*mf - 1) * (a + 2*mf)) + d = 1 / raiseZero(1+numer*d) + c = raiseZero(1 + numer/c) + h *= d * c + + // Odd step of the recurrence. + numer = -(a + mf) * (a + b + mf) * x / ((a + 2*mf) * (a + 2*mf + 1)) + d = 1 / raiseZero(1+numer*d) + c = raiseZero(1 + numer/c) + hfac := d * c + h *= hfac + + if math.Abs(hfac-1) < epsilon { + return h + } + } + panic("betainc: a or b too big; failed to converge") +} From 1fe2810f9ca0dcd34e473f852102e2a49d45d7d8 Mon Sep 17 00:00:00 2001 From: Michael Anthony Knyszek Date: Fri, 10 Jun 2022 16:21:46 +0000 Subject: [PATCH 087/113] sync: move lock linearity test and treat it like a performance test This change moves test/locklinear.go into the sync package tests, and adds a bit of infrastructure since there are other linearity-checking tests that could benefit from it too. This infrastructure is also different than what test/locklinear.go does: instead of trying really hard to get at least one success, we instead treat this like a performance test and look for a significant difference via a t-test. This makes the methodology behind the tests more rigorous, and should reduce flakiness as transient noise should produce an insignificant result. A follow-up CL does more to make these tests even more robust. For #32986. Change-Id: I408c5f643962b70ea708930edb4ac9df1c6123ce Reviewed-on: https://go-review.googlesource.com/c/go/+/411396 Reviewed-by: Michael Pratt --- src/internal/testenv/testenv.go | 65 ++++++++++++ src/internal/testmath/bench.go | 38 +++++++ src/sync/mutex_test.go | 90 +++++++++++++++++ test/locklinear.go | 171 -------------------------------- 4 files changed, 193 insertions(+), 171 deletions(-) create mode 100644 src/internal/testmath/bench.go delete mode 100644 test/locklinear.go diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go index 1feb630cf5..b7cb95063b 100644 --- a/src/internal/testenv/testenv.go +++ b/src/internal/testenv/testenv.go @@ -16,6 +16,7 @@ import ( "flag" "fmt" "internal/cfg" + "internal/testmath" "os" "os/exec" "path/filepath" @@ -463,3 +464,67 @@ func RunWithTimeout(t testing.TB, cmd *exec.Cmd) ([]byte, error) { return b.Bytes(), err } + +// CheckLinear checks if the function produced by f scales linearly. +// +// f must accept a scale factor which causes the input to the function it +// produces to scale by that factor. +func CheckLinear(t *testing.T, f func(scale float64) func(*testing.B)) { + MustHaveExec(t) + + if os.Getenv("GO_PERF_UNIT_TEST") == "" { + // Invoke the same test as a subprocess with the GO_PERF_UNIT_TEST environment variable set. + // We create a subprocess for two reasons: + // + // 1. There's no other way to set the benchmarking parameters of testing.Benchmark. + // 2. Since we're effectively running a performance test, running in a subprocess grants + // us a little bit more isolation than using the same process. + // + // As an alternative, we could fairly easily reimplement the timing code in testing.Benchmark, + // but a subprocess is just as easy to create. + + selfCmd := CleanCmdEnv(exec.Command(os.Args[0], "-test.v", fmt.Sprintf("-test.run=^%s$", t.Name()), "-test.benchtime=1x")) + selfCmd.Env = append(selfCmd.Env, "GO_PERF_UNIT_TEST=1") + output, err := RunWithTimeout(t, selfCmd) + if err != nil { + t.Error(err) + t.Logf("--- subprocess output ---\n%s", string(output)) + } + if bytes.Contains(output, []byte("insignificant result")) { + t.Skip("insignificant result") + } + return + } + + // Pick a reasonable sample count. + const count = 10 + + // Collect samples for scale factor 1. + x1 := make([]testing.BenchmarkResult, 0, count) + for i := 0; i < count; i++ { + x1 = append(x1, testing.Benchmark(f(1.0))) + } + + // Collect samples for scale factor 2. + x2 := make([]testing.BenchmarkResult, 0, count) + for i := 0; i < count; i++ { + x2 = append(x2, testing.Benchmark(f(2.0))) + } + + // Run a t-test on the results. + r1 := testmath.BenchmarkResults(x1) + r2 := testmath.BenchmarkResults(x2) + result, err := testmath.TwoSampleWelchTTest(r1, r2, testmath.LocationDiffers) + if err != nil { + t.Fatalf("failed to run t-test: %v", err) + } + if result.P > 0.005 { + // Insignificant result. + t.Skip("insignificant result") + } + + // Let ourselves be within 3x; 2x is too strict. + if m1, m2 := r1.Mean(), r2.Mean(); 3.0*m1 < m2 { + t.Fatalf("failure to scale linearly: µ_1=%s µ_2=%s p=%f", time.Duration(m1), time.Duration(m2), result.P) + } +} diff --git a/src/internal/testmath/bench.go b/src/internal/testmath/bench.go new file mode 100644 index 0000000000..6f034b4685 --- /dev/null +++ b/src/internal/testmath/bench.go @@ -0,0 +1,38 @@ +// 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 testmath + +import ( + "math" + "testing" + "time" +) + +type BenchmarkResults []testing.BenchmarkResult + +func (b BenchmarkResults) Weight() float64 { + var weight int + for _, r := range b { + weight += r.N + } + return float64(weight) +} + +func (b BenchmarkResults) Mean() float64 { + var dur time.Duration + for _, r := range b { + dur += r.T * time.Duration(r.N) + } + return float64(dur) / b.Weight() +} + +func (b BenchmarkResults) Variance() float64 { + var num float64 + mean := b.Mean() + for _, r := range b { + num += math.Pow(float64(r.T)-mean, 2) * float64(r.N) + } + return float64(num) / b.Weight() +} diff --git a/src/sync/mutex_test.go b/src/sync/mutex_test.go index cca0986a30..9a4187c672 100644 --- a/src/sync/mutex_test.go +++ b/src/sync/mutex_test.go @@ -333,3 +333,93 @@ func BenchmarkMutexSpin(b *testing.B) { } }) } + +const runtimeSemaHashTableSize = 251 // known size of runtime hash table + +func TestMutexLinearOne(t *testing.T) { + testenv.CheckLinear(t, func(scale float64) func(*testing.B) { + n := int(1000 * scale) + return func(b *testing.B) { + ch := make(chan int) + locks := make([]RWMutex, runtimeSemaHashTableSize+1) + for i := 0; i < n; i++ { + go func() { + locks[0].Lock() + ch <- 1 + }() + } + time.Sleep(1 * time.Millisecond) + + go func() { + for j := 0; j < n; j++ { + locks[1].Lock() + locks[runtimeSemaHashTableSize].Lock() + locks[1].Unlock() + runtime.Gosched() + locks[runtimeSemaHashTableSize].Unlock() + } + }() + + for j := 0; j < n; j++ { + locks[1].Lock() + locks[runtimeSemaHashTableSize].Lock() + locks[1].Unlock() + runtime.Gosched() + locks[runtimeSemaHashTableSize].Unlock() + } + + for i := 0; i < n; i++ { + <-ch + locks[0].Unlock() + } + } + }) +} + +func TestMutexLinearMany(t *testing.T) { + if runtime.GOARCH == "arm" && os.Getenv("GOARM") == "5" { + // stressLockMany reliably fails on the linux-arm-arm5spacemonkey + // builder. See https://golang.org/issue/24221. + return + } + testenv.CheckLinear(t, func(scale float64) func(*testing.B) { + n := int(1000 * scale) + return func(b *testing.B) { + locks := make([]RWMutex, n*runtimeSemaHashTableSize+1) + + var wg WaitGroup + for i := 0; i < n; i++ { + wg.Add(1) + go func(i int) { + locks[(i+1)*runtimeSemaHashTableSize].Lock() + wg.Done() + locks[(i+1)*runtimeSemaHashTableSize].Lock() + locks[(i+1)*runtimeSemaHashTableSize].Unlock() + }(i) + } + wg.Wait() + + go func() { + for j := 0; j < n; j++ { + locks[1].Lock() + locks[0].Lock() + locks[1].Unlock() + runtime.Gosched() + locks[0].Unlock() + } + }() + + for j := 0; j < n; j++ { + locks[1].Lock() + locks[0].Lock() + locks[1].Unlock() + runtime.Gosched() + locks[0].Unlock() + } + + for i := 0; i < n; i++ { + locks[(i+1)*runtimeSemaHashTableSize].Unlock() + } + } + }) +} diff --git a/test/locklinear.go b/test/locklinear.go deleted file mode 100644 index 54e40a543b..0000000000 --- a/test/locklinear.go +++ /dev/null @@ -1,171 +0,0 @@ -// run - -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Test that locks don't go quadratic due to runtime hash table collisions. - -package main - -import ( - "bytes" - "fmt" - "log" - "os" - "runtime" - "runtime/pprof" - "sync" - "time" -) - -const debug = false - -// checkLinear asserts that the running time of f(n) is at least linear but sub-quadratic. -// tries is the initial number of iterations. -func checkLinear(typ string, tries int, f func(n int)) { - // Depending on the machine and OS, this test might be too fast - // to measure with accurate enough granularity. On failure, - // make it run longer, hoping that the timing granularity - // is eventually sufficient. - - timeF := func(n int) time.Duration { - t1 := time.Now() - f(n) - return time.Since(t1) - } - - n := tries - fails := 0 - var buf bytes.Buffer - inversions := 0 - for { - t1 := timeF(n) - t2 := timeF(2 * n) - if debug { - println(n, t1.String(), 2*n, t2.String()) - } - fmt.Fprintf(&buf, "%d %v %d %v (%.1fX)\n", n, t1, 2*n, t2, float64(t2)/float64(t1)) - // should be 2x (linear); allow up to 3x - if t1*3/2 < t2 && t2 < t1*3 { - return - } - if t2 < t1 { - if inversions++; inversions >= 5 { - // The system must be overloaded (some builders). Give up. - return - } - continue // try again; don't increment fails - } - // Once the test runs long enough for n ops, - // try to get the right ratio at least once. - // If many in a row all fail, give up. - if fails++; fails >= 5 { - // If 2n ops run in under a second and the ratio - // doesn't work out, make n bigger, trying to reduce - // the effect that a constant amount of overhead has - // on the computed ratio. - if t2 < time.Second*4/10 { - fails = 0 - n *= 2 - continue - } - panic(fmt.Sprintf("%s: too slow: %d ops: %v; %d ops: %v\n\n%s", - typ, n, t1, 2*n, t2, buf.String())) - } - } -} - -const offset = 251 // known size of runtime hash table - -const profile = false - -func main() { - if profile { - f, err := os.Create("lock.prof") - if err != nil { - log.Fatal(err) - } - pprof.StartCPUProfile(f) - defer pprof.StopCPUProfile() - } - - checkLinear("lockone", 1000, func(n int) { - ch := make(chan int) - locks := make([]sync.RWMutex, offset+1) - for i := 0; i < n; i++ { - go func() { - locks[0].Lock() - ch <- 1 - }() - } - time.Sleep(1 * time.Millisecond) - - go func() { - for j := 0; j < n; j++ { - locks[1].Lock() - locks[offset].Lock() - locks[1].Unlock() - runtime.Gosched() - locks[offset].Unlock() - } - }() - - for j := 0; j < n; j++ { - locks[1].Lock() - locks[offset].Lock() - locks[1].Unlock() - runtime.Gosched() - locks[offset].Unlock() - } - - for i := 0; i < n; i++ { - <-ch - locks[0].Unlock() - } - }) - - if runtime.GOARCH == "arm" && os.Getenv("GOARM") == "5" { - // lockmany reliably fails on the linux-arm-arm5spacemonkey - // builder. See https://golang.org/issue/24221. - return - } - - checkLinear("lockmany", 1000, func(n int) { - locks := make([]sync.RWMutex, n*offset+1) - - var wg sync.WaitGroup - for i := 0; i < n; i++ { - wg.Add(1) - go func(i int) { - locks[(i+1)*offset].Lock() - wg.Done() - locks[(i+1)*offset].Lock() - locks[(i+1)*offset].Unlock() - }(i) - } - wg.Wait() - - go func() { - for j := 0; j < n; j++ { - locks[1].Lock() - locks[0].Lock() - locks[1].Unlock() - runtime.Gosched() - locks[0].Unlock() - } - }() - - for j := 0; j < n; j++ { - locks[1].Lock() - locks[0].Lock() - locks[1].Unlock() - runtime.Gosched() - locks[0].Unlock() - } - - for i := 0; i < n; i++ { - locks[(i+1)*offset].Unlock() - } - }) -} From 56bc3098f4ed42b272a45c246dcd42d28d89a69a Mon Sep 17 00:00:00 2001 From: Michael Anthony Knyszek Date: Fri, 10 Jun 2022 18:47:37 +0000 Subject: [PATCH 088/113] sync: improve linearity test robustness This change improves the robustness of the locklinear test in the following ways: * It removes allocations from the timing, which may be very variable if we're unlucky. * It ensures that goroutines are properly cleaned up before the test function returns, reducing the chance that they bleed into repeat attempts. It also stops timing before this cleanup. Fixes #32986. Change-Id: I3a8096e6922f23d899ad602e2845bdfc639ed742 Reviewed-on: https://go-review.googlesource.com/c/go/+/409894 Reviewed-by: Michael Pratt --- src/sync/mutex_test.go | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/sync/mutex_test.go b/src/sync/mutex_test.go index 9a4187c672..8e34b02459 100644 --- a/src/sync/mutex_test.go +++ b/src/sync/mutex_test.go @@ -340,16 +340,25 @@ func TestMutexLinearOne(t *testing.T) { testenv.CheckLinear(t, func(scale float64) func(*testing.B) { n := int(1000 * scale) return func(b *testing.B) { - ch := make(chan int) + ch := make(chan struct{}) locks := make([]RWMutex, runtimeSemaHashTableSize+1) + + b.ResetTimer() + + var wgStart, wgFinish WaitGroup for i := 0; i < n; i++ { + wgStart.Add(1) + wgFinish.Add(1) go func() { + wgStart.Done() locks[0].Lock() - ch <- 1 + ch <- struct{}{} + wgFinish.Done() }() } - time.Sleep(1 * time.Millisecond) + wgStart.Wait() + wgFinish.Add(1) go func() { for j := 0; j < n; j++ { locks[1].Lock() @@ -358,6 +367,7 @@ func TestMutexLinearOne(t *testing.T) { runtime.Gosched() locks[runtimeSemaHashTableSize].Unlock() } + wgFinish.Done() }() for j := 0; j < n; j++ { @@ -368,10 +378,14 @@ func TestMutexLinearOne(t *testing.T) { locks[runtimeSemaHashTableSize].Unlock() } + b.StopTimer() + for i := 0; i < n; i++ { <-ch locks[0].Unlock() } + + wgFinish.Wait() } }) } @@ -387,17 +401,21 @@ func TestMutexLinearMany(t *testing.T) { return func(b *testing.B) { locks := make([]RWMutex, n*runtimeSemaHashTableSize+1) - var wg WaitGroup + b.ResetTimer() + + var wgStart, wgFinish WaitGroup for i := 0; i < n; i++ { - wg.Add(1) + wgStart.Add(1) + wgFinish.Add(1) go func(i int) { locks[(i+1)*runtimeSemaHashTableSize].Lock() - wg.Done() + wgStart.Done() locks[(i+1)*runtimeSemaHashTableSize].Lock() locks[(i+1)*runtimeSemaHashTableSize].Unlock() + wgFinish.Done() }(i) } - wg.Wait() + wgStart.Wait() go func() { for j := 0; j < n; j++ { @@ -417,9 +435,13 @@ func TestMutexLinearMany(t *testing.T) { locks[0].Unlock() } + b.StopTimer() + for i := 0; i < n; i++ { locks[(i+1)*runtimeSemaHashTableSize].Unlock() } + + wgFinish.Wait() } }) } From c5be77b687369783ed8a109482452c7811f7803c Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 8 Jun 2022 12:06:01 -0400 Subject: [PATCH 089/113] doc/go1.19: minor edits For #51400 Change-Id: Ia5289dad84fb63ca6f16a40f076b5ef10511f6b0 Reviewed-on: https://go-review.googlesource.com/c/go/+/411116 Reviewed-by: David Chase Reviewed-by: Cherry Mui --- doc/go1.19.html | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index df42a427ff..727873890b 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -44,8 +44,17 @@ Do not send CLs removing the interior tags from such phrases.

Ports

+

Loongson 64-bit

- Go 1.19 supports the Loongson 64-bit architecture LoongArch on Linux (GOOS=linux, GOARCH=loong64). + Go 1.19 adds support for the Loongson 64-bit architecture LoongArch + on Linux (GOOS=linux, GOARCH=loong64). +

+ +

RISC-V

+

+ The riscv64 port now supports passing function arguments + and result using registers. Benchmarking shows typical performance + improvements of 10% or more on riscv64.

Tools

@@ -116,7 +125,7 @@ as well as support for rendering them to HTML, Markdown, and text.

Vet

-

: +

The vet checker “errorsas” now reports when errors.As is called with a second argument of type *error, @@ -217,11 +226,6 @@ as well as support for rendering them to HTML, Markdown, and text. on the order of 20% faster. (GOARCH=amd64 and GOARCH=arm64 only)

-

- The riscv64 port now supports passing function arguments - and result using registers. Benchmarking shows typical performance - improvements of 10% or more on riscv64. -

The Go compiler now requires the -p=importpath flag to build a linkable object file. This is already supplied by From c29be2d41c6c3ed78a76b4d8d8c1c22d7e0ad5b7 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 18 Apr 2022 13:20:54 -0400 Subject: [PATCH 090/113] runtime: add HACKING section on nosplit functions Change-Id: I247874d5c49d2c0d8db2a3d227aa848093551d9b Reviewed-on: https://go-review.googlesource.com/c/go/+/401759 Reviewed-by: Michael Pratt --- src/runtime/HACKING.md | 48 +++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/runtime/HACKING.md b/src/runtime/HACKING.md index af9fe288aa..61b5a51959 100644 --- a/src/runtime/HACKING.md +++ b/src/runtime/HACKING.md @@ -41,8 +41,20 @@ All `g`, `m`, and `p` objects are heap allocated, but are never freed, so their memory remains type stable. As a result, the runtime can avoid write barriers in the depths of the scheduler. -User stacks and system stacks ------------------------------ +`getg()` and `getg().m.curg` +---------------------------- + +To get the current user `g`, use `getg().m.curg`. + +`getg()` alone returns the current `g`, but when executing on the +system or signal stacks, this will return the current M's "g0" or +"gsignal", respectively. This is usually not what you want. + +To determine if you're running on the user stack or the system stack, +use `getg() == getg().m.curg`. + +Stacks +====== Every non-dead G has a *user stack* associated with it, which is what user Go code executes on. User stacks start small (e.g., 2K) and grow @@ -63,17 +75,33 @@ non-preemptible and the garbage collector does not scan system stacks. While running on the system stack, the current user stack is not used for execution. -`getg()` and `getg().m.curg` ----------------------------- +nosplit functions +----------------- -To get the current user `g`, use `getg().m.curg`. +Most functions start with a prologue that inspects the stack pointer +and the current G's stack bound and calls `morestack` if the stack +needs to grow. -`getg()` alone returns the current `g`, but when executing on the -system or signal stacks, this will return the current M's "g0" or -"gsignal", respectively. This is usually not what you want. +Functions can be marked `//go:nosplit` (or `NOSPLIT` in assembly) to +indicate that they should not get this prologue. This has several +uses: -To determine if you're running on the user stack or the system stack, -use `getg() == getg().m.curg`. +- Functions that must run on the user stack, but must not call into + stack growth, for example because this would cause a deadlock, or + because they have untyped words on the stack. + +- Functions that must not be preempted on entry. + +- Functions that may run without a valid G. For example, functions + that run in early runtime start-up, or that may be entered from C + code such as cgo callbacks or the signal handler. + +Splittable functions ensure there's some amount of space on the stack +for nosplit functions to run in and the linker checks that any static +chain of nosplit function calls cannot exceed this bound. + +Any function with a `//go:nosplit` annotation should explain why it is +nosplit in its documentation comment. Error handling and reporting ============================ From cad477c922b8b6f71e3f0968822841430516c639 Mon Sep 17 00:00:00 2001 From: ag9920 Date: Tue, 14 Jun 2022 02:09:10 +0000 Subject: [PATCH 091/113] cpu: fix typos in test case Change-Id: Id6a27d0b3f3fc4181a00569bacc578e72b04ce09 GitHub-Last-Rev: 85c063d1a2d62181d16044592a60acf970fe3c86 GitHub-Pull-Request: golang/go#53359 Reviewed-on: https://go-review.googlesource.com/c/go/+/411916 Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Peter Zhang Auto-Submit: Ian Lance Taylor Reviewed-by: Robert Griesemer Run-TryBot: Ian Lance Taylor --- src/internal/cpu/cpu_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/internal/cpu/cpu_test.go b/src/internal/cpu/cpu_test.go index e72d2d639c..c95cd51726 100644 --- a/src/internal/cpu/cpu_test.go +++ b/src/internal/cpu/cpu_test.go @@ -19,7 +19,7 @@ func MustHaveDebugOptionsSupport(t *testing.T) { } } -func MustSupportFeatureDectection(t *testing.T) { +func MustSupportFeatureDetection(t *testing.T) { // TODO: add platforms that do not have CPU feature detection support. } @@ -41,7 +41,7 @@ func runDebugOptionsTest(t *testing.T, test string, options string) { } func TestDisableAllCapabilities(t *testing.T) { - MustSupportFeatureDectection(t) + MustSupportFeatureDetection(t) runDebugOptionsTest(t, "TestAllCapabilitiesDisabled", "cpu.all=off") } From cb9bf93078c54187f5be9d8a65c81c249d12d3c5 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 10 Jun 2022 15:46:43 -0700 Subject: [PATCH 092/113] cmd/go: quote package directory when calling glob Fixes #53314 Change-Id: I4933b59ee247daec5cf96eb15c52ff54d3ec26a9 Reviewed-on: https://go-review.googlesource.com/c/go/+/411696 Run-TryBot: Ian Lance Taylor Reviewed-by: Bryan Mills Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot Auto-Submit: Ian Lance Taylor Run-TryBot: Ian Lance Taylor --- src/cmd/go/internal/clean/clean.go | 3 ++- src/cmd/go/internal/load/pkg.go | 2 +- src/cmd/go/internal/modfetch/fetch.go | 5 +++-- src/cmd/go/internal/str/path.go | 18 ++++++++++++++++++ src/cmd/go/testdata/script/embed_brackets.txt | 18 ++++++++++++++++++ 5 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 src/cmd/go/testdata/script/embed_brackets.txt diff --git a/src/cmd/go/internal/clean/clean.go b/src/cmd/go/internal/clean/clean.go index 8564411fb6..019d36490f 100644 --- a/src/cmd/go/internal/clean/clean.go +++ b/src/cmd/go/internal/clean/clean.go @@ -22,6 +22,7 @@ import ( "cmd/go/internal/lockedfile" "cmd/go/internal/modfetch" "cmd/go/internal/modload" + "cmd/go/internal/str" "cmd/go/internal/work" ) @@ -141,7 +142,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { // The top cache directory may have been created with special permissions // and not something that we want to remove. Also, we'd like to preserve // the access log for future analysis, even if the cache is cleared. - subdirs, _ := filepath.Glob(filepath.Join(dir, "[0-9a-f][0-9a-f]")) + subdirs, _ := filepath.Glob(filepath.Join(str.QuoteGlob(dir), "[0-9a-f][0-9a-f]")) printedErrors := false if len(subdirs) > 0 { if cfg.BuildN || cfg.BuildX { diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index 394a4a4383..fe4a82472d 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -2063,7 +2063,7 @@ func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[st } // Glob to find matches. - match, err := fsys.Glob(pkgdir + string(filepath.Separator) + filepath.FromSlash(glob)) + match, err := fsys.Glob(str.QuoteGlob(pkgdir) + string(filepath.Separator) + filepath.FromSlash(glob)) if err != nil { return nil, nil, err } diff --git a/src/cmd/go/internal/modfetch/fetch.go b/src/cmd/go/internal/modfetch/fetch.go index 21d5f54688..a7c8c2c769 100644 --- a/src/cmd/go/internal/modfetch/fetch.go +++ b/src/cmd/go/internal/modfetch/fetch.go @@ -26,6 +26,7 @@ import ( "cmd/go/internal/lockedfile" "cmd/go/internal/par" "cmd/go/internal/robustio" + "cmd/go/internal/str" "cmd/go/internal/trace" "golang.org/x/mod/module" @@ -102,7 +103,7 @@ func download(ctx context.Context, mod module.Version) (dir string, err error) { // active. parentDir := filepath.Dir(dir) tmpPrefix := filepath.Base(dir) + ".tmp-" - if old, err := filepath.Glob(filepath.Join(parentDir, tmpPrefix+"*")); err == nil { + if old, err := filepath.Glob(filepath.Join(str.QuoteGlob(parentDir), str.QuoteGlob(tmpPrefix)+"*")); err == nil { for _, path := range old { RemoveAll(path) // best effort } @@ -224,7 +225,7 @@ func downloadZip(ctx context.Context, mod module.Version, zipfile string) (err e // This is only safe to do because the lock file ensures that their // writers are no longer active. tmpPattern := filepath.Base(zipfile) + "*.tmp" - if old, err := filepath.Glob(filepath.Join(filepath.Dir(zipfile), tmpPattern)); err == nil { + if old, err := filepath.Glob(filepath.Join(str.QuoteGlob(filepath.Dir(zipfile)), tmpPattern)); err == nil { for _, path := range old { os.Remove(path) // best effort } diff --git a/src/cmd/go/internal/str/path.go b/src/cmd/go/internal/str/path.go index a69e171f8c..c165b91785 100644 --- a/src/cmd/go/internal/str/path.go +++ b/src/cmd/go/internal/str/path.go @@ -66,3 +66,21 @@ func TrimFilePathPrefix(s, prefix string) string { } return trimmed[1:] } + +// QuoteGlob returns s with all Glob metacharacters quoted. +// We don't try to handle backslash here, as that can appear in a +// file path on Windows. +func QuoteGlob(s string) string { + if !strings.ContainsAny(s, `*?[]`) { + return s + } + var sb strings.Builder + for _, c := range s { + switch c { + case '*', '?', '[', ']': + sb.WriteByte('\\') + } + sb.WriteRune(c) + } + return sb.String() +} diff --git a/src/cmd/go/testdata/script/embed_brackets.txt b/src/cmd/go/testdata/script/embed_brackets.txt new file mode 100644 index 0000000000..7093a8497e --- /dev/null +++ b/src/cmd/go/testdata/script/embed_brackets.txt @@ -0,0 +1,18 @@ +# issue 53314 +[windows] skip +cd [pkg] +go build + +-- [pkg]/go.mod -- +module m + +go 1.19 +-- [pkg]/x.go -- +package p + +import _ "embed" + +//go:embed t.txt +var S string + +-- [pkg]//t.txt -- From e1e66a03a6bb3210034b640923fa253d7def1a26 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 14 Jun 2022 13:38:02 -0700 Subject: [PATCH 093/113] cmd/compile,runtime,reflect: move embedded bit from offset to name Previously we stole a bit from the field offset to encode whether a struct field was embedded. Instead, encode that bit in the name field, where we already have some unused bits to play with. The bit associates naturally with the name in any case. This leaves a full uintptr to specify field offsets. This will make the fix for #52740 cleaner. Change-Id: I0bfb85564dc26e8c18101bc8b432f332176d7836 Reviewed-on: https://go-review.googlesource.com/c/go/+/412138 Reviewed-by: Cherry Mui Run-TryBot: Keith Randall TryBot-Result: Gopher Robot Reviewed-by: Keith Randall --- .../compile/internal/reflectdata/reflect.go | 33 ++++---- src/cmd/link/internal/ld/decodesym.go | 17 ++++- src/cmd/link/internal/ld/dwarf.go | 6 +- src/internal/reflectlite/export_test.go | 2 +- src/internal/reflectlite/type.go | 21 ++--- src/reflect/abi.go | 2 +- src/reflect/export_test.go | 2 +- src/reflect/type.go | 76 ++++++++++--------- src/reflect/value.go | 2 +- src/runtime/alg.go | 2 +- src/runtime/cgocall.go | 2 +- src/runtime/syscall_windows.go | 2 +- src/runtime/type.go | 19 +++-- 13 files changed, 104 insertions(+), 82 deletions(-) diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index 3ffb7dcefa..21301ab149 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -412,7 +412,7 @@ func dimportpath(p *types.Pkg) { } s := base.Ctxt.Lookup("type..importpath." + p.Prefix + ".") - ot := dnameData(s, 0, p.Path, "", nil, false) + ot := dnameData(s, 0, p.Path, "", nil, false, false) objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA) s.Set(obj.AttrContentAddressable, true) p.Pathsym = s @@ -461,12 +461,12 @@ func dnameField(lsym *obj.LSym, ot int, spkg *types.Pkg, ft *types.Field) int { if !types.IsExported(ft.Sym.Name) && ft.Sym.Pkg != spkg { base.Fatalf("package mismatch for %v", ft.Sym) } - nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name)) + nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name), ft.Embedded != 0) return objw.SymPtr(lsym, ot, nsym, 0) } // dnameData writes the contents of a reflect.name into s at offset ot. -func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported bool) int { +func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported, embedded bool) int { if len(name) >= 1<<29 { base.Fatalf("name too long: %d %s...", len(name), name[:1024]) } @@ -491,6 +491,9 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported b if pkg != nil { bits |= 1 << 2 } + if embedded { + bits |= 1 << 3 + } b := make([]byte, l) b[0] = bits copy(b[1:], nameLen[:nameLenLen]) @@ -513,7 +516,7 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported b var dnameCount int // dname creates a reflect.name for a struct field or method. -func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym { +func dname(name, tag string, pkg *types.Pkg, exported, embedded bool) *obj.LSym { // Write out data as "type.." to signal two things to the // linker, first that when dynamically linking, the symbol // should be moved to a relro section, and second that the @@ -538,11 +541,14 @@ func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym { sname = fmt.Sprintf(`%s"".%d`, sname, dnameCount) dnameCount++ } + if embedded { + sname += ".embedded" + } s := base.Ctxt.Lookup(sname) if len(s.P) > 0 { return s } - ot := dnameData(s, 0, name, tag, pkg, exported) + ot := dnameData(s, 0, name, tag, pkg, exported, embedded) objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA) s.Set(obj.AttrContentAddressable, true) return s @@ -610,7 +616,7 @@ func dextratypeData(lsym *obj.LSym, ot int, t *types.Type) int { if !exported && a.name.Pkg != typePkg(t) { pkg = a.name.Pkg } - nsym := dname(a.name.Name, "", pkg, exported) + nsym := dname(a.name.Name, "", pkg, exported, false) ot = objw.SymPtrOff(lsym, ot, nsym) ot = dmethodptrOff(lsym, ot, writeType(a.mtype)) @@ -775,7 +781,7 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { } ot = objw.SymPtr(lsym, ot, gcsym, 0) // gcdata - nsym := dname(p, "", nil, exported) + nsym := dname(p, "", nil, exported, false) ot = objw.SymPtrOff(lsym, ot, nsym) // str // ptrToThis if sptr == nil { @@ -1074,7 +1080,7 @@ func writeType(t *types.Type) *obj.LSym { if !exported && a.name.Pkg != tpkg { pkg = a.name.Pkg } - nsym := dname(a.name.Name, "", pkg, exported) + nsym := dname(a.name.Name, "", pkg, exported, false) ot = objw.SymPtrOff(lsym, ot, nsym) ot = objw.SymPtrOff(lsym, ot, writeType(a.type_)) @@ -1180,14 +1186,7 @@ func writeType(t *types.Type) *obj.LSym { // ../../../../runtime/type.go:/structField ot = dnameField(lsym, ot, spkg, f) ot = objw.SymPtr(lsym, ot, writeType(f.Type), 0) - offsetAnon := uint64(f.Offset) << 1 - if offsetAnon>>1 != uint64(f.Offset) { - base.Fatalf("%v: bad field offset for %s", t, f.Sym.Name) - } - if f.Embedded != 0 { - offsetAnon |= 1 - } - ot = objw.Uintptr(lsym, ot, offsetAnon) + ot = objw.Uintptr(lsym, ot, uint64(f.Offset)) } } @@ -1356,7 +1355,7 @@ func WriteTabs() { // name nameOff // typ typeOff // pointer to symbol // } - nsym := dname(p.Sym().Name, "", nil, true) + nsym := dname(p.Sym().Name, "", nil, true, false) t := p.Type() if p.Class != ir.PFUNC { t = types.NewPtr(t) diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index a6ae202859..b0f4b87563 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -132,6 +132,15 @@ func decodetypeName(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs return string(data[1+nameLenLen : 1+nameLenLen+int(nameLen)]) } +func decodetypeNameEmbedded(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) bool { + r := decodeRelocSym(ldr, symIdx, relocs, int32(off)) + if r == 0 { + return false + } + data := ldr.Data(r) + return data[0]&(1<<3) != 0 +} + func decodetypeFuncInType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym { uadd := commonsize(arch) + 4 if arch.PtrSize == 8 { @@ -204,12 +213,18 @@ func decodetypeStructFieldType(ldr *loader.Loader, arch *sys.Arch, symIdx loader return decodeRelocSym(ldr, symIdx, &relocs, int32(off+arch.PtrSize)) } -func decodetypeStructFieldOffsAnon(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 { +func decodetypeStructFieldOffset(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 { off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i) data := ldr.Data(symIdx) return int64(decodeInuxi(arch, data[off+2*arch.PtrSize:], arch.PtrSize)) } +func decodetypeStructFieldEmbedded(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) bool { + off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i) + relocs := ldr.Relocs(symIdx) + return decodetypeNameEmbedded(ldr, symIdx, &relocs, off) +} + // decodetypeStr returns the contents of an rtype's str field (a nameOff). func decodetypeStr(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) string { relocs := ldr.Relocs(symIdx) diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 6ed9697aec..c42511ea3f 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -682,9 +682,9 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { } fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f) d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) - offsetAnon := decodetypeStructFieldOffsAnon(d.ldr, d.arch, gotype, i) - newmemberoffsetattr(fld, int32(offsetAnon>>1)) - if offsetAnon&1 != 0 { // is embedded field + offset := decodetypeStructFieldOffset(d.ldr, d.arch, gotype, i) + newmemberoffsetattr(fld, int32(offset)) + if decodetypeStructFieldEmbedded(d.ldr, d.arch, gotype, i) { newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0) } } diff --git a/src/internal/reflectlite/export_test.go b/src/internal/reflectlite/export_test.go index 3e5c258fb1..e9a928bdc6 100644 --- a/src/internal/reflectlite/export_test.go +++ b/src/internal/reflectlite/export_test.go @@ -36,7 +36,7 @@ func Field(v Value, i int) Value { // In the former case, we want v.ptr + offset. // In the latter case, we must have field.offset = 0, // so v.ptr + field.offset is still the correct address. - ptr := add(v.ptr, field.offset(), "same as non-reflect &v.field") + ptr := add(v.ptr, field.offset, "same as non-reflect &v.field") return Value{typ, ptr, fl} } diff --git a/src/internal/reflectlite/type.go b/src/internal/reflectlite/type.go index bc6fc94773..21e3c1278d 100644 --- a/src/internal/reflectlite/type.go +++ b/src/internal/reflectlite/type.go @@ -269,17 +269,13 @@ type sliceType struct { // Struct field type structField struct { - name name // name is always non-empty - typ *rtype // type of field - offsetEmbed uintptr // byte offset of field<<1 | isEmbedded -} - -func (f *structField) offset() uintptr { - return f.offsetEmbed >> 1 + name name // name is always non-empty + typ *rtype // type of field + offset uintptr // byte offset of field } func (f *structField) embedded() bool { - return f.offsetEmbed&1 != 0 + return f.name.embedded() } // structType represents a struct type. @@ -328,6 +324,10 @@ func (n name) hasTag() bool { return (*n.bytes)&(1<<1) != 0 } +func (n name) embedded() bool { + return (*n.bytes)&(1<<3) != 0 +} + // readVarint parses a varint as encoded by encoding/binary. // It returns the number of encoded bytes and the encoded value. func (n name) readVarint(off int) (int, int) { @@ -947,7 +947,10 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { if cmpTags && tf.name.tag() != vf.name.tag() { return false } - if tf.offsetEmbed != vf.offsetEmbed { + if tf.offset != vf.offset { + return false + } + if tf.embedded() != vf.embedded() { return false } } diff --git a/src/reflect/abi.go b/src/reflect/abi.go index 9957d23768..32cb314188 100644 --- a/src/reflect/abi.go +++ b/src/reflect/abi.go @@ -237,7 +237,7 @@ func (a *abiSeq) regAssign(t *rtype, offset uintptr) bool { st := (*structType)(unsafe.Pointer(t)) for i := range st.fields { f := &st.fields[i] - if !a.regAssign(f.typ, offset+f.offset()) { + if !a.regAssign(f.typ, offset+f.offset) { return false } } diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go index a5a3c1c271..f7d2cc362d 100644 --- a/src/reflect/export_test.go +++ b/src/reflect/export_test.go @@ -140,7 +140,7 @@ func IsExported(t Type) bool { } func ResolveReflectName(s string) { - resolveReflectName(newName(s, "", false)) + resolveReflectName(newName(s, "", false, false)) } type Buffer struct { diff --git a/src/reflect/type.go b/src/reflect/type.go index 97040b5188..7b8cf0ee62 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -433,17 +433,13 @@ type sliceType struct { // Struct field type structField struct { - name name // name is always non-empty - typ *rtype // type of field - offsetEmbed uintptr // byte offset of field<<1 | isEmbedded -} - -func (f *structField) offset() uintptr { - return f.offsetEmbed >> 1 + name name // name is always non-empty + typ *rtype // type of field + offset uintptr // byte offset of field } func (f *structField) embedded() bool { - return f.offsetEmbed&1 != 0 + return f.name.embedded() } // structType represents a struct type. @@ -460,6 +456,7 @@ type structType struct { // 1<<0 the name is exported // 1<<1 tag data follows the name // 1<<2 pkgPath nameOff follows the name and tag +// 1<<3 the name is of an embedded (a.k.a. anonymous) field // // Following that, there is a varint-encoded length of the name, // followed by the name itself. @@ -496,6 +493,10 @@ func (n name) hasTag() bool { return (*n.bytes)&(1<<1) != 0 } +func (n name) embedded() bool { + return (*n.bytes)&(1<<3) != 0 +} + // readVarint parses a varint as encoded by encoding/binary. // It returns the number of encoded bytes and the encoded value. func (n name) readVarint(off int) (int, int) { @@ -565,7 +566,7 @@ func (n name) pkgPath() string { return pkgPathName.name() } -func newName(n, tag string, exported bool) name { +func newName(n, tag string, exported, embedded bool) name { if len(n) >= 1<<29 { panic("reflect.nameFrom: name too long: " + n[:1024] + "...") } @@ -586,6 +587,9 @@ func newName(n, tag string, exported bool) name { l += tagLenLen + len(tag) bits |= 1 << 1 } + if embedded { + bits |= 1 << 3 + } b := make([]byte, l) b[0] = bits @@ -1256,7 +1260,7 @@ func (t *structType) Field(i int) (f StructField) { if tag := p.name.tag(); tag != "" { f.Tag = StructTag(tag) } - f.Offset = p.offset() + f.Offset = p.offset // NOTE(rsc): This is the only allocation in the interface // presented by a reflect.Type. It would be nice to avoid, @@ -1472,7 +1476,7 @@ func (t *rtype) ptrTo() *rtype { prototype := *(**ptrType)(unsafe.Pointer(&iptr)) pp := *prototype - pp.str = resolveReflectName(newName(s, "", false)) + pp.str = resolveReflectName(newName(s, "", false, false)) pp.ptrToThis = 0 // For the type structures linked into the binary, the @@ -1739,7 +1743,10 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { if cmpTags && tf.name.tag() != vf.name.tag() { return false } - if tf.offsetEmbed != vf.offsetEmbed { + if tf.offset != vf.offset { + return false + } + if tf.embedded() != vf.embedded() { return false } } @@ -1891,7 +1898,7 @@ func ChanOf(dir ChanDir, t Type) Type { ch := *prototype ch.tflag = tflagRegularMemory ch.dir = uintptr(dir) - ch.str = resolveReflectName(newName(s, "", false)) + ch.str = resolveReflectName(newName(s, "", false, false)) ch.hash = fnv1(typ.hash, 'c', byte(dir)) ch.elem = typ @@ -1934,7 +1941,7 @@ func MapOf(key, elem Type) Type { // in ../cmd/compile/internal/reflectdata/reflect.go:writeType. var imap any = (map[unsafe.Pointer]unsafe.Pointer)(nil) mt := **(**mapType)(unsafe.Pointer(&imap)) - mt.str = resolveReflectName(newName(s, "", false)) + mt.str = resolveReflectName(newName(s, "", false, false)) mt.tflag = 0 mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash)) mt.key = ktyp @@ -2113,7 +2120,7 @@ func FuncOf(in, out []Type, variadic bool) Type { } // Populate the remaining fields of ft and store in cache. - ft.str = resolveReflectName(newName(str, "", false)) + ft.str = resolveReflectName(newName(str, "", false, false)) ft.ptrToThis = 0 return addToCache(&ft.rtype) } @@ -2290,7 +2297,7 @@ func bucketOf(ktyp, etyp *rtype) *rtype { gcdata: gcdata, } s := "bucket(" + ktyp.String() + "," + etyp.String() + ")" - b.str = resolveReflectName(newName(s, "", false)) + b.str = resolveReflectName(newName(s, "", false, false)) return b } @@ -2369,7 +2376,7 @@ func SliceOf(t Type) Type { prototype := *(**sliceType)(unsafe.Pointer(&islice)) slice := *prototype slice.tflag = 0 - slice.str = resolveReflectName(newName(s, "", false)) + slice.str = resolveReflectName(newName(s, "", false, false)) slice.hash = fnv1(typ.hash, '[') slice.elem = typ slice.ptrToThis = 0 @@ -2632,7 +2639,7 @@ func StructOf(fields []StructField) Type { typalign = ft.align } size = offset + ft.size - f.offsetEmbed |= offset << 1 + f.offset = offset if ft.size == 0 { lastzero = size @@ -2698,7 +2705,7 @@ func StructOf(fields []StructField) Type { *typ = *prototype typ.fields = fs if pkgpath != "" { - typ.pkgPath = newName(pkgpath, "", false) + typ.pkgPath = newName(pkgpath, "", false, false) } // Look in cache. @@ -2742,7 +2749,7 @@ func StructOf(fields []StructField) Type { } } - typ.str = resolveReflectName(newName(str, "", false)) + typ.str = resolveReflectName(newName(str, "", false, false)) typ.tflag = 0 // TODO: set tflagRegularMemory typ.hash = hash typ.size = size @@ -2774,14 +2781,14 @@ func StructOf(fields []StructField) Type { continue } // Pad to start of this field with zeros. - if ft.offset() > off { - n := (ft.offset() - off) / goarch.PtrSize + if ft.offset > off { + n := (ft.offset - off) / goarch.PtrSize prog = append(prog, 0x01, 0x00) // emit a 0 bit if n > 1 { prog = append(prog, 0x81) // repeat previous bit prog = appendVarint(prog, n-1) // n-1 times } - off = ft.offset() + off = ft.offset } prog = appendGCProg(prog, ft.typ) @@ -2803,8 +2810,8 @@ func StructOf(fields []StructField) Type { if comparable { typ.equal = func(p, q unsafe.Pointer) bool { for _, ft := range typ.fields { - pi := add(p, ft.offset(), "&x.field safe") - qi := add(q, ft.offset(), "&x.field safe") + pi := add(p, ft.offset, "&x.field safe") + qi := add(q, ft.offset, "&x.field safe") if !ft.typ.equal(pi, qi) { return false } @@ -2841,16 +2848,11 @@ func runtimeStructField(field StructField) (structField, string) { } } - offsetEmbed := uintptr(0) - if field.Anonymous { - offsetEmbed |= 1 - } - resolveReflectType(field.Type.common()) // install in runtime f := structField{ - name: newName(field.Name, string(field.Tag), field.IsExported()), - typ: field.Type.common(), - offsetEmbed: offsetEmbed, + name: newName(field.Name, string(field.Tag), field.IsExported(), field.Anonymous), + typ: field.Type.common(), + offset: 0, } return f, field.PkgPath } @@ -2874,7 +2876,7 @@ func typeptrdata(t *rtype) uintptr { return 0 } f := st.fields[field] - return f.offset() + f.typ.ptrdata + return f.offset + f.typ.ptrdata default: panic("reflect.typeptrdata: unexpected type, " + t.String()) @@ -2917,7 +2919,7 @@ func ArrayOf(length int, elem Type) Type { prototype := *(**arrayType)(unsafe.Pointer(&iarray)) array := *prototype array.tflag = typ.tflag & tflagRegularMemory - array.str = resolveReflectName(newName(s, "", false)) + array.str = resolveReflectName(newName(s, "", false, false)) array.hash = fnv1(typ.hash, '[') for n := uint32(length); n > 0; n >>= 8 { array.hash = fnv1(array.hash, byte(n)) @@ -3097,7 +3099,7 @@ func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Poo } else { s = "funcargs(" + t.String() + ")" } - x.str = resolveReflectName(newName(s, "", false)) + x.str = resolveReflectName(newName(s, "", false, false)) // cache result for future callers framePool = &sync.Pool{New: func() any { @@ -3165,7 +3167,7 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) { tt := (*structType)(unsafe.Pointer(t)) for i := range tt.fields { f := &tt.fields[i] - addTypeBits(bv, offset+f.offset(), f.typ) + addTypeBits(bv, offset+f.offset, f.typ) } } } diff --git a/src/reflect/value.go b/src/reflect/value.go index 5abdca2820..74554a3ac8 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1287,7 +1287,7 @@ func (v Value) Field(i int) Value { // In the former case, we want v.ptr + offset. // In the latter case, we must have field.offset = 0, // so v.ptr + field.offset is still the correct address. - ptr := add(v.ptr, field.offset(), "same as non-reflect &v.field") + ptr := add(v.ptr, field.offset, "same as non-reflect &v.field") return Value{typ, ptr, fl} } diff --git a/src/runtime/alg.go b/src/runtime/alg.go index 5d7d1c77f4..2a413eeef3 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -182,7 +182,7 @@ func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr { if f.name.isBlank() { continue } - h = typehash(f.typ, add(p, f.offset()), h) + h = typehash(f.typ, add(p, f.offset), h) } return h default: diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index 977d049378..892654ed5b 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -536,7 +536,7 @@ func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) { if f.typ.ptrdata == 0 { continue } - cgoCheckArg(f.typ, add(p, f.offset()), true, top, msg) + cgoCheckArg(f.typ, add(p, f.offset), true, top, msg) } case kindPtr, kindUnsafePointer: if indir { diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go index a841a31a27..e42d71ad65 100644 --- a/src/runtime/syscall_windows.go +++ b/src/runtime/syscall_windows.go @@ -174,7 +174,7 @@ func (p *abiDesc) tryRegAssignArg(t *_type, offset uintptr) bool { st := (*structtype)(unsafe.Pointer(t)) for i := range st.fields { f := &st.fields[i] - if !p.tryRegAssignArg(f.typ, offset+f.offset()) { + if !p.tryRegAssignArg(f.typ, offset+f.offset) { return false } } diff --git a/src/runtime/type.go b/src/runtime/type.go index b650d6d795..e8e7819ecf 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -414,13 +414,9 @@ type ptrtype struct { } type structfield struct { - name name - typ *_type - offsetAnon uintptr -} - -func (f *structfield) offset() uintptr { - return f.offsetAnon >> 1 + name name + typ *_type + offset uintptr } type structtype struct { @@ -443,6 +439,10 @@ func (n name) isExported() bool { return (*n.bytes)&(1<<0) != 0 } +func (n name) isEmbedded() bool { + return (*n.bytes)&(1<<3) != 0 +} + func (n name) readvarint(off int) (int, int) { v := 0 for i := 0; ; i++ { @@ -703,7 +703,10 @@ func typesEqual(t, v *_type, seen map[_typePair]struct{}) bool { if tf.name.tag() != vf.name.tag() { return false } - if tf.offsetAnon != vf.offsetAnon { + if tf.offset != vf.offset { + return false + } + if tf.name.isEmbedded() != vf.name.isEmbedded() { return false } } From c22a6c3b906cd37616d76da5f504c4c3e5677d94 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 14 Jun 2022 14:38:56 -0700 Subject: [PATCH 094/113] reflect: when StructOf overflows computing size/offset, panic Fixes #52740 Change-Id: I849e585deb77cfcfc1b517be4a171eb29b30c5f3 Reviewed-on: https://go-review.googlesource.com/c/go/+/412214 Reviewed-by: Cherry Mui Reviewed-by: Keith Randall --- src/reflect/all_test.go | 81 +++++++++++++++++++++++++++++++++++++++++ src/reflect/type.go | 15 +++++++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index febbd5f5a7..56d91105a6 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -5891,6 +5891,87 @@ func TestStructOfDifferentPkgPath(t *testing.T) { }) } +func TestStructOfTooLarge(t *testing.T) { + t1 := TypeOf(byte(0)) + t2 := TypeOf(int16(0)) + t4 := TypeOf(int32(0)) + t0 := ArrayOf(0, t1) + + // 2^64-3 sized type (or 2^32-3 on 32-bit archs) + bigType := StructOf([]StructField{ + {Name: "F1", Type: ArrayOf(int(^uintptr(0)>>1), t1)}, + {Name: "F2", Type: ArrayOf(int(^uintptr(0)>>1-1), t1)}, + }) + + type test struct { + shouldPanic bool + fields []StructField + } + + tests := [...]test{ + { + shouldPanic: false, // 2^64-1, ok + fields: []StructField{ + {Name: "F1", Type: bigType}, + {Name: "F2", Type: ArrayOf(2, t1)}, + }, + }, + { + shouldPanic: true, // overflow in total size + fields: []StructField{ + {Name: "F1", Type: bigType}, + {Name: "F2", Type: ArrayOf(3, t1)}, + }, + }, + { + shouldPanic: true, // overflow while aligning F2 + fields: []StructField{ + {Name: "F1", Type: bigType}, + {Name: "F2", Type: t4}, + }, + }, + { + shouldPanic: true, // overflow while adding trailing byte for zero-sized fields + fields: []StructField{ + {Name: "F1", Type: bigType}, + {Name: "F2", Type: ArrayOf(2, t1)}, + {Name: "F3", Type: t0}, + }, + }, + { + shouldPanic: true, // overflow while aligning total size + fields: []StructField{ + {Name: "F1", Type: t2}, + {Name: "F2", Type: bigType}, + }, + }, + } + + for i, tt := range tests { + func() { + defer func() { + err := recover() + if !tt.shouldPanic { + if err != nil { + t.Errorf("test %d should not panic, got %s", i, err) + } + return + } + if err == nil { + t.Errorf("test %d expected to panic", i) + return + } + s := fmt.Sprintf("%s", err) + if s != "reflect.StructOf: struct size would exceed virtual address space" { + t.Errorf("test %d wrong panic message: %s", i, s) + return + } + }() + _ = StructOf(tt.fields) + }() + } +} + func TestChanOf(t *testing.T) { // check construction and use of type not in binary type T string diff --git a/src/reflect/type.go b/src/reflect/type.go index 7b8cf0ee62..fc591eee69 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -2635,10 +2635,16 @@ func StructOf(fields []StructField) Type { comparable = comparable && (ft.equal != nil) offset := align(size, uintptr(ft.align)) + if offset < size { + panic("reflect.StructOf: struct size would exceed virtual address space") + } if ft.align > typalign { typalign = ft.align } size = offset + ft.size + if size < offset { + panic("reflect.StructOf: struct size would exceed virtual address space") + } f.offset = offset if ft.size == 0 { @@ -2655,6 +2661,9 @@ func StructOf(fields []StructField) Type { // zero-sized field can't manufacture a pointer to the // next object in the heap. See issue 9401. size++ + if size == 0 { + panic("reflect.StructOf: struct size would exceed virtual address space") + } } var typ *structType @@ -2697,7 +2706,11 @@ func StructOf(fields []StructField) Type { str := string(repr) // Round the size up to be a multiple of the alignment. - size = align(size, uintptr(typalign)) + s := align(size, uintptr(typalign)) + if s < size { + panic("reflect.StructOf: struct size would exceed virtual address space") + } + size = s // Make the struct type. var istruct any = struct{}{} From 0dffda13834545317569052a9de7dfbf27b62c5d Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 13 Jun 2022 18:44:44 -0700 Subject: [PATCH 095/113] spec: clarify "slice of bytes" and "slice of runes" through examples The spec section on conversions uses the terms "slice of bytes" and "slice of runes". While not obviously clear, what is meant are slice types whose element types are byte or rune types; specifically the underlying types of the slices' element types must be byte or rune. Some of this was evident from the examples, but not all of it. Made this clearer by adding more examples illustrating various permitted conversions. Note that the 1.17 compiler did not accept the following conversions: string([]myByte{...}) string([]myRune{...}) myString([]myByte{...}) myString([]myRune{...}) (where myByte, myRune, and myString have underlying types of byte, rune, and string respectively) - it reported an internal error. But it did accept the inverse conversions: []myByte("...") []myRune("...") []myByte(myString("...")) []myRune(myString("...")) The 1.18 compiler made those conversions symmetric and they are now permitted in both directions. The extra examples reflect this reality. Fixes #23814. Change-Id: I5a1c200b45ddd0e8c0dc0d11da3a6c39cb2dc848 Reviewed-on: https://go-review.googlesource.com/c/go/+/412094 Reviewed-by: Robert Griesemer Reviewed-by: Ian Lance Taylor --- doc/go_spec.html | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/doc/go_spec.html b/doc/go_spec.html index b5f6c5fd65..cc77fd12a9 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -5245,7 +5245,7 @@ float32(0.49999999) // 0.5 of type float32 float64(-1e-1000) // 0.0 of type float64 string('x') // "x" of type string string(0x266c) // "♬" of type string -MyString("foo" + "bar") // "foobar" of type MyString +myString("foo" + "bar") // "foobar" of type myString string([]byte{'a'}) // not a constant: []byte{'a'} is not a constant (*int)(nil) // not a constant: nil is not a constant, *int is not a boolean, numeric, or string type int(1.2) // illegal: 1.2 cannot be represented as an int @@ -5428,8 +5428,9 @@ the range of valid Unicode code points are converted to "\uFFFD". string('a') // "a" string(-1) // "\ufffd" == "\xef\xbf\xbd" string(0xf8) // "\u00f8" == "ø" == "\xc3\xb8" -type MyString string -MyString(0x65e5) // "\u65e5" == "日" == "\xe6\x97\xa5" + +type myString string +myString(0x65e5) // "\u65e5" == "日" == "\xe6\x97\xa5"

@@ -5442,8 +5443,12 @@ string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" string([]byte{}) // "" string([]byte(nil)) // "" -type MyBytes []byte -string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" +type bytes []byte +string(bytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" + +type myByte byte +string([]myByte{'w', 'o', 'r', 'l', 'd', '!'}) // "world!" +myString([]myByte{'\xf0', '\x9f', '\x8c', '\x8d'}) // "🌍" @@ -5457,8 +5462,12 @@ string([]rune{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" string([]rune{}) // "" string([]rune(nil)) // "" -type MyRunes []rune -string(MyRunes{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" +type runes []rune +string(runes{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" + +type myRune rune +string([]myRune{0x266b, 0x266c}) // "\u266b\u266c" == "♫♬" +myString([]myRune{0x1F30E}) // "\U0001f30e" == "🌎" @@ -5467,10 +5476,13 @@ Converting a value of a string type to a slice of bytes type yields a slice whose successive elements are the bytes of the string.
-[]byte("hellø")   // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
-[]byte("")        // []byte{}
+[]byte("hellø")             // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
+[]byte("")                  // []byte{}
 
-MyBytes("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
+bytes("hellø")              // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
+
+[]myByte("world!")          // []myByte{'w', 'o', 'r', 'l', 'd', '!'}
+[]myByte(myString("🌏"))    // []myByte{'\xf0', '\x9f', '\x8c', '\x8f'}
 
@@ -5479,10 +5491,13 @@ Converting a value of a string type to a slice of runes type yields a slice containing the individual Unicode code points of the string.
-[]rune(MyString("白鵬翔"))  // []rune{0x767d, 0x9d6c, 0x7fd4}
-[]rune("")                 // []rune{}
+[]rune(myString("白鵬翔"))   // []rune{0x767d, 0x9d6c, 0x7fd4}
+[]rune("")                  // []rune{}
 
-MyRunes("白鵬翔")           // []rune{0x767d, 0x9d6c, 0x7fd4}
+runes("白鵬翔")              // []rune{0x767d, 0x9d6c, 0x7fd4}
+
+[]myRune("♫♬")              // []myRune{0x266b, 0x266c}
+[]myRune(myString("🌐"))    // []myRune{0x1f310}
 
From f9c0264107a9a36832d70781fe100cff16917855 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 14 Jun 2022 15:53:59 -0700 Subject: [PATCH 096/113] net: avoid infinite recursion in Windows Resolver.lookupTXT For #33097 Change-Id: I6138dc844f0b29b01c78a02efc1e1b1ad719b803 Reviewed-on: https://go-review.googlesource.com/c/go/+/412139 Reviewed-by: Brad Fitzpatrick Auto-Submit: Ian Lance Taylor Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Dmitri Shuralyov Run-TryBot: Ian Lance Taylor --- src/net/lookup_windows.go | 2 +- src/net/lookup_windows_test.go | 82 ++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/src/net/lookup_windows.go b/src/net/lookup_windows.go index 051f47da39..9ff39c74a4 100644 --- a/src/net/lookup_windows.go +++ b/src/net/lookup_windows.go @@ -329,7 +329,7 @@ func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) { func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) { if r.preferGoOverWindows() { - return r.lookupTXT(ctx, name) + return r.goLookupTXT(ctx, name) } // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. acquireThread() diff --git a/src/net/lookup_windows_test.go b/src/net/lookup_windows_test.go index 9254733364..823ec088b8 100644 --- a/src/net/lookup_windows_test.go +++ b/src/net/lookup_windows_test.go @@ -6,6 +6,7 @@ package net import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -26,104 +27,117 @@ func toJson(v any) string { return string(data) } +func testLookup(t *testing.T, fn func(*testing.T, *Resolver, string)) { + for _, def := range []bool{true, false} { + def := def + for _, server := range nslookupTestServers { + server := server + var name string + if def { + name = "default/" + } else { + name = "go/" + } + t.Run(name+server, func(t *testing.T) { + t.Parallel() + r := DefaultResolver + if !def { + r = &Resolver{PreferGo: true} + } + fn(t, r, server) + }) + } + } +} + func TestNSLookupMX(t *testing.T) { testenv.MustHaveExternalNetwork(t) - for _, server := range nslookupTestServers { - mx, err := LookupMX(server) + testLookup(t, func(t *testing.T, r *Resolver, server string) { + mx, err := r.LookupMX(context.Background(), server) if err != nil { - t.Error(err) - continue + t.Fatal(err) } if len(mx) == 0 { - t.Errorf("no results") - continue + t.Fatal("no results") } expected, err := nslookupMX(server) if err != nil { - t.Logf("skipping failed nslookup %s test: %s", server, err) + t.Skipf("skipping failed nslookup %s test: %s", server, err) } sort.Sort(byPrefAndHost(expected)) sort.Sort(byPrefAndHost(mx)) if !reflect.DeepEqual(expected, mx) { t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(mx)) } - } + }) } func TestNSLookupCNAME(t *testing.T) { testenv.MustHaveExternalNetwork(t) - for _, server := range nslookupTestServers { - cname, err := LookupCNAME(server) + testLookup(t, func(t *testing.T, r *Resolver, server string) { + cname, err := r.LookupCNAME(context.Background(), server) if err != nil { - t.Errorf("failed %s: %s", server, err) - continue + t.Fatalf("failed %s: %s", server, err) } if cname == "" { - t.Errorf("no result %s", server) + t.Fatalf("no result %s", server) } expected, err := nslookupCNAME(server) if err != nil { - t.Logf("skipping failed nslookup %s test: %s", server, err) - continue + t.Skipf("skipping failed nslookup %s test: %s", server, err) } if expected != cname { t.Errorf("different results %s:\texp:%v\tgot:%v", server, expected, cname) } - } + }) } func TestNSLookupNS(t *testing.T) { testenv.MustHaveExternalNetwork(t) - for _, server := range nslookupTestServers { - ns, err := LookupNS(server) + testLookup(t, func(t *testing.T, r *Resolver, server string) { + ns, err := r.LookupNS(context.Background(), server) if err != nil { - t.Errorf("failed %s: %s", server, err) - continue + t.Fatalf("failed %s: %s", server, err) } if len(ns) == 0 { - t.Errorf("no results") - continue + t.Fatal("no results") } expected, err := nslookupNS(server) if err != nil { - t.Logf("skipping failed nslookup %s test: %s", server, err) - continue + t.Skipf("skipping failed nslookup %s test: %s", server, err) } sort.Sort(byHost(expected)) sort.Sort(byHost(ns)) if !reflect.DeepEqual(expected, ns) { t.Errorf("different results %s:\texp:%v\tgot:%v", toJson(server), toJson(expected), ns) } - } + }) } func TestNSLookupTXT(t *testing.T) { testenv.MustHaveExternalNetwork(t) - for _, server := range nslookupTestServers { - txt, err := LookupTXT(server) + testLookup(t, func(t *testing.T, r *Resolver, server string) { + txt, err := r.LookupTXT(context.Background(), server) if err != nil { - t.Errorf("failed %s: %s", server, err) - continue + t.Fatalf("failed %s: %s", server, err) } if len(txt) == 0 { - t.Errorf("no results") - continue + t.Fatalf("no results") } expected, err := nslookupTXT(server) if err != nil { - t.Logf("skipping failed nslookup %s test: %s", server, err) - continue + t.Skipf("skipping failed nslookup %s test: %s", server, err) } sort.Strings(expected) sort.Strings(txt) if !reflect.DeepEqual(expected, txt) { t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(txt)) } - } + }) } func TestLookupLocalPTR(t *testing.T) { From 2a78e8afc0994f5b292bc9a5a7258c749e43032f Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 13 Jun 2022 22:15:25 -0700 Subject: [PATCH 097/113] test: add tests for string/[]byte/[]rune conversions Matches examples in spec section on string conversions. For #23814. Change-Id: I08099c27bfdb98735868266f5a42901321b97b56 Reviewed-on: https://go-review.googlesource.com/c/go/+/412095 TryBot-Result: Gopher Robot Reviewed-by: Robert Griesemer Reviewed-by: Ian Lance Taylor Run-TryBot: Robert Griesemer --- test/fixedbugs/issue23814.go | 61 ++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 test/fixedbugs/issue23814.go diff --git a/test/fixedbugs/issue23814.go b/test/fixedbugs/issue23814.go new file mode 100644 index 0000000000..25ed2322b6 --- /dev/null +++ b/test/fixedbugs/issue23814.go @@ -0,0 +1,61 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Examples from the language spec section on string conversions. + +package main + +func main() { + // 1 + _ = string('a') // "a" + _ = string(-1) // "\ufffd" == "\xef\xbf\xbd" + _ = string(0xf8) // "\u00f8" == "ø" == "\xc3\xb8" + + type myString string + _ = myString(0x65e5) // "\u65e5" == "日" == "\xe6\x97\xa5" + + // 2 + _ = string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" + _ = string([]byte{}) // "" + _ = string([]byte(nil)) // "" + + type bytes []byte + _ = string(bytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" + + type myByte byte + _ = string([]myByte{'w', 'o', 'r', 'l', 'd', '!'}) // "world!" + _ = myString([]myByte{'\xf0', '\x9f', '\x8c', '\x8d'}) // "🌍 + + // 3 + _ = string([]rune{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" + _ = string([]rune{}) // "" + _ = string([]rune(nil)) // "" + + type runes []rune + _ = string(runes{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" + + type myRune rune + _ = string([]myRune{0x266b, 0x266c}) // "\u266b\u266c" == "♫♬" + _ = myString([]myRune{0x1f30e}) // "\U0001f30e" == "🌎 + + // 4 + _ = []byte("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'} + _ = []byte("") // []byte{} + + _ = bytes("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'} + + _ = []myByte("world!") // []myByte{'w', 'o', 'r', 'l', 'd', '!'} + _ = []myByte(myString("🌏")) // []myByte{'\xf0', '\x9f', '\x8c', '\x8f'} + + // 5 + _ = []rune(myString("白鵬翔")) // []rune{0x767d, 0x9d6c, 0x7fd4} + _ = []rune("") // []rune{} + + _ = runes("白鵬翔") // []rune{0x767d, 0x9d6c, 0x7fd4} + + _ = []myRune("♫♬") // []myRune{0x266b, 0x266c} + _ = []myRune(myString("🌐")) // []myRune{0x1f310} +} From 36147dd1e8d8e21affbf5d8a758608e63304e4a7 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Wed, 15 Jun 2022 11:50:27 -0400 Subject: [PATCH 098/113] cmd/go/internal/modindex: disable indexing for modules outside GOROOT and the module cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since CL 410821 we were indexing these modules with a cache key based on the mtimes of the files within the module. However, that seems to be causing test failures (#53269 and maybe #53371). In addition, indexing these modules caused a potentially-expensive operation (re-indexing a whole module) whenever any individual file within the module is changed, even if it isn't relevant to the package(s) being loaded from that module. In some cases, that could cause a significant performance regression for 'go' commands invoked on a small subset of the packages in the module (such as running 'go test' on a single changed package — a common case during development). Instead, we now index only those modules found within the module cache and within GOROOT. In addition, we now check mtimes when indexing GOROOT modules if the Go version begins with the string "devel ", which indicates a non-released Go version that may include local file edits within GOROOT. For #53371. For #53269. Change-Id: Id3aa81b55ecfc478e47dd420148d39d2cf476f2d Reviewed-on: https://go-review.googlesource.com/c/go/+/412394 Reviewed-by: Michael Matloob Auto-Submit: Bryan Mills TryBot-Result: Gopher Robot Run-TryBot: Bryan Mills --- src/cmd/go/internal/modindex/read.go | 68 ++++++++++++++++++---------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/src/cmd/go/internal/modindex/read.go b/src/cmd/go/internal/modindex/read.go index 6ec3a6b3af..ffa091df41 100644 --- a/src/cmd/go/internal/modindex/read.go +++ b/src/cmd/go/internal/modindex/read.go @@ -62,38 +62,56 @@ type ModuleIndex struct { var fcache par.Cache func moduleHash(modroot string, ismodcache bool) (cache.ActionID, error) { + // We expect modules stored within the module cache to be checksummed and + // immutable, and we expect released Go modules to change only infrequently + // (when the Go version changes). + if !ismodcache || !str.HasFilePathPrefix(modroot, cfg.GOROOT) { + return cache.ActionID{}, ErrNotIndexed + } + h := cache.NewHash("moduleIndex") fmt.Fprintf(h, "module index %s %s %v\n", runtime.Version(), indexVersion, modroot) - if ismodcache || str.HasFilePathPrefix(modroot, cfg.GOROOT) { - return h.Sum(), nil - } - // walkdir happens in deterministic order. - err := fsys.Walk(modroot, func(path string, info fs.FileInfo, err error) error { - if modroot == path { - // Check for go.mod in root directory, and return ErrNotIndexed - // if it doesn't exist. Outside the module cache, it's not a module - // if it doesn't have a go.mod file. - } - if err := moduleWalkErr(modroot, path, info, err); err != nil { - return err - } - if info.IsDir() { - return nil - } - fmt.Fprintf(h, "file %v %v\n", info.Name(), info.ModTime()) - if info.Mode()&fs.ModeSymlink != 0 { - targ, err := fsys.Stat(path) - if err != nil { + if strings.HasPrefix(runtime.Version(), "devel ") { + // This copy of the standard library is a development version, not a + // release. It could be based on a Git commit (like "devel go1.19-2a78e8afc0 + // Wed Jun 15 00:06:24 2022 +0000") with or without changes on top of that + // commit, or it could be completly artificial due to lacking a `git` binary + // (like "devel gomote.XXXXX", as synthesized by "gomote push" as of + // 2022-06-15). Compute an inexpensive hash of its files using mtimes so + // that during development we can continue to exercise the logic for cached + // GOROOT indexes. + // + // mtimes may be granular, imprecise, and loosely updated (see + // https://apenwarr.ca/log/20181113), but we don't expect Go contributors to + // be mucking around with the import graphs in GOROOT often enough for mtime + // collisions to matter essentially ever. + // + // Note that fsys.Walk walks paths in deterministic order, so this hash + // should be completely deterministic if the files are unchanged. + err := fsys.Walk(modroot, func(path string, info fs.FileInfo, err error) error { + if err := moduleWalkErr(modroot, path, info, err); err != nil { return err } - fmt.Fprintf(h, "target %v %v\n", targ.Name(), targ.ModTime()) + + if info.IsDir() { + return nil + } + fmt.Fprintf(h, "file %v %v\n", info.Name(), info.ModTime()) + if info.Mode()&fs.ModeSymlink != 0 { + targ, err := fsys.Stat(path) + if err != nil { + return err + } + fmt.Fprintf(h, "target %v %v\n", targ.Name(), targ.ModTime()) + } + return nil + }) + if err != nil { + return cache.ActionID{}, err } - return nil - }) - if err != nil { - return cache.ActionID{}, err } + return h.Sum(), nil } From c2c76c6f198480f3c9aece4aa5d9b8de044d8457 Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Tue, 14 Jun 2022 16:47:57 -0400 Subject: [PATCH 099/113] cmd/link: set alignment for carrier symbols For carrier symbols like type.*, currently we don't set its alignment. Normally it doesn't actually matter as we still align the inner symbols. But in some cases it does make the symbol table a bit weird, e.g. on darwin/arm64, 0000000000070000 s _runtime.types 0000000000070001 s _type.* The address of the symbol _type.* is a bit weird. And the new darwin linker from Xcode 14 beta doesn't like that (see issue 53372). This CL aligns them. Fixes #53372. Change-Id: I1cb19dcf172e9a6bca248d85a7e54da76cbbc8a4 Reviewed-on: https://go-review.googlesource.com/c/go/+/411912 Reviewed-by: Than McIntosh Run-TryBot: Cherry Mui TryBot-Result: Gopher Robot --- src/cmd/link/internal/ld/symtab.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index cc6a2c0e10..ee963bc366 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -475,16 +475,19 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { s = ldr.CreateSymForUpdate("type.*", 0) s.SetType(sym.STYPE) s.SetSize(0) + s.SetAlign(int32(ctxt.Arch.PtrSize)) symtype = s.Sym() s = ldr.CreateSymForUpdate("typerel.*", 0) s.SetType(sym.STYPERELRO) s.SetSize(0) + s.SetAlign(int32(ctxt.Arch.PtrSize)) symtyperel = s.Sym() } else { s = ldr.CreateSymForUpdate("type.*", 0) s.SetType(sym.STYPE) s.SetSize(0) + s.SetAlign(int32(ctxt.Arch.PtrSize)) symtype = s.Sym() symtyperel = s.Sym() } @@ -496,6 +499,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { s := ldr.CreateSymForUpdate(name, 0) s.SetType(t) s.SetSize(0) + s.SetAlign(int32(ctxt.Arch.PtrSize)) s.SetLocal(true) setCarrierSym(t, s.Sym()) return s.Sym() From 937fa5000a7bb07ed62d35a1aea9ea0819659084 Mon Sep 17 00:00:00 2001 From: subham sarkar Date: Wed, 8 Jun 2022 15:08:33 +0530 Subject: [PATCH 100/113] net/netip: add missing ) in ParsePrefix errors The existing error messages didn't add right parenthesis ')' properly leading to improper formation of error messages. Fixes #53283 Change-Id: Iadf9b8059403efa07e39716a81fab68cd10b7f87 Reviewed-on: https://go-review.googlesource.com/c/go/+/411015 Auto-Submit: Tobias Klauser TryBot-Result: Gopher Robot Reviewed-by: Ian Lance Taylor Auto-Submit: Ian Lance Taylor Run-TryBot: Tobias Klauser Run-TryBot: Ian Lance Taylor Reviewed-by: Tobias Klauser Reviewed-by: Damien Neil --- src/net/netip/netip.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net/netip/netip.go b/src/net/netip/netip.go index eae9c29ea7..bb83371a55 100644 --- a/src/net/netip/netip.go +++ b/src/net/netip/netip.go @@ -1310,14 +1310,14 @@ func ParsePrefix(s string) (Prefix, error) { bitsStr := s[i+1:] bits, err := strconv.Atoi(bitsStr) if err != nil { - return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + ": bad bits after slash: " + strconv.Quote(bitsStr)) + return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + "): bad bits after slash: " + strconv.Quote(bitsStr)) } maxBits := 32 if ip.Is6() { maxBits = 128 } if bits < 0 || bits > maxBits { - return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + ": prefix length out of range") + return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + "): prefix length out of range") } return PrefixFrom(ip, bits), nil } From 97bfc77f3897d6268cf4f9bb6756b97ff5e7cc03 Mon Sep 17 00:00:00 2001 From: Guoqi Chen Date: Fri, 10 Jun 2022 19:08:14 +0800 Subject: [PATCH 101/113] syscall, runtime/internal/syscall: always zero the higher bits of return value on linux/loong64 All loong64 syscalls return values only via R4/A0, and R5/A1 may contain unrelated content. Always zero the second return value. Change-Id: I62af59369bece5bd8028b937c74f4694150f7a55 Reviewed-on: https://go-review.googlesource.com/c/go/+/411615 Run-TryBot: Ian Lance Taylor TryBot-Result: Gopher Robot Auto-Submit: Ian Lance Taylor Reviewed-by: Ian Lance Taylor Reviewed-by: Austin Clements --- src/runtime/internal/syscall/asm_linux_loong64.s | 2 +- src/syscall/asm_linux_loong64.s | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/internal/syscall/asm_linux_loong64.s b/src/runtime/internal/syscall/asm_linux_loong64.s index ac500fb3b1..d6a33f90a7 100644 --- a/src/runtime/internal/syscall/asm_linux_loong64.s +++ b/src/runtime/internal/syscall/asm_linux_loong64.s @@ -24,6 +24,6 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80 RET ok: MOVV R4, r1+56(FP) - MOVV R5, r2+64(FP) + MOVV R0, r2+64(FP) // r2 is not used. Always set to 0. MOVV R0, errno+72(FP) RET diff --git a/src/syscall/asm_linux_loong64.s b/src/syscall/asm_linux_loong64.s index 2e7d0c7a3e..7dc69c6612 100644 --- a/src/syscall/asm_linux_loong64.s +++ b/src/syscall/asm_linux_loong64.s @@ -40,5 +40,5 @@ TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48 MOVV trap+0(FP), R11 // syscall entry SYSCALL MOVV R4, r1+32(FP) - MOVV R5, r2+40(FP) + MOVV R0, r2+40(FP) // r2 is not used. Always set to 0. RET From 0cd0c12f576a3be39f44d20145eba334adce0bba Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 15 Jun 2022 14:40:13 +0200 Subject: [PATCH 102/113] doc/go1.19: use matching closing tag in unix build constraint heading Change-Id: Idb990eac60e334a5901b2d6cdc2380225d011dd6 Reviewed-on: https://go-review.googlesource.com/c/go/+/412294 Auto-Submit: Ian Lance Taylor TryBot-Result: Gopher Robot Run-TryBot: Ian Lance Taylor Reviewed-by: Cherry Mui Reviewed-by: Ian Lance Taylor --- doc/go1.19.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/go1.19.html b/doc/go1.19.html index 727873890b..50bc973c13 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -72,7 +72,7 @@ provides parsing and reformatting of doc comments as well as support for rendering them to HTML, Markdown, and text.

-

New unix build constraint

+

New unix build constraint

The build constraint unix is now recognized From 74bf90c779b3d4a4babd3e3de38e3d3e5d9dd7de Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 14 Jun 2022 19:01:04 -0700 Subject: [PATCH 103/113] go/types, types2: add test case for issue for coverage The specific error doesn't occur anymore. Add a test to prevent regressions. For #50729. Change-Id: Ibf6ef6009b3d226b4f345b5a5657939915f19633 Reviewed-on: https://go-review.googlesource.com/c/go/+/412235 Reviewed-by: Robert Griesemer Reviewed-by: Ian Lance Taylor --- .../types2/testdata/fixedbugs/issue50729.go | 19 +++++++++++++++++++ src/go/types/testdata/fixedbugs/issue50729.go | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue50729.go create mode 100644 src/go/types/testdata/fixedbugs/issue50729.go diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50729.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50729.go new file mode 100644 index 0000000000..fe19fdfa68 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50729.go @@ -0,0 +1,19 @@ +// 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 + +// version 1 +var x1 T1[B1] + +type T1[_ any] struct{} +type A1 T1[B1] +type B1 = T1[A1] + +// version 2 +type T2[_ any] struct{} +type A2 T2[B2] +type B2 = T2[A2] + +var x2 T2[B2] diff --git a/src/go/types/testdata/fixedbugs/issue50729.go b/src/go/types/testdata/fixedbugs/issue50729.go new file mode 100644 index 0000000000..fe19fdfa68 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue50729.go @@ -0,0 +1,19 @@ +// 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 + +// version 1 +var x1 T1[B1] + +type T1[_ any] struct{} +type A1 T1[B1] +type B1 = T1[A1] + +// version 2 +type T2[_ any] struct{} +type A2 T2[B2] +type B2 = T2[A2] + +var x2 T2[B2] From 0e3d0c9581d1d31a94f5c70a528ff0bdba5c523d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Bo=C4=8Dek?= Date: Wed, 15 Jun 2022 09:36:27 +0000 Subject: [PATCH 104/113] syscall: clarify Pdeathsig documentation on Linux This is a rather large footgun, so let's mention that it sends the signal on thread termination and not process termination in the documentation. Updates #27505 Change-Id: I489cf7136e34a1a7896067ae24187b0d523d987e GitHub-Last-Rev: c8722b25d1fb8b0b3696257ec7e955eb421f15a6 GitHub-Pull-Request: golang/go#53365 Reviewed-on: https://go-review.googlesource.com/c/go/+/412114 Reviewed-by: Ian Lance Taylor Auto-Submit: Ian Lance Taylor TryBot-Result: Gopher Robot Run-TryBot: Ian Lance Taylor Reviewed-by: Cherry Mui --- src/syscall/exec_linux.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go index 6d4b6939ad..ede8247da9 100644 --- a/src/syscall/exec_linux.go +++ b/src/syscall/exec_linux.go @@ -45,7 +45,11 @@ type SysProcAttr struct { // number in the parent process. Foreground bool Pgid int // Child's process group ID if Setpgid. - Pdeathsig Signal // Signal that the process will get when its parent dies (Linux and FreeBSD only) + // Pdeathsig, if non-zero, is a signal that the kernel will send to + // the child process when the creating thread dies. Note that the signal + // is sent on thread termination, which may happen before process termination. + // There are more details at https://go.dev/issue/27505. + Pdeathsig Signal Cloneflags uintptr // Flags for clone calls (Linux only) Unshareflags uintptr // Flags for unshare calls (Linux only) UidMappings []SysProcIDMap // User ID mappings for user namespaces. From 91baf5ceccc363c21925aee8611c2c279806238b Mon Sep 17 00:00:00 2001 From: Koichi Shiraishi Date: Fri, 27 May 2022 05:49:07 +0900 Subject: [PATCH 105/113] reflect: fix reference comment to runtime/map.go Change-Id: Icb552dc7106afbf6bd4bd3660d632f174153f834 Reviewed-on: https://go-review.googlesource.com/c/go/+/408914 Reviewed-by: Ian Lance Taylor Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot Auto-Submit: Ian Lance Taylor Reviewed-by: Cherry Mui Run-TryBot: Ian Lance Taylor --- src/reflect/type.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reflect/type.go b/src/reflect/type.go index fc591eee69..a52d3129df 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -2232,7 +2232,7 @@ func hashMightPanic(t *rtype) bool { } } -// Make sure these routines stay in sync with ../../runtime/map.go! +// Make sure these routines stay in sync with ../runtime/map.go! // These types exist only for GC, so we only fill out GC relevant info. // Currently, that's just size and the GC program. We also fill in string // for possible debugging use. From b6c16068897c370661f03af91e8ba46860408e61 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 8 Jun 2022 16:33:45 -0700 Subject: [PATCH 106/113] internal/goarch, internal/goos: update generators for syslist.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the generator programs for the changes to syslist.go in CL 390274 and the changes to the generated files in CL 344955. Tested by running the programs and verifying that the files did not change. Fixes #53299 Change-Id: I2b2c5769f7e9283aa05c803256d2ea1eb9ad1547 Reviewed-on: https://go-review.googlesource.com/c/go/+/411334 Reviewed-by: Daniel Martí Run-TryBot: Ian Lance Taylor Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor Reviewed-by: Cherry Mui Auto-Submit: Ian Lance Taylor TryBot-Result: Gopher Robot --- src/go/build/syslist.go | 4 ++++ src/internal/goarch/gengoarch.go | 19 +++++++++++-------- src/internal/goos/gengoos.go | 19 +++++++++++-------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/go/build/syslist.go b/src/go/build/syslist.go index ea67662c3e..35cffce6dc 100644 --- a/src/go/build/syslist.go +++ b/src/go/build/syslist.go @@ -4,6 +4,10 @@ package build +// Note that this file is read by internal/goarch/gengoarch.go and by +// internal/goos/gengoos.go. If you change this file, look at those +// files as well. + // knownOS is the list of past, present, and future known GOOS values. // Do not remove from this list, as it is used for filename matching. // If you add an entry to this list, look at unixOS, below. diff --git a/src/internal/goarch/gengoarch.go b/src/internal/goarch/gengoarch.go index 3c706e04ad..0b0be5cd15 100644 --- a/src/internal/goarch/gengoarch.go +++ b/src/internal/goarch/gengoarch.go @@ -11,7 +11,6 @@ import ( "fmt" "log" "os" - "strconv" "strings" ) @@ -22,14 +21,18 @@ func main() { if err != nil { log.Fatal(err) } - const goarchPrefix = `const goarchList = ` + const goarchPrefix = `var knownArch = map[string]bool{` + inGOARCH := false for _, line := range strings.Split(string(data), "\n") { if strings.HasPrefix(line, goarchPrefix) { - text, err := strconv.Unquote(strings.TrimPrefix(line, goarchPrefix)) - if err != nil { - log.Fatalf("parsing goarchList: %v", err) - } - goarches = strings.Fields(text) + inGOARCH = true + } else if inGOARCH && strings.HasPrefix(line, "}") { + break + } else if inGOARCH { + goarch := strings.Fields(line)[0] + goarch = strings.TrimPrefix(goarch, `"`) + goarch = strings.TrimSuffix(goarch, `":`) + goarches = append(goarches, goarch) } } @@ -39,7 +42,7 @@ func main() { } var buf bytes.Buffer fmt.Fprintf(&buf, "// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT.\n\n") - fmt.Fprintf(&buf, "//go:build %s\n", target) // must explicitly include target for bootstrapping purposes + fmt.Fprintf(&buf, "//go:build %s\n\n", target) // must explicitly include target for bootstrapping purposes fmt.Fprintf(&buf, "package goarch\n\n") fmt.Fprintf(&buf, "const GOARCH = `%s`\n\n", target) for _, goarch := range goarches { diff --git a/src/internal/goos/gengoos.go b/src/internal/goos/gengoos.go index 1b62503fd0..37d9706d1e 100644 --- a/src/internal/goos/gengoos.go +++ b/src/internal/goos/gengoos.go @@ -11,7 +11,6 @@ import ( "fmt" "log" "os" - "strconv" "strings" ) @@ -22,14 +21,18 @@ func main() { if err != nil { log.Fatal(err) } - const goosPrefix = `const goosList = ` + const goosPrefix = `var knownOS = map[string]bool{` + inGOOS := false for _, line := range strings.Split(string(data), "\n") { if strings.HasPrefix(line, goosPrefix) { - text, err := strconv.Unquote(strings.TrimPrefix(line, goosPrefix)) - if err != nil { - log.Fatalf("parsing goosList: %v", err) - } - gooses = strings.Fields(text) + inGOOS = true + } else if inGOOS && strings.HasPrefix(line, "}") { + break + } else if inGOOS { + goos := strings.Fields(line)[0] + goos = strings.TrimPrefix(goos, `"`) + goos = strings.TrimSuffix(goos, `":`) + gooses = append(gooses, goos) } } @@ -50,7 +53,7 @@ func main() { tags = append(tags, target) // must explicitly include target for bootstrapping purposes var buf bytes.Buffer fmt.Fprintf(&buf, "// Code generated by gengoos.go using 'go generate'. DO NOT EDIT.\n\n") - fmt.Fprintf(&buf, "//go:build %s\n", strings.Join(tags, " && ")) + fmt.Fprintf(&buf, "//go:build %s\n\n", strings.Join(tags, " && ")) fmt.Fprintf(&buf, "package goos\n\n") fmt.Fprintf(&buf, "const GOOS = `%s`\n\n", target) for _, goos := range gooses { From ecc268aa26b81cb53f2f6f62ea9d074a610771fe Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 15 Jun 2022 15:42:47 -0700 Subject: [PATCH 107/113] test: add test that gofrontend fails For #52870 Change-Id: Ic0791af4283c9e426f7cbfab0514517ff84cfa80 Reviewed-on: https://go-review.googlesource.com/c/go/+/412535 Run-TryBot: Ian Lance Taylor TryBot-Result: Gopher Robot Run-TryBot: Ian Lance Taylor Reviewed-by: Cherry Mui Reviewed-by: Ian Lance Taylor Auto-Submit: Ian Lance Taylor --- test/fixedbugs/issue52870.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 test/fixedbugs/issue52870.go diff --git a/test/fixedbugs/issue52870.go b/test/fixedbugs/issue52870.go new file mode 100644 index 0000000000..a1c441a77d --- /dev/null +++ b/test/fixedbugs/issue52870.go @@ -0,0 +1,27 @@ +// compile + +// 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. + +// Issue 52870: gofrontend gave incorrect error when incorrectly +// compiling ambiguous promoted method. + +package p + +type S1 struct { + *S2 +} + +type S2 struct { + T3 + T4 +} + +type T3 int32 + +func (T3) M() {} + +type T4 int32 + +func (T4) M() {} From bcce8ef4982cf29715895277ad84aaf16991e06b Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 14 Jun 2022 22:08:31 -0700 Subject: [PATCH 108/113] spec: adjust incorrect sentence in section on rune literals Add an additional example. Fixes #53217. Change-Id: I899376b9c1fa8dc5d475d8d3d6c8788ab79b0847 Reviewed-on: https://go-review.googlesource.com/c/go/+/412238 Reviewed-by: Robert Griesemer Reviewed-by: Ian Lance Taylor Auto-Submit: Robert Griesemer Reviewed-by: Ian Lance Taylor --- doc/go_spec.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/go_spec.html b/doc/go_spec.html index cc77fd12a9..ab172ac40e 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -502,8 +502,9 @@ After a backslash, certain single-character escapes represent special values:

-All other sequences starting with a backslash are illegal inside rune literals. +An unrecognized character following a backslash in a rune literal is illegal.

+
 rune_lit         = "'" ( unicode_value | byte_value ) "'" .
 unicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .
@@ -530,6 +531,7 @@ escaped_char     = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `
 '\U00101234'
 '\''         // rune literal containing single quote character
 'aa'         // illegal: too many characters
+'\k'         // illegal: k is not recognized after a backslash
 '\xa'        // illegal: too few hexadecimal digits
 '\0'         // illegal: too few octal digits
 '\400'       // illegal: octal value over 255

From 1d9d99b7ce279f2af928f79cbc5906d99f29bb67 Mon Sep 17 00:00:00 2001
From: Cherry Mui 
Date: Thu, 16 Jun 2022 11:35:40 -0400
Subject: [PATCH 109/113] cmd/link: consider alignment in carrier symbol size
 calculation

Currently, when we calculate the size of a carrier symbol, we use
the previous symbol's end address as the start. But the symbol
actually starts after applying the alignment. Do this in the
size calculation.

Should fix AIX build.

Updates #53372.

Change-Id: I17942b1fe8027dce12b78c8e8c80ea6cebcee240
Reviewed-on: https://go-review.googlesource.com/c/go/+/412734
Run-TryBot: Cherry Mui 
TryBot-Result: Gopher Robot 
Reviewed-by: Than McIntosh 
---
 src/cmd/link/internal/ld/data.go | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index f12cb78fb8..cb2afeaa9a 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -1854,6 +1854,9 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
 	}
 	for _, symn := range sym.ReadOnly {
 		symnStartValue := state.datsize
+		if len(state.data[symn]) != 0 {
+			symnStartValue = aligndatsize(state, symnStartValue, state.data[symn][0])
+		}
 		state.assignToSection(sect, symn, sym.SRODATA)
 		setCarrierSize(symn, state.datsize-symnStartValue)
 		if ctxt.HeadType == objabi.Haix {
@@ -1935,6 +1938,9 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
 
 			symn := sym.RelROMap[symnro]
 			symnStartValue := state.datsize
+			if len(state.data[symn]) != 0 {
+				symnStartValue = aligndatsize(state, symnStartValue, state.data[symn][0])
+			}
 
 			for _, s := range state.data[symn] {
 				outer := ldr.OuterSym(s)

From 74f1fa6ecbf79c778fc18d2a6b563fbb94f4b740 Mon Sep 17 00:00:00 2001
From: Michael Matloob 
Date: Wed, 4 May 2022 17:11:35 -0400
Subject: [PATCH 110/113] cmd/go: parallelize matchPackages work in each module

In each module matchPackages looks in, when doing the walk, do the
scanDir call in a par.Queue so all the read work can be done in
parallel.

Change-Id: I27153dbb3a2ed417ca24972f47134e9e914a55d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/404097
Reviewed-by: Michael Matloob 
Run-TryBot: Michael Matloob 
TryBot-Result: Gopher Robot 
Reviewed-by: Bryan Mills 
---
 src/cmd/go/internal/modload/search.go | 38 ++++++++++++++++++---------
 1 file changed, 26 insertions(+), 12 deletions(-)

diff --git a/src/cmd/go/internal/modload/search.go b/src/cmd/go/internal/modload/search.go
index 60c68860ed..4b90392d94 100644
--- a/src/cmd/go/internal/modload/search.go
+++ b/src/cmd/go/internal/modload/search.go
@@ -12,12 +12,16 @@ import (
 	"os"
 	"path"
 	"path/filepath"
+	"runtime"
+	"sort"
 	"strings"
+	"sync"
 
 	"cmd/go/internal/cfg"
 	"cmd/go/internal/fsys"
 	"cmd/go/internal/imports"
 	"cmd/go/internal/modindex"
+	"cmd/go/internal/par"
 	"cmd/go/internal/search"
 
 	"golang.org/x/mod/module"
@@ -43,9 +47,15 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f
 		treeCanMatch = search.TreeCanMatchPattern(m.Pattern())
 	}
 
+	var mu sync.Mutex
 	have := map[string]bool{
 		"builtin": true, // ignore pseudo-package that exists only for documentation
 	}
+	addPkg := func(p string) {
+		mu.Lock()
+		m.Pkgs = append(m.Pkgs, p)
+		mu.Unlock()
+	}
 	if !cfg.BuildContext.CgoEnabled {
 		have["runtime/cgo"] = true // ignore during walk
 	}
@@ -56,6 +66,8 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f
 		pruneGoMod
 	)
 
+	q := par.NewQueue(runtime.GOMAXPROCS(0))
+
 	walkPkgs := func(root, importPathRoot string, prune pruning) {
 		root = filepath.Clean(root)
 		err := fsys.Walk(root, func(path string, fi fs.FileInfo, err error) error {
@@ -110,9 +122,11 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f
 			if !have[name] {
 				have[name] = true
 				if isMatch(name) {
-					if _, _, err := scanDir(root, path, tags); err != imports.ErrNoGo {
-						m.Pkgs = append(m.Pkgs, name)
-					}
+					q.Add(func() {
+						if _, _, err := scanDir(root, path, tags); err != imports.ErrNoGo {
+							addPkg(name)
+						}
+					})
 				}
 			}
 
@@ -126,6 +140,12 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f
 		}
 	}
 
+	// Wait for all in-flight operations to complete before returning.
+	defer func() {
+		<-q.Idle()
+		sort.Strings(m.Pkgs) // sort everything we added for determinism
+	}()
+
 	if filter == includeStd {
 		walkPkgs(cfg.GOROOTsrc, "", pruneGoMod)
 		if treeCanMatch("cmd") {
@@ -169,7 +189,7 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f
 			modPrefix = mod.Path
 		}
 		if mi, err := modindex.Get(root); err == nil {
-			walkFromIndex(ctx, m, tags, root, mi, have, modPrefix)
+			walkFromIndex(mi, modPrefix, isMatch, treeCanMatch, tags, have, addPkg)
 			continue
 		} else if !errors.Is(err, modindex.ErrNotIndexed) {
 			m.AddError(err)
@@ -188,13 +208,7 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f
 // walkFromIndex matches packages in a module using the module index. modroot
 // is the module's root directory on disk, index is the ModuleIndex for the
 // module, and importPathRoot is the module's path prefix.
-func walkFromIndex(ctx context.Context, m *search.Match, tags map[string]bool, modroot string, index *modindex.ModuleIndex, have map[string]bool, importPathRoot string) {
-	isMatch := func(string) bool { return true }
-	treeCanMatch := func(string) bool { return true }
-	if !m.IsMeta() {
-		isMatch = search.MatchPattern(m.Pattern())
-		treeCanMatch = search.TreeCanMatchPattern(m.Pattern())
-	}
+func walkFromIndex(index *modindex.ModuleIndex, importPathRoot string, isMatch, treeCanMatch func(string) bool, tags, have map[string]bool, addPkg func(string)) {
 loopPackages:
 	for _, reldir := range index.Packages() {
 		// Avoid .foo, _foo, and testdata subdirectory trees.
@@ -232,7 +246,7 @@ loopPackages:
 			have[name] = true
 			if isMatch(name) {
 				if _, _, err := index.ScanDir(reldir, tags); err != imports.ErrNoGo {
-					m.Pkgs = append(m.Pkgs, name)
+					addPkg(name)
 				}
 			}
 		}

From 32510eea742162bc8048e8eaa68c2c5b1d8712d2 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Thu, 16 Jun 2022 11:10:54 -0700
Subject: [PATCH 111/113] go/parser: remove unused method checkBinaryExpr

Change-Id: Ica981657e50e30cbfa1757e8457819a479f11c7d
Reviewed-on: https://go-review.googlesource.com/c/go/+/412775
Reviewed-by: Ian Lance Taylor 
TryBot-Result: Gopher Robot 
Auto-Submit: Robert Griesemer 
Run-TryBot: Robert Griesemer 
Reviewed-by: Robert Griesemer 
---
 src/go/parser/parser.go | 17 -----------------
 1 file changed, 17 deletions(-)

diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index 18041ff808..ca2f24c8b8 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -1821,23 +1821,6 @@ func (p *parser) parseBinaryExpr(x ast.Expr, prec1 int, check bool) ast.Expr {
 	}
 }
 
-// checkBinaryExpr checks binary expressions that were not already checked by
-// parseBinaryExpr, because the latter was called with check=false.
-func (p *parser) checkBinaryExpr(x ast.Expr) {
-	bx, ok := x.(*ast.BinaryExpr)
-	if !ok {
-		return
-	}
-
-	bx.X = p.checkExpr(bx.X)
-	bx.Y = p.checkExpr(bx.Y)
-
-	// parseBinaryExpr checks x and y for each binary expr in a tree, so we
-	// traverse the tree of binary exprs starting from x.
-	p.checkBinaryExpr(bx.X)
-	p.checkBinaryExpr(bx.Y)
-}
-
 // The result may be a type or even a raw type ([...]int). Callers must
 // check the result (using checkExpr or checkExprOrType), depending on
 // context.

From ef808ae1d446700aeeb19d5aa041ca14db44c951 Mon Sep 17 00:00:00 2001
From: Ian Lance Taylor 
Date: Sat, 28 May 2022 07:51:56 -0700
Subject: [PATCH 112/113] expvar: don't crash if map value set to nil

Fixes #52719

Change-Id: Ib032193d00664090c47ae92e7d59674ec2d0165a
Reviewed-on: https://go-review.googlesource.com/c/go/+/408677
Reviewed-by: Alan Donovan 
Auto-Submit: Ian Lance Taylor 
Reviewed-by: Russ Cox 
Run-TryBot: Ian Lance Taylor 
TryBot-Result: Gopher Robot 
Reviewed-by: Jonathan Amsterdam 
---
 src/expvar/expvar.go      | 10 ++++++++--
 src/expvar/expvar_test.go | 23 +++++++++++++++++++++++
 2 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/src/expvar/expvar.go b/src/expvar/expvar.go
index 5629f89353..08cd05565d 100644
--- a/src/expvar/expvar.go
+++ b/src/expvar/expvar.go
@@ -118,7 +118,12 @@ func (v *Map) String() string {
 		if !first {
 			fmt.Fprintf(&b, ", ")
 		}
-		fmt.Fprintf(&b, "%q: %v", kv.Key, kv.Value)
+		fmt.Fprintf(&b, "%q: ", kv.Key)
+		if kv.Value != nil {
+			fmt.Fprintf(&b, "%v", kv.Value)
+		} else {
+			fmt.Fprint(&b, "null")
+		}
 		first = false
 	})
 	fmt.Fprintf(&b, "}")
@@ -224,7 +229,8 @@ func (v *Map) Do(f func(KeyValue)) {
 	defer v.keysMu.RUnlock()
 	for _, k := range v.keys {
 		i, _ := v.m.Load(k)
-		f(KeyValue{k, i.(Var)})
+		val, _ := i.(Var)
+		f(KeyValue{k, val})
 	}
 }
 
diff --git a/src/expvar/expvar_test.go b/src/expvar/expvar_test.go
index ba95a36066..552bae8c64 100644
--- a/src/expvar/expvar_test.go
+++ b/src/expvar/expvar_test.go
@@ -261,6 +261,29 @@ func TestMapCounter(t *testing.T) {
 	}
 }
 
+func TestMapNil(t *testing.T) {
+	RemoveAll()
+	const key = "key"
+	m := NewMap("issue527719")
+	m.Set(key, nil)
+	s := m.String()
+	var j any
+	if err := json.Unmarshal([]byte(s), &j); err != nil {
+		t.Fatalf("m.String() == %q isn't valid JSON: %v", s, err)
+	}
+	m2, ok := j.(map[string]any)
+	if !ok {
+		t.Fatalf("m.String() produced %T, wanted a map", j)
+	}
+	v, ok := m2[key]
+	if !ok {
+		t.Fatalf("missing %q in %v", key, m2)
+	}
+	if v != nil {
+		t.Fatalf("m[%q] = %v, want nil", key, v)
+	}
+}
+
 func BenchmarkMapSet(b *testing.B) {
 	m := new(Map).Init()
 

From 635b1244aa7671bcd665613680f527452cac7555 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Thu, 16 Jun 2022 15:43:57 -0400
Subject: [PATCH 113/113] cmd/go: pass GOEXPERIMENT through to subtests

This fixes:

	export GOEXPERIMENT=unified
	go install cmd
	go install std cmd
	go install std cmd
	go test -short cmd/go -run=TestScript/test_relative_import_dash_i

That script test checks that runtime is non-stale, but whether it's stale
depends on the setting of GOEXPERIMENT. Stop filtering that variable out.

Change-Id: I71bdbca495c16981cdcddf4ab4d87a38ca72a389
Reviewed-on: https://go-review.googlesource.com/c/go/+/412874
Run-TryBot: Russ Cox 
TryBot-Result: Gopher Robot 
Reviewed-by: Matthew Dempsky 
Auto-Submit: Russ Cox 
---
 src/cmd/go/script_test.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go
index 04bc8d581a..3ad0608725 100644
--- a/src/cmd/go/script_test.go
+++ b/src/cmd/go/script_test.go
@@ -170,6 +170,7 @@ func (ts *testScript) setup() {
 		"GOCACHE=" + testGOCACHE,
 		"GODEBUG=" + os.Getenv("GODEBUG"),
 		"GOEXE=" + cfg.ExeSuffix,
+		"GOEXPERIMENT=" + os.Getenv("GOEXPERIMENT"),
 		"GOOS=" + runtime.GOOS,
 		"GOPATH=" + filepath.Join(ts.workdir, "gopath"),
 		"GOPROXY=" + proxyURL,