From 9ae8f71c9431d287893443fa2b7fbdb72a9b56a2 Mon Sep 17 00:00:00 2001
From: Jeremy Faller
Date: Mon, 3 Aug 2020 13:19:46 -0400
Subject: [PATCH 0001/1082] [dev.link] cmd/link: stop renumbering files for
pclntab generation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Creates two new symbols: runtime.cutab, and runtime.filetab, and strips
the filenames out of runtime.pclntab_old.
All stats are for cmd/compile.
Time:
Pclntab_GC 48.2ms ± 3% 45.5ms ± 9% -5.47% (p=0.004 n=9+9)
Alloc/op:
Pclntab_GC 30.0MB ± 0% 29.5MB ± 0% -1.88% (p=0.000 n=10+10)
Allocs/op:
Pclntab_GC 90.4k ± 0% 73.1k ± 0% -19.11% (p=0.000 n=10+10)
live-B:
Pclntab_GC 29.1M ± 0% 29.2M ± 0% +0.10% (p=0.000 n=10+10)
binary sizes:
NEW: 18565600
OLD: 18532768
The size differences in the binary are caused by the increased size of
the Func objects, and (less likely) some extra alignment padding needed
as a result. This is probably the maximum increase in size we'll size
from the pclntab reworking.
Change-Id: Idd95a9b159fea46f7701cfe6506813b88257fbea
Reviewed-on: https://go-review.googlesource.com/c/go/+/246497
Run-TryBot: Jeremy Faller
TryBot-Result: Gobot Gobot
Reviewed-by: Than McIntosh
Reviewed-by: Austin Clements
---
src/cmd/link/internal/ld/data.go | 4 +
src/cmd/link/internal/ld/link.go | 1 -
src/cmd/link/internal/ld/pcln.go | 303 ++++++++++++++++-------------
src/cmd/link/internal/ld/symtab.go | 12 +-
src/debug/gosym/pclntab.go | 52 +++--
src/runtime/runtime2.go | 7 +-
src/runtime/symtab.go | 19 +-
7 files changed, 231 insertions(+), 167 deletions(-)
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index dc7096ea8cc..a551d464038 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -1923,6 +1923,8 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pcheader", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.funcnametab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.cutab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.filetab", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab_old", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect)
if ctxt.HeadType == objabi.Haix {
@@ -2507,6 +2509,8 @@ func (ctxt *Link) address() []*sym.Segment {
ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr))
ctxt.defineInternal("runtime.pcheader", sym.SRODATA)
ctxt.defineInternal("runtime.funcnametab", sym.SRODATA)
+ ctxt.defineInternal("runtime.cutab", sym.SRODATA)
+ ctxt.defineInternal("runtime.filetab", sym.SRODATA)
ctxt.defineInternal("runtime.pclntab_old", sym.SRODATA)
ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr))
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index a2c8552e940..f26d051a491 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -71,7 +71,6 @@ type Link struct {
LibraryByPkg map[string]*sym.Library
Shlibs []Shlib
Textp []loader.Sym
- NumFilesyms int
Moduledata loader.Sym
PackageFile map[string]string
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 30e0bdc839c..c7535f6a61b 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -6,16 +6,11 @@ package ld
import (
"cmd/internal/goobj"
- "cmd/internal/obj"
"cmd/internal/objabi"
- "cmd/internal/src"
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
- "encoding/binary"
"fmt"
- "log"
- "math"
"os"
"path/filepath"
"strings"
@@ -23,18 +18,13 @@ import (
// oldPclnState holds state information used during pclntab generation. Here
// 'ldr' is just a pointer to the context's loader, 'deferReturnSym' is the
-// index for the symbol "runtime.deferreturn", 'nameToOffset' is a helper
-// function for capturing function names, 'numberedFiles' records the file
-// number assigned to a given file symbol, 'filepaths' is a slice of expanded
-// paths (indexed by file number).
+// index for the symbol "runtime.deferreturn",
//
// NB: This is deprecated, and will be eliminated when pclntab_old is
// eliminated.
type oldPclnState struct {
ldr *loader.Loader
deferReturnSym loader.Sym
- numberedFiles map[string]int64
- filepaths []string
}
// pclntab holds the state needed for pclntab generation.
@@ -42,9 +32,6 @@ type pclntab struct {
// The first and last functions found.
firstFunc, lastFunc loader.Sym
- // The offset to the filetab.
- filetabOffset int32
-
// Running total size of pclntab.
size int64
@@ -54,6 +41,8 @@ type pclntab struct {
pcheader loader.Sym
funcnametab loader.Sym
findfunctab loader.Sym
+ cutab loader.Sym
+ filetab loader.Sym
// The number of functions + number of TEXT sections - 1. This is such an
// unexpected value because platforms that have more than one TEXT section
@@ -64,6 +53,9 @@ type pclntab struct {
// On most platforms this is the number of reachable functions.
nfunc int32
+ // The number of filenames in runtime.filetab.
+ nfiles uint32
+
// maps the function symbol to offset in runtime.funcnametab
// This doesn't need to reside in the state once pclntab_old's been
// deleted -- it can live in generateFuncnametab.
@@ -89,11 +81,6 @@ func makeOldPclnState(ctxt *Link) *oldPclnState {
state := &oldPclnState{
ldr: ldr,
deferReturnSym: drs,
- numberedFiles: make(map[string]int64),
- // NB: initial entry in filepaths below is to reserve the zero value,
- // so that when we do a map lookup in numberedFiles fails, it will not
- // return a value slot in filepaths.
- filepaths: []string{""},
}
return state
@@ -153,78 +140,6 @@ func ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 {
return int32(start)
}
-// numberfile assigns a file number to the file if it hasn't been assigned already.
-// This funciton looks at a CU's file at index [i], and if it's a new filename,
-// stores that filename in the global file table, and adds it to the map lookup
-// for renumbering pcfile.
-func (state *oldPclnState) numberfile(cu *sym.CompilationUnit, i goobj.CUFileIndex) int64 {
- file := cu.FileTable[i]
- if val, ok := state.numberedFiles[file]; ok {
- return val
- }
- path := file
- if strings.HasPrefix(path, src.FileSymPrefix) {
- path = file[len(src.FileSymPrefix):]
- }
- val := int64(len(state.filepaths))
- state.numberedFiles[file] = val
- state.filepaths = append(state.filepaths, expandGoroot(path))
- return val
-}
-
-func (state *oldPclnState) fileVal(cu *sym.CompilationUnit, i int32) int64 {
- file := cu.FileTable[i]
- if val, ok := state.numberedFiles[file]; ok {
- return val
- }
- panic("should have been numbered first")
-}
-
-func (state *oldPclnState) renumberfiles(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, d *sym.Pcdata) {
- // Give files numbers.
- nf := fi.NumFile()
- for i := uint32(0); i < nf; i++ {
- state.numberfile(cu, fi.File(int(i)))
- }
-
- buf := make([]byte, binary.MaxVarintLen32)
- newval := int32(-1)
- var out sym.Pcdata
- it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
- for it.Init(d.P); !it.Done; it.Next() {
- // value delta
- oldval := it.Value
-
- var val int32
- if oldval == -1 {
- val = -1
- } else {
- if oldval < 0 || oldval >= int32(len(cu.FileTable)) {
- log.Fatalf("bad pcdata %d", oldval)
- }
- val = int32(state.fileVal(cu, oldval))
- }
-
- dv := val - newval
- newval = val
-
- // value
- n := binary.PutVarint(buf, int64(dv))
- out.P = append(out.P, buf[:n]...)
-
- // pc delta
- pc := (it.NextPC - it.PC) / it.PCScale
- n = binary.PutUvarint(buf, uint64(pc))
- out.P = append(out.P, buf[:n]...)
- }
-
- // terminating value delta
- // we want to write varint-encoded 0, which is just 0
- out.P = append(out.P, 0)
-
- *d = out
-}
-
// onlycsymbol looks at a symbol's name to report whether this is a
// symbol that is referenced by C code
func onlycsymbol(sname string) bool {
@@ -308,12 +223,7 @@ func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.Func
ninl := fi.NumInlTree()
for i := 0; i < int(ninl); i++ {
call := fi.InlTree(i)
- // Usually, call.File is already numbered since the file
- // shows up in the Pcfile table. However, two inlined calls
- // might overlap exactly so that only the innermost file
- // appears in the Pcfile table. In that case, this assigns
- // the outer file a number.
- val := state.numberfile(cu, call.File)
+ val := call.File
nameoff, ok := newState.funcNameOffset[call.Func]
if !ok {
panic("couldn't find function name offset")
@@ -359,11 +269,14 @@ func (state *pclntab) generatePCHeader(ctxt *Link) {
header.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
header.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
off := header.SetUint(ctxt.Arch, 8, uint64(state.nfunc))
+ off = header.SetUint(ctxt.Arch, off, uint64(state.nfiles))
off = writeSymOffset(off, state.funcnametab)
+ off = writeSymOffset(off, state.cutab)
+ off = writeSymOffset(off, state.filetab)
off = writeSymOffset(off, state.pclntab)
}
- size := int64(8 + 3*ctxt.Arch.PtrSize)
+ size := int64(8 + 6*ctxt.Arch.PtrSize)
state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
}
@@ -417,6 +330,139 @@ func (state *pclntab) generateFuncnametab(ctxt *Link, container loader.Bitmap) {
state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab)
}
+// walkFilenames walks the filenames in the all reachable functions.
+func walkFilenames(ctxt *Link, container loader.Bitmap, f func(*sym.CompilationUnit, goobj.CUFileIndex)) {
+ ldr := ctxt.loader
+
+ // Loop through all functions, finding the filenames we need.
+ for _, ls := range ctxt.Textp {
+ s := loader.Sym(ls)
+ if !emitPcln(ctxt, s, container) {
+ continue
+ }
+
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+
+ cu := ldr.SymUnit(s)
+ for i, nf := 0, int(fi.NumFile()); i < nf; i++ {
+ f(cu, fi.File(i))
+ }
+ for i, ninl := 0, int(fi.NumInlTree()); i < ninl; i++ {
+ call := fi.InlTree(i)
+ f(cu, call.File)
+ }
+ }
+}
+
+// generateFilenameTabs creates LUTs needed for filename lookup. Returns a slice
+// of the index at which each CU begins in runtime.cutab.
+//
+// Function objects keep track of the files they reference to print the stack.
+// This function creates a per-CU list of filenames if CU[M] references
+// files[1-N], the following is generated:
+//
+// runtime.cutab:
+// CU[M]
+// offsetToFilename[0]
+// offsetToFilename[1]
+// ..
+//
+// runtime.filetab
+// filename[0]
+// filename[1]
+//
+// Looking up a filename then becomes:
+// 0) Given a func, and filename index [K]
+// 1) Get Func.CUIndex: M := func.cuOffset
+// 2) Find filename offset: fileOffset := runtime.cutab[M+K]
+// 3) Get the filename: getcstring(runtime.filetab[fileOffset])
+func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, container loader.Bitmap) []uint32 {
+ // On a per-CU basis, keep track of all the filenames we need.
+ //
+ // Note, that we store the filenames in a separate section in the object
+ // files, and deduplicate based on the actual value. It would be better to
+ // store the filenames as symbols, using content addressable symbols (and
+ // then not loading extra filenames), and just use the hash value of the
+ // symbol name to do this cataloging.
+ //
+ // TOOD: Store filenames as symbols. (Note this would be easiest if you
+ // also move strings to ALWAYS using the larger content addressable hash
+ // function, and use that hash value for uniqueness testing.)
+ cuEntries := make([]goobj.CUFileIndex, len(compUnits))
+ fileOffsets := make(map[string]uint32)
+
+ // Walk the filenames.
+ // We store the total filename string length we need to load, and the max
+ // file index we've seen per CU so we can calculate how large the
+ // CU->global table needs to be.
+ var fileSize int64
+ walkFilenames(ctxt, container, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) {
+ // Note we use the raw filename for lookup, but use the expanded filename
+ // when we save the size.
+ filename := cu.FileTable[i]
+ if _, ok := fileOffsets[filename]; !ok {
+ fileOffsets[filename] = uint32(fileSize)
+ fileSize += int64(len(expandFile(filename)) + 1) // NULL terminate
+ }
+
+ // Find the maximum file index we've seen.
+ if cuEntries[cu.PclnIndex] < i+1 {
+ cuEntries[cu.PclnIndex] = i + 1 // Store max + 1
+ }
+ })
+
+ // Calculate the size of the runtime.cutab variable.
+ var totalEntries uint32
+ cuOffsets := make([]uint32, len(cuEntries))
+ for i, entries := range cuEntries {
+ // Note, cutab is a slice of uint32, so an offset to a cu's entry is just the
+ // running total of all cu indices we've needed to store so far, not the
+ // number of bytes we've stored so far.
+ cuOffsets[i] = totalEntries
+ totalEntries += uint32(entries)
+ }
+
+ // Write cutab.
+ writeCutab := func(ctxt *Link, s loader.Sym) {
+ sb := ctxt.loader.MakeSymbolUpdater(s)
+
+ var off int64
+ for i, max := range cuEntries {
+ // Write the per CU LUT.
+ cu := compUnits[i]
+ for j := goobj.CUFileIndex(0); j < max; j++ {
+ fileOffset, ok := fileOffsets[cu.FileTable[j]]
+ if !ok {
+ // We're looping through all possible file indices. It's possible a file's
+ // been deadcode eliminated, and although it's a valid file in the CU, it's
+ // not needed in this binary. When that happens, use an invalid offset.
+ fileOffset = ^uint32(0)
+ }
+ off = sb.SetUint32(ctxt.Arch, off, fileOffset)
+ }
+ }
+ }
+ state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab)
+
+ // Write filetab.
+ writeFiletab := func(ctxt *Link, s loader.Sym) {
+ sb := ctxt.loader.MakeSymbolUpdater(s)
+
+ // Write the strings.
+ for filename, loc := range fileOffsets {
+ sb.AddStringAt(int64(loc), expandFile(filename))
+ }
+ }
+ state.nfiles = uint32(len(fileOffsets))
+ state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab)
+
+ return cuOffsets
+}
+
// pclntab initializes the pclntab symbol with
// runtime function and file name information.
@@ -425,7 +471,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
// Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the
// layout and data has changed since that time.
//
- // As of July 2020, here's the layout of pclntab:
+ // As of August 2020, here's the layout of pclntab:
//
// .gopclntab/__gopclntab [elf/macho section]
// runtime.pclntab
@@ -438,17 +484,23 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
// offset to runtime.pclntab_old from beginning of runtime.pcheader
//
// runtime.funcnametab
- // []list of null terminated function names
+ // []list of null terminated function names
+ //
+ // runtime.cutab
+ // for i=0..#CUs
+ // for j=0..#max used file index in CU[i]
+ // uint32 offset into runtime.filetab for the filename[j]
+ //
+ // runtime.filetab
+ // []null terminated filename strings
//
// runtime.pclntab_old
// function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
// end PC [thearch.ptrsize bytes]
- // offset to file table [4 bytes]
// func structures, pcdata tables.
- // filetable
oldState := makeOldPclnState(ctxt)
- state, _ := makePclntab(ctxt, container)
+ state, compUnits := makePclntab(ctxt, container)
ldr := ctxt.loader
state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
@@ -461,6 +513,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
state.pclntab = ldr.LookupOrCreateSym("runtime.pclntab_old", 0)
state.generatePCHeader(ctxt)
state.generateFuncnametab(ctxt, container)
+ cuOffsets := state.generateFilenameTabs(ctxt, compUnits, container)
funcdataBytes := int64(0)
ldr.SetCarrierSym(state.pclntab, state.carrier)
@@ -583,7 +636,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
// fixed size of struct, checked below
off := funcstart
- end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcdata))*4 + int32(len(funcdata))*int32(ctxt.Arch.PtrSize)
+ end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 6*4 + int32(len(pcdata))*4 + int32(len(funcdata))*int32(ctxt.Arch.PtrSize)
if len(funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) {
end += 4
}
@@ -616,17 +669,6 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
pcsp = sym.Pcdata{P: fi.Pcsp()}
pcfile = sym.Pcdata{P: fi.Pcfile()}
pcline = sym.Pcdata{P: fi.Pcline()}
- oldState.renumberfiles(ctxt, cu, fi, &pcfile)
- if false {
- // Sanity check the new numbering
- it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
- for it.Init(pcfile.P); !it.Done; it.Next() {
- if it.Value < 1 || it.Value > int32(len(oldState.numberedFiles)) {
- ctxt.Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(oldState.numberedFiles))
- errorexit()
- }
- }
- }
}
if fi.Valid() && fi.NumInlTree() > 0 {
@@ -641,15 +683,12 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
off = writepctab(off, pcline.P)
off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcdata))))
- // Store the compilation unit index.
- cuIdx := ^uint16(0)
+ // Store the offset to compilation unit's file table.
+ cuIdx := ^uint32(0)
if cu := ldr.SymUnit(s); cu != nil {
- if cu.PclnIndex > math.MaxUint16 {
- panic("cu limit reached.")
- }
- cuIdx = uint16(cu.PclnIndex)
+ cuIdx = cuOffsets[cu.PclnIndex]
}
- off = int32(ftab.SetUint16(ctxt.Arch, int64(off), cuIdx))
+ off = int32(ftab.SetUint32(ctxt.Arch, int64(off), cuIdx))
// funcID uint8
var funcID objabi.FuncID
@@ -658,6 +697,8 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
}
off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
+ off += 2 // pad
+
// nfuncdata must be the final entry.
off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata))))
for i := range pcdata {
@@ -694,26 +735,8 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
// Final entry of table is just end pc.
setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), state.lastFunc, ldr.SymSize(state.lastFunc))
- // Start file table.
- dSize := len(ftab.Data())
- start := int32(dSize)
- start += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1)
- state.filetabOffset = start
- ftab.SetUint32(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start))
-
- nf := len(oldState.numberedFiles)
- ftab.Grow(int64(start) + int64((nf+1)*4))
- ftab.SetUint32(ctxt.Arch, int64(start), uint32(nf+1))
- for i := nf; i > 0; i-- {
- path := oldState.filepaths[i]
- val := int64(i)
- ftab.SetUint32(ctxt.Arch, int64(start)+val*4, uint32(ftabaddstring(ftab, path)))
- }
-
ftab.SetSize(int64(len(ftab.Data())))
- ctxt.NumFilesyms = len(oldState.numberedFiles)
-
if ctxt.Debugvlog != 0 {
ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size(), funcdataBytes)
}
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index bc880955b8c..d05b98f04a6 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -619,6 +619,14 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
moduledata.AddAddr(ctxt.Arch, pcln.funcnametab)
moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab)))
moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab)))
+ // The cutab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.cutab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab)))
+ // The filetab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.filetab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab)))
// The pclntab slice
moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab)))
@@ -627,10 +635,6 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
- // The filetab slice
- moduledata.AddAddrPlus(ctxt.Arch, pcln.pclntab, int64(pcln.filetabOffset))
- moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
- moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
// findfunctab
moduledata.AddAddr(ctxt.Arch, pcln.findfunctab)
// minpc, maxpc
diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go
index e5c50520fca..e383ea460a4 100644
--- a/src/debug/gosym/pclntab.go
+++ b/src/debug/gosym/pclntab.go
@@ -53,6 +53,7 @@ type LineTable struct {
quantum uint32
ptrsize uint32
funcnametab []byte
+ cutab []byte
funcdata []byte
functab []byte
nfunctab uint32
@@ -223,17 +224,18 @@ func (t *LineTable) parsePclnTab() {
switch possibleVersion {
case ver116:
t.nfunctab = uint32(t.uintptr(t.Data[8:]))
- offset := t.uintptr(t.Data[8+t.ptrsize:])
+ t.nfiletab = uint32(t.uintptr(t.Data[8+t.ptrsize:]))
+ offset := t.uintptr(t.Data[8+2*t.ptrsize:])
t.funcnametab = t.Data[offset:]
- offset = t.uintptr(t.Data[8+2*t.ptrsize:])
+ offset = t.uintptr(t.Data[8+3*t.ptrsize:])
+ t.cutab = t.Data[offset:]
+ offset = t.uintptr(t.Data[8+4*t.ptrsize:])
+ t.filetab = t.Data[offset:]
+ offset = t.uintptr(t.Data[8+5*t.ptrsize:])
t.funcdata = t.Data[offset:]
t.functab = t.Data[offset:]
functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
- fileoff := t.binary.Uint32(t.functab[functabsize:])
- t.filetab = t.functab[fileoff:]
t.functab = t.functab[:functabsize]
- t.nfiletab = t.binary.Uint32(t.filetab)
- t.filetab = t.filetab[:t.nfiletab*4]
case ver12:
t.nfunctab = uint32(t.uintptr(t.Data[8:]))
t.funcdata = t.Data
@@ -330,17 +332,22 @@ func (t *LineTable) funcName(off uint32) string {
return s
}
-// string returns a Go string found at off.
-func (t *LineTable) string(off uint32) string {
+// stringFrom returns a Go string found at off from a position.
+func (t *LineTable) stringFrom(arr []byte, off uint32) string {
if s, ok := t.strings[off]; ok {
return s
}
- i := bytes.IndexByte(t.funcdata[off:], 0)
- s := string(t.funcdata[off : off+uint32(i)])
+ i := bytes.IndexByte(arr[off:], 0)
+ s := string(arr[off : off+uint32(i)])
t.strings[off] = s
return s
}
+// string returns a Go string found at off.
+func (t *LineTable) string(off uint32) string {
+ return t.stringFrom(t.funcdata, off)
+}
+
// step advances to the next pc, value pair in the encoded table.
func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
uvdelta := t.readvarint(p)
@@ -453,7 +460,15 @@ func (t *LineTable) go12PCToFile(pc uint64) (file string) {
if fno <= 0 {
return ""
}
- return t.string(t.binary.Uint32(t.filetab[4*fno:]))
+ if t.version == ver12 {
+ return t.string(t.binary.Uint32(t.filetab[4*fno:]))
+ }
+ // Go ≥ 1.16
+ cuoff := t.binary.Uint32(f[t.ptrsize+7*4:])
+ if fnoff := t.binary.Uint32(t.cutab[(cuoff+uint32(fno))*4:]); fnoff != ^uint32(0) {
+ return t.stringFrom(t.filetab, fnoff)
+ }
+ return ""
}
// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table.
@@ -496,9 +511,18 @@ func (t *LineTable) initFileMap() {
}
m := make(map[string]uint32)
- for i := uint32(1); i < t.nfiletab; i++ {
- s := t.string(t.binary.Uint32(t.filetab[4*i:]))
- m[s] = i
+ if t.version == ver12 {
+ for i := uint32(1); i < t.nfiletab; i++ {
+ s := t.string(t.binary.Uint32(t.filetab[4*i:]))
+ m[s] = i
+ }
+ } else {
+ var pos uint32
+ for i := uint32(1); i < t.nfiletab; i++ {
+ s := t.stringFrom(t.filetab, pos)
+ pos += uint32(len(s) + 1)
+ m[s] = i
+ }
}
t.fileMap = m
}
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index 0bddcaa789b..5a79c7e6ec7 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -804,9 +804,10 @@ type _func struct {
pcfile int32
pcln int32
npcdata int32
- cuIndex uint16 // TODO(jfaller): 16 bits is never enough, make this larger.
- funcID funcID // set for certain special runtime functions
- nfuncdata uint8 // must be last
+ cuOffset uint32 // runtime.cutab offset of this function's CU
+ funcID funcID // set for certain special runtime functions
+ _ [2]byte // pad
+ nfuncdata uint8 // must be last
}
// Pseudo-Func that is returned for PCs that occur in inlined code.
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index ddb5ea82b46..fbd93155223 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -334,14 +334,17 @@ const (
funcID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
)
-// PCHeader holds data used by the pclntab lookups.
+// pcHeader holds data used by the pclntab lookups.
type pcHeader struct {
magic uint32 // 0xFFFFFFFA
pad1, pad2 uint8 // 0,0
minLC uint8 // min instruction size
ptrSize uint8 // size of a ptr in bytes
nfunc int // number of functions in the module
+ nfiles uint // number of entries in the file tab.
funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
+ cuOffset uintptr // offset to the cutab variable from pcHeader
+ filetabOffset uintptr // offset to the filetab variable from pcHeader
pclnOffset uintptr // offset to the pclntab variable from pcHeader
}
@@ -353,9 +356,10 @@ type pcHeader struct {
type moduledata struct {
pcHeader *pcHeader
funcnametab []byte
+ cutab []uint32
+ filetab []byte
pclntable []byte
ftab []functab
- filetab []uint32
findfunctab uintptr
minpc, maxpc uintptr
@@ -851,7 +855,12 @@ func funcfile(f funcInfo, fileno int32) string {
if !f.valid() {
return "?"
}
- return gostringnocopy(&datap.pclntable[datap.filetab[fileno]])
+ // Make sure the cu index and file offset are valid
+ if fileoff := datap.cutab[f.cuOffset+uint32(fileno)]; fileoff != ^uint32(0) {
+ return gostringnocopy(&datap.filetab[fileoff])
+ }
+ // pcln section is corrupt.
+ return "?"
}
func funcline1(f funcInfo, targetpc uintptr, strict bool) (file string, line int32) {
@@ -865,7 +874,7 @@ func funcline1(f funcInfo, targetpc uintptr, strict bool) (file string, line int
// print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n")
return "?", 0
}
- file = gostringnocopy(&datap.pclntable[datap.filetab[fileno]])
+ file = funcfile(f, fileno)
return
}
@@ -1005,7 +1014,7 @@ type inlinedCall struct {
parent int16 // index of parent in the inltree, or < 0
funcID funcID // type of the called function
_ byte
- file int32 // fileno index into filetab
+ file int32 // perCU file index for inlined call. See cmd/link:pcln.go
line int32 // line number of the call site
func_ int32 // offset into pclntab for name of called function
parentPc int32 // position of an instruction whose source position is the call site (offset from entry)
From b249703e3c53cd7f1e5f808fb2f03714fec44b43 Mon Sep 17 00:00:00 2001
From: Jeremy Faller
Date: Wed, 12 Aug 2020 12:54:03 -0400
Subject: [PATCH 0002/1082] [dev.link] cmd/compile, cmd/asm: add length to
hashed symbols
While working on deduplicating pcdata, I found that the following hashed
symbols would result in the same:
[] == [0,0,0,0....]
This makes using content addressable symbols untenable for pcdata.
Adding the length to the hash keeps the dream alive.
No difference in binary size (darwin, cmd/compile), spurious
improvements in DWARF phase memory.
Change-Id: I21101f7754a3d870922b0dea39c947cc8509432f
Reviewed-on: https://go-review.googlesource.com/c/go/+/247903
Run-TryBot: Jeremy Faller
TryBot-Result: Gobot Gobot
Reviewed-by: Than McIntosh
Reviewed-by: Austin Clements
Reviewed-by: Cherry Zhang
---
src/cmd/internal/obj/objfile.go | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index 7bc4f4992e9..8234697d729 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -372,10 +372,22 @@ func contentHash64(s *LSym) goobj.Hash64Type {
// hashed symbols.
func (w *writer) contentHash(s *LSym) goobj.HashType {
h := sha1.New()
+ var tmp [14]byte
+
+ // Include the size of the symbol in the hash.
+ // This preserves the length of symbols, preventing the following two symbols
+ // from hashing the same:
+ //
+ // [2]int{1,2} ≠ [10]int{1,2,0,0,0...}
+ //
+ // In this case, if the smaller symbol is alive, the larger is not kept unless
+ // needed.
+ binary.LittleEndian.PutUint64(tmp[:8], uint64(s.Size))
+ h.Write(tmp[:8])
+
// The compiler trims trailing zeros _sometimes_. We just do
// it always.
h.Write(bytes.TrimRight(s.P, "\x00"))
- var tmp [14]byte
for i := range s.R {
r := &s.R[i]
binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off))
From 954db9fe51154e5d4663c0c1a62c82a99eef1ed4 Mon Sep 17 00:00:00 2001
From: Jeremy Faller
Date: Wed, 12 Aug 2020 16:37:42 -0400
Subject: [PATCH 0003/1082] [dev.link] debug/gosym: fix file mappings
CL 246497 introduced bugs in gosym that the long tests caught. These two
bugs were:
1) In 1.16, 0 is now a valid file number from pcfile tables.
2) Also, in 1.16, when we scan all functions looking for a pc/file pair,
the values returned from pcfile are no longer the direct offset into
the file table. Rather, the values from pcfile are the offset into
the cu->file look-up table.
This CL fixes those two issues.
Change-Id: I0cd280bdcaeda89faaf9fac41809abdb87734499
Reviewed-on: https://go-review.googlesource.com/c/go/+/248317
Reviewed-by: Cherry Zhang
Reviewed-by: Austin Clements
Reviewed-by: Than McIntosh
---
src/debug/gosym/pclntab.go | 41 ++++++++++++++++++++++++++------------
1 file changed, 28 insertions(+), 13 deletions(-)
diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go
index e383ea460a4..21edddda204 100644
--- a/src/debug/gosym/pclntab.go
+++ b/src/debug/gosym/pclntab.go
@@ -59,9 +59,12 @@ type LineTable struct {
nfunctab uint32
filetab []byte
nfiletab uint32
- fileMap map[string]uint32
funcNames map[uint32]string // cache the function names
strings map[uint32]string // interned substrings of Data, keyed by offset
+ // fileMap varies depending on the version of the object file.
+ // For ver12, it maps the name to the index in the file table.
+ // For ver116, it maps the name to the offset in filetab.
+ fileMap map[string]uint32
}
// NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4,
@@ -388,7 +391,7 @@ func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
// to file number. Since most functions come from a single file, these
// are usually short and quick to scan. If a file match is found, then the
// code goes to the expense of looking for a simultaneous line number match.
-func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 {
+func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32, cutab []byte) uint64 {
if filetab == 0 || linetab == 0 {
return 0
}
@@ -401,8 +404,12 @@ func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum,
linePC := entry
fileStartPC := filePC
for t.step(&fp, &filePC, &fileVal, filePC == entry) {
- if fileVal == filenum && fileStartPC < filePC {
- // fileVal is in effect starting at fileStartPC up to
+ fileIndex := fileVal
+ if t.version == ver116 {
+ fileIndex = int32(t.binary.Uint32(cutab[fileVal*4:]))
+ }
+ if fileIndex == filenum && fileStartPC < filePC {
+ // fileIndex is in effect starting at fileStartPC up to
// but not including filePC, and it's the file we want.
// Run the PC table looking for a matching line number
// or until we reach filePC.
@@ -457,13 +464,16 @@ func (t *LineTable) go12PCToFile(pc uint64) (file string) {
entry := t.uintptr(f)
filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
fno := t.pcvalue(filetab, entry, pc)
- if fno <= 0 {
- return ""
- }
if t.version == ver12 {
+ if fno <= 0 {
+ return ""
+ }
return t.string(t.binary.Uint32(t.filetab[4*fno:]))
}
// Go ≥ 1.16
+ if fno < 0 { // 0 is valid for ≥ 1.16
+ return ""
+ }
cuoff := t.binary.Uint32(f[t.ptrsize+7*4:])
if fnoff := t.binary.Uint32(t.cutab[(cuoff+uint32(fno))*4:]); fnoff != ^uint32(0) {
return t.stringFrom(t.filetab, fnoff)
@@ -471,7 +481,7 @@ func (t *LineTable) go12PCToFile(pc uint64) (file string) {
return ""
}
-// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table.
+// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2/1.16 pcln table.
func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
defer func() {
if recover() != nil {
@@ -480,20 +490,25 @@ func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
}()
t.initFileMap()
- filenum := t.fileMap[file]
- if filenum == 0 {
+ filenum, ok := t.fileMap[file]
+ if !ok {
return 0
}
// Scan all functions.
// If this turns out to be a bottleneck, we could build a map[int32][]int32
// mapping file number to a list of functions with code from that file.
+ var cutab []byte
for i := uint32(0); i < t.nfunctab; i++ {
f := t.funcdata[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):]
entry := t.uintptr(f)
filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
linetab := t.binary.Uint32(f[t.ptrsize+5*4:])
- pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line))
+ if t.version == ver116 {
+ cuoff := t.binary.Uint32(f[t.ptrsize+7*4:]) * 4
+ cutab = t.cutab[cuoff:]
+ }
+ pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line), cutab)
if pc != 0 {
return pc
}
@@ -518,10 +533,10 @@ func (t *LineTable) initFileMap() {
}
} else {
var pos uint32
- for i := uint32(1); i < t.nfiletab; i++ {
+ for i := uint32(0); i < t.nfiletab; i++ {
s := t.stringFrom(t.filetab, pos)
+ m[s] = pos
pos += uint32(len(s) + 1)
- m[s] = i
}
}
t.fileMap = m
From 5387cdcb24a07f5d0d49d5105ced2b69e6aafde9 Mon Sep 17 00:00:00 2001
From: Jeremy Faller
Date: Fri, 7 Aug 2020 11:31:20 -0400
Subject: [PATCH 0004/1082] [dev.link] cmd/link, cmd/compile: create content
addressable pcdata syms
Switch pcdata over to content addressable symbols. This is the last
step before removing these from pclntab_old.
No meaningful benchmarks changes come from this work.
Change-Id: I3f74f3d6026a278babe437c8010e22992c92bd89
Reviewed-on: https://go-review.googlesource.com/c/go/+/247399
Reviewed-by: Austin Clements
Reviewed-by: Than McIntosh
---
src/cmd/internal/goobj/funcinfo.go | 82 ++++++++++++++------------
src/cmd/internal/goobj/objfile.go | 12 ++--
src/cmd/internal/obj/link.go | 15 ++---
src/cmd/internal/obj/objfile.go | 69 +++++++++++++++++-----
src/cmd/internal/obj/pcln.go | 55 ++++++++++-------
src/cmd/internal/objfile/goobj.go | 16 +++--
src/cmd/link/internal/ld/dwarf.go | 2 +-
src/cmd/link/internal/ld/lib.go | 2 +-
src/cmd/link/internal/ld/pcln.go | 13 ++--
src/cmd/link/internal/loader/loader.go | 46 +++++++--------
10 files changed, 181 insertions(+), 131 deletions(-)
diff --git a/src/cmd/internal/goobj/funcinfo.go b/src/cmd/internal/goobj/funcinfo.go
index e0e6068b4b2..2cca8f6c4ef 100644
--- a/src/cmd/internal/goobj/funcinfo.go
+++ b/src/cmd/internal/goobj/funcinfo.go
@@ -23,12 +23,11 @@ type FuncInfo struct {
Locals uint32
FuncID objabi.FuncID
- Pcsp uint32
- Pcfile uint32
- Pcline uint32
- Pcinline uint32
- Pcdata []uint32
- PcdataEnd uint32
+ Pcsp SymRef
+ Pcfile SymRef
+ Pcline SymRef
+ Pcinline SymRef
+ Pcdata []SymRef
Funcdataoff []uint32
File []CUFileIndex
@@ -41,20 +40,24 @@ func (a *FuncInfo) Write(w *bytes.Buffer) {
binary.LittleEndian.PutUint32(b[:], x)
w.Write(b[:])
}
+ writeSymRef := func(s SymRef) {
+ writeUint32(s.PkgIdx)
+ writeUint32(s.SymIdx)
+ }
writeUint32(a.Args)
writeUint32(a.Locals)
writeUint32(uint32(a.FuncID))
- writeUint32(a.Pcsp)
- writeUint32(a.Pcfile)
- writeUint32(a.Pcline)
- writeUint32(a.Pcinline)
+ writeSymRef(a.Pcsp)
+ writeSymRef(a.Pcfile)
+ writeSymRef(a.Pcline)
+ writeSymRef(a.Pcinline)
writeUint32(uint32(len(a.Pcdata)))
- for _, x := range a.Pcdata {
- writeUint32(x)
+ for _, sym := range a.Pcdata {
+ writeSymRef(sym)
}
- writeUint32(a.PcdataEnd)
+
writeUint32(uint32(len(a.Funcdataoff)))
for _, x := range a.Funcdataoff {
writeUint32(x)
@@ -75,21 +78,23 @@ func (a *FuncInfo) Read(b []byte) {
b = b[4:]
return x
}
+ readSymIdx := func() SymRef {
+ return SymRef{readUint32(), readUint32()}
+ }
a.Args = readUint32()
a.Locals = readUint32()
a.FuncID = objabi.FuncID(readUint32())
- a.Pcsp = readUint32()
- a.Pcfile = readUint32()
- a.Pcline = readUint32()
- a.Pcinline = readUint32()
- pcdatalen := readUint32()
- a.Pcdata = make([]uint32, pcdatalen)
+ a.Pcsp = readSymIdx()
+ a.Pcfile = readSymIdx()
+ a.Pcline = readSymIdx()
+ a.Pcinline = readSymIdx()
+ a.Pcdata = make([]SymRef, readUint32())
for i := range a.Pcdata {
- a.Pcdata[i] = readUint32()
+ a.Pcdata[i] = readSymIdx()
}
- a.PcdataEnd = readUint32()
+
funcdataofflen := readUint32()
a.Funcdataoff = make([]uint32, funcdataofflen)
for i := range a.Funcdataoff {
@@ -127,11 +132,13 @@ type FuncInfoLengths struct {
func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths {
var result FuncInfoLengths
- const numpcdataOff = 28
+ // Offset to the number of pcdata values. This value is determined by counting
+ // the number of bytes until we write pcdata to the file.
+ const numpcdataOff = 44
result.NumPcdata = binary.LittleEndian.Uint32(b[numpcdataOff:])
result.PcdataOff = numpcdataOff + 4
- numfuncdataoffOff := result.PcdataOff + 4*(result.NumPcdata+1)
+ numfuncdataoffOff := result.PcdataOff + 8*result.NumPcdata
result.NumFuncdataoff = binary.LittleEndian.Uint32(b[numfuncdataoffOff:])
result.FuncdataoffOff = numfuncdataoffOff + 4
@@ -154,29 +161,28 @@ func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32
func (*FuncInfo) ReadFuncID(b []byte) uint32 { return binary.LittleEndian.Uint32(b[8:]) }
-// return start and end offsets.
-func (*FuncInfo) ReadPcsp(b []byte) (uint32, uint32) {
- return binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])
+func (*FuncInfo) ReadPcsp(b []byte) SymRef {
+ return SymRef{binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])}
}
-// return start and end offsets.
-func (*FuncInfo) ReadPcfile(b []byte) (uint32, uint32) {
- return binary.LittleEndian.Uint32(b[16:]), binary.LittleEndian.Uint32(b[20:])
+func (*FuncInfo) ReadPcfile(b []byte) SymRef {
+ return SymRef{binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[24:])}
}
-// return start and end offsets.
-func (*FuncInfo) ReadPcline(b []byte) (uint32, uint32) {
- return binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[24:])
+func (*FuncInfo) ReadPcline(b []byte) SymRef {
+ return SymRef{binary.LittleEndian.Uint32(b[28:]), binary.LittleEndian.Uint32(b[32:])}
}
-// return start and end offsets.
-func (*FuncInfo) ReadPcinline(b []byte, pcdataoffset uint32) (uint32, uint32) {
- return binary.LittleEndian.Uint32(b[24:]), binary.LittleEndian.Uint32(b[pcdataoffset:])
+func (*FuncInfo) ReadPcinline(b []byte) SymRef {
+ return SymRef{binary.LittleEndian.Uint32(b[36:]), binary.LittleEndian.Uint32(b[40:])}
}
-// return start and end offsets.
-func (*FuncInfo) ReadPcdata(b []byte, pcdataoffset uint32, k uint32) (uint32, uint32) {
- return binary.LittleEndian.Uint32(b[pcdataoffset+4*k:]), binary.LittleEndian.Uint32(b[pcdataoffset+4+4*k:])
+func (*FuncInfo) ReadPcdata(b []byte) []SymRef {
+ syms := make([]SymRef, binary.LittleEndian.Uint32(b[44:]))
+ for i := range syms {
+ syms[i] = SymRef{binary.LittleEndian.Uint32(b[48+i*8:]), binary.LittleEndian.Uint32(b[52+i*8:])}
+ }
+ return syms
}
func (*FuncInfo) ReadFuncdataoff(b []byte, funcdataofffoff uint32, k uint32) int64 {
diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go
index 5d4a2530245..9a64f96cd6e 100644
--- a/src/cmd/internal/goobj/objfile.go
+++ b/src/cmd/internal/goobj/objfile.go
@@ -421,8 +421,11 @@ const (
AuxDwarfLoc
AuxDwarfRanges
AuxDwarfLines
-
- // TODO: more. Pcdata?
+ AuxPcsp
+ AuxPcfile
+ AuxPcline
+ AuxPcinline
+ AuxPcdata
)
func (a *Aux) Type() uint8 { return a[0] }
@@ -827,11 +830,6 @@ func (r *Reader) Data(i uint32) []byte {
return r.BytesAt(base+off, int(end-off))
}
-// AuxDataBase returns the base offset of the aux data block.
-func (r *Reader) PcdataBase() uint32 {
- return r.h.Offsets[BlkPcdata]
-}
-
// NRefName returns the number of referenced symbol names.
func (r *Reader) NRefName() int {
return int(r.h.Offsets[BlkRefName+1]-r.h.Offsets[BlkRefName]) / RefNameSize
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index dc47e51be91..11fab63065a 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -624,11 +624,12 @@ func (s *LSym) CanBeAnSSASym() {
}
type Pcln struct {
- Pcsp Pcdata
- Pcfile Pcdata
- Pcline Pcdata
- Pcinline Pcdata
- Pcdata []Pcdata
+ // Aux symbols for pcln
+ Pcsp *LSym
+ Pcfile *LSym
+ Pcline *LSym
+ Pcinline *LSym
+ Pcdata []*LSym
Funcdata []*LSym
Funcdataoff []int64
UsedFiles map[goobj.CUFileIndex]struct{} // file indices used while generating pcfile
@@ -650,10 +651,6 @@ type Auto struct {
Gotype *LSym
}
-type Pcdata struct {
- P []byte
-}
-
// Link holds the context for writing object code from a compiler
// to be linker input or for reading that input into the linker.
type Link struct {
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index 8234697d729..a2bbdff24e0 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -185,7 +185,11 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) {
// Pcdata
h.Offsets[goobj.BlkPcdata] = w.Offset()
for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms
- if s.Func != nil {
+ // Because of the phase order, it's possible that we try to write an invalid
+ // object file, and the Pcln variables haven't been filled in. As such, we
+ // need to check that Pcsp exists, and assume the other pcln variables exist
+ // as well. Tests like test/fixedbugs/issue22200.go demonstrate this issue.
+ if s.Func != nil && s.Func.Pcln.Pcsp != nil {
pc := &s.Func.Pcln
w.Bytes(pc.Pcsp.P)
w.Bytes(pc.Pcfile.P)
@@ -478,6 +482,22 @@ func (w *writer) Aux(s *LSym) {
if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
w.aux1(goobj.AuxDwarfLines, s.Func.dwarfDebugLinesSym)
}
+ if s.Func.Pcln.Pcsp != nil && s.Func.Pcln.Pcsp.Size != 0 {
+ w.aux1(goobj.AuxPcsp, s.Func.Pcln.Pcsp)
+ }
+ if s.Func.Pcln.Pcfile != nil && s.Func.Pcln.Pcfile.Size != 0 {
+ w.aux1(goobj.AuxPcfile, s.Func.Pcln.Pcfile)
+ }
+ if s.Func.Pcln.Pcline != nil && s.Func.Pcln.Pcline.Size != 0 {
+ w.aux1(goobj.AuxPcline, s.Func.Pcln.Pcline)
+ }
+ if s.Func.Pcln.Pcinline != nil && s.Func.Pcln.Pcinline.Size != 0 {
+ w.aux1(goobj.AuxPcinline, s.Func.Pcln.Pcinline)
+ }
+ for _, pcSym := range s.Func.Pcln.Pcdata {
+ w.aux1(goobj.AuxPcdata, pcSym)
+ }
+
}
}
@@ -559,6 +579,19 @@ func nAuxSym(s *LSym) int {
if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
n++
}
+ if s.Func.Pcln.Pcsp != nil && s.Func.Pcln.Pcsp.Size != 0 {
+ n++
+ }
+ if s.Func.Pcln.Pcfile != nil && s.Func.Pcln.Pcfile.Size != 0 {
+ n++
+ }
+ if s.Func.Pcln.Pcline != nil && s.Func.Pcln.Pcline.Size != 0 {
+ n++
+ }
+ if s.Func.Pcln.Pcinline != nil && s.Func.Pcln.Pcinline.Size != 0 {
+ n++
+ }
+ n += len(s.Func.Pcln.Pcdata)
}
return n
}
@@ -566,7 +599,17 @@ func nAuxSym(s *LSym) int {
// generate symbols for FuncInfo.
func genFuncInfoSyms(ctxt *Link) {
infosyms := make([]*LSym, 0, len(ctxt.Text))
- var pcdataoff uint32
+ hashedsyms := make([]*LSym, 0, 4*len(ctxt.Text))
+ preparePcSym := func(s *LSym) *LSym {
+ if s == nil {
+ return s
+ }
+ s.PkgIdx = goobj.PkgIdxHashed
+ s.SymIdx = int32(len(hashedsyms) + len(ctxt.hasheddefs))
+ s.Set(AttrIndexed, true)
+ hashedsyms = append(hashedsyms, s)
+ return s
+ }
var b bytes.Buffer
symidx := int32(len(ctxt.defs))
for _, s := range ctxt.Text {
@@ -579,20 +622,14 @@ func genFuncInfoSyms(ctxt *Link) {
FuncID: objabi.FuncID(s.Func.FuncID),
}
pc := &s.Func.Pcln
- o.Pcsp = pcdataoff
- pcdataoff += uint32(len(pc.Pcsp.P))
- o.Pcfile = pcdataoff
- pcdataoff += uint32(len(pc.Pcfile.P))
- o.Pcline = pcdataoff
- pcdataoff += uint32(len(pc.Pcline.P))
- o.Pcinline = pcdataoff
- pcdataoff += uint32(len(pc.Pcinline.P))
- o.Pcdata = make([]uint32, len(pc.Pcdata))
- for i, pcd := range pc.Pcdata {
- o.Pcdata[i] = pcdataoff
- pcdataoff += uint32(len(pcd.P))
+ o.Pcsp = makeSymRef(preparePcSym(pc.Pcsp))
+ o.Pcfile = makeSymRef(preparePcSym(pc.Pcfile))
+ o.Pcline = makeSymRef(preparePcSym(pc.Pcline))
+ o.Pcinline = makeSymRef(preparePcSym(pc.Pcinline))
+ o.Pcdata = make([]goobj.SymRef, len(pc.Pcdata))
+ for i, pcSym := range pc.Pcdata {
+ o.Pcdata[i] = makeSymRef(preparePcSym(pcSym))
}
- o.PcdataEnd = pcdataoff
o.Funcdataoff = make([]uint32, len(pc.Funcdataoff))
for i, x := range pc.Funcdataoff {
o.Funcdataoff[i] = uint32(x)
@@ -642,9 +679,9 @@ func genFuncInfoSyms(ctxt *Link) {
}
}
ctxt.defs = append(ctxt.defs, infosyms...)
+ ctxt.hasheddefs = append(ctxt.hasheddefs, hashedsyms...)
}
-// debugDumpAux is a dumper for selected aux symbols.
func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) {
// Most aux symbols (ex: funcdata) are not interesting--
// pick out just the DWARF ones for now.
diff --git a/src/cmd/internal/obj/pcln.go b/src/cmd/internal/obj/pcln.go
index 1f7ccf47eff..77506377964 100644
--- a/src/cmd/internal/obj/pcln.go
+++ b/src/cmd/internal/obj/pcln.go
@@ -6,6 +6,7 @@ package obj
import (
"cmd/internal/goobj"
+ "cmd/internal/objabi"
"encoding/binary"
"log"
)
@@ -14,16 +15,19 @@ import (
// returned by valfunc parameterized by arg. The invocation of valfunc to update the
// current value is, for each p,
//
-// val = valfunc(func, val, p, 0, arg);
-// record val as value at p->pc;
-// val = valfunc(func, val, p, 1, arg);
+// sym = valfunc(func, p, 0, arg);
+// record sym.P as value at p->pc;
+// sym = valfunc(func, p, 1, arg);
//
// where func is the function, val is the current value, p is the instruction being
// considered, and arg can be used to further parameterize valfunc.
-func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*Link, *LSym, int32, *Prog, int32, interface{}) int32, arg interface{}) {
+func funcpctab(ctxt *Link, func_ *LSym, desc string, valfunc func(*Link, *LSym, int32, *Prog, int32, interface{}) int32, arg interface{}) *LSym {
dbg := desc == ctxt.Debugpcln
-
- dst.P = dst.P[:0]
+ dst := []byte{}
+ sym := &LSym{
+ Type: objabi.SRODATA,
+ Attribute: AttrContentAddressable,
+ }
if dbg {
ctxt.Logf("funcpctab %s [valfunc=%s]\n", func_.Name, desc)
@@ -32,7 +36,8 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
val := int32(-1)
oldval := val
if func_.Func.Text == nil {
- return
+ // Return the emtpy symbol we've built so far.
+ return sym
}
pc := func_.Func.Text.Pc
@@ -88,13 +93,13 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
if started {
pcdelta := (p.Pc - pc) / int64(ctxt.Arch.MinLC)
n := binary.PutUvarint(buf, uint64(pcdelta))
- dst.P = append(dst.P, buf[:n]...)
+ dst = append(dst, buf[:n]...)
pc = p.Pc
}
delta := val - oldval
n := binary.PutVarint(buf, int64(delta))
- dst.P = append(dst.P, buf[:n]...)
+ dst = append(dst, buf[:n]...)
oldval = val
started = true
val = valfunc(ctxt, func_, val, p, 1, arg)
@@ -109,18 +114,22 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
ctxt.Diag("negative pc offset: %v", v)
}
n := binary.PutUvarint(buf, uint64(v))
- dst.P = append(dst.P, buf[:n]...)
+ dst = append(dst, buf[:n]...)
// add terminating varint-encoded 0, which is just 0
- dst.P = append(dst.P, 0)
+ dst = append(dst, 0)
}
if dbg {
- ctxt.Logf("wrote %d bytes to %p\n", len(dst.P), dst)
- for _, p := range dst.P {
+ ctxt.Logf("wrote %d bytes to %p\n", len(dst), dst)
+ for _, p := range dst {
ctxt.Logf(" %02x", p)
}
ctxt.Logf("\n")
}
+
+ sym.Size = int64(len(dst))
+ sym.P = dst
+ return sym
}
// pctofileline computes either the file number (arg == 0)
@@ -268,18 +277,17 @@ func linkpcln(ctxt *Link, cursym *LSym) {
}
}
- pcln.Pcdata = make([]Pcdata, npcdata)
- pcln.Pcdata = pcln.Pcdata[:npcdata]
+ pcln.Pcdata = make([]*LSym, npcdata)
pcln.Funcdata = make([]*LSym, nfuncdata)
pcln.Funcdataoff = make([]int64, nfuncdata)
pcln.Funcdataoff = pcln.Funcdataoff[:nfuncdata]
- funcpctab(ctxt, &pcln.Pcsp, cursym, "pctospadj", pctospadj, nil)
- funcpctab(ctxt, &pcln.Pcfile, cursym, "pctofile", pctofileline, pcln)
- funcpctab(ctxt, &pcln.Pcline, cursym, "pctoline", pctofileline, nil)
+ pcln.Pcsp = funcpctab(ctxt, cursym, "pctospadj", pctospadj, nil)
+ pcln.Pcfile = funcpctab(ctxt, cursym, "pctofile", pctofileline, pcln)
+ pcln.Pcline = funcpctab(ctxt, cursym, "pctoline", pctofileline, nil)
pcinlineState := new(pcinlineState)
- funcpctab(ctxt, &pcln.Pcinline, cursym, "pctoinline", pcinlineState.pctoinline, nil)
+ pcln.Pcinline = funcpctab(ctxt, cursym, "pctoinline", pcinlineState.pctoinline, nil)
for _, inlMark := range cursym.Func.InlMarks {
pcinlineState.setParentPC(ctxt, int(inlMark.id), int32(inlMark.p.Pc))
}
@@ -309,9 +317,14 @@ func linkpcln(ctxt *Link, cursym *LSym) {
// pcdata.
for i := 0; i < npcdata; i++ {
if (havepc[i/32]>>uint(i%32))&1 == 0 {
- continue
+ // use an empty symbol.
+ pcln.Pcdata[i] = &LSym{
+ Type: objabi.SRODATA,
+ Attribute: AttrContentAddressable,
+ }
+ } else {
+ pcln.Pcdata[i] = funcpctab(ctxt, cursym, "pctopcdata", pctopcdata, interface{}(uint32(i)))
}
- funcpctab(ctxt, &pcln.Pcdata[i], cursym, "pctopcdata", pctopcdata, interface{}(uint32(i)))
}
// funcdata
diff --git a/src/cmd/internal/objfile/goobj.go b/src/cmd/internal/objfile/goobj.go
index e838f58aed8..8eecebb1df7 100644
--- a/src/cmd/internal/objfile/goobj.go
+++ b/src/cmd/internal/objfile/goobj.go
@@ -236,7 +236,15 @@ func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
if arch == nil {
return "", 0, nil
}
- pcdataBase := r.PcdataBase()
+ getSymData := func(s goobj.SymRef) []byte {
+ if s.PkgIdx != goobj.PkgIdxHashed {
+ // We don't need the data for non-hashed symbols, yet.
+ panic("not supported")
+ }
+ i := uint32(s.SymIdx + uint32(r.NSym()+r.NHashed64def()))
+ return r.BytesAt(r.DataOff(i), r.DataSize(i))
+ }
+
ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef())
for i := uint32(0); i < ndef; i++ {
osym := r.Sym(i)
@@ -262,11 +270,9 @@ func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
b := r.BytesAt(r.DataOff(isym), r.DataSize(isym))
var info *goobj.FuncInfo
lengths := info.ReadFuncInfoLengths(b)
- off, end := info.ReadPcline(b)
- pcline := r.BytesAt(pcdataBase+off, int(end-off))
+ pcline := getSymData(info.ReadPcline(b))
line := int(pcValue(pcline, pc-addr, arch))
- off, end = info.ReadPcfile(b)
- pcfile := r.BytesAt(pcdataBase+off, int(end-off))
+ pcfile := getSymData(info.ReadPcfile(b))
fileID := pcValue(pcfile, pc-addr, arch)
globalFileID := info.ReadFile(b, lengths.FileOff, uint32(fileID))
fileName := r.File(int(globalFileID))
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index d1f2ac583dc..2b95ad5a67d 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -1421,7 +1421,7 @@ func (d *dwctxt) writeframes(fs loader.Sym) dwarfSecInfo {
deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr))
}
- for pcsp.Init(fpcsp); !pcsp.Done; pcsp.Next() {
+ for pcsp.Init(d.linkctxt.loader.Data(fpcsp)); !pcsp.Done; pcsp.Next() {
nextpc := pcsp.NextPC
// pciterinit goes up to the end of the function,
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 09c7bbfb53d..caa4566190a 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -2252,7 +2252,7 @@ func (sc *stkChk) check(up *chain, depth int) int {
var ch1 chain
pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
ri := 0
- for pcsp.Init(info.Pcsp()); !pcsp.Done; pcsp.Next() {
+ for pcsp.Init(ldr.Data(info.Pcsp())); !pcsp.Done; pcsp.Next() {
// pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
// Check stack size in effect for this span.
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index c7535f6a61b..e9fd5937e78 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -592,9 +592,8 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
fi := ldr.FuncInfo(s)
if fi.Valid() {
fi.Preload()
- npc := fi.NumPcdata()
- for i := uint32(0); i < npc; i++ {
- pcdata = append(pcdata, sym.Pcdata{P: fi.Pcdata(int(i))})
+ for _, dataSym := range fi.Pcdata() {
+ pcdata = append(pcdata, sym.Pcdata{P: ldr.Data(dataSym)})
}
nfd := fi.NumFuncdataoff()
for i := uint32(0); i < nfd; i++ {
@@ -666,15 +665,15 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
cu := ldr.SymUnit(s)
if fi.Valid() {
- pcsp = sym.Pcdata{P: fi.Pcsp()}
- pcfile = sym.Pcdata{P: fi.Pcfile()}
- pcline = sym.Pcdata{P: fi.Pcline()}
+ pcsp = sym.Pcdata{P: ldr.Data(fi.Pcsp())}
+ pcfile = sym.Pcdata{P: ldr.Data(fi.Pcfile())}
+ pcline = sym.Pcdata{P: ldr.Data(fi.Pcline())}
}
if fi.Valid() && fi.NumInlTree() > 0 {
its := oldState.genInlTreeSym(cu, fi, ctxt.Arch, state)
funcdata[objabi.FUNCDATA_InlTree] = its
- pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: fi.Pcinline()}
+ pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: ldr.Data(fi.Pcinline())}
}
// pcdata
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 8fd10b0848d..f149e3c831f 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -1878,19 +1878,24 @@ func (fi *FuncInfo) FuncID() objabi.FuncID {
return objabi.FuncID((*goobj.FuncInfo)(nil).ReadFuncID(fi.data))
}
-func (fi *FuncInfo) Pcsp() []byte {
- pcsp, end := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data)
- return fi.r.BytesAt(fi.r.PcdataBase()+pcsp, int(end-pcsp))
+func (fi *FuncInfo) Pcsp() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data)
+ return fi.l.resolve(fi.r, sym)
}
-func (fi *FuncInfo) Pcfile() []byte {
- pcf, end := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data)
- return fi.r.BytesAt(fi.r.PcdataBase()+pcf, int(end-pcf))
+func (fi *FuncInfo) Pcfile() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data)
+ return fi.l.resolve(fi.r, sym)
}
-func (fi *FuncInfo) Pcline() []byte {
- pcln, end := (*goobj.FuncInfo)(nil).ReadPcline(fi.data)
- return fi.r.BytesAt(fi.r.PcdataBase()+pcln, int(end-pcln))
+func (fi *FuncInfo) Pcline() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcline(fi.data)
+ return fi.l.resolve(fi.r, sym)
+}
+
+func (fi *FuncInfo) Pcinline() Sym {
+ sym := (*goobj.FuncInfo)(nil).ReadPcinline(fi.data)
+ return fi.l.resolve(fi.r, sym)
}
// Preload has to be called prior to invoking the various methods
@@ -1899,27 +1904,16 @@ func (fi *FuncInfo) Preload() {
fi.lengths = (*goobj.FuncInfo)(nil).ReadFuncInfoLengths(fi.data)
}
-func (fi *FuncInfo) Pcinline() []byte {
+func (fi *FuncInfo) Pcdata() []Sym {
if !fi.lengths.Initialized {
panic("need to call Preload first")
}
- pcinl, end := (*goobj.FuncInfo)(nil).ReadPcinline(fi.data, fi.lengths.PcdataOff)
- return fi.r.BytesAt(fi.r.PcdataBase()+pcinl, int(end-pcinl))
-}
-
-func (fi *FuncInfo) NumPcdata() uint32 {
- if !fi.lengths.Initialized {
- panic("need to call Preload first")
+ syms := (*goobj.FuncInfo)(nil).ReadPcdata(fi.data)
+ ret := make([]Sym, len(syms))
+ for i := range ret {
+ ret[i] = fi.l.resolve(fi.r, syms[i])
}
- return fi.lengths.NumPcdata
-}
-
-func (fi *FuncInfo) Pcdata(k int) []byte {
- if !fi.lengths.Initialized {
- panic("need to call Preload first")
- }
- pcdat, end := (*goobj.FuncInfo)(nil).ReadPcdata(fi.data, fi.lengths.PcdataOff, uint32(k))
- return fi.r.BytesAt(fi.r.PcdataBase()+pcdat, int(end-pcdat))
+ return ret
}
func (fi *FuncInfo) NumFuncdataoff() uint32 {
From 26407b22129e2e54db269c1a92826521addd8d56 Mon Sep 17 00:00:00 2001
From: Jeremy Faller
Date: Wed, 12 Aug 2020 19:26:53 -0400
Subject: [PATCH 0005/1082] [dev.link] cmd/{compile,link}: remove pcdata tables
from pclntab_old
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Move the pctables out of pclntab_old. Creates a new generator symbol,
runtime.pctab, which holds all the deduplicated pctables. Also, tightens
up some of the types in runtime.
Darwin, cmd/compile statistics:
alloc/op
Pclntab_GC 26.4MB ± 0% 13.8MB ± 0%
allocs/op
Pclntab_GC 89.9k ± 0% 86.4k ± 0%
liveB
Pclntab_GC 25.5M ± 0% 24.2M ± 0%
No significant change in binary size.
Change-Id: I1560fd4421f8a210f8d4b508fbc54e1780e338f9
Reviewed-on: https://go-review.googlesource.com/c/go/+/248332
Run-TryBot: Jeremy Faller
TryBot-Result: Gobot Gobot
Reviewed-by: Cherry Zhang
---
src/cmd/link/internal/ld/data.go | 2 +
src/cmd/link/internal/ld/pcln.go | 143 ++++++++++++------
src/cmd/link/internal/ld/symtab.go | 4 +
src/cmd/link/internal/loader/symbolbuilder.go | 9 ++
src/cmd/link/internal/sym/symbol.go | 4 -
src/debug/gosym/pclntab.go | 10 +-
src/runtime/runtime2.go | 8 +-
src/runtime/symtab.go | 28 ++--
8 files changed, 136 insertions(+), 72 deletions(-)
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index a551d464038..2aecbfbeb5c 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -1925,6 +1925,7 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.funcnametab", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.cutab", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.filetab", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab_old", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect)
if ctxt.HeadType == objabi.Haix {
@@ -2511,6 +2512,7 @@ func (ctxt *Link) address() []*sym.Segment {
ctxt.defineInternal("runtime.funcnametab", sym.SRODATA)
ctxt.defineInternal("runtime.cutab", sym.SRODATA)
ctxt.defineInternal("runtime.filetab", sym.SRODATA)
+ ctxt.defineInternal("runtime.pctab", sym.SRODATA)
ctxt.defineInternal("runtime.pclntab_old", sym.SRODATA)
ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr))
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index e9fd5937e78..576f1c3780a 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -43,6 +43,7 @@ type pclntab struct {
findfunctab loader.Sym
cutab loader.Sym
filetab loader.Sym
+ pctab loader.Sym
// The number of functions + number of TEXT sections - 1. This is such an
// unexpected value because platforms that have more than one TEXT section
@@ -273,10 +274,11 @@ func (state *pclntab) generatePCHeader(ctxt *Link) {
off = writeSymOffset(off, state.funcnametab)
off = writeSymOffset(off, state.cutab)
off = writeSymOffset(off, state.filetab)
+ off = writeSymOffset(off, state.pctab)
off = writeSymOffset(off, state.pclntab)
}
- size := int64(8 + 6*ctxt.Arch.PtrSize)
+ size := int64(8 + 7*ctxt.Arch.PtrSize)
state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
}
@@ -463,6 +465,68 @@ func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.Compilat
return cuOffsets
}
+// generatePctab creates the runtime.pctab variable, holding all the
+// deduplicated pcdata.
+func (state *pclntab) generatePctab(ctxt *Link, container loader.Bitmap) {
+ ldr := ctxt.loader
+
+ // Pctab offsets of 0 are considered invalid in the runtime. We respect
+ // that by just padding a single byte at the beginning of runtime.pctab,
+ // that way no real offsets can be zero.
+ size := int64(1)
+
+ // Walk the functions, finding offset to store each pcdata.
+ seen := make(map[loader.Sym]struct{})
+ saveOffset := func(pcSym loader.Sym) {
+ if _, ok := seen[pcSym]; !ok {
+ datSize := ldr.SymSize(pcSym)
+ if datSize != 0 {
+ ldr.SetSymValue(pcSym, size)
+ } else {
+ // Invalid PC data, record as zero.
+ ldr.SetSymValue(pcSym, 0)
+ }
+ size += datSize
+ seen[pcSym] = struct{}{}
+ }
+ }
+ for _, s := range ctxt.Textp {
+ if !emitPcln(ctxt, s, container) {
+ continue
+ }
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+
+ pcSyms := []loader.Sym{fi.Pcsp(), fi.Pcfile(), fi.Pcline()}
+ for _, pcSym := range pcSyms {
+ saveOffset(pcSym)
+ }
+ for _, pcSym := range fi.Pcdata() {
+ saveOffset(pcSym)
+ }
+ if fi.NumInlTree() > 0 {
+ saveOffset(fi.Pcinline())
+ }
+ }
+
+ // TODO: There is no reason we need a generator for this variable, and it
+ // could be moved to a carrier symbol. However, carrier symbols containing
+ // carrier symbols don't work yet (as of Aug 2020). Once this is fixed,
+ // runtime.pctab could just be a carrier sym.
+ writePctab := func(ctxt *Link, s loader.Sym) {
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(s)
+ for sym := range seen {
+ sb.SetBytesAt(ldr.SymValue(sym), ldr.Data(sym))
+ }
+ }
+
+ state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab)
+}
+
// pclntab initializes the pclntab symbol with
// runtime function and file name information.
@@ -494,6 +558,9 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
// runtime.filetab
// []null terminated filename strings
//
+ // runtime.pctab
+ // []byte of deduplicated pc data.
+ //
// runtime.pclntab_old
// function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
// end PC [thearch.ptrsize bytes]
@@ -514,6 +581,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
state.generatePCHeader(ctxt)
state.generateFuncnametab(ctxt, container)
cuOffsets := state.generateFilenameTabs(ctxt, compUnits, container)
+ state.generatePctab(ctxt, container)
funcdataBytes := int64(0)
ldr.SetCarrierSym(state.pclntab, state.carrier)
@@ -525,21 +593,6 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
ftab.Grow(int64(state.nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
- szHint := len(ctxt.Textp) * 2
- pctaboff := make(map[string]uint32, szHint)
- writepctab := func(off int32, p []byte) int32 {
- start, ok := pctaboff[string(p)]
- if !ok {
- if len(p) > 0 {
- start = uint32(len(ftab.Data()))
- ftab.AddBytes(p)
- }
- pctaboff[string(p)] = start
- }
- newoff := int32(ftab.SetUint32(ctxt.Arch, int64(off), start))
- return newoff
- }
-
setAddr := (*loader.SymbolBuilder).SetAddrPlus
if ctxt.IsExe() && ctxt.IsInternal() {
// Internal linking static executable. At this point the function
@@ -555,10 +608,6 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
}
}
- pcsp := sym.Pcdata{}
- pcfile := sym.Pcdata{}
- pcline := sym.Pcdata{}
- pcdata := []sym.Pcdata{}
funcdata := []loader.Sym{}
funcdataoff := []int64{}
@@ -583,18 +632,13 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
}
prevFunc = s
- pcsp.P = pcsp.P[:0]
- pcline.P = pcline.P[:0]
- pcfile.P = pcfile.P[:0]
- pcdata = pcdata[:0]
+ var numPCData int32
funcdataoff = funcdataoff[:0]
funcdata = funcdata[:0]
fi := ldr.FuncInfo(s)
if fi.Valid() {
fi.Preload()
- for _, dataSym := range fi.Pcdata() {
- pcdata = append(pcdata, sym.Pcdata{P: ldr.Data(dataSym)})
- }
+ numPCData = int32(len(fi.Pcdata()))
nfd := fi.NumFuncdataoff()
for i := uint32(0); i < nfd; i++ {
funcdataoff = append(funcdataoff, fi.Funcdataoff(int(i)))
@@ -602,15 +646,12 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
funcdata = fi.Funcdata(funcdata)
}
+ writeInlPCData := false
if fi.Valid() && fi.NumInlTree() > 0 {
-
- if len(pcdata) <= objabi.PCDATA_InlTreeIndex {
- // Create inlining pcdata table.
- newpcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1)
- copy(newpcdata, pcdata)
- pcdata = newpcdata
+ writeInlPCData = true
+ if numPCData <= objabi.PCDATA_InlTreeIndex {
+ numPCData = objabi.PCDATA_InlTreeIndex + 1
}
-
if len(funcdataoff) <= objabi.FUNCDATA_InlTree {
// Create inline tree funcdata.
newfuncdata := make([]loader.Sym, objabi.FUNCDATA_InlTree+1)
@@ -635,7 +676,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
// fixed size of struct, checked below
off := funcstart
- end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 6*4 + int32(len(pcdata))*4 + int32(len(funcdata))*int32(ctxt.Arch.PtrSize)
+ end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 6*4 + numPCData*4 + int32(len(funcdata))*int32(ctxt.Arch.PtrSize)
if len(funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) {
end += 4
}
@@ -664,23 +705,21 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
cu := ldr.SymUnit(s)
- if fi.Valid() {
- pcsp = sym.Pcdata{P: ldr.Data(fi.Pcsp())}
- pcfile = sym.Pcdata{P: ldr.Data(fi.Pcfile())}
- pcline = sym.Pcdata{P: ldr.Data(fi.Pcline())}
- }
if fi.Valid() && fi.NumInlTree() > 0 {
its := oldState.genInlTreeSym(cu, fi, ctxt.Arch, state)
funcdata[objabi.FUNCDATA_InlTree] = its
- pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: ldr.Data(fi.Pcinline())}
}
// pcdata
- off = writepctab(off, pcsp.P)
- off = writepctab(off, pcfile.P)
- off = writepctab(off, pcline.P)
- off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcdata))))
+ if fi.Valid() {
+ off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcsp()))))
+ off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcfile()))))
+ off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcline()))))
+ } else {
+ off += 12
+ }
+ off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(numPCData)))
// Store the offset to compilation unit's file table.
cuIdx := ^uint32(0)
@@ -700,9 +739,17 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
// nfuncdata must be the final entry.
off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata))))
- for i := range pcdata {
- off = writepctab(off, pcdata[i].P)
+
+ // Output the pcdata.
+ if fi.Valid() {
+ for i, pcSym := range fi.Pcdata() {
+ ftab.SetUint32(ctxt.Arch, int64(off+int32(i*4)), uint32(ldr.SymValue(pcSym)))
+ }
+ if writeInlPCData {
+ ftab.SetUint32(ctxt.Arch, int64(off+objabi.PCDATA_InlTreeIndex*4), uint32(ldr.SymValue(fi.Pcinline())))
+ }
}
+ off += numPCData * 4
// funcdata, must be pointer-aligned and we're only int32-aligned.
// Missing funcdata will be 0 (nil pointer).
@@ -724,7 +771,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
}
if off != end {
- ctxt.Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcdata), len(funcdata), ctxt.Arch.PtrSize)
+ ctxt.Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, numPCData, len(funcdata), ctxt.Arch.PtrSize)
errorexit()
}
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index d05b98f04a6..520aaa44c21 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -627,6 +627,10 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
moduledata.AddAddr(ctxt.Arch, pcln.filetab)
moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab)))
moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab)))
+ // The pctab slice
+ moduledata.AddAddr(ctxt.Arch, pcln.pctab)
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab)))
+ moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab)))
// The pclntab slice
moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab)))
diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go
index e14d89a9273..c0c723d7f00 100644
--- a/src/cmd/link/internal/loader/symbolbuilder.go
+++ b/src/cmd/link/internal/loader/symbolbuilder.go
@@ -336,6 +336,15 @@ func (sb *SymbolBuilder) Addstring(str string) int64 {
return r
}
+func (sb *SymbolBuilder) SetBytesAt(off int64, b []byte) int64 {
+ datLen := int64(len(b))
+ if off+datLen > int64(len(sb.data)) {
+ panic("attempt to write past end of buffer")
+ }
+ copy(sb.data[off:off+datLen], b)
+ return off + datLen
+}
+
func (sb *SymbolBuilder) addSymRef(tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
if sb.kind == 0 {
sb.kind = sym.SDATA
diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go
index 1a4165ebf7c..70cf36a87ee 100644
--- a/src/cmd/link/internal/sym/symbol.go
+++ b/src/cmd/link/internal/sym/symbol.go
@@ -33,7 +33,3 @@ func VersionToABI(v int) (obj.ABI, bool) {
}
return ^obj.ABI(0), false
}
-
-type Pcdata struct {
- P []byte
-}
diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go
index 21edddda204..a72f9847d7c 100644
--- a/src/debug/gosym/pclntab.go
+++ b/src/debug/gosym/pclntab.go
@@ -58,6 +58,7 @@ type LineTable struct {
functab []byte
nfunctab uint32
filetab []byte
+ pctab []byte // points to the pctables.
nfiletab uint32
funcNames map[uint32]string // cache the function names
strings map[uint32]string // interned substrings of Data, keyed by offset
@@ -235,6 +236,8 @@ func (t *LineTable) parsePclnTab() {
offset = t.uintptr(t.Data[8+4*t.ptrsize:])
t.filetab = t.Data[offset:]
offset = t.uintptr(t.Data[8+5*t.ptrsize:])
+ t.pctab = t.Data[offset:]
+ offset = t.uintptr(t.Data[8+6*t.ptrsize:])
t.funcdata = t.Data[offset:]
t.functab = t.Data[offset:]
functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
@@ -244,6 +247,7 @@ func (t *LineTable) parsePclnTab() {
t.funcdata = t.Data
t.funcnametab = t.Data
t.functab = t.Data[8+t.ptrsize:]
+ t.pctab = t.Data
functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
fileoff := t.binary.Uint32(t.functab[functabsize:])
t.functab = t.functab[:functabsize]
@@ -373,7 +377,7 @@ func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
// off is the offset to the beginning of the pc-value table,
// and entry is the start PC for the corresponding function.
func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
- p := t.funcdata[off:]
+ p := t.pctab[off:]
val := int32(-1)
pc := entry
@@ -396,8 +400,8 @@ func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum,
return 0
}
- fp := t.funcdata[filetab:]
- fl := t.funcdata[linetab:]
+ fp := t.pctab[filetab:]
+ fl := t.pctab[linetab:]
fileVal := int32(-1)
filePC := entry
lineVal := int32(-1)
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index 5a79c7e6ec7..755c409078d 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -800,10 +800,10 @@ type _func struct {
args int32 // in/out args size
deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
- pcsp int32
- pcfile int32
- pcln int32
- npcdata int32
+ pcsp uint32
+ pcfile uint32
+ pcln uint32
+ npcdata uint32
cuOffset uint32 // runtime.cutab offset of this function's CU
funcID funcID // set for certain special runtime functions
_ [2]byte // pad
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index fbd93155223..0610f751790 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -345,6 +345,7 @@ type pcHeader struct {
funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
cuOffset uintptr // offset to the cutab variable from pcHeader
filetabOffset uintptr // offset to the filetab variable from pcHeader
+ pctabOffset uintptr // offset to the pctab varible from pcHeader
pclnOffset uintptr // offset to the pclntab variable from pcHeader
}
@@ -358,6 +359,7 @@ type moduledata struct {
funcnametab []byte
cutab []uint32
filetab []byte
+ pctab []byte
pclntable []byte
ftab []functab
findfunctab uintptr
@@ -721,7 +723,7 @@ type pcvalueCache struct {
type pcvalueCacheEnt struct {
// targetpc and off together are the key of this cache entry.
targetpc uintptr
- off int32
+ off uint32
// val is the value of this cached pcvalue entry.
val int32
}
@@ -736,7 +738,7 @@ func pcvalueCacheKey(targetpc uintptr) uintptr {
// Returns the PCData value, and the PC where this value starts.
// TODO: the start PC is returned only when cache is nil.
-func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, strict bool) (int32, uintptr) {
+func pcvalue(f funcInfo, off uint32, targetpc uintptr, cache *pcvalueCache, strict bool) (int32, uintptr) {
if off == 0 {
return -1, 0
}
@@ -770,7 +772,7 @@ func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, stric
return -1, 0
}
datap := f.datap
- p := datap.pclntable[off:]
+ p := datap.pctab[off:]
pc := f.entry
prevpc := pc
val := int32(-1)
@@ -812,7 +814,7 @@ func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, stric
print("runtime: invalid pc-encoded table f=", funcname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n")
- p = datap.pclntable[off:]
+ p = datap.pctab[off:]
pc = f.entry
val = -1
for {
@@ -893,7 +895,7 @@ func funcspdelta(f funcInfo, targetpc uintptr, cache *pcvalueCache) int32 {
// funcMaxSPDelta returns the maximum spdelta at any point in f.
func funcMaxSPDelta(f funcInfo) int32 {
datap := f.datap
- p := datap.pclntable[f.pcsp:]
+ p := datap.pctab[f.pcsp:]
pc := f.entry
val := int32(-1)
max := int32(0)
@@ -909,20 +911,20 @@ func funcMaxSPDelta(f funcInfo) int32 {
}
}
-func pcdatastart(f funcInfo, table int32) int32 {
- return *(*int32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4))
+func pcdatastart(f funcInfo, table uint32) uint32 {
+ return *(*uint32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4))
}
-func pcdatavalue(f funcInfo, table int32, targetpc uintptr, cache *pcvalueCache) int32 {
- if table < 0 || table >= f.npcdata {
+func pcdatavalue(f funcInfo, table uint32, targetpc uintptr, cache *pcvalueCache) int32 {
+ if table >= f.npcdata {
return -1
}
r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, true)
return r
}
-func pcdatavalue1(f funcInfo, table int32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 {
- if table < 0 || table >= f.npcdata {
+func pcdatavalue1(f funcInfo, table uint32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 {
+ if table >= f.npcdata {
return -1
}
r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, strict)
@@ -931,8 +933,8 @@ func pcdatavalue1(f funcInfo, table int32, targetpc uintptr, cache *pcvalueCache
// Like pcdatavalue, but also return the start PC of this PCData value.
// It doesn't take a cache.
-func pcdatavalue2(f funcInfo, table int32, targetpc uintptr) (int32, uintptr) {
- if table < 0 || table >= f.npcdata {
+func pcdatavalue2(f funcInfo, table uint32, targetpc uintptr) (int32, uintptr) {
+ if table >= f.npcdata {
return -1, 0
}
return pcvalue(f, pcdatastart(f, table), targetpc, nil, true)
From ac5c406ef0ab20e2a11f57470271266ef4265221 Mon Sep 17 00:00:00 2001
From: Jeremy Faller
Date: Thu, 13 Aug 2020 12:21:18 -0400
Subject: [PATCH 0006/1082] [dev.link] cmd/link: clean up some pclntab state
Clean up some pclntab state, specifically:
1) Remove the oldPclnState type.
2) Move a structure out of pclnState, that was holding some memory.
3) Stop passing container around everywhere and calling emitPcln. Use a
slice of function symbols instead.
Change-Id: I74e916564cd769a706750d024e55ee0d811a79da
Reviewed-on: https://go-review.googlesource.com/c/go/+/248379
Run-TryBot: Jeremy Faller
TryBot-Result: Gobot Gobot
Reviewed-by: Austin Clements
Reviewed-by: Cherry Zhang
---
src/cmd/link/internal/ld/pcln.go | 135 +++++++++++--------------------
1 file changed, 47 insertions(+), 88 deletions(-)
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 576f1c3780a..33476ec292f 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -16,17 +16,6 @@ import (
"strings"
)
-// oldPclnState holds state information used during pclntab generation. Here
-// 'ldr' is just a pointer to the context's loader, 'deferReturnSym' is the
-// index for the symbol "runtime.deferreturn",
-//
-// NB: This is deprecated, and will be eliminated when pclntab_old is
-// eliminated.
-type oldPclnState struct {
- ldr *loader.Loader
- deferReturnSym loader.Sym
-}
-
// pclntab holds the state needed for pclntab generation.
type pclntab struct {
// The first and last functions found.
@@ -56,12 +45,6 @@ type pclntab struct {
// The number of filenames in runtime.filetab.
nfiles uint32
-
- // maps the function symbol to offset in runtime.funcnametab
- // This doesn't need to reside in the state once pclntab_old's been
- // deleted -- it can live in generateFuncnametab.
- // TODO(jfaller): Delete me!
- funcNameOffset map[loader.Sym]int32
}
// addGeneratedSym adds a generator symbol to pclntab, returning the new Sym.
@@ -76,35 +59,26 @@ func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f gen
return s
}
-func makeOldPclnState(ctxt *Link) *oldPclnState {
- ldr := ctxt.loader
- drs := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal)
- state := &oldPclnState{
- ldr: ldr,
- deferReturnSym: drs,
- }
-
- return state
-}
-
// makePclntab makes a pclntab object, and assembles all the compilation units
-// we'll need to write pclntab.
-func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit) {
+// we'll need to write pclntab. Returns the pclntab structure, a slice of the
+// CompilationUnits we need, and a slice of the function symbols we need to
+// generate pclntab.
+func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit, []loader.Sym) {
ldr := ctxt.loader
- state := &pclntab{
- funcNameOffset: make(map[loader.Sym]int32),
- }
+ state := &pclntab{}
// Gather some basic stats and info.
seenCUs := make(map[*sym.CompilationUnit]struct{})
prevSect := ldr.SymSect(ctxt.Textp[0])
compUnits := []*sym.CompilationUnit{}
+ funcs := []loader.Sym{}
for _, s := range ctxt.Textp {
if !emitPcln(ctxt, s, container) {
continue
}
+ funcs = append(funcs, s)
state.nfunc++
if state.firstFunc == 0 {
state.firstFunc = s
@@ -130,15 +104,7 @@ func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.Compilat
compUnits = append(compUnits, cu)
}
}
- return state, compUnits
-}
-
-func ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 {
- start := len(ftab.Data())
- ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL
- ftd := ftab.Data()
- copy(ftd[start:], s)
- return int32(start)
+ return state, compUnits, funcs
}
// onlycsymbol looks at a symbol's name to report whether this is a
@@ -163,11 +129,13 @@ func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool {
return !container.Has(s)
}
-func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 {
+func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 {
+ ldr := ctxt.loader
+ target := ctxt.Target
deferreturn := uint32(0)
lastWasmAddr := uint32(0)
- relocs := state.ldr.Relocs(s)
+ relocs := ldr.Relocs(s)
for ri := 0; ri < relocs.Count(); ri++ {
r := relocs.At(ri)
if target.IsWasm() && r.Type() == objabi.R_ADDR {
@@ -178,7 +146,7 @@ func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint
// set the resumption point to PC_B.
lastWasmAddr = uint32(r.Add())
}
- if r.Type().IsDirectCall() && (r.Sym() == state.deferReturnSym || state.ldr.IsDeferReturnTramp(r.Sym())) {
+ if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) {
if target.IsWasm() {
deferreturn = lastWasmAddr - 1
} else {
@@ -211,8 +179,8 @@ func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint
// genInlTreeSym generates the InlTree sym for a function with the
// specified FuncInfo.
-func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, newState *pclntab) loader.Sym {
- ldr := state.ldr
+func genInlTreeSym(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, nameOffsets map[loader.Sym]uint32) loader.Sym {
+ ldr := ctxt.loader
its := ldr.CreateExtSym("", 0)
inlTreeSym := ldr.MakeSymbolUpdater(its)
// Note: the generated symbol is given a type of sym.SGOFUNC, as a
@@ -225,7 +193,7 @@ func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.Func
for i := 0; i < int(ninl); i++ {
call := fi.InlTree(i)
val := call.File
- nameoff, ok := newState.funcNameOffset[call.Func]
+ nameoff, ok := nameOffsets[call.Func]
if !ok {
panic("couldn't find function name offset")
}
@@ -282,16 +250,12 @@ func (state *pclntab) generatePCHeader(ctxt *Link) {
state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
}
-// walkFuncs iterates over the Textp, calling a function for each unique
+// walkFuncs iterates over the funcs, calling a function for each unique
// function and inlined function.
-func (state *pclntab) walkFuncs(ctxt *Link, container loader.Bitmap, f func(loader.Sym)) {
+func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) {
ldr := ctxt.loader
seen := make(map[loader.Sym]struct{})
- for _, ls := range ctxt.Textp {
- s := loader.Sym(ls)
- if !emitPcln(ctxt, s, container) {
- continue
- }
+ for _, s := range funcs {
if _, ok := seen[s]; !ok {
f(s)
seen[s] = struct{}{}
@@ -312,37 +276,37 @@ func (state *pclntab) walkFuncs(ctxt *Link, container loader.Bitmap, f func(load
}
}
-// generateFuncnametab creates the function name table.
-func (state *pclntab) generateFuncnametab(ctxt *Link, container loader.Bitmap) {
+// generateFuncnametab creates the function name table. Returns a map of
+// func symbol to the name offset in runtime.funcnamtab.
+func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[loader.Sym]uint32 {
+ nameOffsets := make(map[loader.Sym]uint32, state.nfunc)
+
// Write the null terminated strings.
writeFuncNameTab := func(ctxt *Link, s loader.Sym) {
symtab := ctxt.loader.MakeSymbolUpdater(s)
- for s, off := range state.funcNameOffset {
+ for s, off := range nameOffsets {
symtab.AddStringAt(int64(off), ctxt.loader.SymName(s))
}
}
// Loop through the CUs, and calculate the size needed.
var size int64
- state.walkFuncs(ctxt, container, func(s loader.Sym) {
- state.funcNameOffset[s] = int32(size)
+ walkFuncs(ctxt, funcs, func(s loader.Sym) {
+ nameOffsets[s] = uint32(size)
size += int64(ctxt.loader.SymNameLen(s)) + 1 // NULL terminate
})
state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab)
+ return nameOffsets
}
-// walkFilenames walks the filenames in the all reachable functions.
-func walkFilenames(ctxt *Link, container loader.Bitmap, f func(*sym.CompilationUnit, goobj.CUFileIndex)) {
+// walkFilenames walks funcs, calling a function for each filename used in each
+// function's line table.
+func walkFilenames(ctxt *Link, funcs []loader.Sym, f func(*sym.CompilationUnit, goobj.CUFileIndex)) {
ldr := ctxt.loader
// Loop through all functions, finding the filenames we need.
- for _, ls := range ctxt.Textp {
- s := loader.Sym(ls)
- if !emitPcln(ctxt, s, container) {
- continue
- }
-
+ for _, s := range funcs {
fi := ldr.FuncInfo(s)
if !fi.Valid() {
continue
@@ -382,7 +346,7 @@ func walkFilenames(ctxt *Link, container loader.Bitmap, f func(*sym.CompilationU
// 1) Get Func.CUIndex: M := func.cuOffset
// 2) Find filename offset: fileOffset := runtime.cutab[M+K]
// 3) Get the filename: getcstring(runtime.filetab[fileOffset])
-func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, container loader.Bitmap) []uint32 {
+func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, funcs []loader.Sym) []uint32 {
// On a per-CU basis, keep track of all the filenames we need.
//
// Note, that we store the filenames in a separate section in the object
@@ -402,7 +366,7 @@ func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.Compilat
// file index we've seen per CU so we can calculate how large the
// CU->global table needs to be.
var fileSize int64
- walkFilenames(ctxt, container, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) {
+ walkFilenames(ctxt, funcs, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) {
// Note we use the raw filename for lookup, but use the expanded filename
// when we save the size.
filename := cu.FileTable[i]
@@ -467,7 +431,7 @@ func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.Compilat
// generatePctab creates the runtime.pctab variable, holding all the
// deduplicated pcdata.
-func (state *pclntab) generatePctab(ctxt *Link, container loader.Bitmap) {
+func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) {
ldr := ctxt.loader
// Pctab offsets of 0 are considered invalid in the runtime. We respect
@@ -490,10 +454,7 @@ func (state *pclntab) generatePctab(ctxt *Link, container loader.Bitmap) {
seen[pcSym] = struct{}{}
}
}
- for _, s := range ctxt.Textp {
- if !emitPcln(ctxt, s, container) {
- continue
- }
+ for _, s := range funcs {
fi := ldr.FuncInfo(s)
if !fi.Valid() {
continue
@@ -566,8 +527,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
// end PC [thearch.ptrsize bytes]
// func structures, pcdata tables.
- oldState := makeOldPclnState(ctxt)
- state, compUnits := makePclntab(ctxt, container)
+ state, compUnits, funcs := makePclntab(ctxt, container)
ldr := ctxt.loader
state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
@@ -579,9 +539,12 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
// rational form.
state.pclntab = ldr.LookupOrCreateSym("runtime.pclntab_old", 0)
state.generatePCHeader(ctxt)
- state.generateFuncnametab(ctxt, container)
- cuOffsets := state.generateFilenameTabs(ctxt, compUnits, container)
- state.generatePctab(ctxt, container)
+ nameOffsets := state.generateFuncnametab(ctxt, funcs)
+ cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs)
+ state.generatePctab(ctxt, funcs)
+
+ // Used to when computing defer return.
+ deferReturnSym := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal)
funcdataBytes := int64(0)
ldr.SetCarrierSym(state.pclntab, state.carrier)
@@ -613,11 +576,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
var nfunc int32
prevFunc := ctxt.Textp[0]
- for _, s := range ctxt.Textp {
- if !emitPcln(ctxt, s, container) {
- continue
- }
-
+ for _, s := range funcs {
thisSect := ldr.SymSect(s)
prevSect := ldr.SymSect(prevFunc)
if thisSect != prevSect {
@@ -686,7 +645,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
off = int32(setAddr(ftab, ctxt.Arch, int64(off), s, 0))
// name int32
- nameoff, ok := state.funcNameOffset[s]
+ nameoff, ok := nameOffsets[s]
if !ok {
panic("couldn't find function name offset")
}
@@ -701,13 +660,13 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args))
// deferreturn
- deferreturn := oldState.computeDeferReturn(&ctxt.Target, s)
+ deferreturn := computeDeferReturn(ctxt, deferReturnSym, s)
off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
cu := ldr.SymUnit(s)
if fi.Valid() && fi.NumInlTree() > 0 {
- its := oldState.genInlTreeSym(cu, fi, ctxt.Arch, state)
+ its := genInlTreeSym(ctxt, cu, fi, ctxt.Arch, nameOffsets)
funcdata[objabi.FUNCDATA_InlTree] = its
}
From 5402d40d5b041399392b29e4543f5fc4506197bd Mon Sep 17 00:00:00 2001
From: Jeremy Faller
Date: Tue, 18 Aug 2020 16:35:26 -0400
Subject: [PATCH 0007/1082] [dev.link] cmd/link: fix memory growth on dev.link
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
CL 247399 caused memory growth in the linker. Fix this by adjusting how
we preallocate the number of symbols we'll need.
cmd/compile (Darwin), alloc/op:
Loadlib_GC 33.5MB ± 0% 27.3MB ± 0%
Change-Id: I34997329ea4412716114df97fc9dad6ad0c171ee
Reviewed-on: https://go-review.googlesource.com/c/go/+/249024
Run-TryBot: Jeremy Faller
Reviewed-by: Cherry Zhang
Reviewed-by: Austin Clements
TryBot-Result: Gobot Gobot
---
src/cmd/link/internal/ld/lib.go | 2 +-
src/cmd/link/internal/loader/loader.go | 36 +++++++++++++++++---------
2 files changed, 25 insertions(+), 13 deletions(-)
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index caa4566190a..a01bdefa37e 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -543,7 +543,7 @@ func (ctxt *Link) loadlib() {
}
// Add non-package symbols and references of externally defined symbols.
- ctxt.loader.LoadNonpkgSyms(ctxt.Arch)
+ ctxt.loader.LoadSyms(ctxt.Arch)
// Load symbols from shared libraries, after all Go object symbols are loaded.
for _, lib := range ctxt.Library {
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index f149e3c831f..ea9cd1bd2e2 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -328,7 +328,7 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor
ldr := &Loader{
start: make(map[*oReader]Sym),
objs: []objIdx{{}, {extReader, 0}}, // reserve index 0 for nil symbol, 1 for external symbols
- objSyms: make([]objSym, 1, 100000), // reserve index 0 for nil symbol
+ objSyms: make([]objSym, 1, 1), // This will get overwritten later.
extReader: extReader,
symsByName: [2]map[string]Sym{make(map[string]Sym, 80000), make(map[string]Sym, 50000)}, // preallocate ~2MB for ABI0 and ~1MB for ABI1 symbols
objByPkg: make(map[string]*oReader),
@@ -2016,8 +2016,9 @@ func (l *Loader) FuncInfo(i Sym) FuncInfo {
return FuncInfo{}
}
-// Preload a package: add autolibs, add defined package symbols to the symbol table.
-// Does not add non-package symbols yet, which will be done in LoadNonpkgSyms.
+// Preload a package: adds autolib.
+// Does not add defined package or non-packaged symbols to the symbol table.
+// These are done in LoadSyms.
// Does not read symbol data.
// Returns the fingerprint of the object.
func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64) goobj.FingerprintType {
@@ -2060,8 +2061,6 @@ func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, u
}
l.addObj(lib.Pkg, or)
- st := loadState{l: l}
- st.preloadSyms(or, pkgDef)
// The caller expects us consuming all the data
f.MustSeek(length, os.SEEK_CUR)
@@ -2144,17 +2143,30 @@ func (st *loadState) preloadSyms(r *oReader, kind int) {
}
}
-// Add hashed (content-addressable) symbols, non-package symbols, and
+// Add syms, hashed (content-addressable) symbols, non-package symbols, and
// references to external symbols (which are always named).
-func (l *Loader) LoadNonpkgSyms(arch *sys.Arch) {
+func (l *Loader) LoadSyms(arch *sys.Arch) {
+ // Allocate space for symbols, making a guess as to how much space we need.
+ // This function was determined empirically by looking at the cmd/compile on
+ // Darwin, and picking factors for hashed and hashed64 syms.
+ var symSize, hashedSize, hashed64Size int
+ for _, o := range l.objs[goObjStart:] {
+ symSize += o.r.ndef + o.r.nhasheddef/2 + o.r.nhashed64def/2 + o.r.NNonpkgdef()
+ hashedSize += o.r.nhasheddef / 2
+ hashed64Size += o.r.nhashed64def / 2
+ }
+ // Index 0 is invalid for symbols.
+ l.objSyms = make([]objSym, 1, symSize)
+
l.npkgsyms = l.NSym()
- // Preallocate some space (a few hundreds KB) for some symbols.
- // As of Go 1.15, linking cmd/compile has ~8000 hashed64 symbols and
- // ~13000 hashed symbols.
st := loadState{
l: l,
- hashed64Syms: make(map[uint64]symAndSize, 10000),
- hashedSyms: make(map[goobj.HashType]symAndSize, 15000),
+ hashed64Syms: make(map[uint64]symAndSize, hashed64Size),
+ hashedSyms: make(map[goobj.HashType]symAndSize, hashedSize),
+ }
+
+ for _, o := range l.objs[goObjStart:] {
+ st.preloadSyms(o.r, pkgDef)
}
for _, o := range l.objs[goObjStart:] {
st.preloadSyms(o.r, hashed64Def)
From bdb480fd623e58d0d1d0689a3755367379ea57bc Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Tue, 8 Sep 2020 15:28:43 +0700
Subject: [PATCH 0008/1082] cmd/compile: fix mishandling of unsafe-uintptr
arguments in go/defer
Currently, the statement:
go g(uintptr(f()))
gets rewritten into:
tmp := f()
newproc(8, g, uintptr(tmp))
runtime.KeepAlive(tmp)
which doesn't guarantee that tmp is still alive by time the g call is
scheduled to run.
This CL fixes the issue, by wrapping g call in a closure:
go func(p unsafe.Pointer) {
g(uintptr(p))
}(f())
then this will be rewritten into:
tmp := f()
go func(p unsafe.Pointer) {
g(uintptr(p))
runtime.KeepAlive(p)
}(tmp)
runtime.KeepAlive(tmp) // superfluous, but harmless
So the unsafe.Pointer p will be kept alive at the time g call runs.
Updates #24491
Change-Id: Ic10821251cbb1b0073daec92b82a866c6ebaf567
Reviewed-on: https://go-review.googlesource.com/c/go/+/253457
Run-TryBot: Cuong Manh Le
Reviewed-by: Matthew Dempsky
TryBot-Result: Gobot Gobot
---
src/cmd/compile/internal/gc/order.go | 1 +
src/cmd/compile/internal/gc/syntax.go | 41 +++++++++++++-------
src/cmd/compile/internal/gc/walk.go | 54 +++++++++++++++++++++------
test/fixedbugs/issue24491.go | 45 ++++++++++++++++++++++
4 files changed, 117 insertions(+), 24 deletions(-)
create mode 100644 test/fixedbugs/issue24491.go
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
index aa91160e5c9..412f073a8d4 100644
--- a/src/cmd/compile/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -502,6 +502,7 @@ func (o *Order) call(n *Node) {
x := o.copyExpr(arg.Left, arg.Left.Type, false)
x.Name.SetKeepalive(true)
arg.Left = x
+ n.SetNeedsWrapper(true)
}
}
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index 47e5e59156c..5580f789c5b 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -141,19 +141,20 @@ const (
nodeInitorder, _ // tracks state during init1; two bits
_, _ // second nodeInitorder bit
_, nodeHasBreak
- _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
- _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND
- _, nodeIsDDD // is the argument variadic
- _, nodeDiag // already printed error about this
- _, nodeColas // OAS resulting from :=
- _, nodeNonNil // guaranteed to be non-nil
- _, nodeTransient // storage can be reused immediately after this statement
- _, nodeBounded // bounds check unnecessary
- _, nodeHasCall // expression contains a function call
- _, nodeLikely // if statement condition likely
- _, nodeHasVal // node.E contains a Val
- _, nodeHasOpt // node.E contains an Opt
- _, nodeEmbedded // ODCLFIELD embedded type
+ _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
+ _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND
+ _, nodeIsDDD // is the argument variadic
+ _, nodeDiag // already printed error about this
+ _, nodeColas // OAS resulting from :=
+ _, nodeNonNil // guaranteed to be non-nil
+ _, nodeTransient // storage can be reused immediately after this statement
+ _, nodeBounded // bounds check unnecessary
+ _, nodeHasCall // expression contains a function call
+ _, nodeLikely // if statement condition likely
+ _, nodeHasVal // node.E contains a Val
+ _, nodeHasOpt // node.E contains an Opt
+ _, nodeEmbedded // ODCLFIELD embedded type
+ _, nodeNeedsWrapper // OCALLxxx node that needs to be wrapped
)
func (n *Node) Class() Class { return Class(n.flags.get3(nodeClass)) }
@@ -286,6 +287,20 @@ func (n *Node) SetIota(x int64) {
n.Xoffset = x
}
+func (n *Node) NeedsWrapper() bool {
+ return n.flags&nodeNeedsWrapper != 0
+}
+
+// SetNeedsWrapper indicates that OCALLxxx node needs to be wrapped by a closure.
+func (n *Node) SetNeedsWrapper(b bool) {
+ switch n.Op {
+ case OCALLFUNC, OCALLMETH, OCALLINTER:
+ default:
+ Fatalf("Node.SetNeedsWrapper %v", n.Op)
+ }
+ n.flags.set(nodeNeedsWrapper, b)
+}
+
// mayBeShared reports whether n may occur in multiple places in the AST.
// Extra care must be taken when mutating such a node.
func (n *Node) mayBeShared() bool {
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 0158af8700f..ab7f857031f 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -232,7 +232,11 @@ func walkstmt(n *Node) *Node {
n.Left = copyany(n.Left, &n.Ninit, true)
default:
- n.Left = walkexpr(n.Left, &n.Ninit)
+ if n.Left.NeedsWrapper() {
+ n.Left = wrapCall(n.Left, &n.Ninit)
+ } else {
+ n.Left = walkexpr(n.Left, &n.Ninit)
+ }
}
case OFOR, OFORUNTIL:
@@ -3857,6 +3861,14 @@ func candiscard(n *Node) bool {
// builtin(a1, a2, a3)
// }(x, y, z)
// for print, println, and delete.
+//
+// Rewrite
+// go f(x, y, uintptr(unsafe.Pointer(z)))
+// into
+// go func(a1, a2, a3) {
+// builtin(a1, a2, uintptr(a3))
+// }(x, y, unsafe.Pointer(z))
+// for function contains unsafe-uintptr arguments.
var wrapCall_prgen int
@@ -3868,9 +3880,17 @@ func wrapCall(n *Node, init *Nodes) *Node {
init.AppendNodes(&n.Ninit)
}
+ isBuiltinCall := n.Op != OCALLFUNC && n.Op != OCALLMETH && n.Op != OCALLINTER
+ // origArgs keeps track of what argument is uintptr-unsafe/unsafe-uintptr conversion.
+ origArgs := make([]*Node, n.List.Len())
t := nod(OTFUNC, nil, nil)
for i, arg := range n.List.Slice() {
s := lookupN("a", i)
+ if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.Etype == TUNSAFEPTR {
+ origArgs[i] = arg
+ arg = arg.Left
+ n.List.SetIndex(i, arg)
+ }
t.List.Append(symfield(s, arg.Type))
}
@@ -3878,10 +3898,22 @@ func wrapCall(n *Node, init *Nodes) *Node {
sym := lookupN("wrap·", wrapCall_prgen)
fn := dclfunc(sym, t)
- a := nod(n.Op, nil, nil)
- a.List.Set(paramNnames(t.Type))
- a = typecheck(a, ctxStmt)
- fn.Nbody.Set1(a)
+ args := paramNnames(t.Type)
+ for i, origArg := range origArgs {
+ if origArg == nil {
+ continue
+ }
+ arg := nod(origArg.Op, args[i], nil)
+ arg.Type = origArg.Type
+ args[i] = arg
+ }
+ call := nod(n.Op, nil, nil)
+ if !isBuiltinCall {
+ call.Op = OCALL
+ call.Left = n.Left
+ }
+ call.List.Set(args)
+ fn.Nbody.Set1(call)
funcbody()
@@ -3889,12 +3921,12 @@ func wrapCall(n *Node, init *Nodes) *Node {
typecheckslice(fn.Nbody.Slice(), ctxStmt)
xtop = append(xtop, fn)
- a = nod(OCALL, nil, nil)
- a.Left = fn.Func.Nname
- a.List.Set(n.List.Slice())
- a = typecheck(a, ctxStmt)
- a = walkexpr(a, init)
- return a
+ call = nod(OCALL, nil, nil)
+ call.Left = fn.Func.Nname
+ call.List.Set(n.List.Slice())
+ call = typecheck(call, ctxStmt)
+ call = walkexpr(call, init)
+ return call
}
// substArgTypes substitutes the given list of types for
diff --git a/test/fixedbugs/issue24491.go b/test/fixedbugs/issue24491.go
new file mode 100644
index 00000000000..47033687937
--- /dev/null
+++ b/test/fixedbugs/issue24491.go
@@ -0,0 +1,45 @@
+// run
+
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This test makes sure unsafe-uintptr arguments are handled correctly.
+
+package main
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+var done = make(chan bool, 1)
+
+func setup() unsafe.Pointer {
+ s := "ok"
+ runtime.SetFinalizer(&s, func(p *string) { *p = "FAIL" })
+ return unsafe.Pointer(&s)
+}
+
+//go:noinline
+//go:uintptrescapes
+func test(s string, p uintptr) {
+ runtime.GC()
+ if *(*string)(unsafe.Pointer(p)) != "ok" {
+ panic(s + " return unexpected result")
+ }
+ done <- true
+}
+
+func main() {
+ test("normal", uintptr(setup()))
+ <-done
+
+ go test("go", uintptr(setup()))
+ <-done
+
+ func() {
+ defer test("defer", uintptr(setup()))
+ }()
+ <-done
+}
From 9cf88333e8255155be4e136c572883bb5ad546bd Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Wed, 9 Sep 2020 12:06:18 +0700
Subject: [PATCH 0009/1082] cmd/compile: consistently use Type.IsUnsafePtr()
Passes toolstash-check.
Change-Id: Iaeae7cc20e26af733642c7c8c7ca0a059e5b07b2
Reviewed-on: https://go-review.googlesource.com/c/go/+/253657
Run-TryBot: Cuong Manh Le
TryBot-Result: Gobot Gobot
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/escape.go | 6 +++---
src/cmd/compile/internal/gc/ssa.go | 2 +-
src/cmd/compile/internal/gc/subr.go | 4 ++--
src/cmd/compile/internal/gc/walk.go | 10 +++++-----
4 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go
index d5cca4a38bd..dc469e276c6 100644
--- a/src/cmd/compile/internal/gc/escape.go
+++ b/src/cmd/compile/internal/gc/escape.go
@@ -485,7 +485,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
e.discard(max)
case OCONV, OCONVNOP:
- if checkPtr(e.curfn, 2) && n.Type.Etype == TUNSAFEPTR && n.Left.Type.IsPtr() {
+ if checkPtr(e.curfn, 2) && n.Type.IsUnsafePtr() && n.Left.Type.IsPtr() {
// When -d=checkptr=2 is enabled, treat
// conversions to unsafe.Pointer as an
// escaping operation. This allows better
@@ -493,7 +493,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
// easily detect object boundaries on the heap
// than the stack.
e.assignHeap(n.Left, "conversion to unsafe.Pointer", n)
- } else if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR {
+ } else if n.Type.IsUnsafePtr() && n.Left.Type.Etype == TUINTPTR {
e.unsafeValue(k, n.Left)
} else {
e.expr(k, n.Left)
@@ -625,7 +625,7 @@ func (e *Escape) unsafeValue(k EscHole, n *Node) {
switch n.Op {
case OCONV, OCONVNOP:
- if n.Left.Type.Etype == TUNSAFEPTR {
+ if n.Left.Type.IsUnsafePtr() {
e.expr(k, n.Left)
} else {
e.discard(n.Left)
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 52083d999e5..89644cd3f26 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -2113,7 +2113,7 @@ func (s *state) expr(n *Node) *ssa.Value {
}
// unsafe.Pointer <--> *T
- if to.Etype == TUNSAFEPTR && from.IsPtrShaped() || from.Etype == TUNSAFEPTR && to.IsPtrShaped() {
+ if to.IsUnsafePtr() && from.IsPtrShaped() || from.IsUnsafePtr() && to.IsPtrShaped() {
return v
}
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 8fa3fca50f5..6d0a40c287d 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -781,12 +781,12 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
}
// 8. src is a pointer or uintptr and dst is unsafe.Pointer.
- if (src.IsPtr() || src.Etype == TUINTPTR) && dst.Etype == TUNSAFEPTR {
+ if (src.IsPtr() || src.Etype == TUINTPTR) && dst.IsUnsafePtr() {
return OCONVNOP
}
// 9. src is unsafe.Pointer and dst is a pointer or uintptr.
- if src.Etype == TUNSAFEPTR && (dst.IsPtr() || dst.Etype == TUINTPTR) {
+ if src.IsUnsafePtr() && (dst.IsPtr() || dst.Etype == TUINTPTR) {
return OCONVNOP
}
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index ab7f857031f..a9fefb3ddd2 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -958,11 +958,11 @@ opswitch:
case OCONV, OCONVNOP:
n.Left = walkexpr(n.Left, init)
if n.Op == OCONVNOP && checkPtr(Curfn, 1) {
- if n.Type.IsPtr() && n.Left.Type.Etype == TUNSAFEPTR { // unsafe.Pointer to *T
+ if n.Type.IsPtr() && n.Left.Type.IsUnsafePtr() { // unsafe.Pointer to *T
n = walkCheckPtrAlignment(n, init, nil)
break
}
- if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer
+ if n.Type.IsUnsafePtr() && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer
n = walkCheckPtrArithmetic(n, init)
break
}
@@ -1127,7 +1127,7 @@ opswitch:
n.List.SetSecond(walkexpr(n.List.Second(), init))
case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
- checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.Etype == TUNSAFEPTR
+ checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.IsUnsafePtr()
if checkSlice {
n.Left.Left = walkexpr(n.Left.Left, init)
} else {
@@ -3886,7 +3886,7 @@ func wrapCall(n *Node, init *Nodes) *Node {
t := nod(OTFUNC, nil, nil)
for i, arg := range n.List.Slice() {
s := lookupN("a", i)
- if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.Etype == TUNSAFEPTR {
+ if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.IsUnsafePtr() {
origArgs[i] = arg
arg = arg.Left
n.List.SetIndex(i, arg)
@@ -4041,7 +4041,7 @@ func walkCheckPtrArithmetic(n *Node, init *Nodes) *Node {
walk(n.Left)
}
case OCONVNOP:
- if n.Left.Type.Etype == TUNSAFEPTR {
+ if n.Left.Type.IsUnsafePtr() {
n.Left = cheapexpr(n.Left, init)
originals = append(originals, convnop(n.Left, types.Types[TUNSAFEPTR]))
}
From 518369601ca2499cea68af86451f17d2856895f8 Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Wed, 9 Sep 2020 12:09:26 +0700
Subject: [PATCH 0010/1082] cmd/compile: add Type.IsUintptr() to detect type is
an uintptr
Passes toolstash-check.
Change-Id: I7051d45eafbfd4dea73a3d4b5ea6cff39d76cbc1
Reviewed-on: https://go-review.googlesource.com/c/go/+/253658
Run-TryBot: Cuong Manh Le
TryBot-Result: Gobot Gobot
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/esc.go | 6 +++---
src/cmd/compile/internal/gc/escape.go | 2 +-
src/cmd/compile/internal/gc/subr.go | 4 ++--
src/cmd/compile/internal/gc/walk.go | 4 ++--
src/cmd/compile/internal/types/type.go | 5 +++++
5 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
index 4b843aba35b..375331d1f5b 100644
--- a/src/cmd/compile/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -377,7 +377,7 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
// This really doesn't have much to do with escape analysis per se,
// but we are reusing the ability to annotate an individual function
// argument and pass those annotations along to importing code.
- if f.Type.Etype == TUINTPTR {
+ if f.Type.IsUintptr() {
if Debug['m'] != 0 {
Warnl(f.Pos, "assuming %v is unsafe uintptr", name())
}
@@ -407,13 +407,13 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
}
if fn.Func.Pragma&UintptrEscapes != 0 {
- if f.Type.Etype == TUINTPTR {
+ if f.Type.IsUintptr() {
if Debug['m'] != 0 {
Warnl(f.Pos, "marking %v as escaping uintptr", name())
}
return uintptrEscapesTag
}
- if f.IsDDD() && f.Type.Elem().Etype == TUINTPTR {
+ if f.IsDDD() && f.Type.Elem().IsUintptr() {
// final argument is ...uintptr.
if Debug['m'] != 0 {
Warnl(f.Pos, "marking %v as escaping ...uintptr", name())
diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go
index dc469e276c6..75da439bb7f 100644
--- a/src/cmd/compile/internal/gc/escape.go
+++ b/src/cmd/compile/internal/gc/escape.go
@@ -493,7 +493,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
// easily detect object boundaries on the heap
// than the stack.
e.assignHeap(n.Left, "conversion to unsafe.Pointer", n)
- } else if n.Type.IsUnsafePtr() && n.Left.Type.Etype == TUINTPTR {
+ } else if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() {
e.unsafeValue(k, n.Left)
} else {
e.expr(k, n.Left)
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 6d0a40c287d..d3ba53ff0c0 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -781,12 +781,12 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
}
// 8. src is a pointer or uintptr and dst is unsafe.Pointer.
- if (src.IsPtr() || src.Etype == TUINTPTR) && dst.IsUnsafePtr() {
+ if (src.IsPtr() || src.IsUintptr()) && dst.IsUnsafePtr() {
return OCONVNOP
}
// 9. src is unsafe.Pointer and dst is a pointer or uintptr.
- if src.IsUnsafePtr() && (dst.IsPtr() || dst.Etype == TUINTPTR) {
+ if src.IsUnsafePtr() && (dst.IsPtr() || dst.IsUintptr()) {
return OCONVNOP
}
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index a9fefb3ddd2..361de7e0f38 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -962,7 +962,7 @@ opswitch:
n = walkCheckPtrAlignment(n, init, nil)
break
}
- if n.Type.IsUnsafePtr() && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer
+ if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() { // uintptr to unsafe.Pointer
n = walkCheckPtrArithmetic(n, init)
break
}
@@ -3886,7 +3886,7 @@ func wrapCall(n *Node, init *Nodes) *Node {
t := nod(OTFUNC, nil, nil)
for i, arg := range n.List.Slice() {
s := lookupN("a", i)
- if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.IsUnsafePtr() {
+ if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.IsUintptr() && arg.Left.Type.IsUnsafePtr() {
origArgs[i] = arg
arg = arg.Left
n.List.SetIndex(i, arg)
diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go
index e4b3d885d94..a777a5fd907 100644
--- a/src/cmd/compile/internal/types/type.go
+++ b/src/cmd/compile/internal/types/type.go
@@ -1230,6 +1230,11 @@ func (t *Type) IsUnsafePtr() bool {
return t.Etype == TUNSAFEPTR
}
+// IsUintptr reports whether t is an uintptr.
+func (t *Type) IsUintptr() bool {
+ return t.Etype == TUINTPTR
+}
+
// IsPtrShaped reports whether t is represented by a single machine pointer.
// In addition to regular Go pointer types, this includes map, channel, and
// function types and unsafe.Pointer. It does not include array or struct types
From bdad4285709d1c5e04458268880775087be63027 Mon Sep 17 00:00:00 2001
From: Jay Conrod
Date: Fri, 28 Aug 2020 15:05:44 -0400
Subject: [PATCH 0011/1082] cmd/go: make 'go get' preserve sums for content of
new requirements
This preserves zip sums when 'go get' is run on a module that does not
have a package in the root directory. The zip must be fetched to
determine whether the package should be loaded, so we already load and
verify the sum.
Note that 'go mod tidy' may still remove these sums, since they
aren't needed to load packages.
Fixes #41103
Change-Id: I78f10a25f0392461fdc98518a7c92a38ee3233c3
Reviewed-on: https://go-review.googlesource.com/c/go/+/251880
Run-TryBot: Jay Conrod
TryBot-Result: Gobot Gobot
Reviewed-by: Bryan C. Mills
---
src/cmd/go/internal/modload/init.go | 38 ++++++++++++++-----
.../go/testdata/script/mod_get_sum_noroot.txt | 11 ++++++
2 files changed, 39 insertions(+), 10 deletions(-)
create mode 100644 src/cmd/go/testdata/script/mod_get_sum_noroot.txt
diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go
index 6f93b88eab2..8e8fb9e6a1f 100644
--- a/src/cmd/go/internal/modload/init.go
+++ b/src/cmd/go/internal/modload/init.go
@@ -862,14 +862,11 @@ func WriteGoMod() {
}
}
- // Always update go.sum, even if we didn't change go.mod: we may have
- // downloaded modules that we didn't have before.
- modfetch.WriteGoSum(keepSums())
-
if !dirty && cfg.CmdName != "mod tidy" {
// The go.mod file has the same semantic content that it had before
// (but not necessarily the same exact bytes).
- // Ignore any intervening edits.
+ // Don't write go.mod, but write go.sum in case we added or trimmed sums.
+ modfetch.WriteGoSum(keepSums(true))
return
}
@@ -880,6 +877,9 @@ func WriteGoMod() {
defer func() {
// At this point we have determined to make the go.mod file on disk equal to new.
index = indexModFile(new, modFile, false)
+
+ // Update go.sum after releasing the side lock and refreshing the index.
+ modfetch.WriteGoSum(keepSums(true))
}()
// Make a best-effort attempt to acquire the side lock, only to exclude
@@ -920,7 +920,10 @@ func WriteGoMod() {
// the last load function like ImportPaths, LoadALL, etc.). It also contains
// entries for go.mod files needed for MVS (the version of these entries
// ends with "/go.mod").
-func keepSums() map[module.Version]bool {
+//
+// If addDirect is true, the set also includes sums for modules directly
+// required by go.mod, as represented by the index, with replacements applied.
+func keepSums(addDirect bool) map[module.Version]bool {
// Walk the module graph and keep sums needed by MVS.
modkey := func(m module.Version) module.Version {
return module.Version{Path: m.Path, Version: m.Version + "/go.mod"}
@@ -932,9 +935,6 @@ func keepSums() map[module.Version]bool {
walk = func(m module.Version) {
// If we build using a replacement module, keep the sum for the replacement,
// since that's the code we'll actually use during a build.
- //
- // TODO(golang.org/issue/29182): Perhaps we should keep both sums, and the
- // sums for both sets of transitive requirements.
r := Replacement(m)
if r.Path == "" {
keep[modkey(m)] = true
@@ -964,9 +964,27 @@ func keepSums() map[module.Version]bool {
}
}
+ // Add entries for modules directly required by go.mod.
+ if addDirect {
+ for m := range index.require {
+ var kept module.Version
+ if r := Replacement(m); r.Path != "" {
+ kept = r
+ } else {
+ kept = m
+ }
+ keep[kept] = true
+ keep[module.Version{Path: kept.Path, Version: kept.Version + "/go.mod"}] = true
+ }
+ }
+
return keep
}
func TrimGoSum() {
- modfetch.TrimGoSum(keepSums())
+ // Don't retain sums for direct requirements in go.mod. When TrimGoSum is
+ // called, go.mod has not been updated, and it may contain requirements on
+ // modules deleted from the build list.
+ addDirect := false
+ modfetch.TrimGoSum(keepSums(addDirect))
}
diff --git a/src/cmd/go/testdata/script/mod_get_sum_noroot.txt b/src/cmd/go/testdata/script/mod_get_sum_noroot.txt
new file mode 100644
index 00000000000..0d9a840e779
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_get_sum_noroot.txt
@@ -0,0 +1,11 @@
+# When 'go get' is invoked on a module without a package in the root directory,
+# it should add sums for the module's go.mod file and its content to go.sum.
+# Verifies golang.org/issue/41103.
+go mod init m
+go get rsc.io/QUOTE
+grep '^rsc.io/QUOTE v1.5.2/go.mod ' go.sum
+grep '^rsc.io/QUOTE v1.5.2 ' go.sum
+
+# Double-check rsc.io/QUOTE does not have a root package.
+! go list -mod=readonly rsc.io/QUOTE
+stderr '^cannot find module providing package rsc.io/QUOTE: import lookup disabled by -mod=readonly$'
From 83ed734df03fd11d71f06bc02906a723afaf3936 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Tue, 11 Aug 2020 19:57:50 -0400
Subject: [PATCH 0012/1082] cmd/link: pre-resolve package reference
Pre-resolve package index references, so it doesn't need to do a
map lookup in every cross-package symbol reference resolution. It
increases the memory usage very slightly (O(# imported packages)).
Change-Id: Ia76c97ac51f1c2c2d5ea7ae34853850ec69ef0a8
Reviewed-on: https://go-review.googlesource.com/c/go/+/253604
Run-TryBot: Cherry Zhang
Reviewed-by: Than McIntosh
TryBot-Result: Gobot Gobot
---
src/cmd/link/internal/loader/loader.go | 36 ++++++++++++++++----------
1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 8fd10b0848d..43a0352e0bb 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -93,11 +93,12 @@ type oReader struct {
version int // version of static symbol
flags uint32 // read from object file
pkgprefix string
- syms []Sym // Sym's global index, indexed by local index
- ndef int // cache goobj.Reader.NSym()
- nhashed64def int // cache goobj.Reader.NHashed64Def()
- nhasheddef int // cache goobj.Reader.NHashedDef()
- objidx uint32 // index of this reader in the objs slice
+ syms []Sym // Sym's global index, indexed by local index
+ pkg []uint32 // indices of referenced package by PkgIdx (index into loader.objs array)
+ ndef int // cache goobj.Reader.NSym()
+ nhashed64def int // cache goobj.Reader.NHashed64Def()
+ nhasheddef int // cache goobj.Reader.NHashedDef()
+ objidx uint32 // index of this reader in the objs slice
}
// Total number of defined symbols (package symbols, hashed symbols, and
@@ -219,7 +220,7 @@ type Loader struct {
deferReturnTramp map[Sym]bool // whether the symbol is a trampoline of a deferreturn call
- objByPkg map[string]*oReader // map package path to its Go object reader
+ objByPkg map[string]uint32 // map package path to the index of its Go object reader
anonVersion int // most recently assigned ext static sym pseudo-version
@@ -331,7 +332,7 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor
objSyms: make([]objSym, 1, 100000), // reserve index 0 for nil symbol
extReader: extReader,
symsByName: [2]map[string]Sym{make(map[string]Sym, 80000), make(map[string]Sym, 50000)}, // preallocate ~2MB for ABI0 and ~1MB for ABI1 symbols
- objByPkg: make(map[string]*oReader),
+ objByPkg: make(map[string]uint32),
outer: make(map[Sym]Sym),
sub: make(map[Sym]Sym),
dynimplib: make(map[Sym]string),
@@ -370,7 +371,7 @@ func (l *Loader) addObj(pkg string, r *oReader) Sym {
}
pkg = objabi.PathToPrefix(pkg) // the object file contains escaped package path
if _, ok := l.objByPkg[pkg]; !ok {
- l.objByPkg[pkg] = r
+ l.objByPkg[pkg] = r.objidx
}
i := Sym(len(l.objSyms))
l.start[r] = i
@@ -635,12 +636,7 @@ func (l *Loader) resolve(r *oReader, s goobj.SymRef) Sym {
case goobj.PkgIdxSelf:
rr = r
default:
- pkg := r.Pkg(int(p))
- var ok bool
- rr, ok = l.objByPkg[pkg]
- if !ok {
- log.Fatalf("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
- }
+ rr = l.objs[r.pkg[p]].r
}
return l.toGlobal(rr, s.SymIdx)
}
@@ -2195,6 +2191,18 @@ func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {
}
}
+ // referenced packages
+ npkg := r.NPkg()
+ r.pkg = make([]uint32, npkg)
+ for i := 1; i < npkg; i++ { // PkgIdx 0 is a dummy invalid package
+ pkg := r.Pkg(i)
+ objidx, ok := l.objByPkg[pkg]
+ if !ok {
+ log.Fatalf("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
+ }
+ r.pkg[i] = objidx
+ }
+
// load flags of package refs
for i, n := 0, r.NRefFlags(); i < n; i++ {
rf := r.RefFlags(i)
From 9ef3ee339634d1a349e7b9bb4cae32aacc326f61 Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Wed, 9 Sep 2020 23:47:41 +0700
Subject: [PATCH 0013/1082] cmd/link: remove unnecessary type conversion for
nitablinks
It's already an uint64.
Change-Id: Ic4cdb957aa4f9245c1ea3f946bcb740f116dd04b
Reviewed-on: https://go-review.googlesource.com/c/go/+/253679
Run-TryBot: Cuong Manh Le
Reviewed-by: Than McIntosh
TryBot-Result: Gobot Gobot
---
src/cmd/link/internal/ld/symtab.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index bc880955b8c..56363cdaaea 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -681,8 +681,8 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
itablinkSym := ldr.Lookup("runtime.itablink", 0)
nitablinks := uint64(ldr.SymSize(itablinkSym)) / uint64(ctxt.Arch.PtrSize)
moduledata.AddAddr(ctxt.Arch, itablinkSym)
- moduledata.AddUint(ctxt.Arch, uint64(nitablinks))
- moduledata.AddUint(ctxt.Arch, uint64(nitablinks))
+ moduledata.AddUint(ctxt.Arch, nitablinks)
+ moduledata.AddUint(ctxt.Arch, nitablinks)
// The ptab slice
if ptab := ldr.Lookup("go.plugin.tabs", 0); ptab != 0 && ldr.AttrReachable(ptab) {
ldr.SetAttrLocal(ptab, true)
From 34835df04891a1d54394888b763af88f9476101d Mon Sep 17 00:00:00 2001
From: Michael Anthony Knyszek
Date: Wed, 9 Sep 2020 16:52:18 +0000
Subject: [PATCH 0014/1082] runtime: fix ReadMemStatsSlow's and
CheckScavengedBits' chunk iteration
Both ReadMemStatsSlow and CheckScavengedBits iterate over the page
allocator's chunks but don't actually check if they exist. During the
development process the chunks index became sparse, so now this was a
possibility. If the runtime tests' heap is sparse we might end up
segfaulting in either one of these functions, though this will generally
be very rare.
The pattern here to return nil for a nonexistent chunk is also useful
elsewhere, so this change introduces tryChunkOf which won't throw, but
might return nil. It also updates the documentation of chunkOf.
Fixes #41296.
Change-Id: Id5ae0ca3234480de1724fdf2e3677eeedcf76fa0
Reviewed-on: https://go-review.googlesource.com/c/go/+/253777
Run-TryBot: Michael Knyszek
Reviewed-by: Keith Randall
TryBot-Result: Gobot Gobot
---
src/runtime/export_test.go | 17 ++++++++++-------
src/runtime/mpagealloc.go | 13 +++++++++++++
2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index 3307000c51a..929bb35db68 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -358,7 +358,11 @@ func ReadMemStatsSlow() (base, slow MemStats) {
}
for i := mheap_.pages.start; i < mheap_.pages.end; i++ {
- pg := mheap_.pages.chunkOf(i).scavenged.popcntRange(0, pallocChunkPages)
+ chunk := mheap_.pages.tryChunkOf(i)
+ if chunk == nil {
+ continue
+ }
+ pg := chunk.scavenged.popcntRange(0, pallocChunkPages)
slow.HeapReleased += uint64(pg) * pageSize
}
for _, p := range allp {
@@ -756,11 +760,7 @@ func (p *PageAlloc) InUse() []AddrRange {
// Returns nil if the PallocData's L2 is missing.
func (p *PageAlloc) PallocData(i ChunkIdx) *PallocData {
ci := chunkIdx(i)
- l2 := (*pageAlloc)(p).chunks[ci.l1()]
- if l2 == nil {
- return nil
- }
- return (*PallocData)(&l2[ci.l2()])
+ return (*PallocData)((*pageAlloc)(p).tryChunkOf(ci))
}
// AddrRange represents a range over addresses.
@@ -900,7 +900,10 @@ func CheckScavengedBitsCleared(mismatches []BitsMismatch) (n int, ok bool) {
lock(&mheap_.lock)
chunkLoop:
for i := mheap_.pages.start; i < mheap_.pages.end; i++ {
- chunk := mheap_.pages.chunkOf(i)
+ chunk := mheap_.pages.tryChunkOf(i)
+ if chunk == nil {
+ continue
+ }
for j := 0; j < pallocChunkPages/64; j++ {
// Run over each 64-bit bitmap section and ensure
// scavenged is being cleared properly on allocation.
diff --git a/src/runtime/mpagealloc.go b/src/runtime/mpagealloc.go
index 8b3c62c375e..c90a6378bda 100644
--- a/src/runtime/mpagealloc.go
+++ b/src/runtime/mpagealloc.go
@@ -326,7 +326,20 @@ func (s *pageAlloc) init(mheapLock *mutex, sysStat *uint64) {
s.scav.scavLWM = maxSearchAddr
}
+// tryChunkOf returns the bitmap data for the given chunk.
+//
+// Returns nil if the chunk data has not been mapped.
+func (s *pageAlloc) tryChunkOf(ci chunkIdx) *pallocData {
+ l2 := s.chunks[ci.l1()]
+ if l2 == nil {
+ return nil
+ }
+ return &l2[ci.l2()]
+}
+
// chunkOf returns the chunk at the given chunk index.
+//
+// The chunk index must be valid or this method may throw.
func (s *pageAlloc) chunkOf(ci chunkIdx) *pallocData {
return &s.chunks[ci.l1()][ci.l2()]
}
From 2556eb76c8e752907600274d323a52e97418d14f Mon Sep 17 00:00:00 2001
From: Ian Lance Taylor
Date: Wed, 9 Sep 2020 10:40:11 -0700
Subject: [PATCH 0015/1082] runtime: ignore SIGPROF if profiling disable for
thread
This avoids a deadlock on prof.signalLock between setcpuprofilerate
and cpuprof.add if a SIGPROF is delivered to the thread between the
call to setThreadCPUProfiler and acquiring prof.signalLock.
Fixes #41014
Change-Id: Ie825e8594f93a19fb1a6320ed640f4e631553596
Reviewed-on: https://go-review.googlesource.com/c/go/+/253758
Run-TryBot: Ian Lance Taylor
Reviewed-by: Bryan C. Mills
TryBot-Result: Gobot Gobot
---
src/runtime/proc.go | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 341d52aea8e..739745aa265 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -3928,6 +3928,13 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
return
}
+ // If mp.profilehz is 0, then profiling is not enabled for this thread.
+ // We must check this to avoid a deadlock between setcpuprofilerate
+ // and the call to cpuprof.add, below.
+ if mp != nil && mp.profilehz == 0 {
+ return
+ }
+
// On mips{,le}, 64bit atomics are emulated with spinlocks, in
// runtime/internal/atomic. If SIGPROF arrives while the program is inside
// the critical section, it creates a deadlock (when writing the sample).
From b96d32bd92087470f85cfab99e289e609a593d03 Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills"
Date: Wed, 1 Jul 2020 00:45:34 -0400
Subject: [PATCH 0016/1082] cmd/go/internal/modload: track which packages are
in 'all' during loading
If the user requests the 'all' pattern in addition to explicit roots
outside of 'all', we should not load the transitive dependencies of
those explicit roots as if they were *in* 'all'. Without the '-test'
flag, we should not load test dependencies of any package outside of
'all'. Even *with* the '-test' flag, we should only load test
dependencies of the requested roots, not test dependencies of other
packages that happen to be imported by those roots.
More precise tracking of membership in 'all' will be important when we
implement lazy loading, because membership in 'all' determines which
module dependencies we will record in the main module's go.mod file.
This change also reduces reliance on global state, factors out the
loading process into several smaller functions, and sets us up to
reuse the 'go mod vendor' version of the 'all' pattern for lazy
loading.
For #36460
Fixes #40799
Change-Id: I5ca21c86a860daee1316f732cea131a331d8ddf9
Reviewed-on: https://go-review.googlesource.com/c/go/+/240505
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
Reviewed-by: Jay Conrod
Reviewed-by: Michael Matloob
---
src/cmd/go/internal/modload/load.go | 731 ++++++++++++++++------
src/cmd/go/testdata/script/mod_notall.txt | 4 +-
2 files changed, 527 insertions(+), 208 deletions(-)
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 2a37f1d8740..64ef60230e4 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -4,6 +4,95 @@
package modload
+// This file contains the module-mode package loader, as well as some accessory
+// functions pertaining to the package import graph.
+//
+// There are several exported entry points into package loading (such as
+// ImportPathsQuiet and LoadALL), but they are all implemented in terms of
+// loadFromRoots, which itself manipulates an instance of the loader struct.
+//
+// Although most of the loading state is maintained in the loader struct,
+// one key piece - the build list - is a global, so that it can be modified
+// separate from the loading operation, such as during "go get"
+// upgrades/downgrades or in "go mod" operations.
+// TODO(#40775): It might be nice to make the loader take and return
+// a buildList rather than hard-coding use of the global.
+//
+// Loading is an iterative process. On each iteration, we try to load the
+// requested packages and their transitive imports, then try to resolve modules
+// for any imported packages that are still missing.
+//
+// The first step of each iteration identifies a set of “root” packages.
+// Normally the root packages are exactly those matching the named pattern
+// arguments. However, for the "all" meta-pattern and related functions
+// (LoadALL, LoadVendor), the final set of packages is computed from the package
+// import graph, and therefore cannot be an initial input to loading that graph.
+// Instead, the root packages for the "all" pattern are those contained in the
+// main module, and allPatternIsRoot parameter to the loader instructs it to
+// dynamically expand those roots to the full "all" pattern as loading
+// progresses.
+//
+// The pkgInAll flag on each loadPkg instance tracks whether that
+// package is known to match the "all" meta-pattern.
+// A package matches the "all" pattern if:
+// - it is in the main module, or
+// - it is imported by any test in the main module, or
+// - it is imported by another package in "all", or
+// - the main module specifies a go version ≤ 1.15, and the package is imported
+// by a *test of* another package in "all".
+//
+// When we implement lazy loading, we will record the modules providing packages
+// in "all" even when we are only loading individual packages, so we set the
+// pkgInAll flag regardless of the whether the "all" pattern is a root.
+// (This is necessary to maintain the “import invariant” described in
+// https://golang.org/design/36460-lazy-module-loading.)
+//
+// Because "go mod vendor" prunes out the tests of vendored packages, the
+// behavior of the "all" pattern with -mod=vendor in Go 1.11–1.15 is the same
+// as the "all" pattern (regardless of the -mod flag) in 1.16+.
+// The allClosesOverTests parameter to the loader indicates whether the "all"
+// pattern should close over tests (as in Go 1.11–1.15) or stop at only those
+// packages transitively imported by the packages and tests in the main module
+// ("all" in Go 1.16+ and "go mod vendor" in Go 1.11+).
+//
+// Note that it is possible for a loaded package NOT to be in "all" even when we
+// are loading the "all" pattern. For example, packages that are transitive
+// dependencies of other roots named on the command line must be loaded, but are
+// not in "all". (The mod_notall test illustrates this behavior.)
+// Similarly, if the LoadTests flag is set but the "all" pattern does not close
+// over test dependencies, then when we load the test of a package that is in
+// "all" but outside the main module, the dependencies of that test will not
+// necessarily themselves be in "all". That configuration does not arise in Go
+// 1.11–1.15, but it will be possible with lazy loading in Go 1.16+.
+//
+// Loading proceeds from the roots, using a parallel work-queue with a limit on
+// the amount of active work (to avoid saturating disks, CPU cores, and/or
+// network connections). Each package is added to the queue the first time it is
+// imported by another package. When we have finished identifying the imports of
+// a package, we add the test for that package if it is needed. A test may be
+// needed if:
+// - the package matches a root pattern and tests of the roots were requested, or
+// - the package is in the main module and the "all" pattern is requested
+// (because the "all" pattern includes the dependencies of tests in the main
+// module), or
+// - the package is in "all" and the definition of "all" we are using includes
+// dependencies of tests (as is the case in Go ≤1.15).
+//
+// After all available packages have been loaded, we examine the results to
+// identify any requested or imported packages that are still missing, and if
+// so, which modules we could add to the module graph in order to make the
+// missing packages available. We add those to the module graph and iterate,
+// until either all packages resolve successfully or we cannot identify any
+// module that would resolve any remaining missing package.
+//
+// If the main module is “tidy” (that is, if "go mod tidy" is a no-op for it)
+// and all requested packages are in "all", then loading completes in a single
+// iteration.
+// TODO(bcmills): We should also be able to load in a single iteration if the
+// requested packages all come from modules that are themselves tidy, regardless
+// of whether those packages are in "all". Today, that requires two iterations
+// if those packages are not found in existing dependencies of the main module.
+
import (
"bytes"
"context"
@@ -14,8 +103,12 @@ import (
"path"
pathpkg "path"
"path/filepath"
+ "reflect"
+ "runtime"
"sort"
"strings"
+ "sync"
+ "sync/atomic"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
@@ -43,10 +136,6 @@ var buildList []module.Version
// loaded is the most recently-used package loader.
// It holds details about individual packages.
-//
-// Note that loaded.buildList is only valid during a load operation;
-// afterward, it is copied back into the global buildList,
-// which should be used instead.
var loaded *loader
// ImportPaths returns the set of packages matching the args (patterns),
@@ -63,7 +152,18 @@ func ImportPaths(ctx context.Context, patterns []string) []*search.Match {
// packages. The build tags should typically be imports.Tags() or
// imports.AnyTags(); a nil map has no special meaning.
func ImportPathsQuiet(ctx context.Context, patterns []string, tags map[string]bool) []*search.Match {
- updateMatches := func(matches []*search.Match, iterating bool) {
+ InitMod(ctx)
+
+ allPatternIsRoot := false
+ var matches []*search.Match
+ for _, pattern := range search.CleanPatterns(patterns) {
+ matches = append(matches, search.NewMatch(pattern))
+ if pattern == "all" {
+ allPatternIsRoot = true
+ }
+ }
+
+ updateMatches := func(ld *loader) {
for _, m := range matches {
switch {
case m.IsLocal():
@@ -90,7 +190,7 @@ func ImportPathsQuiet(ctx context.Context, patterns []string, tags map[string]bo
// indicates that.
ModRoot()
- if !iterating {
+ if ld != nil {
m.AddError(err)
}
continue
@@ -103,19 +203,18 @@ func ImportPathsQuiet(ctx context.Context, patterns []string, tags map[string]bo
case strings.Contains(m.Pattern(), "..."):
m.Errs = m.Errs[:0]
- matchPackages(ctx, m, loaded.tags, includeStd, buildList)
+ matchPackages(ctx, m, tags, includeStd, buildList)
case m.Pattern() == "all":
- loaded.testAll = true
- if iterating {
- // Enumerate the packages in the main module.
- // We'll load the dependencies as we find them.
+ if ld == nil {
+ // The initial roots are the packages in the main module.
+ // loadFromRoots will expand that to "all".
m.Errs = m.Errs[:0]
- matchPackages(ctx, m, loaded.tags, omitStd, []module.Version{Target})
+ matchPackages(ctx, m, tags, omitStd, []module.Version{Target})
} else {
// Starting with the packages in the main module,
// enumerate the full list of "all".
- m.Pkgs = loaded.computePatternAll(m.Pkgs)
+ m.Pkgs = ld.computePatternAll()
}
case m.Pattern() == "std" || m.Pattern() == "cmd":
@@ -129,25 +228,22 @@ func ImportPathsQuiet(ctx context.Context, patterns []string, tags map[string]bo
}
}
- InitMod(ctx)
+ loaded = loadFromRoots(loaderParams{
+ tags: tags,
+ allPatternIsRoot: allPatternIsRoot,
+ allClosesOverTests: true, // until lazy loading in Go 1.16+
- var matches []*search.Match
- for _, pattern := range search.CleanPatterns(patterns) {
- matches = append(matches, search.NewMatch(pattern))
- }
-
- loaded = newLoader(tags)
- loaded.load(func() []string {
- var roots []string
- updateMatches(matches, true)
- for _, m := range matches {
- roots = append(roots, m.Pkgs...)
- }
- return roots
+ listRoots: func() (roots []string) {
+ updateMatches(nil)
+ for _, m := range matches {
+ roots = append(roots, m.Pkgs...)
+ }
+ return roots
+ },
})
// One last pass to finalize wildcards.
- updateMatches(matches, false)
+ updateMatches(loaded)
checkMultiplePaths()
WriteGoMod()
@@ -347,12 +443,14 @@ func ImportFromFiles(ctx context.Context, gofiles []string) {
base.Fatalf("go: %v", err)
}
- loaded = newLoader(tags)
- loaded.load(func() []string {
- var roots []string
- roots = append(roots, imports...)
- roots = append(roots, testImports...)
- return roots
+ loaded = loadFromRoots(loaderParams{
+ tags: tags,
+ listRoots: func() (roots []string) {
+ roots = append(roots, imports...)
+ roots = append(roots, testImports...)
+ return roots
+ },
+ allClosesOverTests: true, // until lazy loading.
})
WriteGoMod()
}
@@ -397,9 +495,14 @@ func LoadBuildList(ctx context.Context) []module.Version {
return buildList
}
+// ReloadBuildList resets the state of loaded packages, then loads and returns
+// the build list set in SetBuildList.
func ReloadBuildList() []module.Version {
- loaded = newLoader(imports.Tags())
- loaded.load(func() []string { return nil })
+ loaded = loadFromRoots(loaderParams{
+ tags: imports.Tags(),
+ listRoots: func() []string { return nil },
+ allClosesOverTests: true, // until lazy loading, but doesn't matter because the root list is empty.
+ })
return buildList
}
@@ -410,6 +513,7 @@ func ReloadBuildList() []module.Version {
// This set is useful for deciding whether a particular import is needed
// anywhere in a module.
func LoadALL(ctx context.Context) []string {
+ InitMod(ctx)
return loadAll(ctx, true)
}
@@ -418,20 +522,18 @@ func LoadALL(ctx context.Context) []string {
// ignored completely.
// This set is useful for identifying the which packages to include in a vendor directory.
func LoadVendor(ctx context.Context) []string {
+ InitMod(ctx)
return loadAll(ctx, false)
}
-func loadAll(ctx context.Context, testAll bool) []string {
- InitMod(ctx)
-
- loaded = newLoader(imports.AnyTags())
- loaded.isALL = true
- loaded.testAll = testAll
- if !testAll {
- loaded.testRoots = true
- }
- all := TargetPackages(ctx, "...")
- loaded.load(func() []string { return all.Pkgs })
+func loadAll(ctx context.Context, closeOverTests bool) []string {
+ inTarget := TargetPackages(ctx, "...")
+ loaded = loadFromRoots(loaderParams{
+ tags: imports.AnyTags(),
+ listRoots: func() []string { return inTarget.Pkgs },
+ allPatternIsRoot: true,
+ allClosesOverTests: closeOverTests,
+ })
checkMultiplePaths()
WriteGoMod()
@@ -443,7 +545,7 @@ func loadAll(ctx context.Context, testAll bool) []string {
}
paths = append(paths, pkg.path)
}
- for _, err := range all.Errs {
+ for _, err := range inTarget.Errs {
base.Errorf("%v", err)
}
base.ExitIfErrors()
@@ -604,75 +706,157 @@ func Lookup(parentPath string, parentIsStd bool, path string) (dir, realPath str
// the required packages for a particular build,
// checking that the packages are available in the module set,
// and updating the module set if needed.
-// Loading is an iterative process: try to load all the needed packages,
-// but if imports are missing, try to resolve those imports, and repeat.
-//
-// Although most of the loading state is maintained in the loader struct,
-// one key piece - the build list - is a global, so that it can be modified
-// separate from the loading operation, such as during "go get"
-// upgrades/downgrades or in "go mod" operations.
-// TODO(rsc): It might be nice to make the loader take and return
-// a buildList rather than hard-coding use of the global.
type loader struct {
- tags map[string]bool // tags for scanDir
- testRoots bool // include tests for roots
- isALL bool // created with LoadALL
- testAll bool // include tests for all packages
- forceStdVendor bool // if true, load standard-library dependencies from the vendor subtree
+ loaderParams
+
+ forceStdVendor bool // if true, load standard-library dependencies from the vendor subtree
+
+ work *par.Queue
// reset on each iteration
roots []*loadPkg
- pkgs []*loadPkg
- work *par.Work // current work queue
- pkgCache *par.Cache // map from string to *loadPkg
+ pkgCache *par.Cache // package path (string) → *loadPkg
+ pkgs []*loadPkg // transitive closure of loaded packages and tests; populated in buildStacks
// computed at end of iterations
direct map[string]bool // imported directly by main module
}
+type loaderParams struct {
+ tags map[string]bool // tags for scanDir
+ listRoots func() []string
+ allPatternIsRoot bool // Is the "all" pattern an additional root?
+ allClosesOverTests bool // Does the "all" pattern include the transitive closure of tests of packages in "all"?
+}
+
// LoadTests controls whether the loaders load tests of the root packages.
var LoadTests bool
-func newLoader(tags map[string]bool) *loader {
- ld := new(loader)
- ld.tags = tags
- ld.testRoots = LoadTests
-
- // Inside the "std" and "cmd" modules, we prefer to use the vendor directory
- // unless the command explicitly changes the module graph.
- if !targetInGorootSrc || (cfg.CmdName != "get" && !strings.HasPrefix(cfg.CmdName, "mod ")) {
- ld.forceStdVendor = true
+func (ld *loader) reset() {
+ select {
+ case <-ld.work.Idle():
+ default:
+ panic("loader.reset when not idle")
}
- return ld
-}
-
-func (ld *loader) reset() {
ld.roots = nil
- ld.pkgs = nil
- ld.work = new(par.Work)
ld.pkgCache = new(par.Cache)
+ ld.pkgs = nil
}
// A loadPkg records information about a single loaded package.
type loadPkg struct {
- path string // import path
+ // Populated at construction time:
+ path string // import path
+ testOf *loadPkg
+
+ // Populated at construction time and updated by (*loader).applyPkgFlags:
+ flags atomicLoadPkgFlags
+
+ // Populated by (*loader).load:
mod module.Version // module providing package
dir string // directory containing source code
- imports []*loadPkg // packages imported by this one
err error // error loading package
- stack *loadPkg // package importing this one in minimal import stack for this pkg
- test *loadPkg // package with test imports, if we need test
- testOf *loadPkg
- testImports []string // test-only imports, saved for use by pkg.test.
+ imports []*loadPkg // packages imported by this one
+ testImports []string // test-only imports, saved for use by pkg.test.
+ inStd bool
+
+ // Populated by (*loader).pkgTest:
+ testOnce sync.Once
+ test *loadPkg
+
+ // Populated by postprocessing in (*loader).buildStacks:
+ stack *loadPkg // package importing this one in minimal import stack for this pkg
+}
+
+// loadPkgFlags is a set of flags tracking metadata about a package.
+type loadPkgFlags int8
+
+const (
+ // pkgInAll indicates that the package is in the "all" package pattern,
+ // regardless of whether we are loading the "all" package pattern.
+ //
+ // When the pkgInAll flag and pkgImportsLoaded flags are both set, the caller
+ // who set the last of those flags must propagate the pkgInAll marking to all
+ // of the imports of the marked package.
+ //
+ // A test is marked with pkgInAll if that test would promote the packages it
+ // imports to be in "all" (such as when the test is itself within the main
+ // module, or when ld.allClosesOverTests is true).
+ pkgInAll loadPkgFlags = 1 << iota
+
+ // pkgIsRoot indicates that the package matches one of the root package
+ // patterns requested by the caller.
+ //
+ // If LoadTests is set, then when pkgIsRoot and pkgImportsLoaded are both set,
+ // the caller who set the last of those flags must populate a test for the
+ // package (in the pkg.test field).
+ //
+ // If the "all" pattern is included as a root, then non-test packages in "all"
+ // are also roots (and must be marked pkgIsRoot).
+ pkgIsRoot
+
+ // pkgImportsLoaded indicates that the imports and testImports fields of a
+ // loadPkg have been populated.
+ pkgImportsLoaded
+)
+
+// has reports whether all of the flags in cond are set in f.
+func (f loadPkgFlags) has(cond loadPkgFlags) bool {
+ return f&cond == cond
+}
+
+// An atomicLoadPkgFlags stores a loadPkgFlags for which individual flags can be
+// added atomically.
+type atomicLoadPkgFlags struct {
+ bits int32
+}
+
+// update sets the given flags in af (in addition to any flags already set).
+//
+// update returns the previous flag state so that the caller may determine which
+// flags were newly-set.
+func (af *atomicLoadPkgFlags) update(flags loadPkgFlags) (old loadPkgFlags) {
+ for {
+ old := atomic.LoadInt32(&af.bits)
+ new := old | int32(flags)
+ if new == old || atomic.CompareAndSwapInt32(&af.bits, old, new) {
+ return loadPkgFlags(old)
+ }
+ }
+}
+
+// has reports whether all of the flags in cond are set in af.
+func (af *atomicLoadPkgFlags) has(cond loadPkgFlags) bool {
+ return loadPkgFlags(atomic.LoadInt32(&af.bits))&cond == cond
+}
+
+// isTest reports whether pkg is a test of another package.
+func (pkg *loadPkg) isTest() bool {
+ return pkg.testOf != nil
}
var errMissing = errors.New("cannot find package")
-// load attempts to load the build graph needed to process a set of root packages.
-// The set of root packages is defined by the addRoots function,
-// which must call add(path) with the import path of each root package.
-func (ld *loader) load(roots func() []string) {
+// loadFromRoots attempts to load the build graph needed to process a set of
+// root packages and their dependencies.
+//
+// The set of root packages is returned by the params.listRoots function, and
+// expanded to the full set of packages by tracing imports (and possibly tests)
+// as needed.
+func loadFromRoots(params loaderParams) *loader {
+ ld := &loader{
+ loaderParams: params,
+ work: par.NewQueue(runtime.GOMAXPROCS(0)),
+ }
+
+ // Inside the "std" and "cmd" modules, we prefer to use the vendor directory
+ // unless the command explicitly changes the module graph.
+ // TODO(bcmills): Is this still needed now that we have automatic vendoring?
+ if !targetInGorootSrc || (cfg.CmdName != "get" && !strings.HasPrefix(cfg.CmdName, "mod ")) {
+ ld.forceStdVendor = true
+ }
+
var err error
reqs := Reqs()
buildList, err = mvs.BuildList(Target, reqs)
@@ -680,47 +864,34 @@ func (ld *loader) load(roots func() []string) {
base.Fatalf("go: %v", err)
}
- added := make(map[string]bool)
+ addedModuleFor := make(map[string]bool)
for {
ld.reset()
- if roots != nil {
- // Note: the returned roots can change on each iteration,
- // since the expansion of package patterns depends on the
- // build list we're using.
- for _, path := range roots() {
- ld.work.Add(ld.pkg(path, true))
+
+ // Load the root packages and their imports.
+ // Note: the returned roots can change on each iteration,
+ // since the expansion of package patterns depends on the
+ // build list we're using.
+ inRoots := map[*loadPkg]bool{}
+ for _, path := range ld.listRoots() {
+ root := ld.pkg(path, pkgIsRoot)
+ if !inRoots[root] {
+ ld.roots = append(ld.roots, root)
+ inRoots[root] = true
}
}
- ld.work.Do(10, ld.doPkg)
+
+ // ld.pkg adds imported packages to the work queue and calls applyPkgFlags,
+ // which adds tests (and test dependencies) as needed.
+ //
+ // When all of the work in the queue has completed, we'll know that the
+ // transitive closure of dependencies has been loaded.
+ <-ld.work.Idle()
+
ld.buildStacks()
- numAdded := 0
- haveMod := make(map[module.Version]bool)
- for _, m := range buildList {
- haveMod[m] = true
- }
- modAddedBy := make(map[module.Version]*loadPkg)
- for _, pkg := range ld.pkgs {
- if err, ok := pkg.err.(*ImportMissingError); ok && err.Module.Path != "" {
- if err.newMissingVersion != "" {
- base.Fatalf("go: %s: package provided by %s at latest version %s but not at required version %s", pkg.stackText(), err.Module.Path, err.Module.Version, err.newMissingVersion)
- }
- fmt.Fprintf(os.Stderr, "go: found %s in %s %s\n", pkg.path, err.Module.Path, err.Module.Version)
- if added[pkg.path] {
- base.Fatalf("go: %s: looping trying to add package", pkg.stackText())
- }
- added[pkg.path] = true
- numAdded++
- if !haveMod[err.Module] {
- haveMod[err.Module] = true
- modAddedBy[err.Module] = pkg
- buildList = append(buildList, err.Module)
- }
- continue
- }
- // Leave other errors for Import or load.Packages to report.
- }
- base.ExitIfErrors()
- if numAdded == 0 {
+
+ modAddedBy := resolveMissingImports(addedModuleFor, ld.pkgs)
+ if len(modAddedBy) == 0 {
break
}
@@ -753,92 +924,257 @@ func (ld *loader) load(roots func() []string) {
}
}
- // Mix in direct markings (really, lack of indirect markings)
- // from go.mod, unless we scanned the whole module
- // and can therefore be sure we know better than go.mod.
- if !ld.isALL && modFile != nil {
+ // If we didn't scan all of the imports from the main module, or didn't use
+ // imports.AnyTags, then we didn't necessarily load every package that
+ // contributes “direct” imports — so we can't safely mark existing
+ // dependencies as indirect-only.
+ // Conservatively mark those dependencies as direct.
+ if modFile != nil && (!ld.allPatternIsRoot || !reflect.DeepEqual(ld.tags, imports.AnyTags())) {
for _, r := range modFile.Require {
if !r.Indirect {
ld.direct[r.Mod.Path] = true
}
}
}
+
+ return ld
}
-// pkg returns the *loadPkg for path, creating and queuing it if needed.
-// If the package should be tested, its test is created but not queued
-// (the test is queued after processing pkg).
-// If isRoot is true, the pkg is being queued as one of the roots of the work graph.
-func (ld *loader) pkg(path string, isRoot bool) *loadPkg {
- return ld.pkgCache.Do(path, func() interface{} {
+// resolveMissingImports adds module dependencies to the global build list
+// in order to resolve missing packages from pkgs.
+//
+// The newly-resolved packages are added to the addedModuleFor map, and
+// resolveMissingImports returns a map from each newly-added module version to
+// the first package for which that module was added.
+func resolveMissingImports(addedModuleFor map[string]bool, pkgs []*loadPkg) (modAddedBy map[module.Version]*loadPkg) {
+ haveMod := make(map[module.Version]bool)
+ for _, m := range buildList {
+ haveMod[m] = true
+ }
+
+ modAddedBy = make(map[module.Version]*loadPkg)
+ for _, pkg := range pkgs {
+ if pkg.isTest() {
+ // If we are missing a test, we are also missing its non-test version, and
+ // we should only add the missing import once.
+ continue
+ }
+ if err, ok := pkg.err.(*ImportMissingError); ok && err.Module.Path != "" {
+ if err.newMissingVersion != "" {
+ base.Fatalf("go: %s: package provided by %s at latest version %s but not at required version %s", pkg.stackText(), err.Module.Path, err.Module.Version, err.newMissingVersion)
+ }
+ fmt.Fprintf(os.Stderr, "go: found %s in %s %s\n", pkg.path, err.Module.Path, err.Module.Version)
+ if addedModuleFor[pkg.path] {
+ base.Fatalf("go: %s: looping trying to add package", pkg.stackText())
+ }
+ addedModuleFor[pkg.path] = true
+ if !haveMod[err.Module] {
+ haveMod[err.Module] = true
+ modAddedBy[err.Module] = pkg
+ buildList = append(buildList, err.Module)
+ }
+ continue
+ }
+ // Leave other errors for Import or load.Packages to report.
+ }
+ base.ExitIfErrors()
+
+ return modAddedBy
+}
+
+// pkg locates the *loadPkg for path, creating and queuing it for loading if
+// needed, and updates its state to reflect the given flags.
+//
+// The imports of the returned *loadPkg will be loaded asynchronously in the
+// ld.work queue, and its test (if requested) will also be populated once
+// imports have been resolved. When ld.work goes idle, all transitive imports of
+// the requested package (and its test, if requested) will have been loaded.
+func (ld *loader) pkg(path string, flags loadPkgFlags) *loadPkg {
+ if flags.has(pkgImportsLoaded) {
+ panic("internal error: (*loader).pkg called with pkgImportsLoaded flag set")
+ }
+
+ pkg := ld.pkgCache.Do(path, func() interface{} {
pkg := &loadPkg{
path: path,
}
- if ld.testRoots && isRoot || ld.testAll {
- test := &loadPkg{
- path: path,
- testOf: pkg,
- }
- pkg.test = test
- }
- if isRoot {
- ld.roots = append(ld.roots, pkg)
- }
- ld.work.Add(pkg)
+ ld.applyPkgFlags(pkg, flags)
+
+ ld.work.Add(func() { ld.load(pkg) })
return pkg
}).(*loadPkg)
+
+ ld.applyPkgFlags(pkg, flags)
+ return pkg
}
-// doPkg processes a package on the work queue.
-func (ld *loader) doPkg(item interface{}) {
- // TODO: what about replacements?
- pkg := item.(*loadPkg)
- var imports []string
- if pkg.testOf != nil {
- pkg.dir = pkg.testOf.dir
- pkg.mod = pkg.testOf.mod
- imports = pkg.testOf.testImports
- } else {
- if strings.Contains(pkg.path, "@") {
- // Leave for error during load.
- return
- }
- if build.IsLocalImport(pkg.path) || filepath.IsAbs(pkg.path) {
- // Leave for error during load.
- // (Module mode does not allow local imports.)
- return
+// applyPkgFlags updates pkg.flags to set the given flags and propagate the
+// (transitive) effects of those flags, possibly loading or enqueueing further
+// packages as a result.
+func (ld *loader) applyPkgFlags(pkg *loadPkg, flags loadPkgFlags) {
+ if flags == 0 {
+ return
+ }
+
+ if flags.has(pkgInAll) && ld.allPatternIsRoot && !pkg.isTest() {
+ // This package matches a root pattern by virtue of being in "all".
+ flags |= pkgIsRoot
+ }
+
+ old := pkg.flags.update(flags)
+ new := old | flags
+ if new == old || !new.has(pkgImportsLoaded) {
+ // We either didn't change the state of pkg, or we don't know anything about
+ // its dependencies yet. Either way, we can't usefully load its test or
+ // update its dependencies.
+ return
+ }
+
+ if !pkg.isTest() {
+ // Check whether we should add (or update the flags for) a test for pkg.
+ // ld.pkgTest is idempotent and extra invocations are inexpensive,
+ // so it's ok if we call it more than is strictly necessary.
+ wantTest := false
+ switch {
+ case ld.allPatternIsRoot && pkg.mod == Target:
+ // We are loading the "all" pattern, which includes packages imported by
+ // tests in the main module. This package is in the main module, so we
+ // need to identify the imports of its test even if LoadTests is not set.
+ //
+ // (We will filter out the extra tests explicitly in computePatternAll.)
+ wantTest = true
+
+ case ld.allPatternIsRoot && ld.allClosesOverTests && new.has(pkgInAll):
+ // This variant of the "all" pattern includes imports of tests of every
+ // package that is itself in "all", and pkg is in "all", so its test is
+ // also in "all" (as above).
+ wantTest = true
+
+ case LoadTests && new.has(pkgIsRoot):
+ // LoadTest explicitly requests tests of “the root packages”.
+ wantTest = true
}
- // TODO(matloob): Handle TODO context. This needs to be threaded through Do.
- pkg.mod, pkg.dir, pkg.err = Import(context.TODO(), pkg.path)
- if pkg.dir == "" {
- return
- }
- var testImports []string
- var err error
- imports, testImports, err = scanDir(pkg.dir, ld.tags)
- if err != nil {
- pkg.err = err
- return
- }
- if pkg.test != nil {
- pkg.testImports = testImports
+ if wantTest {
+ var testFlags loadPkgFlags
+ if pkg.mod == Target || (ld.allClosesOverTests && new.has(pkgInAll)) {
+ // Tests of packages in the main module are in "all", in the sense that
+ // they cause the packages they import to also be in "all". So are tests
+ // of packages in "all" if "all" closes over test dependencies.
+ testFlags |= pkgInAll
+ }
+ ld.pkgTest(pkg, testFlags)
}
}
- inStd := (search.IsStandardImportPath(pkg.path) && search.InDir(pkg.dir, cfg.GOROOTsrc) != "")
+ if new.has(pkgInAll) && !old.has(pkgInAll|pkgImportsLoaded) {
+ // We have just marked pkg with pkgInAll, or we have just loaded its
+ // imports, or both. Now is the time to propagate pkgInAll to the imports.
+ for _, dep := range pkg.imports {
+ ld.applyPkgFlags(dep, pkgInAll)
+ }
+ }
+}
+
+// load loads an individual package.
+func (ld *loader) load(pkg *loadPkg) {
+ if strings.Contains(pkg.path, "@") {
+ // Leave for error during load.
+ return
+ }
+ if build.IsLocalImport(pkg.path) || filepath.IsAbs(pkg.path) {
+ // Leave for error during load.
+ // (Module mode does not allow local imports.)
+ return
+ }
+
+ pkg.mod, pkg.dir, pkg.err = Import(context.TODO(), pkg.path)
+ if pkg.dir == "" {
+ return
+ }
+ if pkg.mod == Target {
+ // Go ahead and mark pkg as in "all". This provides the invariant that a
+ // package that is *only* imported by other packages in "all" is always
+ // marked as such before loading its imports.
+ //
+ // We don't actually rely on that invariant at the moment, but it may
+ // improve efficiency somewhat and makes the behavior a bit easier to reason
+ // about (by reducing churn on the flag bits of dependencies), and costs
+ // essentially nothing (these atomic flag ops are essentially free compared
+ // to scanning source code for imports).
+ ld.applyPkgFlags(pkg, pkgInAll)
+ }
+
+ imports, testImports, err := scanDir(pkg.dir, ld.tags)
+ if err != nil {
+ pkg.err = err
+ return
+ }
+
+ pkg.inStd = (search.IsStandardImportPath(pkg.path) && search.InDir(pkg.dir, cfg.GOROOTsrc) != "")
+
+ pkg.imports = make([]*loadPkg, 0, len(imports))
+ var importFlags loadPkgFlags
+ if pkg.flags.has(pkgInAll) {
+ importFlags = pkgInAll
+ }
for _, path := range imports {
- if inStd {
+ if pkg.inStd {
+ // Imports from packages in "std" should resolve using GOROOT/src/vendor
+ // even when "std" is not the main module.
path = ld.stdVendor(pkg.path, path)
}
- pkg.imports = append(pkg.imports, ld.pkg(path, false))
+ pkg.imports = append(pkg.imports, ld.pkg(path, importFlags))
+ }
+ pkg.testImports = testImports
+
+ ld.applyPkgFlags(pkg, pkgImportsLoaded)
+}
+
+// pkgTest locates the test of pkg, creating it if needed, and updates its state
+// to reflect the given flags.
+//
+// pkgTest requires that the imports of pkg have already been loaded (flagged
+// with pkgImportsLoaded).
+func (ld *loader) pkgTest(pkg *loadPkg, testFlags loadPkgFlags) *loadPkg {
+ if pkg.isTest() {
+ panic("pkgTest called on a test package")
}
- // Now that pkg.dir, pkg.mod, pkg.testImports are set, we can queue pkg.test.
- // TODO: All that's left is creating new imports. Why not just do it now?
- if pkg.test != nil {
- ld.work.Add(pkg.test)
+ createdTest := false
+ pkg.testOnce.Do(func() {
+ pkg.test = &loadPkg{
+ path: pkg.path,
+ testOf: pkg,
+ mod: pkg.mod,
+ dir: pkg.dir,
+ err: pkg.err,
+ inStd: pkg.inStd,
+ }
+ ld.applyPkgFlags(pkg.test, testFlags)
+ createdTest = true
+ })
+
+ test := pkg.test
+ if createdTest {
+ test.imports = make([]*loadPkg, 0, len(pkg.testImports))
+ var importFlags loadPkgFlags
+ if test.flags.has(pkgInAll) {
+ importFlags = pkgInAll
+ }
+ for _, path := range pkg.testImports {
+ if pkg.inStd {
+ path = ld.stdVendor(test.path, path)
+ }
+ test.imports = append(test.imports, ld.pkg(path, importFlags))
+ }
+ pkg.testImports = nil
+ ld.applyPkgFlags(test, pkgImportsLoaded)
+ } else {
+ ld.applyPkgFlags(test, testFlags)
}
+
+ return test
}
// stdVendor returns the canonical import path for the package with the given
@@ -868,30 +1204,13 @@ func (ld *loader) stdVendor(parentPath, path string) string {
// computePatternAll returns the list of packages matching pattern "all",
// starting with a list of the import paths for the packages in the main module.
-func (ld *loader) computePatternAll(paths []string) []string {
- seen := make(map[*loadPkg]bool)
- var all []string
- var walk func(*loadPkg)
- walk = func(pkg *loadPkg) {
- if seen[pkg] {
- return
- }
- seen[pkg] = true
- if pkg.testOf == nil {
+func (ld *loader) computePatternAll() (all []string) {
+ for _, pkg := range ld.pkgs {
+ if pkg.flags.has(pkgInAll) && !pkg.isTest() {
all = append(all, pkg.path)
}
- for _, p := range pkg.imports {
- walk(p)
- }
- if p := pkg.test; p != nil {
- walk(p)
- }
- }
- for _, path := range paths {
- walk(ld.pkg(path, false))
}
sort.Strings(all)
-
return all
}
diff --git a/src/cmd/go/testdata/script/mod_notall.txt b/src/cmd/go/testdata/script/mod_notall.txt
index 72a02485a43..29ca6066fa4 100644
--- a/src/cmd/go/testdata/script/mod_notall.txt
+++ b/src/cmd/go/testdata/script/mod_notall.txt
@@ -18,7 +18,7 @@ stdout '^x/otherdep$'
! stdout '^x/fromotherroottest$'
! stdout '^y/fromotherdeptest$'
-# TODO(#40799): cmp go.mod go.mod.orig
+cmp go.mod go.mod.orig
# With -deps -test, test dependencies of other roots should be included,
# but test dependencies of non-roots should not.
@@ -33,7 +33,7 @@ stdout '^x/otherdep$'
stdout '^x/fromotherroottest$'
! stdout '^y/fromotherdeptest$'
-# TODO(#40799): cmp go.mod go.mod.orig
+cmp go.mod go.mod.orig
-- m.go --
package m
From 26d27f96fec733fe09751b49b47282c9109fb8ad Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills"
Date: Thu, 27 Aug 2020 16:34:59 -0400
Subject: [PATCH 0017/1082] cmd/go/internal/modload: remove
(*loader).forceStdVendor
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
forceStdVendor was a special-case mechanism to allow Go contributors
to use vendored dependencies by default when working in GOROOT/src.
As of Go 1.14,¹ the 'go' command uses vendored dependencies by default
within all modules, so the 'std' and 'cmd' modules no longer need to
be special cases, and we can remove this special-case code.
¹ https://golang.org/doc/go1.14#vendor
Updates #33848
Updates #30241
Change-Id: Ib2fb5841c253113b17fa86a086ce85a22ac3d121
Reviewed-on: https://go-review.googlesource.com/c/go/+/251159
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
Reviewed-by: Jay Conrod
Reviewed-by: Michael Matloob
---
src/cmd/go/internal/modload/load.go | 25 ++++---
src/cmd/go/testdata/script/mod_list_std.txt | 66 ++++++++++++-------
src/cmd/go/testdata/script/mod_std_vendor.txt | 6 +-
3 files changed, 58 insertions(+), 39 deletions(-)
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 64ef60230e4..8a3af534a57 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -709,8 +709,6 @@ func Lookup(parentPath string, parentIsStd bool, path string) (dir, realPath str
type loader struct {
loaderParams
- forceStdVendor bool // if true, load standard-library dependencies from the vendor subtree
-
work *par.Queue
// reset on each iteration
@@ -850,13 +848,6 @@ func loadFromRoots(params loaderParams) *loader {
work: par.NewQueue(runtime.GOMAXPROCS(0)),
}
- // Inside the "std" and "cmd" modules, we prefer to use the vendor directory
- // unless the command explicitly changes the module graph.
- // TODO(bcmills): Is this still needed now that we have automatic vendoring?
- if !targetInGorootSrc || (cfg.CmdName != "get" && !strings.HasPrefix(cfg.CmdName, "mod ")) {
- ld.forceStdVendor = true
- }
-
var err error
reqs := Reqs()
buildList, err = mvs.BuildList(Target, reqs)
@@ -1120,8 +1111,8 @@ func (ld *loader) load(pkg *loadPkg) {
}
for _, path := range imports {
if pkg.inStd {
- // Imports from packages in "std" should resolve using GOROOT/src/vendor
- // even when "std" is not the main module.
+ // Imports from packages in "std" and "cmd" should resolve using
+ // GOROOT/src/vendor even when "std" is not the main module.
path = ld.stdVendor(pkg.path, path)
}
pkg.imports = append(pkg.imports, ld.pkg(path, importFlags))
@@ -1185,13 +1176,21 @@ func (ld *loader) stdVendor(parentPath, path string) string {
}
if str.HasPathPrefix(parentPath, "cmd") {
- if ld.forceStdVendor || Target.Path != "cmd" {
+ if Target.Path != "cmd" {
vendorPath := pathpkg.Join("cmd", "vendor", path)
if _, err := os.Stat(filepath.Join(cfg.GOROOTsrc, filepath.FromSlash(vendorPath))); err == nil {
return vendorPath
}
}
- } else if ld.forceStdVendor || Target.Path != "std" {
+ } else if Target.Path != "std" || str.HasPathPrefix(parentPath, "vendor") {
+ // If we are outside of the 'std' module, resolve imports from within 'std'
+ // to the vendor directory.
+ //
+ // Do the same for importers beginning with the prefix 'vendor/' even if we
+ // are *inside* of the 'std' module: the 'vendor/' packages that resolve
+ // globally from GOROOT/src/vendor (and are listed as part of 'go list std')
+ // are distinct from the real module dependencies, and cannot import internal
+ // packages from the real module.
vendorPath := pathpkg.Join("vendor", path)
if _, err := os.Stat(filepath.Join(cfg.GOROOTsrc, filepath.FromSlash(vendorPath))); err == nil {
return vendorPath
diff --git a/src/cmd/go/testdata/script/mod_list_std.txt b/src/cmd/go/testdata/script/mod_list_std.txt
index 76a3b00d1c9..baf7908ab93 100644
--- a/src/cmd/go/testdata/script/mod_list_std.txt
+++ b/src/cmd/go/testdata/script/mod_list_std.txt
@@ -6,8 +6,13 @@ env GOPROXY=off
# Outside of GOROOT, our vendored packages should be reported as part of the standard library.
go list -f '{{if .Standard}}{{.ImportPath}}{{end}}' std cmd
-stdout ^vendor/golang.org/x/net/http2/hpack
+stdout ^vendor/golang\.org/x/net/http2/hpack
stdout ^cmd/vendor/golang\.org/x/arch/x86/x86asm
+! stdout ^golang\.org/x/
+
+# The dependencies of those packages should also be vendored.
+go list -deps vendor/golang.org/x/crypto/chacha20
+stdout ^vendor/golang\.org/x/crypto/internal/subtle
# cmd/... should match the same packages it used to match in GOPATH mode.
go list cmd/...
@@ -23,40 +28,57 @@ stdout ^bytes$
! stdout ^builtin$
! stdout ^cmd/
! stdout ^vendor/
+! stdout ^golang\.org/x/
-# Within the std module, listing ./... should omit the 'std' prefix:
-# the package paths should be the same via ./... or the 'std' meta-pattern.
-# TODO(golang.org/issue/30241): Make that work.
-# Today, they are listed in 'std' but not './...'.
+# Vendored dependencies should appear with their 'vendor/' paths in std (they're
+# in GOROOT/src, but not in the 'std' module following the usual module-boundary
+# rules).
+
cd $GOROOT/src
-go list ./...
-! stdout ^vendor/golang.org/x # TODO: should be included, or should be omitted from 'std'.
-cp stdout $WORK/listdot.txt
go list std
-stdout ^vendor/golang.org/x # TODO: remove vendor/ prefix
-# TODO: cmp stdout $WORK/listdot.txt
+stdout ^vendor/golang.org/x/net/http2/hpack
+! stdout ^golang\.org/x
+
+# The dependencies of packages with an explicit 'vendor/' prefix should
+# still themselves resolve to vendored packages.
+go list -deps vendor/golang.org/x/crypto/chacha20
+stdout ^vendor/golang.org/x/crypto/internal/subtle
+! stdout ^golang\.org/x
+
+# Within the std module, the dependencies of the non-vendored packages within
+# std should appear to come from modules, but they should be loaded from the
+# vendor directory (just like ordinary vendored module dependencies).
go list all
-stdout ^vendor/golang.org/x # TODO: remove vendor/ prefix.
+stdout ^golang.org/x/
! stdout ^std/
-
-
-# Within the std module, the vendored dependencies of std should appear
-# to come from the actual modules.
-# TODO(golang.org/issue/30241): Make that work.
-# Today, they still have the vendor/ prefix.
-go list std
-stdout ^vendor/golang.org/x/net/http2/hpack # TODO
-! stdout ^golang.org/x/net/http2/hpack # TODO
+! stdout ^cmd/
+! stdout ^vendor/
go list -deps -f '{{if not .Standard}}{{.ImportPath}}{{end}}' std
-# ! stdout ^vendor/golang.org/x/net/http2/hpack # TODO
-! stdout ^golang.org/x/net/http2/hpack # TODO
+! stdout ^vendor/golang.org/x/net/http2/hpack
+stdout ^golang.org/x/net/http2/hpack
+
+go list -f '{{.Dir}}' golang.org/x/net/http2/hpack
+stdout $GOROOT[/\\]src[/\\]vendor
+
+# Within the std module, the packages within the module should omit the 'std/'
+# prefix (they retain their own identities), but should respect normal module
+# boundaries (vendored packages are not included in the module, even though they
+# are included in the 'std' pattern).
+
+go list ./...
+stdout ^bytes$
+! stdout ^builtin$
+! stdout ^cmd/
+! stdout ^vendor/
+! stdout ^golang\.org/x/
# Within std, the vendored dependencies of cmd should still appear to be part of cmd.
+
go list -f '{{if .Standard}}{{.ImportPath}}{{end}}' cmd
stdout ^cmd/vendor/golang\.org/x/arch/x86/x86asm
diff --git a/src/cmd/go/testdata/script/mod_std_vendor.txt b/src/cmd/go/testdata/script/mod_std_vendor.txt
index 5986cff5946..fb954d74edb 100644
--- a/src/cmd/go/testdata/script/mod_std_vendor.txt
+++ b/src/cmd/go/testdata/script/mod_std_vendor.txt
@@ -37,12 +37,10 @@ stderr 'use of vendored package'
# When run within the 'std' module, 'go list -test' should report vendored
# transitive dependencies at their original module paths.
-# TODO(golang.org/issue/30241): Make that work.
-# Today, they're standard packages as long as they exist.
cd $GOROOT/src
go list -test -f '{{range .Deps}}{{.}}{{"\n"}}{{end}}' net/http
-stdout ^vendor/golang.org/x/net/http2/hpack # TODO: remove vendor/ prefix
-! stdout ^golang.org/x/net/http2/hpack
+stdout ^golang.org/x/net/http2/hpack
+! stdout ^vendor/golang.org/x/net/http2/hpack
-- go.mod --
module m
From 015a5a5c5c4b4ce4dce55601032b8e2f5fbcca9a Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills"
Date: Fri, 28 Aug 2020 18:19:22 -0400
Subject: [PATCH 0018/1082] cmd/go/internal/modload: rework import resolution
modload.Import previously performed two otherwise-separable tasks:
1. Identify which module in the build list contains the requested
package.
2. If no such module exists, search available modules to try to find
the missing package.
This change splits those two tasks into two separate unexported
functions, and reports import-resolution errors by attaching them to
the package rather than emitting them directly to stderr. That allows
'list' to report the errors, but 'list -e' to ignore them.
With the two tasks now separate, it will be easier to avoid the
overhead of resolving missing packages during lazy loading if we
discover that some existing dependency needs to be promoted to the top
level (potentially altering the main module's selected versions, and
thus suppling packages that were previously missing).
For #36460
Updates #26909
Change-Id: I32bd853b266d7cd231d1f45f92b0650d95c4bcbd
Reviewed-on: https://go-review.googlesource.com/c/go/+/251445
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
Reviewed-by: Jay Conrod
Reviewed-by: Michael Matloob
---
src/cmd/go/internal/list/list.go | 21 +++++-
src/cmd/go/internal/modload/import.go | 71 ++++++++++++-------
src/cmd/go/internal/modload/import_test.go | 44 +++++++++---
src/cmd/go/internal/modload/load.go | 59 ++++++++-------
.../go/testdata/script/list_bad_import.txt | 18 ++---
src/cmd/go/testdata/script/list_test_err.txt | 3 +
.../testdata/script/mod_list_bad_import.txt | 18 ++---
.../script/mod_missingpkg_prerelease.txt | 4 +-
8 files changed, 158 insertions(+), 80 deletions(-)
diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go
index 6d81c1cad1d..65003dc883a 100644
--- a/src/cmd/go/internal/list/list.go
+++ b/src/cmd/go/internal/list/list.go
@@ -545,7 +545,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
// Note that -deps is applied after -test,
// so that you only get descriptions of tests for the things named
// explicitly on the command line, not for all dependencies.
- pkgs = load.PackageList(pkgs)
+ pkgs = loadPackageList(pkgs)
}
// Do we need to run a build to gather information?
@@ -580,7 +580,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
if *listTest {
all := pkgs
if !*listDeps {
- all = load.PackageList(pkgs)
+ all = loadPackageList(pkgs)
}
// Update import paths to distinguish the real package p
// from p recompiled for q.test.
@@ -697,6 +697,23 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
}
}
+// loadPackageList is like load.PackageList, but prints error messages and exits
+// with nonzero status if listE is not set and any package in the expanded list
+// has errors.
+func loadPackageList(roots []*load.Package) []*load.Package {
+ pkgs := load.PackageList(roots)
+
+ if !*listE {
+ for _, pkg := range pkgs {
+ if pkg.Error != nil {
+ base.Errorf("%v", pkg.Error)
+ }
+ }
+ }
+
+ return pkgs
+}
+
// TrackingWriter tracks the last byte written on every write so
// we can avoid printing a newline if one was already written or
// if there is no output at all.
diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go
index 6459e716b77..e04d66c5b1b 100644
--- a/src/cmd/go/internal/modload/import.go
+++ b/src/cmd/go/internal/modload/import.go
@@ -26,6 +26,8 @@ import (
"golang.org/x/mod/semver"
)
+var errImportMissing = errors.New("import missing")
+
type ImportMissingError struct {
Path string
Module module.Version
@@ -48,6 +50,11 @@ func (e *ImportMissingError) Error() string {
}
return "cannot find module providing package " + e.Path
}
+
+ if e.newMissingVersion != "" {
+ return fmt.Sprintf("package %s provided by %s at latest version %s but not at required version %s", e.Path, e.Module.Path, e.Module.Version, e.newMissingVersion)
+ }
+
return fmt.Sprintf("missing module for import: %s@%s provides %s", e.Module.Path, e.Module.Version, e.Path)
}
@@ -100,18 +107,20 @@ func (e *AmbiguousImportError) Error() string {
var _ load.ImportPathError = &AmbiguousImportError{}
-// Import finds the module and directory in the build list
-// containing the package with the given import path.
-// The answer must be unique: Import returns an error
-// if multiple modules attempt to provide the same package.
-// Import can return a module with an empty m.Path, for packages in the standard library.
-// Import can return an empty directory string, for fake packages like "C" and "unsafe".
+// importFromBuildList finds the module and directory in the build list
+// containing the package with the given import path. The answer must be unique:
+// importFromBuildList returns an error if multiple modules attempt to provide
+// the same package.
+//
+// importFromBuildList can return a module with an empty m.Path, for packages in
+// the standard library.
+//
+// importFromBuildList can return an empty directory string, for fake packages
+// like "C" and "unsafe".
//
// If the package cannot be found in the current build list,
-// Import returns an ImportMissingError as the error.
-// If Import can identify a module that could be added to supply the package,
-// the ImportMissingError records that module.
-func Import(ctx context.Context, path string) (m module.Version, dir string, err error) {
+// importFromBuildList returns errImportMissing as the error.
+func importFromBuildList(ctx context.Context, path string) (m module.Version, dir string, err error) {
if strings.Contains(path, "@") {
return module.Version{}, "", fmt.Errorf("import path should not have @version")
}
@@ -190,8 +199,14 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
}
- // Look up module containing the package, for addition to the build list.
- // Goal is to determine the module, download it to dir, and return m, dir, ErrMissing.
+ return module.Version{}, "", errImportMissing
+}
+
+// queryImport attempts to locate a module that can be added to the current
+// build list to provide the package with the given import path.
+func queryImport(ctx context.Context, path string) (module.Version, error) {
+ pathIsStd := search.IsStandardImportPath(path)
+
if cfg.BuildMod == "readonly" {
var queryErr error
if !pathIsStd {
@@ -201,10 +216,10 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
queryErr = fmt.Errorf("import lookup disabled by -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
}
}
- return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: queryErr}
+ return module.Version{}, &ImportMissingError{Path: path, QueryErr: queryErr}
}
if modRoot == "" && !allowMissingModuleImports {
- return module.Version{}, "", &ImportMissingError{
+ return module.Version{}, &ImportMissingError{
Path: path,
QueryErr: errors.New("working directory is not part of a module"),
}
@@ -226,7 +241,7 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
}
}
- mods = make([]module.Version, 0, len(latest))
+ mods := make([]module.Version, 0, len(latest))
for p, v := range latest {
// If the replacement didn't specify a version, synthesize a
// pseudo-version with an appropriate major version and a timestamp below
@@ -252,19 +267,19 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
root, isLocal, err := fetch(ctx, m)
if err != nil {
// Report fetch error as above.
- return module.Version{}, "", err
+ return module.Version{}, err
}
if _, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
- return m, "", err
+ return m, err
} else if ok {
- return m, "", &ImportMissingError{Path: path, Module: m}
+ return m, nil
}
}
if len(mods) > 0 && module.CheckPath(path) != nil {
// The package path is not valid to fetch remotely,
// so it can only exist if in a replaced module,
// and we know from the above loop that it is not.
- return module.Version{}, "", &PackageNotInModuleError{
+ return module.Version{}, &PackageNotInModuleError{
Mod: mods[0],
Query: "latest",
Pattern: path,
@@ -281,7 +296,7 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
// QueryPackage cannot possibly find a module containing this package.
//
// Instead of trying QueryPackage, report an ImportMissingError immediately.
- return module.Version{}, "", &ImportMissingError{Path: path}
+ return module.Version{}, &ImportMissingError{Path: path}
}
fmt.Fprintf(os.Stderr, "go: finding module for package %s\n", path)
@@ -291,12 +306,13 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
if errors.Is(err, os.ErrNotExist) {
// Return "cannot find module providing package […]" instead of whatever
// low-level error QueryPackage produced.
- return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: err}
+ return module.Version{}, &ImportMissingError{Path: path, QueryErr: err}
} else {
- return module.Version{}, "", err
+ return module.Version{}, err
}
}
- m = candidates[0].Mod
+
+ m := candidates[0].Mod
newMissingVersion := ""
for _, c := range candidates {
cm := c.Mod
@@ -310,13 +326,20 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
// version (e.g., v1.0.0) of a module, but we have a newer version
// of the same module in the build list (e.g., v1.0.1-beta), and
// the package is not present there.
+ //
+ // TODO(#41113): This is probably incorrect when there are multiple
+ // candidates, such as when a nested module is split out but only one
+ // half of the split is tagged.
m = cm
newMissingVersion = bm.Version
break
}
}
}
- return m, "", &ImportMissingError{Path: path, Module: m, newMissingVersion: newMissingVersion}
+ if newMissingVersion != "" {
+ return m, &ImportMissingError{Path: path, Module: m, newMissingVersion: newMissingVersion}
+ }
+ return m, nil
}
// maybeInModule reports whether, syntactically,
diff --git a/src/cmd/go/internal/modload/import_test.go b/src/cmd/go/internal/modload/import_test.go
index 47ce89a0849..22d5b82e211 100644
--- a/src/cmd/go/internal/modload/import_test.go
+++ b/src/cmd/go/internal/modload/import_test.go
@@ -10,15 +10,20 @@ import (
"regexp"
"strings"
"testing"
+
+ "golang.org/x/mod/module"
)
var importTests = []struct {
path string
+ m module.Version
err string
}{
{
path: "golang.org/x/net/context",
- err: "missing module for import: golang.org/x/net@.* provides golang.org/x/net/context",
+ m: module.Version{
+ Path: "golang.org/x/net",
+ },
},
{
path: "golang.org/x/net",
@@ -26,15 +31,23 @@ var importTests = []struct {
},
{
path: "golang.org/x/text",
- err: "missing module for import: golang.org/x/text@.* provides golang.org/x/text",
+ m: module.Version{
+ Path: "golang.org/x/text",
+ },
},
{
path: "github.com/rsc/quote/buggy",
- err: "missing module for import: github.com/rsc/quote@v1.5.2 provides github.com/rsc/quote/buggy",
+ m: module.Version{
+ Path: "github.com/rsc/quote",
+ Version: "v1.5.2",
+ },
},
{
path: "github.com/rsc/quote",
- err: "missing module for import: github.com/rsc/quote@v1.5.2 provides github.com/rsc/quote",
+ m: module.Version{
+ Path: "github.com/rsc/quote",
+ Version: "v1.5.2",
+ },
},
{
path: "golang.org/x/foo/bar",
@@ -42,7 +55,7 @@ var importTests = []struct {
},
}
-func TestImport(t *testing.T) {
+func TestQueryImport(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
defer func(old bool) {
@@ -55,12 +68,23 @@ func TestImport(t *testing.T) {
for _, tt := range importTests {
t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) {
// Note that there is no build list, so Import should always fail.
- m, dir, err := Import(ctx, tt.path)
- if err == nil {
- t.Fatalf("Import(%q) = %v, %v, nil; expected error", tt.path, m, dir)
+ m, err := queryImport(ctx, tt.path)
+
+ if tt.err == "" {
+ if err != nil {
+ t.Fatalf("queryImport(_, %q): %v", tt.path, err)
+ }
+ } else {
+ if err == nil {
+ t.Fatalf("queryImport(_, %q) = %v, nil; expected error", tt.path, m)
+ }
+ if !regexp.MustCompile(tt.err).MatchString(err.Error()) {
+ t.Fatalf("queryImport(_, %q): error %q, want error matching %#q", tt.path, err, tt.err)
+ }
}
- if !regexp.MustCompile(tt.err).MatchString(err.Error()) {
- t.Fatalf("Import(%q): error %q, want error matching %#q", tt.path, err, tt.err)
+
+ if m.Path != tt.m.Path || (tt.m.Version != "" && m.Version != tt.m.Version) {
+ t.Errorf("queryImport(_, %q) = %v, _; want %v", tt.path, m, tt.m)
}
})
}
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 8a3af534a57..2096dfb636f 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -881,7 +881,7 @@ func loadFromRoots(params loaderParams) *loader {
ld.buildStacks()
- modAddedBy := resolveMissingImports(addedModuleFor, ld.pkgs)
+ modAddedBy := ld.resolveMissingImports(addedModuleFor)
if len(modAddedBy) == 0 {
break
}
@@ -937,38 +937,45 @@ func loadFromRoots(params loaderParams) *loader {
// The newly-resolved packages are added to the addedModuleFor map, and
// resolveMissingImports returns a map from each newly-added module version to
// the first package for which that module was added.
-func resolveMissingImports(addedModuleFor map[string]bool, pkgs []*loadPkg) (modAddedBy map[module.Version]*loadPkg) {
- haveMod := make(map[module.Version]bool)
- for _, m := range buildList {
- haveMod[m] = true
- }
-
- modAddedBy = make(map[module.Version]*loadPkg)
- for _, pkg := range pkgs {
+func (ld *loader) resolveMissingImports(addedModuleFor map[string]bool) (modAddedBy map[module.Version]*loadPkg) {
+ var needPkgs []*loadPkg
+ for _, pkg := range ld.pkgs {
if pkg.isTest() {
// If we are missing a test, we are also missing its non-test version, and
// we should only add the missing import once.
continue
}
- if err, ok := pkg.err.(*ImportMissingError); ok && err.Module.Path != "" {
- if err.newMissingVersion != "" {
- base.Fatalf("go: %s: package provided by %s at latest version %s but not at required version %s", pkg.stackText(), err.Module.Path, err.Module.Version, err.newMissingVersion)
- }
- fmt.Fprintf(os.Stderr, "go: found %s in %s %s\n", pkg.path, err.Module.Path, err.Module.Version)
- if addedModuleFor[pkg.path] {
- base.Fatalf("go: %s: looping trying to add package", pkg.stackText())
- }
- addedModuleFor[pkg.path] = true
- if !haveMod[err.Module] {
- haveMod[err.Module] = true
- modAddedBy[err.Module] = pkg
- buildList = append(buildList, err.Module)
- }
+ if pkg.err != errImportMissing {
+ // Leave other errors for Import or load.Packages to report.
continue
}
- // Leave other errors for Import or load.Packages to report.
+
+ needPkgs = append(needPkgs, pkg)
+
+ pkg := pkg
+ ld.work.Add(func() {
+ pkg.mod, pkg.err = queryImport(context.TODO(), pkg.path)
+ })
+ }
+ <-ld.work.Idle()
+
+ modAddedBy = map[module.Version]*loadPkg{}
+ for _, pkg := range needPkgs {
+ if pkg.err != nil {
+ continue
+ }
+
+ fmt.Fprintf(os.Stderr, "go: found %s in %s %s\n", pkg.path, pkg.mod.Path, pkg.mod.Version)
+ if addedModuleFor[pkg.path] {
+ // TODO(bcmills): This should only be an error if pkg.mod is the same
+ // version we already tried to add previously.
+ base.Fatalf("go: %s: looping trying to add package", pkg.stackText())
+ }
+ if modAddedBy[pkg.mod] == nil {
+ modAddedBy[pkg.mod] = pkg
+ buildList = append(buildList, pkg.mod)
+ }
}
- base.ExitIfErrors()
return modAddedBy
}
@@ -1079,7 +1086,7 @@ func (ld *loader) load(pkg *loadPkg) {
return
}
- pkg.mod, pkg.dir, pkg.err = Import(context.TODO(), pkg.path)
+ pkg.mod, pkg.dir, pkg.err = importFromBuildList(context.TODO(), pkg.path)
if pkg.dir == "" {
return
}
diff --git a/src/cmd/go/testdata/script/list_bad_import.txt b/src/cmd/go/testdata/script/list_bad_import.txt
index b8f9d586f30..dbec35069c6 100644
--- a/src/cmd/go/testdata/script/list_bad_import.txt
+++ b/src/cmd/go/testdata/script/list_bad_import.txt
@@ -15,10 +15,11 @@ stdout 'incomplete'
stdout 'bad dep: .*example.com[/\\]notfound'
# Listing with -deps should also fail.
-# BUG: Today, it does not.
-# ! go list -deps example.com/direct
-# stderr example.com[/\\]notfound
-go list -deps example.com/direct
+! go list -deps example.com/direct
+stderr example.com[/\\]notfound
+
+# But -e -deps should succeed.
+go list -e -deps example.com/direct
stdout example.com/notfound
@@ -31,10 +32,11 @@ stdout incomplete
stdout 'bad dep: .*example.com[/\\]notfound'
# Again, -deps should fail.
-# BUG: Again, it does not.
-# ! go list -deps example.com/indirect
-# stderr example.com[/\\]notfound
-go list -deps example.com/indirect
+! go list -deps example.com/indirect
+stderr example.com[/\\]notfound
+
+# But -deps -e should succeed.
+go list -e -deps example.com/indirect
stdout example.com/notfound
diff --git a/src/cmd/go/testdata/script/list_test_err.txt b/src/cmd/go/testdata/script/list_test_err.txt
index a174b5e9ad2..c6f1ecf4003 100644
--- a/src/cmd/go/testdata/script/list_test_err.txt
+++ b/src/cmd/go/testdata/script/list_test_err.txt
@@ -22,6 +22,9 @@ go list -e -test -deps -f '{{.ImportPath}} {{.Error | printf "%q"}}' syntaxerr
stdout 'pkgdep '
stdout 'testdep_a '
stdout 'testdep_b '
+stdout 'syntaxerr '
+stdout 'syntaxerr \[syntaxerr.test\] '
+stdout 'syntaxerr_test \[syntaxerr.test\] '
stdout 'syntaxerr\.test "[^"]*expected declaration'
! stderr 'expected declaration'
diff --git a/src/cmd/go/testdata/script/mod_list_bad_import.txt b/src/cmd/go/testdata/script/mod_list_bad_import.txt
index 8a66e0b72a0..b3e2fff67d7 100644
--- a/src/cmd/go/testdata/script/mod_list_bad_import.txt
+++ b/src/cmd/go/testdata/script/mod_list_bad_import.txt
@@ -12,10 +12,11 @@ stdout 'incomplete'
stdout 'bad dep: .*example.com/notfound'
# Listing with -deps should also fail.
-# BUG: Today, it does not.
-# ! go list -deps example.com/direct
-# stderr example.com/notfound
-go list -deps example.com/direct
+! go list -deps example.com/direct
+stderr example.com/notfound
+
+# But -e -deps should succeed.
+go list -e -deps example.com/direct
stdout example.com/notfound
@@ -28,10 +29,11 @@ stdout incomplete
stdout 'bad dep: .*example.com/notfound'
# Again, -deps should fail.
-# BUG: Again, it does not.
-# ! go list -deps example.com/indirect
-# stderr example.com/notfound
-go list -deps example.com/indirect
+! go list -deps example.com/indirect
+stderr example.com/notfound
+
+# But -e -deps should succeed.
+go list -e -deps example.com/indirect
stdout example.com/notfound
diff --git a/src/cmd/go/testdata/script/mod_missingpkg_prerelease.txt b/src/cmd/go/testdata/script/mod_missingpkg_prerelease.txt
index 319ff855872..1ba8d3d22ab 100644
--- a/src/cmd/go/testdata/script/mod_missingpkg_prerelease.txt
+++ b/src/cmd/go/testdata/script/mod_missingpkg_prerelease.txt
@@ -1,7 +1,7 @@
env GO111MODULE=on
-! go list use.go
-stderr 'example.com/missingpkg/deprecated: package provided by example.com/missingpkg at latest version v1.0.0 but not at required version v1.0.1-beta'
+! go list -deps use.go
+stderr '^use.go:4:2: package example.com/missingpkg/deprecated provided by example.com/missingpkg at latest version v1.0.0 but not at required version v1.0.1-beta$'
-- go.mod --
module m
From 2c8d2a0c51f4085e56b5ab05ed9fb17fc6d08261 Mon Sep 17 00:00:00 2001
From: Steven Hartland
Date: Mon, 31 Aug 2020 21:37:40 +0100
Subject: [PATCH 0019/1082] net/http: fix data race due to writeLoop goroutine
left running
Fix a data race for clients that mutate requests after receiving a
response error which is caused by the writeLoop goroutine left
running, this can be seen on cancelled requests.
Fixes #37669
Change-Id: Ia4743c6b8abde3a7503de362cc6a3782e19e7f60
Reviewed-on: https://go-review.googlesource.com/c/go/+/251858
Reviewed-by: Bryan C. Mills
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
---
src/net/http/transport.go | 10 +++-
src/net/http/transport_test.go | 99 ++++++++++++++++++++++++++++++++++
2 files changed, 108 insertions(+), 1 deletion(-)
diff --git a/src/net/http/transport.go b/src/net/http/transport.go
index c23042b1e3d..b97c4268b57 100644
--- a/src/net/http/transport.go
+++ b/src/net/http/transport.go
@@ -1967,6 +1967,15 @@ func (pc *persistConn) mapRoundTripError(req *transportRequest, startBytesWritte
return nil
}
+ // Wait for the writeLoop goroutine to terminate to avoid data
+ // races on callers who mutate the request on failure.
+ //
+ // When resc in pc.roundTrip and hence rc.ch receives a responseAndError
+ // with a non-nil error it implies that the persistConn is either closed
+ // or closing. Waiting on pc.writeLoopDone is hence safe as all callers
+ // close closech which in turn ensures writeLoop returns.
+ <-pc.writeLoopDone
+
// If the request was canceled, that's better than network
// failures that were likely the result of tearing down the
// connection.
@@ -1992,7 +2001,6 @@ func (pc *persistConn) mapRoundTripError(req *transportRequest, startBytesWritte
return err
}
if pc.isBroken() {
- <-pc.writeLoopDone
if pc.nwrite == startBytesWritten {
return nothingWrittenError{err}
}
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index 2d9ca10bf0d..f4b76236308 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -25,6 +25,7 @@ import (
"io"
"io/ioutil"
"log"
+ mrand "math/rand"
"net"
. "net/http"
"net/http/httptest"
@@ -6284,3 +6285,101 @@ func TestTransportRejectsSignInContentLength(t *testing.T) {
t.Fatalf("Error mismatch\nGot: %q\nWanted substring: %q", got, want)
}
}
+
+// dumpConn is a net.Conn which writes to Writer and reads from Reader
+type dumpConn struct {
+ io.Writer
+ io.Reader
+}
+
+func (c *dumpConn) Close() error { return nil }
+func (c *dumpConn) LocalAddr() net.Addr { return nil }
+func (c *dumpConn) RemoteAddr() net.Addr { return nil }
+func (c *dumpConn) SetDeadline(t time.Time) error { return nil }
+func (c *dumpConn) SetReadDeadline(t time.Time) error { return nil }
+func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil }
+
+// delegateReader is a reader that delegates to another reader,
+// once it arrives on a channel.
+type delegateReader struct {
+ c chan io.Reader
+ r io.Reader // nil until received from c
+}
+
+func (r *delegateReader) Read(p []byte) (int, error) {
+ if r.r == nil {
+ var ok bool
+ if r.r, ok = <-r.c; !ok {
+ return 0, errors.New("delegate closed")
+ }
+ }
+ return r.r.Read(p)
+}
+
+func testTransportRace(req *Request) {
+ save := req.Body
+ pr, pw := io.Pipe()
+ defer pr.Close()
+ defer pw.Close()
+ dr := &delegateReader{c: make(chan io.Reader)}
+
+ t := &Transport{
+ Dial: func(net, addr string) (net.Conn, error) {
+ return &dumpConn{pw, dr}, nil
+ },
+ }
+ defer t.CloseIdleConnections()
+
+ quitReadCh := make(chan struct{})
+ // Wait for the request before replying with a dummy response:
+ go func() {
+ defer close(quitReadCh)
+
+ req, err := ReadRequest(bufio.NewReader(pr))
+ if err == nil {
+ // Ensure all the body is read; otherwise
+ // we'll get a partial dump.
+ io.Copy(ioutil.Discard, req.Body)
+ req.Body.Close()
+ }
+ select {
+ case dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n"):
+ case quitReadCh <- struct{}{}:
+ // Ensure delegate is closed so Read doesn't block forever.
+ close(dr.c)
+ }
+ }()
+
+ t.RoundTrip(req)
+
+ // Ensure the reader returns before we reset req.Body to prevent
+ // a data race on req.Body.
+ pw.Close()
+ <-quitReadCh
+
+ req.Body = save
+}
+
+// Issue 37669
+// Test that a cancellation doesn't result in a data race due to the writeLoop
+// goroutine being left running, if the caller mutates the processed Request
+// upon completion.
+func TestErrorWriteLoopRace(t *testing.T) {
+ if testing.Short() {
+ return
+ }
+ t.Parallel()
+ for i := 0; i < 1000; i++ {
+ delay := time.Duration(mrand.Intn(5)) * time.Millisecond
+ ctx, cancel := context.WithTimeout(context.Background(), delay)
+ defer cancel()
+
+ r := bytes.NewBuffer(make([]byte, 10000))
+ req, err := NewRequestWithContext(ctx, MethodPost, "http://example.com", r)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ testTransportRace(req)
+ }
+}
From cd91ab5d9601c975286f1ac83cd289e34aa117f8 Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills"
Date: Fri, 28 Aug 2020 21:32:05 -0400
Subject: [PATCH 0020/1082] cmd/go/internal/modload: fix spurious import
resolution error
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Due to a bug in CL 173017, if QueryPackages found multiple candidates
for the given package and *at least* one of those candidates was not
available to add, we would reject *all* such candidates — even those
that were still viable.
Now, we return the first viable candidate, and only return an error if
*no* candidate is viable given the current build list.
Fixes #41113
Change-Id: Idb2e77244be7c0f5dd511efb142c3059925d7336
Reviewed-on: https://go-review.googlesource.com/c/go/+/251446
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
Reviewed-by: Jay Conrod
Reviewed-by: Michael Matloob
---
src/cmd/go/internal/modload/import.go | 26 +++++++++--------
...e.com_split-incompatible_subpkg_v0.1.0.txt | 14 ++++++++++
...split-incompatible_v2.0.0+incompatible.txt | 10 +++++++
...t-incompatible_v2.1.0-pre+incompatible.txt | 10 +++++++
.../testdata/script/mod_import_issue41113.txt | 28 +++++++++++++++++++
5 files changed, 76 insertions(+), 12 deletions(-)
create mode 100644 src/cmd/go/testdata/mod/example.com_split-incompatible_subpkg_v0.1.0.txt
create mode 100644 src/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt
create mode 100644 src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt
create mode 100644 src/cmd/go/testdata/script/mod_import_issue41113.txt
diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go
index e04d66c5b1b..c625184b8b8 100644
--- a/src/cmd/go/internal/modload/import.go
+++ b/src/cmd/go/internal/modload/import.go
@@ -312,10 +312,10 @@ func queryImport(ctx context.Context, path string) (module.Version, error) {
}
}
- m := candidates[0].Mod
- newMissingVersion := ""
- for _, c := range candidates {
+ candidate0MissingVersion := ""
+ for i, c := range candidates {
cm := c.Mod
+ canAdd := true
for _, bm := range buildList {
if bm.Path == cm.Path && semver.Compare(bm.Version, cm.Version) > 0 {
// QueryPackage proposed that we add module cm to provide the package,
@@ -326,20 +326,22 @@ func queryImport(ctx context.Context, path string) (module.Version, error) {
// version (e.g., v1.0.0) of a module, but we have a newer version
// of the same module in the build list (e.g., v1.0.1-beta), and
// the package is not present there.
- //
- // TODO(#41113): This is probably incorrect when there are multiple
- // candidates, such as when a nested module is split out but only one
- // half of the split is tagged.
- m = cm
- newMissingVersion = bm.Version
+ canAdd = false
+ if i == 0 {
+ candidate0MissingVersion = bm.Version
+ }
break
}
}
+ if canAdd {
+ return cm, nil
+ }
}
- if newMissingVersion != "" {
- return m, &ImportMissingError{Path: path, Module: m, newMissingVersion: newMissingVersion}
+ return module.Version{}, &ImportMissingError{
+ Path: path,
+ Module: candidates[0].Mod,
+ newMissingVersion: candidate0MissingVersion,
}
- return m, nil
}
// maybeInModule reports whether, syntactically,
diff --git a/src/cmd/go/testdata/mod/example.com_split-incompatible_subpkg_v0.1.0.txt b/src/cmd/go/testdata/mod/example.com_split-incompatible_subpkg_v0.1.0.txt
new file mode 100644
index 00000000000..8f9e49176c7
--- /dev/null
+++ b/src/cmd/go/testdata/mod/example.com_split-incompatible_subpkg_v0.1.0.txt
@@ -0,0 +1,14 @@
+Written by hand.
+Test case for getting a package that has been moved to a nested module,
+with a +incompatible verison (and thus no go.mod file) at the root module.
+
+-- .mod --
+module example.com/split-incompatible/subpkg
+-- .info --
+{"Version": "v0.1.0"}
+-- go.mod --
+module example.com/split-incompatible/subpkg
+
+go 1.16
+-- subpkg.go --
+package subpkg
diff --git a/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt
new file mode 100644
index 00000000000..35c3f277103
--- /dev/null
+++ b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt
@@ -0,0 +1,10 @@
+Written by hand.
+Test case for getting a package that has been moved to a nested module,
+with a +incompatible verison (and thus no go.mod file) at the root module.
+
+-- .mod --
+module example.com/split-incompatible
+-- .info --
+{"Version": "v2.0.0+incompatible"}
+-- subpkg/subpkg.go --
+package subpkg
diff --git a/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt
new file mode 100644
index 00000000000..917fc0f5599
--- /dev/null
+++ b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt
@@ -0,0 +1,10 @@
+Written by hand.
+Test case for getting a package that has been moved to a nested module,
+with a +incompatible verison (and thus no go.mod file) at the root module.
+
+-- .mod --
+module example.com/split-incompatible
+-- .info --
+{"Version": "v2.1.0-pre+incompatible"}
+-- README.txt --
+subpkg has moved to module example.com/split-incompatible/subpkg
diff --git a/src/cmd/go/testdata/script/mod_import_issue41113.txt b/src/cmd/go/testdata/script/mod_import_issue41113.txt
new file mode 100644
index 00000000000..e98ac63d48e
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_import_issue41113.txt
@@ -0,0 +1,28 @@
+# Regression test for https://golang.org/issue/41113.
+#
+# When resolving a missing import path, the inability to add the package from
+# one module path should not interfere with adding a nested path.
+
+# Initially, our module depends on split-incompatible v2.1.0-pre+incompatible,
+# from which an imported package has been removed (and relocated to the nested
+# split-incompatible/subpkg module). modload.QueryPackage will suggest
+# split-incompatible v2.0.0+incompatible, which we cannot use (because it would
+# be an implicit downgrade), and split-incompatible/subpkg v0.1.0, which we
+# *should* use.
+
+go mod tidy
+
+go list -m all
+stdout '^example.com/split-incompatible/subpkg v0\.1\.0$'
+! stdout '^example.com/split-incompatible .*'
+
+-- go.mod --
+module golang.org/issue/41113
+
+go 1.16
+
+require example.com/split-incompatible v2.1.0-pre+incompatible
+-- x.go --
+package issue41113
+
+import _ "example.com/split-incompatible/subpkg"
From b4944ef310ed43fad53c6128344e4bed2b346c88 Mon Sep 17 00:00:00 2001
From: Tzu-Chiao Yeh
Date: Sun, 6 Sep 2020 09:43:34 +0800
Subject: [PATCH 0021/1082] cmd: update golang.org/x/tools to
v0.0.0-20200901153117-6e59e24738da
Includes the latest fix on vet to warn unused context.WithValue result.
Fixes #41149
Change-Id: I06c204f40ef12b0f62f59b1bbdf1fe06ccd6565d
Reviewed-on: https://go-review.googlesource.com/c/go/+/252941
Run-TryBot: Emmanuel Odeke
TryBot-Result: Gobot Gobot
Reviewed-by: Bryan C. Mills
---
src/cmd/go.mod | 2 +-
src/cmd/go.sum | 15 +-
.../go/analysis/passes/structtag/structtag.go | 6 +-
.../go/analysis/passes/unmarshal/unmarshal.go | 7 +-
.../passes/unusedresult/unusedresult.go | 2 +-
.../internal/analysisinternal/analysis.go | 343 ++++++++++++++-
.../x/tools/internal/lsp/fuzzy/input.go | 168 ++++++++
.../x/tools/internal/lsp/fuzzy/matcher.go | 398 ++++++++++++++++++
src/cmd/vendor/modules.txt | 3 +-
9 files changed, 912 insertions(+), 32 deletions(-)
create mode 100644 src/cmd/vendor/golang.org/x/tools/internal/lsp/fuzzy/input.go
create mode 100644 src/cmd/vendor/golang.org/x/tools/internal/lsp/fuzzy/matcher.go
diff --git a/src/cmd/go.mod b/src/cmd/go.mod
index 68ce1705e41..0952dbb84cb 100644
--- a/src/cmd/go.mod
+++ b/src/cmd/go.mod
@@ -9,6 +9,6 @@ require (
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 // indirect
- golang.org/x/tools v0.0.0-20200616133436-c1934b75d054
+ golang.org/x/tools v0.0.0-20200901153117-6e59e24738da
golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316 // indirect
)
diff --git a/src/cmd/go.sum b/src/cmd/go.sum
index cb64a5d4757..adbc5a96ac3 100644
--- a/src/cmd/go.sum
+++ b/src/cmd/go.sum
@@ -6,33 +6,34 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 h1:S1+yTUaFPXuDZnPDbO+TrDFIjPzQraYH8/CwSlu9Fac=
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/arch v0.0.0-20200511175325-f7c78586839d h1:YvwchuJby5xEAPdBGmdAVSiVME50C+RJfJJwJJsGEV8=
golang.org/x/arch v0.0.0-20200511175325-f7c78586839d/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449 h1:xUIPaMhvROX9dhPvRCenIJtU78+lbEenGbgqB5hfHCQ=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
-golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200901153117-6e59e24738da h1:8nFbt74voFOsM+Hb5XtF+1SNbbf3dzikH5osZO1hyyo=
+golang.org/x/tools v0.0.0-20200901153117-6e59e24738da/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316 h1:Jhw4VC65LaKnpq9FvcK+a8ZzrFm3D+UygvMMrhkOw70=
golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag.go
index e09160379f6..f0b15051c52 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag.go
@@ -116,7 +116,11 @@ func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, s
}
for _, enc := range [...]string{"json", "xml"} {
- if reflect.StructTag(tag).Get(enc) != "" {
+ switch reflect.StructTag(tag).Get(enc) {
+ // Ignore warning if the field not exported and the tag is marked as
+ // ignored.
+ case "", "-":
+ default:
pass.Reportf(field.Pos(), "struct field %s has %s tag but is not exported", field.Name(), enc)
return
}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go
index f9cc993cbb4..92b37caff9f 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go
@@ -30,7 +30,7 @@ var Analyzer = &analysis.Analyzer{
func run(pass *analysis.Pass) (interface{}, error) {
switch pass.Pkg.Path() {
- case "encoding/gob", "encoding/json", "encoding/xml":
+ case "encoding/gob", "encoding/json", "encoding/xml", "encoding/asn1":
// These packages know how to use their own APIs.
// Sometimes they are testing what happens to incorrect programs.
return nil, nil
@@ -53,9 +53,10 @@ func run(pass *analysis.Pass) (interface{}, error) {
recv := fn.Type().(*types.Signature).Recv()
if fn.Name() == "Unmarshal" && recv == nil {
// "encoding/json".Unmarshal
- // "encoding/xml".Unmarshal
+ // "encoding/xml".Unmarshal
+ // "encoding/asn1".Unmarshal
switch fn.Pkg().Path() {
- case "encoding/json", "encoding/xml":
+ case "encoding/json", "encoding/xml", "encoding/asn1":
argidx = 1 // func([]byte, interface{})
}
} else if fn.Name() == "Decode" && recv != nil {
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go
index 76d4ab23827..bececee7e93 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go
@@ -44,7 +44,7 @@ var funcs, stringMethods stringSetFlag
func init() {
// TODO(adonovan): provide a comment syntax to allow users to
// add their functions to this set using facts.
- funcs.Set("errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse")
+ funcs.Set("errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse,context.WithValue,context.WithCancel,context.WithDeadline,context.WithTimeout")
Analyzer.Flags.Var(&funcs, "funcs",
"comma-separated list of functions whose results must be used")
diff --git a/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go
index 26586810c7f..01f6e829f75 100644
--- a/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go
+++ b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go
@@ -14,6 +14,12 @@ import (
"strings"
"golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/internal/lsp/fuzzy"
+)
+
+var (
+ GetTypeErrors func(p interface{}) []types.Error
+ SetTypeErrors func(p interface{}, errors []types.Error)
)
func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos {
@@ -45,32 +51,34 @@ func ZeroValue(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.T
default:
panic("unknown basic type")
}
- case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice:
+ case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice, *types.Array:
return ast.NewIdent("nil")
case *types.Struct:
- texpr := typeExpr(fset, f, pkg, typ) // typ because we want the name here.
+ texpr := TypeExpr(fset, f, pkg, typ) // typ because we want the name here.
if texpr == nil {
return nil
}
return &ast.CompositeLit{
Type: texpr,
}
- case *types.Array:
- texpr := typeExpr(fset, f, pkg, u.Elem())
- if texpr == nil {
- return nil
- }
- return &ast.CompositeLit{
- Type: &ast.ArrayType{
- Elt: texpr,
- Len: &ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf("%v", u.Len())},
- },
- }
}
return nil
}
-func typeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
+// IsZeroValue checks whether the given expression is a 'zero value' (as determined by output of
+// analysisinternal.ZeroValue)
+func IsZeroValue(expr ast.Expr) bool {
+ switch e := expr.(type) {
+ case *ast.BasicLit:
+ return e.Value == "0" || e.Value == `""`
+ case *ast.Ident:
+ return e.Name == "nil" || e.Name == "false"
+ default:
+ return false
+ }
+}
+
+func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
switch t := typ.(type) {
case *types.Basic:
switch t.Kind() {
@@ -79,7 +87,96 @@ func typeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
default:
return ast.NewIdent(t.Name())
}
+ case *types.Pointer:
+ x := TypeExpr(fset, f, pkg, t.Elem())
+ if x == nil {
+ return nil
+ }
+ return &ast.UnaryExpr{
+ Op: token.MUL,
+ X: x,
+ }
+ case *types.Array:
+ elt := TypeExpr(fset, f, pkg, t.Elem())
+ if elt == nil {
+ return nil
+ }
+ return &ast.ArrayType{
+ Len: &ast.BasicLit{
+ Kind: token.INT,
+ Value: fmt.Sprintf("%d", t.Len()),
+ },
+ Elt: elt,
+ }
+ case *types.Slice:
+ elt := TypeExpr(fset, f, pkg, t.Elem())
+ if elt == nil {
+ return nil
+ }
+ return &ast.ArrayType{
+ Elt: elt,
+ }
+ case *types.Map:
+ key := TypeExpr(fset, f, pkg, t.Key())
+ value := TypeExpr(fset, f, pkg, t.Elem())
+ if key == nil || value == nil {
+ return nil
+ }
+ return &ast.MapType{
+ Key: key,
+ Value: value,
+ }
+ case *types.Chan:
+ dir := ast.ChanDir(t.Dir())
+ if t.Dir() == types.SendRecv {
+ dir = ast.SEND | ast.RECV
+ }
+ value := TypeExpr(fset, f, pkg, t.Elem())
+ if value == nil {
+ return nil
+ }
+ return &ast.ChanType{
+ Dir: dir,
+ Value: value,
+ }
+ case *types.Signature:
+ var params []*ast.Field
+ for i := 0; i < t.Params().Len(); i++ {
+ p := TypeExpr(fset, f, pkg, t.Params().At(i).Type())
+ if p == nil {
+ return nil
+ }
+ params = append(params, &ast.Field{
+ Type: p,
+ Names: []*ast.Ident{
+ {
+ Name: t.Params().At(i).Name(),
+ },
+ },
+ })
+ }
+ var returns []*ast.Field
+ for i := 0; i < t.Results().Len(); i++ {
+ r := TypeExpr(fset, f, pkg, t.Results().At(i).Type())
+ if r == nil {
+ return nil
+ }
+ returns = append(returns, &ast.Field{
+ Type: r,
+ })
+ }
+ return &ast.FuncType{
+ Params: &ast.FieldList{
+ List: params,
+ },
+ Results: &ast.FieldList{
+ List: returns,
+ },
+ }
case *types.Named:
+ if t.Obj().Pkg() == nil {
+ return ast.NewIdent(t.Obj().Name())
+ }
if t.Obj().Pkg() == pkg {
return ast.NewIdent(t.Obj().Name())
}
@@ -101,14 +198,15 @@ func typeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
X: ast.NewIdent(pkgName),
Sel: ast.NewIdent(t.Obj().Name()),
}
+ case *types.Struct:
+ return ast.NewIdent(t.String())
+ case *types.Interface:
+ return ast.NewIdent(t.String())
default:
- return nil // TODO: anonymous structs, but who does that
+ return nil
}
}
-var GetTypeErrors = func(p interface{}) []types.Error { return nil }
-var SetTypeErrors = func(p interface{}, errors []types.Error) {}
-
type TypeErrorPass string
const (
@@ -116,3 +214,212 @@ const (
NoResultValues TypeErrorPass = "noresultvalues"
UndeclaredName TypeErrorPass = "undeclaredname"
)
+
+// StmtToInsertVarBefore returns the ast.Stmt before which we can safely insert a new variable.
+// Some examples:
+//
+// Basic Example:
+// z := 1
+// y := z + x
+// If x is undeclared, then this function would return `y := z + x`, so that we
+// can insert `x := ` on the line before `y := z + x`.
+//
+// If stmt example:
+// if z == 1 {
+// } else if z == y {}
+// If y is undeclared, then this function would return `if z == 1 {`, because we cannot
+// insert a statement between an if and an else if statement. As a result, we need to find
+// the top of the if chain to insert `y := ` before.
+func StmtToInsertVarBefore(path []ast.Node) ast.Stmt {
+ enclosingIndex := -1
+ for i, p := range path {
+ if _, ok := p.(ast.Stmt); ok {
+ enclosingIndex = i
+ break
+ }
+ }
+ if enclosingIndex == -1 {
+ return nil
+ }
+ enclosingStmt := path[enclosingIndex]
+ switch enclosingStmt.(type) {
+ case *ast.IfStmt:
+ // The enclosingStmt is inside of the if declaration,
+ // We need to check if we are in an else-if stmt and
+ // get the base if statement.
+ return baseIfStmt(path, enclosingIndex)
+ case *ast.CaseClause:
+ // Get the enclosing switch stmt if the enclosingStmt is
+ // inside of the case statement.
+ for i := enclosingIndex + 1; i < len(path); i++ {
+ if node, ok := path[i].(*ast.SwitchStmt); ok {
+ return node
+ } else if node, ok := path[i].(*ast.TypeSwitchStmt); ok {
+ return node
+ }
+ }
+ }
+ if len(path) <= enclosingIndex+1 {
+ return enclosingStmt.(ast.Stmt)
+ }
+ // Check if the enclosing statement is inside another node.
+ switch expr := path[enclosingIndex+1].(type) {
+ case *ast.IfStmt:
+ // Get the base if statement.
+ return baseIfStmt(path, enclosingIndex+1)
+ case *ast.ForStmt:
+ if expr.Init == enclosingStmt || expr.Post == enclosingStmt {
+ return expr
+ }
+ }
+ return enclosingStmt.(ast.Stmt)
+}
+
+// baseIfStmt walks up the if/else-if chain until we get to
+// the top of the current if chain.
+func baseIfStmt(path []ast.Node, index int) ast.Stmt {
+ stmt := path[index]
+ for i := index + 1; i < len(path); i++ {
+ if node, ok := path[i].(*ast.IfStmt); ok && node.Else == stmt {
+ stmt = node
+ continue
+ }
+ break
+ }
+ return stmt.(ast.Stmt)
+}
+
+// WalkASTWithParent walks the AST rooted at n. The semantics are
+// similar to ast.Inspect except it does not call f(nil).
+func WalkASTWithParent(n ast.Node, f func(n ast.Node, parent ast.Node) bool) {
+ var ancestors []ast.Node
+ ast.Inspect(n, func(n ast.Node) (recurse bool) {
+ if n == nil {
+ ancestors = ancestors[:len(ancestors)-1]
+ return false
+ }
+
+ var parent ast.Node
+ if len(ancestors) > 0 {
+ parent = ancestors[len(ancestors)-1]
+ }
+ ancestors = append(ancestors, n)
+ return f(n, parent)
+ })
+}
+
+// FindMatchingIdents finds all identifiers in 'node' that match any of the given types.
+// 'pos' represents the position at which the identifiers may be inserted. 'pos' must be within
+// the scope of each of identifier we select. Otherwise, we will insert a variable at 'pos' that
+// is unrecognized.
+func FindMatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *types.Info, pkg *types.Package) map[types.Type][]*ast.Ident {
+ matches := map[types.Type][]*ast.Ident{}
+ // Initialize matches to contain the variable types we are searching for.
+ for _, typ := range typs {
+ if typ == nil {
+ continue
+ }
+ matches[typ] = []*ast.Ident{}
+ }
+ seen := map[types.Object]struct{}{}
+ ast.Inspect(node, func(n ast.Node) bool {
+ if n == nil {
+ return false
+ }
+ // Prevent circular definitions. If 'pos' is within an assignment statement, do not
+ // allow any identifiers in that assignment statement to be selected. Otherwise,
+ // we could do the following, where 'x' satisfies the type of 'f0':
+ //
+ // x := fakeStruct{f0: x}
+ //
+ assignment, ok := n.(*ast.AssignStmt)
+ if ok && pos > assignment.Pos() && pos <= assignment.End() {
+ return false
+ }
+ if n.End() > pos {
+ return n.Pos() <= pos
+ }
+ ident, ok := n.(*ast.Ident)
+ if !ok || ident.Name == "_" {
+ return true
+ }
+ obj := info.Defs[ident]
+ if obj == nil || obj.Type() == nil {
+ return true
+ }
+ if _, ok := obj.(*types.TypeName); ok {
+ return true
+ }
+ // Prevent duplicates in matches' values.
+ if _, ok = seen[obj]; ok {
+ return true
+ }
+ seen[obj] = struct{}{}
+ // Find the scope for the given position. Then, check whether the object
+ // exists within the scope.
+ innerScope := pkg.Scope().Innermost(pos)
+ if innerScope == nil {
+ return true
+ }
+ _, foundObj := innerScope.LookupParent(ident.Name, pos)
+ if foundObj != obj {
+ return true
+ }
+ // The object must match one of the types that we are searching for.
+ if idents, ok := matches[obj.Type()]; ok {
+ matches[obj.Type()] = append(idents, ast.NewIdent(ident.Name))
+ }
+ // If the object type does not exactly match any of the target types, greedily
+ // find the first target type that the object type can satisfy.
+ for typ := range matches {
+ if obj.Type() == typ {
+ continue
+ }
+ if equivalentTypes(obj.Type(), typ) {
+ matches[typ] = append(matches[typ], ast.NewIdent(ident.Name))
+ }
+ }
+ return true
+ })
+ return matches
+}
+
+func equivalentTypes(want, got types.Type) bool {
+ if want == got || types.Identical(want, got) {
+ return true
+ }
+ // Code segment to help check for untyped equality from (golang/go#32146).
+ if rhs, ok := want.(*types.Basic); ok && rhs.Info()&types.IsUntyped > 0 {
+ if lhs, ok := got.Underlying().(*types.Basic); ok {
+ return rhs.Info()&types.IsConstType == lhs.Info()&types.IsConstType
+ }
+ }
+ return types.AssignableTo(want, got)
+}
+
+// FindBestMatch employs fuzzy matching to evaluate the similarity of each given identifier to the
+// given pattern. We return the identifier whose name is most similar to the pattern.
+func FindBestMatch(pattern string, idents []*ast.Ident) ast.Expr {
+ fuzz := fuzzy.NewMatcher(pattern)
+ var bestFuzz ast.Expr
+ highScore := float32(0) // minimum score is 0 (no match)
+ for _, ident := range idents {
+ // TODO: Improve scoring algorithm.
+ score := fuzz.Score(ident.Name)
+ if score > highScore {
+ highScore = score
+ bestFuzz = ident
+ } else if score == 0 {
+ // Order matters in the fuzzy matching algorithm. If we find no match
+ // when matching the target to the identifier, try matching the identifier
+ // to the target.
+ revFuzz := fuzzy.NewMatcher(ident.Name)
+ revScore := revFuzz.Score(pattern)
+ if revScore > highScore {
+ highScore = revScore
+ bestFuzz = ident
+ }
+ }
+ }
+ return bestFuzz
+}
diff --git a/src/cmd/vendor/golang.org/x/tools/internal/lsp/fuzzy/input.go b/src/cmd/vendor/golang.org/x/tools/internal/lsp/fuzzy/input.go
new file mode 100644
index 00000000000..ac377035ec6
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/tools/internal/lsp/fuzzy/input.go
@@ -0,0 +1,168 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuzzy
+
+import (
+ "unicode"
+)
+
+// RuneRole specifies the role of a rune in the context of an input.
+type RuneRole byte
+
+const (
+ // RNone specifies a rune without any role in the input (i.e., whitespace/non-ASCII).
+ RNone RuneRole = iota
+ // RSep specifies a rune with the role of segment separator.
+ RSep
+ // RTail specifies a rune which is a lower-case tail in a word in the input.
+ RTail
+ // RUCTail specifies a rune which is an upper-case tail in a word in the input.
+ RUCTail
+ // RHead specifies a rune which is the first character in a word in the input.
+ RHead
+)
+
+// RuneRoles detects the roles of each byte rune in an input string and stores it in the output
+// slice. The rune role depends on the input type. Stops when it parsed all the runes in the string
+// or when it filled the output. If output is nil, then it gets created.
+func RuneRoles(str string, reuse []RuneRole) []RuneRole {
+ var output []RuneRole
+ if cap(reuse) < len(str) {
+ output = make([]RuneRole, 0, len(str))
+ } else {
+ output = reuse[:0]
+ }
+
+ prev, prev2 := rtNone, rtNone
+ for i := 0; i < len(str); i++ {
+ r := rune(str[i])
+
+ role := RNone
+
+ curr := rtLower
+ if str[i] <= unicode.MaxASCII {
+ curr = runeType(rt[str[i]] - '0')
+ }
+
+ if curr == rtLower {
+ if prev == rtNone || prev == rtPunct {
+ role = RHead
+ } else {
+ role = RTail
+ }
+ } else if curr == rtUpper {
+ role = RHead
+
+ if prev == rtUpper {
+ // This and previous characters are both upper case.
+
+ if i+1 == len(str) {
+ // This is last character, previous was also uppercase -> this is UCTail
+ // i.e., (current char is C): aBC / BC / ABC
+ role = RUCTail
+ }
+ }
+ } else if curr == rtPunct {
+ switch r {
+ case '.', ':':
+ role = RSep
+ }
+ }
+ if curr != rtLower {
+ if i > 1 && output[i-1] == RHead && prev2 == rtUpper && (output[i-2] == RHead || output[i-2] == RUCTail) {
+ // The previous two characters were uppercase. The current one is not a lower case, so the
+ // previous one can't be a HEAD. Make it a UCTail.
+ // i.e., (last char is current char - B must be a UCTail): ABC / ZABC / AB.
+ output[i-1] = RUCTail
+ }
+ }
+
+ output = append(output, role)
+ prev2 = prev
+ prev = curr
+ }
+ return output
+}
+
+type runeType byte
+
+const (
+ rtNone runeType = iota
+ rtPunct
+ rtLower
+ rtUpper
+)
+
+const rt = "00000000000000000000000000000000000000000000001122222222221000000333333333333333333333333330000002222222222222222222222222200000"
+
+// LastSegment returns the substring representing the last segment from the input, where each
+// byte has an associated RuneRole in the roles slice. This makes sense only for inputs of Symbol
+// or Filename type.
+func LastSegment(input string, roles []RuneRole) string {
+ // Exclude ending separators.
+ end := len(input) - 1
+ for end >= 0 && roles[end] == RSep {
+ end--
+ }
+ if end < 0 {
+ return ""
+ }
+
+ start := end - 1
+ for start >= 0 && roles[start] != RSep {
+ start--
+ }
+
+ return input[start+1 : end+1]
+}
+
+// ToLower transforms the input string to lower case, which is stored in the output byte slice.
+// The lower casing considers only ASCII values - non ASCII values are left unmodified.
+// Stops when parsed all input or when it filled the output slice. If output is nil, then it gets
+// created.
+func ToLower(input string, reuse []byte) []byte {
+ output := reuse
+ if cap(reuse) < len(input) {
+ output = make([]byte, len(input))
+ }
+
+ for i := 0; i < len(input); i++ {
+ r := rune(input[i])
+ if r <= unicode.MaxASCII {
+ if 'A' <= r && r <= 'Z' {
+ r += 'a' - 'A'
+ }
+ }
+ output[i] = byte(r)
+ }
+ return output[:len(input)]
+}
+
+// WordConsumer defines a consumer for a word delimited by the [start,end) byte offsets in an input
+// (start is inclusive, end is exclusive).
+type WordConsumer func(start, end int)
+
+// Words find word delimiters in an input based on its bytes' mappings to rune roles. The offset
+// delimiters for each word are fed to the provided consumer function.
+func Words(roles []RuneRole, consume WordConsumer) {
+ var wordStart int
+ for i, r := range roles {
+ switch r {
+ case RUCTail, RTail:
+ case RHead, RNone, RSep:
+ if i != wordStart {
+ consume(wordStart, i)
+ }
+ wordStart = i
+ if r != RHead {
+ // Skip this character.
+ wordStart = i + 1
+ }
+ }
+ }
+ if wordStart != len(roles) {
+ consume(wordStart, len(roles))
+ }
+}
diff --git a/src/cmd/vendor/golang.org/x/tools/internal/lsp/fuzzy/matcher.go b/src/cmd/vendor/golang.org/x/tools/internal/lsp/fuzzy/matcher.go
new file mode 100644
index 00000000000..16a643097de
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/tools/internal/lsp/fuzzy/matcher.go
@@ -0,0 +1,398 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package fuzzy implements a fuzzy matching algorithm.
+package fuzzy
+
+import (
+ "bytes"
+ "fmt"
+)
+
+const (
+ // MaxInputSize is the maximum size of the input scored against the fuzzy matcher. Longer inputs
+ // will be truncated to this size.
+ MaxInputSize = 127
+ // MaxPatternSize is the maximum size of the pattern used to construct the fuzzy matcher. Longer
+ // inputs are truncated to this size.
+ MaxPatternSize = 63
+)
+
+type scoreVal int
+
+func (s scoreVal) val() int {
+ return int(s) >> 1
+}
+
+func (s scoreVal) prevK() int {
+ return int(s) & 1
+}
+
+func score(val int, prevK int /*0 or 1*/) scoreVal {
+ return scoreVal(val<<1 + prevK)
+}
+
+// Matcher implements a fuzzy matching algorithm for scoring candidates against a pattern.
+// The matcher does not support parallel usage.
+type Matcher struct {
+ pattern string
+ patternLower []byte // lower-case version of the pattern
+ patternShort []byte // first characters of the pattern
+ caseSensitive bool // set if the pattern is mix-cased
+
+ patternRoles []RuneRole // the role of each character in the pattern
+ roles []RuneRole // the role of each character in the tested string
+
+ scores [MaxInputSize + 1][MaxPatternSize + 1][2]scoreVal
+
+ scoreScale float32
+
+ lastCandidateLen int // in bytes
+ lastCandidateMatched bool
+
+ // Here we save the last candidate in lower-case. This is basically a byte slice we reuse for
+ // performance reasons, so the slice is not reallocated for every candidate.
+ lowerBuf [MaxInputSize]byte
+ rolesBuf [MaxInputSize]RuneRole
+}
+
+func (m *Matcher) bestK(i, j int) int {
+ if m.scores[i][j][0].val() < m.scores[i][j][1].val() {
+ return 1
+ }
+ return 0
+}
+
+// NewMatcher returns a new fuzzy matcher for scoring candidates against the provided pattern.
+func NewMatcher(pattern string) *Matcher {
+ if len(pattern) > MaxPatternSize {
+ pattern = pattern[:MaxPatternSize]
+ }
+
+ m := &Matcher{
+ pattern: pattern,
+ patternLower: ToLower(pattern, nil),
+ }
+
+ for i, c := range m.patternLower {
+ if pattern[i] != c {
+ m.caseSensitive = true
+ break
+ }
+ }
+
+ if len(pattern) > 3 {
+ m.patternShort = m.patternLower[:3]
+ } else {
+ m.patternShort = m.patternLower
+ }
+
+ m.patternRoles = RuneRoles(pattern, nil)
+
+ if len(pattern) > 0 {
+ maxCharScore := 4
+ m.scoreScale = 1 / float32(maxCharScore*len(pattern))
+ }
+
+ return m
+}
+
+// Score returns the score returned by matching the candidate to the pattern.
+// This is not designed for parallel use. Multiple candidates must be scored sequentially.
+// Returns a score between 0 and 1 (0 - no match, 1 - perfect match).
+func (m *Matcher) Score(candidate string) float32 {
+ if len(candidate) > MaxInputSize {
+ candidate = candidate[:MaxInputSize]
+ }
+ lower := ToLower(candidate, m.lowerBuf[:])
+ m.lastCandidateLen = len(candidate)
+
+ if len(m.pattern) == 0 {
+ // Empty patterns perfectly match candidates.
+ return 1
+ }
+
+ if m.match(candidate, lower) {
+ sc := m.computeScore(candidate, lower)
+ if sc > minScore/2 && !m.poorMatch() {
+ m.lastCandidateMatched = true
+ if len(m.pattern) == len(candidate) {
+ // Perfect match.
+ return 1
+ }
+
+ if sc < 0 {
+ sc = 0
+ }
+ normalizedScore := float32(sc) * m.scoreScale
+ if normalizedScore > 1 {
+ normalizedScore = 1
+ }
+
+ return normalizedScore
+ }
+ }
+
+ m.lastCandidateMatched = false
+ return 0
+}
+
+const minScore = -10000
+
+// MatchedRanges returns matches ranges for the last scored string as a flattened array of
+// [begin, end) byte offset pairs.
+func (m *Matcher) MatchedRanges() []int {
+ if len(m.pattern) == 0 || !m.lastCandidateMatched {
+ return nil
+ }
+ i, j := m.lastCandidateLen, len(m.pattern)
+ if m.scores[i][j][0].val() < minScore/2 && m.scores[i][j][1].val() < minScore/2 {
+ return nil
+ }
+
+ var ret []int
+ k := m.bestK(i, j)
+ for i > 0 {
+ take := (k == 1)
+ k = m.scores[i][j][k].prevK()
+ if take {
+ if len(ret) == 0 || ret[len(ret)-1] != i {
+ ret = append(ret, i)
+ ret = append(ret, i-1)
+ } else {
+ ret[len(ret)-1] = i - 1
+ }
+ j--
+ }
+ i--
+ }
+ // Reverse slice.
+ for i := 0; i < len(ret)/2; i++ {
+ ret[i], ret[len(ret)-1-i] = ret[len(ret)-1-i], ret[i]
+ }
+ return ret
+}
+
+func (m *Matcher) match(candidate string, candidateLower []byte) bool {
+ i, j := 0, 0
+ for ; i < len(candidateLower) && j < len(m.patternLower); i++ {
+ if candidateLower[i] == m.patternLower[j] {
+ j++
+ }
+ }
+ if j != len(m.patternLower) {
+ return false
+ }
+
+ // The input passes the simple test against pattern, so it is time to classify its characters.
+ // Character roles are used below to find the last segment.
+ m.roles = RuneRoles(candidate, m.rolesBuf[:])
+
+ return true
+}
+
+func (m *Matcher) computeScore(candidate string, candidateLower []byte) int {
+ pattLen, candLen := len(m.pattern), len(candidate)
+
+ for j := 0; j <= len(m.pattern); j++ {
+ m.scores[0][j][0] = minScore << 1
+ m.scores[0][j][1] = minScore << 1
+ }
+ m.scores[0][0][0] = score(0, 0) // Start with 0.
+
+ segmentsLeft, lastSegStart := 1, 0
+ for i := 0; i < candLen; i++ {
+ if m.roles[i] == RSep {
+ segmentsLeft++
+ lastSegStart = i + 1
+ }
+ }
+
+ // A per-character bonus for a consecutive match.
+ consecutiveBonus := 2
+ wordIdx := 0 // Word count within segment.
+ for i := 1; i <= candLen; i++ {
+
+ role := m.roles[i-1]
+ isHead := role == RHead
+
+ if isHead {
+ wordIdx++
+ } else if role == RSep && segmentsLeft > 1 {
+ wordIdx = 0
+ segmentsLeft--
+ }
+
+ var skipPenalty int
+ if i == 1 || (i-1) == lastSegStart {
+ // Skipping the start of first or last segment.
+ skipPenalty++
+ }
+
+ for j := 0; j <= pattLen; j++ {
+ // By default, we don't have a match. Fill in the skip data.
+ m.scores[i][j][1] = minScore << 1
+
+ // Compute the skip score.
+ k := 0
+ if m.scores[i-1][j][0].val() < m.scores[i-1][j][1].val() {
+ k = 1
+ }
+
+ skipScore := m.scores[i-1][j][k].val()
+ // Do not penalize missing characters after the last matched segment.
+ if j != pattLen {
+ skipScore -= skipPenalty
+ }
+ m.scores[i][j][0] = score(skipScore, k)
+
+ if j == 0 || candidateLower[i-1] != m.patternLower[j-1] {
+ // Not a match.
+ continue
+ }
+ pRole := m.patternRoles[j-1]
+
+ if role == RTail && pRole == RHead {
+ if j > 1 {
+ // Not a match: a head in the pattern matches a tail character in the candidate.
+ continue
+ }
+ // Special treatment for the first character of the pattern. We allow
+ // matches in the middle of a word if they are long enough, at least
+ // min(3, pattern.length) characters.
+ if !bytes.HasPrefix(candidateLower[i-1:], m.patternShort) {
+ continue
+ }
+ }
+
+ // Compute the char score.
+ var charScore int
+ // Bonus 1: the char is in the candidate's last segment.
+ if segmentsLeft <= 1 {
+ charScore++
+ }
+ // Bonus 2: Case match or a Head in the pattern aligns with one in the word.
+ // Single-case patterns lack segmentation signals and we assume any character
+ // can be a head of a segment.
+ if candidate[i-1] == m.pattern[j-1] || role == RHead && (!m.caseSensitive || pRole == RHead) {
+ charScore++
+ }
+
+ // Penalty 1: pattern char is Head, candidate char is Tail.
+ if role == RTail && pRole == RHead {
+ charScore--
+ }
+ // Penalty 2: first pattern character matched in the middle of a word.
+ if j == 1 && role == RTail {
+ charScore -= 4
+ }
+
+ // Third dimension encodes whether there is a gap between the previous match and the current
+ // one.
+ for k := 0; k < 2; k++ {
+ sc := m.scores[i-1][j-1][k].val() + charScore
+
+ isConsecutive := k == 1 || i-1 == 0 || i-1 == lastSegStart
+ if isConsecutive {
+ // Bonus 3: a consecutive match. First character match also gets a bonus to
+ // ensure prefix final match score normalizes to 1.0.
+ // Logically, this is a part of charScore, but we have to compute it here because it
+ // only applies for consecutive matches (k == 1).
+ sc += consecutiveBonus
+ }
+ if k == 0 {
+ // Penalty 3: Matching inside a segment (and previous char wasn't matched). Penalize for the lack
+ // of alignment.
+ if role == RTail || role == RUCTail {
+ sc -= 3
+ }
+ }
+
+ if sc > m.scores[i][j][1].val() {
+ m.scores[i][j][1] = score(sc, k)
+ }
+ }
+ }
+ }
+
+ result := m.scores[len(candidate)][len(m.pattern)][m.bestK(len(candidate), len(m.pattern))].val()
+
+ return result
+}
+
+// ScoreTable returns the score table computed for the provided candidate. Used only for debugging.
+func (m *Matcher) ScoreTable(candidate string) string {
+ var buf bytes.Buffer
+
+ var line1, line2, separator bytes.Buffer
+ line1.WriteString("\t")
+ line2.WriteString("\t")
+ for j := 0; j < len(m.pattern); j++ {
+ line1.WriteString(fmt.Sprintf("%c\t\t", m.pattern[j]))
+ separator.WriteString("----------------")
+ }
+
+ buf.WriteString(line1.String())
+ buf.WriteString("\n")
+ buf.WriteString(separator.String())
+ buf.WriteString("\n")
+
+ for i := 1; i <= len(candidate); i++ {
+ line1.Reset()
+ line2.Reset()
+
+ line1.WriteString(fmt.Sprintf("%c\t", candidate[i-1]))
+ line2.WriteString("\t")
+
+ for j := 1; j <= len(m.pattern); j++ {
+ line1.WriteString(fmt.Sprintf("M%6d(%c)\t", m.scores[i][j][0].val(), dir(m.scores[i][j][0].prevK())))
+ line2.WriteString(fmt.Sprintf("H%6d(%c)\t", m.scores[i][j][1].val(), dir(m.scores[i][j][1].prevK())))
+ }
+ buf.WriteString(line1.String())
+ buf.WriteString("\n")
+ buf.WriteString(line2.String())
+ buf.WriteString("\n")
+ buf.WriteString(separator.String())
+ buf.WriteString("\n")
+ }
+
+ return buf.String()
+}
+
+func dir(prevK int) rune {
+ if prevK == 0 {
+ return 'M'
+ }
+ return 'H'
+}
+
+func (m *Matcher) poorMatch() bool {
+ if len(m.pattern) < 2 {
+ return false
+ }
+
+ i, j := m.lastCandidateLen, len(m.pattern)
+ k := m.bestK(i, j)
+
+ var counter, len int
+ for i > 0 {
+ take := (k == 1)
+ k = m.scores[i][j][k].prevK()
+ if take {
+ len++
+ if k == 0 && len < 3 && m.roles[i-1] == RTail {
+ // Short match in the middle of a word
+ counter++
+ if counter > 1 {
+ return true
+ }
+ }
+ j--
+ } else {
+ len = 0
+ }
+ i--
+ }
+ return false
+}
diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt
index c0c008e038d..c8273654003 100644
--- a/src/cmd/vendor/modules.txt
+++ b/src/cmd/vendor/modules.txt
@@ -45,7 +45,7 @@ golang.org/x/mod/zip
golang.org/x/sys/internal/unsafeheader
golang.org/x/sys/unix
golang.org/x/sys/windows
-# golang.org/x/tools v0.0.0-20200616133436-c1934b75d054
+# golang.org/x/tools v0.0.0-20200901153117-6e59e24738da
## explicit
golang.org/x/tools/go/analysis
golang.org/x/tools/go/analysis/internal/analysisflags
@@ -84,6 +84,7 @@ golang.org/x/tools/go/cfg
golang.org/x/tools/go/types/objectpath
golang.org/x/tools/go/types/typeutil
golang.org/x/tools/internal/analysisinternal
+golang.org/x/tools/internal/lsp/fuzzy
# golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316
## explicit
golang.org/x/xerrors
From d27ebc7b8630993269c36e7728a7f30543ffa048 Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills"
Date: Fri, 13 Mar 2020 16:32:37 -0400
Subject: [PATCH 0022/1082] cmd/go/internal/modload: implement the "all"
pattern for lazy loading
The new semantics of the "all" package pattern can be implemented
without actually changing module loading per se. This change
implements those semantics, so that the change can be decoupled from
the changes to the module requirement graph.
For #36460
Change-Id: I0ee8b17afa8b728dc470a42a540fcc01764a4442
Reviewed-on: https://go-review.googlesource.com/c/go/+/240623
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
Reviewed-by: Jay Conrod
Reviewed-by: Michael Matloob
---
doc/go1.16.html | 14 +++-
src/cmd/go/internal/modcmd/tidy.go | 12 +++
src/cmd/go/internal/modcmd/why.go | 2 +
src/cmd/go/internal/modload/load.go | 16 ++--
src/cmd/go/internal/modload/modfile.go | 22 +++++
src/cmd/go/testdata/script/mod_all.txt | 106 ++++++++++++++++++++++---
6 files changed, 157 insertions(+), 15 deletions(-)
diff --git a/doc/go1.16.html b/doc/go1.16.html
index 0ffaecc5a9a..95e63d0d5a9 100644
--- a/doc/go1.16.html
+++ b/doc/go1.16.html
@@ -52,7 +52,7 @@ Do not send CLs removing the interior tags from such phrases.
TODO: write and link to tutorial or blog post
-
+
When using go test, a test that
calls os.Exit(0) during execution of a test function
will now be considered to fail.
@@ -62,6 +62,18 @@ Do not send CLs removing the interior tags from such phrases.
that is still considered to be a passing test.
+The all pattern
+
+
+ When the main module's go.mod file
+ declares go 1.16 or higher, the all
+ package pattern now matches only those packages that are transitively imported
+ by a package or test found in the main module. (Packages imported by tests
+ of packages imported by the main module are no longer included.) This is
+ the same set of packages retained
+ by go mod vendor since Go 1.11.
+
+
TODO
diff --git a/src/cmd/go/internal/modcmd/tidy.go b/src/cmd/go/internal/modcmd/tidy.go
index c7c53d7c0c1..4dcb62e02f6 100644
--- a/src/cmd/go/internal/modcmd/tidy.go
+++ b/src/cmd/go/internal/modcmd/tidy.go
@@ -40,6 +40,18 @@ func runTidy(ctx context.Context, cmd *base.Command, args []string) {
base.Fatalf("go mod tidy: no arguments allowed")
}
+ // Tidy aims to make 'go test' reproducible for any package in 'all', so we
+ // need to include test dependencies. For modules that specify go 1.15 or
+ // earlier this is a no-op (because 'all' saturates transitive test
+ // dependencies).
+ //
+ // However, with lazy loading (go 1.16+) 'all' includes only the packages that
+ // are transitively imported by the main module, not the test dependencies of
+ // those packages. In order to make 'go test' reproducible for the packages
+ // that are in 'all' but outside of the main module, we must explicitly
+ // request that their test dependencies be included.
+ modload.LoadTests = true
+
modload.LoadALL(ctx)
modload.TidyBuildList()
modload.TrimGoSum()
diff --git a/src/cmd/go/internal/modcmd/why.go b/src/cmd/go/internal/modcmd/why.go
index b16887d3186..30b15fc1532 100644
--- a/src/cmd/go/internal/modcmd/why.go
+++ b/src/cmd/go/internal/modcmd/why.go
@@ -65,6 +65,8 @@ func runWhy(ctx context.Context, cmd *base.Command, args []string) {
loadALL := modload.LoadALL
if *whyVendor {
loadALL = modload.LoadVendor
+ } else {
+ modload.LoadTests = true
}
if *whyM {
listU := false
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 2096dfb636f..9cedc219b61 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -231,7 +231,7 @@ func ImportPathsQuiet(ctx context.Context, patterns []string, tags map[string]bo
loaded = loadFromRoots(loaderParams{
tags: tags,
allPatternIsRoot: allPatternIsRoot,
- allClosesOverTests: true, // until lazy loading in Go 1.16+
+ allClosesOverTests: index.allPatternClosesOverTests(),
listRoots: func() (roots []string) {
updateMatches(nil)
@@ -450,7 +450,7 @@ func ImportFromFiles(ctx context.Context, gofiles []string) {
roots = append(roots, testImports...)
return roots
},
- allClosesOverTests: true, // until lazy loading.
+ allClosesOverTests: index.allPatternClosesOverTests(),
})
WriteGoMod()
}
@@ -501,7 +501,7 @@ func ReloadBuildList() []module.Version {
loaded = loadFromRoots(loaderParams{
tags: imports.Tags(),
listRoots: func() []string { return nil },
- allClosesOverTests: true, // until lazy loading, but doesn't matter because the root list is empty.
+ allClosesOverTests: index.allPatternClosesOverTests(), // but doesn't matter because the root list is empty.
})
return buildList
}
@@ -512,9 +512,13 @@ func ReloadBuildList() []module.Version {
// It adds modules to the build list as needed to satisfy new imports.
// This set is useful for deciding whether a particular import is needed
// anywhere in a module.
+//
+// In modules that specify "go 1.16" or higher, ALL follows only one layer of
+// test dependencies. In "go 1.15" or lower, ALL follows the imports of tests of
+// dependencies of tests.
func LoadALL(ctx context.Context) []string {
InitMod(ctx)
- return loadAll(ctx, true)
+ return loadAll(ctx, index.allPatternClosesOverTests())
}
// LoadVendor is like LoadALL but only follows test dependencies
@@ -523,7 +527,9 @@ func LoadALL(ctx context.Context) []string {
// This set is useful for identifying the which packages to include in a vendor directory.
func LoadVendor(ctx context.Context) []string {
InitMod(ctx)
- return loadAll(ctx, false)
+ // 'go mod vendor' has never followed test dependencies since Go 1.11.
+ const closeOverTests = false
+ return loadAll(ctx, closeOverTests)
}
func loadAll(ctx context.Context, closeOverTests bool) []string {
diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go
index a45c4a63be2..18dd293ac92 100644
--- a/src/cmd/go/internal/modload/modfile.go
+++ b/src/cmd/go/internal/modload/modfile.go
@@ -25,6 +25,11 @@ import (
"golang.org/x/mod/semver"
)
+// lazyLoadingVersion is the Go version (plus leading "v") at which lazy module
+// loading takes effect.
+const lazyLoadingVersionV = "v1.16"
+const go116EnableLazyLoading = true
+
var modFile *modfile.File
// A modFileIndex is an index of data corresponding to a modFile
@@ -249,6 +254,23 @@ func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileInd
return i
}
+// allPatternClosesOverTests reports whether the "all" pattern includes
+// dependencies of tests outside the main module (as in Go 1.11–1.15).
+// (Otherwise — as in Go 1.16+ — the "all" pattern includes only the packages
+// transitively *imported by* the packages and tests in the main module.)
+func (i *modFileIndex) allPatternClosesOverTests() bool {
+ if !go116EnableLazyLoading {
+ return true
+ }
+ if i != nil && semver.Compare(i.goVersionV, lazyLoadingVersionV) < 0 {
+ // The module explicitly predates the change in "all" for lazy loading, so
+ // continue to use the older interpretation. (If i == nil, we not in any
+ // module at all and should use the latest semantics.)
+ return true
+ }
+ return false
+}
+
// modFileIsDirty reports whether the go.mod file differs meaningfully
// from what was indexed.
// If modFile has been changed (even cosmetically) since it was first read,
diff --git a/src/cmd/go/testdata/script/mod_all.txt b/src/cmd/go/testdata/script/mod_all.txt
index 9f4b0a4e4d7..aac66292d66 100644
--- a/src/cmd/go/testdata/script/mod_all.txt
+++ b/src/cmd/go/testdata/script/mod_all.txt
@@ -187,16 +187,104 @@ stdout '^example.com/main_test \[example.com/main.test\]$'
stdout '^example.com/main/testonly.test$'
stdout '^example.com/main/testonly_test \[example.com/main/testonly.test\]$'
+rm vendor
+
+# Convert all modules to go 1.16 to enable lazy loading.
+go mod edit -go=1.16 a/go.mod
+go mod edit -go=1.16 b/go.mod
+go mod edit -go=1.16 c/go.mod
+go mod edit -go=1.16 d/go.mod
+go mod edit -go=1.16 q/go.mod
+go mod edit -go=1.16 r/go.mod
+go mod edit -go=1.16 s/go.mod
+go mod edit -go=1.16 t/go.mod
+go mod edit -go=1.16 u/go.mod
+go mod edit -go=1.16 w/go.mod
+go mod edit -go=1.16 x/go.mod
+go mod edit -go=1.16
+
+# With lazy loading, 'go list all' with neither -mod=vendor nor -test should
+# match -mod=vendor without -test in 1.15.
+
+go list -f $PKGFMT all
+stdout -count=8 '^.'
+stdout '^example.com/a$'
+stdout '^example.com/b$'
+stdout '^example.com/main$'
+stdout '^example.com/main/testonly$'
+stdout '^example.com/q$'
+stdout '^example.com/r$'
+stdout '^example.com/t$'
+stdout '^example.com/u$'
+
+# 'go list -test all' should expand that to include the test variants of the
+# packages in 'all', but not the dependencies of outside tests.
+
+go list -test -f $PKGFMT all
+stdout -count=25 '^.'
+stdout '^example.com/a$'
+stdout '^example.com/b$'
+stdout '^example.com/main$'
+stdout '^example.com/main/testonly$'
+stdout '^example.com/q$'
+stdout '^example.com/r$'
+stdout '^example.com/t$'
+stdout '^example.com/u$'
+stdout '^example.com/a.test$'
+stdout '^example.com/a_test \[example.com/a.test\]$'
+stdout '^example.com/b.test$'
+stdout '^example.com/b_test \[example.com/b.test\]$'
+stdout '^example.com/main.test$'
+stdout '^example.com/main \[example.com/main.test\]$'
+stdout '^example.com/main_test \[example.com/main.test\]$'
+stdout '^example.com/main/testonly.test$'
+stdout '^example.com/main/testonly_test \[example.com/main/testonly.test\]$'
+stdout '^example.com/q.test$'
+stdout '^example.com/q_test \[example.com/q.test\]$'
+stdout '^example.com/r.test$'
+stdout '^example.com/r_test \[example.com/r.test\]$'
+stdout '^example.com/t.test$'
+stdout '^example.com/t_test \[example.com/t.test\]$'
+stdout '^example.com/u.test$'
+stdout '^example.com/u_test \[example.com/u.test\]$'
+
+# 'go list -test -deps all' should include the dependencies of those tests,
+# but not the tests of the dependencies of outside tests.
+
+go list -test -deps -f $PKGFMT all
+stdout -count=28 '^.'
+stdout '^example.com/a$'
+stdout '^example.com/b$'
+stdout '^example.com/c$'
+stdout '^example.com/main$'
+stdout '^example.com/main/testonly$'
+stdout '^example.com/q$'
+stdout '^example.com/r$'
+stdout '^example.com/s$'
+stdout '^example.com/t$'
+stdout '^example.com/u$'
+stdout '^example.com/w$'
+stdout '^example.com/a.test$'
+stdout '^example.com/a_test \[example.com/a.test\]$'
+stdout '^example.com/b.test$'
+stdout '^example.com/b_test \[example.com/b.test\]$'
+stdout '^example.com/main.test$'
+stdout '^example.com/main \[example.com/main.test\]$'
+stdout '^example.com/main_test \[example.com/main.test\]$'
+stdout '^example.com/main/testonly.test$'
+stdout '^example.com/main/testonly_test \[example.com/main/testonly.test\]$'
+stdout '^example.com/q.test$'
+stdout '^example.com/q_test \[example.com/q.test\]$'
+stdout '^example.com/r.test$'
+stdout '^example.com/r_test \[example.com/r.test\]$'
+stdout '^example.com/t.test$'
+stdout '^example.com/t_test \[example.com/t.test\]$'
+stdout '^example.com/u.test$'
+stdout '^example.com/u_test \[example.com/u.test\]$'
+
+
# TODO(#36460):
-
-# With lazy loading, 'go list all' without -mod=vendor should match
-# 'go mod vendor'.
-
-# 'go list -test all' should expand that to cover test dependencies
-# of packages imported by the main module.
-
-# 'go list -m all' should cover the packages in 'go list -test all'.
-
+# 'go list -m all' should exactly cover the packages in 'go list -test all'.
-- go.mod --
module example.com/main
From 363fb4bcc814069e3d80e22bd022599179ec1c62 Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills"
Date: Wed, 1 Jul 2020 22:38:45 -0400
Subject: [PATCH 0023/1082] cmd/go/internal/modload: consolidate buildList and
associated functions into one file
Change-Id: I310c37c7f0ce5581f07cf6e27d1f6361d03b92ef
Reviewed-on: https://go-review.googlesource.com/c/go/+/244077
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
Reviewed-by: Michael Matloob
Reviewed-by: Jay Conrod
---
src/cmd/go/internal/modget/get.go | 4 +
src/cmd/go/internal/modload/buildlist.go | 117 +++++++++++++++++++++++
src/cmd/go/internal/modload/load.go | 103 --------------------
3 files changed, 121 insertions(+), 103 deletions(-)
create mode 100644 src/cmd/go/internal/modload/buildlist.go
diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go
index 4ca7f5b5292..126b1f4bd42 100644
--- a/src/cmd/go/internal/modget/get.go
+++ b/src/cmd/go/internal/modget/get.go
@@ -628,6 +628,10 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
if err != nil {
base.Fatalf("go: %v", err)
}
+
+ // TODO(bcmills) What should happen here under lazy loading?
+ // Downgrading may intentionally violate the lazy-loading invariants.
+
modload.SetBuildList(buildList)
modload.ReloadBuildList() // note: does not update go.mod
base.ExitIfErrors()
diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go
new file mode 100644
index 00000000000..2302b044e81
--- /dev/null
+++ b/src/cmd/go/internal/modload/buildlist.go
@@ -0,0 +1,117 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modload
+
+import (
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/imports"
+ "cmd/go/internal/mvs"
+ "context"
+ "fmt"
+ "os"
+
+ "golang.org/x/mod/module"
+)
+
+// buildList is the list of modules to use for building packages.
+// It is initialized by calling ImportPaths, ImportFromFiles,
+// LoadALL, or LoadBuildList, each of which uses loaded.load.
+//
+// Ideally, exactly ONE of those functions would be called,
+// and exactly once. Most of the time, that's true.
+// During "go get" it may not be. TODO(rsc): Figure out if
+// that restriction can be established, or else document why not.
+//
+var buildList []module.Version
+
+// LoadBuildList loads and returns the build list from go.mod.
+// The loading of the build list happens automatically in ImportPaths:
+// LoadBuildList need only be called if ImportPaths is not
+// (typically in commands that care about the module but
+// no particular package).
+func LoadBuildList(ctx context.Context) []module.Version {
+ InitMod(ctx)
+ ReloadBuildList()
+ WriteGoMod()
+ return buildList
+}
+
+// ReloadBuildList resets the state of loaded packages, then loads and returns
+// the build list set in SetBuildList.
+func ReloadBuildList() []module.Version {
+ loaded = loadFromRoots(loaderParams{
+ tags: imports.Tags(),
+ listRoots: func() []string { return nil },
+ allClosesOverTests: index.allPatternClosesOverTests(), // but doesn't matter because the root list is empty.
+ })
+ return buildList
+}
+
+// BuildList returns the module build list,
+// typically constructed by a previous call to
+// LoadBuildList or ImportPaths.
+// The caller must not modify the returned list.
+func BuildList() []module.Version {
+ return buildList
+}
+
+// SetBuildList sets the module build list.
+// The caller is responsible for ensuring that the list is valid.
+// SetBuildList does not retain a reference to the original list.
+func SetBuildList(list []module.Version) {
+ buildList = append([]module.Version{}, list...)
+}
+
+// TidyBuildList trims the build list to the minimal requirements needed to
+// retain the same versions of all packages from the preceding Load* or
+// ImportPaths* call.
+func TidyBuildList() {
+ used := map[module.Version]bool{Target: true}
+ for _, pkg := range loaded.pkgs {
+ used[pkg.mod] = true
+ }
+
+ keep := []module.Version{Target}
+ var direct []string
+ for _, m := range buildList[1:] {
+ if used[m] {
+ keep = append(keep, m)
+ if loaded.direct[m.Path] {
+ direct = append(direct, m.Path)
+ }
+ } else if cfg.BuildV {
+ if _, ok := index.require[m]; ok {
+ fmt.Fprintf(os.Stderr, "unused %s\n", m.Path)
+ }
+ }
+ }
+
+ min, err := mvs.Req(Target, direct, &mvsReqs{buildList: keep})
+ if err != nil {
+ base.Fatalf("go: %v", err)
+ }
+ buildList = append([]module.Version{Target}, min...)
+}
+
+// checkMultiplePaths verifies that a given module path is used as itself
+// or as a replacement for another module, but not both at the same time.
+//
+// (See https://golang.org/issue/26607 and https://golang.org/issue/34650.)
+func checkMultiplePaths() {
+ firstPath := make(map[module.Version]string, len(buildList))
+ for _, mod := range buildList {
+ src := mod
+ if rep := Replacement(mod); rep.Path != "" {
+ src = rep
+ }
+ if prev, ok := firstPath[src]; !ok {
+ firstPath[src] = mod.Path
+ } else if prev != mod.Path {
+ base.Errorf("go: %s@%s used for two different module paths (%s and %s)", src.Path, src.Version, prev, mod.Path)
+ }
+ }
+ base.ExitIfErrors()
+}
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 9cedc219b61..6050646594b 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -118,22 +118,10 @@ import (
"cmd/go/internal/par"
"cmd/go/internal/search"
"cmd/go/internal/str"
- "cmd/go/internal/trace"
"golang.org/x/mod/module"
)
-// buildList is the list of modules to use for building packages.
-// It is initialized by calling ImportPaths, ImportFromFiles,
-// LoadALL, or LoadBuildList, each of which uses loaded.load.
-//
-// Ideally, exactly ONE of those functions would be called,
-// and exactly once. Most of the time, that's true.
-// During "go get" it may not be. TODO(rsc): Figure out if
-// that restriction can be established, or else document why not.
-//
-var buildList []module.Version
-
// loaded is the most recently-used package loader.
// It holds details about individual packages.
var loaded *loader
@@ -250,26 +238,6 @@ func ImportPathsQuiet(ctx context.Context, patterns []string, tags map[string]bo
return matches
}
-// checkMultiplePaths verifies that a given module path is used as itself
-// or as a replacement for another module, but not both at the same time.
-//
-// (See https://golang.org/issue/26607 and https://golang.org/issue/34650.)
-func checkMultiplePaths() {
- firstPath := make(map[module.Version]string, len(buildList))
- for _, mod := range buildList {
- src := mod
- if rep := Replacement(mod); rep.Path != "" {
- src = rep
- }
- if prev, ok := firstPath[src]; !ok {
- firstPath[src] = mod.Path
- } else if prev != mod.Path {
- base.Errorf("go: %s@%s used for two different module paths (%s and %s)", src.Path, src.Version, prev, mod.Path)
- }
- }
- base.ExitIfErrors()
-}
-
// matchLocalDirs is like m.MatchDirs, but tries to avoid scanning directories
// outside of the standard library and active modules.
func matchLocalDirs(m *search.Match) {
@@ -481,31 +449,6 @@ func DirImportPath(dir string) string {
return "."
}
-// LoadBuildList loads and returns the build list from go.mod.
-// The loading of the build list happens automatically in ImportPaths:
-// LoadBuildList need only be called if ImportPaths is not
-// (typically in commands that care about the module but
-// no particular package).
-func LoadBuildList(ctx context.Context) []module.Version {
- ctx, span := trace.StartSpan(ctx, "LoadBuildList")
- defer span.Done()
- InitMod(ctx)
- ReloadBuildList()
- WriteGoMod()
- return buildList
-}
-
-// ReloadBuildList resets the state of loaded packages, then loads and returns
-// the build list set in SetBuildList.
-func ReloadBuildList() []module.Version {
- loaded = loadFromRoots(loaderParams{
- tags: imports.Tags(),
- listRoots: func() []string { return nil },
- allClosesOverTests: index.allPatternClosesOverTests(), // but doesn't matter because the root list is empty.
- })
- return buildList
-}
-
// LoadALL returns the set of all packages in the current module
// and their dependencies in any other modules, without filtering
// due to build tags, except "+build ignore".
@@ -571,52 +514,6 @@ func TargetPackages(ctx context.Context, pattern string) *search.Match {
return m
}
-// BuildList returns the module build list,
-// typically constructed by a previous call to
-// LoadBuildList or ImportPaths.
-// The caller must not modify the returned list.
-func BuildList() []module.Version {
- return buildList
-}
-
-// SetBuildList sets the module build list.
-// The caller is responsible for ensuring that the list is valid.
-// SetBuildList does not retain a reference to the original list.
-func SetBuildList(list []module.Version) {
- buildList = append([]module.Version{}, list...)
-}
-
-// TidyBuildList trims the build list to the minimal requirements needed to
-// retain the same versions of all packages from the preceding Load* or
-// ImportPaths* call.
-func TidyBuildList() {
- used := map[module.Version]bool{Target: true}
- for _, pkg := range loaded.pkgs {
- used[pkg.mod] = true
- }
-
- keep := []module.Version{Target}
- var direct []string
- for _, m := range buildList[1:] {
- if used[m] {
- keep = append(keep, m)
- if loaded.direct[m.Path] {
- direct = append(direct, m.Path)
- }
- } else if cfg.BuildV {
- if _, ok := index.require[m]; ok {
- fmt.Fprintf(os.Stderr, "unused %s\n", m.Path)
- }
- }
- }
-
- min, err := mvs.Req(Target, direct, &mvsReqs{buildList: keep})
- if err != nil {
- base.Fatalf("go: %v", err)
- }
- buildList = append([]module.Version{Target}, min...)
-}
-
// ImportMap returns the actual package import path
// for an import path found in source code.
// If the given import path does not appear in the source code
From 521393e7e05cd9272ae6023387fa92839d72eb4f Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills"
Date: Mon, 27 Jul 2020 13:57:12 -0400
Subject: [PATCH 0024/1082] cmd/go/internal/modget: move MVS code to a separate
file
For #36460
Change-Id: Ie81c03df18c6987527da765d5f6575556340cb01
Reviewed-on: https://go-review.googlesource.com/c/go/+/249877
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
Reviewed-by: Michael Matloob
Reviewed-by: Jay Conrod
---
src/cmd/go/internal/modget/get.go | 188 +--------------------------
src/cmd/go/internal/modget/mvs.go | 202 ++++++++++++++++++++++++++++++
2 files changed, 203 insertions(+), 187 deletions(-)
create mode 100644 src/cmd/go/internal/modget/mvs.go
diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go
index 126b1f4bd42..cf9ad66b3d0 100644
--- a/src/cmd/go/internal/modget/get.go
+++ b/src/cmd/go/internal/modget/get.go
@@ -290,7 +290,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
// what was requested.
modload.DisallowWriteGoMod()
- // Allow looking up modules for import paths outside of a module.
+ // Allow looking up modules for import paths when outside of a module.
// 'go get' is expected to do this, unlike other commands.
modload.AllowMissingModuleImports()
@@ -885,192 +885,6 @@ func getQuery(ctx context.Context, path, vers string, prevM module.Version, forc
return m, nil
}
-// An upgrader adapts an underlying mvs.Reqs to apply an
-// upgrade policy to a list of targets and their dependencies.
-type upgrader struct {
- mvs.Reqs
-
- // cmdline maps a module path to a query made for that module at a
- // specific target version. Each query corresponds to a module
- // matched by a command line argument.
- cmdline map[string]*query
-
- // upgrade is a set of modules providing dependencies of packages
- // matched by command line arguments. If -u or -u=patch is set,
- // these modules are upgraded accordingly.
- upgrade map[string]bool
-}
-
-// newUpgrader creates an upgrader. cmdline contains queries made at
-// specific versions for modules matched by command line arguments. pkgs
-// is the set of packages matched by command line arguments. If -u or -u=patch
-// is set, modules providing dependencies of pkgs are upgraded accordingly.
-func newUpgrader(cmdline map[string]*query, pkgs map[string]bool) *upgrader {
- u := &upgrader{
- Reqs: modload.Reqs(),
- cmdline: cmdline,
- }
- if getU != "" {
- u.upgrade = make(map[string]bool)
-
- // Traverse package import graph.
- // Initialize work queue with root packages.
- seen := make(map[string]bool)
- var work []string
- add := func(path string) {
- if !seen[path] {
- seen[path] = true
- work = append(work, path)
- }
- }
- for pkg := range pkgs {
- add(pkg)
- }
- for len(work) > 0 {
- pkg := work[0]
- work = work[1:]
- m := modload.PackageModule(pkg)
- u.upgrade[m.Path] = true
-
- // testImports is empty unless test imports were actually loaded,
- // i.e., -t was set or "all" was one of the arguments.
- imports, testImports := modload.PackageImports(pkg)
- for _, imp := range imports {
- add(imp)
- }
- for _, imp := range testImports {
- add(imp)
- }
- }
- }
- return u
-}
-
-// Required returns the requirement list for m.
-// For the main module, we override requirements with the modules named
-// one the command line, and we include new requirements. Otherwise,
-// we defer to u.Reqs.
-func (u *upgrader) Required(m module.Version) ([]module.Version, error) {
- rs, err := u.Reqs.Required(m)
- if err != nil {
- return nil, err
- }
- if m != modload.Target {
- return rs, nil
- }
-
- overridden := make(map[string]bool)
- for i, m := range rs {
- if q := u.cmdline[m.Path]; q != nil && q.m.Version != "none" {
- rs[i] = q.m
- overridden[q.m.Path] = true
- }
- }
- for _, q := range u.cmdline {
- if !overridden[q.m.Path] && q.m.Path != modload.Target.Path && q.m.Version != "none" {
- rs = append(rs, q.m)
- }
- }
- return rs, nil
-}
-
-// Upgrade returns the desired upgrade for m.
-//
-// If m was requested at a specific version on the command line, then
-// Upgrade returns that version.
-//
-// If -u is set and m provides a dependency of a package matched by
-// command line arguments, then Upgrade may provider a newer tagged version.
-// If m is a tagged version, then Upgrade will return the latest tagged
-// version (with the same minor version number if -u=patch).
-// If m is a pseudo-version, then Upgrade returns the latest tagged version
-// only if that version has a time-stamp newer than m. This special case
-// prevents accidental downgrades when already using a pseudo-version
-// newer than the latest tagged version.
-//
-// If none of the above cases apply, then Upgrade returns m.
-func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
- // Allow pkg@vers on the command line to override the upgrade choice v.
- // If q's version is < m.Version, then we're going to downgrade anyway,
- // and it's cleaner to avoid moving back and forth and picking up
- // extraneous other newer dependencies.
- // If q's version is > m.Version, then we're going to upgrade past
- // m.Version anyway, and again it's cleaner to avoid moving back and forth
- // picking up extraneous other newer dependencies.
- if q := u.cmdline[m.Path]; q != nil {
- return q.m, nil
- }
-
- if !u.upgrade[m.Path] {
- // Not involved in upgrade. Leave alone.
- return m, nil
- }
-
- // Run query required by upgrade semantics.
- // Note that Query "latest" is not the same as using repo.Latest,
- // which may return a pseudoversion for the latest commit.
- // Query "latest" returns the newest tagged version or the newest
- // prerelease version if there are no non-prereleases, or repo.Latest
- // if there aren't any tagged versions.
- // If we're querying "upgrade" or "patch", Query will compare the current
- // version against the chosen version and will return the current version
- // if it is newer.
- info, err := modload.Query(context.TODO(), m.Path, string(getU), m.Version, modload.CheckAllowed)
- if err != nil {
- // Report error but return m, to let version selection continue.
- // (Reporting the error will fail the command at the next base.ExitIfErrors.)
-
- // Special case: if the error is for m.Version itself and m.Version has a
- // replacement, then keep it and don't report the error: the fact that the
- // version is invalid is likely the reason it was replaced to begin with.
- var vErr *module.InvalidVersionError
- if errors.As(err, &vErr) && vErr.Version == m.Version && modload.Replacement(m).Path != "" {
- return m, nil
- }
-
- // Special case: if the error is "no matching versions" then don't
- // even report the error. Because Query does not consider pseudo-versions,
- // it may happen that we have a pseudo-version but during -u=patch
- // the query v0.0 matches no versions (not even the one we're using).
- var noMatch *modload.NoMatchingVersionError
- if !errors.As(err, &noMatch) {
- base.Errorf("go get: upgrading %s@%s: %v", m.Path, m.Version, err)
- }
- return m, nil
- }
-
- if info.Version != m.Version {
- logOncef("go: %s %s => %s", m.Path, getU, info.Version)
- }
- return module.Version{Path: m.Path, Version: info.Version}, nil
-}
-
-// buildListForLostUpgrade returns the build list for the module graph
-// rooted at lost. Unlike mvs.BuildList, the target module (lost) is not
-// treated specially. The returned build list may contain a newer version
-// of lost.
-//
-// buildListForLostUpgrade is used after a downgrade has removed a module
-// requested at a specific version. This helps us understand the requirements
-// implied by each downgrade.
-func buildListForLostUpgrade(lost module.Version, reqs mvs.Reqs) ([]module.Version, error) {
- return mvs.BuildList(lostUpgradeRoot, &lostUpgradeReqs{Reqs: reqs, lost: lost})
-}
-
-var lostUpgradeRoot = module.Version{Path: "lost-upgrade-root", Version: ""}
-
-type lostUpgradeReqs struct {
- mvs.Reqs
- lost module.Version
-}
-
-func (r *lostUpgradeReqs) Required(mod module.Version) ([]module.Version, error) {
- if mod == lostUpgradeRoot {
- return []module.Version{r.lost}, nil
- }
- return r.Reqs.Required(mod)
-}
-
// reportRetractions prints warnings if any modules in the build list are
// retracted.
func reportRetractions(ctx context.Context) {
diff --git a/src/cmd/go/internal/modget/mvs.go b/src/cmd/go/internal/modget/mvs.go
new file mode 100644
index 00000000000..19fffd2947f
--- /dev/null
+++ b/src/cmd/go/internal/modget/mvs.go
@@ -0,0 +1,202 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modget
+
+import (
+ "context"
+ "errors"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/modload"
+ "cmd/go/internal/mvs"
+
+ "golang.org/x/mod/module"
+)
+
+// An upgrader adapts an underlying mvs.Reqs to apply an
+// upgrade policy to a list of targets and their dependencies.
+type upgrader struct {
+ mvs.Reqs
+
+ // cmdline maps a module path to a query made for that module at a
+ // specific target version. Each query corresponds to a module
+ // matched by a command line argument.
+ cmdline map[string]*query
+
+ // upgrade is a set of modules providing dependencies of packages
+ // matched by command line arguments. If -u or -u=patch is set,
+ // these modules are upgraded accordingly.
+ upgrade map[string]bool
+}
+
+// newUpgrader creates an upgrader. cmdline contains queries made at
+// specific versions for modules matched by command line arguments. pkgs
+// is the set of packages matched by command line arguments. If -u or -u=patch
+// is set, modules providing dependencies of pkgs are upgraded accordingly.
+func newUpgrader(cmdline map[string]*query, pkgs map[string]bool) *upgrader {
+ u := &upgrader{
+ Reqs: modload.Reqs(),
+ cmdline: cmdline,
+ }
+ if getU != "" {
+ u.upgrade = make(map[string]bool)
+
+ // Traverse package import graph.
+ // Initialize work queue with root packages.
+ seen := make(map[string]bool)
+ var work []string
+ add := func(path string) {
+ if !seen[path] {
+ seen[path] = true
+ work = append(work, path)
+ }
+ }
+ for pkg := range pkgs {
+ add(pkg)
+ }
+ for len(work) > 0 {
+ pkg := work[0]
+ work = work[1:]
+ m := modload.PackageModule(pkg)
+ u.upgrade[m.Path] = true
+
+ // testImports is empty unless test imports were actually loaded,
+ // i.e., -t was set or "all" was one of the arguments.
+ imports, testImports := modload.PackageImports(pkg)
+ for _, imp := range imports {
+ add(imp)
+ }
+ for _, imp := range testImports {
+ add(imp)
+ }
+ }
+ }
+ return u
+}
+
+// Required returns the requirement list for m.
+// For the main module, we override requirements with the modules named
+// one the command line, and we include new requirements. Otherwise,
+// we defer to u.Reqs.
+func (u *upgrader) Required(m module.Version) ([]module.Version, error) {
+ rs, err := u.Reqs.Required(m)
+ if err != nil {
+ return nil, err
+ }
+ if m != modload.Target {
+ return rs, nil
+ }
+
+ overridden := make(map[string]bool)
+ for i, m := range rs {
+ if q := u.cmdline[m.Path]; q != nil && q.m.Version != "none" {
+ rs[i] = q.m
+ overridden[q.m.Path] = true
+ }
+ }
+ for _, q := range u.cmdline {
+ if !overridden[q.m.Path] && q.m.Path != modload.Target.Path && q.m.Version != "none" {
+ rs = append(rs, q.m)
+ }
+ }
+ return rs, nil
+}
+
+// Upgrade returns the desired upgrade for m.
+//
+// If m was requested at a specific version on the command line, then
+// Upgrade returns that version.
+//
+// If -u is set and m provides a dependency of a package matched by
+// command line arguments, then Upgrade may provider a newer tagged version.
+// If m is a tagged version, then Upgrade will return the latest tagged
+// version (with the same minor version number if -u=patch).
+// If m is a pseudo-version, then Upgrade returns the latest tagged version
+// only if that version has a time-stamp newer than m. This special case
+// prevents accidental downgrades when already using a pseudo-version
+// newer than the latest tagged version.
+//
+// If none of the above cases apply, then Upgrade returns m.
+func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
+ // Allow pkg@vers on the command line to override the upgrade choice v.
+ // If q's version is < m.Version, then we're going to downgrade anyway,
+ // and it's cleaner to avoid moving back and forth and picking up
+ // extraneous other newer dependencies.
+ // If q's version is > m.Version, then we're going to upgrade past
+ // m.Version anyway, and again it's cleaner to avoid moving back and forth
+ // picking up extraneous other newer dependencies.
+ if q := u.cmdline[m.Path]; q != nil {
+ return q.m, nil
+ }
+
+ if !u.upgrade[m.Path] {
+ // Not involved in upgrade. Leave alone.
+ return m, nil
+ }
+
+ // Run query required by upgrade semantics.
+ // Note that Query "latest" is not the same as using repo.Latest,
+ // which may return a pseudoversion for the latest commit.
+ // Query "latest" returns the newest tagged version or the newest
+ // prerelease version if there are no non-prereleases, or repo.Latest
+ // if there aren't any tagged versions.
+ // If we're querying "upgrade" or "patch", Query will compare the current
+ // version against the chosen version and will return the current version
+ // if it is newer.
+ info, err := modload.Query(context.TODO(), m.Path, string(getU), m.Version, modload.CheckAllowed)
+ if err != nil {
+ // Report error but return m, to let version selection continue.
+ // (Reporting the error will fail the command at the next base.ExitIfErrors.)
+
+ // Special case: if the error is for m.Version itself and m.Version has a
+ // replacement, then keep it and don't report the error: the fact that the
+ // version is invalid is likely the reason it was replaced to begin with.
+ var vErr *module.InvalidVersionError
+ if errors.As(err, &vErr) && vErr.Version == m.Version && modload.Replacement(m).Path != "" {
+ return m, nil
+ }
+
+ // Special case: if the error is "no matching versions" then don't
+ // even report the error. Because Query does not consider pseudo-versions,
+ // it may happen that we have a pseudo-version but during -u=patch
+ // the query v0.0 matches no versions (not even the one we're using).
+ var noMatch *modload.NoMatchingVersionError
+ if !errors.As(err, &noMatch) {
+ base.Errorf("go get: upgrading %s@%s: %v", m.Path, m.Version, err)
+ }
+ return m, nil
+ }
+
+ if info.Version != m.Version {
+ logOncef("go: %s %s => %s", m.Path, getU, info.Version)
+ }
+ return module.Version{Path: m.Path, Version: info.Version}, nil
+}
+
+// buildListForLostUpgrade returns the build list for the module graph
+// rooted at lost. Unlike mvs.BuildList, the target module (lost) is not
+// treated specially. The returned build list may contain a newer version
+// of lost.
+//
+// buildListForLostUpgrade is used after a downgrade has removed a module
+// requested at a specific version. This helps us understand the requirements
+// implied by each downgrade.
+func buildListForLostUpgrade(lost module.Version, reqs mvs.Reqs) ([]module.Version, error) {
+ return mvs.BuildList(lostUpgradeRoot, &lostUpgradeReqs{Reqs: reqs, lost: lost})
+}
+
+var lostUpgradeRoot = module.Version{Path: "lost-upgrade-root", Version: ""}
+
+type lostUpgradeReqs struct {
+ mvs.Reqs
+ lost module.Version
+}
+
+func (r *lostUpgradeReqs) Required(mod module.Version) ([]module.Version, error) {
+ if mod == lostUpgradeRoot {
+ return []module.Version{r.lost}, nil
+ }
+ return r.Reqs.Required(mod)
+}
From 564b350c08a1906e8f6a876fef4cca71f6516d4c Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills"
Date: Mon, 27 Jul 2020 12:57:36 -0400
Subject: [PATCH 0025/1082] cmd/go/internal/modload: rename LoadBuildList and
BuildList
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
With lazy loading, the “build list” can be refined as packages are loaded.
Rename functions that return the build list to more precisely describe
the set of modules returned by the call.
Also eliminate a redundant call to LoadBuildList (right before
ListModules, which itself begins with the same call).
For #36460
Change-Id: I0fc4f9dd7602e0df5e166e329ee5d516d810ca53
Reviewed-on: https://go-review.googlesource.com/c/go/+/249878
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
Reviewed-by: Jay Conrod
Reviewed-by: Michael Matloob
---
src/cmd/go/internal/list/list.go | 2 -
src/cmd/go/internal/modcmd/graph.go | 2 +-
src/cmd/go/internal/modcmd/vendor.go | 2 +-
src/cmd/go/internal/modcmd/verify.go | 2 +-
src/cmd/go/internal/modget/get.go | 10 ++---
src/cmd/go/internal/modload/build.go | 2 +-
src/cmd/go/internal/modload/buildlist.go | 47 +++++++++++++-----------
src/cmd/go/internal/modload/list.go | 2 +-
8 files changed, 36 insertions(+), 33 deletions(-)
diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go
index 65003dc883a..23500dd9d8e 100644
--- a/src/cmd/go/internal/list/list.go
+++ b/src/cmd/go/internal/list/list.go
@@ -437,8 +437,6 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
}
}
- modload.LoadBuildList(ctx)
-
mods := modload.ListModules(ctx, args, *listU, *listVersions, *listRetracted)
if !*listE {
for _, m := range mods {
diff --git a/src/cmd/go/internal/modcmd/graph.go b/src/cmd/go/internal/modcmd/graph.go
index 6da12b9cab2..513536a010c 100644
--- a/src/cmd/go/internal/modcmd/graph.go
+++ b/src/cmd/go/internal/modcmd/graph.go
@@ -48,7 +48,7 @@ func runGraph(ctx context.Context, cmd *base.Command, args []string) {
base.Fatalf("go: cannot find main module; see 'go help modules'")
}
}
- modload.LoadBuildList(ctx)
+ modload.LoadAllModules(ctx)
reqs := modload.MinReqs()
format := func(m module.Version) string {
diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go
index e5353b5c7fe..30334f3a429 100644
--- a/src/cmd/go/internal/modcmd/vendor.go
+++ b/src/cmd/go/internal/modcmd/vendor.go
@@ -77,7 +77,7 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) {
}
var buf bytes.Buffer
- for _, m := range modload.BuildList()[1:] {
+ for _, m := range modload.LoadedModules()[1:] {
if pkgs := modpkgs[m]; len(pkgs) > 0 || isExplicit[m] {
line := moduleLine(m, modload.Replacement(m))
buf.WriteString(line)
diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go
index 73ab714d101..d5428258234 100644
--- a/src/cmd/go/internal/modcmd/verify.go
+++ b/src/cmd/go/internal/modcmd/verify.go
@@ -60,7 +60,7 @@ func runVerify(ctx context.Context, cmd *base.Command, args []string) {
sem := make(chan token, runtime.GOMAXPROCS(0))
// Use a slice of result channels, so that the output is deterministic.
- mods := modload.LoadBuildList(ctx)[1:]
+ mods := modload.LoadAllModules(ctx)[1:]
errsChans := make([]<-chan []error, len(mods))
for i, mod := range mods {
diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go
index cf9ad66b3d0..a2a8287d846 100644
--- a/src/cmd/go/internal/modget/get.go
+++ b/src/cmd/go/internal/modget/get.go
@@ -278,7 +278,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
}
modload.LoadTests = *getT
- buildList := modload.LoadBuildList(ctx)
+ buildList := modload.LoadAllModules(ctx)
buildList = buildList[:len(buildList):len(buildList)] // copy on append
versionByPath := make(map[string]string)
for _, m := range buildList {
@@ -599,7 +599,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
base.ExitIfErrors()
// Stop if no changes have been made to the build list.
- buildList = modload.BuildList()
+ buildList = modload.LoadedModules()
eq := len(buildList) == len(prevBuildList)
for i := 0; eq && i < len(buildList); i++ {
eq = buildList[i] == prevBuildList[i]
@@ -617,7 +617,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
// Handle downgrades.
var down []module.Version
- for _, m := range modload.BuildList() {
+ for _, m := range modload.LoadedModules() {
q := byPath[m.Path]
if q != nil && semver.Compare(m.Version, q.m.Version) > 0 {
down = append(down, module.Version{Path: m.Path, Version: q.m.Version})
@@ -641,7 +641,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
var lostUpgrades []*query
if len(down) > 0 {
versionByPath = make(map[string]string)
- for _, m := range modload.BuildList() {
+ for _, m := range modload.LoadedModules() {
versionByPath[m.Path] = m.Version
}
for _, q := range byPath {
@@ -892,7 +892,7 @@ func reportRetractions(ctx context.Context) {
// Use modload.ListModules, since that provides information in the same format
// as 'go list -m'. Don't query for "all", since that's not allowed outside a
// module.
- buildList := modload.BuildList()
+ buildList := modload.LoadedModules()
args := make([]string, 0, len(buildList))
for _, m := range buildList {
if m.Version == "" {
diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go
index e9f9a82fabb..9ca6230500e 100644
--- a/src/cmd/go/internal/modload/build.go
+++ b/src/cmd/go/internal/modload/build.go
@@ -76,7 +76,7 @@ func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic {
return moduleInfo(ctx, m, fromBuildList, listRetracted)
}
- for _, m := range BuildList() {
+ for _, m := range LoadedModules() {
if m.Path == path {
fromBuildList := true
return moduleInfo(ctx, m, fromBuildList, listRetracted)
diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go
index 2302b044e81..581a1b944ac 100644
--- a/src/cmd/go/internal/modload/buildlist.go
+++ b/src/cmd/go/internal/modload/buildlist.go
@@ -27,18 +27,38 @@ import (
//
var buildList []module.Version
-// LoadBuildList loads and returns the build list from go.mod.
-// The loading of the build list happens automatically in ImportPaths:
-// LoadBuildList need only be called if ImportPaths is not
-// (typically in commands that care about the module but
-// no particular package).
-func LoadBuildList(ctx context.Context) []module.Version {
+// LoadAllModules loads and returns the list of modules matching the "all"
+// module pattern, starting with the Target module and in a deterministic
+// (stable) order, without loading any packages.
+//
+// Modules are loaded automatically (and lazily) in ImportPaths:
+// LoadAllModules need only be called if ImportPaths is not,
+// typically in commands that care about modules but no particular package.
+//
+// The caller must not modify the returned list.
+func LoadAllModules(ctx context.Context) []module.Version {
InitMod(ctx)
ReloadBuildList()
WriteGoMod()
return buildList
}
+// LoadedModules returns the list of module requirements loaded or set by a
+// previous call (typically LoadAllModules or ImportPaths), starting with the
+// Target module and in a deterministic (stable) order.
+//
+// The caller must not modify the returned list.
+func LoadedModules() []module.Version {
+ return buildList
+}
+
+// SetBuildList sets the module build list.
+// The caller is responsible for ensuring that the list is valid.
+// SetBuildList does not retain a reference to the original list.
+func SetBuildList(list []module.Version) {
+ buildList = append([]module.Version{}, list...)
+}
+
// ReloadBuildList resets the state of loaded packages, then loads and returns
// the build list set in SetBuildList.
func ReloadBuildList() []module.Version {
@@ -50,21 +70,6 @@ func ReloadBuildList() []module.Version {
return buildList
}
-// BuildList returns the module build list,
-// typically constructed by a previous call to
-// LoadBuildList or ImportPaths.
-// The caller must not modify the returned list.
-func BuildList() []module.Version {
- return buildList
-}
-
-// SetBuildList sets the module build list.
-// The caller is responsible for ensuring that the list is valid.
-// SetBuildList does not retain a reference to the original list.
-func SetBuildList(list []module.Version) {
- buildList = append([]module.Version{}, list...)
-}
-
// TidyBuildList trims the build list to the minimal requirements needed to
// retain the same versions of all packages from the preceding Load* or
// ImportPaths* call.
diff --git a/src/cmd/go/internal/modload/list.go b/src/cmd/go/internal/modload/list.go
index 8c7b9a3950b..3491f941cd3 100644
--- a/src/cmd/go/internal/modload/list.go
+++ b/src/cmd/go/internal/modload/list.go
@@ -58,7 +58,7 @@ func ListModules(ctx context.Context, args []string, listU, listVersions, listRe
}
func listModules(ctx context.Context, args []string, listVersions, listRetracted bool) []*modinfo.ModulePublic {
- LoadBuildList(ctx)
+ LoadAllModules(ctx)
if len(args) == 0 {
return []*modinfo.ModulePublic{moduleInfo(ctx, buildList[0], true, listRetracted)}
}
From aa476ba6f43ebc4e7ddb6599a7ad35d9fbf1ec6d Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills"
Date: Tue, 1 Sep 2020 00:34:03 -0400
Subject: [PATCH 0026/1082] cmd/go/internal/modload: refactor pathInModuleCache
I found the control flow of this function a bit tricky to reason about
due to nesting and interaction between conditions and iteration. This
change factors out a helper function that can return early instead of
mixing conditionals and 'continue' statements.
Also remove the (unused) ModuleUsedDirectly function.
For #36460
Change-Id: I60a2a5a1b32989e5a17a14e1a8c858b280cda8f2
Reviewed-on: https://go-review.googlesource.com/c/go/+/251998
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
Reviewed-by: Jay Conrod
Reviewed-by: Michael Matloob
---
src/cmd/go/internal/modload/load.go | 33 +++++++++++++++++------------
1 file changed, 20 insertions(+), 13 deletions(-)
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 6050646594b..1664d8c5be8 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -374,7 +374,7 @@ var (
// pathInModuleCache returns the import path of the directory dir,
// if dir is in the module cache copy of a module in our build list.
func pathInModuleCache(dir string) string {
- for _, m := range buildList[1:] {
+ tryMod := func(m module.Version) (string, bool) {
var root string
var err error
if repl := Replacement(m); repl.Path != "" && repl.Version == "" {
@@ -388,13 +388,26 @@ func pathInModuleCache(dir string) string {
root, err = modfetch.DownloadDir(m)
}
if err != nil {
- continue
+ return "", false
}
- if sub := search.InDir(dir, root); sub != "" {
- sub = filepath.ToSlash(sub)
- if !strings.Contains(sub, "/vendor/") && !strings.HasPrefix(sub, "vendor/") && !strings.Contains(sub, "@") {
- return path.Join(m.Path, filepath.ToSlash(sub))
- }
+
+ sub := search.InDir(dir, root)
+ if sub == "" {
+ return "", false
+ }
+ sub = filepath.ToSlash(sub)
+ if strings.Contains(sub, "/vendor/") || strings.HasPrefix(sub, "vendor/") || strings.Contains(sub, "@") {
+ return "", false
+ }
+
+ return path.Join(m.Path, filepath.ToSlash(sub)), true
+ }
+
+ for _, m := range buildList[1:] {
+ if importPath, ok := tryMod(m); ok {
+ // checkMultiplePaths ensures that a module can be used for at most one
+ // requirement, so this must be it.
+ return importPath
}
}
return ""
@@ -568,12 +581,6 @@ func PackageImports(path string) (imports, testImports []string) {
return imports, testImports
}
-// ModuleUsedDirectly reports whether the main module directly imports
-// some package in the module with the given path.
-func ModuleUsedDirectly(path string) bool {
- return loaded.direct[path]
-}
-
// Lookup returns the source directory, import path, and any loading error for
// the package at path as imported from the package in parentDir.
// Lookup requires that one of the Load functions in this package has already
From dfdc3880b01d46d1d8125ab9eea0606b2fa5b819 Mon Sep 17 00:00:00 2001
From: fanzha02
Date: Thu, 20 Aug 2020 17:02:18 +0800
Subject: [PATCH 0027/1082] cmd/internal/obj/arm64: enable some SIMD
instructions
Enable VBSL, VBIT, VCMTST, VUXTL VUXTL2 and FMOVQ SIMD
instructions required by the issue #40725. And FMOVQ
instrucion is used to move a large constant to a Vn
register.
Add test cases.
Fixes #40725
Change-Id: I1cac1922a0a0165d698a4b73a41f7a5f0a0ad549
Reviewed-on: https://go-review.googlesource.com/c/go/+/249758
Reviewed-by: Cherry Zhang
---
src/cmd/asm/internal/asm/testdata/arm64.s | 15 +++
.../asm/internal/asm/testdata/arm64error.s | 5 +
src/cmd/internal/obj/arm64/a.out.go | 6 +
src/cmd/internal/obj/arm64/anames.go | 6 +
src/cmd/internal/obj/arm64/asm7.go | 121 ++++++++++++++++--
5 files changed, 139 insertions(+), 14 deletions(-)
diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s
index f0c716a2b5d..451ca749ba0 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64.s
@@ -145,6 +145,17 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
VZIP2 V10.D2, V13.D2, V3.D2 // a379ca4e
VZIP1 V17.S2, V4.S2, V26.S2 // 9a38910e
VZIP2 V25.S2, V14.S2, V25.S2 // d979990e
+ VUXTL V30.B8, V30.H8 // dea7082f
+ VUXTL V30.H4, V29.S4 // dda7102f
+ VUXTL V29.S2, V2.D2 // a2a7202f
+ VUXTL2 V30.H8, V30.S4 // dea7106f
+ VUXTL2 V29.S4, V2.D2 // a2a7206f
+ VUXTL2 V30.B16, V2.H8 // c2a7086f
+ VBIT V21.B16, V25.B16, V4.B16 // 241fb56e
+ VBSL V23.B16, V3.B16, V7.B16 // 671c776e
+ VCMTST V2.B8, V29.B8, V2.B8 // a28f220e
+ VCMTST V2.D2, V23.D2, V3.D2 // e38ee24e
+ VSUB V2.B8, V30.B8, V30.B8 // de87222e
MOVD (R2)(R6.SXTW), R4 // 44c866f8
MOVD (R3)(R6), R5 // MOVD (R3)(R6*1), R5 // 656866f8
MOVD (R2)(R6), R4 // MOVD (R2)(R6*1), R4 // 446866f8
@@ -186,6 +197,10 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
FMOVS $(0.96875), F3 // 03f02d1e
FMOVD $(28.0), F4 // 0490671e
+// move a large constant to a Vd.
+ FMOVD $0x8040201008040201, V20 // FMOVD $-9205322385119247871, V20
+ FMOVQ $0x8040201008040202, V29 // FMOVQ $-9205322385119247870, V29
+
FMOVS (R2)(R6), F4 // FMOVS (R2)(R6*1), F4 // 446866bc
FMOVS (R2)(R6<<2), F4 // 447866bc
FMOVD (R2)(R6), F4 // FMOVD (R2)(R6*1), F4 // 446866fc
diff --git a/src/cmd/asm/internal/asm/testdata/arm64error.s b/src/cmd/asm/internal/asm/testdata/arm64error.s
index 9f377817a90..2a911b4cced 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64error.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64error.s
@@ -340,4 +340,9 @@ TEXT errors(SB),$0
MRS PMSWINC_EL0, R3 // ERROR "system register is not readable"
MRS OSLAR_EL1, R3 // ERROR "system register is not readable"
VLD3R.P 24(R15), [V15.H4,V16.H4,V17.H4] // ERROR "invalid post-increment offset"
+ VBIT V1.H4, V12.H4, V3.H4 // ERROR "invalid arrangement"
+ VBSL V1.D2, V12.D2, V3.D2 // ERROR "invalid arrangement"
+ VUXTL V30.D2, V30.H8 // ERROR "operand mismatch"
+ VUXTL2 V20.B8, V21.H8 // ERROR "operand mismatch"
+ VUXTL V3.D2, V4.B8 // ERROR "operand mismatch"
RET
diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go
index 03e0278a33e..ab065e07e5d 100644
--- a/src/cmd/internal/obj/arm64/a.out.go
+++ b/src/cmd/internal/obj/arm64/a.out.go
@@ -874,6 +874,7 @@ const (
AFLDPS
AFMOVD
AFMOVS
+ AFMOVQ
AFMULD
AFMULS
AFNEGD
@@ -987,9 +988,14 @@ const (
AVUSHR
AVSHL
AVSRI
+ AVBSL
+ AVBIT
AVTBL
AVZIP1
AVZIP2
+ AVCMTST
+ AVUXTL
+ AVUXTL2
ALAST
AB = obj.AJMP
ABL = obj.ACALL
diff --git a/src/cmd/internal/obj/arm64/anames.go b/src/cmd/internal/obj/arm64/anames.go
index 65ecd007ea3..8961f04b0cb 100644
--- a/src/cmd/internal/obj/arm64/anames.go
+++ b/src/cmd/internal/obj/arm64/anames.go
@@ -381,6 +381,7 @@ var Anames = []string{
"FLDPS",
"FMOVD",
"FMOVS",
+ "FMOVQ",
"FMULD",
"FMULS",
"FNEGD",
@@ -494,8 +495,13 @@ var Anames = []string{
"VUSHR",
"VSHL",
"VSRI",
+ "VBSL",
+ "VBIT",
"VTBL",
"VZIP1",
"VZIP2",
+ "VCMTST",
+ "VUXTL",
+ "VUXTL2",
"LAST",
}
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index 0b90e313924..7ce18d0f137 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -393,6 +393,11 @@ var optab = []Optab{
{AMOVK, C_VCON, C_NONE, C_NONE, C_REG, 33, 4, 0, 0, 0},
{AMOVD, C_AACON, C_NONE, C_NONE, C_REG, 4, 4, REGFROM, 0, 0},
+ // Move a large constant to a Vn.
+ {AFMOVQ, C_VCON, C_NONE, C_NONE, C_VREG, 101, 4, 0, LFROM, 0},
+ {AFMOVD, C_VCON, C_NONE, C_NONE, C_VREG, 101, 4, 0, LFROM, 0},
+ {AFMOVS, C_LCON, C_NONE, C_NONE, C_VREG, 101, 4, 0, LFROM, 0},
+
/* jump operations */
{AB, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0},
{ABL, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0},
@@ -403,12 +408,14 @@ var optab = []Optab{
{obj.ARET, C_NONE, C_NONE, C_NONE, C_REG, 6, 4, 0, 0, 0},
{obj.ARET, C_NONE, C_NONE, C_NONE, C_ZOREG, 6, 4, 0, 0, 0},
{ABEQ, C_NONE, C_NONE, C_NONE, C_SBRA, 7, 4, 0, 0, 0},
- {AADRP, C_SBRA, C_NONE, C_NONE, C_REG, 60, 4, 0, 0, 0},
- {AADR, C_SBRA, C_NONE, C_NONE, C_REG, 61, 4, 0, 0, 0},
{ACBZ, C_REG, C_NONE, C_NONE, C_SBRA, 39, 4, 0, 0, 0},
{ATBZ, C_VCON, C_REG, C_NONE, C_SBRA, 40, 4, 0, 0, 0},
{AERET, C_NONE, C_NONE, C_NONE, C_NONE, 41, 4, 0, 0, 0},
+ // get a PC-relative address
+ {AADRP, C_SBRA, C_NONE, C_NONE, C_REG, 60, 4, 0, 0, 0},
+ {AADR, C_SBRA, C_NONE, C_NONE, C_REG, 61, 4, 0, 0, 0},
+
{ACLREX, C_NONE, C_NONE, C_NONE, C_VCON, 38, 4, 0, 0, 0},
{ACLREX, C_NONE, C_NONE, C_NONE, C_NONE, 38, 4, 0, 0, 0},
{ABFM, C_VCON, C_REG, C_VCON, C_REG, 42, 4, 0, 0, 0},
@@ -473,6 +480,7 @@ var optab = []Optab{
{AVTBL, C_ARNG, C_NONE, C_LIST, C_ARNG, 100, 4, 0, 0, 0},
{AVUSHR, C_VCON, C_ARNG, C_NONE, C_ARNG, 95, 4, 0, 0, 0},
{AVZIP1, C_ARNG, C_ARNG, C_NONE, C_ARNG, 72, 4, 0, 0, 0},
+ {AVUXTL, C_ARNG, C_NONE, C_NONE, C_ARNG, 102, 4, 0, 0, 0},
/* conditional operations */
{ACSEL, C_COND, C_REG, C_REG, C_REG, 18, 4, 0, 0, 0},
@@ -2657,7 +2665,7 @@ func buildop(ctxt *obj.Link) {
case AFCSELD:
oprangeset(AFCSELS, t)
- case AFMOVS, AFMOVD:
+ case AFMOVS, AFMOVD, AFMOVQ:
break
case AFCVTZSD:
@@ -2740,6 +2748,9 @@ func buildop(ctxt *obj.Link) {
oprangeset(AVCMEQ, t)
oprangeset(AVORR, t)
oprangeset(AVEOR, t)
+ oprangeset(AVBSL, t)
+ oprangeset(AVBIT, t)
+ oprangeset(AVCMTST, t)
case AVADD:
oprangeset(AVSUB, t)
@@ -2787,6 +2798,9 @@ func buildop(ctxt *obj.Link) {
case AVZIP1:
oprangeset(AVZIP2, t)
+ case AVUXTL:
+ oprangeset(AVUXTL2, t)
+
case AVLD1R:
oprangeset(AVLD2, t)
oprangeset(AVLD2R, t)
@@ -4163,7 +4177,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
rel.Add = 0
rel.Type = objabi.R_ARM64_GOTPCREL
- case 72: /* vaddp/vand/vcmeq/vorr/vadd/veor/vfmla/vfmls Vm., Vn., Vd. */
+ case 72: /* vaddp/vand/vcmeq/vorr/vadd/veor/vfmla/vfmls/vbit/vbsl/vcmtst/vsub Vm., Vn., Vd. */
af := int((p.From.Reg >> 5) & 15)
af3 := int((p.Reg >> 5) & 15)
at := int((p.To.Reg >> 5) & 15)
@@ -4204,17 +4218,24 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
c.ctxt.Diag("invalid arrangement: %v", p)
}
- if (p.As == AVORR || p.As == AVAND || p.As == AVEOR) &&
- (af != ARNG_16B && af != ARNG_8B) {
- c.ctxt.Diag("invalid arrangement: %v", p)
- } else if (p.As == AVFMLA || p.As == AVFMLS) &&
- (af != ARNG_2D && af != ARNG_2S && af != ARNG_4S) {
- c.ctxt.Diag("invalid arrangement: %v", p)
- } else if p.As == AVORR {
- size = 2
- } else if p.As == AVAND || p.As == AVEOR {
+ switch p.As {
+ case AVORR, AVAND, AVEOR, AVBIT, AVBSL:
+ if af != ARNG_16B && af != ARNG_8B {
+ c.ctxt.Diag("invalid arrangement: %v", p)
+ }
+ case AVFMLA, AVFMLS:
+ if af != ARNG_2D && af != ARNG_2S && af != ARNG_4S {
+ c.ctxt.Diag("invalid arrangement: %v", p)
+ }
+ }
+ switch p.As {
+ case AVAND, AVEOR:
size = 0
- } else if p.As == AVFMLA || p.As == AVFMLS {
+ case AVBSL:
+ size = 1
+ case AVORR, AVBIT:
+ size = 2
+ case AVFMLA, AVFMLS:
if af == ARNG_2D {
size = 1
} else {
@@ -5096,6 +5117,59 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
o1 = q<<30 | 0xe<<24 | len<<13
o1 |= (uint32(rf&31) << 16) | uint32(offset&31)<<5 | uint32(rt&31)
+ case 101: // FOMVQ/FMOVD $vcon, Vd -> load from constant pool.
+ o1 = c.omovlit(p.As, p, &p.From, int(p.To.Reg))
+
+ case 102: // VUXTL{2} Vn., Vd.
+ af := int((p.From.Reg >> 5) & 15)
+ at := int((p.To.Reg >> 5) & 15)
+ var Q, immh uint32
+ switch at {
+ case ARNG_8H:
+ if af == ARNG_8B {
+ immh = 1
+ Q = 0
+ } else if af == ARNG_16B {
+ immh = 1
+ Q = 1
+ } else {
+ c.ctxt.Diag("operand mismatch: %v\n", p)
+ }
+ case ARNG_4S:
+ if af == ARNG_4H {
+ immh = 2
+ Q = 0
+ } else if af == ARNG_8H {
+ immh = 2
+ Q = 1
+ } else {
+ c.ctxt.Diag("operand mismatch: %v\n", p)
+ }
+ case ARNG_2D:
+ if af == ARNG_2S {
+ immh = 4
+ Q = 0
+ } else if af == ARNG_4S {
+ immh = 4
+ Q = 1
+ } else {
+ c.ctxt.Diag("operand mismatch: %v\n", p)
+ }
+ default:
+ c.ctxt.Diag("operand mismatch: %v\n", p)
+ }
+
+ if p.As == AVUXTL && Q == 1 {
+ c.ctxt.Diag("operand mismatch: %v\n", p)
+ }
+ if p.As == AVUXTL2 && Q == 0 {
+ c.ctxt.Diag("operand mismatch: %v\n", p)
+ }
+
+ o1 = c.oprrr(p, p.As)
+ rf := int((p.From.Reg) & 31)
+ rt := int((p.To.Reg) & 31)
+ o1 |= Q<<30 | immh<<19 | uint32((rf&31)<<5) | uint32(rt&31)
}
out[0] = o1
out[1] = o2
@@ -5662,6 +5736,9 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 {
case AVADD:
return 7<<25 | 1<<21 | 1<<15 | 1<<10
+ case AVSUB:
+ return 0x17<<25 | 1<<21 | 1<<15 | 1<<10
+
case AVADDP:
return 7<<25 | 1<<21 | 1<<15 | 15<<10
@@ -5724,6 +5801,18 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 {
case AVLD2R, AVLD4R:
return 0xD<<24 | 3<<21
+
+ case AVBIT:
+ return 1<<29 | 0x75<<21 | 7<<10
+
+ case AVBSL:
+ return 1<<29 | 0x73<<21 | 7<<10
+
+ case AVCMTST:
+ return 0xE<<24 | 1<<21 | 0x23<<10
+
+ case AVUXTL, AVUXTL2:
+ return 0x5e<<23 | 0x29<<10
}
c.ctxt.Diag("%v: bad rrr %d %v", p, a, a)
@@ -6566,6 +6655,10 @@ func (c *ctxt7) omovlit(as obj.As, p *obj.Prog, a *obj.Addr, dr int) uint32 {
fp = 1
w = 1 /* 64-bit SIMD/FP */
+ case AFMOVQ:
+ fp = 1
+ w = 2 /* 128-bit SIMD/FP */
+
case AMOVD:
if p.Pool.As == ADWORD {
w = 1 /* 64-bit */
From 07d19b2597af253ed78ef43ba6e7a49db9a8f4ba Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Thu, 10 Sep 2020 13:21:41 +0700
Subject: [PATCH 0028/1082] all: check GOROOT_BOOTSTRAP executable before
bootsrappping
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Otherwise we’d still be writing that we're building cmd/dist
even if GOROOT_BOOTSTRAP we’re wrongly set.
Change-Id: I940e18c3cebae5664f85babc9919e9eb215d5093
Reviewed-on: https://go-review.googlesource.com/c/go/+/253877
Run-TryBot: Cuong Manh Le
TryBot-Result: Gobot Gobot
Reviewed-by: Emmanuel Odeke
---
src/make.bash | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/make.bash b/src/make.bash
index 880a0f43d59..3a84658c72d 100755
--- a/src/make.bash
+++ b/src/make.bash
@@ -162,16 +162,16 @@ IFS=$'\n'; for go_exe in $(type -ap go); do
fi
fi
done; unset IFS
-GOROOT_BOOTSTRAP_VERSION=$($GOROOT_BOOTSTRAP/bin/go version | sed 's/go version //')
-echo "Building Go cmd/dist using $GOROOT_BOOTSTRAP. ($GOROOT_BOOTSTRAP_VERSION)"
-if $verbose; then
- echo cmd/dist
-fi
if [ ! -x "$GOROOT_BOOTSTRAP/bin/go" ]; then
echo "ERROR: Cannot find $GOROOT_BOOTSTRAP/bin/go." >&2
echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4." >&2
exit 1
fi
+GOROOT_BOOTSTRAP_VERSION=$($GOROOT_BOOTSTRAP/bin/go version | sed 's/go version //')
+echo "Building Go cmd/dist using $GOROOT_BOOTSTRAP. ($GOROOT_BOOTSTRAP_VERSION)"
+if $verbose; then
+ echo cmd/dist
+fi
if [ "$GOROOT_BOOTSTRAP" = "$GOROOT" ]; then
echo "ERROR: \$GOROOT_BOOTSTRAP must not be set to \$GOROOT" >&2
echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4." >&2
From 8098dbb30e3d0d0b4d467f823c4bbdb8dcefc92f Mon Sep 17 00:00:00 2001
From: ipriver
Date: Thu, 10 Sep 2020 10:08:21 +0000
Subject: [PATCH 0029/1082] runtime: update docs for GOMAXPROCS
https://github.com/golang/go/blob/master/doc/effective_go.html#L3211 is used to update the docs comment for `GOMAXPROCS` function.
Fixes #41275
Change-Id: I39f58e93a267c6e9f3ac6638ed51acbe5284ada2
GitHub-Last-Rev: e45c8ac5873979397d747838fd8d41e252aec489
GitHub-Pull-Request: golang/go#41276
Reviewed-on: https://go-review.googlesource.com/c/go/+/253537
Reviewed-by: Keith Randall
---
src/runtime/debug.go | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/runtime/debug.go b/src/runtime/debug.go
index 76eeb2e41af..f411b226766 100644
--- a/src/runtime/debug.go
+++ b/src/runtime/debug.go
@@ -10,9 +10,8 @@ import (
)
// GOMAXPROCS sets the maximum number of CPUs that can be executing
-// simultaneously and returns the previous setting. If n < 1, it does not
-// change the current setting.
-// The number of logical CPUs on the local machine can be queried with NumCPU.
+// simultaneously and returns the previous setting. It defaults to
+// the value of runtime.NumCPU. If n < 1, it does not change the current setting.
// This call will go away when the scheduler improves.
func GOMAXPROCS(n int) int {
if GOARCH == "wasm" && n > 1 {
From 9b2df72b63ff977004756e9b847f926b4fb8d8a8 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Thu, 10 Sep 2020 11:07:48 -0400
Subject: [PATCH 0030/1082] cmd/link: add copyright header
Change-Id: I44f57019bb8e659d4aa3da8b13e8bd9a20b9d2e1
Reviewed-on: https://go-review.googlesource.com/c/go/+/253920
Reviewed-by: Than McIntosh
---
src/cmd/link/link_test.go | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index 72ff01c9320..98798be4653 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -1,3 +1,7 @@
+// 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 main
import (
From d7ab277eed4d2e5ede4f3361adf42d4ad76ced8f Mon Sep 17 00:00:00 2001
From: Junchen Li
Date: Mon, 31 Aug 2020 13:32:33 +0800
Subject: [PATCH 0031/1082] cmd/asm: add more SIMD instructions on arm64
This CL adds USHLL, USHLL2, UZP1, UZP2, and BIF instructions requested
by #40725. And since UXTL* are aliases of USHLL*, this CL also merges
them into one case.
Updates #40725
Change-Id: I404a4fdaf953319f72eea548175bec1097a2a816
Reviewed-on: https://go-review.googlesource.com/c/go/+/253659
Reviewed-by: Cherry Zhang
Run-TryBot: Cherry Zhang
TryBot-Result: Gobot Gobot
---
src/cmd/asm/internal/asm/testdata/arm64.s | 20 ++++
.../asm/internal/asm/testdata/arm64error.s | 8 ++
src/cmd/internal/obj/arm64/a.out.go | 9 +-
src/cmd/internal/obj/arm64/anames.go | 9 +-
src/cmd/internal/obj/arm64/asm7.go | 109 ++++++++++--------
5 files changed, 100 insertions(+), 55 deletions(-)
diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s
index 451ca749ba0..e106ff2ae11 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64.s
@@ -156,6 +156,26 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
VCMTST V2.B8, V29.B8, V2.B8 // a28f220e
VCMTST V2.D2, V23.D2, V3.D2 // e38ee24e
VSUB V2.B8, V30.B8, V30.B8 // de87222e
+ VUZP1 V0.B8, V30.B8, V1.B8 // c11b000e
+ VUZP1 V1.B16, V29.B16, V2.B16 // a21b014e
+ VUZP1 V2.H4, V28.H4, V3.H4 // 831b420e
+ VUZP1 V3.H8, V27.H8, V4.H8 // 641b434e
+ VUZP1 V28.S2, V2.S2, V5.S2 // 45189c0e
+ VUZP1 V29.S4, V1.S4, V6.S4 // 26189d4e
+ VUZP1 V30.D2, V0.D2, V7.D2 // 0718de4e
+ VUZP2 V0.D2, V30.D2, V1.D2 // c15bc04e
+ VUZP2 V30.D2, V0.D2, V29.D2 // 1d58de4e
+ VUSHLL $0, V30.B8, V30.H8 // dea7082f
+ VUSHLL $0, V30.H4, V29.S4 // dda7102f
+ VUSHLL $0, V29.S2, V2.D2 // a2a7202f
+ VUSHLL2 $0, V30.B16, V2.H8 // c2a7086f
+ VUSHLL2 $0, V30.H8, V30.S4 // dea7106f
+ VUSHLL2 $0, V29.S4, V2.D2 // a2a7206f
+ VUSHLL $7, V30.B8, V30.H8 // dea70f2f
+ VUSHLL $15, V30.H4, V29.S4 // dda71f2f
+ VUSHLL2 $31, V30.S4, V2.D2 // c2a73f6f
+ VBIF V0.B8, V30.B8, V1.B8 // c11fe02e
+ VBIF V30.B16, V0.B16, V2.B16 // 021cfe6e
MOVD (R2)(R6.SXTW), R4 // 44c866f8
MOVD (R3)(R6), R5 // MOVD (R3)(R6*1), R5 // 656866f8
MOVD (R2)(R6), R4 // MOVD (R2)(R6*1), R4 // 446866f8
diff --git a/src/cmd/asm/internal/asm/testdata/arm64error.s b/src/cmd/asm/internal/asm/testdata/arm64error.s
index 2a911b4cced..20b1f3e9f06 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64error.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64error.s
@@ -345,4 +345,12 @@ TEXT errors(SB),$0
VUXTL V30.D2, V30.H8 // ERROR "operand mismatch"
VUXTL2 V20.B8, V21.H8 // ERROR "operand mismatch"
VUXTL V3.D2, V4.B8 // ERROR "operand mismatch"
+ VUZP1 V0.B8, V30.B8, V1.B16 // ERROR "operand mismatch"
+ VUZP2 V0.Q1, V30.Q1, V1.Q1 // ERROR "invalid arrangement"
+ VUSHLL $0, V30.D2, V30.H8 // ERROR "operand mismatch"
+ VUSHLL2 $0, V20.B8, V21.H8 // ERROR "operand mismatch"
+ VUSHLL $8, V30.B8, V30.H8 // ERROR "shift amount out of range"
+ VUSHLL2 $32, V30.S4, V2.D2 // ERROR "shift amount out of range"
+ VBIF V0.B8, V1.B8, V2.B16 // ERROR "operand mismatch"
+ VBIF V0.D2, V1.D2, V2.D2 // ERROR "invalid arrangement"
RET
diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go
index ab065e07e5d..2839da1437b 100644
--- a/src/cmd/internal/obj/arm64/a.out.go
+++ b/src/cmd/internal/obj/arm64/a.out.go
@@ -954,6 +954,7 @@ const (
AVADD
AVADDP
AVAND
+ AVBIF
AVCMEQ
AVCNT
AVEOR
@@ -986,6 +987,12 @@ const (
AVEXT
AVRBIT
AVUSHR
+ AVUSHLL
+ AVUSHLL2
+ AVUXTL
+ AVUXTL2
+ AVUZP1
+ AVUZP2
AVSHL
AVSRI
AVBSL
@@ -994,8 +1001,6 @@ const (
AVZIP1
AVZIP2
AVCMTST
- AVUXTL
- AVUXTL2
ALAST
AB = obj.AJMP
ABL = obj.ACALL
diff --git a/src/cmd/internal/obj/arm64/anames.go b/src/cmd/internal/obj/arm64/anames.go
index 8961f04b0cb..48c066abfda 100644
--- a/src/cmd/internal/obj/arm64/anames.go
+++ b/src/cmd/internal/obj/arm64/anames.go
@@ -461,6 +461,7 @@ var Anames = []string{
"VADD",
"VADDP",
"VAND",
+ "VBIF",
"VCMEQ",
"VCNT",
"VEOR",
@@ -493,6 +494,12 @@ var Anames = []string{
"VEXT",
"VRBIT",
"VUSHR",
+ "VUSHLL",
+ "VUSHLL2",
+ "VUXTL",
+ "VUXTL2",
+ "VUZP1",
+ "VUZP2",
"VSHL",
"VSRI",
"VBSL",
@@ -501,7 +508,5 @@ var Anames = []string{
"VZIP1",
"VZIP2",
"VCMTST",
- "VUXTL",
- "VUXTL2",
"LAST",
}
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index 7ce18d0f137..df4bbbbd352 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -480,6 +480,7 @@ var optab = []Optab{
{AVTBL, C_ARNG, C_NONE, C_LIST, C_ARNG, 100, 4, 0, 0, 0},
{AVUSHR, C_VCON, C_ARNG, C_NONE, C_ARNG, 95, 4, 0, 0, 0},
{AVZIP1, C_ARNG, C_ARNG, C_NONE, C_ARNG, 72, 4, 0, 0, 0},
+ {AVUSHLL, C_VCON, C_ARNG, C_NONE, C_ARNG, 102, 4, 0, 0, 0},
{AVUXTL, C_ARNG, C_NONE, C_NONE, C_ARNG, 102, 4, 0, 0, 0},
/* conditional operations */
@@ -2751,6 +2752,9 @@ func buildop(ctxt *obj.Link) {
oprangeset(AVBSL, t)
oprangeset(AVBIT, t)
oprangeset(AVCMTST, t)
+ oprangeset(AVUZP1, t)
+ oprangeset(AVUZP2, t)
+ oprangeset(AVBIF, t)
case AVADD:
oprangeset(AVSUB, t)
@@ -2801,6 +2805,9 @@ func buildop(ctxt *obj.Link) {
case AVUXTL:
oprangeset(AVUXTL2, t)
+ case AVUSHLL:
+ oprangeset(AVUSHLL2, t)
+
case AVLD1R:
oprangeset(AVLD2, t)
oprangeset(AVLD2R, t)
@@ -4177,7 +4184,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
rel.Add = 0
rel.Type = objabi.R_ARM64_GOTPCREL
- case 72: /* vaddp/vand/vcmeq/vorr/vadd/veor/vfmla/vfmls/vbit/vbsl/vcmtst/vsub Vm., Vn., Vd. */
+ case 72: /* vaddp/vand/vcmeq/vorr/vadd/veor/vfmla/vfmls/vbit/vbsl/vcmtst/vsub/vbif/vuzip1/vuzip2 Vm., Vn., Vd. */
af := int((p.From.Reg >> 5) & 15)
af3 := int((p.Reg >> 5) & 15)
at := int((p.To.Reg >> 5) & 15)
@@ -4219,7 +4226,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
}
switch p.As {
- case AVORR, AVAND, AVEOR, AVBIT, AVBSL:
+ case AVORR, AVAND, AVEOR, AVBIT, AVBSL, AVBIF:
if af != ARNG_16B && af != ARNG_8B {
c.ctxt.Diag("invalid arrangement: %v", p)
}
@@ -4233,7 +4240,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
size = 0
case AVBSL:
size = 1
- case AVORR, AVBIT:
+ case AVORR, AVBIT, AVBIF:
size = 2
case AVFMLA, AVFMLS:
if af == ARNG_2D {
@@ -5120,56 +5127,44 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
case 101: // FOMVQ/FMOVD $vcon, Vd -> load from constant pool.
o1 = c.omovlit(p.As, p, &p.From, int(p.To.Reg))
- case 102: // VUXTL{2} Vn., Vd.
- af := int((p.From.Reg >> 5) & 15)
- at := int((p.To.Reg >> 5) & 15)
- var Q, immh uint32
- switch at {
- case ARNG_8H:
- if af == ARNG_8B {
- immh = 1
- Q = 0
- } else if af == ARNG_16B {
- immh = 1
- Q = 1
- } else {
- c.ctxt.Diag("operand mismatch: %v\n", p)
- }
- case ARNG_4S:
- if af == ARNG_4H {
- immh = 2
- Q = 0
- } else if af == ARNG_8H {
- immh = 2
- Q = 1
- } else {
- c.ctxt.Diag("operand mismatch: %v\n", p)
- }
- case ARNG_2D:
- if af == ARNG_2S {
- immh = 4
- Q = 0
- } else if af == ARNG_4S {
- immh = 4
- Q = 1
- } else {
- c.ctxt.Diag("operand mismatch: %v\n", p)
- }
+ case 102: /* vushll, vushll2, vuxtl, vuxtl2 */
+ o1 = c.opirr(p, p.As)
+ rf := p.Reg
+ af := uint8((p.Reg >> 5) & 15)
+ at := uint8((p.To.Reg >> 5) & 15)
+ shift := int(p.From.Offset)
+ if p.As == AVUXTL || p.As == AVUXTL2 {
+ rf = p.From.Reg
+ af = uint8((p.From.Reg >> 5) & 15)
+ shift = 0
+ }
+
+ pack := func(q, x, y uint8) uint32 {
+ return uint32(q)<<16 | uint32(x)<<8 | uint32(y)
+ }
+
+ var Q uint8 = uint8(o1>>30) & 1
+ var immh, width uint8
+ switch pack(Q, af, at) {
+ case pack(0, ARNG_8B, ARNG_8H):
+ immh, width = 1, 8
+ case pack(1, ARNG_16B, ARNG_8H):
+ immh, width = 1, 8
+ case pack(0, ARNG_4H, ARNG_4S):
+ immh, width = 2, 16
+ case pack(1, ARNG_8H, ARNG_4S):
+ immh, width = 2, 16
+ case pack(0, ARNG_2S, ARNG_2D):
+ immh, width = 4, 32
+ case pack(1, ARNG_4S, ARNG_2D):
+ immh, width = 4, 32
default:
c.ctxt.Diag("operand mismatch: %v\n", p)
}
-
- if p.As == AVUXTL && Q == 1 {
- c.ctxt.Diag("operand mismatch: %v\n", p)
+ if !(0 <= shift && shift <= int(width-1)) {
+ c.ctxt.Diag("shift amount out of range: %v\n", p)
}
- if p.As == AVUXTL2 && Q == 0 {
- c.ctxt.Diag("operand mismatch: %v\n", p)
- }
-
- o1 = c.oprrr(p, p.As)
- rf := int((p.From.Reg) & 31)
- rt := int((p.To.Reg) & 31)
- o1 |= Q<<30 | immh<<19 | uint32((rf&31)<<5) | uint32(rt&31)
+ o1 |= uint32(immh)<<19 | uint32(shift)<<16 | uint32(rf&31)<<5 | uint32(p.To.Reg&31)
}
out[0] = o1
out[1] = o2
@@ -5802,6 +5797,9 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 {
case AVLD2R, AVLD4R:
return 0xD<<24 | 3<<21
+ case AVBIF:
+ return 1<<29 | 7<<25 | 7<<21 | 7<<10
+
case AVBIT:
return 1<<29 | 0x75<<21 | 7<<10
@@ -5811,8 +5809,11 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 {
case AVCMTST:
return 0xE<<24 | 1<<21 | 0x23<<10
- case AVUXTL, AVUXTL2:
- return 0x5e<<23 | 0x29<<10
+ case AVUZP1:
+ return 7<<25 | 3<<11
+
+ case AVUZP2:
+ return 7<<25 | 1<<14 | 3<<11
}
c.ctxt.Diag("%v: bad rrr %d %v", p, a, a)
@@ -6011,6 +6012,12 @@ func (c *ctxt7) opirr(p *obj.Prog, a obj.As) uint32 {
case AVSRI:
return 0x5E<<23 | 17<<10
+
+ case AVUSHLL, AVUXTL:
+ return 1<<29 | 15<<24 | 0x29<<10
+
+ case AVUSHLL2, AVUXTL2:
+ return 3<<29 | 15<<24 | 0x29<<10
}
c.ctxt.Diag("%v: bad irr %v", p, a)
From a1762c2cc67822d86cb37747a56f0d4a07d24ced Mon Sep 17 00:00:00 2001
From: eric fang
Date: Wed, 13 May 2020 06:38:39 +0000
Subject: [PATCH 0032/1082] unicode/utf8: refactor benchmarks for FullRune
function
BenchmarkFullASCIIRune tests the performance of function utf8.FullRune,
which will be inlined in BenchmarkFullASCIIRune. Since the return value
of FullRune is not referenced, it will be removed as dead code.
This CL makes the FullRune functions return value referenced by a global
variable to avoid this point. In addition, this CL adds one more benchmark
to cover more code paths, and puts them together as sub benchmarks of
BenchmarkFullRune.
Change-Id: I6e79f4c087adf70e351498a4b58d7482dcd1ec4a
Reviewed-on: https://go-review.googlesource.com/c/go/+/233979
Run-TryBot: eric fang
TryBot-Result: Gobot Gobot
Reviewed-by: Ian Lance Taylor
---
src/unicode/utf8/utf8_test.go | 28 ++++++++++++++++++----------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/src/unicode/utf8/utf8_test.go b/src/unicode/utf8/utf8_test.go
index 359461bd057..eaf1b5ffee3 100644
--- a/src/unicode/utf8/utf8_test.go
+++ b/src/unicode/utf8/utf8_test.go
@@ -597,16 +597,24 @@ func BenchmarkDecodeJapaneseRune(b *testing.B) {
}
}
-func BenchmarkFullASCIIRune(b *testing.B) {
- a := []byte{'a'}
- for i := 0; i < b.N; i++ {
- FullRune(a)
- }
-}
+// boolSink is used to reference the return value of benchmarked
+// functions to avoid dead code elimination.
+var boolSink bool
-func BenchmarkFullJapaneseRune(b *testing.B) {
- nihon := []byte("本")
- for i := 0; i < b.N; i++ {
- FullRune(nihon)
+func BenchmarkFullRune(b *testing.B) {
+ benchmarks := []struct {
+ name string
+ data []byte
+ }{
+ {"ASCII", []byte("a")},
+ {"Incomplete", []byte("\xf0\x90\x80")},
+ {"Japanese", []byte("本")},
+ }
+ for _, bm := range benchmarks {
+ b.Run(bm.name, func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ boolSink = FullRune(bm.data)
+ }
+ })
}
}
From d277a361231485999cc2b7433e3244e559c7d7da Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Thu, 10 Sep 2020 17:18:57 -0400
Subject: [PATCH 0033/1082] runtime: remove darwin/arm specifc code
That port is gone.
Change-Id: I212d435e290d1890d6cd5531be98bb692650595e
Reviewed-on: https://go-review.googlesource.com/c/go/+/254077
Run-TryBot: Cherry Zhang
TryBot-Result: Gobot Gobot
Reviewed-by: Ian Lance Taylor
---
src/runtime/stack.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index 403b3c313ef..821c2e84360 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -66,7 +66,7 @@ const (
// to each stack below the usual guard area for OS-specific
// purposes like signal handling. Used on Windows, Plan 9,
// and iOS because they do not use a separate stack.
- _StackSystem = sys.GoosWindows*512*sys.PtrSize + sys.GoosPlan9*512 + sys.GoosDarwin*sys.GoarchArm*1024 + sys.GoosDarwin*sys.GoarchArm64*1024
+ _StackSystem = sys.GoosWindows*512*sys.PtrSize + sys.GoosPlan9*512 + sys.GoosDarwin*sys.GoarchArm64*1024
// The minimum size of stack used by Go code
_StackMin = 2048
From 03a686069191e3515c7f27f6d90b66d272e0e3a2 Mon Sep 17 00:00:00 2001
From: Jay Conrod
Date: Wed, 9 Sep 2020 15:58:05 -0400
Subject: [PATCH 0034/1082] cmd/go: update tests to work with -mod=readonly on
by default
For #40728
Change-Id: Ic2b025ff75c6e73c0cb58c1737e44e2a41c71571
Reviewed-on: https://go-review.googlesource.com/c/go/+/253837
Run-TryBot: Jay Conrod
TryBot-Result: Gobot Gobot
Reviewed-by: Michael Matloob
Reviewed-by: Bryan C. Mills
---
.../example.com_retract_missingmod_v1.0.0.txt | 2 ++
src/cmd/go/testdata/script/mod_auth.txt | 2 +-
src/cmd/go/testdata/script/mod_case.txt | 7 ++++++-
src/cmd/go/testdata/script/mod_concurrent.txt | 1 +
src/cmd/go/testdata/script/mod_doc.txt | 2 ++
.../go/testdata/script/mod_domain_root.txt | 2 +-
src/cmd/go/testdata/script/mod_download.txt | 2 +-
.../testdata/script/mod_download_partial.txt | 9 ++++++--
.../testdata/script/mod_get_incompatible.txt | 2 +-
.../go/testdata/script/mod_get_indirect.txt | 2 +-
.../testdata/script/mod_get_latest_pseudo.txt | 2 +-
.../script/mod_get_trailing_slash.txt | 3 +++
src/cmd/go/testdata/script/mod_import.txt | 2 +-
.../testdata/script/mod_in_testdata_dir.txt | 6 +++---
src/cmd/go/testdata/script/mod_init_dep.txt | 21 ++++---------------
.../testdata/script/mod_install_versioned.txt | 2 ++
src/cmd/go/testdata/script/mod_internal.txt | 14 +++++++++----
.../testdata/script/mod_invalid_version.txt | 1 +
src/cmd/go/testdata/script/mod_list.txt | 8 +++----
src/cmd/go/testdata/script/mod_list_dir.txt | 5 +++++
.../go/testdata/script/mod_list_direct.txt | 2 +-
.../testdata/script/mod_list_replace_dir.txt | 12 +++++++++--
.../go/testdata/script/mod_list_upgrade.txt | 4 ++++
.../go/testdata/script/mod_load_badchain.txt | 4 ++--
.../go/testdata/script/mod_load_badmod.txt | 7 +++----
.../go/testdata/script/mod_load_badzip.txt | 4 +---
.../script/mod_missingpkg_prerelease.txt | 2 +-
src/cmd/go/testdata/script/mod_modinfo.txt | 1 +
src/cmd/go/testdata/script/mod_multirepo.txt | 1 +
src/cmd/go/testdata/script/mod_notall.txt | 1 +
.../go/testdata/script/mod_permissions.txt | 2 +-
src/cmd/go/testdata/script/mod_query.txt | 10 +++++++++
src/cmd/go/testdata/script/mod_replace.txt | 4 ++--
.../testdata/script/mod_replace_gopkgin.txt | 1 +
.../go/testdata/script/mod_replace_import.txt | 1 +
.../testdata/script/mod_require_exclude.txt | 8 +++----
src/cmd/go/testdata/script/mod_retention.txt | 6 +++---
src/cmd/go/testdata/script/mod_retract.txt | 5 ++++-
.../testdata/script/mod_retract_replace.txt | 14 +++++++++++--
src/cmd/go/testdata/script/mod_sum_lookup.txt | 5 +++--
.../go/testdata/script/mod_sumdb_golang.txt | 4 ++--
src/cmd/go/testdata/script/mod_symlink.txt | 5 ++++-
src/cmd/go/testdata/script/mod_test.txt | 1 +
.../go/testdata/script/mod_tidy_replace.txt | 1 +
.../go/testdata/script/mod_upgrade_patch.txt | 1 +
.../go/testdata/script/mod_vcs_missing.txt | 4 ++--
.../go/testdata/script/mod_vendor_build.txt | 3 +++
src/cmd/go/testdata/script/mod_verify.txt | 2 +-
src/cmd/go/testdata/script/mod_why.txt | 3 +++
src/cmd/go/testdata/script/modfile_flag.txt | 6 +++---
src/cmd/go/testdata/script/version.txt | 1 +
.../go/testdata/script/version_replace.txt | 2 +-
52 files changed, 146 insertions(+), 76 deletions(-)
diff --git a/src/cmd/go/testdata/mod/example.com_retract_missingmod_v1.0.0.txt b/src/cmd/go/testdata/mod/example.com_retract_missingmod_v1.0.0.txt
index 2023c7b0969..1d8d81071ee 100644
--- a/src/cmd/go/testdata/mod/example.com_retract_missingmod_v1.0.0.txt
+++ b/src/cmd/go/testdata/mod/example.com_retract_missingmod_v1.0.0.txt
@@ -6,3 +6,5 @@ module example.com/retract/missingmod
go 1.14
-- .info --
{"Version":"v1.0.0"}
+-- missingmod.go --
+package missingmod
diff --git a/src/cmd/go/testdata/script/mod_auth.txt b/src/cmd/go/testdata/script/mod_auth.txt
index 5bcbcd1a188..544acbc1f86 100644
--- a/src/cmd/go/testdata/script/mod_auth.txt
+++ b/src/cmd/go/testdata/script/mod_auth.txt
@@ -7,7 +7,7 @@ env GOSUMDB=off
# Without credentials, downloading a module from a path that requires HTTPS
# basic auth should fail.
env NETRC=$WORK/empty
-! go list all
+! go mod tidy
stderr '^\tserver response: ACCESS DENIED, buddy$'
stderr '^\tserver response: File\? What file\?$'
diff --git a/src/cmd/go/testdata/script/mod_case.txt b/src/cmd/go/testdata/script/mod_case.txt
index ee818c2c07b..6f8d869c447 100644
--- a/src/cmd/go/testdata/script/mod_case.txt
+++ b/src/cmd/go/testdata/script/mod_case.txt
@@ -1,6 +1,6 @@
env GO111MODULE=on
-go get rsc.io/QUOTE
+go get -d
go list -m all
stdout '^rsc.io/quote v1.5.2'
stdout '^rsc.io/QUOTE v1.5.2'
@@ -18,3 +18,8 @@ stdout '!q!u!o!t!e@v1.5.3-!p!r!e'
-- go.mod --
module x
+
+-- use.go --
+package use
+
+import _ "rsc.io/QUOTE/QUOTE"
diff --git a/src/cmd/go/testdata/script/mod_concurrent.txt b/src/cmd/go/testdata/script/mod_concurrent.txt
index e03e5e5edbe..8c215251587 100644
--- a/src/cmd/go/testdata/script/mod_concurrent.txt
+++ b/src/cmd/go/testdata/script/mod_concurrent.txt
@@ -1,6 +1,7 @@
env GO111MODULE=on
# Concurrent builds should succeed, even if they need to download modules.
+go get -d ./x ./y
go build ./x &
go build ./y
wait
diff --git a/src/cmd/go/testdata/script/mod_doc.txt b/src/cmd/go/testdata/script/mod_doc.txt
index aac3db00be1..595ad679fc0 100644
--- a/src/cmd/go/testdata/script/mod_doc.txt
+++ b/src/cmd/go/testdata/script/mod_doc.txt
@@ -1,6 +1,7 @@
# go doc should find module documentation
env GO111MODULE=on
+env GOFLAGS=-mod=mod
[short] skip
# Check when module x is inside GOPATH/src.
@@ -48,6 +49,7 @@ stderr '^doc: cannot find module providing package example.com/hello: module loo
# path used in source code, not to the absolute path relative to GOROOT.
cd $GOROOT/src
+env GOFLAGS=
go doc cryptobyte
stdout '// import "golang.org/x/crypto/cryptobyte"'
diff --git a/src/cmd/go/testdata/script/mod_domain_root.txt b/src/cmd/go/testdata/script/mod_domain_root.txt
index e34cc29fa64..14745b5812c 100644
--- a/src/cmd/go/testdata/script/mod_domain_root.txt
+++ b/src/cmd/go/testdata/script/mod_domain_root.txt
@@ -2,7 +2,7 @@
# (example.com not example.com/something)
env GO111MODULE=on
-go build
+go get -d
-- go.mod --
module x
diff --git a/src/cmd/go/testdata/script/mod_download.txt b/src/cmd/go/testdata/script/mod_download.txt
index 5acb83266bb..b9bf67cad59 100644
--- a/src/cmd/go/testdata/script/mod_download.txt
+++ b/src/cmd/go/testdata/script/mod_download.txt
@@ -46,7 +46,7 @@ go mod edit -require rsc.io/quote@v1.5.3-pre1
! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.3-pre1.zip
# module loading will page in the info and mod files
-go list -m all
+go list -m -mod=mod all
exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.3-pre1.info
exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.3-pre1.mod
! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.3-pre1.zip
diff --git a/src/cmd/go/testdata/script/mod_download_partial.txt b/src/cmd/go/testdata/script/mod_download_partial.txt
index 4978982dab2..8d319701608 100644
--- a/src/cmd/go/testdata/script/mod_download_partial.txt
+++ b/src/cmd/go/testdata/script/mod_download_partial.txt
@@ -1,5 +1,5 @@
-# Download a module
-go mod download -modcacherw rsc.io/quote
+# Download modules and populate go.sum.
+go get -d -modcacherw
exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
# 'go mod verify' should fail if we delete a file.
@@ -61,4 +61,9 @@ go 1.14
require rsc.io/quote v1.5.2
+-- use.go --
+package use
+
+import _ "rsc.io/quote"
+
-- empty --
diff --git a/src/cmd/go/testdata/script/mod_get_incompatible.txt b/src/cmd/go/testdata/script/mod_get_incompatible.txt
index b210715a5de..b28718a6946 100644
--- a/src/cmd/go/testdata/script/mod_get_incompatible.txt
+++ b/src/cmd/go/testdata/script/mod_get_incompatible.txt
@@ -1,6 +1,6 @@
env GO111MODULE=on
-go list x
+go get -d x
go list -m all
stdout 'rsc.io/breaker v2.0.0\+incompatible'
diff --git a/src/cmd/go/testdata/script/mod_get_indirect.txt b/src/cmd/go/testdata/script/mod_get_indirect.txt
index f25e170a499..e1cc1ab4115 100644
--- a/src/cmd/go/testdata/script/mod_get_indirect.txt
+++ b/src/cmd/go/testdata/script/mod_get_indirect.txt
@@ -27,7 +27,7 @@ grep 'golang.org/x/text v0.3.0 // indirect$' go.mod
# indirect tag should be removed upon seeing direct import.
cp $WORK/tmp/uselang.go x.go
-go list
+go get -d
grep 'rsc.io/quote v1.5.2$' go.mod
grep 'golang.org/x/text [v0-9a-f\.-]+$' go.mod
diff --git a/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt b/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt
index 825ee8cf896..241a0c2f0df 100644
--- a/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt
+++ b/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt
@@ -5,6 +5,6 @@
env GO111MODULE=on
go mod init m
-go list example.com/notags
+go get -d example.com/notags
go list -m all
stdout '^example.com/notags v0.0.0-20190507143103-cc8cbe209b64$'
diff --git a/src/cmd/go/testdata/script/mod_get_trailing_slash.txt b/src/cmd/go/testdata/script/mod_get_trailing_slash.txt
index 7b5d90c50b5..3b38d8ba7d3 100644
--- a/src/cmd/go/testdata/script/mod_get_trailing_slash.txt
+++ b/src/cmd/go/testdata/script/mod_get_trailing_slash.txt
@@ -1,3 +1,6 @@
+# Populate go.sum
+go mod download
+
# go list should succeed to load a package ending with ".go" if the path does
# not correspond to an existing local file. Listing a pattern ending with
# ".go/" should try to list a package regardless of whether a file exists at the
diff --git a/src/cmd/go/testdata/script/mod_import.txt b/src/cmd/go/testdata/script/mod_import.txt
index 3985b43144c..28358b5b0c5 100644
--- a/src/cmd/go/testdata/script/mod_import.txt
+++ b/src/cmd/go/testdata/script/mod_import.txt
@@ -1,7 +1,7 @@
env GO111MODULE=on
# latest rsc.io/quote should be v1.5.2 not v1.5.3-pre1
-go list
+go get -d
go list -m all
stdout 'rsc.io/quote v1.5.2'
diff --git a/src/cmd/go/testdata/script/mod_in_testdata_dir.txt b/src/cmd/go/testdata/script/mod_in_testdata_dir.txt
index f582569798b..66f79faa6d9 100644
--- a/src/cmd/go/testdata/script/mod_in_testdata_dir.txt
+++ b/src/cmd/go/testdata/script/mod_in_testdata_dir.txt
@@ -8,8 +8,8 @@ env GO111MODULE=on
cd $WORK/testdata
go mod init testdata.tld/foo
-# Building a package within that module should resolve its dependencies.
-go build
+# Getting a package within that module should resolve its dependencies.
+go get -d
grep 'rsc.io/quote' go.mod
# Tidying the module should preserve those dependencies.
@@ -26,7 +26,7 @@ exists vendor/rsc.io/quote
cd $WORK/_ignored
go mod init testdata.tld/foo
-go build
+go get
grep 'rsc.io/quote' go.mod
go mod tidy
diff --git a/src/cmd/go/testdata/script/mod_init_dep.txt b/src/cmd/go/testdata/script/mod_init_dep.txt
index 755076eae80..f8cf1d563ab 100644
--- a/src/cmd/go/testdata/script/mod_init_dep.txt
+++ b/src/cmd/go/testdata/script/mod_init_dep.txt
@@ -1,24 +1,14 @@
env GO111MODULE=on
+env GOFLAGS=-mod=mod
# modconv uses git directly to examine what old 'go get' would
[!net] skip
[!exec:git] skip
-# go build should populate go.mod from Gopkg.lock
-cp go.mod1 go.mod
-go build
+# go mod init should populate go.mod from Gopkg.lock
+go mod init x
stderr 'copying requirements from Gopkg.lock'
go list -m all
-! stderr 'copying requirements from Gopkg.lock'
-stdout 'rsc.io/sampler v1.0.0'
-
-# go list should populate go.mod from Gopkg.lock
-cp go.mod1 go.mod
-go list
-stderr 'copying requirements from Gopkg.lock'
-go list
-! stderr 'copying requirements from Gopkg.lock'
-go list -m all
stdout 'rsc.io/sampler v1.0.0'
# test dep replacement
@@ -26,9 +16,6 @@ cd y
go mod init
cmpenv go.mod go.mod.replace
--- go.mod1 --
-module x
-
-- x.go --
package x
@@ -54,4 +41,4 @@ go $goversion
replace z v1.0.0 => rsc.io/quote v1.0.0
-require rsc.io/quote v1.0.0
\ No newline at end of file
+require rsc.io/quote v1.0.0
diff --git a/src/cmd/go/testdata/script/mod_install_versioned.txt b/src/cmd/go/testdata/script/mod_install_versioned.txt
index 03986d06a0a..c6bce418b4f 100644
--- a/src/cmd/go/testdata/script/mod_install_versioned.txt
+++ b/src/cmd/go/testdata/script/mod_install_versioned.txt
@@ -1,9 +1,11 @@
env GO111MODULE=on
+go get -d rsc.io/fortune
go list -f '{{.Target}}' rsc.io/fortune
! stdout fortune@v1
stdout 'fortune(\.exe)?$'
+go get -d rsc.io/fortune/v2
go list -f '{{.Target}}' rsc.io/fortune/v2
! stdout v2
stdout 'fortune(\.exe)?$'
diff --git a/src/cmd/go/testdata/script/mod_internal.txt b/src/cmd/go/testdata/script/mod_internal.txt
index 1193d528ece..687269d18f6 100644
--- a/src/cmd/go/testdata/script/mod_internal.txt
+++ b/src/cmd/go/testdata/script/mod_internal.txt
@@ -3,30 +3,34 @@ env GO111MODULE=on
# golang.org/x/internal should be importable from other golang.org/x modules.
go mod edit -module=golang.org/x/anything
-go build .
+go get -d .
# ...and their tests...
go test
stdout PASS
# ...but that should not leak into other modules.
+go get -d ./baddep
! go build ./baddep
stderr golang.org[/\\]notx[/\\]useinternal
stderr 'use of internal package golang.org/x/.* not allowed'
# Internal packages in the standard library should not leak into modules.
+go get -d ./fromstd
! go build ./fromstd
stderr 'use of internal package internal/testenv not allowed'
# Dependencies should be able to use their own internal modules...
go mod edit -module=golang.org/notx
-go build ./throughdep
+go get -d ./throughdep
# ... but other modules should not, even if they have transitive dependencies.
+go get -d .
! go build .
stderr 'use of internal package golang.org/x/.* not allowed'
# And transitive dependencies still should not leak.
+go get -d ./baddep
! go build ./baddep
stderr golang.org[/\\]notx[/\\]useinternal
stderr 'use of internal package golang.org/x/.* not allowed'
@@ -34,15 +38,17 @@ stderr 'use of internal package golang.org/x/.* not allowed'
# Replacing an internal module should keep it internal to the same paths.
go mod edit -module=golang.org/notx
go mod edit -replace golang.org/x/internal=./replace/golang.org/notx/internal
-go build ./throughdep
+go get -d ./throughdep
+go get -d ./baddep
! go build ./baddep
stderr golang.org[/\\]notx[/\\]useinternal
stderr 'use of internal package golang.org/x/.* not allowed'
go mod edit -replace golang.org/x/internal=./vendor/golang.org/x/internal
-go build ./throughdep
+go get -d ./throughdep
+go get -d ./baddep
! go build ./baddep
stderr golang.org[/\\]notx[/\\]useinternal
stderr 'use of internal package golang.org/x/.* not allowed'
diff --git a/src/cmd/go/testdata/script/mod_invalid_version.txt b/src/cmd/go/testdata/script/mod_invalid_version.txt
index 6dddd4b0365..f9dfdd63462 100644
--- a/src/cmd/go/testdata/script/mod_invalid_version.txt
+++ b/src/cmd/go/testdata/script/mod_invalid_version.txt
@@ -4,6 +4,7 @@
env GO111MODULE=on
env GOPROXY=direct
env GOSUMDB=off
+env GOFLAGS=-mod=mod
# Regression test for golang.org/issue/27173: if the user (or go.mod file)
# requests a pseudo-version that does not match both the module path and commit
diff --git a/src/cmd/go/testdata/script/mod_list.txt b/src/cmd/go/testdata/script/mod_list.txt
index 17b33fcc7bf..1ba6d7c910e 100644
--- a/src/cmd/go/testdata/script/mod_list.txt
+++ b/src/cmd/go/testdata/script/mod_list.txt
@@ -2,12 +2,12 @@ env GO111MODULE=on
[short] skip
# list {{.Dir}} shows main module and go.mod but not not-yet-downloaded dependency dir.
-go list -m -f '{{.Path}} {{.Main}} {{.GoMod}} {{.Dir}}' all
+go list -mod=mod -m -f '{{.Path}} {{.Main}} {{.GoMod}} {{.Dir}}' all
stdout '^x true .*[\\/]src[\\/]go.mod .*[\\/]src$'
stdout '^rsc.io/quote false .*[\\/]v1.5.2.mod $'
# list {{.Dir}} shows dependency after download (and go list without -m downloads it)
-go list -f '{{.Dir}}' rsc.io/quote
+go list -mod=mod -f '{{.Dir}}' rsc.io/quote
stdout '.*mod[\\/]rsc.io[\\/]quote@v1.5.2$'
# downloaded dependencies are read-only
@@ -20,7 +20,7 @@ go clean -modcache
# list {{.Dir}} shows replaced directories
cp go.mod2 go.mod
-go list -f {{.Dir}} rsc.io/quote
+go list -mod=mod -f {{.Dir}} rsc.io/quote
go list -m -f '{{.Path}} {{.Version}} {{.Dir}}{{with .Replace}} {{.GoMod}} => {{.Version}} {{.Dir}} {{.GoMod}}{{end}}' all
stdout 'mod[\\/]rsc.io[\\/]quote@v1.5.1'
stdout 'v1.3.0.*mod[\\/]rsc.io[\\/]sampler@v1.3.1 .*[\\/]v1.3.1.mod => v1.3.1.*sampler@v1.3.1 .*[\\/]v1.3.1.mod'
@@ -30,7 +30,7 @@ go list std
stdout ^math/big
# rsc.io/quote/buggy should be listable as a package
-go list rsc.io/quote/buggy
+go list -mod=mod rsc.io/quote/buggy
# rsc.io/quote/buggy should not be listable as a module
go list -m -e -f '{{.Error.Err}}' nonexist rsc.io/quote/buggy
diff --git a/src/cmd/go/testdata/script/mod_list_dir.txt b/src/cmd/go/testdata/script/mod_list_dir.txt
index 6653435a06e..1adab8f027d 100644
--- a/src/cmd/go/testdata/script/mod_list_dir.txt
+++ b/src/cmd/go/testdata/script/mod_list_dir.txt
@@ -2,6 +2,9 @@
# go list with path to directory should work
+# populate go.sum
+go get -d
+
env GO111MODULE=off
go list -f '{{.ImportPath}}' $GOROOT/src/math
stdout ^math$
@@ -29,3 +32,5 @@ require rsc.io/quote v1.5.2
-- x.go --
package x
+
+import _ "rsc.io/quote"
diff --git a/src/cmd/go/testdata/script/mod_list_direct.txt b/src/cmd/go/testdata/script/mod_list_direct.txt
index 8f858711897..62a472f475e 100644
--- a/src/cmd/go/testdata/script/mod_list_direct.txt
+++ b/src/cmd/go/testdata/script/mod_list_direct.txt
@@ -10,7 +10,7 @@ env GOSUMDB=off
# For a while, (*modfetch.codeRepo).Stat was not checking for a go.mod file,
# which would produce a hard error at the subsequent call to GoMod.
-go list all
+go get -d
-- go.mod --
module example.com
diff --git a/src/cmd/go/testdata/script/mod_list_replace_dir.txt b/src/cmd/go/testdata/script/mod_list_replace_dir.txt
index cad7fe25280..f2f2d2b2bb2 100644
--- a/src/cmd/go/testdata/script/mod_list_replace_dir.txt
+++ b/src/cmd/go/testdata/script/mod_list_replace_dir.txt
@@ -2,8 +2,11 @@
# module within the module cache.
# Verifies golang.org/issue/29548
-env GO111MODULE=on
-go mod download rsc.io/quote@v1.5.1 rsc.io/quote@v1.5.2
+# Populate go.sum and download dependencies.
+go get -d
+
+# Ensure v1.5.2 is also in the cache so we can list it.
+go mod download rsc.io/quote@v1.5.2
! go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
stderr '^directory ..[/\\]pkg[/\\]mod[/\\]rsc.io[/\\]quote@v1.5.2 outside available modules$'
@@ -17,3 +20,8 @@ module example.com/quoter
require rsc.io/quote v1.5.2
replace rsc.io/quote => rsc.io/quote v1.5.1
+
+-- use.go --
+package use
+
+import _ "rsc.io/quote"
diff --git a/src/cmd/go/testdata/script/mod_list_upgrade.txt b/src/cmd/go/testdata/script/mod_list_upgrade.txt
index 474df0dc269..0cef04b89ad 100644
--- a/src/cmd/go/testdata/script/mod_list_upgrade.txt
+++ b/src/cmd/go/testdata/script/mod_list_upgrade.txt
@@ -1,5 +1,9 @@
env GO111MODULE=on
+# Populate go.sum
+go list -m -mod=mod all
+
+# Check for upgrades.
go list -m -u all
stdout 'rsc.io/quote v1.2.0 \[v1\.5\.2\]'
diff --git a/src/cmd/go/testdata/script/mod_load_badchain.txt b/src/cmd/go/testdata/script/mod_load_badchain.txt
index 67d9a1584f5..e943179c541 100644
--- a/src/cmd/go/testdata/script/mod_load_badchain.txt
+++ b/src/cmd/go/testdata/script/mod_load_badchain.txt
@@ -28,10 +28,10 @@ cmp stderr list-expected
# Try listing a package that imports a package
# in a module without a requirement.
go mod edit -droprequire example.com/badchain/a
-! go list m/use
+! go list -mod=mod m/use
cmp stderr list-missing-expected
-! go list -test m/testuse
+! go list -mod=mod -test m/testuse
cmp stderr list-missing-test-expected
-- go.mod.orig --
diff --git a/src/cmd/go/testdata/script/mod_load_badmod.txt b/src/cmd/go/testdata/script/mod_load_badmod.txt
index 68c8b3792bd..fa22e1808b7 100644
--- a/src/cmd/go/testdata/script/mod_load_badmod.txt
+++ b/src/cmd/go/testdata/script/mod_load_badmod.txt
@@ -1,14 +1,13 @@
# Unknown lines should be ignored in dependency go.mod files.
-env GO111MODULE=on
-go list -m all
+go list -m -mod=mod all
# ... and in replaced dependency go.mod files.
cp go.mod go.mod.usesub
-go list -m all
+go list -m -mod=mod all
# ... but not in the main module.
cp go.mod.bad go.mod
-! go list -m all
+! go list -m -mod=mod all
stderr 'unknown directive: hello'
-- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_load_badzip.txt b/src/cmd/go/testdata/script/mod_load_badzip.txt
index c5ba18e9f06..65374d2a6d1 100644
--- a/src/cmd/go/testdata/script/mod_load_badzip.txt
+++ b/src/cmd/go/testdata/script/mod_load_badzip.txt
@@ -5,10 +5,8 @@ env GO111MODULE=on
stderr 'zip for rsc.io/badzip@v1.0.0 has unexpected file rsc.io/badzip@v1.0.0.txt'
! grep rsc.io/badzip go.mod
-# TODO(golang.org/issue/31730): 'go build' should print the error below if the
-# requirement is not present.
go mod edit -require rsc.io/badzip@v1.0.0
-! go build rsc.io/badzip
+! go build -mod=mod rsc.io/badzip
stderr 'zip for rsc.io/badzip@v1.0.0 has unexpected file rsc.io/badzip@v1.0.0.txt'
-- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_missingpkg_prerelease.txt b/src/cmd/go/testdata/script/mod_missingpkg_prerelease.txt
index 1ba8d3d22ab..9c250e7d1c4 100644
--- a/src/cmd/go/testdata/script/mod_missingpkg_prerelease.txt
+++ b/src/cmd/go/testdata/script/mod_missingpkg_prerelease.txt
@@ -1,6 +1,6 @@
env GO111MODULE=on
-! go list -deps use.go
+! go list -mod=mod -deps use.go
stderr '^use.go:4:2: package example.com/missingpkg/deprecated provided by example.com/missingpkg at latest version v1.0.0 but not at required version v1.0.1-beta$'
-- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_modinfo.txt b/src/cmd/go/testdata/script/mod_modinfo.txt
index fb31f9e43b2..d9e9fdec215 100644
--- a/src/cmd/go/testdata/script/mod_modinfo.txt
+++ b/src/cmd/go/testdata/script/mod_modinfo.txt
@@ -6,6 +6,7 @@ env GO111MODULE=on
cd x
go mod edit -require=rsc.io/quote@v1.5.2
go mod edit -replace=rsc.io/quote@v1.5.2=rsc.io/quote@v1.0.0
+go mod tidy # populate go.sum
# Build a binary and ensure that it can output its own debug info.
# The debug info should be accessible before main starts (golang.org/issue/29628).
diff --git a/src/cmd/go/testdata/script/mod_multirepo.txt b/src/cmd/go/testdata/script/mod_multirepo.txt
index 7f977e80f6e..0f335a11f0f 100644
--- a/src/cmd/go/testdata/script/mod_multirepo.txt
+++ b/src/cmd/go/testdata/script/mod_multirepo.txt
@@ -7,6 +7,7 @@ go list -deps -f {{.Dir}}
# v2 import should use a downloaded module
# both without an explicit go.mod entry ...
cp tmp/use_v2.go x.go
+go get -d .
go list -deps -f {{.Dir}}
stdout 'pkg[\\/]mod[\\/]rsc.io[\\/]quote[\\/]v2@v2.0.1$'
diff --git a/src/cmd/go/testdata/script/mod_notall.txt b/src/cmd/go/testdata/script/mod_notall.txt
index 29ca6066fa4..1657c8d2d00 100644
--- a/src/cmd/go/testdata/script/mod_notall.txt
+++ b/src/cmd/go/testdata/script/mod_notall.txt
@@ -5,6 +5,7 @@
# module, but not should not include test dependencies of packages imported only
# by other root patterns.
+env GOFLAGS=-mod=mod
cp go.mod go.mod.orig
go list -deps all x/otherroot
diff --git a/src/cmd/go/testdata/script/mod_permissions.txt b/src/cmd/go/testdata/script/mod_permissions.txt
index 11fb4754f83..2d32dcd10fd 100644
--- a/src/cmd/go/testdata/script/mod_permissions.txt
+++ b/src/cmd/go/testdata/script/mod_permissions.txt
@@ -12,7 +12,7 @@ chmod 0640 go.mod
chmod 0604 go.sum
go mod edit -module=golang.org/issue/34634
-go build .
+go get -d
cmp go.mod go.mod.want
cmp go.sum go.sum.want
diff --git a/src/cmd/go/testdata/script/mod_query.txt b/src/cmd/go/testdata/script/mod_query.txt
index e87ca302f0c..e10185709d9 100644
--- a/src/cmd/go/testdata/script/mod_query.txt
+++ b/src/cmd/go/testdata/script/mod_query.txt
@@ -1,5 +1,10 @@
env GO111MODULE=on
+# Populate go.sum.
+# TODO(golang.org/issue/41297): we shouldn't need go.sum. None of the commands
+# below depend on the build list.
+go mod download
+
go list -m -versions rsc.io/quote
stdout '^rsc.io/quote v1.0.0 v1.1.0 v1.2.0 v1.2.1 v1.3.0 v1.4.0 v1.5.0 v1.5.1 v1.5.2 v1.5.3-pre1$'
@@ -30,3 +35,8 @@ stdout 'no matching versions for query ">v1.5.3"'
-- go.mod --
module x
require rsc.io/quote v1.0.0
+
+-- use.go --
+package use
+
+import _ "rsc.io/quote"
diff --git a/src/cmd/go/testdata/script/mod_replace.txt b/src/cmd/go/testdata/script/mod_replace.txt
index c21f1720021..dc9667f1d0d 100644
--- a/src/cmd/go/testdata/script/mod_replace.txt
+++ b/src/cmd/go/testdata/script/mod_replace.txt
@@ -4,7 +4,7 @@ env GO111MODULE=on
cp go.mod go.mod.orig
# Make sure the test builds without replacement.
-go build -o a1.exe .
+go build -mod=mod -o a1.exe .
exec ./a1.exe
stdout 'Don''t communicate by sharing memory'
@@ -32,7 +32,7 @@ stderr 'rsc.io/quote/v3@v3.0.0 used for two different module paths \(not-rsc.io/
# Modules that do not (yet) exist upstream can be replaced too.
cp go.mod.orig go.mod
go mod edit -replace=not-rsc.io/quote/v3@v3.1.0=./local/rsc.io/quote/v3
-go build -o a5.exe ./usenewmodule
+go build -mod=mod -o a5.exe ./usenewmodule
! stderr 'finding not-rsc.io/quote/v3'
grep 'not-rsc.io/quote/v3 v3.1.0' go.mod
exec ./a5.exe
diff --git a/src/cmd/go/testdata/script/mod_replace_gopkgin.txt b/src/cmd/go/testdata/script/mod_replace_gopkgin.txt
index 674c99cb0cd..df752d9716e 100644
--- a/src/cmd/go/testdata/script/mod_replace_gopkgin.txt
+++ b/src/cmd/go/testdata/script/mod_replace_gopkgin.txt
@@ -11,6 +11,7 @@
env GO111MODULE=on
env GOPROXY=direct
env GOSUMDB=off
+env GOFLAGS=-mod=mod
# Replacing gopkg.in/[…].vN with a repository with a root go.mod file
# specifying […].vN and a compatible version should succeed, even if
diff --git a/src/cmd/go/testdata/script/mod_replace_import.txt b/src/cmd/go/testdata/script/mod_replace_import.txt
index 54b1a124485..b4de5c50f70 100644
--- a/src/cmd/go/testdata/script/mod_replace_import.txt
+++ b/src/cmd/go/testdata/script/mod_replace_import.txt
@@ -7,6 +7,7 @@ cp go.mod go.mod.orig
cmp go.mod go.mod.orig
# 'go list' should resolve imports using replacements.
+go get -d
go list all
stdout 'example.com/a/b$'
stdout 'example.com/x/v3$'
diff --git a/src/cmd/go/testdata/script/mod_require_exclude.txt b/src/cmd/go/testdata/script/mod_require_exclude.txt
index 1a0fc3097b0..9156d4ce5d5 100644
--- a/src/cmd/go/testdata/script/mod_require_exclude.txt
+++ b/src/cmd/go/testdata/script/mod_require_exclude.txt
@@ -20,7 +20,7 @@ cmp go.mod go.mod.orig
# With the selected version excluded, commands that load only modules should
# drop the excluded module.
-go list -m all
+go list -m -mod=mod all
stderr '^go: dropping requirement on excluded version rsc.io/sampler v1\.99\.99$'
stdout '^x$'
! stdout '^rsc.io/sampler'
@@ -30,7 +30,7 @@ cmp go.mod go.moddrop
# from the next-highest version.
cp go.mod.orig go.mod
-go list -f '{{with .Module}}{{.Path}} {{.Version}}{{end}}' all
+go list -mod=mod -f '{{with .Module}}{{.Path}} {{.Version}}{{end}}' all
stderr '^go: dropping requirement on excluded version rsc.io/sampler v1\.99\.99$'
stdout '^x $'
! stdout '^rsc.io/sampler v1.99.99'
@@ -38,13 +38,13 @@ stdout '^rsc.io/sampler v1.3.0'
# build with newer version available
cp go.mod2 go.mod
-go list -f '{{with .Module}}{{.Path}} {{.Version}}{{end}}' all
+go list -mod=mod -f '{{with .Module}}{{.Path}} {{.Version}}{{end}}' all
stderr '^go: dropping requirement on excluded version rsc.io/quote v1\.5\.1$'
stdout 'rsc.io/quote v1.5.2'
# build with excluded newer version
cp go.mod3 go.mod
-go list -f '{{with .Module}}{{.Path}} {{.Version}}{{end}}' all
+go list -mod=mod -f '{{with .Module}}{{.Path}} {{.Version}}{{end}}' all
! stderr '^go: dropping requirement'
stdout 'rsc.io/quote v1.5.1'
diff --git a/src/cmd/go/testdata/script/mod_retention.txt b/src/cmd/go/testdata/script/mod_retention.txt
index 1d83e6c07e8..a4441c4b3c7 100644
--- a/src/cmd/go/testdata/script/mod_retention.txt
+++ b/src/cmd/go/testdata/script/mod_retention.txt
@@ -7,7 +7,7 @@ env GO111MODULE=on
# Control case: verify that go.mod.tidy is actually tidy.
cp go.mod.tidy go.mod
-go list all
+go list -mod=mod all
cmp go.mod go.mod.tidy
@@ -35,7 +35,7 @@ cmp go.mod go.mod.tidy
# "// indirect" comments should be removed if direct dependencies are seen.
# changes.
cp go.mod.indirect go.mod
-go list all
+go list -mod=mod all
cmp go.mod go.mod.tidy
# "// indirect" comments should be added if appropriate.
@@ -63,7 +63,7 @@ cmp go.mod go.mod.tidy
# A missing "go" version directive should be added.
# However, that should not remove other redundant requirements.
cp go.mod.nogo go.mod
-go list all
+go list -mod=mod all
cmpenv go.mod go.mod.currentgo
diff --git a/src/cmd/go/testdata/script/mod_retract.txt b/src/cmd/go/testdata/script/mod_retract.txt
index 5d21902043c..a52e05bc72e 100644
--- a/src/cmd/go/testdata/script/mod_retract.txt
+++ b/src/cmd/go/testdata/script/mod_retract.txt
@@ -1,5 +1,8 @@
cp go.mod go.mod.orig
+# Populate go.sum.
+go mod download
+
# 'go list pkg' does not report an error when a retracted version is used.
go list -e -f '{{if .Error}}{{.Error}}{{end}}' ./use
! stdout .
@@ -17,7 +20,7 @@ exists $GOPATH/pkg/mod/cache/download/example.com/retract/@v/v1.0.0-bad.mod
# Importing a package from a module with a retracted latest version will
# select the latest non-retracted version.
-go list ./use_self_prev
+go get -d ./use_self_prev
go list -m example.com/retract/self/prev
stdout '^example.com/retract/self/prev v1.1.0$'
exists $GOPATH/pkg/mod/cache/download/example.com/retract/self/prev/@v/v1.9.0.mod
diff --git a/src/cmd/go/testdata/script/mod_retract_replace.txt b/src/cmd/go/testdata/script/mod_retract_replace.txt
index b710485fa71..7aec438ddae 100644
--- a/src/cmd/go/testdata/script/mod_retract_replace.txt
+++ b/src/cmd/go/testdata/script/mod_retract_replace.txt
@@ -1,6 +1,9 @@
# If the latest unretracted version of a module is replaced, 'go list' should
# obtain retractions from the replacement.
+# Populate go.sum.
+go get -d
+
# The latest version, v1.9.0, is not available on the proxy.
! go list -m -retracted example.com/retract/missingmod
stderr '^go list -m: loading module retractions: example.com/retract/missingmod@v1.9.0:.*404 Not Found$'
@@ -24,9 +27,9 @@ go list -m -retracted -f '{{range .Retracted}}{{.}}{{end}}' example.com/retract
go list -m -retracted -f '{{if .Replace}}replaced{{end}}' example.com/retract
! stdout .
go mod edit -replace example.com/retract@v1.0.0-good=example.com/retract@v1.0.0-bad
-go list -m -retracted -f '{{range .Retracted}}{{.}}{{end}}' example.com/retract
+go list -m -mod=mod -retracted -f '{{range .Retracted}}{{.}}{{end}}' example.com/retract
stdout '^bad$'
-go list -m -retracted -f '{{with .Replace}}{{range .Retracted}}{{.}}{{end}}{{end}}' example.com/retract
+go list -m -mod=mod -retracted -f '{{with .Replace}}{{range .Retracted}}{{.}}{{end}}{{end}}' example.com/retract
stdout '^bad$'
-- go.mod --
@@ -38,6 +41,13 @@ require (
example.com/retract v1.0.0-good
example.com/retract/missingmod v1.0.0
)
+-- use.go --
+package use
+
+import (
+ _ "example.com/retract"
+ _ "example.com/retract/missingmod"
+)
-- missingmod-v1.0.0/go.mod --
module example.com/retract/missingmod
diff --git a/src/cmd/go/testdata/script/mod_sum_lookup.txt b/src/cmd/go/testdata/script/mod_sum_lookup.txt
index ed80a449845..e0219213800 100644
--- a/src/cmd/go/testdata/script/mod_sum_lookup.txt
+++ b/src/cmd/go/testdata/script/mod_sum_lookup.txt
@@ -1,13 +1,14 @@
# When we attempt to resolve an import that doesn't exist, we should not save
# hashes for downloaded modules.
# Verifies golang.org/issue/36260.
-go list -e -tags=ignore ./noexist
+# TODO(golang.org/issue/26603): use 'go mod tidy -e' when implemented.
+go list -e -mod=mod -tags=ignore ./noexist
! exists go.sum
# When an import is resolved successfully, we should only save hashes for
# the module that provides the package, not for other modules looked up.
# Verifies golang.org/issue/31580.
-go list ./exist
+go get -d ./exist
grep '^example.com/join v1.1.0 h1:' go.sum
! grep '^example.com/join/subpkg' go.sum
cp go.sum go.list.sum
diff --git a/src/cmd/go/testdata/script/mod_sumdb_golang.txt b/src/cmd/go/testdata/script/mod_sumdb_golang.txt
index d9fb63acb03..cc0b0da474a 100644
--- a/src/cmd/go/testdata/script/mod_sumdb_golang.txt
+++ b/src/cmd/go/testdata/script/mod_sumdb_golang.txt
@@ -34,7 +34,7 @@ cmp go.sum saved.sum
# Should use the checksum database to validate new go.sum lines,
# but not need to fetch any new data from the proxy.
rm go.sum
-go list -x rsc.io/quote
+go list -mod=mod -x rsc.io/quote
! stderr github
! stderr proxy.golang.org/rsc.io/quote
stderr sum.golang.org/tile
@@ -45,7 +45,7 @@ cmp go.sum saved.sum
env TESTGOPROXY404=1
go clean -modcache
rm go.sum
-go list -x rsc.io/quote
+go list -mod=mod -x rsc.io/quote
stderr 'proxy.golang.org.*404 testing'
stderr github.com/rsc
cmp go.sum saved.sum
diff --git a/src/cmd/go/testdata/script/mod_symlink.txt b/src/cmd/go/testdata/script/mod_symlink.txt
index 49bece2b84d..dbc23fb8f09 100644
--- a/src/cmd/go/testdata/script/mod_symlink.txt
+++ b/src/cmd/go/testdata/script/mod_symlink.txt
@@ -1,16 +1,19 @@
env GO111MODULE=on
[!symlink] skip
-# 'go list' should resolve modules of imported packages.
+# 'go get -d' should resolve modules of imported packages.
+go get -d
go list -deps -f '{{.Module}}' .
stdout golang.org/x/text
+go get -d ./subpkg
go list -deps -f '{{.Module}}' ./subpkg
stdout golang.org/x/text
# Create a copy of the module using symlinks in src/links.
mkdir links
symlink links/go.mod -> $GOPATH/src/go.mod
+symlink links/go.sum -> $GOPATH/src/go.sum
symlink links/issue.go -> $GOPATH/src/issue.go
mkdir links/subpkg
symlink links/subpkg/issue.go -> $GOPATH/src/subpkg/issue.go
diff --git a/src/cmd/go/testdata/script/mod_test.txt b/src/cmd/go/testdata/script/mod_test.txt
index 8f2da2f2a5b..50f00355c17 100644
--- a/src/cmd/go/testdata/script/mod_test.txt
+++ b/src/cmd/go/testdata/script/mod_test.txt
@@ -1,4 +1,5 @@
env GO111MODULE=on
+env GOFLAGS=-mod=mod
[short] skip
# TODO(bcmills): Convert the 'go test' calls below to 'go list -test' once 'go
diff --git a/src/cmd/go/testdata/script/mod_tidy_replace.txt b/src/cmd/go/testdata/script/mod_tidy_replace.txt
index c3158f8610e..7b00bf13842 100644
--- a/src/cmd/go/testdata/script/mod_tidy_replace.txt
+++ b/src/cmd/go/testdata/script/mod_tidy_replace.txt
@@ -1,4 +1,5 @@
env GO111MODULE=on
+env GOFLAGS=-mod=mod
[short] skip
# golang.org/issue/30166: 'go mod tidy' should not crash if a replaced module is
diff --git a/src/cmd/go/testdata/script/mod_upgrade_patch.txt b/src/cmd/go/testdata/script/mod_upgrade_patch.txt
index 3939e54c1bf..1ef25b9aef5 100644
--- a/src/cmd/go/testdata/script/mod_upgrade_patch.txt
+++ b/src/cmd/go/testdata/script/mod_upgrade_patch.txt
@@ -2,6 +2,7 @@ env GO111MODULE=on
[short] skip
# Initially, we are at v1.0.0 for all dependencies.
+go get -d
cp go.mod go.mod.orig
go list -m all
stdout '^patch.example.com/direct v1.0.0'
diff --git a/src/cmd/go/testdata/script/mod_vcs_missing.txt b/src/cmd/go/testdata/script/mod_vcs_missing.txt
index a755935b53e..f8be43cf4c6 100644
--- a/src/cmd/go/testdata/script/mod_vcs_missing.txt
+++ b/src/cmd/go/testdata/script/mod_vcs_missing.txt
@@ -5,14 +5,14 @@ env GO111MODULE=on
env GOPROXY=direct
cd empty
-! go list launchpad.net/gocheck
+! go get -d launchpad.net/gocheck
stderr '"bzr": executable file not found'
cd ..
# 1.11 used to give the cryptic error "cannot find module for path" here, but
# only for a main package.
cd main
-! go build
+! go build -mod=mod
stderr '"bzr": executable file not found'
cd ..
diff --git a/src/cmd/go/testdata/script/mod_vendor_build.txt b/src/cmd/go/testdata/script/mod_vendor_build.txt
index 0c359cea6e9..4efda55e08f 100644
--- a/src/cmd/go/testdata/script/mod_vendor_build.txt
+++ b/src/cmd/go/testdata/script/mod_vendor_build.txt
@@ -1,6 +1,9 @@
env GO111MODULE=on
[short] skip
+# Populate go.mod and go.sum.
+go mod tidy
+
# initial conditions: using sampler v1.3.0, not listed in go.mod.
go list -deps
stdout rsc.io/sampler
diff --git a/src/cmd/go/testdata/script/mod_verify.txt b/src/cmd/go/testdata/script/mod_verify.txt
index 39184004356..43812d069f6 100644
--- a/src/cmd/go/testdata/script/mod_verify.txt
+++ b/src/cmd/go/testdata/script/mod_verify.txt
@@ -56,7 +56,7 @@ go mod tidy
# Packages below module root should not be mentioned in go.sum.
rm go.sum
go mod edit -droprequire rsc.io/quote
-go list rsc.io/quote/buggy # re-resolves import path and updates go.mod
+go get -d rsc.io/quote/buggy
grep '^rsc.io/quote v1.5.2/go.mod ' go.sum
! grep buggy go.sum
diff --git a/src/cmd/go/testdata/script/mod_why.txt b/src/cmd/go/testdata/script/mod_why.txt
index 10a4f9fbea7..c0ff4647a75 100644
--- a/src/cmd/go/testdata/script/mod_why.txt
+++ b/src/cmd/go/testdata/script/mod_why.txt
@@ -1,6 +1,9 @@
env GO111MODULE=on
[short] skip
+# Populate go.sum.
+go mod tidy
+
go list -test all
stdout rsc.io/quote
stdout golang.org/x/text/language
diff --git a/src/cmd/go/testdata/script/modfile_flag.txt b/src/cmd/go/testdata/script/modfile_flag.txt
index f05bf03fbf0..0ad08808178 100644
--- a/src/cmd/go/testdata/script/modfile_flag.txt
+++ b/src/cmd/go/testdata/script/modfile_flag.txt
@@ -37,10 +37,10 @@ go mod why rsc.io/quote
# 'go list' and other commands with build flags should work.
# They should update the alternate go.mod when a dependency is missing.
go mod edit -droprequire rsc.io/quote
-go list .
+go list -mod=mod .
grep rsc.io/quote go.alt.mod
-go build -n .
-go test -n .
+go build -n -mod=mod .
+go test -n -mod=mod .
go get -d rsc.io/quote
diff --git a/src/cmd/go/testdata/script/version.txt b/src/cmd/go/testdata/script/version.txt
index 0123ac6d53f..81ca6986208 100644
--- a/src/cmd/go/testdata/script/version.txt
+++ b/src/cmd/go/testdata/script/version.txt
@@ -14,6 +14,7 @@ env GO111MODULE=on
[short] skip
# Check that 'go version' and 'go version -m' work on a binary built in module mode.
+go get -d rsc.io/fortune
go build -o fortune.exe rsc.io/fortune
go version fortune.exe
stdout '^fortune.exe: .+'
diff --git a/src/cmd/go/testdata/script/version_replace.txt b/src/cmd/go/testdata/script/version_replace.txt
index b657086f095..ec98f4e3f3a 100644
--- a/src/cmd/go/testdata/script/version_replace.txt
+++ b/src/cmd/go/testdata/script/version_replace.txt
@@ -1,7 +1,7 @@
[short] skip
go mod download example.com/printversion@v0.1.0 example.com/printversion@v1.0.0
-
+go get -d example.com/printversion@v0.1.0
go install example.com/printversion
go run example.com/printversion
From b22af9b407dc29d1a733976484904ad0ab168466 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Thu, 10 Sep 2020 22:41:29 -0400
Subject: [PATCH 0035/1082] cmd/link: record only the first occurance in
Reachparent graph
In the deadcode pass, a type symbol may be marked twice, one
without UsedInIface, one with. For the second time, don't
update the Reachparent graph, so it only records the path of
the first time the symbol is reached. This ensures the
Reachparent graph is acyclic.
TODO: add a test. (This only affects GOEXPERIMENT=fieldtrack)
Change-Id: I68e8a1a69c3830bc8aee5df946151dc22dcb2b29
Reviewed-on: https://go-review.googlesource.com/c/go/+/254297
Run-TryBot: Cherry Zhang
TryBot-Result: Gobot Gobot
Reviewed-by: Than McIntosh
---
src/cmd/link/internal/ld/deadcode.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 02694297236..35545f950ef 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -209,7 +209,7 @@ func (d *deadcodePass) mark(symIdx, parent loader.Sym) {
if symIdx != 0 && !d.ldr.AttrReachable(symIdx) {
d.wq.push(symIdx)
d.ldr.SetAttrReachable(symIdx, true)
- if objabi.Fieldtrack_enabled != 0 {
+ if objabi.Fieldtrack_enabled != 0 && d.ldr.Reachparent[symIdx] == 0 {
d.ldr.Reachparent[symIdx] = parent
}
if *flagDumpDep {
From 6e3df749b1058ecfaf5f6601f6f8678c0971da8e Mon Sep 17 00:00:00 2001
From: Jay Conrod
Date: Wed, 9 Sep 2020 16:35:56 -0400
Subject: [PATCH 0036/1082] cmd/go: refactor -mod flag parsing
Keep track of whether the -mod flag was set explicitly. When
-mod=readonly is the default, we'll want to adjust our error messages
if it's set explicitly.
Also, register the -mod, -modcacherw, and -modfile flags in functions
in internal/base instead of internal/work. 'go mod' commands that
don't load packages shouldn't depend on internal/work.
For #40728
Change-Id: I272aea9e19908ba37e151baac4ea8630e90f241f
Reviewed-on: https://go-review.googlesource.com/c/go/+/253744
Run-TryBot: Jay Conrod
TryBot-Result: Gobot Gobot
Reviewed-by: Michael Matloob
Reviewed-by: Bryan C. Mills
---
src/cmd/go/internal/base/flag.go | 35 +++++++++++++++++++++++---
src/cmd/go/internal/cfg/cfg.go | 3 ++-
src/cmd/go/internal/fmtcmd/fmt.go | 3 ++-
src/cmd/go/internal/modcmd/download.go | 3 +--
src/cmd/go/internal/modcmd/edit.go | 3 +--
src/cmd/go/internal/modcmd/graph.go | 3 +--
src/cmd/go/internal/modcmd/init.go | 3 +--
src/cmd/go/internal/modcmd/tidy.go | 3 +--
src/cmd/go/internal/modcmd/vendor.go | 3 +--
src/cmd/go/internal/modcmd/verify.go | 3 +--
src/cmd/go/internal/modcmd/why.go | 3 +--
src/cmd/go/internal/modload/init.go | 23 +++++++++--------
src/cmd/go/internal/work/build.go | 14 +++--------
src/cmd/go/internal/work/init.go | 2 +-
14 files changed, 61 insertions(+), 43 deletions(-)
diff --git a/src/cmd/go/internal/base/flag.go b/src/cmd/go/internal/base/flag.go
index 6727196816b..c97c7445200 100644
--- a/src/cmd/go/internal/base/flag.go
+++ b/src/cmd/go/internal/base/flag.go
@@ -28,13 +28,42 @@ func (v *StringsFlag) String() string {
return ""
}
+// explicitStringFlag is like a regular string flag, but it also tracks whether
+// the string was set explicitly to a non-empty value.
+type explicitStringFlag struct {
+ value *string
+ explicit *bool
+}
+
+func (f explicitStringFlag) String() string {
+ if f.value == nil {
+ return ""
+ }
+ return *f.value
+}
+
+func (f explicitStringFlag) Set(v string) error {
+ *f.value = v
+ if v != "" {
+ *f.explicit = true
+ }
+ return nil
+}
+
// AddBuildFlagsNX adds the -n and -x build flags to the flag set.
func AddBuildFlagsNX(flags *flag.FlagSet) {
flags.BoolVar(&cfg.BuildN, "n", false, "")
flags.BoolVar(&cfg.BuildX, "x", false, "")
}
-// AddLoadFlags adds the -mod build flag to the flag set.
-func AddLoadFlags(flags *flag.FlagSet) {
- flags.StringVar(&cfg.BuildMod, "mod", "", "")
+// AddModFlag adds the -mod build flag to the flag set.
+func AddModFlag(flags *flag.FlagSet) {
+ flags.Var(explicitStringFlag{value: &cfg.BuildMod, explicit: &cfg.BuildModExplicit}, "mod", "")
+}
+
+// AddModCommonFlags adds the module-related flags common to build commands
+// and 'go mod' subcommands.
+func AddModCommonFlags(flags *flag.FlagSet) {
+ flags.BoolVar(&cfg.ModCacheRW, "modcacherw", false, "")
+ flags.StringVar(&cfg.ModFile, "modfile", "", "")
}
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index f9bbcd9180a..f874b880a6d 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -27,7 +27,8 @@ var (
BuildBuildmode string // -buildmode flag
BuildContext = defaultContext()
BuildMod string // -mod flag
- BuildModReason string // reason -mod flag is set, if set by default
+ BuildModExplicit bool // whether -mod was set explicitly
+ BuildModReason string // reason -mod was set, if set by default
BuildI bool // -i flag
BuildLinkshared bool // -linkshared flag
BuildMSan bool // -msan flag
diff --git a/src/cmd/go/internal/fmtcmd/fmt.go b/src/cmd/go/internal/fmtcmd/fmt.go
index f96cff429cb..b0c1c59b40c 100644
--- a/src/cmd/go/internal/fmtcmd/fmt.go
+++ b/src/cmd/go/internal/fmtcmd/fmt.go
@@ -23,7 +23,8 @@ import (
func init() {
base.AddBuildFlagsNX(&CmdFmt.Flag)
- base.AddLoadFlags(&CmdFmt.Flag)
+ base.AddModFlag(&CmdFmt.Flag)
+ base.AddModCommonFlags(&CmdFmt.Flag)
}
var CmdFmt = &base.Command{
diff --git a/src/cmd/go/internal/modcmd/download.go b/src/cmd/go/internal/modcmd/download.go
index 41f294d4755..0ea5638e703 100644
--- a/src/cmd/go/internal/modcmd/download.go
+++ b/src/cmd/go/internal/modcmd/download.go
@@ -14,7 +14,6 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
- "cmd/go/internal/work"
"golang.org/x/mod/module"
)
@@ -64,7 +63,7 @@ func init() {
// TODO(jayconrod): https://golang.org/issue/35849 Apply -x to other 'go mod' commands.
cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "")
- work.AddModCommonFlags(cmdDownload)
+ base.AddModCommonFlags(&cmdDownload.Flag)
}
type moduleJSON struct {
diff --git a/src/cmd/go/internal/modcmd/edit.go b/src/cmd/go/internal/modcmd/edit.go
index 18bdd34cd07..03a774b8248 100644
--- a/src/cmd/go/internal/modcmd/edit.go
+++ b/src/cmd/go/internal/modcmd/edit.go
@@ -19,7 +19,6 @@ import (
"cmd/go/internal/lockedfile"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
- "cmd/go/internal/work"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
@@ -154,7 +153,7 @@ func init() {
cmdEdit.Flag.Var(flagFunc(flagRetract), "retract", "")
cmdEdit.Flag.Var(flagFunc(flagDropRetract), "dropretract", "")
- work.AddModCommonFlags(cmdEdit)
+ base.AddModCommonFlags(&cmdEdit.Flag)
base.AddBuildFlagsNX(&cmdEdit.Flag)
}
diff --git a/src/cmd/go/internal/modcmd/graph.go b/src/cmd/go/internal/modcmd/graph.go
index 513536a010c..a149b656053 100644
--- a/src/cmd/go/internal/modcmd/graph.go
+++ b/src/cmd/go/internal/modcmd/graph.go
@@ -15,7 +15,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/modload"
- "cmd/go/internal/work"
"golang.org/x/mod/module"
)
@@ -33,7 +32,7 @@ path@version, except for the main module, which has no @version suffix.
}
func init() {
- work.AddModCommonFlags(cmdGraph)
+ base.AddModCommonFlags(&cmdGraph.Flag)
}
func runGraph(ctx context.Context, cmd *base.Command, args []string) {
diff --git a/src/cmd/go/internal/modcmd/init.go b/src/cmd/go/internal/modcmd/init.go
index b6cffd332df..21b235653ea 100644
--- a/src/cmd/go/internal/modcmd/init.go
+++ b/src/cmd/go/internal/modcmd/init.go
@@ -9,7 +9,6 @@ package modcmd
import (
"cmd/go/internal/base"
"cmd/go/internal/modload"
- "cmd/go/internal/work"
"context"
"os"
"strings"
@@ -30,7 +29,7 @@ To override this guess, supply the module path as an argument.
}
func init() {
- work.AddModCommonFlags(cmdInit)
+ base.AddModCommonFlags(&cmdInit.Flag)
}
func runInit(ctx context.Context, cmd *base.Command, args []string) {
diff --git a/src/cmd/go/internal/modcmd/tidy.go b/src/cmd/go/internal/modcmd/tidy.go
index 4dcb62e02f6..30df674ef62 100644
--- a/src/cmd/go/internal/modcmd/tidy.go
+++ b/src/cmd/go/internal/modcmd/tidy.go
@@ -10,7 +10,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/modload"
- "cmd/go/internal/work"
"context"
)
@@ -32,7 +31,7 @@ to standard error.
func init() {
cmdTidy.Run = runTidy // break init cycle
cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
- work.AddModCommonFlags(cmdTidy)
+ base.AddModCommonFlags(&cmdTidy.Flag)
}
func runTidy(ctx context.Context, cmd *base.Command, args []string) {
diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go
index 30334f3a429..91d2509452c 100644
--- a/src/cmd/go/internal/modcmd/vendor.go
+++ b/src/cmd/go/internal/modcmd/vendor.go
@@ -19,7 +19,6 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/imports"
"cmd/go/internal/modload"
- "cmd/go/internal/work"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
@@ -41,7 +40,7 @@ modules and packages to standard error.
func init() {
cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "")
- work.AddModCommonFlags(cmdVendor)
+ base.AddModCommonFlags(&cmdVendor.Flag)
}
func runVendor(ctx context.Context, cmd *base.Command, args []string) {
diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go
index d5428258234..7700588bde5 100644
--- a/src/cmd/go/internal/modcmd/verify.go
+++ b/src/cmd/go/internal/modcmd/verify.go
@@ -17,7 +17,6 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
- "cmd/go/internal/work"
"golang.org/x/mod/module"
"golang.org/x/mod/sumdb/dirhash"
@@ -38,7 +37,7 @@ non-zero status.
}
func init() {
- work.AddModCommonFlags(cmdVerify)
+ base.AddModCommonFlags(&cmdVerify.Flag)
}
func runVerify(ctx context.Context, cmd *base.Command, args []string) {
diff --git a/src/cmd/go/internal/modcmd/why.go b/src/cmd/go/internal/modcmd/why.go
index 30b15fc1532..8454fdfec6b 100644
--- a/src/cmd/go/internal/modcmd/why.go
+++ b/src/cmd/go/internal/modcmd/why.go
@@ -11,7 +11,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/modload"
- "cmd/go/internal/work"
"golang.org/x/mod/module"
)
@@ -58,7 +57,7 @@ var (
func init() {
cmdWhy.Run = runWhy // break init cycle
- work.AddModCommonFlags(cmdWhy)
+ base.AddModCommonFlags(&cmdWhy.Flag)
}
func runWhy(ctx context.Context, cmd *base.Command, args []string) {
diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go
index 8e8fb9e6a1f..1f50dcb11c1 100644
--- a/src/cmd/go/internal/modload/init.go
+++ b/src/cmd/go/internal/modload/init.go
@@ -518,17 +518,20 @@ func modFileToBuildList() {
// setDefaultBuildMod sets a default value for cfg.BuildMod
// if it is currently empty.
func setDefaultBuildMod() {
- if cfg.BuildMod != "" {
+ if cfg.BuildModExplicit {
// Don't override an explicit '-mod=' argument.
return
}
- cfg.BuildMod = "mod"
+
if cfg.CmdName == "get" || strings.HasPrefix(cfg.CmdName, "mod ") {
- // Don't set -mod implicitly for commands whose purpose is to
- // manipulate the build list.
+ // 'get' and 'go mod' commands may update go.mod automatically.
+ // TODO(jayconrod): should this narrower? Should 'go mod download' or
+ // 'go mod graph' update go.mod by default?
+ cfg.BuildMod = "mod"
return
}
if modRoot == "" {
+ cfg.BuildMod = "mod"
return
}
@@ -546,18 +549,18 @@ func setDefaultBuildMod() {
}
}
- // Since a vendor directory exists, we have a non-trivial reason for
- // choosing -mod=mod, although it probably won't be used for anything.
- // Record the reason anyway for consistency.
- // It may be overridden if we switch to mod=readonly below.
- cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s.", modGo)
+ // Since a vendor directory exists, we should record why we didn't use it.
+ // This message won't normally be shown, but it may appear with import errors.
+ cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo)
}
p := ModFilePath()
if fi, err := os.Stat(p); err == nil && !hasWritePerm(p, fi) {
cfg.BuildMod = "readonly"
cfg.BuildModReason = "go.mod file is read-only."
+ return
}
+ cfg.BuildMod = "mod"
}
func legacyModInit() {
@@ -857,7 +860,7 @@ func WriteGoMod() {
// prefer to report a dirty go.mod over a dirty go.sum
if cfg.BuildModReason != "" {
base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly\n\t(%s)", cfg.BuildModReason)
- } else {
+ } else if cfg.BuildModExplicit {
base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly")
}
}
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index d020aa6e9ff..e99982ed36e 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -240,13 +240,12 @@ const (
// AddBuildFlags adds the flags common to the build, clean, get,
// install, list, run, and test commands.
func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
+ base.AddBuildFlagsNX(&cmd.Flag)
cmd.Flag.BoolVar(&cfg.BuildA, "a", false, "")
- cmd.Flag.BoolVar(&cfg.BuildN, "n", false, "")
cmd.Flag.IntVar(&cfg.BuildP, "p", cfg.BuildP, "")
if mask&OmitVFlag == 0 {
cmd.Flag.BoolVar(&cfg.BuildV, "v", false, "")
}
- cmd.Flag.BoolVar(&cfg.BuildX, "x", false, "")
cmd.Flag.Var(&load.BuildAsmflags, "asmflags", "")
cmd.Flag.Var(buildCompiler{}, "compiler", "")
@@ -254,10 +253,10 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
cmd.Flag.Var(&load.BuildGcflags, "gcflags", "")
cmd.Flag.Var(&load.BuildGccgoflags, "gccgoflags", "")
if mask&OmitModFlag == 0 {
- cmd.Flag.StringVar(&cfg.BuildMod, "mod", "", "")
+ base.AddModFlag(&cmd.Flag)
}
if mask&OmitModCommonFlags == 0 {
- AddModCommonFlags(cmd)
+ base.AddModCommonFlags(&cmd.Flag)
}
cmd.Flag.StringVar(&cfg.BuildContext.InstallSuffix, "installsuffix", "", "")
cmd.Flag.Var(&load.BuildLdflags, "ldflags", "")
@@ -275,13 +274,6 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
cmd.Flag.StringVar(&cfg.DebugTrace, "debug-trace", "", "")
}
-// AddModCommonFlags adds the module-related flags common to build commands
-// and 'go mod' subcommands.
-func AddModCommonFlags(cmd *base.Command) {
- cmd.Flag.BoolVar(&cfg.ModCacheRW, "modcacherw", false, "")
- cmd.Flag.StringVar(&cfg.ModFile, "modfile", "", "")
-}
-
// tagsFlag is the implementation of the -tags flag.
type tagsFlag []string
diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go
index dad3b10111d..f78020032cd 100644
--- a/src/cmd/go/internal/work/init.go
+++ b/src/cmd/go/internal/work/init.go
@@ -252,7 +252,7 @@ func buildModeInit() {
switch cfg.BuildMod {
case "":
- // ok
+ // Behavior will be determined automatically, as if no flag were passed.
case "readonly", "vendor", "mod":
if !cfg.ModulesEnabled && !inGOFLAGS("-mod") {
base.Fatalf("build flag -mod=%s only valid when using modules", cfg.BuildMod)
From 9214677e7df1e6130249bc83d721130b00d829c4 Mon Sep 17 00:00:00 2001
From: Jay Conrod
Date: Wed, 9 Sep 2020 16:41:55 -0400
Subject: [PATCH 0037/1082] cmd/go: refactor modload.Import for better
-mod=readonly errors
When -mod=readonly is set, Import will now allow imports from
replacements without explicit requirements. With -mod=mod, this would
add a new requirement but does not trigger a module lookup, so it's
determinisitic.
Before reporting an error for an unknown import with -mod=readonly,
check whether the import is valid. If there's a typo in the import,
that's more relevant.
For #40728
Change-Id: I05e138ff76ba3d0eb2e3010c15589fa363deb8d3
Reviewed-on: https://go-review.googlesource.com/c/go/+/253745
Run-TryBot: Jay Conrod
TryBot-Result: Gobot Gobot
Reviewed-by: Michael Matloob
Reviewed-by: Bryan C. Mills
---
src/cmd/go/internal/modload/import.go | 53 ++++++++++++++-----
.../go/testdata/script/mod_build_info_err.txt | 2 +-
2 files changed, 41 insertions(+), 14 deletions(-)
diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go
index c625184b8b8..10b1e7f4b84 100644
--- a/src/cmd/go/internal/modload/import.go
+++ b/src/cmd/go/internal/modload/import.go
@@ -107,6 +107,25 @@ func (e *AmbiguousImportError) Error() string {
var _ load.ImportPathError = &AmbiguousImportError{}
+type invalidImportError struct {
+ importPath string
+ err error
+}
+
+func (e *invalidImportError) ImportPath() string {
+ return e.importPath
+}
+
+func (e *invalidImportError) Error() string {
+ return e.err.Error()
+}
+
+func (e *invalidImportError) Unwrap() error {
+ return e.err
+}
+
+var _ load.ImportPathError = &invalidImportError{}
+
// importFromBuildList finds the module and directory in the build list
// containing the package with the given import path. The answer must be unique:
// importFromBuildList returns an error if multiple modules attempt to provide
@@ -207,17 +226,6 @@ func importFromBuildList(ctx context.Context, path string) (m module.Version, di
func queryImport(ctx context.Context, path string) (module.Version, error) {
pathIsStd := search.IsStandardImportPath(path)
- if cfg.BuildMod == "readonly" {
- var queryErr error
- if !pathIsStd {
- if cfg.BuildModReason == "" {
- queryErr = fmt.Errorf("import lookup disabled by -mod=%s", cfg.BuildMod)
- } else {
- queryErr = fmt.Errorf("import lookup disabled by -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
- }
- }
- return module.Version{}, &ImportMissingError{Path: path, QueryErr: queryErr}
- }
if modRoot == "" && !allowMissingModuleImports {
return module.Version{}, &ImportMissingError{
Path: path,
@@ -226,8 +234,9 @@ func queryImport(ctx context.Context, path string) (module.Version, error) {
}
// Not on build list.
- // To avoid spurious remote fetches, next try the latest replacement for each module.
- // (golang.org/issue/26241)
+ // To avoid spurious remote fetches, next try the latest replacement for each
+ // module (golang.org/issue/26241). This should give a useful message
+ // in -mod=readonly, and it will allow us to add a requirement with -mod=mod.
if modFile != nil {
latest := map[string]string{} // path -> version
for _, r := range modFile.Replace {
@@ -288,6 +297,11 @@ func queryImport(ctx context.Context, path string) (module.Version, error) {
}
}
+ // Before any further lookup, check that the path is valid.
+ if err := module.CheckImportPath(path); err != nil {
+ return module.Version{}, &invalidImportError{importPath: path, err: err}
+ }
+
if pathIsStd {
// This package isn't in the standard library, isn't in any module already
// in the build list, and isn't in any other module that the user has
@@ -299,6 +313,19 @@ func queryImport(ctx context.Context, path string) (module.Version, error) {
return module.Version{}, &ImportMissingError{Path: path}
}
+ if cfg.BuildMod == "readonly" {
+ var queryErr error
+ if cfg.BuildModExplicit {
+ queryErr = fmt.Errorf("import lookup disabled by -mod=%s", cfg.BuildMod)
+ } else if cfg.BuildModReason != "" {
+ queryErr = fmt.Errorf("import lookup disabled by -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
+ }
+ return module.Version{}, &ImportMissingError{Path: path, QueryErr: queryErr}
+ }
+
+ // Look up module containing the package, for addition to the build list.
+ // Goal is to determine the module, download it to dir,
+ // and return m, dir, ImpportMissingError.
fmt.Fprintf(os.Stderr, "go: finding module for package %s\n", path)
candidates, err := QueryPackage(ctx, path, "latest", CheckAllowed)
diff --git a/src/cmd/go/testdata/script/mod_build_info_err.txt b/src/cmd/go/testdata/script/mod_build_info_err.txt
index 87a099b219c..a6853b5c869 100644
--- a/src/cmd/go/testdata/script/mod_build_info_err.txt
+++ b/src/cmd/go/testdata/script/mod_build_info_err.txt
@@ -2,7 +2,7 @@
# Verifies golang.org/issue/34393.
go list -e -deps -f '{{with .Error}}{{.Pos}}: {{.Err}}{{end}}' ./main
-stdout 'bad[/\\]bad.go:3:8: malformed module path "🐧.example.com/string": invalid char ''🐧'''
+stdout 'bad[/\\]bad.go:3:8: malformed import path "🐧.example.com/string": invalid char ''🐧'''
-- go.mod --
module m
From a531bd5a59177dfef354df8b5b5b529a2a55d015 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Tue, 8 Sep 2020 20:41:51 -0400
Subject: [PATCH 0038/1082] cmd/internal/objfile: recognize Mach-O __DATA_CONST
segment as read-only data
Updates #38830.
Change-Id: I826c6b0a42bc8e48fcda556250ca4a95c73987eb
Reviewed-on: https://go-review.googlesource.com/c/go/+/253918
Run-TryBot: Cherry Zhang
TryBot-Result: Gobot Gobot
Reviewed-by: Than McIntosh
---
src/cmd/internal/objfile/macho.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cmd/internal/objfile/macho.go b/src/cmd/internal/objfile/macho.go
index fdb7e76dfce..1d6963f7c45 100644
--- a/src/cmd/internal/objfile/macho.go
+++ b/src/cmd/internal/objfile/macho.go
@@ -60,7 +60,7 @@ func (f *machoFile) symbols() ([]Sym, error) {
} else if int(s.Sect) <= len(f.macho.Sections) {
sect := f.macho.Sections[s.Sect-1]
switch sect.Seg {
- case "__TEXT":
+ case "__TEXT", "__DATA_CONST":
sym.Code = 'R'
case "__DATA":
sym.Code = 'D'
From ffd95aadcddc34ec2c83971346f04cf7234e0fca Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Thu, 3 Sep 2020 12:59:09 -0400
Subject: [PATCH 0039/1082] cmd/link: put read-only data in __DATA_CONST
segment
On darwin, we put read-only data in __TEXT segment on AMD64 in
exe (non-PIE) buildmode, and in __DATA on everywhere else. This
is not ideal: things in __DATA segment are not read-only, and
being mapped R/W may use more run-time resources.
In fact, newer darwin systems support a __DATA_CONST segment,
which the dynamic linker will map it read-only after applying
relocations. Use that.
Fixes #38830.
Change-Id: Ic281e6c6ca8ef5fec4bb7c5b71c50dd5393e78ae
Reviewed-on: https://go-review.googlesource.com/c/go/+/253919
Reviewed-by: Than McIntosh
---
src/cmd/link/internal/ld/data.go | 20 ++++++++++-----
src/cmd/link/internal/ld/macho.go | 41 ++++++++++++++++++------------
src/cmd/link/internal/ld/target.go | 6 ++++-
3 files changed, 43 insertions(+), 24 deletions(-)
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 8324a98a26e..a730125cf2c 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -930,7 +930,7 @@ func writeBlock(ctxt *Link, out *OutBuf, ldr *loader.Loader, syms []loader.Sym,
break
}
if val < addr {
- ldr.Errorf(s, "phase error: addr=%#x but sym=%#x type=%d", addr, val, ldr.SymType(s))
+ ldr.Errorf(s, "phase error: addr=%#x but sym=%#x type=%v sect=%v", addr, val, ldr.SymType(s), ldr.SymSect(s).Name)
errorexit()
}
if addr < val {
@@ -1308,9 +1308,9 @@ func (state *dodataState) makeRelroForSharedLib(target *Link) {
// relro Type before it reaches here.
isRelro = true
case sym.SFUNCTAB:
- if target.IsAIX() && ldr.SymName(s) == "runtime.etypes" {
+ if ldr.SymName(s) == "runtime.etypes" {
// runtime.etypes must be at the end of
- // the relro datas.
+ // the relro data.
isRelro = true
}
}
@@ -1706,7 +1706,7 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.ebss", 0), sect)
bssGcEnd := state.datsize - int64(sect.Vaddr)
- // Emit gcdata for bcc symbols now that symbol values have been assigned.
+ // Emit gcdata for bss symbols now that symbol values have been assigned.
gcsToEmit := []struct {
symName string
symKind sym.SymKind
@@ -1826,13 +1826,16 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
const fallbackPerm = 04
relroSecPerm := fallbackPerm
genrelrosecname := func(suffix string) string {
+ if suffix == "" {
+ return ".rodata"
+ }
return suffix
}
seg := segro
if ctxt.UseRelro() {
segrelro := &Segrelrodata
- if ctxt.LinkMode == LinkExternal && ctxt.HeadType != objabi.Haix {
+ if ctxt.LinkMode == LinkExternal && !ctxt.IsAIX() && !ctxt.IsDarwin() {
// Using a separate segment with an external
// linker results in some programs moving
// their data sections unexpectedly, which
@@ -1845,9 +1848,12 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
state.datsize = 0
}
- genrelrosecname = func(suffix string) string {
- return ".data.rel.ro" + suffix
+ if !ctxt.IsDarwin() { // We don't need the special names on darwin.
+ genrelrosecname = func(suffix string) string {
+ return ".data.rel.ro" + suffix
+ }
}
+
relroReadOnly := []sym.SymKind{}
for _, symnro := range sym.ReadOnly {
symn := sym.RelROMap[symnro]
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index f6356729a6f..9765ce18d36 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -499,16 +499,7 @@ func machoadddynlib(lib string, linkmode LinkMode) {
func machoshbits(ctxt *Link, mseg *MachoSeg, sect *sym.Section, segname string) {
buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1)
- var msect *MachoSect
- if sect.Rwx&1 == 0 && segname != "__DWARF" && (ctxt.Arch.Family == sys.ARM64 ||
- (ctxt.Arch.Family == sys.AMD64 && ctxt.BuildMode != BuildModeExe)) {
- // Darwin external linker on arm64, and on amd64 in c-shared/c-archive buildmode
- // complains about absolute relocs in __TEXT, so if the section is not
- // executable, put it in __DATA segment.
- msect = newMachoSect(mseg, buf, "__DATA")
- } else {
- msect = newMachoSect(mseg, buf, segname)
- }
+ msect := newMachoSect(mseg, buf, segname)
if sect.Rellen > 0 {
msect.reloc = uint32(sect.Reloff)
@@ -633,13 +624,28 @@ func asmbMacho(ctxt *Link) {
machoshbits(ctxt, ms, sect, "__TEXT")
}
+ /* rodata */
+ if ctxt.LinkMode != LinkExternal && Segrelrodata.Length > 0 {
+ ms = newMachoSeg("__DATA_CONST", 20)
+ ms.vaddr = Segrelrodata.Vaddr
+ ms.vsize = Segrelrodata.Length
+ ms.fileoffset = Segrelrodata.Fileoff
+ ms.filesize = Segrelrodata.Filelen
+ ms.prot1 = 3
+ ms.prot2 = 3
+ ms.flag = 0x10 // SG_READ_ONLY
+ }
+
+ for _, sect := range Segrelrodata.Sections {
+ machoshbits(ctxt, ms, sect, "__DATA_CONST")
+ }
+
/* data */
if ctxt.LinkMode != LinkExternal {
- w := int64(Segdata.Length)
ms = newMachoSeg("__DATA", 20)
- ms.vaddr = uint64(va) + uint64(v)
- ms.vsize = uint64(w)
- ms.fileoffset = uint64(v)
+ ms.vaddr = Segdata.Vaddr
+ ms.vsize = Segdata.Length
+ ms.fileoffset = Segdata.Fileoff
ms.filesize = Segdata.Filelen
ms.prot1 = 3
ms.prot2 = 3
@@ -695,7 +701,7 @@ func asmbMacho(ctxt *Link) {
if ctxt.LinkMode != LinkExternal {
ms := newMachoSeg("__LINKEDIT", 0)
- ms.vaddr = uint64(va) + uint64(v) + uint64(Rnd(int64(Segdata.Length), int64(*FlagRound)))
+ ms.vaddr = uint64(Rnd(int64(Segdata.Vaddr+Segdata.Length), int64(*FlagRound)))
ms.vsize = uint64(s1) + uint64(s2) + uint64(s3) + uint64(s4)
ms.fileoffset = uint64(linkoff)
ms.filesize = ms.vsize
@@ -1008,7 +1014,7 @@ func doMachoLink(ctxt *Link) int64 {
size := int(ldr.SymSize(s1) + ldr.SymSize(s2) + ldr.SymSize(s3) + ldr.SymSize(s4))
if size > 0 {
- linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))
+ linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segrelrodata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))
ctxt.Out.SeekSet(linkoff)
ctxt.Out.Write(ldr.Data(s1))
@@ -1086,6 +1092,9 @@ func machoEmitReloc(ctxt *Link) {
for _, sect := range Segtext.Sections[1:] {
relocSect(ctxt, sect, ctxt.datap)
}
+ for _, sect := range Segrelrodata.Sections {
+ relocSect(ctxt, sect, ctxt.datap)
+ }
for _, sect := range Segdata.Sections {
relocSect(ctxt, sect, ctxt.datap)
}
diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go
index 102b6c54360..f68de8fff13 100644
--- a/src/cmd/link/internal/ld/target.go
+++ b/src/cmd/link/internal/ld/target.go
@@ -74,8 +74,12 @@ func (t *Target) IsDynlinkingGo() bool {
func (t *Target) UseRelro() bool {
switch t.BuildMode {
case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin:
- return t.IsELF || t.HeadType == objabi.Haix
+ return t.IsELF || t.HeadType == objabi.Haix || t.HeadType == objabi.Hdarwin
default:
+ if t.HeadType == objabi.Hdarwin && t.IsARM64() {
+ // On darwin/ARM64, everything is PIE.
+ return true
+ }
return t.linkShared || (t.HeadType == objabi.Haix && t.LinkMode == LinkExternal)
}
}
From 1ed4f12f4a6b9d783cf9a6fc3a292a433b8539c6 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Thu, 10 Sep 2020 11:14:27 -0400
Subject: [PATCH 0040/1082] cmd/link: add a test to test RODATA is indeed
read-only
Updates #38830.
Change-Id: Ie1f6ccef40a773f038aac587dfc26bf70a1a8536
Reviewed-on: https://go-review.googlesource.com/c/go/+/253921
Run-TryBot: Cherry Zhang
Reviewed-by: Than McIntosh
TryBot-Result: Gobot Gobot
---
src/cmd/link/link_test.go | 14 ++++++++++++++
src/cmd/link/testdata/testRO/x.go | 22 ++++++++++++++++++++++
2 files changed, 36 insertions(+)
create mode 100644 src/cmd/link/testdata/testRO/x.go
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index 98798be4653..4e60996d8e6 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -800,3 +800,17 @@ func TestContentAddressableSymbols(t *testing.T) {
t.Errorf("command %s failed: %v\n%s", cmd, err, out)
}
}
+
+func TestReadOnly(t *testing.T) {
+ // Test that read-only data is indeed read-only.
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ src := filepath.Join("testdata", "testRO", "x.go")
+ cmd := exec.Command(testenv.GoToolPath(t), "run", src)
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Errorf("running test program did not fail. output:\n%s", out)
+ }
+}
diff --git a/src/cmd/link/testdata/testRO/x.go b/src/cmd/link/testdata/testRO/x.go
new file mode 100644
index 00000000000..d77db6d563c
--- /dev/null
+++ b/src/cmd/link/testdata/testRO/x.go
@@ -0,0 +1,22 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that read-only data is indeed read-only. This
+// program attempts to modify read-only data, and it
+// should fail.
+
+package main
+
+import "unsafe"
+
+var s = "hello"
+
+func main() {
+ println(s)
+ *(*struct {
+ p *byte
+ l int
+ })(unsafe.Pointer(&s)).p = 'H'
+ println(s)
+}
From b459bc8152210c14b66e23351690ff774cd68d2c Mon Sep 17 00:00:00 2001
From: Jay Conrod
Date: Fri, 11 Sep 2020 09:31:30 -0400
Subject: [PATCH 0041/1082] cmd/go: make 'go mod download' update go.sum after
downloads are complete
'go mod download' calls WriteGoMod once via modload.ListModules when
it loads the build list. This saves sums for go.mod files needed by
MVS, but the write occurs before any zip files are downloaded.
With this change, 'go mod download' calls WriteGoMod again (and thus,
modfetch.WriteGoSum) after downloading and verifying module zip files,
so the sums of the zip files will be saved, too.
Fixes #41341
Change-Id: I7d56754aa255256ed45fd93cb154c2e6ea5f45a9
Reviewed-on: https://go-review.googlesource.com/c/go/+/254357
Run-TryBot: Jay Conrod
TryBot-Result: Gobot Gobot
Reviewed-by: Bryan C. Mills
---
src/cmd/go/internal/modcmd/download.go | 3 +++
src/cmd/go/testdata/script/mod_download.txt | 18 ++++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/src/cmd/go/internal/modcmd/download.go b/src/cmd/go/internal/modcmd/download.go
index 0ea5638e703..6227fd9f33d 100644
--- a/src/cmd/go/internal/modcmd/download.go
+++ b/src/cmd/go/internal/modcmd/download.go
@@ -187,4 +187,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
}
base.ExitIfErrors()
}
+
+ // Update go.mod and especially go.sum if needed.
+ modload.WriteGoMod()
}
diff --git a/src/cmd/go/testdata/script/mod_download.txt b/src/cmd/go/testdata/script/mod_download.txt
index b9bf67cad59..c53bbe45674 100644
--- a/src/cmd/go/testdata/script/mod_download.txt
+++ b/src/cmd/go/testdata/script/mod_download.txt
@@ -107,6 +107,14 @@ stderr '^go mod download: skipping argument m that resolves to the main module\n
go mod download m@latest
stderr '^go mod download: skipping argument m@latest that resolves to the main module\n'
+# download updates go.mod and populates go.sum
+cd update
+! exists go.sum
+go mod download
+grep '^rsc.io/sampler v1.3.0 ' go.sum
+go list -m rsc.io/sampler
+stdout '^rsc.io/sampler v1.3.0$'
+
# allow go mod download without go.mod
env GO111MODULE=auto
rm go.mod
@@ -122,3 +130,13 @@ stderr 'get '$GOPROXY
-- go.mod --
module m
+
+-- update/go.mod --
+module m
+
+go 1.16
+
+require (
+ rsc.io/quote v1.5.2
+ rsc.io/sampler v1.2.1 // older version than in build list
+)
From 86ee84c40e2770ff189b6a4d835849107d9c749a Mon Sep 17 00:00:00 2001
From: Jay Conrod
Date: Wed, 2 Sep 2020 13:25:11 -0400
Subject: [PATCH 0042/1082] cmd/go: move get.Insecure to cfg.Insecure to break
dependency cycle
Change-Id: If9c73ff5adc7e080a48ecc6b35ce40822193d66f
Reviewed-on: https://go-review.googlesource.com/c/go/+/254363
Run-TryBot: Jay Conrod
Reviewed-by: Bryan C. Mills
Reviewed-by: Michael Matloob
TryBot-Result: Gobot Gobot
---
src/cmd/go/internal/cfg/cfg.go | 2 ++
src/cmd/go/internal/get/get.go | 6 ++----
src/cmd/go/internal/modfetch/insecure.go | 3 +--
src/cmd/go/internal/modfetch/sumdb.go | 3 +--
src/cmd/go/internal/modget/get.go | 6 +++---
5 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index f874b880a6d..9bf1db73ef6 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -49,6 +49,8 @@ var (
ModCacheRW bool // -modcacherw flag
ModFile string // -modfile flag
+ Insecure bool // -insecure flag
+
CmdName string // "build", "install", "list", "mod tidy", etc.
DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go
index d0be3fe1e70..9e4825eb379 100644
--- a/src/cmd/go/internal/get/get.go
+++ b/src/cmd/go/internal/get/get.go
@@ -108,14 +108,12 @@ var (
getT = CmdGet.Flag.Bool("t", false, "")
getU = CmdGet.Flag.Bool("u", false, "")
getFix = CmdGet.Flag.Bool("fix", false, "")
-
- Insecure bool
)
func init() {
work.AddBuildFlags(CmdGet, work.OmitModFlag|work.OmitModCommonFlags)
CmdGet.Run = runGet // break init loop
- CmdGet.Flag.BoolVar(&Insecure, "insecure", Insecure, "")
+ CmdGet.Flag.BoolVar(&cfg.Insecure, "insecure", cfg.Insecure, "")
}
func runGet(ctx context.Context, cmd *base.Command, args []string) {
@@ -431,7 +429,7 @@ func downloadPackage(p *load.Package) error {
return fmt.Errorf("%s: invalid import path: %v", p.ImportPath, err)
}
security := web.SecureOnly
- if Insecure || module.MatchPrefixPatterns(cfg.GOINSECURE, importPrefix) {
+ if cfg.Insecure || module.MatchPrefixPatterns(cfg.GOINSECURE, importPrefix) {
security = web.Insecure
}
diff --git a/src/cmd/go/internal/modfetch/insecure.go b/src/cmd/go/internal/modfetch/insecure.go
index b692669cbab..012d05f29db 100644
--- a/src/cmd/go/internal/modfetch/insecure.go
+++ b/src/cmd/go/internal/modfetch/insecure.go
@@ -6,12 +6,11 @@ package modfetch
import (
"cmd/go/internal/cfg"
- "cmd/go/internal/get"
"golang.org/x/mod/module"
)
// allowInsecure reports whether we are allowed to fetch this path in an insecure manner.
func allowInsecure(path string) bool {
- return get.Insecure || module.MatchPrefixPatterns(cfg.GOINSECURE, path)
+ return cfg.Insecure || module.MatchPrefixPatterns(cfg.GOINSECURE, path)
}
diff --git a/src/cmd/go/internal/modfetch/sumdb.go b/src/cmd/go/internal/modfetch/sumdb.go
index 783c4a433b4..47a25715314 100644
--- a/src/cmd/go/internal/modfetch/sumdb.go
+++ b/src/cmd/go/internal/modfetch/sumdb.go
@@ -22,7 +22,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
- "cmd/go/internal/get"
"cmd/go/internal/lockedfile"
"cmd/go/internal/web"
@@ -33,7 +32,7 @@ import (
// useSumDB reports whether to use the Go checksum database for the given module.
func useSumDB(mod module.Version) bool {
- return cfg.GOSUMDB != "off" && !get.Insecure && !module.MatchPrefixPatterns(cfg.GONOSUMDB, mod.Path)
+ return cfg.GOSUMDB != "off" && !cfg.Insecure && !module.MatchPrefixPatterns(cfg.GONOSUMDB, mod.Path)
}
// lookupSumDB returns the Go checksum database's go.sum lines for the given module,
diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go
index a2a8287d846..829cfe055ad 100644
--- a/src/cmd/go/internal/modget/get.go
+++ b/src/cmd/go/internal/modget/get.go
@@ -17,7 +17,7 @@ import (
"sync"
"cmd/go/internal/base"
- "cmd/go/internal/get"
+ "cmd/go/internal/cfg"
"cmd/go/internal/imports"
"cmd/go/internal/load"
"cmd/go/internal/modload"
@@ -181,7 +181,7 @@ var (
getM = CmdGet.Flag.Bool("m", false, "")
getT = CmdGet.Flag.Bool("t", false, "")
getU upgradeFlag
- // -insecure is get.Insecure
+ // -insecure is cfg.Insecure
// -v is cfg.BuildV
)
@@ -206,7 +206,7 @@ func (v *upgradeFlag) String() string { return "" }
func init() {
work.AddBuildFlags(CmdGet, work.OmitModFlag)
CmdGet.Run = runGet // break init loop
- CmdGet.Flag.BoolVar(&get.Insecure, "insecure", get.Insecure, "")
+ CmdGet.Flag.BoolVar(&cfg.Insecure, "insecure", cfg.Insecure, "")
CmdGet.Flag.Var(&getU, "u", "")
}
From 07c1788357cfe6a4ee5f6f6a54d4fe9f579fa844 Mon Sep 17 00:00:00 2001
From: Jay Conrod
Date: Wed, 2 Sep 2020 14:53:02 -0400
Subject: [PATCH 0043/1082] cmd/go: move repository resolution from
internal/get to internal/vcs
This is a refactoring intended to break the dependency from
internal/modfetch to internal/get. No change in functionality is intended.
Change-Id: If51aba7139cc0b62ecc9ba454c055c99e8f36f0f
Reviewed-on: https://go-review.googlesource.com/c/go/+/254364
Run-TryBot: Jay Conrod
Reviewed-by: Bryan C. Mills
Reviewed-by: Michael Matloob
TryBot-Result: Gobot Gobot
---
src/cmd/go/internal/get/get.go | 35 +--
src/cmd/go/internal/modfetch/repo.go | 12 +-
src/cmd/go/internal/str/str_test.go | 27 ++
src/cmd/go/internal/{get => vcs}/discovery.go | 2 +-
.../pkg_test.go => vcs/discovery_test.go} | 23 +-
src/cmd/go/internal/{get => vcs}/vcs.go | 277 +++++++++---------
src/cmd/go/internal/{get => vcs}/vcs_test.go | 46 +--
7 files changed, 217 insertions(+), 205 deletions(-)
create mode 100644 src/cmd/go/internal/str/str_test.go
rename src/cmd/go/internal/{get => vcs}/discovery.go (99%)
rename src/cmd/go/internal/{get/pkg_test.go => vcs/discovery_test.go} (85%)
rename src/cmd/go/internal/{get => vcs}/vcs.go (83%)
rename src/cmd/go/internal/{get => vcs}/vcs_test.go (94%)
diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go
index 9e4825eb379..3f7a66384ad 100644
--- a/src/cmd/go/internal/get/get.go
+++ b/src/cmd/go/internal/get/get.go
@@ -18,6 +18,7 @@ import (
"cmd/go/internal/load"
"cmd/go/internal/search"
"cmd/go/internal/str"
+ "cmd/go/internal/vcs"
"cmd/go/internal/web"
"cmd/go/internal/work"
@@ -406,7 +407,7 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
// to make the first copy of or update a copy of the given package.
func downloadPackage(p *load.Package) error {
var (
- vcs *vcsCmd
+ vcsCmd *vcs.Cmd
repo, rootPath string
err error
blindRepo bool // set if the repo has unusual configuration
@@ -435,16 +436,16 @@ func downloadPackage(p *load.Package) error {
if p.Internal.Build.SrcRoot != "" {
// Directory exists. Look for checkout along path to src.
- vcs, rootPath, err = vcsFromDir(p.Dir, p.Internal.Build.SrcRoot)
+ vcsCmd, rootPath, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot)
if err != nil {
return err
}
repo = "" // should be unused; make distinctive
// Double-check where it came from.
- if *getU && vcs.remoteRepo != nil {
+ if *getU && vcsCmd.RemoteRepo != nil {
dir := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
- remote, err := vcs.remoteRepo(vcs, dir)
+ remote, err := vcsCmd.RemoteRepo(vcsCmd, dir)
if err != nil {
// Proceed anyway. The package is present; we likely just don't understand
// the repo configuration (e.g. unusual remote protocol).
@@ -452,10 +453,10 @@ func downloadPackage(p *load.Package) error {
}
repo = remote
if !*getF && err == nil {
- if rr, err := RepoRootForImportPath(importPrefix, IgnoreMod, security); err == nil {
+ if rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security); err == nil {
repo := rr.Repo
- if rr.vcs.resolveRepo != nil {
- resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo)
+ if rr.VCS.ResolveRepo != nil {
+ resolved, err := rr.VCS.ResolveRepo(rr.VCS, dir, repo)
if err == nil {
repo = resolved
}
@@ -469,13 +470,13 @@ func downloadPackage(p *load.Package) error {
} else {
// Analyze the import path to determine the version control system,
// repository, and the import path for the root of the repository.
- rr, err := RepoRootForImportPath(importPrefix, IgnoreMod, security)
+ rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security)
if err != nil {
return err
}
- vcs, repo, rootPath = rr.vcs, rr.Repo, rr.Root
+ vcsCmd, repo, rootPath = rr.VCS, rr.Repo, rr.Root
}
- if !blindRepo && !vcs.isSecure(repo) && security != web.Insecure {
+ if !blindRepo && !vcsCmd.IsSecure(repo) && security != web.Insecure {
return fmt.Errorf("cannot download, %v uses insecure protocol", repo)
}
@@ -498,7 +499,7 @@ func downloadPackage(p *load.Package) error {
}
root := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
- if err := checkNestedVCS(vcs, root, p.Internal.Build.SrcRoot); err != nil {
+ if err := vcs.CheckNested(vcsCmd, root, p.Internal.Build.SrcRoot); err != nil {
return err
}
@@ -514,7 +515,7 @@ func downloadPackage(p *load.Package) error {
// Check that this is an appropriate place for the repo to be checked out.
// The target directory must either not exist or have a repo checked out already.
- meta := filepath.Join(root, "."+vcs.cmd)
+ meta := filepath.Join(root, "."+vcsCmd.Cmd)
if _, err := os.Stat(meta); err != nil {
// Metadata file or directory does not exist. Prepare to checkout new copy.
// Some version control tools require the target directory not to exist.
@@ -535,12 +536,12 @@ func downloadPackage(p *load.Package) error {
fmt.Fprintf(os.Stderr, "created GOPATH=%s; see 'go help gopath'\n", p.Internal.Build.Root)
}
- if err = vcs.create(root, repo); err != nil {
+ if err = vcsCmd.Create(root, repo); err != nil {
return err
}
} else {
// Metadata directory does exist; download incremental updates.
- if err = vcs.download(root); err != nil {
+ if err = vcsCmd.Download(root); err != nil {
return err
}
}
@@ -549,12 +550,12 @@ func downloadPackage(p *load.Package) error {
// Do not show tag sync in -n; it's noise more than anything,
// and since we're not running commands, no tag will be found.
// But avoid printing nothing.
- fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcs.cmd)
+ fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcsCmd.Cmd)
return nil
}
// Select and sync to appropriate version of the repository.
- tags, err := vcs.tags(root)
+ tags, err := vcsCmd.Tags(root)
if err != nil {
return err
}
@@ -562,7 +563,7 @@ func downloadPackage(p *load.Package) error {
if i := strings.Index(vers, " "); i >= 0 {
vers = vers[:i]
}
- if err := vcs.tagSync(root, selectTag(vers, tags)); err != nil {
+ if err := vcsCmd.TagSync(root, selectTag(vers, tags)); err != nil {
return err
}
diff --git a/src/cmd/go/internal/modfetch/repo.go b/src/cmd/go/internal/modfetch/repo.go
index 34f805d58ae..eed4dd4258b 100644
--- a/src/cmd/go/internal/modfetch/repo.go
+++ b/src/cmd/go/internal/modfetch/repo.go
@@ -13,9 +13,9 @@ import (
"time"
"cmd/go/internal/cfg"
- "cmd/go/internal/get"
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/par"
+ "cmd/go/internal/vcs"
web "cmd/go/internal/web"
"golang.org/x/mod/module"
@@ -261,13 +261,13 @@ func lookupDirect(path string) (Repo, error) {
if allowInsecure(path) {
security = web.Insecure
}
- rr, err := get.RepoRootForImportPath(path, get.PreferMod, security)
+ rr, err := vcs.RepoRootForImportPath(path, vcs.PreferMod, security)
if err != nil {
// We don't know where to find code for a module with this path.
return nil, notExistError{err: err}
}
- if rr.VCS == "mod" {
+ if rr.VCS.Name == "mod" {
// Fetch module from proxy with base URL rr.Repo.
return newProxyRepo(rr.Repo, path)
}
@@ -279,8 +279,8 @@ func lookupDirect(path string) (Repo, error) {
return newCodeRepo(code, rr.Root, path)
}
-func lookupCodeRepo(rr *get.RepoRoot) (codehost.Repo, error) {
- code, err := codehost.NewRepo(rr.VCS, rr.Repo)
+func lookupCodeRepo(rr *vcs.RepoRoot) (codehost.Repo, error) {
+ code, err := codehost.NewRepo(rr.VCS.Cmd, rr.Repo)
if err != nil {
if _, ok := err.(*codehost.VCSError); ok {
return nil, err
@@ -306,7 +306,7 @@ func ImportRepoRev(path, rev string) (Repo, *RevInfo, error) {
if allowInsecure(path) {
security = web.Insecure
}
- rr, err := get.RepoRootForImportPath(path, get.IgnoreMod, security)
+ rr, err := vcs.RepoRootForImportPath(path, vcs.IgnoreMod, security)
if err != nil {
return nil, nil, err
}
diff --git a/src/cmd/go/internal/str/str_test.go b/src/cmd/go/internal/str/str_test.go
new file mode 100644
index 00000000000..147ce1a63ef
--- /dev/null
+++ b/src/cmd/go/internal/str/str_test.go
@@ -0,0 +1,27 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package str
+
+import "testing"
+
+var foldDupTests = []struct {
+ list []string
+ f1, f2 string
+}{
+ {StringList("math/rand", "math/big"), "", ""},
+ {StringList("math", "strings"), "", ""},
+ {StringList("strings"), "", ""},
+ {StringList("strings", "strings"), "strings", "strings"},
+ {StringList("Rand", "rand", "math", "math/rand", "math/Rand"), "Rand", "rand"},
+}
+
+func TestFoldDup(t *testing.T) {
+ for _, tt := range foldDupTests {
+ f1, f2 := FoldDup(tt.list)
+ if f1 != tt.f1 || f2 != tt.f2 {
+ t.Errorf("foldDup(%q) = %q, %q, want %q, %q", tt.list, f1, f2, tt.f1, tt.f2)
+ }
+ }
+}
diff --git a/src/cmd/go/internal/get/discovery.go b/src/cmd/go/internal/vcs/discovery.go
similarity index 99%
rename from src/cmd/go/internal/get/discovery.go
rename to src/cmd/go/internal/vcs/discovery.go
index afa6ef455f9..327b44cb9af 100644
--- a/src/cmd/go/internal/get/discovery.go
+++ b/src/cmd/go/internal/vcs/discovery.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package get
+package vcs
import (
"encoding/xml"
diff --git a/src/cmd/go/internal/get/pkg_test.go b/src/cmd/go/internal/vcs/discovery_test.go
similarity index 85%
rename from src/cmd/go/internal/get/pkg_test.go
rename to src/cmd/go/internal/vcs/discovery_test.go
index fc6a179c2e1..eb99fdf64c1 100644
--- a/src/cmd/go/internal/get/pkg_test.go
+++ b/src/cmd/go/internal/vcs/discovery_test.go
@@ -2,35 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package get
+package vcs
import (
- "cmd/go/internal/str"
"reflect"
"strings"
"testing"
)
-var foldDupTests = []struct {
- list []string
- f1, f2 string
-}{
- {str.StringList("math/rand", "math/big"), "", ""},
- {str.StringList("math", "strings"), "", ""},
- {str.StringList("strings"), "", ""},
- {str.StringList("strings", "strings"), "strings", "strings"},
- {str.StringList("Rand", "rand", "math", "math/rand", "math/Rand"), "Rand", "rand"},
-}
-
-func TestFoldDup(t *testing.T) {
- for _, tt := range foldDupTests {
- f1, f2 := str.FoldDup(tt.list)
- if f1 != tt.f1 || f2 != tt.f2 {
- t.Errorf("foldDup(%q) = %q, %q, want %q, %q", tt.list, f1, f2, tt.f1, tt.f2)
- }
- }
-}
-
var parseMetaGoImportsTests = []struct {
in string
mod ModuleMode
diff --git a/src/cmd/go/internal/get/vcs.go b/src/cmd/go/internal/vcs/vcs.go
similarity index 83%
rename from src/cmd/go/internal/get/vcs.go
rename to src/cmd/go/internal/vcs/vcs.go
index 24c32935d0b..e535998d892 100644
--- a/src/cmd/go/internal/get/vcs.go
+++ b/src/cmd/go/internal/vcs/vcs.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package get
+package vcs
import (
"encoding/json"
@@ -27,23 +27,23 @@ import (
// A vcsCmd describes how to use a version control system
// like Mercurial, Git, or Subversion.
-type vcsCmd struct {
- name string
- cmd string // name of binary to invoke command
+type Cmd struct {
+ Name string
+ Cmd string // name of binary to invoke command
- createCmd []string // commands to download a fresh copy of a repository
- downloadCmd []string // commands to download updates into an existing repository
+ CreateCmd []string // commands to download a fresh copy of a repository
+ DownloadCmd []string // commands to download updates into an existing repository
- tagCmd []tagCmd // commands to list tags
- tagLookupCmd []tagCmd // commands to lookup tags before running tagSyncCmd
- tagSyncCmd []string // commands to sync to specific tag
- tagSyncDefault []string // commands to sync to default tag
+ TagCmd []tagCmd // commands to list tags
+ TagLookupCmd []tagCmd // commands to lookup tags before running tagSyncCmd
+ TagSyncCmd []string // commands to sync to specific tag
+ TagSyncDefault []string // commands to sync to default tag
- scheme []string
- pingCmd string
+ Scheme []string
+ PingCmd string
- remoteRepo func(v *vcsCmd, rootDir string) (remoteRepo string, err error)
- resolveRepo func(v *vcsCmd, rootDir, remoteRepo string) (realRepo string, err error)
+ RemoteRepo func(v *Cmd, rootDir string) (remoteRepo string, err error)
+ ResolveRepo func(v *Cmd, rootDir, remoteRepo string) (realRepo string, err error)
}
var defaultSecureScheme = map[string]bool{
@@ -54,7 +54,7 @@ var defaultSecureScheme = map[string]bool{
"ssh": true,
}
-func (v *vcsCmd) isSecure(repo string) bool {
+func (v *Cmd) IsSecure(repo string) bool {
u, err := urlpkg.Parse(repo)
if err != nil {
// If repo is not a URL, it's not secure.
@@ -63,8 +63,8 @@ func (v *vcsCmd) isSecure(repo string) bool {
return v.isSecureScheme(u.Scheme)
}
-func (v *vcsCmd) isSecureScheme(scheme string) bool {
- switch v.cmd {
+func (v *Cmd) isSecureScheme(scheme string) bool {
+ switch v.Cmd {
case "git":
// GIT_ALLOW_PROTOCOL is an environment variable defined by Git. It is a
// colon-separated list of schemes that are allowed to be used with git
@@ -89,7 +89,7 @@ type tagCmd struct {
}
// vcsList lists the known version control systems
-var vcsList = []*vcsCmd{
+var vcsList = []*Cmd{
vcsHg,
vcsGit,
vcsSvn,
@@ -97,11 +97,15 @@ var vcsList = []*vcsCmd{
vcsFossil,
}
+// vcsMod is a stub for the "mod" scheme. It's returned by
+// repoRootForImportPathDynamic, but is otherwise not treated as a VCS command.
+var vcsMod = &Cmd{Name: "mod"}
+
// vcsByCmd returns the version control system for the given
// command name (hg, git, svn, bzr).
-func vcsByCmd(cmd string) *vcsCmd {
+func vcsByCmd(cmd string) *Cmd {
for _, vcs := range vcsList {
- if vcs.cmd == cmd {
+ if vcs.Cmd == cmd {
return vcs
}
}
@@ -109,31 +113,31 @@ func vcsByCmd(cmd string) *vcsCmd {
}
// vcsHg describes how to use Mercurial.
-var vcsHg = &vcsCmd{
- name: "Mercurial",
- cmd: "hg",
+var vcsHg = &Cmd{
+ Name: "Mercurial",
+ Cmd: "hg",
- createCmd: []string{"clone -U -- {repo} {dir}"},
- downloadCmd: []string{"pull"},
+ CreateCmd: []string{"clone -U -- {repo} {dir}"},
+ DownloadCmd: []string{"pull"},
// We allow both tag and branch names as 'tags'
// for selecting a version. This lets people have
// a go.release.r60 branch and a go1 branch
// and make changes in both, without constantly
// editing .hgtags.
- tagCmd: []tagCmd{
+ TagCmd: []tagCmd{
{"tags", `^(\S+)`},
{"branches", `^(\S+)`},
},
- tagSyncCmd: []string{"update -r {tag}"},
- tagSyncDefault: []string{"update default"},
+ TagSyncCmd: []string{"update -r {tag}"},
+ TagSyncDefault: []string{"update default"},
- scheme: []string{"https", "http", "ssh"},
- pingCmd: "identify -- {scheme}://{repo}",
- remoteRepo: hgRemoteRepo,
+ Scheme: []string{"https", "http", "ssh"},
+ PingCmd: "identify -- {scheme}://{repo}",
+ RemoteRepo: hgRemoteRepo,
}
-func hgRemoteRepo(vcsHg *vcsCmd, rootDir string) (remoteRepo string, err error) {
+func hgRemoteRepo(vcsHg *Cmd, rootDir string) (remoteRepo string, err error) {
out, err := vcsHg.runOutput(rootDir, "paths default")
if err != nil {
return "", err
@@ -142,45 +146,45 @@ func hgRemoteRepo(vcsHg *vcsCmd, rootDir string) (remoteRepo string, err error)
}
// vcsGit describes how to use Git.
-var vcsGit = &vcsCmd{
- name: "Git",
- cmd: "git",
+var vcsGit = &Cmd{
+ Name: "Git",
+ Cmd: "git",
- createCmd: []string{"clone -- {repo} {dir}", "-go-internal-cd {dir} submodule update --init --recursive"},
- downloadCmd: []string{"pull --ff-only", "submodule update --init --recursive"},
+ CreateCmd: []string{"clone -- {repo} {dir}", "-go-internal-cd {dir} submodule update --init --recursive"},
+ DownloadCmd: []string{"pull --ff-only", "submodule update --init --recursive"},
- tagCmd: []tagCmd{
+ TagCmd: []tagCmd{
// tags/xxx matches a git tag named xxx
// origin/xxx matches a git branch named xxx on the default remote repository
{"show-ref", `(?:tags|origin)/(\S+)$`},
},
- tagLookupCmd: []tagCmd{
+ TagLookupCmd: []tagCmd{
{"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`},
},
- tagSyncCmd: []string{"checkout {tag}", "submodule update --init --recursive"},
+ TagSyncCmd: []string{"checkout {tag}", "submodule update --init --recursive"},
// both createCmd and downloadCmd update the working dir.
// No need to do more here. We used to 'checkout master'
// but that doesn't work if the default branch is not named master.
// DO NOT add 'checkout master' here.
// See golang.org/issue/9032.
- tagSyncDefault: []string{"submodule update --init --recursive"},
+ TagSyncDefault: []string{"submodule update --init --recursive"},
- scheme: []string{"git", "https", "http", "git+ssh", "ssh"},
+ Scheme: []string{"git", "https", "http", "git+ssh", "ssh"},
// Leave out the '--' separator in the ls-remote command: git 2.7.4 does not
// support such a separator for that command, and this use should be safe
// without it because the {scheme} value comes from the predefined list above.
// See golang.org/issue/33836.
- pingCmd: "ls-remote {scheme}://{repo}",
+ PingCmd: "ls-remote {scheme}://{repo}",
- remoteRepo: gitRemoteRepo,
+ RemoteRepo: gitRemoteRepo,
}
// scpSyntaxRe matches the SCP-like addresses used by Git to access
// repositories by SSH.
var scpSyntaxRe = lazyregexp.New(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`)
-func gitRemoteRepo(vcsGit *vcsCmd, rootDir string) (remoteRepo string, err error) {
+func gitRemoteRepo(vcsGit *Cmd, rootDir string) (remoteRepo string, err error) {
cmd := "config remote.origin.url"
errParse := errors.New("unable to parse output of git " + cmd)
errRemoteOriginNotFound := errors.New("remote origin not found")
@@ -216,7 +220,7 @@ func gitRemoteRepo(vcsGit *vcsCmd, rootDir string) (remoteRepo string, err error
// Iterate over insecure schemes too, because this function simply
// reports the state of the repo. If we can't see insecure schemes then
// we can't report the actual repo URL.
- for _, s := range vcsGit.scheme {
+ for _, s := range vcsGit.Scheme {
if repoURL.Scheme == s {
return repoURL.String(), nil
}
@@ -225,27 +229,27 @@ func gitRemoteRepo(vcsGit *vcsCmd, rootDir string) (remoteRepo string, err error
}
// vcsBzr describes how to use Bazaar.
-var vcsBzr = &vcsCmd{
- name: "Bazaar",
- cmd: "bzr",
+var vcsBzr = &Cmd{
+ Name: "Bazaar",
+ Cmd: "bzr",
- createCmd: []string{"branch -- {repo} {dir}"},
+ CreateCmd: []string{"branch -- {repo} {dir}"},
// Without --overwrite bzr will not pull tags that changed.
// Replace by --overwrite-tags after http://pad.lv/681792 goes in.
- downloadCmd: []string{"pull --overwrite"},
+ DownloadCmd: []string{"pull --overwrite"},
- tagCmd: []tagCmd{{"tags", `^(\S+)`}},
- tagSyncCmd: []string{"update -r {tag}"},
- tagSyncDefault: []string{"update -r revno:-1"},
+ TagCmd: []tagCmd{{"tags", `^(\S+)`}},
+ TagSyncCmd: []string{"update -r {tag}"},
+ TagSyncDefault: []string{"update -r revno:-1"},
- scheme: []string{"https", "http", "bzr", "bzr+ssh"},
- pingCmd: "info -- {scheme}://{repo}",
- remoteRepo: bzrRemoteRepo,
- resolveRepo: bzrResolveRepo,
+ Scheme: []string{"https", "http", "bzr", "bzr+ssh"},
+ PingCmd: "info -- {scheme}://{repo}",
+ RemoteRepo: bzrRemoteRepo,
+ ResolveRepo: bzrResolveRepo,
}
-func bzrRemoteRepo(vcsBzr *vcsCmd, rootDir string) (remoteRepo string, err error) {
+func bzrRemoteRepo(vcsBzr *Cmd, rootDir string) (remoteRepo string, err error) {
outb, err := vcsBzr.runOutput(rootDir, "config parent_location")
if err != nil {
return "", err
@@ -253,7 +257,7 @@ func bzrRemoteRepo(vcsBzr *vcsCmd, rootDir string) (remoteRepo string, err error
return strings.TrimSpace(string(outb)), nil
}
-func bzrResolveRepo(vcsBzr *vcsCmd, rootDir, remoteRepo string) (realRepo string, err error) {
+func bzrResolveRepo(vcsBzr *Cmd, rootDir, remoteRepo string) (realRepo string, err error) {
outb, err := vcsBzr.runOutput(rootDir, "info "+remoteRepo)
if err != nil {
return "", err
@@ -287,22 +291,22 @@ func bzrResolveRepo(vcsBzr *vcsCmd, rootDir, remoteRepo string) (realRepo string
}
// vcsSvn describes how to use Subversion.
-var vcsSvn = &vcsCmd{
- name: "Subversion",
- cmd: "svn",
+var vcsSvn = &Cmd{
+ Name: "Subversion",
+ Cmd: "svn",
- createCmd: []string{"checkout -- {repo} {dir}"},
- downloadCmd: []string{"update"},
+ CreateCmd: []string{"checkout -- {repo} {dir}"},
+ DownloadCmd: []string{"update"},
// There is no tag command in subversion.
// The branch information is all in the path names.
- scheme: []string{"https", "http", "svn", "svn+ssh"},
- pingCmd: "info -- {scheme}://{repo}",
- remoteRepo: svnRemoteRepo,
+ Scheme: []string{"https", "http", "svn", "svn+ssh"},
+ PingCmd: "info -- {scheme}://{repo}",
+ RemoteRepo: svnRemoteRepo,
}
-func svnRemoteRepo(vcsSvn *vcsCmd, rootDir string) (remoteRepo string, err error) {
+func svnRemoteRepo(vcsSvn *Cmd, rootDir string) (remoteRepo string, err error) {
outb, err := vcsSvn.runOutput(rootDir, "info")
if err != nil {
return "", err
@@ -337,22 +341,22 @@ func svnRemoteRepo(vcsSvn *vcsCmd, rootDir string) (remoteRepo string, err error
const fossilRepoName = ".fossil"
// vcsFossil describes how to use Fossil (fossil-scm.org)
-var vcsFossil = &vcsCmd{
- name: "Fossil",
- cmd: "fossil",
+var vcsFossil = &Cmd{
+ Name: "Fossil",
+ Cmd: "fossil",
- createCmd: []string{"-go-internal-mkdir {dir} clone -- {repo} " + filepath.Join("{dir}", fossilRepoName), "-go-internal-cd {dir} open .fossil"},
- downloadCmd: []string{"up"},
+ CreateCmd: []string{"-go-internal-mkdir {dir} clone -- {repo} " + filepath.Join("{dir}", fossilRepoName), "-go-internal-cd {dir} open .fossil"},
+ DownloadCmd: []string{"up"},
- tagCmd: []tagCmd{{"tag ls", `(.*)`}},
- tagSyncCmd: []string{"up tag:{tag}"},
- tagSyncDefault: []string{"up trunk"},
+ TagCmd: []tagCmd{{"tag ls", `(.*)`}},
+ TagSyncCmd: []string{"up tag:{tag}"},
+ TagSyncDefault: []string{"up trunk"},
- scheme: []string{"https", "http"},
- remoteRepo: fossilRemoteRepo,
+ Scheme: []string{"https", "http"},
+ RemoteRepo: fossilRemoteRepo,
}
-func fossilRemoteRepo(vcsFossil *vcsCmd, rootDir string) (remoteRepo string, err error) {
+func fossilRemoteRepo(vcsFossil *Cmd, rootDir string) (remoteRepo string, err error) {
out, err := vcsFossil.runOutput(rootDir, "remote-url")
if err != nil {
return "", err
@@ -360,8 +364,8 @@ func fossilRemoteRepo(vcsFossil *vcsCmd, rootDir string) (remoteRepo string, err
return strings.TrimSpace(string(out)), nil
}
-func (v *vcsCmd) String() string {
- return v.name
+func (v *Cmd) String() string {
+ return v.Name
}
// run runs the command line cmd in the given directory.
@@ -371,24 +375,24 @@ func (v *vcsCmd) String() string {
// If an error occurs, run prints the command line and the
// command's combined stdout+stderr to standard error.
// Otherwise run discards the command's output.
-func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
+func (v *Cmd) run(dir string, cmd string, keyval ...string) error {
_, err := v.run1(dir, cmd, keyval, true)
return err
}
// runVerboseOnly is like run but only generates error output to standard error in verbose mode.
-func (v *vcsCmd) runVerboseOnly(dir string, cmd string, keyval ...string) error {
+func (v *Cmd) runVerboseOnly(dir string, cmd string, keyval ...string) error {
_, err := v.run1(dir, cmd, keyval, false)
return err
}
// runOutput is like run but returns the output of the command.
-func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
+func (v *Cmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
return v.run1(dir, cmd, keyval, true)
}
// run1 is the generalized implementation of run and runOutput.
-func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) {
+func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) {
m := make(map[string]string)
for i := 0; i < len(keyval); i += 2 {
m[keyval[i]] = keyval[i+1]
@@ -420,25 +424,25 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool)
args = args[2:]
}
- _, err := exec.LookPath(v.cmd)
+ _, err := exec.LookPath(v.Cmd)
if err != nil {
fmt.Fprintf(os.Stderr,
"go: missing %s command. See https://golang.org/s/gogetcmd\n",
- v.name)
+ v.Name)
return nil, err
}
- cmd := exec.Command(v.cmd, args...)
+ cmd := exec.Command(v.Cmd, args...)
cmd.Dir = dir
cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir)
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "cd %s\n", dir)
- fmt.Fprintf(os.Stderr, "%s %s\n", v.cmd, strings.Join(args, " "))
+ fmt.Fprintf(os.Stderr, "%s %s\n", v.Cmd, strings.Join(args, " "))
}
out, err := cmd.Output()
if err != nil {
if verbose || cfg.BuildV {
- fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
+ fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.Cmd, strings.Join(args, " "))
if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
os.Stderr.Write(ee.Stderr)
} else {
@@ -449,15 +453,15 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool)
return out, err
}
-// ping pings to determine scheme to use.
-func (v *vcsCmd) ping(scheme, repo string) error {
- return v.runVerboseOnly(".", v.pingCmd, "scheme", scheme, "repo", repo)
+// Ping pings to determine scheme to use.
+func (v *Cmd) Ping(scheme, repo string) error {
+ return v.runVerboseOnly(".", v.PingCmd, "scheme", scheme, "repo", repo)
}
-// create creates a new copy of repo in dir.
+// Create creates a new copy of repo in dir.
// The parent of dir must exist; dir must not.
-func (v *vcsCmd) create(dir, repo string) error {
- for _, cmd := range v.createCmd {
+func (v *Cmd) Create(dir, repo string) error {
+ for _, cmd := range v.CreateCmd {
if err := v.run(".", cmd, "dir", dir, "repo", repo); err != nil {
return err
}
@@ -465,9 +469,9 @@ func (v *vcsCmd) create(dir, repo string) error {
return nil
}
-// download downloads any new changes for the repo in dir.
-func (v *vcsCmd) download(dir string) error {
- for _, cmd := range v.downloadCmd {
+// Download downloads any new changes for the repo in dir.
+func (v *Cmd) Download(dir string) error {
+ for _, cmd := range v.DownloadCmd {
if err := v.run(dir, cmd); err != nil {
return err
}
@@ -475,10 +479,10 @@ func (v *vcsCmd) download(dir string) error {
return nil
}
-// tags returns the list of available tags for the repo in dir.
-func (v *vcsCmd) tags(dir string) ([]string, error) {
+// Tags returns the list of available tags for the repo in dir.
+func (v *Cmd) Tags(dir string) ([]string, error) {
var tags []string
- for _, tc := range v.tagCmd {
+ for _, tc := range v.TagCmd {
out, err := v.runOutput(dir, tc.cmd)
if err != nil {
return nil, err
@@ -493,12 +497,12 @@ func (v *vcsCmd) tags(dir string) ([]string, error) {
// tagSync syncs the repo in dir to the named tag,
// which either is a tag returned by tags or is v.tagDefault.
-func (v *vcsCmd) tagSync(dir, tag string) error {
- if v.tagSyncCmd == nil {
+func (v *Cmd) TagSync(dir, tag string) error {
+ if v.TagSyncCmd == nil {
return nil
}
if tag != "" {
- for _, tc := range v.tagLookupCmd {
+ for _, tc := range v.TagLookupCmd {
out, err := v.runOutput(dir, tc.cmd, "tag", tag)
if err != nil {
return err
@@ -512,8 +516,8 @@ func (v *vcsCmd) tagSync(dir, tag string) error {
}
}
- if tag == "" && v.tagSyncDefault != nil {
- for _, cmd := range v.tagSyncDefault {
+ if tag == "" && v.TagSyncDefault != nil {
+ for _, cmd := range v.TagSyncDefault {
if err := v.run(dir, cmd); err != nil {
return err
}
@@ -521,7 +525,7 @@ func (v *vcsCmd) tagSync(dir, tag string) error {
return nil
}
- for _, cmd := range v.tagSyncCmd {
+ for _, cmd := range v.TagSyncCmd {
if err := v.run(dir, cmd, "tag", tag); err != nil {
return err
}
@@ -540,11 +544,11 @@ type vcsPath struct {
schemelessRepo bool // if true, the repo pattern lacks a scheme
}
-// vcsFromDir inspects dir and its parents to determine the
+// FromDir inspects dir and its parents to determine the
// version control system and code repository to use.
// On return, root is the import path
// corresponding to the root of the repository.
-func vcsFromDir(dir, srcRoot string) (vcs *vcsCmd, root string, err error) {
+func FromDir(dir, srcRoot string) (vcs *Cmd, root string, err error) {
// Clean and double-check that dir is in (a subdirectory of) srcRoot.
dir = filepath.Clean(dir)
srcRoot = filepath.Clean(srcRoot)
@@ -552,13 +556,13 @@ func vcsFromDir(dir, srcRoot string) (vcs *vcsCmd, root string, err error) {
return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot)
}
- var vcsRet *vcsCmd
+ var vcsRet *Cmd
var rootRet string
origDir := dir
for len(dir) > len(srcRoot) {
for _, vcs := range vcsList {
- if _, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil {
+ if _, err := os.Stat(filepath.Join(dir, "."+vcs.Cmd)); err == nil {
root := filepath.ToSlash(dir[len(srcRoot)+1:])
// Record first VCS we find, but keep looking,
// to detect mistakes like one kind of VCS inside another.
@@ -568,12 +572,12 @@ func vcsFromDir(dir, srcRoot string) (vcs *vcsCmd, root string, err error) {
continue
}
// Allow .git inside .git, which can arise due to submodules.
- if vcsRet == vcs && vcs.cmd == "git" {
+ if vcsRet == vcs && vcs.Cmd == "git" {
continue
}
// Otherwise, we have one VCS inside a different VCS.
return nil, "", fmt.Errorf("directory %q uses %s, but parent %q uses %s",
- filepath.Join(srcRoot, rootRet), vcsRet.cmd, filepath.Join(srcRoot, root), vcs.cmd)
+ filepath.Join(srcRoot, rootRet), vcsRet.Cmd, filepath.Join(srcRoot, root), vcs.Cmd)
}
}
@@ -593,9 +597,9 @@ func vcsFromDir(dir, srcRoot string) (vcs *vcsCmd, root string, err error) {
return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir)
}
-// checkNestedVCS checks for an incorrectly-nested VCS-inside-VCS
+// CheckNested checks for an incorrectly-nested VCS-inside-VCS
// situation for dir, checking parents up until srcRoot.
-func checkNestedVCS(vcs *vcsCmd, dir, srcRoot string) error {
+func CheckNested(vcs *Cmd, dir, srcRoot string) error {
if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator {
return fmt.Errorf("directory %q is outside source root %q", dir, srcRoot)
}
@@ -603,17 +607,17 @@ func checkNestedVCS(vcs *vcsCmd, dir, srcRoot string) error {
otherDir := dir
for len(otherDir) > len(srcRoot) {
for _, otherVCS := range vcsList {
- if _, err := os.Stat(filepath.Join(otherDir, "."+otherVCS.cmd)); err == nil {
+ if _, err := os.Stat(filepath.Join(otherDir, "."+otherVCS.Cmd)); err == nil {
// Allow expected vcs in original dir.
if otherDir == dir && otherVCS == vcs {
continue
}
// Allow .git inside .git, which can arise due to submodules.
- if otherVCS == vcs && vcs.cmd == "git" {
+ if otherVCS == vcs && vcs.Cmd == "git" {
continue
}
// Otherwise, we have one VCS inside a different VCS.
- return fmt.Errorf("directory %q uses %s, but parent %q uses %s", dir, vcs.cmd, otherDir, otherVCS.cmd)
+ return fmt.Errorf("directory %q uses %s, but parent %q uses %s", dir, vcs.Cmd, otherDir, otherVCS.Cmd)
}
}
// Move to parent.
@@ -633,9 +637,7 @@ type RepoRoot struct {
Repo string // repository URL, including scheme
Root string // import path corresponding to root of repo
IsCustom bool // defined by served tags (as opposed to hard-coded pattern)
- VCS string // vcs type ("mod", "git", ...)
-
- vcs *vcsCmd // internal: vcs command access
+ VCS *Cmd
}
func httpPrefix(s string) string {
@@ -735,15 +737,15 @@ func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths
if !srv.schemelessRepo {
repoURL = match["repo"]
} else {
- scheme := vcs.scheme[0] // default to first scheme
+ scheme := vcs.Scheme[0] // default to first scheme
repo := match["repo"]
- if vcs.pingCmd != "" {
+ if vcs.PingCmd != "" {
// If we know how to test schemes, scan to find one.
- for _, s := range vcs.scheme {
+ for _, s := range vcs.Scheme {
if security == web.SecureOnly && !vcs.isSecureScheme(s) {
continue
}
- if vcs.ping(s, repo) == nil {
+ if vcs.Ping(s, repo) == nil {
scheme = s
break
}
@@ -754,8 +756,7 @@ func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths
rr := &RepoRoot{
Repo: repoURL,
Root: match["root"],
- VCS: vcs.cmd,
- vcs: vcs,
+ VCS: vcs,
}
return rr, nil
}
@@ -846,17 +847,21 @@ func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.Se
if err := validateRepoRoot(mmi.RepoRoot); err != nil {
return nil, fmt.Errorf("%s: invalid repo root %q: %v", resp.URL, mmi.RepoRoot, err)
}
- vcs := vcsByCmd(mmi.VCS)
- if vcs == nil && mmi.VCS != "mod" {
- return nil, fmt.Errorf("%s: unknown vcs %q", resp.URL, mmi.VCS)
+ var vcs *Cmd
+ if mmi.VCS == "mod" {
+ vcs = vcsMod
+ } else {
+ vcs = vcsByCmd(mmi.VCS)
+ if vcs == nil {
+ return nil, fmt.Errorf("%s: unknown vcs %q", resp.URL, mmi.VCS)
+ }
}
rr := &RepoRoot{
Repo: mmi.RepoRoot,
Root: mmi.Prefix,
IsCustom: true,
- VCS: mmi.VCS,
- vcs: vcs,
+ VCS: vcs,
}
return rr, nil
}
@@ -1103,7 +1108,7 @@ var vcsPathsAfterDynamic = []*vcsPath{
func noVCSSuffix(match map[string]string) error {
repo := match["repo"]
for _, vcs := range vcsList {
- if strings.HasSuffix(repo, "."+vcs.cmd) {
+ if strings.HasSuffix(repo, "."+vcs.Cmd) {
return fmt.Errorf("invalid version control suffix in %s path", match["prefix"])
}
}
@@ -1133,7 +1138,7 @@ func bitbucketVCS(match map[string]string) error {
// VCS it uses. See issue 5375.
root := match["root"]
for _, vcs := range []string{"git", "hg"} {
- if vcsByCmd(vcs).ping("https", root) == nil {
+ if vcsByCmd(vcs).Ping("https", root) == nil {
resp.SCM = vcs
break
}
diff --git a/src/cmd/go/internal/get/vcs_test.go b/src/cmd/go/internal/vcs/vcs_test.go
similarity index 94%
rename from src/cmd/go/internal/get/vcs_test.go
rename to src/cmd/go/internal/vcs/vcs_test.go
index 195bc231eb1..5b874204f1e 100644
--- a/src/cmd/go/internal/get/vcs_test.go
+++ b/src/cmd/go/internal/vcs/vcs_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package get
+package vcs
import (
"errors"
@@ -28,7 +28,7 @@ func TestRepoRootForImportPath(t *testing.T) {
{
"github.com/golang/groupcache",
&RepoRoot{
- vcs: vcsGit,
+ VCS: vcsGit,
Repo: "https://github.com/golang/groupcache",
},
},
@@ -41,14 +41,14 @@ func TestRepoRootForImportPath(t *testing.T) {
{
"hub.jazz.net/git/user1/pkgname",
&RepoRoot{
- vcs: vcsGit,
+ VCS: vcsGit,
Repo: "https://hub.jazz.net/git/user1/pkgname",
},
},
{
"hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule",
&RepoRoot{
- vcs: vcsGit,
+ VCS: vcsGit,
Repo: "https://hub.jazz.net/git/user1/pkgname",
},
},
@@ -89,7 +89,7 @@ func TestRepoRootForImportPath(t *testing.T) {
{
"hub.jazz.net/git/user/pkg.name",
&RepoRoot{
- vcs: vcsGit,
+ VCS: vcsGit,
Repo: "https://hub.jazz.net/git/user/pkg.name",
},
},
@@ -102,7 +102,7 @@ func TestRepoRootForImportPath(t *testing.T) {
{
"git.openstack.org/openstack/swift",
&RepoRoot{
- vcs: vcsGit,
+ VCS: vcsGit,
Repo: "https://git.openstack.org/openstack/swift",
},
},
@@ -112,14 +112,14 @@ func TestRepoRootForImportPath(t *testing.T) {
{
"git.openstack.org/openstack/swift.git",
&RepoRoot{
- vcs: vcsGit,
+ VCS: vcsGit,
Repo: "https://git.openstack.org/openstack/swift.git",
},
},
{
"git.openstack.org/openstack/swift/go/hummingbird",
&RepoRoot{
- vcs: vcsGit,
+ VCS: vcsGit,
Repo: "https://git.openstack.org/openstack/swift",
},
},
@@ -148,21 +148,21 @@ func TestRepoRootForImportPath(t *testing.T) {
{
"git.apache.org/package-name.git",
&RepoRoot{
- vcs: vcsGit,
+ VCS: vcsGit,
Repo: "https://git.apache.org/package-name.git",
},
},
{
"git.apache.org/package-name_2.x.git/path/to/lib",
&RepoRoot{
- vcs: vcsGit,
+ VCS: vcsGit,
Repo: "https://git.apache.org/package-name_2.x.git",
},
},
{
"chiselapp.com/user/kyle/repository/fossilgg",
&RepoRoot{
- vcs: vcsFossil,
+ VCS: vcsFossil,
Repo: "https://chiselapp.com/user/kyle/repository/fossilgg",
},
},
@@ -191,8 +191,8 @@ func TestRepoRootForImportPath(t *testing.T) {
t.Errorf("RepoRootForImportPath(%q): %v", test.path, err)
continue
}
- if got.vcs.name != want.vcs.name || got.Repo != want.Repo {
- t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.Repo, want.vcs, want.Repo)
+ if got.VCS.Name != want.VCS.Name || got.Repo != want.Repo {
+ t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.VCS, got.Repo, want.VCS, want.Repo)
}
}
}
@@ -206,7 +206,7 @@ func TestFromDir(t *testing.T) {
defer os.RemoveAll(tempDir)
for j, vcs := range vcsList {
- dir := filepath.Join(tempDir, "example.com", vcs.name, "."+vcs.cmd)
+ dir := filepath.Join(tempDir, "example.com", vcs.Name, "."+vcs.Cmd)
if j&1 == 0 {
err := os.MkdirAll(dir, 0755)
if err != nil {
@@ -225,24 +225,24 @@ func TestFromDir(t *testing.T) {
}
want := RepoRoot{
- vcs: vcs,
- Root: path.Join("example.com", vcs.name),
+ VCS: vcs,
+ Root: path.Join("example.com", vcs.Name),
}
var got RepoRoot
- got.vcs, got.Root, err = vcsFromDir(dir, tempDir)
+ got.VCS, got.Root, err = FromDir(dir, tempDir)
if err != nil {
t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
continue
}
- if got.vcs.name != want.vcs.name || got.Root != want.Root {
- t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.Root, want.vcs, want.Root)
+ if got.VCS.Name != want.VCS.Name || got.Root != want.Root {
+ t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.VCS, got.Root, want.VCS, want.Root)
}
}
}
func TestIsSecure(t *testing.T) {
tests := []struct {
- vcs *vcsCmd
+ vcs *Cmd
url string
secure bool
}{
@@ -267,7 +267,7 @@ func TestIsSecure(t *testing.T) {
}
for _, test := range tests {
- secure := test.vcs.isSecure(test.url)
+ secure := test.vcs.IsSecure(test.url)
if secure != test.secure {
t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure)
}
@@ -276,7 +276,7 @@ func TestIsSecure(t *testing.T) {
func TestIsSecureGitAllowProtocol(t *testing.T) {
tests := []struct {
- vcs *vcsCmd
+ vcs *Cmd
url string
secure bool
}{
@@ -307,7 +307,7 @@ func TestIsSecureGitAllowProtocol(t *testing.T) {
defer os.Unsetenv("GIT_ALLOW_PROTOCOL")
os.Setenv("GIT_ALLOW_PROTOCOL", "https:foo")
for _, test := range tests {
- secure := test.vcs.isSecure(test.url)
+ secure := test.vcs.IsSecure(test.url)
if secure != test.secure {
t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure)
}
From d7384f36121d52191097af50d6dc12c0eb08fd75 Mon Sep 17 00:00:00 2001
From: Constantin Konstantinidis
Date: Sun, 16 Aug 2020 13:48:09 +0200
Subject: [PATCH 0044/1082] os: implement File.Chmod on Windows
Fixes: #39606
Change-Id: I4def67ef18bd3ff866b140f6e76cdabe5d51a1c5
Reviewed-on: https://go-review.googlesource.com/c/go/+/250077
Run-TryBot: Alex Brainman
TryBot-Result: Gobot Gobot
Reviewed-by: Alex Brainman
---
src/internal/poll/fd_posix.go | 11 --------
src/internal/poll/fd_unix.go | 11 ++++++++
src/internal/poll/fd_windows.go | 27 +++++++++++++++++++
.../syscall/windows/syscall_windows.go | 9 +++++++
.../syscall/windows/zsyscall_windows.go | 13 +++++++++
src/os/os_test.go | 27 +++++++++++--------
6 files changed, 76 insertions(+), 22 deletions(-)
diff --git a/src/internal/poll/fd_posix.go b/src/internal/poll/fd_posix.go
index e5fb05c9c22..4edfa953a4b 100644
--- a/src/internal/poll/fd_posix.go
+++ b/src/internal/poll/fd_posix.go
@@ -29,17 +29,6 @@ func (fd *FD) Shutdown(how int) error {
return syscall.Shutdown(fd.Sysfd, how)
}
-// Fchmod wraps syscall.Fchmod.
-func (fd *FD) Fchmod(mode uint32) error {
- if err := fd.incref(); err != nil {
- return err
- }
- defer fd.decref()
- return ignoringEINTR(func() error {
- return syscall.Fchmod(fd.Sysfd, mode)
- })
-}
-
// Fchown wraps syscall.Fchown.
func (fd *FD) Fchown(uid, gid int) error {
if err := fd.incref(); err != nil {
diff --git a/src/internal/poll/fd_unix.go b/src/internal/poll/fd_unix.go
index 1d5101eac3e..f6f6c52f318 100644
--- a/src/internal/poll/fd_unix.go
+++ b/src/internal/poll/fd_unix.go
@@ -437,6 +437,17 @@ func (fd *FD) ReadDirent(buf []byte) (int, error) {
}
}
+// Fchmod wraps syscall.Fchmod.
+func (fd *FD) Fchmod(mode uint32) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+ return ignoringEINTR(func() error {
+ return syscall.Fchmod(fd.Sysfd, mode)
+ })
+}
+
// Fchdir wraps syscall.Fchdir.
func (fd *FD) Fchdir() error {
if err := fd.incref(); err != nil {
diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go
index e1ef6199b37..d8c834f9299 100644
--- a/src/internal/poll/fd_windows.go
+++ b/src/internal/poll/fd_windows.go
@@ -886,6 +886,33 @@ func (fd *FD) FindNextFile(data *syscall.Win32finddata) error {
return syscall.FindNextFile(fd.Sysfd, data)
}
+// Fchmod updates syscall.ByHandleFileInformation.Fileattributes when needed.
+func (fd *FD) Fchmod(mode uint32) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+
+ var d syscall.ByHandleFileInformation
+ if err := syscall.GetFileInformationByHandle(fd.Sysfd, &d); err != nil {
+ return err
+ }
+ attrs := d.FileAttributes
+ if mode&syscall.S_IWRITE != 0 {
+ attrs &^= syscall.FILE_ATTRIBUTE_READONLY
+ } else {
+ attrs |= syscall.FILE_ATTRIBUTE_READONLY
+ }
+ if attrs == d.FileAttributes {
+ return nil
+ }
+
+ var du windows.FILE_BASIC_INFO
+ du.FileAttributes = attrs
+ l := uint32(unsafe.Sizeof(d))
+ return windows.SetFileInformationByHandle(fd.Sysfd, windows.FileBasicInfo, uintptr(unsafe.Pointer(&du)), l)
+}
+
// Fchdir wraps syscall.Fchdir.
func (fd *FD) Fchdir() error {
if err := fd.incref(); err != nil {
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
index edf0b5a40b8..1f40c118205 100644
--- a/src/internal/syscall/windows/syscall_windows.go
+++ b/src/internal/syscall/windows/syscall_windows.go
@@ -131,6 +131,14 @@ type IpAdapterAddresses struct {
/* more fields might be present here. */
}
+type FILE_BASIC_INFO struct {
+ CreationTime syscall.Filetime
+ LastAccessTime syscall.Filetime
+ LastWriteTime syscall.Filetime
+ ChangedTime syscall.Filetime
+ FileAttributes uint32
+}
+
const (
IfOperStatusUp = 1
IfOperStatusDown = 2
@@ -145,6 +153,7 @@ const (
//sys GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
//sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW
//sys GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) = kernel32.GetModuleFileNameW
+//sys SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf uintptr, bufsize uint32) (err error) = kernel32.SetFileInformationByHandle
const (
WSA_FLAG_OVERLAPPED = 0x01
diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go
index ca5b4e6f16d..0840dc283ae 100644
--- a/src/internal/syscall/windows/zsyscall_windows.go
+++ b/src/internal/syscall/windows/zsyscall_windows.go
@@ -71,6 +71,7 @@ var (
procNetUserGetLocalGroups = modnetapi32.NewProc("NetUserGetLocalGroups")
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
+ procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
)
func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) {
@@ -81,6 +82,18 @@ func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapter
return
}
+func SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf uintptr, bufsize uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(handle), uintptr(fileInformationClass), uintptr(buf), uintptr(bufsize), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
func GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) {
r1, _, e1 := syscall.Syscall(procGetComputerNameExW.Addr(), 3, uintptr(nameformat), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)))
if r1 == 0 {
diff --git a/src/os/os_test.go b/src/os/os_test.go
index 520916d8800..3359301316e 100644
--- a/src/os/os_test.go
+++ b/src/os/os_test.go
@@ -1099,29 +1099,34 @@ func checkMode(t *testing.T, path string, mode FileMode) {
if err != nil {
t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
}
- if dir.Mode()&0777 != mode {
+ if dir.Mode()&ModePerm != mode {
t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode)
}
}
func TestChmod(t *testing.T) {
- // Chmod is not supported under windows.
- if runtime.GOOS == "windows" {
- return
- }
f := newFile("TestChmod", t)
defer Remove(f.Name())
defer f.Close()
+ // Creation mode is read write
- if err := Chmod(f.Name(), 0456); err != nil {
- t.Fatalf("chmod %s 0456: %s", f.Name(), err)
+ fm := FileMode(0456)
+ if runtime.GOOS == "windows" {
+ fm = FileMode(0444) // read-only file
}
- checkMode(t, f.Name(), 0456)
+ if err := Chmod(f.Name(), fm); err != nil {
+ t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
+ }
+ checkMode(t, f.Name(), fm)
- if err := f.Chmod(0123); err != nil {
- t.Fatalf("chmod %s 0123: %s", f.Name(), err)
+ fm = FileMode(0123)
+ if runtime.GOOS == "windows" {
+ fm = FileMode(0666) // read-write file
}
- checkMode(t, f.Name(), 0123)
+ if err := f.Chmod(fm); err != nil {
+ t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
+ }
+ checkMode(t, f.Name(), fm)
}
func checkSize(t *testing.T, f *File, size int64) {
From 2c95e3a6a8377ca9c72608c25b4cf2506baf782f Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Wed, 9 Sep 2020 11:09:01 +0700
Subject: [PATCH 0045/1082] cmd/compile: use clearer error message for stuct
literal
This CL changes "T literal.M" error message to "T{...}.M". It's clearer
expression and focusing user on actual issue.
Updates #38745
Change-Id: I84b455a86742f37e0bde5bf390aa02984eecc3c9
Reviewed-on: https://go-review.googlesource.com/c/go/+/253677
Run-TryBot: Cuong Manh Le
TryBot-Result: Gobot Gobot
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/fmt.go | 11 +-
test/alias2.go | 10 +-
test/ddd1.go | 2 +-
test/escape2.go | 56 +++++-----
test/escape2n.go | 56 +++++-----
test/escape_calls.go | 2 +-
test/escape_field.go | 6 +-
test/escape_iface.go | 50 ++++-----
test/escape_indir.go | 34 +++----
test/escape_map.go | 26 ++---
test/escape_param.go | 60 +++++------
test/escape_slice.go | 14 +--
test/escape_struct_param1.go | 158 ++++++++++++++---------------
test/escape_struct_param2.go | 158 ++++++++++++++---------------
test/fixedbugs/issue12006.go | 4 +-
test/fixedbugs/issue13799.go | 4 +-
test/fixedbugs/issue17645.go | 2 +-
test/fixedbugs/issue21709.go | 4 +-
test/fixedbugs/issue23732.go | 6 +-
test/fixedbugs/issue26855.go | 4 +-
test/fixedbugs/issue30898.go | 2 +-
test/fixedbugs/issue31573.go | 24 ++---
test/fixedbugs/issue38745.go | 19 ++++
test/fixedbugs/issue39292.go | 6 +-
test/fixedbugs/issue41247.go | 2 +-
test/fixedbugs/issue7921.go | 10 +-
test/inline_variadic.go | 2 +-
27 files changed, 379 insertions(+), 353 deletions(-)
create mode 100644 test/fixedbugs/issue38745.go
diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go
index 866cd0a7145..43e501deaf1 100644
--- a/src/cmd/compile/internal/gc/fmt.go
+++ b/src/cmd/compile/internal/gc/fmt.go
@@ -1407,7 +1407,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
return
}
if n.Right != nil {
- mode.Fprintf(s, "%v literal", n.Right)
+ mode.Fprintf(s, "%v{%s}", n.Right, ellipsisIf(n.List.Len() != 0))
return
}
@@ -1421,7 +1421,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT:
if mode == FErr {
- mode.Fprintf(s, "%v literal", n.Type)
+ mode.Fprintf(s, "%v{%s}", n.Type, ellipsisIf(n.List.Len() != 0))
return
}
mode.Fprintf(s, "(%v{ %.v })", n.Type, n.List)
@@ -1934,3 +1934,10 @@ func indent(s fmt.State) {
fmt.Fprint(s, ". ")
}
}
+
+func ellipsisIf(b bool) string {
+ if b {
+ return "..."
+ }
+ return ""
+}
diff --git a/test/alias2.go b/test/alias2.go
index 7ea1b2908dd..1c141ac490c 100644
--- a/test/alias2.go
+++ b/test/alias2.go
@@ -46,8 +46,8 @@ var _ A0 = T0{}
var _ T0 = A0{}
// But aliases and original types cannot be used with new types based on them.
-var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
-var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
+var _ N0 = T0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type"
+var _ N0 = A0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type"
var _ A5 = Value{}
@@ -82,10 +82,10 @@ func _() {
var _ A0 = T0{}
var _ T0 = A0{}
- var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
- var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
+ var _ N0 = T0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type"
+ var _ N0 = A0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type"
- var _ A5 = Value{} // ERROR "cannot use reflect\.Value literal \(type reflect.Value\) as type A5 in assignment|incompatible type"
+ var _ A5 = Value{} // ERROR "cannot use reflect\.Value{} \(type reflect.Value\) as type A5 in assignment|incompatible type"
}
// Invalid type alias declarations.
diff --git a/test/ddd1.go b/test/ddd1.go
index b582f221b75..2c7e83e3745 100644
--- a/test/ddd1.go
+++ b/test/ddd1.go
@@ -19,7 +19,7 @@ var (
_ = sum(1.0, 2.0)
_ = sum(1.5) // ERROR "integer"
_ = sum("hello") // ERROR ".hello. .type untyped string. as type int|incompatible"
- _ = sum([]int{1}) // ERROR "\[\]int literal.*as type int|incompatible"
+ _ = sum([]int{1}) // ERROR "\[\]int{...}.*as type int|incompatible"
)
func sum3(int, int, int) int { return 0 }
diff --git a/test/escape2.go b/test/escape2.go
index cf24f4bebc8..5c6eb559faf 100644
--- a/test/escape2.go
+++ b/test/escape2.go
@@ -118,15 +118,15 @@ type Bar struct {
}
func NewBar() *Bar {
- return &Bar{42, nil} // ERROR "&Bar literal escapes to heap$"
+ return &Bar{42, nil} // ERROR "&Bar{...} escapes to heap$"
}
func NewBarp(x *int) *Bar { // ERROR "leaking param: x$"
- return &Bar{42, x} // ERROR "&Bar literal escapes to heap$"
+ return &Bar{42, x} // ERROR "&Bar{...} escapes to heap$"
}
func NewBarp2(x *int) *Bar { // ERROR "x does not escape$"
- return &Bar{*x, nil} // ERROR "&Bar literal escapes to heap$"
+ return &Bar{*x, nil} // ERROR "&Bar{...} escapes to heap$"
}
func (b *Bar) NoLeak() int { // ERROR "b does not escape$"
@@ -173,7 +173,7 @@ type Bar2 struct {
}
func NewBar2() *Bar2 {
- return &Bar2{[12]int{42}, nil} // ERROR "&Bar2 literal escapes to heap$"
+ return &Bar2{[12]int{42}, nil} // ERROR "&Bar2{...} escapes to heap$"
}
func (b *Bar2) NoLeak() int { // ERROR "b does not escape$"
@@ -539,7 +539,7 @@ func foo72b() [10]*int {
// issue 2145
func foo73() {
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for _, v := range s {
vv := v
// actually just escapes its scope
@@ -550,7 +550,7 @@ func foo73() {
}
func foo731() {
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for _, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -562,7 +562,7 @@ func foo731() {
}
func foo74() {
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for _, v := range s {
vv := v
// actually just escapes its scope
@@ -574,7 +574,7 @@ func foo74() {
}
func foo74a() {
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for _, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -589,7 +589,7 @@ func foo74a() {
// issue 3975
func foo74b() {
var array [3]func()
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for i, v := range s {
vv := v
// actually just escapes its scope
@@ -601,7 +601,7 @@ func foo74b() {
func foo74c() {
var array [3]func()
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for i, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -759,15 +759,15 @@ type LimitedFooer struct {
}
func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: r$"
- return &LimitedFooer{r, n} // ERROR "&LimitedFooer literal escapes to heap$"
+ return &LimitedFooer{r, n} // ERROR "&LimitedFooer{...} escapes to heap$"
}
func foo90(x *int) map[*int]*int { // ERROR "leaking param: x$"
- return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$"
+ return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$"
}
func foo91(x *int) map[*int]*int { // ERROR "leaking param: x$"
- return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$"
+ return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$"
}
func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r1 level=0$"
@@ -870,15 +870,15 @@ func foo106(x *int) { // ERROR "leaking param: x$"
}
func foo107(x *int) map[*int]*int { // ERROR "leaking param: x$"
- return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$"
+ return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$"
}
func foo108(x *int) map[*int]*int { // ERROR "leaking param: x$"
- return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$"
+ return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$"
}
func foo109(x *int) *int { // ERROR "leaking param: x$"
- m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal does not escape$"
+ m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} does not escape$"
for k, _ := range m {
return k
}
@@ -886,12 +886,12 @@ func foo109(x *int) *int { // ERROR "leaking param: x$"
}
func foo110(x *int) *int { // ERROR "leaking param: x$"
- m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal does not escape$"
+ m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} does not escape$"
return m[nil]
}
func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0"
- m := []*int{x} // ERROR "\[\]\*int literal does not escape$"
+ m := []*int{x} // ERROR "\[\]\*int{...} does not escape$"
return m[0]
}
@@ -906,7 +906,7 @@ func foo113(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$"
}
func foo114(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$"
- m := &Bar{ii: x} // ERROR "&Bar literal does not escape$"
+ m := &Bar{ii: x} // ERROR "&Bar{...} does not escape$"
return m.ii
}
@@ -1343,8 +1343,8 @@ func foo140() interface{} {
X string
T *T
}
- t := &T{} // ERROR "&T literal escapes to heap$"
- return U{ // ERROR "U literal escapes to heap$"
+ t := &T{} // ERROR "&T{} escapes to heap$"
+ return U{ // ERROR "U{...} escapes to heap$"
X: t.X,
T: t,
}
@@ -1530,7 +1530,7 @@ type V struct {
}
func NewV(u U) *V { // ERROR "leaking param: u$"
- return &V{u.String()} // ERROR "&V literal escapes to heap$"
+ return &V{u.String()} // ERROR "&V{...} escapes to heap$"
}
func foo152() {
@@ -1571,21 +1571,21 @@ type Lit struct {
func ptrlitNoescape() {
// Both literal and element do not escape.
i := 0
- x := &Lit{&i} // ERROR "&Lit literal does not escape$"
+ x := &Lit{&i} // ERROR "&Lit{...} does not escape$"
_ = x
}
func ptrlitNoEscape2() {
// Literal does not escape, but element does.
i := 0 // ERROR "moved to heap: i$"
- x := &Lit{&i} // ERROR "&Lit literal does not escape$"
+ x := &Lit{&i} // ERROR "&Lit{...} does not escape$"
sink = *x
}
func ptrlitEscape() {
// Both literal and element escape.
i := 0 // ERROR "moved to heap: i$"
- x := &Lit{&i} // ERROR "&Lit literal escapes to heap$"
+ x := &Lit{&i} // ERROR "&Lit{...} escapes to heap$"
sink = x
}
@@ -1760,18 +1760,18 @@ func stringtoslicerune2() {
}
func slicerunetostring0() {
- r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$"
s := string(r) // ERROR "string\(r\) does not escape$"
_ = s
}
func slicerunetostring1() string {
- r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$"
return string(r) // ERROR "string\(r\) escapes to heap$"
}
func slicerunetostring2() {
- r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$"
sink = string(r) // ERROR "string\(r\) escapes to heap$"
}
diff --git a/test/escape2n.go b/test/escape2n.go
index f771e0aef26..46e58f85661 100644
--- a/test/escape2n.go
+++ b/test/escape2n.go
@@ -118,15 +118,15 @@ type Bar struct {
}
func NewBar() *Bar {
- return &Bar{42, nil} // ERROR "&Bar literal escapes to heap$"
+ return &Bar{42, nil} // ERROR "&Bar{...} escapes to heap$"
}
func NewBarp(x *int) *Bar { // ERROR "leaking param: x$"
- return &Bar{42, x} // ERROR "&Bar literal escapes to heap$"
+ return &Bar{42, x} // ERROR "&Bar{...} escapes to heap$"
}
func NewBarp2(x *int) *Bar { // ERROR "x does not escape$"
- return &Bar{*x, nil} // ERROR "&Bar literal escapes to heap$"
+ return &Bar{*x, nil} // ERROR "&Bar{...} escapes to heap$"
}
func (b *Bar) NoLeak() int { // ERROR "b does not escape$"
@@ -173,7 +173,7 @@ type Bar2 struct {
}
func NewBar2() *Bar2 {
- return &Bar2{[12]int{42}, nil} // ERROR "&Bar2 literal escapes to heap$"
+ return &Bar2{[12]int{42}, nil} // ERROR "&Bar2{...} escapes to heap$"
}
func (b *Bar2) NoLeak() int { // ERROR "b does not escape$"
@@ -539,7 +539,7 @@ func foo72b() [10]*int {
// issue 2145
func foo73() {
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for _, v := range s {
vv := v
// actually just escapes its scope
@@ -550,7 +550,7 @@ func foo73() {
}
func foo731() {
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for _, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -562,7 +562,7 @@ func foo731() {
}
func foo74() {
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for _, v := range s {
vv := v
// actually just escapes its scope
@@ -574,7 +574,7 @@ func foo74() {
}
func foo74a() {
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for _, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -589,7 +589,7 @@ func foo74a() {
// issue 3975
func foo74b() {
var array [3]func()
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for i, v := range s {
vv := v
// actually just escapes its scope
@@ -601,7 +601,7 @@ func foo74b() {
func foo74c() {
var array [3]func()
- s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int{...} does not escape$"
for i, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -759,15 +759,15 @@ type LimitedFooer struct {
}
func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: r$"
- return &LimitedFooer{r, n} // ERROR "&LimitedFooer literal escapes to heap$"
+ return &LimitedFooer{r, n} // ERROR "&LimitedFooer{...} escapes to heap$"
}
func foo90(x *int) map[*int]*int { // ERROR "leaking param: x$"
- return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$"
+ return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$"
}
func foo91(x *int) map[*int]*int { // ERROR "leaking param: x$"
- return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$"
+ return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$"
}
func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r1 level=0$"
@@ -870,15 +870,15 @@ func foo106(x *int) { // ERROR "leaking param: x$"
}
func foo107(x *int) map[*int]*int { // ERROR "leaking param: x$"
- return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap$"
+ return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$"
}
func foo108(x *int) map[*int]*int { // ERROR "leaking param: x$"
- return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap$"
+ return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} escapes to heap$"
}
func foo109(x *int) *int { // ERROR "leaking param: x$"
- m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal does not escape$"
+ m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} does not escape$"
for k, _ := range m {
return k
}
@@ -886,12 +886,12 @@ func foo109(x *int) *int { // ERROR "leaking param: x$"
}
func foo110(x *int) *int { // ERROR "leaking param: x$"
- m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal does not escape$"
+ m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int{...} does not escape$"
return m[nil]
}
func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0"
- m := []*int{x} // ERROR "\[\]\*int literal does not escape$"
+ m := []*int{x} // ERROR "\[\]\*int{...} does not escape$"
return m[0]
}
@@ -906,7 +906,7 @@ func foo113(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$"
}
func foo114(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$"
- m := &Bar{ii: x} // ERROR "&Bar literal does not escape$"
+ m := &Bar{ii: x} // ERROR "&Bar{...} does not escape$"
return m.ii
}
@@ -1343,8 +1343,8 @@ func foo140() interface{} {
X string
T *T
}
- t := &T{} // ERROR "&T literal escapes to heap$"
- return U{ // ERROR "U literal escapes to heap$"
+ t := &T{} // ERROR "&T{} escapes to heap$"
+ return U{ // ERROR "U{...} escapes to heap$"
X: t.X,
T: t,
}
@@ -1530,7 +1530,7 @@ type V struct {
}
func NewV(u U) *V { // ERROR "leaking param: u$"
- return &V{u.String()} // ERROR "&V literal escapes to heap$"
+ return &V{u.String()} // ERROR "&V{...} escapes to heap$"
}
func foo152() {
@@ -1571,21 +1571,21 @@ type Lit struct {
func ptrlitNoescape() {
// Both literal and element do not escape.
i := 0
- x := &Lit{&i} // ERROR "&Lit literal does not escape$"
+ x := &Lit{&i} // ERROR "&Lit{...} does not escape$"
_ = x
}
func ptrlitNoEscape2() {
// Literal does not escape, but element does.
i := 0 // ERROR "moved to heap: i$"
- x := &Lit{&i} // ERROR "&Lit literal does not escape$"
+ x := &Lit{&i} // ERROR "&Lit{...} does not escape$"
sink = *x
}
func ptrlitEscape() {
// Both literal and element escape.
i := 0 // ERROR "moved to heap: i$"
- x := &Lit{&i} // ERROR "&Lit literal escapes to heap$"
+ x := &Lit{&i} // ERROR "&Lit{...} escapes to heap$"
sink = x
}
@@ -1760,18 +1760,18 @@ func stringtoslicerune2() {
}
func slicerunetostring0() {
- r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$"
s := string(r) // ERROR "string\(r\) does not escape$"
_ = s
}
func slicerunetostring1() string {
- r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$"
return string(r) // ERROR "string\(r\) escapes to heap$"
}
func slicerunetostring2() {
- r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune{...} does not escape$"
sink = string(r) // ERROR "string\(r\) escapes to heap$"
}
diff --git a/test/escape_calls.go b/test/escape_calls.go
index 2dbfee15586..9e1db5426ed 100644
--- a/test/escape_calls.go
+++ b/test/escape_calls.go
@@ -50,5 +50,5 @@ func bar() {
f := prototype
f = func(ss []string) { got = append(got, ss) } // ERROR "leaking param: ss" "func literal does not escape"
s := "string"
- f([]string{s}) // ERROR "\[\]string literal escapes to heap"
+ f([]string{s}) // ERROR "\[\]string{...} escapes to heap"
}
diff --git a/test/escape_field.go b/test/escape_field.go
index bf1dfb18ff1..95d0784d916 100644
--- a/test/escape_field.go
+++ b/test/escape_field.go
@@ -127,20 +127,20 @@ func field12() {
func field13() {
i := 0 // ERROR "moved to heap: i$"
- x := &X{p1: &i} // ERROR "&X literal does not escape$"
+ x := &X{p1: &i} // ERROR "&X{...} does not escape$"
sink = x.p1
}
func field14() {
i := 0 // ERROR "moved to heap: i$"
// BAD: &i should not escape
- x := &X{p1: &i} // ERROR "&X literal does not escape$"
+ x := &X{p1: &i} // ERROR "&X{...} does not escape$"
sink = x.p2
}
func field15() {
i := 0 // ERROR "moved to heap: i$"
- x := &X{p1: &i} // ERROR "&X literal escapes to heap$"
+ x := &X{p1: &i} // ERROR "&X{...} escapes to heap$"
sink = x
}
diff --git a/test/escape_iface.go b/test/escape_iface.go
index 118ed3c56ff..7b0914cadbb 100644
--- a/test/escape_iface.go
+++ b/test/escape_iface.go
@@ -37,7 +37,7 @@ func efaceEscape0() {
_ = x
}
{
- i := 0 // ERROR "moved to heap: i"
+ i := 0 // ERROR "moved to heap: i"
v := M0{&i}
var x M = v
sink = x
@@ -50,7 +50,7 @@ func efaceEscape0() {
_ = v1
}
{
- i := 0 // ERROR "moved to heap: i"
+ i := 0 // ERROR "moved to heap: i"
v := M0{&i}
// BAD: v does not escape to heap here
var x M = v
@@ -58,14 +58,14 @@ func efaceEscape0() {
sink = v1
}
{
- i := 0 // ERROR "moved to heap: i"
+ i := 0 // ERROR "moved to heap: i"
v := M0{&i}
// BAD: v does not escape to heap here
var x M = v
x.M()
}
{
- i := 0 // ERROR "moved to heap: i"
+ i := 0 // ERROR "moved to heap: i"
v := M0{&i}
var x M = v
mescapes(x)
@@ -91,46 +91,46 @@ func efaceEscape1() {
{
i := 0
v := M1{&i, 0}
- var x M = v // ERROR "v does not escape"
+ var x M = v // ERROR "v does not escape"
_ = x
}
{
- i := 0 // ERROR "moved to heap: i"
+ i := 0 // ERROR "moved to heap: i"
v := M1{&i, 0}
- var x M = v // ERROR "v escapes to heap"
+ var x M = v // ERROR "v escapes to heap"
sink = x
}
{
i := 0
v := M1{&i, 0}
- var x M = v // ERROR "v does not escape"
+ var x M = v // ERROR "v does not escape"
v1 := x.(M1)
_ = v1
}
{
- i := 0 // ERROR "moved to heap: i"
+ i := 0 // ERROR "moved to heap: i"
v := M1{&i, 0}
var x M = v // ERROR "v does not escape"
v1 := x.(M1)
sink = v1 // ERROR "v1 escapes to heap"
}
{
- i := 0 // ERROR "moved to heap: i"
+ i := 0 // ERROR "moved to heap: i"
v := M1{&i, 0}
// BAD: v does not escape to heap here
var x M = v // ERROR "v escapes to heap"
x.M()
}
{
- i := 0 // ERROR "moved to heap: i"
+ i := 0 // ERROR "moved to heap: i"
v := M1{&i, 0}
- var x M = v // ERROR "v escapes to heap"
+ var x M = v // ERROR "v escapes to heap"
mescapes(x)
}
{
i := 0
v := M1{&i, 0}
- var x M = v // ERROR "v does not escape"
+ var x M = v // ERROR "v does not escape"
mdoesnotescape(x)
}
}
@@ -146,26 +146,26 @@ func (*M2) M() {
func efaceEscape2() {
{
i := 0
- v := &M2{&i} // ERROR "&M2 literal does not escape"
+ v := &M2{&i} // ERROR "&M2{...} does not escape"
var x M = v
_ = x
}
{
i := 0 // ERROR "moved to heap: i"
- v := &M2{&i} // ERROR "&M2 literal escapes to heap"
+ v := &M2{&i} // ERROR "&M2{...} escapes to heap"
var x M = v
sink = x
}
{
i := 0
- v := &M2{&i} // ERROR "&M2 literal does not escape"
+ v := &M2{&i} // ERROR "&M2{...} does not escape"
var x M = v
v1 := x.(*M2)
_ = v1
}
{
i := 0 // ERROR "moved to heap: i"
- v := &M2{&i} // ERROR "&M2 literal escapes to heap"
+ v := &M2{&i} // ERROR "&M2{...} escapes to heap"
// BAD: v does not escape to heap here
var x M = v
v1 := x.(*M2)
@@ -173,7 +173,7 @@ func efaceEscape2() {
}
{
i := 0 // ERROR "moved to heap: i"
- v := &M2{&i} // ERROR "&M2 literal does not escape"
+ v := &M2{&i} // ERROR "&M2{...} does not escape"
// BAD: v does not escape to heap here
var x M = v
v1 := x.(*M2)
@@ -181,7 +181,7 @@ func efaceEscape2() {
}
{
i := 0 // ERROR "moved to heap: i"
- v := &M2{&i} // ERROR "&M2 literal does not escape"
+ v := &M2{&i} // ERROR "&M2{...} does not escape"
// BAD: v does not escape to heap here
var x M = v
v1, ok := x.(*M2)
@@ -190,20 +190,20 @@ func efaceEscape2() {
}
{
i := 0 // ERROR "moved to heap: i"
- v := &M2{&i} // ERROR "&M2 literal escapes to heap"
+ v := &M2{&i} // ERROR "&M2{...} escapes to heap"
// BAD: v does not escape to heap here
var x M = v
x.M()
}
{
i := 0 // ERROR "moved to heap: i"
- v := &M2{&i} // ERROR "&M2 literal escapes to heap"
+ v := &M2{&i} // ERROR "&M2{...} escapes to heap"
var x M = v
mescapes(x)
}
{
i := 0
- v := &M2{&i} // ERROR "&M2 literal does not escape"
+ v := &M2{&i} // ERROR "&M2{...} does not escape"
var x M = v
mdoesnotescape(x)
}
@@ -219,8 +219,8 @@ type T2 struct {
func dotTypeEscape() *T2 { // #11931
var x interface{}
- x = &T1{p: new(int)} // ERROR "new\(int\) escapes to heap" "&T1 literal does not escape"
- return &T2{ // ERROR "&T2 literal escapes to heap"
+ x = &T1{p: new(int)} // ERROR "new\(int\) escapes to heap" "&T1{...} does not escape"
+ return &T2{ // ERROR "&T2{...} escapes to heap"
T1: *(x.(*T1)),
}
}
@@ -244,7 +244,7 @@ func dotTypeEscape2() { // #13805, #15796
var x interface{} = i // ERROR "i does not escape"
var y interface{} = j // ERROR "j does not escape"
- sink = x.(int) // ERROR "x.\(int\) escapes to heap"
+ sink = x.(int) // ERROR "x.\(int\) escapes to heap"
sink, *(&ok) = y.(int)
}
{
diff --git a/test/escape_indir.go b/test/escape_indir.go
index 19889f259f8..12005e35f9f 100644
--- a/test/escape_indir.go
+++ b/test/escape_indir.go
@@ -23,7 +23,7 @@ type ConstPtr2 struct {
func constptr0() {
i := 0 // ERROR "moved to heap: i"
- x := &ConstPtr{} // ERROR "&ConstPtr literal does not escape"
+ x := &ConstPtr{} // ERROR "&ConstPtr{} does not escape"
// BAD: i should not escape here
x.p = &i
_ = x
@@ -31,55 +31,55 @@ func constptr0() {
func constptr01() *ConstPtr {
i := 0 // ERROR "moved to heap: i"
- x := &ConstPtr{} // ERROR "&ConstPtr literal escapes to heap"
+ x := &ConstPtr{} // ERROR "&ConstPtr{} escapes to heap"
x.p = &i
return x
}
func constptr02() ConstPtr {
i := 0 // ERROR "moved to heap: i"
- x := &ConstPtr{} // ERROR "&ConstPtr literal does not escape"
+ x := &ConstPtr{} // ERROR "&ConstPtr{} does not escape"
x.p = &i
return *x
}
func constptr03() **ConstPtr {
i := 0 // ERROR "moved to heap: i"
- x := &ConstPtr{} // ERROR "&ConstPtr literal escapes to heap" "moved to heap: x"
+ x := &ConstPtr{} // ERROR "&ConstPtr{} escapes to heap" "moved to heap: x"
x.p = &i
return &x
}
func constptr1() {
i := 0 // ERROR "moved to heap: i"
- x := &ConstPtr{} // ERROR "&ConstPtr literal escapes to heap"
+ x := &ConstPtr{} // ERROR "&ConstPtr{} escapes to heap"
x.p = &i
sink = x
}
func constptr2() {
i := 0 // ERROR "moved to heap: i"
- x := &ConstPtr{} // ERROR "&ConstPtr literal does not escape"
+ x := &ConstPtr{} // ERROR "&ConstPtr{} does not escape"
x.p = &i
- sink = *x // ERROR "\*x escapes to heap"
+ sink = *x // ERROR "\*x escapes to heap"
}
func constptr4() *ConstPtr {
p := new(ConstPtr) // ERROR "new\(ConstPtr\) escapes to heap"
- *p = *&ConstPtr{} // ERROR "&ConstPtr literal does not escape"
+ *p = *&ConstPtr{} // ERROR "&ConstPtr{} does not escape"
return p
}
func constptr5() *ConstPtr {
p := new(ConstPtr) // ERROR "new\(ConstPtr\) escapes to heap"
- p1 := &ConstPtr{} // ERROR "&ConstPtr literal does not escape"
+ p1 := &ConstPtr{} // ERROR "&ConstPtr{} does not escape"
*p = *p1
return p
}
// BAD: p should not escape here
func constptr6(p *ConstPtr) { // ERROR "leaking param content: p"
- p1 := &ConstPtr{} // ERROR "&ConstPtr literal does not escape"
+ p1 := &ConstPtr{} // ERROR "&ConstPtr{} does not escape"
*p1 = *p
_ = p1
}
@@ -102,17 +102,17 @@ func constptr8() *ConstPtr {
func constptr9() ConstPtr {
p := new(ConstPtr) // ERROR "new\(ConstPtr\) does not escape"
var p1 ConstPtr2
- i := 0 // ERROR "moved to heap: i"
+ i := 0 // ERROR "moved to heap: i"
p1.p = &i
p.c = p1
return *p
}
func constptr10() ConstPtr {
- x := &ConstPtr{} // ERROR "moved to heap: x" "&ConstPtr literal escapes to heap"
+ x := &ConstPtr{} // ERROR "moved to heap: x" "&ConstPtr{} escapes to heap"
i := 0 // ERROR "moved to heap: i"
var p *ConstPtr
- p = &ConstPtr{p: &i, x: &x} // ERROR "&ConstPtr literal does not escape"
+ p = &ConstPtr{p: &i, x: &x} // ERROR "&ConstPtr{...} does not escape"
var pp **ConstPtr
pp = &p
return **pp
@@ -121,7 +121,7 @@ func constptr10() ConstPtr {
func constptr11() *ConstPtr {
i := 0 // ERROR "moved to heap: i"
p := new(ConstPtr) // ERROR "new\(ConstPtr\) escapes to heap"
- p1 := &ConstPtr{} // ERROR "&ConstPtr literal does not escape"
+ p1 := &ConstPtr{} // ERROR "&ConstPtr{} does not escape"
p1.p = &i
*p = *p1
return p
@@ -134,7 +134,7 @@ func foo(p **int) { // ERROR "p does not escape"
}
func foo1(p *int) { // ERROR "p does not escape"
- i := 0 // ERROR "moved to heap: i"
+ i := 0 // ERROR "moved to heap: i"
y := &p
*y = &i
}
@@ -148,13 +148,13 @@ func foo2() {
var z Z
z.f = &x
p := z.f
- i := 0 // ERROR "moved to heap: i"
+ i := 0 // ERROR "moved to heap: i"
*p = &i
}
var global *byte
func f() {
- var x byte // ERROR "moved to heap: x"
+ var x byte // ERROR "moved to heap: x"
global = &*&x
}
diff --git a/test/escape_map.go b/test/escape_map.go
index 0e9896a9fc5..23abaa1e0cc 100644
--- a/test/escape_map.go
+++ b/test/escape_map.go
@@ -15,7 +15,7 @@ func map0() {
// BAD: i should not escape
i := 0 // ERROR "moved to heap: i"
// BAD: j should not escape
- j := 0 // ERROR "moved to heap: j"
+ j := 0 // ERROR "moved to heap: j"
m[&i] = &j
_ = m
}
@@ -23,8 +23,8 @@ func map0() {
func map1() *int {
m := make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape"
// BAD: i should not escape
- i := 0 // ERROR "moved to heap: i"
- j := 0 // ERROR "moved to heap: j"
+ i := 0 // ERROR "moved to heap: i"
+ j := 0 // ERROR "moved to heap: j"
m[&i] = &j
return m[&i]
}
@@ -41,7 +41,7 @@ func map3() []*int {
m := make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape"
i := 0 // ERROR "moved to heap: i"
// BAD: j should not escape
- j := 0 // ERROR "moved to heap: j"
+ j := 0 // ERROR "moved to heap: j"
m[&i] = &j
var r []*int
for k := range m {
@@ -53,8 +53,8 @@ func map3() []*int {
func map4() []*int {
m := make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape"
// BAD: i should not escape
- i := 0 // ERROR "moved to heap: i"
- j := 0 // ERROR "moved to heap: j"
+ i := 0 // ERROR "moved to heap: i"
+ j := 0 // ERROR "moved to heap: j"
m[&i] = &j
var r []*int
for k, v := range m {
@@ -68,8 +68,8 @@ func map4() []*int {
}
func map5(m map[*int]*int) { // ERROR "m does not escape"
- i := 0 // ERROR "moved to heap: i"
- j := 0 // ERROR "moved to heap: j"
+ i := 0 // ERROR "moved to heap: i"
+ j := 0 // ERROR "moved to heap: j"
m[&i] = &j
}
@@ -77,8 +77,8 @@ func map6(m map[*int]*int) { // ERROR "m does not escape"
if m != nil {
m = make(map[*int]*int) // ERROR "make\(map\[\*int\]\*int\) does not escape"
}
- i := 0 // ERROR "moved to heap: i"
- j := 0 // ERROR "moved to heap: j"
+ i := 0 // ERROR "moved to heap: i"
+ j := 0 // ERROR "moved to heap: j"
m[&i] = &j
}
@@ -87,14 +87,14 @@ func map7() {
i := 0 // ERROR "moved to heap: i"
// BAD: j should not escape
j := 0 // ERROR "moved to heap: j"
- m := map[*int]*int{&i: &j} // ERROR "literal does not escape"
+ m := map[*int]*int{&i: &j} // ERROR "map\[\*int\]\*int{...} does not escape"
_ = m
}
func map8() {
i := 0 // ERROR "moved to heap: i"
j := 0 // ERROR "moved to heap: j"
- m := map[*int]*int{&i: &j} // ERROR "literal escapes to heap"
+ m := map[*int]*int{&i: &j} // ERROR "map\[\*int\]\*int{...} escapes to heap"
sink = m
}
@@ -102,6 +102,6 @@ func map9() *int {
// BAD: i should not escape
i := 0 // ERROR "moved to heap: i"
j := 0 // ERROR "moved to heap: j"
- m := map[*int]*int{&i: &j} // ERROR "literal does not escape"
+ m := map[*int]*int{&i: &j} // ERROR "map\[\*int\]\*int{...} does not escape"
return m[nil]
}
diff --git a/test/escape_param.go b/test/escape_param.go
index d8fafc53f87..993e914e1d0 100644
--- a/test/escape_param.go
+++ b/test/escape_param.go
@@ -26,7 +26,7 @@ func caller0a() {
}
func caller0b() {
- i := 0 // ERROR "moved to heap: i$"
+ i := 0 // ERROR "moved to heap: i$"
sink = param0(&i)
}
@@ -150,11 +150,11 @@ func caller3a() {
}
func caller3b() {
- i := 0 // ERROR "moved to heap: i$"
- j := 0 // ERROR "moved to heap: j$"
+ i := 0 // ERROR "moved to heap: i$"
+ j := 0 // ERROR "moved to heap: j$"
p := Pair{&i, &j}
param3(&p)
- sink = p // ERROR "p escapes to heap$"
+ sink = p // ERROR "p escapes to heap$"
}
// in -> rcvr
@@ -173,7 +173,7 @@ func caller4b() {
i := 0 // ERROR "moved to heap: i$"
p := Pair{}
p.param4(&i)
- sink = p // ERROR "p escapes to heap$"
+ sink = p // ERROR "p escapes to heap$"
}
// in -> heap
@@ -182,7 +182,7 @@ func param5(i *int) { // ERROR "leaking param: i$"
}
func caller5() {
- i := 0 // ERROR "moved to heap: i$"
+ i := 0 // ERROR "moved to heap: i$"
param5(&i)
}
@@ -192,8 +192,8 @@ func param6(i ***int) { // ERROR "leaking param content: i$"
}
func caller6a() {
- i := 0 // ERROR "moved to heap: i$"
- p := &i // ERROR "moved to heap: p$"
+ i := 0 // ERROR "moved to heap: i$"
+ p := &i // ERROR "moved to heap: p$"
p2 := &p
param6(&p2)
}
@@ -204,7 +204,7 @@ func param7(i ***int) { // ERROR "leaking param content: i$"
}
func caller7() {
- i := 0 // ERROR "moved to heap: i$"
+ i := 0 // ERROR "moved to heap: i$"
p := &i
p2 := &p
param7(&p2)
@@ -234,8 +234,8 @@ func caller9a() {
}
func caller9b() {
- i := 0 // ERROR "moved to heap: i$"
- p := &i // ERROR "moved to heap: p$"
+ i := 0 // ERROR "moved to heap: i$"
+ p := &i // ERROR "moved to heap: p$"
p2 := &p
sink = param9(&p2)
}
@@ -253,7 +253,7 @@ func caller10a() {
}
func caller10b() {
- i := 0 // ERROR "moved to heap: i$"
+ i := 0 // ERROR "moved to heap: i$"
p := &i
p2 := &p
sink = param10(&p2)
@@ -265,26 +265,26 @@ func param11(i **int) ***int { // ERROR "moved to heap: i$"
}
func caller11a() {
- i := 0 // ERROR "moved to heap: i"
- p := &i // ERROR "moved to heap: p"
+ i := 0 // ERROR "moved to heap: i"
+ p := &i // ERROR "moved to heap: p"
_ = param11(&p)
}
func caller11b() {
- i := 0 // ERROR "moved to heap: i$"
- p := &i // ERROR "moved to heap: p$"
+ i := 0 // ERROR "moved to heap: i$"
+ p := &i // ERROR "moved to heap: p$"
sink = param11(&p)
}
func caller11c() { // GOOD
- i := 0 // ERROR "moved to heap: i$"
- p := &i // ERROR "moved to heap: p"
+ i := 0 // ERROR "moved to heap: i$"
+ p := &i // ERROR "moved to heap: p"
sink = *param11(&p)
}
func caller11d() {
- i := 0 // ERROR "moved to heap: i$"
- p := &i // ERROR "moved to heap: p"
+ i := 0 // ERROR "moved to heap: i$"
+ p := &i // ERROR "moved to heap: p"
p2 := &p
sink = param11(p2)
}
@@ -309,7 +309,7 @@ func caller12a() {
func caller12b() {
i := 0 // ERROR "moved to heap: i$"
p := &i // ERROR "moved to heap: p$"
- r := &Indir{} // ERROR "&Indir literal does not escape$"
+ r := &Indir{} // ERROR "&Indir{} does not escape$"
r.param12(&p)
_ = r
}
@@ -359,7 +359,7 @@ func caller13b() {
func caller13c() {
i := 0 // ERROR "moved to heap: i$"
var p *int
- v := &Val{&p} // ERROR "&Val literal does not escape$"
+ v := &Val{&p} // ERROR "&Val{...} does not escape$"
v.param13(&i)
_ = v
}
@@ -374,8 +374,8 @@ func caller13d() {
}
func caller13e() {
- i := 0 // ERROR "moved to heap: i$"
- var p *int // ERROR "moved to heap: p$"
+ i := 0 // ERROR "moved to heap: i$"
+ var p *int // ERROR "moved to heap: p$"
v := Val{&p}
v.param13(&i)
sink = v
@@ -384,7 +384,7 @@ func caller13e() {
func caller13f() {
i := 0 // ERROR "moved to heap: i$"
var p *int // ERROR "moved to heap: p$"
- v := &Val{&p} // ERROR "&Val literal escapes to heap$"
+ v := &Val{&p} // ERROR "&Val{...} escapes to heap$"
v.param13(&i)
sink = v
}
@@ -400,9 +400,9 @@ func caller13g() {
func caller13h() {
i := 0 // ERROR "moved to heap: i$"
var p *int
- v := &Val{&p} // ERROR "&Val literal does not escape$"
+ v := &Val{&p} // ERROR "&Val{...} does not escape$"
v.param13(&i)
- sink = **v.p // ERROR "\* \(\*v\.p\) escapes to heap"
+ sink = **v.p // ERROR "\* \(\*v\.p\) escapes to heap"
}
type Node struct {
@@ -412,15 +412,15 @@ type Node struct {
var Sink *Node
func f(x *Node) { // ERROR "leaking param content: x"
- Sink = &Node{x.p} // ERROR "&Node literal escapes to heap"
+ Sink = &Node{x.p} // ERROR "&Node{...} escapes to heap"
}
func g(x *Node) *Node { // ERROR "leaking param content: x"
- return &Node{x.p} // ERROR "&Node literal escapes to heap"
+ return &Node{x.p} // ERROR "&Node{...} escapes to heap"
}
func h(x *Node) { // ERROR "leaking param: x"
- y := &Node{x} // ERROR "&Node literal does not escape"
+ y := &Node{x} // ERROR "&Node{...} does not escape"
Sink = g(y)
f(y)
}
diff --git a/test/escape_slice.go b/test/escape_slice.go
index d2cdaa6a013..6ce852e9c55 100644
--- a/test/escape_slice.go
+++ b/test/escape_slice.go
@@ -77,19 +77,19 @@ func slice7() *int {
func slice8() {
i := 0
- s := []*int{&i} // ERROR "literal does not escape"
+ s := []*int{&i} // ERROR "\[\]\*int{...} does not escape"
_ = s
}
func slice9() *int {
i := 0 // ERROR "moved to heap: i"
- s := []*int{&i} // ERROR "literal does not escape"
+ s := []*int{&i} // ERROR "\[\]\*int{...} does not escape"
return s[0]
}
func slice10() []*int {
i := 0 // ERROR "moved to heap: i"
- s := []*int{&i} // ERROR "literal escapes to heap"
+ s := []*int{&i} // ERROR "\[\]\*int{...} escapes to heap"
return s
}
@@ -103,7 +103,7 @@ func slice11() {
func envForDir(dir string) []string { // ERROR "dir does not escape"
env := os.Environ()
- return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string literal does not escape"
+ return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string{...} does not escape"
}
func mergeEnvLists(in, out []string) []string { // ERROR "leaking param content: in" "leaking param content: out" "leaking param: out to result ~r2 level=0"
@@ -160,14 +160,14 @@ var resolveIPAddrTests = []resolveIPAddrTest{
func setupTestData() {
resolveIPAddrTests = append(resolveIPAddrTests,
- []resolveIPAddrTest{ // ERROR "\[\]resolveIPAddrTest literal does not escape"
+ []resolveIPAddrTest{ // ERROR "\[\]resolveIPAddrTest{...} does not escape"
{"ip",
"localhost",
- &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap"
+ &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr{...} escapes to heap"
nil},
{"ip4",
"localhost",
- &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap"
+ &IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr{...} escapes to heap"
nil},
}...)
}
diff --git a/test/escape_struct_param1.go b/test/escape_struct_param1.go
index 70b36191abd..496172c166a 100644
--- a/test/escape_struct_param1.go
+++ b/test/escape_struct_param1.go
@@ -35,27 +35,27 @@ func (u *U) SPPi() *string { // ERROR "leaking param: u to result ~r0 level=2$"
}
func tSPPi() {
- s := "cat" // ERROR "moved to heap: s$"
+ s := "cat" // ERROR "moved to heap: s$"
ps := &s
pps := &ps
- pu := &U{ps, pps} // ERROR "&U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U{...} does not escape$"
Ssink = pu.SPPi()
}
func tiSPP() {
- s := "cat" // ERROR "moved to heap: s$"
+ s := "cat" // ERROR "moved to heap: s$"
ps := &s
pps := &ps
- pu := &U{ps, pps} // ERROR "&U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U{...} does not escape$"
Ssink = *pu.SPP()
}
// BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of ps
func tSP() {
- s := "cat" // ERROR "moved to heap: s$"
- ps := &s // ERROR "moved to heap: ps$"
+ s := "cat" // ERROR "moved to heap: s$"
+ ps := &s // ERROR "moved to heap: ps$"
pps := &ps
- pu := &U{ps, pps} // ERROR "&U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U{...} does not escape$"
Ssink = pu.SP()
}
@@ -114,72 +114,72 @@ func (v *V) UPiSPd() *string { // ERROR "leaking param: v to result ~r0 level=2$
// BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3
func tUPiSPa() {
s1 := "ant"
- s2 := "bat" // ERROR "moved to heap: s2$"
- s3 := "cat" // ERROR "moved to heap: s3$"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s2 := "bat" // ERROR "moved to heap: s2$"
+ s3 := "cat" // ERROR "moved to heap: s3$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
- ps4 := &s4 // ERROR "moved to heap: ps4$"
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps4 := &s4 // ERROR "moved to heap: ps4$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPa() // Ssink = &s3 (only &s3 really escapes)
}
// BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3
func tUPiSPb() {
s1 := "ant"
- s2 := "bat" // ERROR "moved to heap: s2$"
- s3 := "cat" // ERROR "moved to heap: s3$"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s2 := "bat" // ERROR "moved to heap: s2$"
+ s3 := "cat" // ERROR "moved to heap: s3$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
- ps4 := &s4 // ERROR "moved to heap: ps4$"
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps4 := &s4 // ERROR "moved to heap: ps4$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPb() // Ssink = &s3 (only &s3 really escapes)
}
// BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3
func tUPiSPc() {
s1 := "ant"
- s2 := "bat" // ERROR "moved to heap: s2$"
- s3 := "cat" // ERROR "moved to heap: s3$"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s2 := "bat" // ERROR "moved to heap: s2$"
+ s3 := "cat" // ERROR "moved to heap: s3$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
- ps4 := &s4 // ERROR "moved to heap: ps4$"
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps4 := &s4 // ERROR "moved to heap: ps4$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPc() // Ssink = &s3 (only &s3 really escapes)
}
// BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3
func tUPiSPd() {
s1 := "ant"
- s2 := "bat" // ERROR "moved to heap: s2$"
- s3 := "cat" // ERROR "moved to heap: s3$"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s2 := "bat" // ERROR "moved to heap: s2$"
+ s3 := "cat" // ERROR "moved to heap: s3$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
- ps4 := &s4 // ERROR "moved to heap: ps4$"
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps4 := &s4 // ERROR "moved to heap: ps4$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPd() // Ssink = &s3 (only &s3 really escapes)
}
@@ -204,16 +204,16 @@ func tUPiSPPia() {
s1 := "ant"
s2 := "bat"
s3 := "cat"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
ps4 := &s4
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPPia() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -222,16 +222,16 @@ func tUPiSPPib() {
s1 := "ant"
s2 := "bat"
s3 := "cat"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
ps4 := &s4
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPPib() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -240,16 +240,16 @@ func tUPiSPPic() {
s1 := "ant"
s2 := "bat"
s3 := "cat"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
ps4 := &s4
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPPic() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -258,16 +258,16 @@ func tUPiSPPid() {
s1 := "ant"
s2 := "bat"
s3 := "cat"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
ps4 := &s4
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPPid() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -286,13 +286,13 @@ func tUPPiSPPia() {
s3 := "cat"
s4 := "dog"
s5 := "emu"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
ps4 := &s4
ps6 := &s6
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPPiSPPia() // Ssink = *&ps6 = &s6 (only &s6 really escapes)
}
diff --git a/test/escape_struct_param2.go b/test/escape_struct_param2.go
index e42be797935..946397ea9f5 100644
--- a/test/escape_struct_param2.go
+++ b/test/escape_struct_param2.go
@@ -35,27 +35,27 @@ func (u U) SPPi() *string { // ERROR "leaking param: u to result ~r0 level=1$"
}
func tSPPi() {
- s := "cat" // ERROR "moved to heap: s$"
+ s := "cat" // ERROR "moved to heap: s$"
ps := &s
pps := &ps
- pu := &U{ps, pps} // ERROR "&U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U{...} does not escape$"
Ssink = pu.SPPi()
}
func tiSPP() {
- s := "cat" // ERROR "moved to heap: s$"
+ s := "cat" // ERROR "moved to heap: s$"
ps := &s
pps := &ps
- pu := &U{ps, pps} // ERROR "&U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U{...} does not escape$"
Ssink = *pu.SPP()
}
// BAD: need fine-grained analysis to avoid spurious escape of ps
func tSP() {
- s := "cat" // ERROR "moved to heap: s$"
- ps := &s // ERROR "moved to heap: ps$"
+ s := "cat" // ERROR "moved to heap: s$"
+ ps := &s // ERROR "moved to heap: ps$"
pps := &ps
- pu := &U{ps, pps} // ERROR "&U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U{...} does not escape$"
Ssink = pu.SP()
}
@@ -114,72 +114,72 @@ func (v V) UPiSPd() *string { // ERROR "leaking param: v to result ~r0 level=1$"
// BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3
func tUPiSPa() {
s1 := "ant"
- s2 := "bat" // ERROR "moved to heap: s2$"
- s3 := "cat" // ERROR "moved to heap: s3$"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s2 := "bat" // ERROR "moved to heap: s2$"
+ s3 := "cat" // ERROR "moved to heap: s3$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
- ps4 := &s4 // ERROR "moved to heap: ps4$"
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps4 := &s4 // ERROR "moved to heap: ps4$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPa() // Ssink = &s3 (only &s3 really escapes)
}
// BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3
func tUPiSPb() {
s1 := "ant"
- s2 := "bat" // ERROR "moved to heap: s2$"
- s3 := "cat" // ERROR "moved to heap: s3$"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s2 := "bat" // ERROR "moved to heap: s2$"
+ s3 := "cat" // ERROR "moved to heap: s3$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
- ps4 := &s4 // ERROR "moved to heap: ps4$"
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps4 := &s4 // ERROR "moved to heap: ps4$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPb() // Ssink = &s3 (only &s3 really escapes)
}
// BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3
func tUPiSPc() {
s1 := "ant"
- s2 := "bat" // ERROR "moved to heap: s2$"
- s3 := "cat" // ERROR "moved to heap: s3$"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s2 := "bat" // ERROR "moved to heap: s2$"
+ s3 := "cat" // ERROR "moved to heap: s3$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
- ps4 := &s4 // ERROR "moved to heap: ps4$"
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps4 := &s4 // ERROR "moved to heap: ps4$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPc() // Ssink = &s3 (only &s3 really escapes)
}
// BAD: need fine-grained (field-sensitive) analysis to avoid spurious escape of all but &s3
func tUPiSPd() {
s1 := "ant"
- s2 := "bat" // ERROR "moved to heap: s2$"
- s3 := "cat" // ERROR "moved to heap: s3$"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s2 := "bat" // ERROR "moved to heap: s2$"
+ s3 := "cat" // ERROR "moved to heap: s3$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
- ps4 := &s4 // ERROR "moved to heap: ps4$"
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps4 := &s4 // ERROR "moved to heap: ps4$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} escapes to heap$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPd() // Ssink = &s3 (only &s3 really escapes)
}
@@ -204,16 +204,16 @@ func tUPiSPPia() {
s1 := "ant"
s2 := "bat"
s3 := "cat"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
ps4 := &s4
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPPia() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -222,16 +222,16 @@ func tUPiSPPib() {
s1 := "ant"
s2 := "bat"
s3 := "cat"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
ps4 := &s4
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPPib() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -240,16 +240,16 @@ func tUPiSPPic() {
s1 := "ant"
s2 := "bat"
s3 := "cat"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
ps4 := &s4
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPPic() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -258,16 +258,16 @@ func tUPiSPPid() {
s1 := "ant"
s2 := "bat"
s3 := "cat"
- s4 := "dog" // ERROR "moved to heap: s4$"
- s5 := "emu" // ERROR "moved to heap: s5$"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s4 := "dog" // ERROR "moved to heap: s4$"
+ s5 := "emu" // ERROR "moved to heap: s5$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
ps4 := &s4
- ps6 := &s6 // ERROR "moved to heap: ps6$"
+ ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPiSPPid() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -286,13 +286,13 @@ func tUPPiSPPia() { // This test is sensitive to the level cap in function summa
s3 := "cat"
s4 := "dog"
s5 := "emu"
- s6 := "fox" // ERROR "moved to heap: s6$"
+ s6 := "fox" // ERROR "moved to heap: s6$"
ps2 := &s2
ps4 := &s4
ps6 := &s6
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U{...} does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U{...} does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V{...} does not escape$"
Ssink = v.UPPiSPPia() // Ssink = *&ps6 = &s6 (only &s6 really escapes)
}
diff --git a/test/fixedbugs/issue12006.go b/test/fixedbugs/issue12006.go
index c44f2e55471..0a2ef8dad04 100644
--- a/test/fixedbugs/issue12006.go
+++ b/test/fixedbugs/issue12006.go
@@ -144,7 +144,7 @@ func TFooK2() {
a := int32(1) // ERROR "moved to heap: a"
b := "cat"
c := &a
- fs := fakeSlice{3, &[4]interface{}{a, b, c, nil}} // ERROR "a escapes to heap" "b escapes to heap" "&\[4\]interface {} literal does not escape"
+ fs := fakeSlice{3, &[4]interface{}{a, b, c, nil}} // ERROR "a escapes to heap" "b escapes to heap" "&\[4\]interface {}{...} does not escape"
isink = FooK(fs)
}
@@ -169,6 +169,6 @@ func TFooL2() {
a := int32(1) // ERROR "moved to heap: a"
b := "cat"
c := &a
- s := []interface{}{a, b, c} // ERROR "a escapes to heap" "b escapes to heap" "\[\]interface {} literal does not escape"
+ s := []interface{}{a, b, c} // ERROR "a escapes to heap" "b escapes to heap" "\[\]interface {}{...} does not escape"
isink = FooL(s)
}
diff --git a/test/fixedbugs/issue13799.go b/test/fixedbugs/issue13799.go
index 5c574947778..fbdd4c32bc8 100644
--- a/test/fixedbugs/issue13799.go
+++ b/test/fixedbugs/issue13799.go
@@ -162,7 +162,7 @@ func test5(iter int) {
var fn *str
for i := 0; i < maxI; i++ {
// var fn *str // this makes it work, because fn stays off heap
- fn = &str{m} // ERROR "&str literal escapes to heap"
+ fn = &str{m} // ERROR "&str{...} escapes to heap"
recur1(0, fn)
}
@@ -180,7 +180,7 @@ func test6(iter int) {
// var fn *str
for i := 0; i < maxI; i++ {
var fn *str // this makes it work, because fn stays off heap
- fn = &str{m} // ERROR "&str literal does not escape"
+ fn = &str{m} // ERROR "&str{...} does not escape"
recur1(0, fn)
}
diff --git a/test/fixedbugs/issue17645.go b/test/fixedbugs/issue17645.go
index af785eae2a8..95fcecd1e05 100644
--- a/test/fixedbugs/issue17645.go
+++ b/test/fixedbugs/issue17645.go
@@ -12,5 +12,5 @@ type Foo struct {
func main() {
var s []int
- var _ string = append(s, Foo{""}) // ERROR "cannot use .. \(type untyped string\) as type int in field value" "cannot use Foo literal \(type Foo\) as type int in append" "cannot use append\(s\, Foo literal\) \(type \[\]int\) as type string in assignment"
+ var _ string = append(s, Foo{""}) // ERROR "cannot use .. \(type untyped string\) as type int in field value" "cannot use Foo{...} \(type Foo\) as type int in append" "cannot use append\(s\, Foo{...}\) \(type \[\]int\) as type string in assignment"
}
diff --git a/test/fixedbugs/issue21709.go b/test/fixedbugs/issue21709.go
index cc5896ab53e..20be10e7928 100644
--- a/test/fixedbugs/issue21709.go
+++ b/test/fixedbugs/issue21709.go
@@ -16,7 +16,7 @@ var N int
func F1() {
var s S
for i := 0; i < N; i++ {
- fs := []func(){ // ERROR "\[\]func\(\) literal does not escape"
+ fs := []func(){ // ERROR "\[\]func\(\){...} does not escape"
s.Inc, // ERROR "s.Inc does not escape"
}
for _, f := range fs {
@@ -28,7 +28,7 @@ func F1() {
func F2() {
var s S
for i := 0; i < N; i++ {
- for _, f := range []func(){ // ERROR "\[\]func\(\) literal does not escape"
+ for _, f := range []func(){ // ERROR "\[\]func\(\){...} does not escape"
s.Inc, // ERROR "s.Inc does not escape"
} {
f()
diff --git a/test/fixedbugs/issue23732.go b/test/fixedbugs/issue23732.go
index be17bf4f61b..5e63eb20749 100644
--- a/test/fixedbugs/issue23732.go
+++ b/test/fixedbugs/issue23732.go
@@ -24,19 +24,19 @@ func main() {
_ = Foo{
1,
2,
- 3, // ERROR "too few values in Foo literal"
+ 3, // ERROR "too few values in Foo{...}"
}
_ = Foo{
1,
2,
3,
- Bar{"A", "B"}, // ERROR "too many values in Bar literal"
+ Bar{"A", "B"}, // ERROR "too many values in Bar{...}"
}
_ = Foo{
1,
2,
- Bar{"A", "B"}, // ERROR "too many values in Bar literal" "too few values in Foo literal"
+ Bar{"A", "B"}, // ERROR "too many values in Bar{...}" "too few values in Foo{...}"
}
}
diff --git a/test/fixedbugs/issue26855.go b/test/fixedbugs/issue26855.go
index d5b95ddbf1b..144e4415f7f 100644
--- a/test/fixedbugs/issue26855.go
+++ b/test/fixedbugs/issue26855.go
@@ -20,9 +20,9 @@ type P struct {
type T struct{}
var _ = S{
- f: &T{}, // ERROR "cannot use &T literal"
+ f: &T{}, // ERROR "cannot use &T{}"
}
var _ = P{
- f: T{}, // ERROR "cannot use T literal"
+ f: T{}, // ERROR "cannot use T{}"
}
diff --git a/test/fixedbugs/issue30898.go b/test/fixedbugs/issue30898.go
index 012d5a2634d..b6376d3f9e7 100644
--- a/test/fixedbugs/issue30898.go
+++ b/test/fixedbugs/issue30898.go
@@ -15,5 +15,5 @@ func debugf(format string, args ...interface{}) { // ERROR "can inline debugf" "
func bar() { // ERROR "can inline bar"
value := 10
- debugf("value is %d", value) // ERROR "inlining call to debugf" "value does not escape" "\[\]interface {} literal does not escape"
+ debugf("value is %d", value) // ERROR "inlining call to debugf" "value does not escape" "\[\]interface {}{...} does not escape"
}
diff --git a/test/fixedbugs/issue31573.go b/test/fixedbugs/issue31573.go
index c9ea84bbae9..005910e00d1 100644
--- a/test/fixedbugs/issue31573.go
+++ b/test/fixedbugs/issue31573.go
@@ -14,18 +14,18 @@ func g() {
defer f(new(int), new(int)) // ERROR "... argument does not escape$" "new\(int\) does not escape$"
defer f(nil...)
- defer f([]*int{}...) // ERROR "\[\]\*int literal does not escape$"
- defer f([]*int{new(int)}...) // ERROR "\[\]\*int literal does not escape$" "new\(int\) does not escape$"
- defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal does not escape$" "new\(int\) does not escape$"
+ defer f([]*int{}...) // ERROR "\[\]\*int{} does not escape$"
+ defer f([]*int{new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) does not escape$"
+ defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) does not escape$"
go f()
go f(new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$"
go f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$"
go f(nil...)
- go f([]*int{}...) // ERROR "\[\]\*int literal escapes to heap$"
- go f([]*int{new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$"
- go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$"
+ go f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$"
+ go f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$"
+ go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$"
for {
defer f()
@@ -33,17 +33,17 @@ func g() {
defer f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$"
defer f(nil...)
- defer f([]*int{}...) // ERROR "\[\]\*int literal escapes to heap$"
- defer f([]*int{new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$"
- defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$"
+ defer f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$"
+ defer f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$"
+ defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$"
go f()
go f(new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$"
go f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$"
go f(nil...)
- go f([]*int{}...) // ERROR "\[\]\*int literal escapes to heap$"
- go f([]*int{new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$"
- go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int literal escapes to heap$" "new\(int\) escapes to heap$"
+ go f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$"
+ go f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$"
+ go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$"
}
}
diff --git a/test/fixedbugs/issue38745.go b/test/fixedbugs/issue38745.go
new file mode 100644
index 00000000000..21bd1ff3a7c
--- /dev/null
+++ b/test/fixedbugs/issue38745.go
@@ -0,0 +1,19 @@
+// errorcheck
+
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type t struct{ x int }
+
+func f1() {
+ t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)"
+ t{x: 1}.M() // ERROR "t{...}.M undefined \(type t has no field or method M\)"
+}
+
+func f2() (*t, error) {
+ // BAD: should report undefined error only.
+ return t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)" "not enough arguments to return"
+}
diff --git a/test/fixedbugs/issue39292.go b/test/fixedbugs/issue39292.go
index 1be88653e91..7dac2e5fc6b 100644
--- a/test/fixedbugs/issue39292.go
+++ b/test/fixedbugs/issue39292.go
@@ -12,18 +12,18 @@ func (t) f() {
}
func x() {
- x := t{}.f // ERROR "t literal.f escapes to heap"
+ x := t{}.f // ERROR "t{}.f escapes to heap"
x()
}
func y() {
var i int // ERROR "moved to heap: i"
- y := (&t{&i}).f // ERROR "\(&t literal\).f escapes to heap" "&t literal escapes to heap"
+ y := (&t{&i}).f // ERROR "\(&t{...}\).f escapes to heap" "&t{...} escapes to heap"
y()
}
func z() {
var i int // ERROR "moved to heap: i"
- z := t{&i}.f // ERROR "t literal.f escapes to heap"
+ z := t{&i}.f // ERROR "t{...}.f escapes to heap"
z()
}
diff --git a/test/fixedbugs/issue41247.go b/test/fixedbugs/issue41247.go
index 2df919c9e6c..b8bd81274f8 100644
--- a/test/fixedbugs/issue41247.go
+++ b/test/fixedbugs/issue41247.go
@@ -7,5 +7,5 @@
package p
func f() [2]int {
- return [...]int{2: 0} // ERROR "cannot use \[\.\.\.\]int literal \(type \[3\]int\)"
+ return [...]int{2: 0} // ERROR "cannot use \[\.\.\.\]int{...} \(type \[3\]int\)"
}
diff --git a/test/fixedbugs/issue7921.go b/test/fixedbugs/issue7921.go
index a8efc8dd9ef..5dce557ca33 100644
--- a/test/fixedbugs/issue7921.go
+++ b/test/fixedbugs/issue7921.go
@@ -18,12 +18,12 @@ func bufferNotEscape() string {
// can be stack-allocated.
var b bytes.Buffer
b.WriteString("123")
- b.Write([]byte{'4'}) // ERROR "\[\]byte literal does not escape$"
+ b.Write([]byte{'4'}) // ERROR "\[\]byte{...} does not escape$"
return b.String() // ERROR "inlining call to bytes.\(\*Buffer\).String$" "string\(bytes.b.buf\[bytes.b.off:\]\) escapes to heap$"
}
func bufferNoEscape2(xs []string) int { // ERROR "xs does not escape$"
- b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$"
+ b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer{...} does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$"
for _, x := range xs {
b.WriteString(x)
}
@@ -31,7 +31,7 @@ func bufferNoEscape2(xs []string) int { // ERROR "xs does not escape$"
}
func bufferNoEscape3(xs []string) string { // ERROR "xs does not escape$"
- b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$"
+ b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer{...} does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$"
for _, x := range xs {
b.WriteString(x)
b.WriteByte(',')
@@ -41,13 +41,13 @@ func bufferNoEscape3(xs []string) string { // ERROR "xs does not escape$"
func bufferNoEscape4() []byte {
var b bytes.Buffer
- b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m·3\]$" "inlining call to bytes.\(\*Buffer\).Grow$"
+ b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m·3\]$" "inlining call to bytes.\(\*Buffer\).Grow$"
useBuffer(&b)
return b.Bytes() // ERROR "inlining call to bytes.\(\*Buffer\).Bytes$"
}
func bufferNoEscape5() { // ERROR "can inline bufferNoEscape5$"
- b := bytes.NewBuffer(make([]byte, 0, 128)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 128\) does not escape$" "inlining call to bytes.NewBuffer$"
+ b := bytes.NewBuffer(make([]byte, 0, 128)) // ERROR "&bytes.Buffer{...} does not escape$" "make\(\[\]byte, 0, 128\) does not escape$" "inlining call to bytes.NewBuffer$"
useBuffer(b)
}
diff --git a/test/inline_variadic.go b/test/inline_variadic.go
index fcc1cff1e83..687048a1922 100644
--- a/test/inline_variadic.go
+++ b/test/inline_variadic.go
@@ -14,6 +14,6 @@ func head(xs ...string) string { // ERROR "can inline head" "leaking param: xs t
}
func f() string { // ERROR "can inline f"
- x := head("hello", "world") // ERROR "inlining call to head" "\[\]string literal does not escape"
+ x := head("hello", "world") // ERROR "inlining call to head" "\[\]string{...} does not escape"
return x
}
From 806f478499b57c5167fb5301101961b7563903d2 Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Wed, 9 Sep 2020 16:25:48 +0700
Subject: [PATCH 0046/1082] cmd/compile: don't report not enough args error if
call is undefined
Fixes #38745
Change-Id: I2fbd8b512a8cf911b81a087162c74416116efea5
Reviewed-on: https://go-review.googlesource.com/c/go/+/253678
Run-TryBot: Cuong Manh Le
TryBot-Result: Gobot Gobot
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/typecheck.go | 2 +-
test/ddd1.go | 2 +-
test/fixedbugs/issue38745.go | 3 +--
3 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index dec4b96fc4c..fb169cfec84 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -2667,7 +2667,7 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *types.Type, nl Nodes,
return
notenough:
- if n == nil || !n.Diag() {
+ if n == nil || (!n.Diag() && n.Type != nil) {
details := errorDetails(nl, tstruct, isddd)
if call != nil {
// call is the expression being called, not the overall call.
diff --git a/test/ddd1.go b/test/ddd1.go
index 2c7e83e3745..9857814648b 100644
--- a/test/ddd1.go
+++ b/test/ddd1.go
@@ -29,7 +29,7 @@ var (
_ = sum(tuple())
_ = sum(tuple()...) // ERROR "multiple-value"
_ = sum3(tuple())
- _ = sum3(tuple()...) // ERROR "multiple-value" "not enough"
+ _ = sum3(tuple()...) // ERROR "multiple-value"
)
type T []T
diff --git a/test/fixedbugs/issue38745.go b/test/fixedbugs/issue38745.go
index 21bd1ff3a7c..83a3bc6fad6 100644
--- a/test/fixedbugs/issue38745.go
+++ b/test/fixedbugs/issue38745.go
@@ -14,6 +14,5 @@ func f1() {
}
func f2() (*t, error) {
- // BAD: should report undefined error only.
- return t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)" "not enough arguments to return"
+ return t{}.M() // ERROR "t{}.M undefined \(type t has no field or method M\)"
}
From 92b2b8860dcc28461198c6125fbae2383161d2e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Mart=C3=AD?=
Date: Sat, 15 Aug 2020 16:20:50 +0200
Subject: [PATCH 0047/1082] cmd/go: avoid flag.FlagSet.VisitAll at init time
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We want to error early if GOFLAGS contains any flag that isn't known to
any cmd/go command. Thus, at init time we would recursively use VisitAll
on each of the flagsets to populate a map of all registered flags.
This was unfortunate, as populating said map constituted a whole 5% of
the run-time of 'go env GOARCH'. This is because VisitAll is pretty
expensive; it copies all the maps from the flagset's map to a slice,
sorts the slice, then does one callback per flag.
First, this was a bit wasteful. We only ever needed to query the
knownFlag map if GOFLAGS wasn't empty. If it's empty, there's no work to
do, thus we can skip the map populating work.
Second and most important, we don't actually need the map at all. A
flag.FlagSet already has a Lookup method, so we can simply recursively
call those methods for each flag in GOFLAGS. Add a hasFlag func to make
that evident.
This mechanism is different; its upfront cost is none, but it will
likely mean a handful of map lookups for each flag in GOFLAGS. However,
that tradeoff is worth it; we don't expect GOFLAGS to contain thousands
of flags. The most likely scenario is less than a dozen flags, in which
case constructing a "unified" map is not at all a net win.
One possible reason the previous mechanism was that way could be
AddKnownFlag. Thankfully, the one and only use of that API was removed
last year when Bryan cleaned up flag parsing in cmd/go.
The wins for the existing benchmark with an empty GOFLAGS are
significant:
name old time/op new time/op delta
ExecGoEnv-8 575µs ± 1% 549µs ± 2% -4.44% (p=0.000 n=7+8)
name old sys-time/op new sys-time/op delta
ExecGoEnv-8 1.69ms ± 1% 1.68ms ± 2% ~ (p=0.281 n=7+8)
name old user-time/op new user-time/op delta
ExecGoEnv-8 1.80ms ± 1% 1.66ms ± 2% -8.09% (p=0.000 n=7+8)
To prove that a relatively large number of GOFLAGS isn't getting
noticeably slower, we measured that as well, via benchcmd and GOFLAGS
containing 50 valid flags:
GOFLAGS=$(yes -- -race | sed 50q) benchcmd -n 500 GoEnvGOFLAGS go env GOARCH
And the result, while noisy, shows no noticeable difference (note that
it measures 3ms instead of 0.6ms since it's sequential):
name old time/op new time/op delta
GoEnvGOFLAGS 3.04ms ±32% 3.03ms ±35% ~ (p=0.156 n=487+481)
Finally, we've improved the existing Go benchmark. Now it's parallel,
and it also reports sys-time and user-time, which are useful metrics.
Change-Id: I9b4551415cedf2f819eb184a02324b8bd919e2bd
Reviewed-on: https://go-review.googlesource.com/c/go/+/248757
Reviewed-by: Bryan C. Mills
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
---
src/cmd/go/init_test.go | 26 +++++++++++++-------
src/cmd/go/internal/base/base.go | 14 +++++++++++
src/cmd/go/internal/base/goflags.go | 37 ++++++-----------------------
3 files changed, 38 insertions(+), 39 deletions(-)
diff --git a/src/cmd/go/init_test.go b/src/cmd/go/init_test.go
index ed90a778416..5a5cbe52936 100644
--- a/src/cmd/go/init_test.go
+++ b/src/cmd/go/init_test.go
@@ -7,6 +7,7 @@ package main_test
import (
"internal/testenv"
"os/exec"
+ "sync/atomic"
"testing"
)
@@ -15,20 +16,27 @@ import (
// the benchmark if any changes were done.
func BenchmarkExecGoEnv(b *testing.B) {
testenv.MustHaveExec(b)
- b.StopTimer()
gotool, err := testenv.GoTool()
if err != nil {
b.Fatal(err)
}
- for i := 0; i < b.N; i++ {
- cmd := exec.Command(gotool, "env", "GOARCH")
- b.StartTimer()
- err := cmd.Run()
- b.StopTimer()
+ // We collect extra metrics.
+ var n, userTime, systemTime int64
- if err != nil {
- b.Fatal(err)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ cmd := exec.Command(gotool, "env", "GOARCH")
+
+ if err := cmd.Run(); err != nil {
+ b.Fatal(err)
+ }
+ atomic.AddInt64(&n, 1)
+ atomic.AddInt64(&userTime, int64(cmd.ProcessState.UserTime()))
+ atomic.AddInt64(&systemTime, int64(cmd.ProcessState.SystemTime()))
}
- }
+ })
+ b.ReportMetric(float64(userTime)/float64(n), "user-ns/op")
+ b.ReportMetric(float64(systemTime)/float64(n), "sys-ns/op")
}
diff --git a/src/cmd/go/internal/base/base.go b/src/cmd/go/internal/base/base.go
index db3ebef9338..004588c7320 100644
--- a/src/cmd/go/internal/base/base.go
+++ b/src/cmd/go/internal/base/base.go
@@ -56,6 +56,20 @@ var Go = &Command{
// Commands initialized in package main
}
+// hasFlag reports whether a command or any of its subcommands contain the given
+// flag.
+func hasFlag(c *Command, name string) bool {
+ if f := c.Flag.Lookup(name); f != nil {
+ return true
+ }
+ for _, sub := range c.Commands {
+ if hasFlag(sub, name) {
+ return true
+ }
+ }
+ return false
+}
+
// LongName returns the command's long name: all the words in the usage line between "go" and a flag or argument,
func (c *Command) LongName() string {
name := c.UsageLine
diff --git a/src/cmd/go/internal/base/goflags.go b/src/cmd/go/internal/base/goflags.go
index 34766134b09..f11f9a5d336 100644
--- a/src/cmd/go/internal/base/goflags.go
+++ b/src/cmd/go/internal/base/goflags.go
@@ -13,15 +13,7 @@ import (
"cmd/go/internal/cfg"
)
-var (
- goflags []string // cached $GOFLAGS list; can be -x or --x form
- knownFlag = make(map[string]bool) // flags allowed to appear in $GOFLAGS; no leading dashes
-)
-
-// AddKnownFlag adds name to the list of known flags for use in $GOFLAGS.
-func AddKnownFlag(name string) {
- knownFlag[name] = true
-}
+var goflags []string // cached $GOFLAGS list; can be -x or --x form
// GOFLAGS returns the flags from $GOFLAGS.
// The list can be assumed to contain one string per flag,
@@ -38,22 +30,12 @@ func InitGOFLAGS() {
return
}
- // Build list of all flags for all commands.
- // If no command has that flag, then we report the problem.
- // This catches typos while still letting users record flags in GOFLAGS
- // that only apply to a subset of go commands.
- // Commands using CustomFlags can report their flag names
- // by calling AddKnownFlag instead.
- var walkFlags func(*Command)
- walkFlags = func(cmd *Command) {
- for _, sub := range cmd.Commands {
- walkFlags(sub)
- }
- cmd.Flag.VisitAll(func(f *flag.Flag) {
- knownFlag[f.Name] = true
- })
+ goflags = strings.Fields(cfg.Getenv("GOFLAGS"))
+ if len(goflags) == 0 {
+ // nothing to do; avoid work on later InitGOFLAGS call
+ goflags = []string{}
+ return
}
- walkFlags(Go)
// Ignore bad flag in go env and go bug, because
// they are what people reach for when debugging
@@ -61,11 +43,6 @@ func InitGOFLAGS() {
// (Both will show the GOFLAGS setting if let succeed.)
hideErrors := cfg.CmdName == "env" || cfg.CmdName == "bug"
- goflags = strings.Fields(cfg.Getenv("GOFLAGS"))
- if goflags == nil {
- goflags = []string{} // avoid work on later InitGOFLAGS call
- }
-
// Each of the words returned by strings.Fields must be its own flag.
// To set flag arguments use -x=value instead of -x value.
// For boolean flags, -x is fine instead of -x=true.
@@ -85,7 +62,7 @@ func InitGOFLAGS() {
if i := strings.Index(name, "="); i >= 0 {
name = name[:i]
}
- if !knownFlag[name] {
+ if !hasFlag(Go, name) {
if hideErrors {
continue
}
From b3ef90ec7304a28b89f616ced20b09f56be30cc4 Mon Sep 17 00:00:00 2001
From: Carlos Alexandro Becker
Date: Fri, 11 Sep 2020 22:16:47 +0000
Subject: [PATCH 0048/1082] encoding/json: implement Is on SyntaxError
Allows users to check:
errors.Is(err, &json.SyntaxError{})
which is the recommended way of checking for kinds of errors.
Change-Id: I20dc805f20212765e9936a82d9cb7822e73ec4ef
GitHub-Last-Rev: e2627ccf8e2a00cc3459bb9fee86c3c8675a33af
GitHub-Pull-Request: golang/go#41210
Reviewed-on: https://go-review.googlesource.com/c/go/+/253037
Reviewed-by: Emmanuel Odeke
Run-TryBot: Emmanuel Odeke
TryBot-Result: Gobot Gobot
---
src/encoding/json/scanner.go | 6 ++++++
src/encoding/json/scanner_test.go | 9 +++++++++
2 files changed, 15 insertions(+)
diff --git a/src/encoding/json/scanner.go b/src/encoding/json/scanner.go
index 9dc1903e2db..05218f9cc3e 100644
--- a/src/encoding/json/scanner.go
+++ b/src/encoding/json/scanner.go
@@ -49,6 +49,12 @@ type SyntaxError struct {
func (e *SyntaxError) Error() string { return e.msg }
+// Is returns true if target is a SyntaxError.
+func (e *SyntaxError) Is(target error) bool {
+ _, ok := target.(*SyntaxError)
+ return ok
+}
+
// A scanner is a JSON scanning state machine.
// Callers call scan.reset and then pass bytes in one at a time
// by calling scan.step(&scan, c) for each byte.
diff --git a/src/encoding/json/scanner_test.go b/src/encoding/json/scanner_test.go
index 3737516a450..c12d9bf3d73 100644
--- a/src/encoding/json/scanner_test.go
+++ b/src/encoding/json/scanner_test.go
@@ -6,6 +6,8 @@ package json
import (
"bytes"
+ "errors"
+ "fmt"
"math"
"math/rand"
"reflect"
@@ -201,6 +203,13 @@ func TestIndentErrors(t *testing.T) {
}
}
+func TestSyntaxErrorIs(t *testing.T) {
+ err := fmt.Errorf("apackage: %w: failed to parse struct", &SyntaxError{"some error", 43})
+ if !errors.Is(err, &SyntaxError{}) {
+ t.Fatalf("%v should be unwrapped to a SyntaxError", err)
+ }
+}
+
func diff(t *testing.T, a, b []byte) {
for i := 0; ; i++ {
if i >= len(a) || i >= len(b) || a[i] != b[i] {
From 95bb00d1088767ed14e3bd1a5f533a690d619a5f Mon Sep 17 00:00:00 2001
From: Carlos Alexandro Becker
Date: Sun, 13 Sep 2020 02:12:02 +0000
Subject: [PATCH 0049/1082] encoding/json: implement Is on all errors
Allows users to check:
errors.Is(err, &UnmarshalTypeError{})
errors.Is(err, &UnmarshalFieldError{})
errors.Is(err, &InvalidUnmarshalError{})
errors.Is(err, &UnsupportedValueError{})
errors.Is(err, &MarshalerError{})
which is the recommended way of checking for kinds of errors.
SyntaxError.Is was implemented in CL 253037.
As and Unwrap relevant methods will be added in future CLs.
Change-Id: I1f8a503b8fdc0f3afdfe9669a91f3af8d960e028
GitHub-Last-Rev: 930cda5384c987a0b31f277ba3b4ab690ea74ac3
GitHub-Pull-Request: golang/go#41360
Reviewed-on: https://go-review.googlesource.com/c/go/+/254537
Run-TryBot: Emmanuel Odeke
TryBot-Result: Gobot Gobot
Reviewed-by: Emmanuel Odeke
Trust: Emmanuel Odeke
---
src/encoding/json/decode.go | 18 ++++++++++++++++++
src/encoding/json/decode_test.go | 31 +++++++++++++++++++++++++++++++
src/encoding/json/encode.go | 12 ++++++++++++
src/encoding/json/encode_test.go | 24 +++++++++++++++++++++++-
4 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index 86d8a69db7e..1b006ffb178 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -136,6 +136,12 @@ func (e *UnmarshalTypeError) Error() string {
return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}
+// Is returns true if target is a UnmarshalTypeError.
+func (e *UnmarshalTypeError) Is(target error) bool {
+ _, ok := target.(*UnmarshalTypeError)
+ return ok
+}
+
// An UnmarshalFieldError describes a JSON object key that
// led to an unexported (and therefore unwritable) struct field.
//
@@ -150,12 +156,24 @@ func (e *UnmarshalFieldError) Error() string {
return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String()
}
+// Is returns true if target is a UnmarshalFieldError.
+func (e *UnmarshalFieldError) Is(target error) bool {
+ _, ok := target.(*UnmarshalFieldError)
+ return ok
+}
+
// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal.
// (The argument to Unmarshal must be a non-nil pointer.)
type InvalidUnmarshalError struct {
Type reflect.Type
}
+// Is returns true if target is a InvalidUnmarshalError.
+func (e *InvalidUnmarshalError) Is(target error) bool {
+ _, ok := target.(*InvalidUnmarshalError)
+ return ok
+}
+
func (e *InvalidUnmarshalError) Error() string {
if e.Type == nil {
return "json: Unmarshal(nil)"
diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go
index 219e845c7b0..b707dcfa992 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -2572,3 +2572,34 @@ func TestUnmarshalMaxDepth(t *testing.T) {
}
}
}
+
+func TestInvalidUnmarshalErrorIs(t *testing.T) {
+ err := fmt.Errorf("apackage: %w: failed to parse struct", &InvalidUnmarshalError{reflect.TypeOf("a")})
+ if !errors.Is(err, &InvalidUnmarshalError{}) {
+ t.Fatalf("%v should be unwrapped to a InvalidUnmarshalError", err)
+ }
+}
+
+func TestUnmarshalFieldErrorIs(t *testing.T) {
+ err := fmt.Errorf("apackage: %w: failed to parse struct", &UnmarshalFieldError{
+ Key: "foo",
+ Type: reflect.TypeOf("a"),
+ Field: reflect.StructField{Name: "b"},
+ })
+ if !errors.Is(err, &UnmarshalFieldError{}) {
+ t.Fatalf("%v should be unwrapped to a UnmarshalFieldError", err)
+ }
+}
+
+func TestUnmarshalTypeErrorIs(t *testing.T) {
+ err := fmt.Errorf("apackage: %w: failed to parse struct", &UnmarshalTypeError{
+ Value: "foo",
+ Type: reflect.TypeOf("a"),
+ Offset: 1,
+ Struct: "Foo",
+ Field: "Bar",
+ })
+ if !errors.Is(err, &UnmarshalTypeError{}) {
+ t.Fatalf("%v should be unwrapped to a UnmarshalTypeError", err)
+ }
+}
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index 578d551102d..8e6b342b595 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -245,6 +245,12 @@ func (e *UnsupportedValueError) Error() string {
return "json: unsupported value: " + e.Str
}
+// Is returns true if target is a UnsupportedValueError.
+func (e *UnsupportedValueError) Is(target error) bool {
+ _, ok := target.(*UnsupportedValueError)
+ return ok
+}
+
// Before Go 1.2, an InvalidUTF8Error was returned by Marshal when
// attempting to encode a string value with invalid UTF-8 sequences.
// As of Go 1.2, Marshal instead coerces the string to valid UTF-8 by
@@ -279,6 +285,12 @@ func (e *MarshalerError) Error() string {
// Unwrap returns the underlying error.
func (e *MarshalerError) Unwrap() error { return e.Err }
+// Is returns true if target is a MarshalerError.
+func (e *MarshalerError) Is(target error) bool {
+ _, ok := target.(*MarshalerError)
+ return ok
+}
+
var hex = "0123456789abcdef"
// An encodeState encodes JSON into a bytes.Buffer.
diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go
index 7290eca06f0..90826a7f476 100644
--- a/src/encoding/json/encode_test.go
+++ b/src/encoding/json/encode_test.go
@@ -7,6 +7,7 @@ package json
import (
"bytes"
"encoding"
+ "errors"
"fmt"
"log"
"math"
@@ -211,7 +212,7 @@ var unsupportedValues = []interface{}{
func TestUnsupportedValues(t *testing.T) {
for _, v := range unsupportedValues {
if _, err := Marshal(v); err != nil {
- if _, ok := err.(*UnsupportedValueError); !ok {
+ if !errors.Is(err, &UnsupportedValueError{}) {
t.Errorf("for %v, got %T want UnsupportedValueError", v, err)
}
} else {
@@ -1155,3 +1156,24 @@ func TestMarshalerError(t *testing.T) {
}
}
}
+
+func TestMarshalerErrorIs(t *testing.T) {
+ err := fmt.Errorf("apackage: %w: failed to parse struct", &MarshalerError{
+ reflect.TypeOf("a"),
+ fmt.Errorf("something"),
+ "TestMarshalerErrorIs",
+ })
+ if !errors.Is(err, &MarshalerError{}) {
+ t.Fatalf("%v should be unwrapped to a MarshalerError", err)
+ }
+}
+
+func TestUnsupportedValueErrorIs(t *testing.T) {
+ err := fmt.Errorf("apackage: %w: failed to parse struct", &UnsupportedValueError{
+ Value: reflect.Value{},
+ Str: "Foo",
+ })
+ if !errors.Is(err, &UnsupportedValueError{}) {
+ t.Fatalf("%v should be unwrapped to a UnsupportedValueError", err)
+ }
+}
From 1f4521669416a2e14fb0b84481447f4a93f19878 Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Sat, 12 Sep 2020 01:57:27 +0700
Subject: [PATCH 0050/1082] cmd/compile: attach OVARLIVE nodes to OCALLxxx
So we can insert theses OVARLIVE nodes right after OpStaticCall in SSA.
This helps fixing issue that unsafe-uintptr arguments are not kept alive
during return statement, or can be kept alive longer than expected.
Fixes #24491
Change-Id: Ic04a5d1bbb5c90dcfae65bd95cdd1da393a66800
Reviewed-on: https://go-review.googlesource.com/c/go/+/254397
Run-TryBot: Cuong Manh Le
TryBot-Result: Gobot Gobot
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/order.go | 14 ++----
src/cmd/compile/internal/gc/ssa.go | 2 +
src/cmd/compile/internal/gc/syntax.go | 4 +-
.../{issue24491.go => issue24491a.go} | 11 ++++-
test/fixedbugs/issue24491b.go | 46 +++++++++++++++++++
5 files changed, 63 insertions(+), 14 deletions(-)
rename test/fixedbugs/{issue24491.go => issue24491a.go} (85%)
create mode 100644 test/fixedbugs/issue24491b.go
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
index 412f073a8d4..341f4ee66fc 100644
--- a/src/cmd/compile/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -288,20 +288,13 @@ func (o *Order) popTemp(mark ordermarker) {
o.temp = o.temp[:mark]
}
-// cleanTempNoPop emits VARKILL and if needed VARLIVE instructions
-// to *out for each temporary above the mark on the temporary stack.
+// cleanTempNoPop emits VARKILL instructions to *out
+// for each temporary above the mark on the temporary stack.
// It does not pop the temporaries from the stack.
func (o *Order) cleanTempNoPop(mark ordermarker) []*Node {
var out []*Node
for i := len(o.temp) - 1; i >= int(mark); i-- {
n := o.temp[i]
- if n.Name.Keepalive() {
- n.Name.SetKeepalive(false)
- n.Name.SetAddrtaken(true) // ensure SSA keeps the n variable
- live := nod(OVARLIVE, n, nil)
- live = typecheck(live, ctxStmt)
- out = append(out, live)
- }
kill := nod(OVARKILL, n, nil)
kill = typecheck(kill, ctxStmt)
out = append(out, kill)
@@ -500,8 +493,9 @@ func (o *Order) call(n *Node) {
// still alive when we pop the temp stack.
if arg.Op == OCONVNOP && arg.Left.Type.IsUnsafePtr() {
x := o.copyExpr(arg.Left, arg.Left.Type, false)
- x.Name.SetKeepalive(true)
arg.Left = x
+ x.Name.SetAddrtaken(true) // ensure SSA keeps the x variable
+ n.Nbody.Append(typecheck(nod(OVARLIVE, x, nil), ctxStmt))
n.SetNeedsWrapper(true)
}
}
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 89644cd3f26..3bdb5b0b9f9 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -4498,6 +4498,8 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
}
s.vars[&memVar] = call
+ // Insert OVARLIVE nodes
+ s.stmtList(n.Nbody)
// Finish block for defers
if k == callDefer || k == callDeferStack {
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index 5580f789c5b..9592b7484ce 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -374,7 +374,6 @@ const (
nameReadonly
nameByval // is the variable captured by value or by reference
nameNeedzero // if it contains pointers, needs to be zeroed on function entry
- nameKeepalive // mark value live across unknown assembly call
nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
nameUsed // for variable declared and not used error
nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original at n.Name.Defn
@@ -391,7 +390,6 @@ func (n *Name) Captured() bool { return n.flags&nameCaptured != 0 }
func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 }
func (n *Name) Byval() bool { return n.flags&nameByval != 0 }
func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 0 }
-func (n *Name) Keepalive() bool { return n.flags&nameKeepalive != 0 }
func (n *Name) AutoTemp() bool { return n.flags&nameAutoTemp != 0 }
func (n *Name) Used() bool { return n.flags&nameUsed != 0 }
func (n *Name) IsClosureVar() bool { return n.flags&nameIsClosureVar != 0 }
@@ -407,7 +405,6 @@ func (n *Name) SetCaptured(b bool) { n.flags.set(nameCaptured, b) }
func (n *Name) SetReadonly(b bool) { n.flags.set(nameReadonly, b) }
func (n *Name) SetByval(b bool) { n.flags.set(nameByval, b) }
func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) }
-func (n *Name) SetKeepalive(b bool) { n.flags.set(nameKeepalive, b) }
func (n *Name) SetAutoTemp(b bool) { n.flags.set(nameAutoTemp, b) }
func (n *Name) SetUsed(b bool) { n.flags.set(nameUsed, b) }
func (n *Name) SetIsClosureVar(b bool) { n.flags.set(nameIsClosureVar, b) }
@@ -707,6 +704,7 @@ const (
// Prior to walk, they are: Left(List), where List is all regular arguments.
// After walk, List is a series of assignments to temporaries,
// and Rlist is an updated set of arguments.
+ // Nbody is all OVARLIVE nodes that are attached to OCALLxxx.
// TODO(josharian/khr): Use Ninit instead of List for the assignments to temporaries. See CL 114797.
OCALLFUNC // Left(List/Rlist) (function call f(args))
OCALLMETH // Left(List/Rlist) (direct method call x.Method(args))
diff --git a/test/fixedbugs/issue24491.go b/test/fixedbugs/issue24491a.go
similarity index 85%
rename from test/fixedbugs/issue24491.go
rename to test/fixedbugs/issue24491a.go
index 47033687937..148134d1870 100644
--- a/test/fixedbugs/issue24491.go
+++ b/test/fixedbugs/issue24491a.go
@@ -23,12 +23,18 @@ func setup() unsafe.Pointer {
//go:noinline
//go:uintptrescapes
-func test(s string, p uintptr) {
+func test(s string, p uintptr) int {
runtime.GC()
if *(*string)(unsafe.Pointer(p)) != "ok" {
panic(s + " return unexpected result")
}
done <- true
+ return 0
+}
+
+//go:noinline
+func f() int {
+ return test("return", uintptr(setup()))
}
func main() {
@@ -42,4 +48,7 @@ func main() {
defer test("defer", uintptr(setup()))
}()
<-done
+
+ f()
+ <-done
}
diff --git a/test/fixedbugs/issue24491b.go b/test/fixedbugs/issue24491b.go
new file mode 100644
index 00000000000..5f4a2f233e0
--- /dev/null
+++ b/test/fixedbugs/issue24491b.go
@@ -0,0 +1,46 @@
+// run
+
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This test makes sure unsafe-uintptr arguments are not
+// kept alive longer than expected.
+
+package main
+
+import (
+ "runtime"
+ "sync/atomic"
+ "unsafe"
+)
+
+var done uint32
+
+func setup() unsafe.Pointer {
+ s := "ok"
+ runtime.SetFinalizer(&s, func(p *string) { atomic.StoreUint32(&done, 1) })
+ return unsafe.Pointer(&s)
+}
+
+//go:noinline
+//go:uintptrescapes
+func before(p uintptr) int {
+ runtime.GC()
+ if atomic.LoadUint32(&done) != 0 {
+ panic("GC early")
+ }
+ return 0
+}
+
+func after() int {
+ runtime.GC()
+ if atomic.LoadUint32(&done) == 0 {
+ panic("GC late")
+ }
+ return 0
+}
+
+func main() {
+ _ = before(uintptr(setup())) + after()
+}
From 5f1b12bfbeb04ca6dbecbf064f5e5a42d8ba4b5a Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Sat, 12 Sep 2020 07:19:22 +0700
Subject: [PATCH 0051/1082] cmd/compile: remove nodeNeedsWrapper flag
CL 254397 attached OVARLIVE nodes to OCALLxxx nodes Nbody.
The NeedsWrapper flag is now redundant with n.Nbody.Len() > 0
condition, so use that condition instead and remove the flag.
Passes toolstash-check.
Change-Id: Iebc3e674d3c0040a876ca4be05025943d2b4fb31
Reviewed-on: https://go-review.googlesource.com/c/go/+/254398
Run-TryBot: Cuong Manh Le
TryBot-Result: Gobot Gobot
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/order.go | 1 -
src/cmd/compile/internal/gc/syntax.go | 41 +++++++++------------------
src/cmd/compile/internal/gc/walk.go | 7 +++--
3 files changed, 18 insertions(+), 31 deletions(-)
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
index 341f4ee66fc..75da154fe25 100644
--- a/src/cmd/compile/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -496,7 +496,6 @@ func (o *Order) call(n *Node) {
arg.Left = x
x.Name.SetAddrtaken(true) // ensure SSA keeps the x variable
n.Nbody.Append(typecheck(nod(OVARLIVE, x, nil), ctxStmt))
- n.SetNeedsWrapper(true)
}
}
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index 9592b7484ce..14d2710da4f 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -141,20 +141,19 @@ const (
nodeInitorder, _ // tracks state during init1; two bits
_, _ // second nodeInitorder bit
_, nodeHasBreak
- _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
- _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND
- _, nodeIsDDD // is the argument variadic
- _, nodeDiag // already printed error about this
- _, nodeColas // OAS resulting from :=
- _, nodeNonNil // guaranteed to be non-nil
- _, nodeTransient // storage can be reused immediately after this statement
- _, nodeBounded // bounds check unnecessary
- _, nodeHasCall // expression contains a function call
- _, nodeLikely // if statement condition likely
- _, nodeHasVal // node.E contains a Val
- _, nodeHasOpt // node.E contains an Opt
- _, nodeEmbedded // ODCLFIELD embedded type
- _, nodeNeedsWrapper // OCALLxxx node that needs to be wrapped
+ _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
+ _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND
+ _, nodeIsDDD // is the argument variadic
+ _, nodeDiag // already printed error about this
+ _, nodeColas // OAS resulting from :=
+ _, nodeNonNil // guaranteed to be non-nil
+ _, nodeTransient // storage can be reused immediately after this statement
+ _, nodeBounded // bounds check unnecessary
+ _, nodeHasCall // expression contains a function call
+ _, nodeLikely // if statement condition likely
+ _, nodeHasVal // node.E contains a Val
+ _, nodeHasOpt // node.E contains an Opt
+ _, nodeEmbedded // ODCLFIELD embedded type
)
func (n *Node) Class() Class { return Class(n.flags.get3(nodeClass)) }
@@ -287,20 +286,6 @@ func (n *Node) SetIota(x int64) {
n.Xoffset = x
}
-func (n *Node) NeedsWrapper() bool {
- return n.flags&nodeNeedsWrapper != 0
-}
-
-// SetNeedsWrapper indicates that OCALLxxx node needs to be wrapped by a closure.
-func (n *Node) SetNeedsWrapper(b bool) {
- switch n.Op {
- case OCALLFUNC, OCALLMETH, OCALLINTER:
- default:
- Fatalf("Node.SetNeedsWrapper %v", n.Op)
- }
- n.flags.set(nodeNeedsWrapper, b)
-}
-
// mayBeShared reports whether n may occur in multiple places in the AST.
// Extra care must be taken when mutating such a node.
func (n *Node) mayBeShared() bool {
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 361de7e0f38..2d293668804 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -231,12 +231,15 @@ func walkstmt(n *Node) *Node {
case OCOPY:
n.Left = copyany(n.Left, &n.Ninit, true)
- default:
- if n.Left.NeedsWrapper() {
+ case OCALLFUNC, OCALLMETH, OCALLINTER:
+ if n.Left.Nbody.Len() > 0 {
n.Left = wrapCall(n.Left, &n.Ninit)
} else {
n.Left = walkexpr(n.Left, &n.Ninit)
}
+
+ default:
+ n.Left = walkexpr(n.Left, &n.Ninit)
}
case OFOR, OFORUNTIL:
From afb5fca25a6f59e9045317727f6899a58471d5f0 Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Sun, 13 Sep 2020 13:22:42 +0700
Subject: [PATCH 0052/1082] test: fix flaky test for issue24491
runtime.GC() doesn't guarantee the finalizer has run, so use a channel
instead to make sure finalizer was run in call to "after()".
Fixes #41361
Change-Id: I69c801e29aea49757ea72c52e8db13239de19ddc
Reviewed-on: https://go-review.googlesource.com/c/go/+/254401
Run-TryBot: Cuong Manh Le
TryBot-Result: Gobot Gobot
Reviewed-by: Matthew Dempsky
Trust: Cuong Manh Le
---
test/fixedbugs/issue24491b.go | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/test/fixedbugs/issue24491b.go b/test/fixedbugs/issue24491b.go
index 5f4a2f233e0..142d798500b 100644
--- a/test/fixedbugs/issue24491b.go
+++ b/test/fixedbugs/issue24491b.go
@@ -11,15 +11,14 @@ package main
import (
"runtime"
- "sync/atomic"
"unsafe"
)
-var done uint32
+var done = make(chan bool)
func setup() unsafe.Pointer {
s := "ok"
- runtime.SetFinalizer(&s, func(p *string) { atomic.StoreUint32(&done, 1) })
+ runtime.SetFinalizer(&s, func(p *string) { close(done) })
return unsafe.Pointer(&s)
}
@@ -27,17 +26,18 @@ func setup() unsafe.Pointer {
//go:uintptrescapes
func before(p uintptr) int {
runtime.GC()
- if atomic.LoadUint32(&done) != 0 {
+ select {
+ case <-done:
panic("GC early")
+ default:
}
return 0
}
func after() int {
runtime.GC()
- if atomic.LoadUint32(&done) == 0 {
- panic("GC late")
- }
+ runtime.GC()
+ <-done
return 0
}
From 66e66e71132034aa620ffbae9008f951da0f9f27 Mon Sep 17 00:00:00 2001
From: Dominic Della Valle
Date: Sun, 13 Sep 2020 17:03:48 +0000
Subject: [PATCH 0053/1082] make.bat: fix compare between GOROOT and srcdir
paths, when either contains whitespace.
CL 96455 brings CL 57753 to Windows
However, a path comparison within it was left unquoted.
If the Go source directory resides in a path containing whitespace,
the interpreter will compare against the first portion of the path string,
and treat the remainder as an expression.
This patch amends that.
For example, consider the path
`C:\Users\Dominic Della Valle\Projects\Go\goroot\src`
Issuing `make.bat` will print out `'Della' is not recognized as an internal or external command, operable program or batch file.` before proceeding.
Change-Id: Ifcec159baeec940c29c61aa721c64c13c6fd8c14
GitHub-Last-Rev: 809ddbb4dbc80d834f8108ca44c2826016d78d1c
GitHub-Pull-Request: golang/go#41319
Reviewed-on: https://go-review.googlesource.com/c/go/+/253898
Run-TryBot: Giovanni Bajo
TryBot-Result: Gobot Gobot
Reviewed-by: Giovanni Bajo
Trust: Giovanni Bajo
---
src/make.bat | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/make.bat b/src/make.bat
index 277a34d5d7a..b4a8e708490 100644
--- a/src/make.bat
+++ b/src/make.bat
@@ -77,7 +77,7 @@ if not "x%GOROOT_BOOTSTRAP%"=="x" goto bootstrapset
for /f "tokens=*" %%g in ('where go 2^>nul') do (
if "x%GOROOT_BOOTSTRAP%"=="x" (
for /f "tokens=*" %%i in ('%%g env GOROOT 2^>nul') do (
- if /I not %%i==%GOROOT_TEMP% (
+ if /I not "%%i"=="%GOROOT_TEMP%" (
set GOROOT_BOOTSTRAP=%%i
)
)
From 4f5cd0c0331943c7ec72df3b827d972584f77833 Mon Sep 17 00:00:00 2001
From: Roberto Clapis
Date: Wed, 26 Aug 2020 08:53:03 +0200
Subject: [PATCH 0054/1082] net/http/cgi,net/http/fcgi: add Content-Type
detection
This CL ensures that responses served via CGI and FastCGI
have a Content-Type header based on the content of the
response if not explicitly set by handlers.
If the implementers of the handler did not explicitly
specify a Content-Type both CGI implementations would default
to "text/html", potentially causing cross-site scripting.
Thanks to RedTeam Pentesting GmbH for reporting this.
Fixes #40928
Fixes CVE-2020-24553
Change-Id: I82cfc396309b5ab2e8d6e9a87eda8ea7e3799473
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/823217
Reviewed-by: Russ Cox
Reviewed-on: https://go-review.googlesource.com/c/go/+/252179
Run-TryBot: Filippo Valsorda
TryBot-Result: Go Bot
Reviewed-by: Katie Hockman
---
src/net/http/cgi/child.go | 38 ++++++++++++------
src/net/http/cgi/child_test.go | 58 ++++++++++++++++++++++++++++
src/net/http/cgi/integration_test.go | 53 ++++++++++++++++++++++++-
src/net/http/fcgi/child.go | 39 ++++++++++++++-----
src/net/http/fcgi/fcgi_test.go | 52 +++++++++++++++++++++++++
5 files changed, 217 insertions(+), 23 deletions(-)
diff --git a/src/net/http/cgi/child.go b/src/net/http/cgi/child.go
index d7d813e68a8..690986335c0 100644
--- a/src/net/http/cgi/child.go
+++ b/src/net/http/cgi/child.go
@@ -166,10 +166,12 @@ func Serve(handler http.Handler) error {
}
type response struct {
- req *http.Request
- header http.Header
- bufw *bufio.Writer
- headerSent bool
+ req *http.Request
+ header http.Header
+ code int
+ wroteHeader bool
+ wroteCGIHeader bool
+ bufw *bufio.Writer
}
func (r *response) Flush() {
@@ -181,26 +183,38 @@ func (r *response) Header() http.Header {
}
func (r *response) Write(p []byte) (n int, err error) {
- if !r.headerSent {
+ if !r.wroteHeader {
r.WriteHeader(http.StatusOK)
}
+ if !r.wroteCGIHeader {
+ r.writeCGIHeader(p)
+ }
return r.bufw.Write(p)
}
func (r *response) WriteHeader(code int) {
- if r.headerSent {
+ if r.wroteHeader {
// Note: explicitly using Stderr, as Stdout is our HTTP output.
fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL)
return
}
- r.headerSent = true
- fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code))
+ r.wroteHeader = true
+ r.code = code
+}
- // Set a default Content-Type
- if _, hasType := r.header["Content-Type"]; !hasType {
- r.header.Add("Content-Type", "text/html; charset=utf-8")
+// writeCGIHeader finalizes the header sent to the client and writes it to the output.
+// p is not written by writeHeader, but is the first chunk of the body
+// that will be written. It is sniffed for a Content-Type if none is
+// set explicitly.
+func (r *response) writeCGIHeader(p []byte) {
+ if r.wroteCGIHeader {
+ return
+ }
+ r.wroteCGIHeader = true
+ fmt.Fprintf(r.bufw, "Status: %d %s\r\n", r.code, http.StatusText(r.code))
+ if _, hasType := r.header["Content-Type"]; !hasType {
+ r.header.Set("Content-Type", http.DetectContentType(p))
}
-
r.header.Write(r.bufw)
r.bufw.WriteString("\r\n")
r.bufw.Flush()
diff --git a/src/net/http/cgi/child_test.go b/src/net/http/cgi/child_test.go
index 14e0af475f5..18cf789bd59 100644
--- a/src/net/http/cgi/child_test.go
+++ b/src/net/http/cgi/child_test.go
@@ -7,6 +7,11 @@
package cgi
import (
+ "bufio"
+ "bytes"
+ "net/http"
+ "net/http/httptest"
+ "strings"
"testing"
)
@@ -148,3 +153,56 @@ func TestRequestWithoutRemotePort(t *testing.T) {
t.Errorf("RemoteAddr: got %q; want %q", g, e)
}
}
+
+func TestResponse(t *testing.T) {
+ var tests = []struct {
+ name string
+ body string
+ wantCT string
+ }{
+ {
+ name: "no body",
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "html",
+ body: "test pageThis is a body",
+ wantCT: "text/html; charset=utf-8",
+ },
+ {
+ name: "text",
+ body: strings.Repeat("gopher", 86),
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "jpg",
+ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
+ wantCT: "image/jpeg",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var buf bytes.Buffer
+ resp := response{
+ req: httptest.NewRequest("GET", "/", nil),
+ header: http.Header{},
+ bufw: bufio.NewWriter(&buf),
+ }
+ n, err := resp.Write([]byte(tt.body))
+ if err != nil {
+ t.Errorf("Write: unexpected %v", err)
+ }
+ if want := len(tt.body); n != want {
+ t.Errorf("reported short Write: got %v want %v", n, want)
+ }
+ resp.writeCGIHeader(nil)
+ resp.Flush()
+ if got := resp.Header().Get("Content-Type"); got != tt.wantCT {
+ t.Errorf("wrong content-type: got %q, want %q", got, tt.wantCT)
+ }
+ if !bytes.HasSuffix(buf.Bytes(), []byte(tt.body)) {
+ t.Errorf("body was not correctly written")
+ }
+ })
+ }
+}
diff --git a/src/net/http/cgi/integration_test.go b/src/net/http/cgi/integration_test.go
index eaa090f6fe4..76cbca8e603 100644
--- a/src/net/http/cgi/integration_test.go
+++ b/src/net/http/cgi/integration_test.go
@@ -16,7 +16,9 @@ import (
"io"
"net/http"
"net/http/httptest"
+ "net/url"
"os"
+ "strings"
"testing"
"time"
)
@@ -52,7 +54,7 @@ func TestHostingOurselves(t *testing.T) {
}
replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
- if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
+ if expected, got := "text/plain; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
t.Errorf("got a Content-Type of %q; expected %q", got, expected)
}
if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
@@ -169,6 +171,51 @@ func TestNilRequestBody(t *testing.T) {
_ = runCgiTest(t, h, "POST /test.go?nil-request-body=1 HTTP/1.0\nHost: example.com\nContent-Length: 0\n\n", expectedMap)
}
+func TestChildContentType(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ h := &Handler{
+ Path: os.Args[0],
+ Root: "/test.go",
+ Args: []string{"-test.run=TestBeChildCGIProcess"},
+ }
+ var tests = []struct {
+ name string
+ body string
+ wantCT string
+ }{
+ {
+ name: "no body",
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "html",
+ body: "test pageThis is a body",
+ wantCT: "text/html; charset=utf-8",
+ },
+ {
+ name: "text",
+ body: strings.Repeat("gopher", 86),
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "jpg",
+ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
+ wantCT: "image/jpeg",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ expectedMap := map[string]string{"_body": tt.body}
+ req := fmt.Sprintf("GET /test.go?exact-body=%s HTTP/1.0\nHost: example.com\n\n", url.QueryEscape(tt.body))
+ replay := runCgiTest(t, h, req, expectedMap)
+ if got := replay.Header().Get("Content-Type"); got != tt.wantCT {
+ t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT)
+ }
+ })
+ }
+}
+
// golang.org/issue/7198
func Test500WithNoHeaders(t *testing.T) { want500Test(t, "/immediate-disconnect") }
func Test500WithNoContentType(t *testing.T) { want500Test(t, "/no-content-type") }
@@ -224,6 +271,10 @@ func TestBeChildCGIProcess(t *testing.T) {
if req.FormValue("no-body") == "1" {
return
}
+ if eb, ok := req.Form["exact-body"]; ok {
+ io.WriteString(rw, eb[0])
+ return
+ }
if req.FormValue("write-forever") == "1" {
io.Copy(rw, neverEnding('a'))
for {
diff --git a/src/net/http/fcgi/child.go b/src/net/http/fcgi/child.go
index 0e91042543b..34761f32ee1 100644
--- a/src/net/http/fcgi/child.go
+++ b/src/net/http/fcgi/child.go
@@ -74,10 +74,12 @@ func (r *request) parseParams() {
// response implements http.ResponseWriter.
type response struct {
- req *request
- header http.Header
- w *bufWriter
- wroteHeader bool
+ req *request
+ header http.Header
+ code int
+ wroteHeader bool
+ wroteCGIHeader bool
+ w *bufWriter
}
func newResponse(c *child, req *request) *response {
@@ -92,11 +94,14 @@ func (r *response) Header() http.Header {
return r.header
}
-func (r *response) Write(data []byte) (int, error) {
+func (r *response) Write(p []byte) (n int, err error) {
if !r.wroteHeader {
r.WriteHeader(http.StatusOK)
}
- return r.w.Write(data)
+ if !r.wroteCGIHeader {
+ r.writeCGIHeader(p)
+ }
+ return r.w.Write(p)
}
func (r *response) WriteHeader(code int) {
@@ -104,22 +109,34 @@ func (r *response) WriteHeader(code int) {
return
}
r.wroteHeader = true
+ r.code = code
if code == http.StatusNotModified {
// Must not have body.
r.header.Del("Content-Type")
r.header.Del("Content-Length")
r.header.Del("Transfer-Encoding")
- } else if r.header.Get("Content-Type") == "" {
- r.header.Set("Content-Type", "text/html; charset=utf-8")
}
-
if r.header.Get("Date") == "" {
r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
}
+}
- fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code))
+// writeCGIHeader finalizes the header sent to the client and writes it to the output.
+// p is not written by writeHeader, but is the first chunk of the body
+// that will be written. It is sniffed for a Content-Type if none is
+// set explicitly.
+func (r *response) writeCGIHeader(p []byte) {
+ if r.wroteCGIHeader {
+ return
+ }
+ r.wroteCGIHeader = true
+ fmt.Fprintf(r.w, "Status: %d %s\r\n", r.code, http.StatusText(r.code))
+ if _, hasType := r.header["Content-Type"]; r.code != http.StatusNotModified && !hasType {
+ r.header.Set("Content-Type", http.DetectContentType(p))
+ }
r.header.Write(r.w)
r.w.WriteString("\r\n")
+ r.w.Flush()
}
func (r *response) Flush() {
@@ -293,6 +310,8 @@ func (c *child) serveRequest(req *request, body io.ReadCloser) {
httpReq = httpReq.WithContext(envVarCtx)
c.handler.ServeHTTP(r, httpReq)
}
+ // Make sure we serve something even if nothing was written to r
+ r.Write(nil)
r.Close()
c.mu.Lock()
delete(c.requests, req.reqId)
diff --git a/src/net/http/fcgi/fcgi_test.go b/src/net/http/fcgi/fcgi_test.go
index e9d2b34023c..4a27a12c35a 100644
--- a/src/net/http/fcgi/fcgi_test.go
+++ b/src/net/http/fcgi/fcgi_test.go
@@ -10,6 +10,7 @@ import (
"io"
"io/ioutil"
"net/http"
+ "strings"
"testing"
)
@@ -344,3 +345,54 @@ func TestChildServeReadsEnvVars(t *testing.T) {
<-done
}
}
+
+func TestResponseWriterSniffsContentType(t *testing.T) {
+ var tests = []struct {
+ name string
+ body string
+ wantCT string
+ }{
+ {
+ name: "no body",
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "html",
+ body: "test pageThis is a body",
+ wantCT: "text/html; charset=utf-8",
+ },
+ {
+ name: "text",
+ body: strings.Repeat("gopher", 86),
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "jpg",
+ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
+ wantCT: "image/jpeg",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ input := make([]byte, len(streamFullRequestStdin))
+ copy(input, streamFullRequestStdin)
+ rc := nopWriteCloser{bytes.NewBuffer(input)}
+ done := make(chan bool)
+ var resp *response
+ c := newChild(rc, http.HandlerFunc(func(
+ w http.ResponseWriter,
+ r *http.Request,
+ ) {
+ io.WriteString(w, tt.body)
+ resp = w.(*response)
+ done <- true
+ }))
+ defer c.cleanUp()
+ go c.serve()
+ <-done
+ if got := resp.Header().Get("Content-Type"); got != tt.wantCT {
+ t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT)
+ }
+ })
+ }
+}
From 86dbeefe1f2770daad3c8d8b46a8b7f21b2c69e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?=
Date: Mon, 14 Sep 2020 13:06:40 +0200
Subject: [PATCH 0055/1082] syscall: fix fsync for read-only files on aix
AIX fsync syscall doesn't work on read-only files. Using fsync_range
instead allows syscall.Fsync to work on any files.
Fixes #41372
Change-Id: I66d33e847875496af53da60828c1bddf6c2b76b7
Reviewed-on: https://go-review.googlesource.com/c/go/+/254657
Run-TryBot: Tobias Klauser
TryBot-Result: Go Bot
Reviewed-by: Tobias Klauser
Reviewed-by: Ian Lance Taylor
---
src/syscall/syscall_aix.go | 6 +++++-
src/syscall/zsyscall_aix_ppc64.go | 26 +++++++++++++-------------
2 files changed, 18 insertions(+), 14 deletions(-)
diff --git a/src/syscall/syscall_aix.go b/src/syscall/syscall_aix.go
index 8bb5fa9eade..8837dd5a7f3 100644
--- a/src/syscall/syscall_aix.go
+++ b/src/syscall/syscall_aix.go
@@ -214,6 +214,11 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int,
return
}
+//sys fsyncRange(fd int, how int, start int64, length int64) (err error) = fsync_range
+func Fsync(fd int) error {
+ return fsyncRange(fd, O_SYNC, 0, 0)
+}
+
/*
* Socket
*/
@@ -600,7 +605,6 @@ func PtraceDetach(pid int) (err error) { return ptrace64(PT_DETACH, int64(pid),
//sys Fstat(fd int, stat *Stat_t) (err error)
//sys Fstatfs(fd int, buf *Statfs_t) (err error)
//sys Ftruncate(fd int, length int64) (err error)
-//sys Fsync(fd int) (err error)
//sysnb Getgid() (gid int)
//sysnb Getpid() (pid int)
//sys Geteuid() (euid int)
diff --git a/src/syscall/zsyscall_aix_ppc64.go b/src/syscall/zsyscall_aix_ppc64.go
index 384fead4d26..20625c1a3e0 100644
--- a/src/syscall/zsyscall_aix_ppc64.go
+++ b/src/syscall/zsyscall_aix_ppc64.go
@@ -19,6 +19,7 @@ import "unsafe"
//go:cgo_import_dynamic libc_setgroups setgroups "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_getdirent getdirent "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_wait4 wait4 "libc.a/shr_64.o"
+//go:cgo_import_dynamic libc_fsync_range fsync_range "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_bind bind "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_connect connect "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_Getkerninfo getkerninfo "libc.a/shr_64.o"
@@ -54,7 +55,6 @@ import "unsafe"
//go:cgo_import_dynamic libc_Fstat fstat "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_Fstatfs fstatfs "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_Ftruncate ftruncate "libc.a/shr_64.o"
-//go:cgo_import_dynamic libc_Fsync fsync "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_Getgid getgid "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_Getpid getpid "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_Geteuid geteuid "libc.a/shr_64.o"
@@ -111,6 +111,7 @@ import "unsafe"
//go:linkname libc_setgroups libc_setgroups
//go:linkname libc_getdirent libc_getdirent
//go:linkname libc_wait4 libc_wait4
+//go:linkname libc_fsync_range libc_fsync_range
//go:linkname libc_bind libc_bind
//go:linkname libc_connect libc_connect
//go:linkname libc_Getkerninfo libc_Getkerninfo
@@ -146,7 +147,6 @@ import "unsafe"
//go:linkname libc_Fstat libc_Fstat
//go:linkname libc_Fstatfs libc_Fstatfs
//go:linkname libc_Ftruncate libc_Ftruncate
-//go:linkname libc_Fsync libc_Fsync
//go:linkname libc_Getgid libc_Getgid
//go:linkname libc_Getpid libc_Getpid
//go:linkname libc_Geteuid libc_Geteuid
@@ -206,6 +206,7 @@ var (
libc_setgroups,
libc_getdirent,
libc_wait4,
+ libc_fsync_range,
libc_bind,
libc_connect,
libc_Getkerninfo,
@@ -241,7 +242,6 @@ var (
libc_Fstat,
libc_Fstatfs,
libc_Ftruncate,
- libc_Fsync,
libc_Getgid,
libc_Getpid,
libc_Geteuid,
@@ -442,6 +442,16 @@ func wait4(pid _Pid_t, status *_C_int, options int, rusage *Rusage) (wpid _Pid_t
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func fsyncRange(fd int, how int, start int64, length int64) (err error) {
+ _, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_fsync_range)), 4, uintptr(fd), uintptr(how), uintptr(start), uintptr(length), 0, 0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
_, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_bind)), 3, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0)
if e1 != 0 {
@@ -854,16 +864,6 @@ func Ftruncate(fd int, length int64) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Fsync(fd int) (err error) {
- _, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_Fsync)), 1, uintptr(fd), 0, 0, 0, 0, 0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Getgid() (gid int) {
r0, _, _ := rawSyscall6(uintptr(unsafe.Pointer(&libc_Getgid)), 0, 0, 0, 0, 0, 0, 0)
gid = int(r0)
From c0c396bd6ad2aea40f7f302711c8b89e20feb371 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Fri, 11 Sep 2020 20:09:40 -0400
Subject: [PATCH 0056/1082] misc/ios: quote paths
The paths may contain spaces. Quote them.
Change-Id: I1f67085a1e7c40f60282c2fea7104fb44a01e310
Reviewed-on: https://go-review.googlesource.com/c/go/+/254739
Run-TryBot: Cherry Zhang
TryBot-Result: Go Bot
Reviewed-by: Ian Lance Taylor
Reviewed-by: Dmitri Shuralyov
Trust: Dmitri Shuralyov
---
misc/ios/clangwrap.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/misc/ios/clangwrap.sh b/misc/ios/clangwrap.sh
index 5fdbb6db4ac..1d6dee28a83 100755
--- a/misc/ios/clangwrap.sh
+++ b/misc/ios/clangwrap.sh
@@ -15,4 +15,4 @@ else
exit 1
fi
-exec $CLANG -arch $CLANGARCH -isysroot $SDK_PATH -mios-version-min=10.0 "$@"
+exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -mios-version-min=10.0 "$@"
From 14c7caae5074fdf0d97a3ad995e20c63e4065cbf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20M=C3=B6hrmann?=
Date: Mon, 13 Jul 2020 18:12:20 +0200
Subject: [PATCH 0057/1082] runtime: add 24 byte allocation size class
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This CL introduces a 24 byte allocation size class which
fits 3 pointers on 64 bit and 6 pointers on 32 bit architectures.
Notably this new size class fits a slice header on 64 bit
architectures exactly while previously a 32 byte size class
would have been used for allocating a slice header on the heap.
The main complexity added with this CL is that heapBitsSetType
needs to handle objects that aren't 16-byte aligned but contain
more than a single pointer on 64-bit architectures.
Due to having a non 16 byte aligned size class on 32 bit a
h.shift of 2 is now possible which means a heap bitmap byte might
only be partially written. Due to this already having been
possible on 64 bit before the heap bitmap code only needed
minor adjustments for 32 bit doublecheck code paths.
Note that this CL changes the slice capacity allocated by append
for slice growth to a target capacity of 17 to 24 bytes.
On 64 bit architectures the capacity of the slice returned by
append([]byte{}, make([]byte, 24)...)) is 32 bytes before and
24 bytes after this CL. Depending on allocation patterns of the
specific Go program this can increase the number of total
alloctions as subsequent appends to the slice can trigger slice
growth earlier than before. On the other side if the slice is
never appended to again above its capacity this will lower heap
usage by 8 bytes.
This CL changes the set of size classes reported in the
runtime.MemStats.BySize array due to it being limited to a
total of 61 size classes. The new 24 byte size class is now
included and the 20480 byte size class is not included anymore.
Fixes #8885
name old time/op new time/op delta
Template 196ms ± 3% 194ms ± 2% ~ (p=0.247 n=10+10)
Unicode 85.6ms ±16% 88.1ms ± 1% ~ (p=0.165 n=10+10)
GoTypes 673ms ± 2% 668ms ± 2% ~ (p=0.258 n=9+9)
Compiler 3.14s ± 6% 3.08s ± 1% ~ (p=0.243 n=10+9)
SSA 6.82s ± 1% 6.76s ± 1% -0.87% (p=0.006 n=9+10)
Flate 128ms ± 7% 127ms ± 3% ~ (p=0.739 n=10+10)
GoParser 154ms ± 3% 153ms ± 4% ~ (p=0.730 n=9+9)
Reflect 404ms ± 1% 412ms ± 4% +1.99% (p=0.022 n=9+10)
Tar 172ms ± 4% 170ms ± 4% ~ (p=0.065 n=10+9)
XML 231ms ± 4% 230ms ± 3% ~ (p=0.912 n=10+10)
LinkCompiler 341ms ± 1% 339ms ± 1% ~ (p=0.243 n=9+10)
ExternalLinkCompiler 1.72s ± 1% 1.72s ± 1% ~ (p=0.661 n=9+10)
LinkWithoutDebugCompiler 221ms ± 2% 221ms ± 2% ~ (p=0.529 n=10+10)
StdCmd 18.4s ± 3% 18.2s ± 1% ~ (p=0.515 n=10+8)
name old user-time/op new user-time/op delta
Template 238ms ± 4% 243ms ± 6% ~ (p=0.661 n=9+10)
Unicode 116ms ± 6% 113ms ± 3% -3.37% (p=0.035 n=9+10)
GoTypes 854ms ± 2% 848ms ± 2% ~ (p=0.604 n=9+10)
Compiler 4.10s ± 1% 4.11s ± 1% ~ (p=0.481 n=8+9)
SSA 9.49s ± 1% 9.41s ± 1% -0.92% (p=0.001 n=9+10)
Flate 149ms ± 6% 151ms ± 7% ~ (p=0.481 n=10+10)
GoParser 189ms ± 2% 190ms ± 2% ~ (p=0.497 n=9+10)
Reflect 511ms ± 2% 508ms ± 2% ~ (p=0.211 n=9+10)
Tar 215ms ± 4% 212ms ± 3% ~ (p=0.105 n=10+10)
XML 288ms ± 2% 288ms ± 2% ~ (p=0.971 n=10+10)
LinkCompiler 559ms ± 4% 557ms ± 1% ~ (p=0.968 n=9+10)
ExternalLinkCompiler 1.78s ± 1% 1.77s ± 1% ~ (p=0.055 n=8+10)
LinkWithoutDebugCompiler 245ms ± 3% 245ms ± 2% ~ (p=0.684 n=10+10)
name old alloc/op new alloc/op delta
Template 34.8MB ± 0% 34.4MB ± 0% -0.95% (p=0.000 n=9+10)
Unicode 28.6MB ± 0% 28.3MB ± 0% -0.95% (p=0.000 n=10+10)
GoTypes 115MB ± 0% 114MB ± 0% -1.02% (p=0.000 n=10+9)
Compiler 554MB ± 0% 549MB ± 0% -0.86% (p=0.000 n=9+10)
SSA 1.28GB ± 0% 1.27GB ± 0% -0.83% (p=0.000 n=10+10)
Flate 21.8MB ± 0% 21.6MB ± 0% -0.87% (p=0.000 n=8+10)
GoParser 26.7MB ± 0% 26.4MB ± 0% -0.97% (p=0.000 n=10+9)
Reflect 75.0MB ± 0% 74.1MB ± 0% -1.18% (p=0.000 n=10+10)
Tar 32.6MB ± 0% 32.3MB ± 0% -0.94% (p=0.000 n=10+7)
XML 41.5MB ± 0% 41.2MB ± 0% -0.90% (p=0.000 n=10+8)
LinkCompiler 105MB ± 0% 104MB ± 0% -0.94% (p=0.000 n=10+10)
ExternalLinkCompiler 153MB ± 0% 152MB ± 0% -0.69% (p=0.000 n=10+10)
LinkWithoutDebugCompiler 63.7MB ± 0% 63.6MB ± 0% -0.13% (p=0.000 n=10+10)
name old allocs/op new allocs/op delta
Template 336k ± 0% 336k ± 0% +0.02% (p=0.002 n=10+10)
Unicode 332k ± 0% 332k ± 0% ~ (p=0.447 n=10+10)
GoTypes 1.16M ± 0% 1.16M ± 0% +0.01% (p=0.001 n=10+10)
Compiler 4.92M ± 0% 4.92M ± 0% +0.01% (p=0.000 n=10+10)
SSA 11.9M ± 0% 11.9M ± 0% +0.02% (p=0.000 n=9+10)
Flate 214k ± 0% 214k ± 0% +0.02% (p=0.032 n=10+8)
GoParser 270k ± 0% 270k ± 0% +0.02% (p=0.004 n=10+9)
Reflect 877k ± 0% 877k ± 0% +0.01% (p=0.000 n=10+10)
Tar 313k ± 0% 313k ± 0% ~ (p=0.075 n=9+10)
XML 387k ± 0% 387k ± 0% +0.02% (p=0.007 n=10+10)
LinkCompiler 455k ± 0% 456k ± 0% +0.08% (p=0.000 n=10+9)
ExternalLinkCompiler 670k ± 0% 671k ± 0% +0.06% (p=0.000 n=10+10)
LinkWithoutDebugCompiler 113k ± 0% 113k ± 0% ~ (p=0.149 n=10+10)
name old maxRSS/op new maxRSS/op delta
Template 34.1M ± 1% 34.1M ± 1% ~ (p=0.853 n=10+10)
Unicode 35.1M ± 1% 34.6M ± 1% -1.43% (p=0.000 n=10+10)
GoTypes 72.8M ± 3% 73.3M ± 2% ~ (p=0.724 n=10+10)
Compiler 288M ± 3% 295M ± 4% ~ (p=0.393 n=10+10)
SSA 630M ± 1% 622M ± 1% -1.18% (p=0.001 n=10+10)
Flate 26.0M ± 1% 26.2M ± 2% ~ (p=0.493 n=10+10)
GoParser 28.6M ± 1% 28.5M ± 2% ~ (p=0.256 n=10+10)
Reflect 55.5M ± 2% 55.4M ± 1% ~ (p=0.436 n=10+10)
Tar 33.0M ± 1% 32.8M ± 2% ~ (p=0.075 n=10+10)
XML 38.7M ± 1% 39.0M ± 1% ~ (p=0.053 n=9+10)
LinkCompiler 164M ± 1% 164M ± 1% -0.27% (p=0.029 n=10+10)
ExternalLinkCompiler 174M ± 0% 173M ± 0% -0.33% (p=0.002 n=9+10)
LinkWithoutDebugCompiler 137M ± 0% 136M ± 2% ~ (p=0.825 n=9+10)
Change-Id: I9ecf2a10024513abef8fbfbe519e44e0b29b6167
Reviewed-on: https://go-review.googlesource.com/c/go/+/242258
Trust: Martin Möhrmann
Trust: Michael Knyszek
Run-TryBot: Martin Möhrmann
TryBot-Result: Go Bot
Reviewed-by: Michael Knyszek
Reviewed-by: Keith Randall
---
src/runtime/mbitmap.go | 112 ++++++++++++++++++++++++----
src/runtime/mksizeclasses.go | 6 +-
src/runtime/mstats.go | 4 +
src/runtime/sizeclasses.go | 141 ++++++++++++++++++-----------------
4 files changed, 174 insertions(+), 89 deletions(-)
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index 8de44c14b99..51c3625c3d9 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -30,10 +30,9 @@
// indicates scanning can ignore the rest of the allocation.
//
// The 2-bit entries are split when written into the byte, so that the top half
-// of the byte contains 4 high bits and the bottom half contains 4 low (pointer)
-// bits.
-// This form allows a copy from the 1-bit to the 4-bit form to keep the
-// pointer bits contiguous, instead of having to space them out.
+// of the byte contains 4 high (scan) bits and the bottom half contains 4 low
+// (pointer) bits. This form allows a copy from the 1-bit to the 4-bit form to
+// keep the pointer bits contiguous, instead of having to space them out.
//
// The code makes use of the fact that the zero value for a heap
// bitmap means scalar/dead. This property must be preserved when
@@ -816,6 +815,12 @@ func (s *mspan) countAlloc() int {
func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
const doubleCheck = false // slow but helpful; enable to test modifications to this code
+ const (
+ mask1 = bitPointer | bitScan // 00010001
+ mask2 = bitPointer | bitScan | mask1<> 1
+
+ // For h.shift > 1 heap bits cross a byte boundary and need to be written part
+ // to h.bitp and part to the next h.bitp.
+ switch h.shift {
+ case 0:
+ *h.bitp &^= mask3 << 0
+ *h.bitp |= hb << 0
+ case 1:
+ *h.bitp &^= mask3 << 1
+ *h.bitp |= hb << 1
+ case 2:
+ *h.bitp &^= mask2 << 2
+ *h.bitp |= (hb & mask2) << 2
+ // Two words written to the first byte.
+ // Advance two words to get to the next byte.
+ h = h.next().next()
+ *h.bitp &^= mask1
+ *h.bitp |= (hb >> 2) & mask1
+ case 3:
+ *h.bitp &^= mask1 << 3
+ *h.bitp |= (hb & mask1) << 3
+ // One word written to the first byte.
+ // Advance one word to get to the next byte.
+ h = h.next()
+ *h.bitp &^= mask2
+ *h.bitp |= (hb >> 1) & mask2
+ }
+ return
}
// Copy from 1-bit ptrmask into 2-bit bitmap.
@@ -1079,7 +1149,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
// word must be set to scan since there are pointers
// somewhere in the object.
// In all following words, we set the scan/dead
- // appropriately to indicate that the object contains
+ // appropriately to indicate that the object continues
// to the next 2-bit entry in the bitmap.
//
// We set four bits at a time here, but if the object
@@ -1095,12 +1165,22 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
b >>= 4
nb -= 4
- case sys.PtrSize == 8 && h.shift == 2:
+ case h.shift == 2:
// Ptrmask and heap bitmap are misaligned.
+ //
+ // On 32 bit architectures only the 6-word object that corresponds
+ // to a 24 bytes size class can start with h.shift of 2 here since
+ // all other non 16 byte aligned size classes have been handled by
+ // special code paths at the beginning of heapBitsSetType on 32 bit.
+ //
+ // Many size classes are only 16 byte aligned. On 64 bit architectures
+ // this results in a heap bitmap position starting with a h.shift of 2.
+ //
// The bits for the first two words are in a byte shared
// with another object, so we must be careful with the bits
// already there.
- // We took care of 1-word and 2-word objects above,
+ //
+ // We took care of 1-word, 2-word, and 3-word objects above,
// so this is at least a 6-word object.
hb = (b & (bitPointer | bitPointer<= nw {
- // We know that there is more data, because we handled 2-word objects above.
+ // We know that there is more data, because we handled 2-word and 3-word objects above.
// This must be at least a 6-word object. If we're out of pointer words,
// mark no scan in next bitmap byte and finish.
hb = 0
@@ -1248,12 +1328,12 @@ Phase4:
// Handle the first byte specially if it's shared. See
// Phase 1 for why this is the only special case we need.
if doubleCheck {
- if !(h.shift == 0 || (sys.PtrSize == 8 && h.shift == 2)) {
+ if !(h.shift == 0 || h.shift == 2) {
print("x=", x, " size=", size, " cnw=", h.shift, "\n")
throw("bad start shift")
}
}
- if sys.PtrSize == 8 && h.shift == 2 {
+ if h.shift == 2 {
*h.bitp = *h.bitp&^((bitPointer|bitScan|(bitPointer|bitScan)<= 128 {
align = size / 8
- } else if size >= 16 {
- align = 16 // required for x86 SSE instructions, if we want to use them
+ } else if size >= 32 {
+ align = 16 // heap bitmaps assume 16 byte alignment for allocations >= 32 bytes.
}
}
if !powerOfTwo(align) {
@@ -157,7 +157,7 @@ func makeClasses() []class {
}
}
- if len(classes) != 67 {
+ if len(classes) != 68 {
panic("number of size classes has changed")
}
diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go
index 6a8a34d1edc..b95b3321343 100644
--- a/src/runtime/mstats.go
+++ b/src/runtime/mstats.go
@@ -78,6 +78,10 @@ type mstats struct {
nfree uint64
}
+ // Add an uint32 for even number of size classes to align below fields
+ // to 64 bits for atomic operations on 32 bit platforms.
+ _ [1 - _NumSizeClasses%2]uint32
+
// Statistics below here are not exported to MemStats directly.
last_gc_nanotime uint64 // last gc (monotonic time)
diff --git a/src/runtime/sizeclasses.go b/src/runtime/sizeclasses.go
index 9c1b44fe0b2..c5521ce1bd2 100644
--- a/src/runtime/sizeclasses.go
+++ b/src/runtime/sizeclasses.go
@@ -6,82 +6,83 @@ package runtime
// class bytes/obj bytes/span objects tail waste max waste
// 1 8 8192 1024 0 87.50%
// 2 16 8192 512 0 43.75%
-// 3 32 8192 256 0 46.88%
-// 4 48 8192 170 32 31.52%
-// 5 64 8192 128 0 23.44%
-// 6 80 8192 102 32 19.07%
-// 7 96 8192 85 32 15.95%
-// 8 112 8192 73 16 13.56%
-// 9 128 8192 64 0 11.72%
-// 10 144 8192 56 128 11.82%
-// 11 160 8192 51 32 9.73%
-// 12 176 8192 46 96 9.59%
-// 13 192 8192 42 128 9.25%
-// 14 208 8192 39 80 8.12%
-// 15 224 8192 36 128 8.15%
-// 16 240 8192 34 32 6.62%
-// 17 256 8192 32 0 5.86%
-// 18 288 8192 28 128 12.16%
-// 19 320 8192 25 192 11.80%
-// 20 352 8192 23 96 9.88%
-// 21 384 8192 21 128 9.51%
-// 22 416 8192 19 288 10.71%
-// 23 448 8192 18 128 8.37%
-// 24 480 8192 17 32 6.82%
-// 25 512 8192 16 0 6.05%
-// 26 576 8192 14 128 12.33%
-// 27 640 8192 12 512 15.48%
-// 28 704 8192 11 448 13.93%
-// 29 768 8192 10 512 13.94%
-// 30 896 8192 9 128 15.52%
-// 31 1024 8192 8 0 12.40%
-// 32 1152 8192 7 128 12.41%
-// 33 1280 8192 6 512 15.55%
-// 34 1408 16384 11 896 14.00%
-// 35 1536 8192 5 512 14.00%
-// 36 1792 16384 9 256 15.57%
-// 37 2048 8192 4 0 12.45%
-// 38 2304 16384 7 256 12.46%
-// 39 2688 8192 3 128 15.59%
-// 40 3072 24576 8 0 12.47%
-// 41 3200 16384 5 384 6.22%
-// 42 3456 24576 7 384 8.83%
-// 43 4096 8192 2 0 15.60%
-// 44 4864 24576 5 256 16.65%
-// 45 5376 16384 3 256 10.92%
-// 46 6144 24576 4 0 12.48%
-// 47 6528 32768 5 128 6.23%
-// 48 6784 40960 6 256 4.36%
-// 49 6912 49152 7 768 3.37%
-// 50 8192 8192 1 0 15.61%
-// 51 9472 57344 6 512 14.28%
-// 52 9728 49152 5 512 3.64%
-// 53 10240 40960 4 0 4.99%
-// 54 10880 32768 3 128 6.24%
-// 55 12288 24576 2 0 11.45%
-// 56 13568 40960 3 256 9.99%
-// 57 14336 57344 4 0 5.35%
-// 58 16384 16384 1 0 12.49%
-// 59 18432 73728 4 0 11.11%
-// 60 19072 57344 3 128 3.57%
-// 61 20480 40960 2 0 6.87%
-// 62 21760 65536 3 256 6.25%
-// 63 24576 24576 1 0 11.45%
-// 64 27264 81920 3 128 10.00%
-// 65 28672 57344 2 0 4.91%
-// 66 32768 32768 1 0 12.50%
+// 3 24 8192 341 8 29.24%
+// 4 32 8192 256 0 21.88%
+// 5 48 8192 170 32 31.52%
+// 6 64 8192 128 0 23.44%
+// 7 80 8192 102 32 19.07%
+// 8 96 8192 85 32 15.95%
+// 9 112 8192 73 16 13.56%
+// 10 128 8192 64 0 11.72%
+// 11 144 8192 56 128 11.82%
+// 12 160 8192 51 32 9.73%
+// 13 176 8192 46 96 9.59%
+// 14 192 8192 42 128 9.25%
+// 15 208 8192 39 80 8.12%
+// 16 224 8192 36 128 8.15%
+// 17 240 8192 34 32 6.62%
+// 18 256 8192 32 0 5.86%
+// 19 288 8192 28 128 12.16%
+// 20 320 8192 25 192 11.80%
+// 21 352 8192 23 96 9.88%
+// 22 384 8192 21 128 9.51%
+// 23 416 8192 19 288 10.71%
+// 24 448 8192 18 128 8.37%
+// 25 480 8192 17 32 6.82%
+// 26 512 8192 16 0 6.05%
+// 27 576 8192 14 128 12.33%
+// 28 640 8192 12 512 15.48%
+// 29 704 8192 11 448 13.93%
+// 30 768 8192 10 512 13.94%
+// 31 896 8192 9 128 15.52%
+// 32 1024 8192 8 0 12.40%
+// 33 1152 8192 7 128 12.41%
+// 34 1280 8192 6 512 15.55%
+// 35 1408 16384 11 896 14.00%
+// 36 1536 8192 5 512 14.00%
+// 37 1792 16384 9 256 15.57%
+// 38 2048 8192 4 0 12.45%
+// 39 2304 16384 7 256 12.46%
+// 40 2688 8192 3 128 15.59%
+// 41 3072 24576 8 0 12.47%
+// 42 3200 16384 5 384 6.22%
+// 43 3456 24576 7 384 8.83%
+// 44 4096 8192 2 0 15.60%
+// 45 4864 24576 5 256 16.65%
+// 46 5376 16384 3 256 10.92%
+// 47 6144 24576 4 0 12.48%
+// 48 6528 32768 5 128 6.23%
+// 49 6784 40960 6 256 4.36%
+// 50 6912 49152 7 768 3.37%
+// 51 8192 8192 1 0 15.61%
+// 52 9472 57344 6 512 14.28%
+// 53 9728 49152 5 512 3.64%
+// 54 10240 40960 4 0 4.99%
+// 55 10880 32768 3 128 6.24%
+// 56 12288 24576 2 0 11.45%
+// 57 13568 40960 3 256 9.99%
+// 58 14336 57344 4 0 5.35%
+// 59 16384 16384 1 0 12.49%
+// 60 18432 73728 4 0 11.11%
+// 61 19072 57344 3 128 3.57%
+// 62 20480 40960 2 0 6.87%
+// 63 21760 65536 3 256 6.25%
+// 64 24576 24576 1 0 11.45%
+// 65 27264 81920 3 128 10.00%
+// 66 28672 57344 2 0 4.91%
+// 67 32768 32768 1 0 12.50%
const (
_MaxSmallSize = 32768
smallSizeDiv = 8
smallSizeMax = 1024
largeSizeDiv = 128
- _NumSizeClasses = 67
+ _NumSizeClasses = 68
_PageShift = 13
)
-var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768}
-var class_to_allocnpages = [_NumSizeClasses]uint8{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 2, 3, 1, 3, 2, 3, 4, 5, 6, 1, 7, 6, 5, 4, 3, 5, 7, 2, 9, 7, 5, 8, 3, 10, 7, 4}
+var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 24, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768}
+var class_to_allocnpages = [_NumSizeClasses]uint8{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 2, 3, 1, 3, 2, 3, 4, 5, 6, 1, 7, 6, 5, 4, 3, 5, 7, 2, 9, 7, 5, 8, 3, 10, 7, 4}
type divMagic struct {
shift uint8
@@ -90,6 +91,6 @@ type divMagic struct {
baseMask uint16
}
-var class_to_divmagic = [_NumSizeClasses]divMagic{{0, 0, 0, 0}, {3, 0, 1, 65528}, {4, 0, 1, 65520}, {5, 0, 1, 65504}, {4, 11, 683, 0}, {6, 0, 1, 65472}, {4, 10, 205, 0}, {5, 9, 171, 0}, {4, 11, 293, 0}, {7, 0, 1, 65408}, {4, 13, 911, 0}, {5, 10, 205, 0}, {4, 12, 373, 0}, {6, 9, 171, 0}, {4, 13, 631, 0}, {5, 11, 293, 0}, {4, 13, 547, 0}, {8, 0, 1, 65280}, {5, 9, 57, 0}, {6, 9, 103, 0}, {5, 12, 373, 0}, {7, 7, 43, 0}, {5, 10, 79, 0}, {6, 10, 147, 0}, {5, 11, 137, 0}, {9, 0, 1, 65024}, {6, 9, 57, 0}, {7, 9, 103, 0}, {6, 11, 187, 0}, {8, 7, 43, 0}, {7, 8, 37, 0}, {10, 0, 1, 64512}, {7, 9, 57, 0}, {8, 6, 13, 0}, {7, 11, 187, 0}, {9, 5, 11, 0}, {8, 8, 37, 0}, {11, 0, 1, 63488}, {8, 9, 57, 0}, {7, 10, 49, 0}, {10, 5, 11, 0}, {7, 10, 41, 0}, {7, 9, 19, 0}, {12, 0, 1, 61440}, {8, 9, 27, 0}, {8, 10, 49, 0}, {11, 5, 11, 0}, {7, 13, 161, 0}, {7, 13, 155, 0}, {8, 9, 19, 0}, {13, 0, 1, 57344}, {8, 12, 111, 0}, {9, 9, 27, 0}, {11, 6, 13, 0}, {7, 14, 193, 0}, {12, 3, 3, 0}, {8, 13, 155, 0}, {11, 8, 37, 0}, {14, 0, 1, 49152}, {11, 8, 29, 0}, {7, 13, 55, 0}, {12, 5, 7, 0}, {8, 14, 193, 0}, {13, 3, 3, 0}, {7, 14, 77, 0}, {12, 7, 19, 0}, {15, 0, 1, 32768}}
-var size_to_class8 = [smallSizeMax/smallSizeDiv + 1]uint8{0, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31}
-var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv + 1]uint8{31, 32, 33, 34, 35, 36, 36, 37, 37, 38, 38, 39, 39, 39, 40, 40, 40, 41, 42, 42, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, 47, 47, 48, 48, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66}
+var class_to_divmagic = [_NumSizeClasses]divMagic{{0, 0, 0, 0}, {3, 0, 1, 65528}, {4, 0, 1, 65520}, {3, 11, 683, 0}, {5, 0, 1, 65504}, {4, 11, 683, 0}, {6, 0, 1, 65472}, {4, 10, 205, 0}, {5, 9, 171, 0}, {4, 11, 293, 0}, {7, 0, 1, 65408}, {4, 13, 911, 0}, {5, 10, 205, 0}, {4, 12, 373, 0}, {6, 9, 171, 0}, {4, 13, 631, 0}, {5, 11, 293, 0}, {4, 13, 547, 0}, {8, 0, 1, 65280}, {5, 9, 57, 0}, {6, 9, 103, 0}, {5, 12, 373, 0}, {7, 7, 43, 0}, {5, 10, 79, 0}, {6, 10, 147, 0}, {5, 11, 137, 0}, {9, 0, 1, 65024}, {6, 9, 57, 0}, {7, 9, 103, 0}, {6, 11, 187, 0}, {8, 7, 43, 0}, {7, 8, 37, 0}, {10, 0, 1, 64512}, {7, 9, 57, 0}, {8, 6, 13, 0}, {7, 11, 187, 0}, {9, 5, 11, 0}, {8, 8, 37, 0}, {11, 0, 1, 63488}, {8, 9, 57, 0}, {7, 10, 49, 0}, {10, 5, 11, 0}, {7, 10, 41, 0}, {7, 9, 19, 0}, {12, 0, 1, 61440}, {8, 9, 27, 0}, {8, 10, 49, 0}, {11, 5, 11, 0}, {7, 13, 161, 0}, {7, 13, 155, 0}, {8, 9, 19, 0}, {13, 0, 1, 57344}, {8, 12, 111, 0}, {9, 9, 27, 0}, {11, 6, 13, 0}, {7, 14, 193, 0}, {12, 3, 3, 0}, {8, 13, 155, 0}, {11, 8, 37, 0}, {14, 0, 1, 49152}, {11, 8, 29, 0}, {7, 13, 55, 0}, {12, 5, 7, 0}, {8, 14, 193, 0}, {13, 3, 3, 0}, {7, 14, 77, 0}, {12, 7, 19, 0}, {15, 0, 1, 32768}}
+var size_to_class8 = [smallSizeMax/smallSizeDiv + 1]uint8{0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}
+var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv + 1]uint8{32, 33, 34, 35, 36, 37, 37, 38, 38, 39, 39, 40, 40, 40, 41, 41, 41, 42, 43, 43, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67}
From 57646534297a9bd193e6aaa4239c98984f371b97 Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills"
Date: Mon, 14 Sep 2020 10:19:47 -0400
Subject: [PATCH 0058/1082] cmd/api: omit outside dependencies when listing the
packages in "std"
As of CL 251159, when 'go list -deps std' is run within GOROOT/src, it
treats the vendored external dependencies as real module dependencies,
not standard-library "vendor/" packages (which still exist in that
case, but are treated as distinct packages outside the "std" module).
Fixes #41358
Updates #30241
Change-Id: Ic23eae9829d90e74a340d49ca9052e9191597410
Reviewed-on: https://go-review.googlesource.com/c/go/+/254738
Run-TryBot: Bryan C. Mills
Trust: Bryan C. Mills
TryBot-Result: Go Bot
Reviewed-by: Michael Matloob
Reviewed-by: Jay Conrod
---
src/cmd/api/goapi.go | 17 +++++++++++++----
src/cmd/api/goapi_test.go | 13 +++++++++++++
src/cmd/go/internal/modload/load.go | 8 ++++++--
3 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go
index 01b17b88392..6a80ed269b4 100644
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -87,7 +87,10 @@ var contexts = []*build.Context{
func contextName(c *build.Context) string {
s := c.GOOS + "-" + c.GOARCH
if c.CgoEnabled {
- return s + "-cgo"
+ s += "-cgo"
+ }
+ if c.Dir != "" {
+ s += fmt.Sprintf(" [%s]", c.Dir)
}
return s
}
@@ -478,6 +481,9 @@ func (w *Walker) loadImports() {
cmd := exec.Command(goCmd(), "list", "-e", "-deps", "-json", "std")
cmd.Env = listEnv(w.context)
+ if w.context.Dir != "" {
+ cmd.Dir = w.context.Dir
+ }
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("loading imports: %v\n%s", err, out)
@@ -491,6 +497,7 @@ func (w *Walker) loadImports() {
var pkg struct {
ImportPath, Dir string
ImportMap map[string]string
+ Standard bool
}
err := dec.Decode(&pkg)
if err == io.EOF {
@@ -503,11 +510,13 @@ func (w *Walker) loadImports() {
// - Package "unsafe" contains special signatures requiring
// extra care when printing them - ignore since it is not
// going to change w/o a language change.
- // - internal and vendored packages do not contribute to our
- // API surface.
+ // - Internal and vendored packages do not contribute to our
+ // API surface. (If we are running within the "std" module,
+ // vendored dependencies appear as themselves instead of
+ // their "vendor/" standard-library copies.)
// - 'go list std' does not include commands, which cannot be
// imported anyway.
- if ip := pkg.ImportPath; ip != "unsafe" && !strings.HasPrefix(ip, "vendor/") && !internalPkg.MatchString(ip) {
+ if ip := pkg.ImportPath; pkg.Standard && ip != "unsafe" && !strings.HasPrefix(ip, "vendor/") && !internalPkg.MatchString(ip) {
stdPackages = append(stdPackages, ip)
}
importDir[pkg.ImportPath] = pkg.Dir
diff --git a/src/cmd/api/goapi_test.go b/src/cmd/api/goapi_test.go
index eaccc5ceb53..24620a94afc 100644
--- a/src/cmd/api/goapi_test.go
+++ b/src/cmd/api/goapi_test.go
@@ -216,3 +216,16 @@ func TestIssue29837(t *testing.T) {
}
}
}
+
+func TestIssue41358(t *testing.T) {
+ context := new(build.Context)
+ *context = build.Default
+ context.Dir = filepath.Join(context.GOROOT, "src")
+
+ w := NewWalker(context, context.Dir)
+ for _, pkg := range w.stdPackages {
+ if strings.HasPrefix(pkg, "vendor/") || strings.HasPrefix(pkg, "golang.org/x/") {
+ t.Fatalf("stdPackages contains unexpected package %s", pkg)
+ }
+ }
+}
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 1664d8c5be8..2fe68e6f880 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -1106,8 +1106,12 @@ func (ld *loader) stdVendor(parentPath, path string) string {
// Do the same for importers beginning with the prefix 'vendor/' even if we
// are *inside* of the 'std' module: the 'vendor/' packages that resolve
// globally from GOROOT/src/vendor (and are listed as part of 'go list std')
- // are distinct from the real module dependencies, and cannot import internal
- // packages from the real module.
+ // are distinct from the real module dependencies, and cannot import
+ // internal packages from the real module.
+ //
+ // (Note that although the 'vendor/' packages match the 'std' *package*
+ // pattern, they are not part of the std *module*, and do not affect
+ // 'go mod tidy' and similar module commands when working within std.)
vendorPath := pathpkg.Join("vendor", path)
if _, err := os.Stat(filepath.Join(cfg.GOROOTsrc, filepath.FromSlash(vendorPath))); err == nil {
return vendorPath
From a408139bb0166f6e0a5d9fd17fc934da960c354e Mon Sep 17 00:00:00 2001
From: Changkun Ou
Date: Mon, 14 Sep 2020 10:24:59 +0200
Subject: [PATCH 0059/1082] testing: fix panicking tests hang if Cleanup calls
FailNow
Previously, it was impossible to call FailNow in a Cleanup.
Because it can terminate a panicking goroutine and cause its
parent hangs on t.signal channel. This CL sends the signal
in a deferred call to prevent the hang.
Fixes #41355
Change-Id: I4552d3a7ea763ef86817bf9b50c0e37fb34bf20f
Reviewed-on: https://go-review.googlesource.com/c/go/+/254637
Reviewed-by: Ian Lance Taylor
Reviewed-by: Emmanuel Odeke
Run-TryBot: Ian Lance Taylor
TryBot-Result: Go Bot
Trust: Emmanuel Odeke
---
.../testdata/script/test_cleanup_failnow.txt | 33 +++++++++++++++++++
src/testing/testing.go | 12 ++++++-
2 files changed, 44 insertions(+), 1 deletion(-)
create mode 100644 src/cmd/go/testdata/script/test_cleanup_failnow.txt
diff --git a/src/cmd/go/testdata/script/test_cleanup_failnow.txt b/src/cmd/go/testdata/script/test_cleanup_failnow.txt
new file mode 100644
index 00000000000..5ad4185fc10
--- /dev/null
+++ b/src/cmd/go/testdata/script/test_cleanup_failnow.txt
@@ -0,0 +1,33 @@
+# For issue 41355
+[short] skip
+
+! go test -v cleanup_failnow/panic_nocleanup_test.go
+stdout '(?s)panic: die \[recovered\].*panic: die'
+! stdout '(?s)panic: die \[recovered\].*panic: die.*panic: die'
+
+! go test -v cleanup_failnow/panic_withcleanup_test.go
+stdout '(?s)panic: die \[recovered\].*panic: die'
+! stdout '(?s)panic: die \[recovered\].*panic: die.*panic: die'
+
+-- cleanup_failnow/panic_nocleanup_test.go --
+package panic_nocleanup_test
+import "testing"
+func TestX(t *testing.T) {
+ t.Run("x", func(t *testing.T) {
+ panic("die")
+ })
+}
+
+-- cleanup_failnow/panic_withcleanup_test.go --
+package panic_withcleanup_test
+import "testing"
+func TestCleanupWithFailNow(t *testing.T) {
+ t.Cleanup(func() {
+ t.FailNow()
+ })
+ t.Run("x", func(t *testing.T) {
+ t.Run("y", func(t *testing.T) {
+ panic("die")
+ })
+ })
+}
\ No newline at end of file
diff --git a/src/testing/testing.go b/src/testing/testing.go
index 66f296234a5..d86354093a4 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -1075,6 +1075,7 @@ func tRunner(t *T, fn func(t *T)) {
// If the test panicked, print any test output before dying.
err := recover()
signal := true
+
if !t.finished && err == nil {
err = errNilPanicOrGoexit
for p := t.parent; p != nil; p = p.parent {
@@ -1086,6 +1087,15 @@ func tRunner(t *T, fn func(t *T)) {
}
}
}
+ // Use a deferred call to ensure that we report that the test is
+ // complete even if a cleanup function calls t.FailNow. See issue 41355.
+ didPanic := false
+ defer func() {
+ t.signal <- signal
+ if err != nil && !didPanic {
+ panic(err)
+ }
+ }()
doPanic := func(err interface{}) {
t.Fail()
@@ -1103,6 +1113,7 @@ func tRunner(t *T, fn func(t *T)) {
fmt.Fprintf(root.parent.w, "cleanup panicked with %v", r)
}
}
+ didPanic = true
panic(err)
}
if err != nil {
@@ -1144,7 +1155,6 @@ func tRunner(t *T, fn func(t *T)) {
if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 {
t.setRan()
}
- t.signal <- signal
}()
defer func() {
if len(t.sub) == 0 {
From 114719e16e9681bd1001326598ededa719c17944 Mon Sep 17 00:00:00 2001
From: Damien Neil
Date: Mon, 14 Sep 2020 21:00:52 +0000
Subject: [PATCH 0060/1082] Revert "encoding/json: implement Is on all errors"
This reverts CL 254537.
Reason for revert: Reason for revert: The recommended way to check for a type of error is errors.As. API changes should also start with a proposal.
Change-Id: I07c37428575e99c80b17525833a61831d10963bb
Reviewed-on: https://go-review.googlesource.com/c/go/+/254857
Trust: Damien Neil
Reviewed-by: Emmanuel Odeke
---
src/encoding/json/decode.go | 18 ------------------
src/encoding/json/decode_test.go | 31 -------------------------------
src/encoding/json/encode.go | 12 ------------
src/encoding/json/encode_test.go | 24 +-----------------------
4 files changed, 1 insertion(+), 84 deletions(-)
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index 1b006ffb178..86d8a69db7e 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -136,12 +136,6 @@ func (e *UnmarshalTypeError) Error() string {
return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}
-// Is returns true if target is a UnmarshalTypeError.
-func (e *UnmarshalTypeError) Is(target error) bool {
- _, ok := target.(*UnmarshalTypeError)
- return ok
-}
-
// An UnmarshalFieldError describes a JSON object key that
// led to an unexported (and therefore unwritable) struct field.
//
@@ -156,24 +150,12 @@ func (e *UnmarshalFieldError) Error() string {
return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String()
}
-// Is returns true if target is a UnmarshalFieldError.
-func (e *UnmarshalFieldError) Is(target error) bool {
- _, ok := target.(*UnmarshalFieldError)
- return ok
-}
-
// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal.
// (The argument to Unmarshal must be a non-nil pointer.)
type InvalidUnmarshalError struct {
Type reflect.Type
}
-// Is returns true if target is a InvalidUnmarshalError.
-func (e *InvalidUnmarshalError) Is(target error) bool {
- _, ok := target.(*InvalidUnmarshalError)
- return ok
-}
-
func (e *InvalidUnmarshalError) Error() string {
if e.Type == nil {
return "json: Unmarshal(nil)"
diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go
index b707dcfa992..219e845c7b0 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -2572,34 +2572,3 @@ func TestUnmarshalMaxDepth(t *testing.T) {
}
}
}
-
-func TestInvalidUnmarshalErrorIs(t *testing.T) {
- err := fmt.Errorf("apackage: %w: failed to parse struct", &InvalidUnmarshalError{reflect.TypeOf("a")})
- if !errors.Is(err, &InvalidUnmarshalError{}) {
- t.Fatalf("%v should be unwrapped to a InvalidUnmarshalError", err)
- }
-}
-
-func TestUnmarshalFieldErrorIs(t *testing.T) {
- err := fmt.Errorf("apackage: %w: failed to parse struct", &UnmarshalFieldError{
- Key: "foo",
- Type: reflect.TypeOf("a"),
- Field: reflect.StructField{Name: "b"},
- })
- if !errors.Is(err, &UnmarshalFieldError{}) {
- t.Fatalf("%v should be unwrapped to a UnmarshalFieldError", err)
- }
-}
-
-func TestUnmarshalTypeErrorIs(t *testing.T) {
- err := fmt.Errorf("apackage: %w: failed to parse struct", &UnmarshalTypeError{
- Value: "foo",
- Type: reflect.TypeOf("a"),
- Offset: 1,
- Struct: "Foo",
- Field: "Bar",
- })
- if !errors.Is(err, &UnmarshalTypeError{}) {
- t.Fatalf("%v should be unwrapped to a UnmarshalTypeError", err)
- }
-}
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index 8e6b342b595..578d551102d 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -245,12 +245,6 @@ func (e *UnsupportedValueError) Error() string {
return "json: unsupported value: " + e.Str
}
-// Is returns true if target is a UnsupportedValueError.
-func (e *UnsupportedValueError) Is(target error) bool {
- _, ok := target.(*UnsupportedValueError)
- return ok
-}
-
// Before Go 1.2, an InvalidUTF8Error was returned by Marshal when
// attempting to encode a string value with invalid UTF-8 sequences.
// As of Go 1.2, Marshal instead coerces the string to valid UTF-8 by
@@ -285,12 +279,6 @@ func (e *MarshalerError) Error() string {
// Unwrap returns the underlying error.
func (e *MarshalerError) Unwrap() error { return e.Err }
-// Is returns true if target is a MarshalerError.
-func (e *MarshalerError) Is(target error) bool {
- _, ok := target.(*MarshalerError)
- return ok
-}
-
var hex = "0123456789abcdef"
// An encodeState encodes JSON into a bytes.Buffer.
diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go
index 90826a7f476..7290eca06f0 100644
--- a/src/encoding/json/encode_test.go
+++ b/src/encoding/json/encode_test.go
@@ -7,7 +7,6 @@ package json
import (
"bytes"
"encoding"
- "errors"
"fmt"
"log"
"math"
@@ -212,7 +211,7 @@ var unsupportedValues = []interface{}{
func TestUnsupportedValues(t *testing.T) {
for _, v := range unsupportedValues {
if _, err := Marshal(v); err != nil {
- if !errors.Is(err, &UnsupportedValueError{}) {
+ if _, ok := err.(*UnsupportedValueError); !ok {
t.Errorf("for %v, got %T want UnsupportedValueError", v, err)
}
} else {
@@ -1156,24 +1155,3 @@ func TestMarshalerError(t *testing.T) {
}
}
}
-
-func TestMarshalerErrorIs(t *testing.T) {
- err := fmt.Errorf("apackage: %w: failed to parse struct", &MarshalerError{
- reflect.TypeOf("a"),
- fmt.Errorf("something"),
- "TestMarshalerErrorIs",
- })
- if !errors.Is(err, &MarshalerError{}) {
- t.Fatalf("%v should be unwrapped to a MarshalerError", err)
- }
-}
-
-func TestUnsupportedValueErrorIs(t *testing.T) {
- err := fmt.Errorf("apackage: %w: failed to parse struct", &UnsupportedValueError{
- Value: reflect.Value{},
- Str: "Foo",
- })
- if !errors.Is(err, &UnsupportedValueError{}) {
- t.Fatalf("%v should be unwrapped to a UnsupportedValueError", err)
- }
-}
From 506eb0a9b1a7051f64788f330ea26722fa293f3c Mon Sep 17 00:00:00 2001
From: Damien Neil
Date: Mon, 14 Sep 2020 20:59:50 +0000
Subject: [PATCH 0061/1082] Revert "encoding/json: implement Is on SyntaxError"
This reverts CL 253037.
Reason for revert: The recommended way to check for a type of error is errors.As. API changes should also start with a proposal.
Change-Id: I62896717aa47ed491c2c4775d2b05d80e5e9cde3
Reviewed-on: https://go-review.googlesource.com/c/go/+/254837
Trust: Damien Neil
Reviewed-by: Emmanuel Odeke
---
src/encoding/json/scanner.go | 6 ------
src/encoding/json/scanner_test.go | 9 ---------
2 files changed, 15 deletions(-)
diff --git a/src/encoding/json/scanner.go b/src/encoding/json/scanner.go
index 05218f9cc3e..9dc1903e2db 100644
--- a/src/encoding/json/scanner.go
+++ b/src/encoding/json/scanner.go
@@ -49,12 +49,6 @@ type SyntaxError struct {
func (e *SyntaxError) Error() string { return e.msg }
-// Is returns true if target is a SyntaxError.
-func (e *SyntaxError) Is(target error) bool {
- _, ok := target.(*SyntaxError)
- return ok
-}
-
// A scanner is a JSON scanning state machine.
// Callers call scan.reset and then pass bytes in one at a time
// by calling scan.step(&scan, c) for each byte.
diff --git a/src/encoding/json/scanner_test.go b/src/encoding/json/scanner_test.go
index c12d9bf3d73..3737516a450 100644
--- a/src/encoding/json/scanner_test.go
+++ b/src/encoding/json/scanner_test.go
@@ -6,8 +6,6 @@ package json
import (
"bytes"
- "errors"
- "fmt"
"math"
"math/rand"
"reflect"
@@ -203,13 +201,6 @@ func TestIndentErrors(t *testing.T) {
}
}
-func TestSyntaxErrorIs(t *testing.T) {
- err := fmt.Errorf("apackage: %w: failed to parse struct", &SyntaxError{"some error", 43})
- if !errors.Is(err, &SyntaxError{}) {
- t.Fatalf("%v should be unwrapped to a SyntaxError", err)
- }
-}
-
func diff(t *testing.T, a, b []byte) {
for i := 0; ; i++ {
if i >= len(a) || i >= len(b) || a[i] != b[i] {
From 237410547bb81ae3c58e9c5bf0cf59edc989e243 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Mon, 14 Sep 2020 12:53:36 -0700
Subject: [PATCH 0062/1082] cmd/compile: better dclcontext handling in
func{hdr,body}
funchdr and funcbody currently assume that either (1) Curfn == nil &&
dclcontext == PEXTERN, or (2) Curfn != nil && dclcontext == PAUTO.
This is a reasonable assumption during parsing. However, these
functions end up getting used in other contexts, and not all callers
are so disciplined about Curfn/dclcontext handling.
This CL changes them to save/restore arbitrary Curfn/dclcontext pairs
instead. This is necessary for the followup CL, which pushes fninit
earlier. Otherwise, Curfn/dclcontext fall out of sync, and funchdr
panics.
Passes toolstash-check.
Updates #33485.
Change-Id: I19b1be23db1bad6475345ae5c81bbdc66291a3a7
Reviewed-on: https://go-review.googlesource.com/c/go/+/254838
Run-TryBot: Matthew Dempsky
TryBot-Result: Go Bot
Reviewed-by: Keith Randall
Trust: Matthew Dempsky
---
src/cmd/compile/internal/gc/dcl.go | 30 ++++++++++++++---------------
src/cmd/compile/internal/gc/main.go | 3 +++
2 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index 69eb13f6076..a362d1a643b 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -382,14 +382,11 @@ func ifacedcl(n *Node) {
// returns in auto-declaration context.
func funchdr(n *Node) {
// change the declaration context from extern to auto
- if Curfn == nil && dclcontext != PEXTERN {
- Fatalf("funchdr: dclcontext = %d", dclcontext)
- }
-
- dclcontext = PAUTO
- types.Markdcl()
- funcstack = append(funcstack, Curfn)
+ funcStack = append(funcStack, funcStackEnt{Curfn, dclcontext})
Curfn = n
+ dclcontext = PAUTO
+
+ types.Markdcl()
if n.Func.Nname != nil {
funcargs(n.Func.Nname.Name.Param.Ntype)
@@ -497,21 +494,22 @@ func funcarg2(f *types.Field, ctxt Class) {
declare(n, ctxt)
}
-var funcstack []*Node // stack of previous values of Curfn
+var funcStack []funcStackEnt // stack of previous values of Curfn/dclcontext
+
+type funcStackEnt struct {
+ curfn *Node
+ dclcontext Class
+}
// finish the body.
// called in auto-declaration context.
// returns in extern-declaration context.
func funcbody() {
- // change the declaration context from auto to extern
- if dclcontext != PAUTO {
- Fatalf("funcbody: unexpected dclcontext %d", dclcontext)
- }
+ // change the declaration context from auto to previous context
types.Popdcl()
- funcstack, Curfn = funcstack[:len(funcstack)-1], funcstack[len(funcstack)-1]
- if Curfn == nil {
- dclcontext = PEXTERN
- }
+ var e funcStackEnt
+ funcStack, e = funcStack[:len(funcStack)-1], funcStack[len(funcStack)-1]
+ Curfn, dclcontext = e.curfn, e.dclcontext
}
// structs, functions, and methods.
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index eedfc4bb259..9bce6cf8cb4 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -809,6 +809,9 @@ func Main(archInit func(*Arch)) {
}
}
+ if len(funcStack) != 0 {
+ Fatalf("funcStack is non-empty: %v", len(funcStack))
+ }
if len(compilequeue) != 0 {
Fatalf("%d uncompiled functions", len(compilequeue))
}
From f4936d09fd5a1fff890d63ee2ab9543243dc4da6 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Mon, 14 Sep 2020 12:56:37 -0700
Subject: [PATCH 0063/1082] cmd/compile: call fninit earlier
This allows the global initializers function to go through normal
mid-end optimizations (e.g., inlining, escape analysis) like any other
function.
Updates #33485.
Change-Id: I9bcfe98b8628d1aca09b4c238d8d3b74c69010a5
Reviewed-on: https://go-review.googlesource.com/c/go/+/254839
Reviewed-by: Keith Randall
Trust: Matthew Dempsky
---
src/cmd/compile/internal/gc/init.go | 8 +++-----
src/cmd/compile/internal/gc/main.go | 6 ++----
test/inline.go | 2 +-
3 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go
index 94cbcf98467..ec9cc4bddc5 100644
--- a/src/cmd/compile/internal/gc/init.go
+++ b/src/cmd/compile/internal/gc/init.go
@@ -59,7 +59,7 @@ func fninit(n []*Node) {
Curfn = fn
typecheckslice(nf, ctxStmt)
Curfn = nil
- funccompile(fn)
+ xtop = append(xtop, fn)
fns = append(fns, initializers.Linksym())
}
if dummyInitFn.Func.Dcl != nil {
@@ -68,16 +68,14 @@ func fninit(n []*Node) {
// something's weird if we get here.
Fatalf("dummyInitFn still has declarations")
}
+ dummyInitFn = nil
// Record user init functions.
for i := 0; i < renameinitgen; i++ {
s := lookupN("init.", i)
fn := asNode(s.Def).Name.Defn
// Skip init functions with empty bodies.
- // noder.go doesn't allow external init functions, and
- // order.go has already removed any OEMPTY nodes, so
- // checking Len() == 0 is sufficient here.
- if fn.Nbody.Len() == 0 {
+ if fn.Nbody.Len() == 1 && fn.Nbody.First().Op == OEMPTY {
continue
}
fns = append(fns, s.Linksym())
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 9bce6cf8cb4..8783cb4e463 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -642,6 +642,8 @@ func Main(archInit func(*Arch)) {
errorexit()
}
+ fninit(xtop)
+
// Phase 4: Decide how to capture closed variables.
// This needs to run before escape analysis,
// because variables captured by value do not escape.
@@ -751,10 +753,6 @@ func Main(archInit func(*Arch)) {
}
timings.AddEvent(fcount, "funcs")
- if nsavederrors+nerrors == 0 {
- fninit(xtop)
- }
-
compileFunctions()
if nowritebarrierrecCheck != nil {
diff --git a/test/inline.go b/test/inline.go
index 0b3ad55d466..1c5c1bc8d36 100644
--- a/test/inline.go
+++ b/test/inline.go
@@ -50,7 +50,7 @@ func j(x int) int { // ERROR "can inline j"
}
}
-var somethingWrong error = errors.New("something went wrong")
+var somethingWrong error = errors.New("something went wrong") // ERROR "can inline init" "inlining call to errors.New" "errors.errorString.* escapes to heap"
// local closures can be inlined
func l(x, y int) (int, int, error) {
From d20298e1c7d1df794a11ce7768e027c6759df2a4 Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Mon, 14 Sep 2020 10:38:45 +0700
Subject: [PATCH 0064/1082] cmd/compile: make funccompile non-reentrant
Currently, there's awkward reentrancy issue with funccompile:
funccompile -> compile -> dtypesym -> geneq/genhash/genwrapper -> funccompile
Though it's not a problem at this moment, some attempts by @mdempsky to
move order/walk/instrument into buildssa was failed, due to SSA cache
corruption.
This commit fixes that reentrancy issue, by making generated functions
to be pumped through the same compile workqueue that normal functions
are compiled. We do this by adding them to xtop, instead of calling
funccompile directly in geneq/genhash/genwrapper. In dumpdata, we look
for uncompiled functions in xtop instead of compilequeue, then finish
compiling them.
Updates #38463
Fixes #33485
Change-Id: Ic9f0ce45b56ae2ff3862f17fd979253ddc144bb5
Reviewed-on: https://go-review.googlesource.com/c/go/+/254617
Run-TryBot: Cuong Manh Le
Reviewed-by: Matthew Dempsky
TryBot-Result: Go Bot
Trust: Keith Randall
Trust: Cuong Manh Le
---
src/cmd/compile/internal/gc/alg.go | 4 ++--
src/cmd/compile/internal/gc/obj.go | 26 +++++++++++++++++++++++++-
src/cmd/compile/internal/gc/subr.go | 2 +-
3 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go
index c9d71ea00b4..6302b88f597 100644
--- a/src/cmd/compile/internal/gc/alg.go
+++ b/src/cmd/compile/internal/gc/alg.go
@@ -392,7 +392,7 @@ func genhash(t *types.Type) *obj.LSym {
}
fn.Func.SetNilCheckDisabled(true)
- funccompile(fn)
+ xtop = append(xtop, fn)
// Build closure. It doesn't close over any variables, so
// it contains just the function pointer.
@@ -754,7 +754,7 @@ func geneq(t *types.Type) *obj.LSym {
// neither of which can be nil, and our comparisons
// are shallow.
fn.Func.SetNilCheckDisabled(true)
- funccompile(fn)
+ xtop = append(xtop, fn)
// Generate a closure which points at the function we just generated.
dsymptr(closure, 0, sym.Linksym(), 0)
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index af5037c5a8b..b55331a948f 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -113,12 +113,16 @@ func dumpCompilerObj(bout *bio.Writer) {
func dumpdata() {
externs := len(externdcl)
+ xtops := len(xtop)
dumpglobls()
addptabs()
+ exportlistLen := len(exportlist)
addsignats(externdcl)
dumpsignats()
dumptabs()
+ ptabsLen := len(ptabs)
+ itabsLen := len(itabs)
dumpimportstrings()
dumpbasictypes()
@@ -129,9 +133,19 @@ func dumpdata() {
// number of types in a finite amount of code.
// In the typical case, we loop 0 or 1 times.
// It was not until issue 24761 that we found any code that required a loop at all.
- for len(compilequeue) > 0 {
+ for {
+ for i := xtops; i < len(xtop); i++ {
+ n := xtop[i]
+ if n.Op == ODCLFUNC {
+ funccompile(n)
+ }
+ }
+ xtops = len(xtop)
compileFunctions()
dumpsignats()
+ if xtops == len(xtop) {
+ break
+ }
}
// Dump extra globals.
@@ -149,6 +163,16 @@ func dumpdata() {
}
addGCLocals()
+
+ if exportlistLen != len(exportlist) {
+ Fatalf("exportlist changed after compile functions loop")
+ }
+ if ptabsLen != len(ptabs) {
+ Fatalf("ptabs changed after compile functions loop")
+ }
+ if itabsLen != len(itabs) {
+ Fatalf("itabs changed after compile functions loop")
+ }
}
func dumpLinkerObj(bout *bio.Writer) {
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index d3ba53ff0c0..5a5833d19f6 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -1615,7 +1615,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
escapeFuncs([]*Node{fn}, false)
Curfn = nil
- funccompile(fn)
+ xtop = append(xtop, fn)
}
func paramNnames(ft *types.Type) []*Node {
From bae9cf651796db898b1e4bd77a1a47c5f2d7b04d Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Mon, 14 Sep 2020 19:24:55 -0700
Subject: [PATCH 0065/1082] test: fix inline.go to pass linux-amd64-noopt
Updates #33485.
Change-Id: I3330860cdff1e9797466a7630bcdb7792c465b06
Reviewed-on: https://go-review.googlesource.com/c/go/+/254938
Run-TryBot: Matthew Dempsky
Reviewed-by: Cuong Manh Le
TryBot-Result: Go Bot
Trust: Matthew Dempsky
---
test/inline.go | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/test/inline.go b/test/inline.go
index 1c5c1bc8d36..3edcf2edfd1 100644
--- a/test/inline.go
+++ b/test/inline.go
@@ -10,7 +10,6 @@
package foo
import (
- "errors"
"runtime"
"unsafe"
)
@@ -50,7 +49,7 @@ func j(x int) int { // ERROR "can inline j"
}
}
-var somethingWrong error = errors.New("something went wrong") // ERROR "can inline init" "inlining call to errors.New" "errors.errorString.* escapes to heap"
+var somethingWrong error
// local closures can be inlined
func l(x, y int) (int, int, error) {
From 3b64e6b010775839f2daef4ac3fb607bf1519e05 Mon Sep 17 00:00:00 2001
From: Tobias Klauser
Date: Mon, 14 Sep 2020 11:06:39 +0200
Subject: [PATCH 0066/1082] internal/poll, internal/syscall/unix, net: enable
writev on illumos
Illumos supports iovec read/write. Add the writev wrapper to
internal/syscall/unix and use it to implement internal/poll.writev for
net.(*netFD).writeBuffers.
Change-Id: Ie256c2f96aba8e61fb21991788789a049425f792
Reviewed-on: https://go-review.googlesource.com/c/go/+/254638
Run-TryBot: Tobias Klauser
TryBot-Result: Go Bot
Reviewed-by: Ian Lance Taylor
Trust: Tobias Klauser
---
src/internal/poll/fd_writev_illumos.go | 16 +++++++++++
src/internal/poll/iovec_illumos.go | 16 +++++++++++
src/internal/poll/iovec_unix.go | 13 +++++++++
src/internal/poll/writev.go | 4 +--
src/internal/syscall/unix/writev_illumos.go | 30 +++++++++++++++++++++
src/net/writev_test.go | 2 +-
src/net/writev_unix.go | 2 +-
7 files changed, 79 insertions(+), 4 deletions(-)
create mode 100644 src/internal/poll/fd_writev_illumos.go
create mode 100644 src/internal/poll/iovec_illumos.go
create mode 100644 src/internal/poll/iovec_unix.go
create mode 100644 src/internal/syscall/unix/writev_illumos.go
diff --git a/src/internal/poll/fd_writev_illumos.go b/src/internal/poll/fd_writev_illumos.go
new file mode 100644
index 00000000000..1fa47ab1a32
--- /dev/null
+++ b/src/internal/poll/fd_writev_illumos.go
@@ -0,0 +1,16 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build illumos
+
+package poll
+
+import (
+ "internal/syscall/unix"
+ "syscall"
+)
+
+func writev(fd int, iovecs []syscall.Iovec) (uintptr, error) {
+ return unix.Writev(fd, iovecs)
+}
diff --git a/src/internal/poll/iovec_illumos.go b/src/internal/poll/iovec_illumos.go
new file mode 100644
index 00000000000..057067465bf
--- /dev/null
+++ b/src/internal/poll/iovec_illumos.go
@@ -0,0 +1,16 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build illumos
+
+package poll
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func newIovecWithBase(base *byte) syscall.Iovec {
+ return syscall.Iovec{Base: (*int8)(unsafe.Pointer(base))}
+}
diff --git a/src/internal/poll/iovec_unix.go b/src/internal/poll/iovec_unix.go
new file mode 100644
index 00000000000..6f98947866a
--- /dev/null
+++ b/src/internal/poll/iovec_unix.go
@@ -0,0 +1,13 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package poll
+
+import "syscall"
+
+func newIovecWithBase(base *byte) syscall.Iovec {
+ return syscall.Iovec{Base: base}
+}
diff --git a/src/internal/poll/writev.go b/src/internal/poll/writev.go
index 305e2fd2094..0123fc33de7 100644
--- a/src/internal/poll/writev.go
+++ b/src/internal/poll/writev.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd illumos linux netbsd openbsd
package poll
@@ -38,7 +38,7 @@ func (fd *FD) Writev(v *[][]byte) (int64, error) {
if len(chunk) == 0 {
continue
}
- iovecs = append(iovecs, syscall.Iovec{Base: &chunk[0]})
+ iovecs = append(iovecs, newIovecWithBase(&chunk[0]))
if fd.IsStream && len(chunk) > 1<<30 {
iovecs[len(iovecs)-1].SetLen(1 << 30)
break // continue chunk on next writev
diff --git a/src/internal/syscall/unix/writev_illumos.go b/src/internal/syscall/unix/writev_illumos.go
new file mode 100644
index 00000000000..eb7973d65b4
--- /dev/null
+++ b/src/internal/syscall/unix/writev_illumos.go
@@ -0,0 +1,30 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build illumos
+
+package unix
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+//go:cgo_import_dynamic libc_writev writev "libc.so"
+
+//go:linkname procwritev libc_writev
+
+var procwritev uintptr
+
+func Writev(fd int, iovs []syscall.Iovec) (uintptr, error) {
+ var p *syscall.Iovec
+ if len(iovs) > 0 {
+ p = &iovs[0]
+ }
+ n, _, errno := syscall6(uintptr(unsafe.Pointer(&procwritev)), 3, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(len(iovs)), 0, 0, 0)
+ if errno != 0 {
+ return 0, errno
+ }
+ return n, nil
+}
diff --git a/src/net/writev_test.go b/src/net/writev_test.go
index c43be844189..d6dce3cc695 100644
--- a/src/net/writev_test.go
+++ b/src/net/writev_test.go
@@ -154,7 +154,7 @@ func testBuffer_writeTo(t *testing.T, chunks int, useCopy bool) {
var wantSum int
switch runtime.GOOS {
- case "android", "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd":
+ case "android", "darwin", "dragonfly", "freebsd", "illumos", "linux", "netbsd", "openbsd":
var wantMinCalls int
wantSum = want.Len()
v := chunks
diff --git a/src/net/writev_unix.go b/src/net/writev_unix.go
index bf0fbf8a136..8b20f42b34d 100644
--- a/src/net/writev_unix.go
+++ b/src/net/writev_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd illumos linux netbsd openbsd
package net
From ea33523877e4c7a136f2db94a8b5bc4e40220be2 Mon Sep 17 00:00:00 2001
From: Xiangdong Ji
Date: Tue, 8 Sep 2020 09:55:20 +0000
Subject: [PATCH 0067/1082] cmd/compile: rewrite some ARM64 rules to use typed
aux
Passes toolstash-check -all.
Change-Id: I7ec36bc048f3031c8201107e6fc5d1257271dbf1
Reviewed-on: https://go-review.googlesource.com/c/go/+/234379
Run-TryBot: Alberto Donizetti
TryBot-Result: Go Bot
Trust: Alberto Donizetti
Reviewed-by: Keith Randall
---
src/cmd/compile/internal/ssa/gen/ARM64.rules | 472 +++++-----
src/cmd/compile/internal/ssa/rewriteARM64.go | 912 +++++++++----------
2 files changed, 692 insertions(+), 692 deletions(-)
diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules
index 311067e87a3..c4a35326322 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM64.rules
+++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules
@@ -1116,280 +1116,280 @@
// if a register move has only 1 use, just use the same register without emitting instruction
// MOVDnop doesn't emit instruction, only for ensuring the type.
-(MOVDreg x) && x.Uses == 1 -> (MOVDnop x)
+(MOVDreg x) && x.Uses == 1 => (MOVDnop x)
// fold constant into arithmatic ops
-(ADD x (MOVDconst [c])) -> (ADDconst [c] x)
-(SUB x (MOVDconst [c])) -> (SUBconst [c] x)
-(AND x (MOVDconst [c])) -> (ANDconst [c] x)
-(OR x (MOVDconst [c])) -> (ORconst [c] x)
-(XOR x (MOVDconst [c])) -> (XORconst [c] x)
-(TST x (MOVDconst [c])) -> (TSTconst [c] x)
-(TSTW x (MOVDconst [c])) -> (TSTWconst [c] x)
-(CMN x (MOVDconst [c])) -> (CMNconst [c] x)
-(CMNW x (MOVDconst [c])) -> (CMNWconst [c] x)
-(BIC x (MOVDconst [c])) -> (ANDconst [^c] x)
-(EON x (MOVDconst [c])) -> (XORconst [^c] x)
-(ORN x (MOVDconst [c])) -> (ORconst [^c] x)
+(ADD x (MOVDconst [c])) => (ADDconst [c] x)
+(SUB x (MOVDconst [c])) => (SUBconst [c] x)
+(AND x (MOVDconst [c])) => (ANDconst [c] x)
+(OR x (MOVDconst [c])) => (ORconst [c] x)
+(XOR x (MOVDconst [c])) => (XORconst [c] x)
+(TST x (MOVDconst [c])) => (TSTconst [c] x)
+(TSTW x (MOVDconst [c])) => (TSTWconst [int32(c)] x)
+(CMN x (MOVDconst [c])) => (CMNconst [c] x)
+(CMNW x (MOVDconst [c])) => (CMNWconst [int32(c)] x)
+(BIC x (MOVDconst [c])) => (ANDconst [^c] x)
+(EON x (MOVDconst [c])) => (XORconst [^c] x)
+(ORN x (MOVDconst [c])) => (ORconst [^c] x)
-(SLL x (MOVDconst [c])) -> (SLLconst x [c&63]) // Note: I don't think we ever generate bad constant shifts (i.e. c>=64)
-(SRL x (MOVDconst [c])) -> (SRLconst x [c&63])
-(SRA x (MOVDconst [c])) -> (SRAconst x [c&63])
+(SLL x (MOVDconst [c])) => (SLLconst x [c&63]) // Note: I don't think we ever generate bad constant shifts (i.e. c>=64)
+(SRL x (MOVDconst [c])) => (SRLconst x [c&63])
+(SRA x (MOVDconst [c])) => (SRAconst x [c&63])
-(CMP x (MOVDconst [c])) -> (CMPconst [c] x)
-(CMP (MOVDconst [c]) x) -> (InvertFlags (CMPconst [c] x))
+(CMP x (MOVDconst [c])) => (CMPconst [c] x)
+(CMP (MOVDconst [c]) x) => (InvertFlags (CMPconst [c] x))
(CMPW x (MOVDconst [c])) => (CMPWconst [int32(c)] x)
(CMPW (MOVDconst [c]) x) => (InvertFlags (CMPWconst [int32(c)] x))
// Canonicalize the order of arguments to comparisons - helps with CSE.
-((CMP|CMPW) x y) && x.ID > y.ID -> (InvertFlags ((CMP|CMPW) y x))
+((CMP|CMPW) x y) && x.ID > y.ID => (InvertFlags ((CMP|CMPW) y x))
-// mul-neg -> mneg
-(NEG (MUL x y)) -> (MNEG x y)
-(NEG (MULW x y)) -> (MNEGW x y)
-(MUL (NEG x) y) -> (MNEG x y)
-(MULW (NEG x) y) -> (MNEGW x y)
+// mul-neg => mneg
+(NEG (MUL x y)) => (MNEG x y)
+(NEG (MULW x y)) => (MNEGW x y)
+(MUL (NEG x) y) => (MNEG x y)
+(MULW (NEG x) y) => (MNEGW x y)
// madd/msub
-(ADD a l:(MUL x y)) && l.Uses==1 && clobber(l) -> (MADD a x y)
-(SUB a l:(MUL x y)) && l.Uses==1 && clobber(l) -> (MSUB a x y)
-(ADD a l:(MNEG x y)) && l.Uses==1 && clobber(l) -> (MSUB a x y)
-(SUB a l:(MNEG x y)) && l.Uses==1 && clobber(l) -> (MADD a x y)
+(ADD a l:(MUL x y)) && l.Uses==1 && clobber(l) => (MADD a x y)
+(SUB a l:(MUL x y)) && l.Uses==1 && clobber(l) => (MSUB a x y)
+(ADD a l:(MNEG x y)) && l.Uses==1 && clobber(l) => (MSUB a x y)
+(SUB a l:(MNEG x y)) && l.Uses==1 && clobber(l) => (MADD a x y)
-(ADD a l:(MULW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) -> (MADDW a x y)
-(SUB a l:(MULW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) -> (MSUBW a x y)
-(ADD a l:(MNEGW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) -> (MSUBW a x y)
-(SUB a l:(MNEGW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) -> (MADDW a x y)
+(ADD a l:(MULW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) => (MADDW a x y)
+(SUB a l:(MULW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) => (MSUBW a x y)
+(ADD a l:(MNEGW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) => (MSUBW a x y)
+(SUB a l:(MNEGW x y)) && a.Type.Size() != 8 && l.Uses==1 && clobber(l) => (MADDW a x y)
// optimize ADCSflags, SBCSflags and friends
-(ADCSflags x y (Select1 (ADDSconstflags [-1] (ADCzerocarry c)))) -> (ADCSflags x y c)
-(ADCSflags x y (Select1 (ADDSconstflags [-1] (MOVDconst [0])))) -> (ADDSflags x y)
-(SBCSflags x y (Select1 (NEGSflags (NEG (NGCzerocarry bo))))) -> (SBCSflags x y bo)
-(SBCSflags x y (Select1 (NEGSflags (MOVDconst [0])))) -> (SUBSflags x y)
+(ADCSflags x y (Select1 (ADDSconstflags [-1] (ADCzerocarry c)))) => (ADCSflags x y c)
+(ADCSflags x y (Select1 (ADDSconstflags [-1] (MOVDconst [0])))) => (ADDSflags x y)
+(SBCSflags x y (Select1 (NEGSflags (NEG (NGCzerocarry bo))))) => (SBCSflags x y bo)
+(SBCSflags x y (Select1 (NEGSflags (MOVDconst [0])))) => (SUBSflags x y)
// mul by constant
-(MUL x (MOVDconst [-1])) -> (NEG x)
-(MUL _ (MOVDconst [0])) -> (MOVDconst [0])
-(MUL x (MOVDconst [1])) -> x
-(MUL x (MOVDconst [c])) && isPowerOfTwo(c) -> (SLLconst [log2(c)] x)
-(MUL x (MOVDconst [c])) && isPowerOfTwo(c-1) && c >= 3 -> (ADDshiftLL x x [log2(c-1)])
-(MUL x (MOVDconst [c])) && isPowerOfTwo(c+1) && c >= 7 -> (ADDshiftLL (NEG x) x [log2(c+1)])
-(MUL x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) -> (SLLconst [log2(c/3)] (ADDshiftLL x x [1]))
-(MUL x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) -> (SLLconst [log2(c/5)] (ADDshiftLL x x [2]))
-(MUL x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) -> (SLLconst [log2(c/7)] (ADDshiftLL (NEG x) x [3]))
-(MUL x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) -> (SLLconst [log2(c/9)] (ADDshiftLL x x [3]))
+(MUL x (MOVDconst [-1])) => (NEG x)
+(MUL _ (MOVDconst [0])) => (MOVDconst [0])
+(MUL x (MOVDconst [1])) => x
+(MUL x (MOVDconst [c])) && isPowerOfTwo(c) => (SLLconst [log2(c)] x)
+(MUL x (MOVDconst [c])) && isPowerOfTwo(c-1) && c >= 3 => (ADDshiftLL x x [log2(c-1)])
+(MUL x (MOVDconst [c])) && isPowerOfTwo(c+1) && c >= 7 => (ADDshiftLL (NEG x) x [log2(c+1)])
+(MUL x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) => (SLLconst [log2(c/3)] (ADDshiftLL x x [1]))
+(MUL x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) => (SLLconst [log2(c/5)] (ADDshiftLL x x [2]))
+(MUL x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) => (SLLconst [log2(c/7)] (ADDshiftLL (NEG x) x [3]))
+(MUL x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) => (SLLconst [log2(c/9)] (ADDshiftLL x x [3]))
-(MULW x (MOVDconst [c])) && int32(c)==-1 -> (NEG x)
-(MULW _ (MOVDconst [c])) && int32(c)==0 -> (MOVDconst [0])
-(MULW x (MOVDconst [c])) && int32(c)==1 -> x
-(MULW x (MOVDconst [c])) && isPowerOfTwo(c) -> (SLLconst [log2(c)] x)
-(MULW x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c) >= 3 -> (ADDshiftLL x x [log2(c-1)])
-(MULW x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c) >= 7 -> (ADDshiftLL (NEG x) x [log2(c+1)])
-(MULW x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (SLLconst [log2(c/3)] (ADDshiftLL x x [1]))
-(MULW x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (SLLconst [log2(c/5)] (ADDshiftLL x x [2]))
-(MULW x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (SLLconst [log2(c/7)] (ADDshiftLL (NEG x) x [3]))
-(MULW x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (SLLconst [log2(c/9)] (ADDshiftLL x x [3]))
+(MULW x (MOVDconst [c])) && int32(c)==-1 => (NEG x)
+(MULW _ (MOVDconst [c])) && int32(c)==0 => (MOVDconst [0])
+(MULW x (MOVDconst [c])) && int32(c)==1 => x
+(MULW x (MOVDconst [c])) && isPowerOfTwo(c) => (SLLconst [log2(c)] x)
+(MULW x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c) >= 3 => (ADDshiftLL x x [log2(c-1)])
+(MULW x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c) >= 7 => (ADDshiftLL (NEG x) x [log2(c+1)])
+(MULW x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) => (SLLconst [log2(c/3)] (ADDshiftLL x x [1]))
+(MULW x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) => (SLLconst [log2(c/5)] (ADDshiftLL