mirror of
https://github.com/golang/go.git
synced 2025-05-07 16:43:03 +00:00
Compare commits
54 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c390a1c22e | ||
|
1207de4f6c | ||
|
a0d15cb9c8 | ||
|
958f3a0309 | ||
|
6ba3a8a6ba | ||
|
5472853843 | ||
|
cfe0ae0b70 | ||
|
58babf6e0b | ||
|
8d79bf799b | ||
|
35c010ad6d | ||
|
6495ce0495 | ||
|
7fc8312673 | ||
|
cc16cdf48f | ||
|
9563300f6e | ||
|
f8080edefd | ||
|
ed07b321ae | ||
|
3b2e846e11 | ||
|
fbddfae62f | ||
|
c8c6f9abfb | ||
|
a74951c5af | ||
|
e6598e7baa | ||
|
82575f76b8 | ||
|
a886959aa2 | ||
|
80ff7cd35a | ||
|
69234ded30 | ||
|
032ac075c2 | ||
|
fa8ff1a46d | ||
|
53487e5477 | ||
|
3d1f1f27cf | ||
|
6de5a7180c | ||
|
9625a7faae | ||
|
9c939a1e60 | ||
|
7afe17bbdb | ||
|
8002845759 | ||
|
9166d2feec | ||
|
76346b3543 | ||
|
3c9340557c | ||
|
dbecb416d1 | ||
|
6885bad7dd | ||
|
ec7d6094e6 | ||
|
63b0f805cd | ||
|
7adb012205 | ||
|
c9940fe2a9 | ||
|
3509415eca | ||
|
559c77592f | ||
|
f5e4e45ef7 | ||
|
30b6fd60a6 | ||
|
7e4d6c2bcb | ||
|
8bd4ed6cbb | ||
|
7dff7439dc | ||
|
62c3a6350b | ||
|
eba9e08766 | ||
|
f3bdcda88a | ||
|
362f22d2d2 |
@ -1 +1,2 @@
|
||||
branch: master
|
||||
branch: release-branch.go1.23
|
||||
parent-branch: master
|
||||
|
@ -2579,6 +2579,11 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
if dt.BitSize > 0 {
|
||||
fatalf("%s: unexpected: %d-bit int type - %s", lineno(pos), dt.BitSize, dtype)
|
||||
}
|
||||
|
||||
if t.Align = t.Size; t.Align >= c.ptrSize {
|
||||
t.Align = c.ptrSize
|
||||
}
|
||||
|
||||
switch t.Size {
|
||||
default:
|
||||
fatalf("%s: unexpected: %d-byte int type - %s", lineno(pos), t.Size, dtype)
|
||||
@ -2595,9 +2600,8 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
Len: c.intExpr(t.Size),
|
||||
Elt: c.uint8,
|
||||
}
|
||||
}
|
||||
if t.Align = t.Size; t.Align >= c.ptrSize {
|
||||
t.Align = c.ptrSize
|
||||
// t.Align is the alignment of the Go type.
|
||||
t.Align = 1
|
||||
}
|
||||
|
||||
case *dwarf.PtrType:
|
||||
@ -2826,6 +2830,11 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
if dt.BitSize > 0 {
|
||||
fatalf("%s: unexpected: %d-bit uint type - %s", lineno(pos), dt.BitSize, dtype)
|
||||
}
|
||||
|
||||
if t.Align = t.Size; t.Align >= c.ptrSize {
|
||||
t.Align = c.ptrSize
|
||||
}
|
||||
|
||||
switch t.Size {
|
||||
default:
|
||||
fatalf("%s: unexpected: %d-byte uint type - %s", lineno(pos), t.Size, dtype)
|
||||
@ -2842,9 +2851,8 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
Len: c.intExpr(t.Size),
|
||||
Elt: c.uint8,
|
||||
}
|
||||
}
|
||||
if t.Align = t.Size; t.Align >= c.ptrSize {
|
||||
t.Align = c.ptrSize
|
||||
// t.Align is the alignment of the Go type.
|
||||
t.Align = 1
|
||||
}
|
||||
|
||||
case *dwarf.VoidType:
|
||||
@ -3110,10 +3118,11 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
|
||||
}
|
||||
|
||||
// Round off up to talign, assumed to be a power of 2.
|
||||
origOff := off
|
||||
off = (off + talign - 1) &^ (talign - 1)
|
||||
|
||||
if f.ByteOffset > off {
|
||||
fld, sizes = c.pad(fld, sizes, f.ByteOffset-off)
|
||||
fld, sizes = c.pad(fld, sizes, f.ByteOffset-origOff)
|
||||
off = f.ByteOffset
|
||||
}
|
||||
if f.ByteOffset < off {
|
||||
|
@ -70,6 +70,7 @@ func Test31891(t *testing.T) { test31891(t) }
|
||||
func Test42018(t *testing.T) { test42018(t) }
|
||||
func Test45451(t *testing.T) { test45451(t) }
|
||||
func Test49633(t *testing.T) { test49633(t) }
|
||||
func Test69086(t *testing.T) { test69086(t) }
|
||||
func TestAlign(t *testing.T) { testAlign(t) }
|
||||
func TestAtol(t *testing.T) { testAtol(t) }
|
||||
func TestBlocking(t *testing.T) { testBlocking(t) }
|
||||
|
@ -940,6 +940,19 @@ typedef struct {
|
||||
} issue67517struct;
|
||||
static void issue67517(issue67517struct* p) {}
|
||||
|
||||
// Issue 69086.
|
||||
// GCC added the __int128 type in GCC 4.6, released in 2011.
|
||||
typedef struct {
|
||||
int a;
|
||||
#ifdef __SIZEOF_INT128__
|
||||
unsigned __int128 b;
|
||||
#else
|
||||
uint64_t b;
|
||||
#endif
|
||||
unsigned char c;
|
||||
} issue69086struct;
|
||||
static int issue690861(issue69086struct* p) { p->b = 1234; return p->c; }
|
||||
static int issue690862(unsigned long ul1, unsigned long ul2, unsigned int u, issue69086struct s) { return (int)(s.b); }
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@ -2349,3 +2362,24 @@ func issue67517() {
|
||||
b: nil,
|
||||
})
|
||||
}
|
||||
|
||||
// Issue 69086.
|
||||
func test69086(t *testing.T) {
|
||||
var s C.issue69086struct
|
||||
|
||||
typ := reflect.TypeOf(s)
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
f := typ.Field(i)
|
||||
t.Logf("field %d: name %s size %d align %d offset %d", i, f.Name, f.Type.Size(), f.Type.Align(), f.Offset)
|
||||
}
|
||||
|
||||
s.c = 1
|
||||
got := C.issue690861(&s)
|
||||
if got != 1 {
|
||||
t.Errorf("field: got %d, want 1", got)
|
||||
}
|
||||
got = C.issue690862(1, 2, 3, s)
|
||||
if got != 1234 {
|
||||
t.Errorf("call: got %d, want 1234", got)
|
||||
}
|
||||
}
|
||||
|
@ -318,9 +318,9 @@ func containsClosure(f, c *ir.Func) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Closures within function Foo are named like "Foo.funcN..."
|
||||
// Closures within function Foo are named like "Foo.funcN..." or "Foo-rangeN".
|
||||
// TODO(mdempsky): Better way to recognize this.
|
||||
fn := f.Sym().Name
|
||||
cn := c.Sym().Name
|
||||
return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.'
|
||||
return len(cn) > len(fn) && cn[:len(fn)] == fn && (cn[len(fn)] == '.' || cn[len(fn)] == '-')
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/types2"
|
||||
"cmd/internal/src"
|
||||
"internal/buildcfg"
|
||||
"internal/pkgbits"
|
||||
)
|
||||
|
||||
@ -411,6 +412,14 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types2.Package, string) {
|
||||
panic("weird")
|
||||
|
||||
case pkgbits.ObjAlias:
|
||||
if buildcfg.Experiment.AliasTypeParams && len(r.dict.bounds) > 0 {
|
||||
// Temporary work-around for issue #68526: rather than panicking
|
||||
// with an non-descriptive index-out-of-bounds panic when trying
|
||||
// to access a missing type parameter, instead panic with a more
|
||||
// descriptive error. Only needed for Go 1.23; Go 1.24 will have
|
||||
// the correct implementation.
|
||||
panic("importing generic type aliases is not supported in Go 1.23 (see issue #68526)")
|
||||
}
|
||||
pos := r.pos()
|
||||
typ := r.typ()
|
||||
return newAliasTypeName(pr.enableAlias, pos, objPkg, objName, typ)
|
||||
|
@ -134,10 +134,10 @@ func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
|
||||
// newAliasInstance creates a new alias instance for the given origin and type
|
||||
// arguments, recording pos as the position of its synthetic object (for error
|
||||
// reporting).
|
||||
func (check *Checker) newAliasInstance(pos syntax.Pos, orig *Alias, targs []Type, ctxt *Context) *Alias {
|
||||
func (check *Checker) newAliasInstance(pos syntax.Pos, orig *Alias, targs []Type, expanding *Named, ctxt *Context) *Alias {
|
||||
assert(len(targs) > 0)
|
||||
obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
|
||||
rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), nil, ctxt)
|
||||
rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), expanding, ctxt)
|
||||
res := check.newAlias(obj, rhs)
|
||||
res.orig = orig
|
||||
res.tparams = orig.tparams
|
||||
|
@ -2898,22 +2898,48 @@ func TestFileVersions(t *testing.T) {
|
||||
fileVersion string
|
||||
wantVersion string
|
||||
}{
|
||||
{"", "", ""}, // no versions specified
|
||||
{"go1.19", "", "go1.19"}, // module version specified
|
||||
{"", "go1.20", ""}, // file upgrade ignored
|
||||
{"go1.19", "go1.20", "go1.20"}, // file upgrade permitted
|
||||
{"go1.20", "go1.19", "go1.20"}, // file downgrade not permitted
|
||||
{"go1.21", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
|
||||
{"", "", ""}, // no versions specified
|
||||
{"go1.19", "", "go1.19"}, // module version specified
|
||||
{"", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1", "", "go1"}, // no file version specified
|
||||
{"go1", "goo1.22", "go1"}, // invalid file version specified
|
||||
{"go1", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||
{"go1", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||
{"go1.19", "", "go1.19"}, // no file version specified
|
||||
{"go1.19", "goo1.22", "go1.19"}, // invalid file version specified
|
||||
{"go1.19", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.19", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||
{"go1.19", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||
{"go1.20", "", "go1.20"}, // no file version specified
|
||||
{"go1.20", "goo1.22", "go1.20"}, // invalid file version specified
|
||||
{"go1.20", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.20", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.20", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||
{"go1.20", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||
{"go1.21", "", "go1.21"}, // no file version specified
|
||||
{"go1.21", "goo1.22", "go1.21"}, // invalid file version specified
|
||||
{"go1.21", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.21", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.21", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||
{"go1.21", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||
{"go1.22", "", "go1.22"}, // no file version specified
|
||||
{"go1.22", "goo1.22", "go1.22"}, // invalid file version specified
|
||||
{"go1.22", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.22", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.22", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||
{"go1.22", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||
|
||||
// versions containing release numbers
|
||||
// (file versions containing release numbers are considered invalid)
|
||||
{"go1.19.0", "", "go1.19.0"}, // no file version specified
|
||||
{"go1.20", "go1.20.1", "go1.20"}, // file upgrade ignored
|
||||
{"go1.20.1", "go1.20", "go1.20.1"}, // file upgrade ignored
|
||||
{"go1.20.1", "go1.21", "go1.21"}, // file upgrade permitted
|
||||
{"go1.20.1", "go1.19", "go1.20.1"}, // file downgrade not permitted
|
||||
{"go1.21.1", "go1.19.1", "go1.21.1"}, // file downgrade not permitted (invalid file version)
|
||||
{"go1.21.1", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
|
||||
{"go1.20.1", "go1.19.1", "go1.20.1"}, // invalid file version
|
||||
{"go1.20.1", "go1.21.1", "go1.20.1"}, // invalid file version
|
||||
{"go1.21.1", "go1.19.1", "go1.21.1"}, // invalid file version
|
||||
{"go1.21.1", "go1.21.1", "go1.21.1"}, // invalid file version
|
||||
{"go1.22.1", "go1.19.1", "go1.22.1"}, // invalid file version
|
||||
{"go1.22.1", "go1.21.1", "go1.22.1"}, // invalid file version
|
||||
} {
|
||||
var src string
|
||||
if test.fileVersion != "" {
|
||||
|
@ -327,7 +327,6 @@ func (check *Checker) initFiles(files []*syntax.File) {
|
||||
check.errorf(files[0], TooNew, "package requires newer Go version %v (application built with %v)",
|
||||
check.version, go_current)
|
||||
}
|
||||
downgradeOk := check.version.cmp(go1_21) >= 0
|
||||
|
||||
// determine Go version for each file
|
||||
for _, file := range check.files {
|
||||
@ -336,33 +335,18 @@ func (check *Checker) initFiles(files []*syntax.File) {
|
||||
// unlike file versions which are Go language versions only, if valid.)
|
||||
v := check.conf.GoVersion
|
||||
|
||||
fileVersion := asGoVersion(file.GoVersion)
|
||||
if fileVersion.isValid() {
|
||||
// use the file version, if applicable
|
||||
// (file versions are either the empty string or of the form go1.dd)
|
||||
if pkgVersionOk {
|
||||
cmp := fileVersion.cmp(check.version)
|
||||
// Go 1.21 introduced the feature of setting the go.mod
|
||||
// go line to an early version of Go and allowing //go:build lines
|
||||
// to “upgrade” (cmp > 0) the Go version in a given file.
|
||||
// We can do that backwards compatibly.
|
||||
//
|
||||
// Go 1.21 also introduced the feature of allowing //go:build lines
|
||||
// to “downgrade” (cmp < 0) the Go version in a given file.
|
||||
// That can't be done compatibly in general, since before the
|
||||
// build lines were ignored and code got the module's Go version.
|
||||
// To work around this, downgrades are only allowed when the
|
||||
// module's Go version is Go 1.21 or later.
|
||||
//
|
||||
// If there is no valid check.version, then we don't really know what
|
||||
// Go version to apply.
|
||||
// Legacy tools may do this, and they historically have accepted everything.
|
||||
// Preserve that behavior by ignoring //go:build constraints entirely in that
|
||||
// case (!pkgVersionOk).
|
||||
if cmp > 0 || cmp < 0 && downgradeOk {
|
||||
v = file.GoVersion
|
||||
}
|
||||
}
|
||||
// If the file specifies a version, use max(fileVersion, go1.21).
|
||||
if fileVersion := asGoVersion(file.GoVersion); fileVersion.isValid() {
|
||||
// Go 1.21 introduced the feature of allowing //go:build lines
|
||||
// to sometimes set the Go version in a given file. Versions Go 1.21 and later
|
||||
// can be set backwards compatibly as that was the first version
|
||||
// files with go1.21 or later build tags could be built with.
|
||||
//
|
||||
// Set the version to max(fileVersion, go1.21): That will allow a
|
||||
// downgrade to a version before go1.22, where the for loop semantics
|
||||
// change was made, while being backwards compatible with versions of
|
||||
// go before the new //go:build semantics were introduced.
|
||||
v = string(versionMax(fileVersion, go1_21))
|
||||
|
||||
// Report a specific error for each tagged file that's too new.
|
||||
// (Normally the build system will have filtered files by version,
|
||||
@ -377,6 +361,13 @@ func (check *Checker) initFiles(files []*syntax.File) {
|
||||
}
|
||||
}
|
||||
|
||||
func versionMax(a, b goVersion) goVersion {
|
||||
if a.cmp(b) > 0 {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// A bailout panic is used for early termination.
|
||||
type bailout struct{}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"cmd/compile/internal/syntax"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/buildcfg"
|
||||
. "internal/types/errors"
|
||||
)
|
||||
|
||||
@ -126,8 +127,9 @@ func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, e
|
||||
res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily
|
||||
|
||||
case *Alias:
|
||||
// TODO(gri) is this correct?
|
||||
assert(expanding == nil) // Alias instances cannot be reached from Named types
|
||||
if !buildcfg.Experiment.AliasTypeParams {
|
||||
assert(expanding == nil) // Alias instances cannot be reached from Named types
|
||||
}
|
||||
|
||||
tparams := orig.TypeParams()
|
||||
// TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
|
||||
@ -138,7 +140,7 @@ func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, e
|
||||
return orig // nothing to do (minor optimization)
|
||||
}
|
||||
|
||||
return check.newAliasInstance(pos, orig, targs, ctxt)
|
||||
return check.newAliasInstance(pos, orig, targs, expanding, ctxt)
|
||||
|
||||
case *Signature:
|
||||
assert(expanding == nil) // function instances cannot be reached from Named types
|
||||
|
@ -1121,3 +1121,23 @@ func f(x int) {
|
||||
t.Errorf("got: %s want: %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue68877(t *testing.T) {
|
||||
const src = `
|
||||
package p
|
||||
|
||||
type (
|
||||
S struct{}
|
||||
A = S
|
||||
T A
|
||||
)`
|
||||
|
||||
conf := Config{EnableAlias: true}
|
||||
pkg := mustTypecheck(src, &conf, nil)
|
||||
T := pkg.Scope().Lookup("T").(*TypeName)
|
||||
got := T.String() // this must not panic (was issue)
|
||||
const want = "type p.T struct{}"
|
||||
if got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +282,7 @@ func (t *Named) cleanup() {
|
||||
if t.TypeArgs().Len() == 0 {
|
||||
panic("nil underlying")
|
||||
}
|
||||
case *Named:
|
||||
case *Named, *Alias:
|
||||
t.under() // t.under may add entries to check.cleaners
|
||||
}
|
||||
t.check = nil
|
||||
|
@ -115,7 +115,7 @@ func (subst *subster) typ(typ Type) Type {
|
||||
// that has a type argument for it.
|
||||
targs, updated := subst.typeList(t.TypeArgs().list())
|
||||
if updated {
|
||||
return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.ctxt)
|
||||
return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.expanding, subst.ctxt)
|
||||
}
|
||||
|
||||
case *Array:
|
||||
|
@ -131,8 +131,8 @@ func (s *_TypeSet) underIs(f func(Type) bool) bool {
|
||||
}
|
||||
for _, t := range s.terms {
|
||||
assert(t.typ != nil)
|
||||
// x == under(x) for ~x terms
|
||||
u := t.typ
|
||||
// Unalias(x) == under(x) for ~x terms
|
||||
u := Unalias(t.typ)
|
||||
if !t.tilde {
|
||||
u = under(u)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ require (
|
||||
golang.org/x/mod v0.19.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/sys v0.22.0
|
||||
golang.org/x/telemetry v0.0.0-20240717194752-0b706e19b701
|
||||
golang.org/x/telemetry v0.0.0-20240828213427-40b6b7fe7147
|
||||
golang.org/x/term v0.20.0
|
||||
golang.org/x/tools v0.22.1-0.20240618181713-f2d2ebe43e72
|
||||
)
|
||||
|
@ -16,8 +16,8 @@ golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240717194752-0b706e19b701 h1:+bltxAtk8YFEQ61B/lcYQM8e+7XjLwSDbpspVaVYkz8=
|
||||
golang.org/x/telemetry v0.0.0-20240717194752-0b706e19b701/go.mod h1:amNmu/SBSm2GAF3X+9U2C0epLocdh+r5Z+7oMYO5cLM=
|
||||
golang.org/x/telemetry v0.0.0-20240828213427-40b6b7fe7147 h1:Lj8KbuZmoFUbI6pQ28G3Diz/5bRYD2UY5vfAmhrLZWo=
|
||||
golang.org/x/telemetry v0.0.0-20240828213427-40b6b7fe7147/go.mod h1:amNmu/SBSm2GAF3X+9U2C0epLocdh+r5Z+7oMYO5cLM=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
|
@ -805,13 +805,19 @@ func elfwritefreebsdsig(out *OutBuf) int {
|
||||
return int(sh.Size)
|
||||
}
|
||||
|
||||
func addbuildinfo(val string) {
|
||||
func addbuildinfo(ctxt *Link) {
|
||||
val := *flagHostBuildid
|
||||
if val == "gobuildid" {
|
||||
buildID := *flagBuildid
|
||||
if buildID == "" {
|
||||
Exitf("-B gobuildid requires a Go build ID supplied via -buildid")
|
||||
}
|
||||
|
||||
if ctxt.IsDarwin() {
|
||||
buildinfo = uuidFromGoBuildId(buildID)
|
||||
return
|
||||
}
|
||||
|
||||
hashedBuildID := notsha256.Sum256([]byte(buildID))
|
||||
buildinfo = hashedBuildID[:20]
|
||||
|
||||
@ -821,11 +827,13 @@ func addbuildinfo(val string) {
|
||||
if !strings.HasPrefix(val, "0x") {
|
||||
Exitf("-B argument must start with 0x: %s", val)
|
||||
}
|
||||
|
||||
ov := val
|
||||
val = val[2:]
|
||||
|
||||
const maxLen = 32
|
||||
maxLen := 32
|
||||
if ctxt.IsDarwin() {
|
||||
maxLen = 16
|
||||
}
|
||||
if hex.DecodedLen(len(val)) > maxLen {
|
||||
Exitf("-B option too long (max %d digits): %s", maxLen, ov)
|
||||
}
|
||||
|
@ -297,6 +297,8 @@ func getMachoHdr() *MachoHdr {
|
||||
return &machohdr
|
||||
}
|
||||
|
||||
// Create a new Mach-O load command. ndata is the number of 32-bit words for
|
||||
// the data (not including the load command header).
|
||||
func newMachoLoad(arch *sys.Arch, type_ uint32, ndata uint32) *MachoLoad {
|
||||
if arch.PtrSize == 8 && (ndata&1 != 0) {
|
||||
ndata++
|
||||
@ -849,6 +851,20 @@ func asmbMacho(ctxt *Link) {
|
||||
}
|
||||
}
|
||||
|
||||
if ctxt.IsInternal() && len(buildinfo) > 0 {
|
||||
ml := newMachoLoad(ctxt.Arch, LC_UUID, 4)
|
||||
// Mach-O UUID is 16 bytes
|
||||
if len(buildinfo) < 16 {
|
||||
buildinfo = append(buildinfo, make([]byte, 16)...)
|
||||
}
|
||||
// By default, buildinfo is already in UUIDv3 format
|
||||
// (see uuidFromGoBuildId).
|
||||
ml.data[0] = ctxt.Arch.ByteOrder.Uint32(buildinfo)
|
||||
ml.data[1] = ctxt.Arch.ByteOrder.Uint32(buildinfo[4:])
|
||||
ml.data[2] = ctxt.Arch.ByteOrder.Uint32(buildinfo[8:])
|
||||
ml.data[3] = ctxt.Arch.ByteOrder.Uint32(buildinfo[12:])
|
||||
}
|
||||
|
||||
if ctxt.IsInternal() && ctxt.NeedCodeSign() {
|
||||
ml := newMachoLoad(ctxt.Arch, LC_CODE_SIGNATURE, 2)
|
||||
ml.data[0] = uint32(codesigOff)
|
||||
|
@ -42,7 +42,7 @@ func uuidFromGoBuildId(buildID string) []byte {
|
||||
// to use this UUID flavor than any of the others. This is similar
|
||||
// to how other linkers handle this (for example this code in lld:
|
||||
// https://github.com/llvm/llvm-project/blob/2a3a79ce4c2149d7787d56f9841b66cacc9061d0/lld/MachO/Writer.cpp#L524).
|
||||
rv[6] &= 0xcf
|
||||
rv[6] &= 0x0f
|
||||
rv[6] |= 0x30
|
||||
rv[8] &= 0x3f
|
||||
rv[8] |= 0xc0
|
||||
|
@ -95,6 +95,7 @@ var (
|
||||
flagN = flag.Bool("n", false, "no-op (deprecated)")
|
||||
FlagS = flag.Bool("s", false, "disable symbol table")
|
||||
flag8 bool // use 64-bit addresses in symbol table
|
||||
flagHostBuildid = flag.String("B", "", "set ELF NT_GNU_BUILD_ID `note` or Mach-O UUID; use \"gobuildid\" to generate it from the Go build ID")
|
||||
flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
|
||||
flagCheckLinkname = flag.Bool("checklinkname", true, "check linkname symbol references")
|
||||
FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
|
||||
@ -196,7 +197,6 @@ func Main(arch *sys.Arch, theArch Arch) {
|
||||
flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`")
|
||||
flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`")
|
||||
flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible")
|
||||
objabi.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF; use \"gobuildid\" to generate it from the Go build ID", addbuildinfo)
|
||||
objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) })
|
||||
objabi.AddVersionFlag() // -V
|
||||
objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })
|
||||
@ -294,6 +294,10 @@ func Main(arch *sys.Arch, theArch Arch) {
|
||||
*flagBuildid = "go-openbsd"
|
||||
}
|
||||
|
||||
if *flagHostBuildid != "" {
|
||||
addbuildinfo(ctxt)
|
||||
}
|
||||
|
||||
// enable benchmarking
|
||||
var bench *benchmark.Metrics
|
||||
if len(*benchmarkFlag) != 0 {
|
||||
|
11
src/cmd/vendor/golang.org/x/telemetry/internal/configstore/download.go
generated
vendored
11
src/cmd/vendor/golang.org/x/telemetry/internal/configstore/download.go
generated
vendored
@ -16,6 +16,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/telemetry/internal/telemetry"
|
||||
)
|
||||
@ -29,12 +30,22 @@ const (
|
||||
// creation flag.
|
||||
var needNoConsole = func(cmd *exec.Cmd) {}
|
||||
|
||||
var downloads int64
|
||||
|
||||
// Downloads reports, for testing purposes, the number of times [Download] has
|
||||
// been called.
|
||||
func Downloads() int64 {
|
||||
return atomic.LoadInt64(&downloads)
|
||||
}
|
||||
|
||||
// Download fetches the requested telemetry UploadConfig using "go mod
|
||||
// download". If envOverlay is provided, it is appended to the environment used
|
||||
// for invoking the go command.
|
||||
//
|
||||
// The second result is the canonical version of the requested configuration.
|
||||
func Download(version string, envOverlay []string) (*telemetry.UploadConfig, string, error) {
|
||||
atomic.AddInt64(&downloads, 1)
|
||||
|
||||
if version == "" {
|
||||
version = "latest"
|
||||
}
|
||||
|
4
src/cmd/vendor/golang.org/x/telemetry/internal/crashmonitor/monitor.go
generated
vendored
4
src/cmd/vendor/golang.org/x/telemetry/internal/crashmonitor/monitor.go
generated
vendored
@ -21,12 +21,12 @@ import (
|
||||
"golang.org/x/telemetry/internal/counter"
|
||||
)
|
||||
|
||||
// Supported reports whether the runtime supports [runtime.SetCrashOutput].
|
||||
// Supported reports whether the runtime supports [runtime/debug.SetCrashOutput].
|
||||
//
|
||||
// TODO(adonovan): eliminate once go1.23+ is assured.
|
||||
func Supported() bool { return setCrashOutput != nil }
|
||||
|
||||
var setCrashOutput func(*os.File) error // = runtime.SetCrashOutput on go1.23+
|
||||
var setCrashOutput func(*os.File) error // = runtime/debug.SetCrashOutput on go1.23+
|
||||
|
||||
// Parent sets up the parent side of the crashmonitor. It requires
|
||||
// exclusive use of a writable pipe connected to the child process's stdin.
|
||||
|
21
src/cmd/vendor/golang.org/x/telemetry/internal/upload/run.go
generated
vendored
21
src/cmd/vendor/golang.org/x/telemetry/internal/upload/run.go
generated
vendored
@ -112,9 +112,24 @@ func newUploader(rcfg RunConfig) (*uploader, error) {
|
||||
logger := log.New(logWriter, "", log.Ltime|log.Lmicroseconds|log.Lshortfile)
|
||||
|
||||
// Fetch the upload config, if it is not provided.
|
||||
config, configVersion, err := configstore.Download("latest", rcfg.Env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var (
|
||||
config *telemetry.UploadConfig
|
||||
configVersion string
|
||||
)
|
||||
|
||||
if mode, _ := dir.Mode(); mode == "on" {
|
||||
// golang/go#68946: only download the upload config if it will be used.
|
||||
//
|
||||
// TODO(rfindley): This is a narrow change aimed at minimally fixing the
|
||||
// associated bug. In the future, we should read the mode only once during
|
||||
// the upload process.
|
||||
config, configVersion, err = configstore.Download("latest", rcfg.Env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
config = &telemetry.UploadConfig{}
|
||||
configVersion = "v0.0.0-0"
|
||||
}
|
||||
|
||||
// Set the start time, if it is not provided.
|
||||
|
19
src/cmd/vendor/golang.org/x/telemetry/start.go
generated
vendored
19
src/cmd/vendor/golang.org/x/telemetry/start.go
generated
vendored
@ -206,7 +206,8 @@ func startChild(reportCrashes, upload bool, result *StartResult) {
|
||||
fd, err := os.Stat(telemetry.Default.DebugDir())
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
log.Fatalf("failed to stat debug directory: %v", err)
|
||||
log.Printf("failed to stat debug directory: %v", err)
|
||||
return
|
||||
}
|
||||
} else if fd.IsDir() {
|
||||
// local/debug exists and is a directory. Set stderr to a log file path
|
||||
@ -214,23 +215,31 @@ func startChild(reportCrashes, upload bool, result *StartResult) {
|
||||
childLogPath := filepath.Join(telemetry.Default.DebugDir(), "sidecar.log")
|
||||
childLog, err := os.OpenFile(childLogPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("opening sidecar log file for child: %v", err)
|
||||
log.Printf("opening sidecar log file for child: %v", err)
|
||||
return
|
||||
}
|
||||
defer childLog.Close()
|
||||
cmd.Stderr = childLog
|
||||
}
|
||||
|
||||
var crashOutputFile *os.File
|
||||
if reportCrashes {
|
||||
pipe, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
log.Fatalf("StdinPipe: %v", err)
|
||||
log.Printf("StdinPipe: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
crashmonitor.Parent(pipe.(*os.File)) // (this conversion is safe)
|
||||
crashOutputFile = pipe.(*os.File) // (this conversion is safe)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("can't start telemetry child process: %v", err)
|
||||
// The child couldn't be started. Log the failure.
|
||||
log.Printf("can't start telemetry child process: %v", err)
|
||||
return
|
||||
}
|
||||
if reportCrashes {
|
||||
crashmonitor.Parent(crashOutputFile)
|
||||
}
|
||||
result.wg.Add(1)
|
||||
go func() {
|
||||
|
2
src/cmd/vendor/modules.txt
vendored
2
src/cmd/vendor/modules.txt
vendored
@ -45,7 +45,7 @@ golang.org/x/sync/semaphore
|
||||
golang.org/x/sys/plan9
|
||||
golang.org/x/sys/unix
|
||||
golang.org/x/sys/windows
|
||||
# golang.org/x/telemetry v0.0.0-20240717194752-0b706e19b701
|
||||
# golang.org/x/telemetry v0.0.0-20240828213427-40b6b7fe7147
|
||||
## explicit; go 1.20
|
||||
golang.org/x/telemetry
|
||||
golang.org/x/telemetry/counter
|
||||
|
@ -491,9 +491,10 @@ func testHandshake(t *testing.T, clientConfig, serverConfig *Config) (serverStat
|
||||
if got := string(buf); got != sentinel {
|
||||
t.Errorf("read %q from TLS connection, but expected %q", got, sentinel)
|
||||
}
|
||||
if err := cli.Close(); err != nil {
|
||||
t.Errorf("failed to call cli.Close: %v", err)
|
||||
}
|
||||
// We discard the error because after ReadAll returns the server must
|
||||
// have already closed the connection. Sending data (the closeNotify
|
||||
// alert) can cause a reset, that will make Close return an error.
|
||||
cli.Close()
|
||||
}()
|
||||
server := Server(s, serverConfig)
|
||||
err = server.Handshake()
|
||||
|
@ -1368,8 +1368,8 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn
|
||||
|
||||
db.waitDuration.Add(int64(time.Since(waitStart)))
|
||||
|
||||
// If we failed to delete it, that means something else
|
||||
// grabbed it and is about to send on it.
|
||||
// If we failed to delete it, that means either the DB was closed or
|
||||
// something else grabbed it and is about to send on it.
|
||||
if !deleted {
|
||||
// TODO(bradfitz): rather than this best effort select, we
|
||||
// should probably start a goroutine to read from req. This best
|
||||
@ -3594,6 +3594,7 @@ type connRequestAndIndex struct {
|
||||
// and clears the set.
|
||||
func (s *connRequestSet) CloseAndRemoveAll() {
|
||||
for _, v := range s.s {
|
||||
*v.curIdx = -1
|
||||
close(v.req)
|
||||
}
|
||||
s.s = nil
|
||||
|
@ -4920,6 +4920,17 @@ func TestConnRequestSet(t *testing.T) {
|
||||
t.Error("wasn't random")
|
||||
}
|
||||
})
|
||||
t.Run("close-delete", func(t *testing.T) {
|
||||
reset()
|
||||
ch := make(chan connRequest)
|
||||
dh := s.Add(ch)
|
||||
wantLen(1)
|
||||
s.CloseAndRemoveAll()
|
||||
wantLen(0)
|
||||
if s.Delete(dh) {
|
||||
t.Error("unexpected delete after CloseAndRemoveAll")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkConnRequestSet(b *testing.B) {
|
||||
|
@ -911,8 +911,11 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
|
||||
var maxIgnoreNestingDepth = 10000
|
||||
|
||||
// decIgnoreOpFor returns the decoding op for a field that has no destination.
|
||||
func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp, depth int) *decOp {
|
||||
if depth > maxIgnoreNestingDepth {
|
||||
func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp) *decOp {
|
||||
// Track how deep we've recursed trying to skip nested ignored fields.
|
||||
dec.ignoreDepth++
|
||||
defer func() { dec.ignoreDepth-- }()
|
||||
if dec.ignoreDepth > maxIgnoreNestingDepth {
|
||||
error_(errors.New("invalid nesting depth"))
|
||||
}
|
||||
// If this type is already in progress, it's a recursive type (e.g. map[string]*T).
|
||||
@ -938,7 +941,7 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp,
|
||||
errorf("bad data: undefined type %s", wireId.string())
|
||||
case wire.ArrayT != nil:
|
||||
elemId := wire.ArrayT.Elem
|
||||
elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1)
|
||||
elemOp := dec.decIgnoreOpFor(elemId, inProgress)
|
||||
op = func(i *decInstr, state *decoderState, value reflect.Value) {
|
||||
state.dec.ignoreArray(state, *elemOp, wire.ArrayT.Len)
|
||||
}
|
||||
@ -946,15 +949,15 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp,
|
||||
case wire.MapT != nil:
|
||||
keyId := dec.wireType[wireId].MapT.Key
|
||||
elemId := dec.wireType[wireId].MapT.Elem
|
||||
keyOp := dec.decIgnoreOpFor(keyId, inProgress, depth+1)
|
||||
elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1)
|
||||
keyOp := dec.decIgnoreOpFor(keyId, inProgress)
|
||||
elemOp := dec.decIgnoreOpFor(elemId, inProgress)
|
||||
op = func(i *decInstr, state *decoderState, value reflect.Value) {
|
||||
state.dec.ignoreMap(state, *keyOp, *elemOp)
|
||||
}
|
||||
|
||||
case wire.SliceT != nil:
|
||||
elemId := wire.SliceT.Elem
|
||||
elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1)
|
||||
elemOp := dec.decIgnoreOpFor(elemId, inProgress)
|
||||
op = func(i *decInstr, state *decoderState, value reflect.Value) {
|
||||
state.dec.ignoreSlice(state, *elemOp)
|
||||
}
|
||||
@ -1115,7 +1118,7 @@ func (dec *Decoder) compileSingle(remoteId typeId, ut *userTypeInfo) (engine *de
|
||||
func (dec *Decoder) compileIgnoreSingle(remoteId typeId) *decEngine {
|
||||
engine := new(decEngine)
|
||||
engine.instr = make([]decInstr, 1) // one item
|
||||
op := dec.decIgnoreOpFor(remoteId, make(map[typeId]*decOp), 0)
|
||||
op := dec.decIgnoreOpFor(remoteId, make(map[typeId]*decOp))
|
||||
ovfl := overflow(dec.typeString(remoteId))
|
||||
engine.instr[0] = decInstr{*op, 0, nil, ovfl}
|
||||
engine.numInstr = 1
|
||||
@ -1160,7 +1163,7 @@ func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEn
|
||||
localField, present := srt.FieldByName(wireField.Name)
|
||||
// TODO(r): anonymous names
|
||||
if !present || !isExported(wireField.Name) {
|
||||
op := dec.decIgnoreOpFor(wireField.Id, make(map[typeId]*decOp), 0)
|
||||
op := dec.decIgnoreOpFor(wireField.Id, make(map[typeId]*decOp))
|
||||
engine.instr[fieldnum] = decInstr{*op, fieldnum, nil, ovfl}
|
||||
continue
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ type Decoder struct {
|
||||
freeList *decoderState // list of free decoderStates; avoids reallocation
|
||||
countBuf []byte // used for decoding integers while parsing messages
|
||||
err error
|
||||
// ignoreDepth tracks the depth of recursively parsed ignored fields
|
||||
ignoreDepth int
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder that reads from the [io.Reader].
|
||||
|
@ -806,6 +806,8 @@ func TestIgnoreDepthLimit(t *testing.T) {
|
||||
defer func() { maxIgnoreNestingDepth = oldNestingDepth }()
|
||||
b := new(bytes.Buffer)
|
||||
enc := NewEncoder(b)
|
||||
|
||||
// Nested slice
|
||||
typ := reflect.TypeFor[int]()
|
||||
nested := reflect.ArrayOf(1, typ)
|
||||
for i := 0; i < 100; i++ {
|
||||
@ -819,4 +821,16 @@ func TestIgnoreDepthLimit(t *testing.T) {
|
||||
if err := dec.Decode(&output); err == nil || err.Error() != expectedErr {
|
||||
t.Errorf("Decode didn't fail with depth limit of 100: want %q, got %q", expectedErr, err)
|
||||
}
|
||||
|
||||
// Nested struct
|
||||
nested = reflect.StructOf([]reflect.StructField{{Name: "F", Type: typ}})
|
||||
for i := 0; i < 100; i++ {
|
||||
nested = reflect.StructOf([]reflect.StructField{{Name: "F", Type: nested}})
|
||||
}
|
||||
badStruct = reflect.New(reflect.StructOf([]reflect.StructField{{Name: "F", Type: nested}}))
|
||||
enc.Encode(badStruct.Interface())
|
||||
dec = NewDecoder(b)
|
||||
if err := dec.Decode(&output); err == nil || err.Error() != expectedErr {
|
||||
t.Errorf("Decode didn't fail with depth limit of 100: want %q, got %q", expectedErr, err)
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,10 @@ import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// maxSize is a limit used to control the complexity of expressions, in order
|
||||
// to prevent stack exhaustion issues due to recursion.
|
||||
const maxSize = 1000
|
||||
|
||||
// An Expr is a build tag constraint expression.
|
||||
// The underlying concrete type is *[AndExpr], *[OrExpr], *[NotExpr], or *[TagExpr].
|
||||
type Expr interface {
|
||||
@ -151,7 +155,7 @@ func Parse(line string) (Expr, error) {
|
||||
return parseExpr(text)
|
||||
}
|
||||
if text, ok := splitPlusBuild(line); ok {
|
||||
return parsePlusBuildExpr(text), nil
|
||||
return parsePlusBuildExpr(text)
|
||||
}
|
||||
return nil, errNotConstraint
|
||||
}
|
||||
@ -201,6 +205,8 @@ type exprParser struct {
|
||||
tok string // last token read
|
||||
isTag bool
|
||||
pos int // position (start) of last token
|
||||
|
||||
size int
|
||||
}
|
||||
|
||||
// parseExpr parses a boolean build tag expression.
|
||||
@ -249,6 +255,10 @@ func (p *exprParser) and() Expr {
|
||||
// On entry, the next input token has not yet been lexed.
|
||||
// On exit, the next input token has been lexed and is in p.tok.
|
||||
func (p *exprParser) not() Expr {
|
||||
p.size++
|
||||
if p.size > maxSize {
|
||||
panic(&SyntaxError{Offset: p.pos, Err: "build expression too large"})
|
||||
}
|
||||
p.lex()
|
||||
if p.tok == "!" {
|
||||
p.lex()
|
||||
@ -388,7 +398,13 @@ func splitPlusBuild(line string) (expr string, ok bool) {
|
||||
}
|
||||
|
||||
// parsePlusBuildExpr parses a legacy build tag expression (as used with “// +build”).
|
||||
func parsePlusBuildExpr(text string) Expr {
|
||||
func parsePlusBuildExpr(text string) (Expr, error) {
|
||||
// Only allow up to 100 AND/OR operators for "old" syntax.
|
||||
// This is much less than the limit for "new" syntax,
|
||||
// but uses of old syntax were always very simple.
|
||||
const maxOldSize = 100
|
||||
size := 0
|
||||
|
||||
var x Expr
|
||||
for _, clause := range strings.Fields(text) {
|
||||
var y Expr
|
||||
@ -414,19 +430,25 @@ func parsePlusBuildExpr(text string) Expr {
|
||||
if y == nil {
|
||||
y = z
|
||||
} else {
|
||||
if size++; size > maxOldSize {
|
||||
return nil, errComplex
|
||||
}
|
||||
y = and(y, z)
|
||||
}
|
||||
}
|
||||
if x == nil {
|
||||
x = y
|
||||
} else {
|
||||
if size++; size > maxOldSize {
|
||||
return nil, errComplex
|
||||
}
|
||||
x = or(x, y)
|
||||
}
|
||||
}
|
||||
if x == nil {
|
||||
x = tag("ignore")
|
||||
}
|
||||
return x
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// isValidTag reports whether the word is a valid build tag.
|
||||
|
@ -222,7 +222,7 @@ var parsePlusBuildExprTests = []struct {
|
||||
func TestParsePlusBuildExpr(t *testing.T) {
|
||||
for i, tt := range parsePlusBuildExprTests {
|
||||
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||
x := parsePlusBuildExpr(tt.in)
|
||||
x, _ := parsePlusBuildExpr(tt.in)
|
||||
if x.String() != tt.x.String() {
|
||||
t.Errorf("parsePlusBuildExpr(%q):\nhave %v\nwant %v", tt.in, x, tt.x)
|
||||
}
|
||||
@ -319,3 +319,66 @@ func TestPlusBuildLines(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSizeLimits(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
expr string
|
||||
}{
|
||||
{
|
||||
name: "go:build or limit",
|
||||
expr: "//go:build " + strings.Repeat("a || ", maxSize+2),
|
||||
},
|
||||
{
|
||||
name: "go:build and limit",
|
||||
expr: "//go:build " + strings.Repeat("a && ", maxSize+2),
|
||||
},
|
||||
{
|
||||
name: "go:build and depth limit",
|
||||
expr: "//go:build " + strings.Repeat("(a &&", maxSize+2),
|
||||
},
|
||||
{
|
||||
name: "go:build or depth limit",
|
||||
expr: "//go:build " + strings.Repeat("(a ||", maxSize+2),
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := Parse(tc.expr)
|
||||
if err == nil {
|
||||
t.Error("expression did not trigger limit")
|
||||
} else if syntaxErr, ok := err.(*SyntaxError); !ok || syntaxErr.Err != "build expression too large" {
|
||||
if !ok {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
} else {
|
||||
t.Errorf("unexpected syntax error: %s", syntaxErr.Err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlusSizeLimits(t *testing.T) {
|
||||
maxOldSize := 100
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
expr string
|
||||
}{
|
||||
{
|
||||
name: "+build or limit",
|
||||
expr: "// +build " + strings.Repeat("a ", maxOldSize+2),
|
||||
},
|
||||
{
|
||||
name: "+build and limit",
|
||||
expr: "// +build " + strings.Repeat("a,", maxOldSize+2),
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := Parse(tc.expr)
|
||||
if err == nil {
|
||||
t.Error("expression did not trigger limit")
|
||||
} else if err != errComplex {
|
||||
t.Errorf("unexpected error: got %q, want %q", err, errComplex)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1676,6 +1676,8 @@ func (p *parser) parseElementList() (list []ast.Expr) {
|
||||
}
|
||||
|
||||
func (p *parser) parseLiteralValue(typ ast.Expr) ast.Expr {
|
||||
defer decNestLev(incNestLev(p))
|
||||
|
||||
if p.trace {
|
||||
defer un(trace(p, "LiteralValue"))
|
||||
}
|
||||
|
@ -598,10 +598,11 @@ var parseDepthTests = []struct {
|
||||
{name: "chan2", format: "package main; var x «<-chan »int"},
|
||||
{name: "interface", format: "package main; var x «interface { M() «int» }»", scope: true, scopeMultiplier: 2}, // Scopes: InterfaceType, FuncType
|
||||
{name: "map", format: "package main; var x «map[int]»int"},
|
||||
{name: "slicelit", format: "package main; var x = «[]any{«»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
|
||||
{name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
|
||||
{name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
|
||||
{name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 2}, // Parser nodes: CompositeLit, KeyValueExpr
|
||||
{name: "slicelit", format: "package main; var x = []any{«[]any{«»}»}", parseMultiplier: 3}, // Parser nodes: UnaryExpr, CompositeLit
|
||||
{name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 3}, // Parser nodes: UnaryExpr, CompositeLit
|
||||
{name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 3}, // Parser nodes: UnaryExpr, CompositeLit
|
||||
{name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 3}, // Parser nodes: CompositeLit, KeyValueExpr
|
||||
{name: "element", format: "package main; var x = struct{x any}{x: «{«»}»}"},
|
||||
{name: "dot", format: "package main; var x = «x.»x"},
|
||||
{name: "index", format: "package main; var x = x«[1]»"},
|
||||
{name: "slice", format: "package main; var x = x«[1:2]»"},
|
||||
|
@ -137,10 +137,10 @@ func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
|
||||
// newAliasInstance creates a new alias instance for the given origin and type
|
||||
// arguments, recording pos as the position of its synthetic object (for error
|
||||
// reporting).
|
||||
func (check *Checker) newAliasInstance(pos token.Pos, orig *Alias, targs []Type, ctxt *Context) *Alias {
|
||||
func (check *Checker) newAliasInstance(pos token.Pos, orig *Alias, targs []Type, expanding *Named, ctxt *Context) *Alias {
|
||||
assert(len(targs) > 0)
|
||||
obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
|
||||
rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), nil, ctxt)
|
||||
rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), expanding, ctxt)
|
||||
res := check.newAlias(obj, rhs)
|
||||
res.orig = orig
|
||||
res.tparams = orig.tparams
|
||||
|
@ -2904,22 +2904,48 @@ func TestFileVersions(t *testing.T) {
|
||||
fileVersion string
|
||||
wantVersion string
|
||||
}{
|
||||
{"", "", ""}, // no versions specified
|
||||
{"go1.19", "", "go1.19"}, // module version specified
|
||||
{"", "go1.20", ""}, // file upgrade ignored
|
||||
{"go1.19", "go1.20", "go1.20"}, // file upgrade permitted
|
||||
{"go1.20", "go1.19", "go1.20"}, // file downgrade not permitted
|
||||
{"go1.21", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
|
||||
{"", "", ""}, // no versions specified
|
||||
{"go1.19", "", "go1.19"}, // module version specified
|
||||
{"", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1", "", "go1"}, // no file version specified
|
||||
{"go1", "goo1.22", "go1"}, // invalid file version specified
|
||||
{"go1", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||
{"go1", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||
{"go1.19", "", "go1.19"}, // no file version specified
|
||||
{"go1.19", "goo1.22", "go1.19"}, // invalid file version specified
|
||||
{"go1.19", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.19", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||
{"go1.19", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||
{"go1.20", "", "go1.20"}, // no file version specified
|
||||
{"go1.20", "goo1.22", "go1.20"}, // invalid file version specified
|
||||
{"go1.20", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.20", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.20", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||
{"go1.20", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||
{"go1.21", "", "go1.21"}, // no file version specified
|
||||
{"go1.21", "goo1.22", "go1.21"}, // invalid file version specified
|
||||
{"go1.21", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.21", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.21", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||
{"go1.21", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||
{"go1.22", "", "go1.22"}, // no file version specified
|
||||
{"go1.22", "goo1.22", "go1.22"}, // invalid file version specified
|
||||
{"go1.22", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.22", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
|
||||
{"go1.22", "go1.21", "go1.21"}, // file version specified at 1.21
|
||||
{"go1.22", "go1.22", "go1.22"}, // file version specified above 1.21
|
||||
|
||||
// versions containing release numbers
|
||||
// (file versions containing release numbers are considered invalid)
|
||||
{"go1.19.0", "", "go1.19.0"}, // no file version specified
|
||||
{"go1.20", "go1.20.1", "go1.20"}, // file upgrade ignored
|
||||
{"go1.20.1", "go1.20", "go1.20.1"}, // file upgrade ignored
|
||||
{"go1.20.1", "go1.21", "go1.21"}, // file upgrade permitted
|
||||
{"go1.20.1", "go1.19", "go1.20.1"}, // file downgrade not permitted
|
||||
{"go1.21.1", "go1.19.1", "go1.21.1"}, // file downgrade not permitted (invalid file version)
|
||||
{"go1.21.1", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
|
||||
{"go1.20.1", "go1.19.1", "go1.20.1"}, // invalid file version
|
||||
{"go1.20.1", "go1.21.1", "go1.20.1"}, // invalid file version
|
||||
{"go1.21.1", "go1.19.1", "go1.21.1"}, // invalid file version
|
||||
{"go1.21.1", "go1.21.1", "go1.21.1"}, // invalid file version
|
||||
{"go1.22.1", "go1.19.1", "go1.22.1"}, // invalid file version
|
||||
{"go1.22.1", "go1.21.1", "go1.22.1"}, // invalid file version
|
||||
} {
|
||||
var src string
|
||||
if test.fileVersion != "" {
|
||||
|
@ -349,7 +349,6 @@ func (check *Checker) initFiles(files []*ast.File) {
|
||||
check.errorf(files[0], TooNew, "package requires newer Go version %v (application built with %v)",
|
||||
check.version, go_current)
|
||||
}
|
||||
downgradeOk := check.version.cmp(go1_21) >= 0
|
||||
|
||||
// determine Go version for each file
|
||||
for _, file := range check.files {
|
||||
@ -358,33 +357,19 @@ func (check *Checker) initFiles(files []*ast.File) {
|
||||
// unlike file versions which are Go language versions only, if valid.)
|
||||
v := check.conf.GoVersion
|
||||
|
||||
fileVersion := asGoVersion(file.GoVersion)
|
||||
if fileVersion.isValid() {
|
||||
// use the file version, if applicable
|
||||
// (file versions are either the empty string or of the form go1.dd)
|
||||
if pkgVersionOk {
|
||||
cmp := fileVersion.cmp(check.version)
|
||||
// Go 1.21 introduced the feature of setting the go.mod
|
||||
// go line to an early version of Go and allowing //go:build lines
|
||||
// to “upgrade” (cmp > 0) the Go version in a given file.
|
||||
// We can do that backwards compatibly.
|
||||
//
|
||||
// Go 1.21 also introduced the feature of allowing //go:build lines
|
||||
// to “downgrade” (cmp < 0) the Go version in a given file.
|
||||
// That can't be done compatibly in general, since before the
|
||||
// build lines were ignored and code got the module's Go version.
|
||||
// To work around this, downgrades are only allowed when the
|
||||
// module's Go version is Go 1.21 or later.
|
||||
//
|
||||
// If there is no valid check.version, then we don't really know what
|
||||
// Go version to apply.
|
||||
// Legacy tools may do this, and they historically have accepted everything.
|
||||
// Preserve that behavior by ignoring //go:build constraints entirely in that
|
||||
// case (!pkgVersionOk).
|
||||
if cmp > 0 || cmp < 0 && downgradeOk {
|
||||
v = file.GoVersion
|
||||
}
|
||||
}
|
||||
// If the file specifies a version, use max(fileVersion, go1.21).
|
||||
if fileVersion := asGoVersion(file.GoVersion); fileVersion.isValid() {
|
||||
// Go 1.21 introduced the feature of setting the go.mod
|
||||
// go line to an early version of Go and allowing //go:build lines
|
||||
// to set the Go version in a given file. Versions Go 1.21 and later
|
||||
// can be set backwards compatibly as that was the first version
|
||||
// files with go1.21 or later build tags could be built with.
|
||||
//
|
||||
// Set the version to max(fileVersion, go1.21): That will allow a
|
||||
// downgrade to a version before go1.22, where the for loop semantics
|
||||
// change was made, while being backwards compatible with versions of
|
||||
// go before the new //go:build semantics were introduced.
|
||||
v = string(versionMax(fileVersion, go1_21))
|
||||
|
||||
// Report a specific error for each tagged file that's too new.
|
||||
// (Normally the build system will have filtered files by version,
|
||||
@ -399,6 +384,13 @@ func (check *Checker) initFiles(files []*ast.File) {
|
||||
}
|
||||
}
|
||||
|
||||
func versionMax(a, b goVersion) goVersion {
|
||||
if a.cmp(b) < 0 {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// A bailout panic is used for early termination.
|
||||
type bailout struct{}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"internal/buildcfg"
|
||||
. "internal/types/errors"
|
||||
)
|
||||
|
||||
@ -129,8 +130,9 @@ func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, ex
|
||||
res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily
|
||||
|
||||
case *Alias:
|
||||
// TODO(gri) is this correct?
|
||||
assert(expanding == nil) // Alias instances cannot be reached from Named types
|
||||
if !buildcfg.Experiment.AliasTypeParams {
|
||||
assert(expanding == nil) // Alias instances cannot be reached from Named types
|
||||
}
|
||||
|
||||
tparams := orig.TypeParams()
|
||||
// TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
|
||||
@ -141,7 +143,7 @@ func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, ex
|
||||
return orig // nothing to do (minor optimization)
|
||||
}
|
||||
|
||||
return check.newAliasInstance(pos, orig, targs, ctxt)
|
||||
return check.newAliasInstance(pos, orig, targs, expanding, ctxt)
|
||||
|
||||
case *Signature:
|
||||
assert(expanding == nil) // function instances cannot be reached from Named types
|
||||
|
@ -1131,3 +1131,23 @@ func f(x int) {
|
||||
t.Errorf("got: %s want: %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue68877(t *testing.T) {
|
||||
const src = `
|
||||
package p
|
||||
|
||||
type (
|
||||
S struct{}
|
||||
A = S
|
||||
T A
|
||||
)`
|
||||
|
||||
t.Setenv("GODEBUG", "gotypesalias=1")
|
||||
pkg := mustTypecheck(src, nil, nil)
|
||||
T := pkg.Scope().Lookup("T").(*TypeName)
|
||||
got := T.String() // this must not panic (was issue)
|
||||
const want = "type p.T struct{}"
|
||||
if got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ func (t *Named) cleanup() {
|
||||
if t.TypeArgs().Len() == 0 {
|
||||
panic("nil underlying")
|
||||
}
|
||||
case *Named:
|
||||
case *Named, *Alias:
|
||||
t.under() // t.under may add entries to check.cleaners
|
||||
}
|
||||
t.check = nil
|
||||
|
@ -118,7 +118,7 @@ func (subst *subster) typ(typ Type) Type {
|
||||
// that has a type argument for it.
|
||||
targs, updated := subst.typeList(t.TypeArgs().list())
|
||||
if updated {
|
||||
return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.ctxt)
|
||||
return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.expanding, subst.ctxt)
|
||||
}
|
||||
|
||||
case *Array:
|
||||
|
@ -134,8 +134,8 @@ func (s *_TypeSet) underIs(f func(Type) bool) bool {
|
||||
}
|
||||
for _, t := range s.terms {
|
||||
assert(t.typ != nil)
|
||||
// x == under(x) for ~x terms
|
||||
u := t.typ
|
||||
// Unalias(x) == under(x) for ~x terms
|
||||
u := Unalias(t.typ)
|
||||
if !t.tilde {
|
||||
u = under(u)
|
||||
}
|
||||
|
@ -177,6 +177,15 @@ func TypeOf(a any) *Type {
|
||||
return (*Type)(NoEscape(unsafe.Pointer(eface.Type)))
|
||||
}
|
||||
|
||||
// TypeFor returns the abi.Type for a type parameter.
|
||||
func TypeFor[T any]() *Type {
|
||||
var v T
|
||||
if t := TypeOf(v); t != nil {
|
||||
return t // optimize for T being a non-interface kind
|
||||
}
|
||||
return TypeOf((*T)(nil)).Elem() // only for an interface kind
|
||||
}
|
||||
|
||||
func (t *Type) Kind() Kind { return t.Kind_ & KindMask }
|
||||
|
||||
func (t *Type) HasName() bool {
|
||||
|
@ -32,28 +32,46 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error,
|
||||
if int64(n) > remain {
|
||||
n = int(remain)
|
||||
}
|
||||
m := n
|
||||
pos1 := pos
|
||||
n, err = syscall.Sendfile(dst, src, &pos1, n)
|
||||
if n > 0 {
|
||||
pos += int64(n)
|
||||
written += int64(n)
|
||||
remain -= int64(n)
|
||||
// (n, nil) indicates that sendfile(2) has transferred
|
||||
// the exact number of bytes we requested, or some unretryable
|
||||
// error have occurred with partial bytes sent. Either way, we
|
||||
// don't need to go through the following logic to check EINTR
|
||||
// or fell into dstFD.pd.waitWrite, just continue to send the
|
||||
// next chunk or break the loop.
|
||||
if n == m {
|
||||
continue
|
||||
} else if err != syscall.EAGAIN &&
|
||||
err != syscall.EINTR &&
|
||||
err != syscall.EBUSY {
|
||||
// Particularly, EPIPE. Errors like that would normally lead
|
||||
// the subsequent sendfile(2) call to (-1, EBADF).
|
||||
break
|
||||
}
|
||||
} else if err != syscall.EAGAIN && err != syscall.EINTR {
|
||||
// This includes syscall.ENOSYS (no kernel
|
||||
// support) and syscall.EINVAL (fd types which
|
||||
// don't implement sendfile), and other errors.
|
||||
// We should end the loop when there is no error
|
||||
// returned from sendfile(2) or it is not a retryable error.
|
||||
break
|
||||
}
|
||||
if err == syscall.EINTR {
|
||||
continue
|
||||
}
|
||||
// This includes syscall.ENOSYS (no kernel
|
||||
// support) and syscall.EINVAL (fd types which
|
||||
// don't implement sendfile), and other errors.
|
||||
// We should end the loop when there is no error
|
||||
// returned from sendfile(2) or it is not a retryable error.
|
||||
if err != syscall.EAGAIN {
|
||||
break
|
||||
}
|
||||
if err = dstFD.pd.waitWrite(dstFD.isFile); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err == syscall.EAGAIN {
|
||||
err = nil
|
||||
}
|
||||
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
|
||||
return
|
||||
}
|
||||
|
@ -50,6 +50,9 @@ func SendFile(dstFD *FD, src int, remain int64) (written int64, err error, handl
|
||||
break
|
||||
}
|
||||
}
|
||||
if err == syscall.EAGAIN {
|
||||
err = nil
|
||||
}
|
||||
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
|
||||
return
|
||||
}
|
||||
|
@ -61,6 +61,9 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error,
|
||||
break
|
||||
}
|
||||
}
|
||||
if err == syscall.EAGAIN {
|
||||
err = nil
|
||||
}
|
||||
handled = written != 0 || (err != syscall.ENOSYS && err != syscall.EINVAL)
|
||||
return
|
||||
}
|
||||
|
@ -14,4 +14,4 @@ type Slice []byte
|
||||
type Array [8]byte
|
||||
|
||||
var s Slice
|
||||
var p = (Array)(s /* ok because Go 1.20 ignored the //go:build go1.19 */)
|
||||
var p = (Array)(s /* ok because file versions below go1.21 set the langage version to go1.21 */)
|
||||
|
@ -14,4 +14,4 @@ type Slice []byte
|
||||
type Array [8]byte
|
||||
|
||||
var s Slice
|
||||
var p = (Array)(s /* ERROR "requires go1.20 or later" */)
|
||||
var p = (Array)(s /* ok because file versions below go1.21 set the langage version to go1.21 */)
|
||||
|
16
src/internal/types/testdata/check/go1_21_22.go
vendored
Normal file
16
src/internal/types/testdata/check/go1_21_22.go
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// -lang=go1.21
|
||||
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Check Go language version-specific errors.
|
||||
|
||||
//go:build go1.22
|
||||
|
||||
package p
|
||||
|
||||
func f() {
|
||||
for _ = range /* ok because of upgrade to 1.22 */ 10 {
|
||||
}
|
||||
}
|
16
src/internal/types/testdata/check/go1_22_21.go
vendored
Normal file
16
src/internal/types/testdata/check/go1_22_21.go
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// -lang=go1.22
|
||||
|
||||
// Copyright 2024 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.
|
||||
|
||||
// Check Go language version-specific errors.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
package p
|
||||
|
||||
func f() {
|
||||
for _ = range 10 /* ERROR "requires go1.22 or later" */ {
|
||||
}
|
||||
}
|
@ -1,14 +1,9 @@
|
||||
// -lang=go1.21
|
||||
// -lang=go1.13
|
||||
|
||||
// Copyright 2024 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.
|
||||
|
||||
// Note: Downgrading to go1.13 requires at least go1.21,
|
||||
// hence the need for -lang=go1.21 at the top.
|
||||
|
||||
//go:build go1.13
|
||||
|
||||
package p
|
||||
|
||||
import "io"
|
||||
|
24
src/internal/types/testdata/fixedbugs/issue68903.go
vendored
Normal file
24
src/internal/types/testdata/fixedbugs/issue68903.go
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2024 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 A = [4]int
|
||||
type B = map[string]interface{}
|
||||
|
||||
func _[T ~A](x T) {
|
||||
_ = len(x)
|
||||
}
|
||||
|
||||
func _[U ~A](x U) {
|
||||
_ = cap(x)
|
||||
}
|
||||
|
||||
func _[V ~A]() {
|
||||
_ = V{}
|
||||
}
|
||||
|
||||
func _[W ~B](a interface{}) {
|
||||
_ = a.(W)["key"]
|
||||
}
|
26
src/internal/types/testdata/fixedbugs/issue68935.go
vendored
Normal file
26
src/internal/types/testdata/fixedbugs/issue68935.go
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2024 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 A = struct {
|
||||
F string
|
||||
G int
|
||||
}
|
||||
|
||||
func Make[T ~A]() T {
|
||||
return T{
|
||||
F: "blah",
|
||||
G: 1234,
|
||||
}
|
||||
}
|
||||
|
||||
type N struct {
|
||||
F string
|
||||
G int
|
||||
}
|
||||
|
||||
func _() {
|
||||
_ = Make[N]()
|
||||
}
|
@ -5,9 +5,12 @@
|
||||
package weak_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"internal/weak"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
@ -128,3 +131,82 @@ func TestPointerFinalizer(t *testing.T) {
|
||||
t.Errorf("weak pointer is non-nil even after finalization: %v", wt)
|
||||
}
|
||||
}
|
||||
|
||||
// Regression test for issue 69210.
|
||||
//
|
||||
// Weak-to-strong conversions must shade the new strong pointer, otherwise
|
||||
// that might be creating the only strong pointer to a white object which
|
||||
// is hidden in a blackened stack.
|
||||
//
|
||||
// Never fails if correct, fails with some high probability if incorrect.
|
||||
func TestIssue69210(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("this is a stress test that takes seconds to run on its own")
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// What we're trying to do is manufacture the conditions under which this
|
||||
// bug happens. Specifically, we want:
|
||||
//
|
||||
// 1. To create a whole bunch of objects that are only weakly-pointed-to,
|
||||
// 2. To call Strong while the GC is in the mark phase,
|
||||
// 3. The new strong pointer to be missed by the GC,
|
||||
// 4. The following GC cycle to mark a free object.
|
||||
//
|
||||
// Unfortunately, (2) and (3) are hard to control, but we can increase
|
||||
// the likelihood by having several goroutines do (1) at once while
|
||||
// another goroutine constantly keeps us in the GC with runtime.GC.
|
||||
// Like throwing darts at a dart board until they land just right.
|
||||
// We can increase the likelihood of (4) by adding some delay after
|
||||
// creating the strong pointer, but only if it's non-nil. If it's nil,
|
||||
// that means it was already collected in which case there's no chance
|
||||
// of triggering the bug, so we want to retry as fast as possible.
|
||||
// Our heap here is tiny, so the GCs will go by fast.
|
||||
//
|
||||
// As of 2024-09-03, removing the line that shades pointers during
|
||||
// the weak-to-strong conversion causes this test to fail about 50%
|
||||
// of the time.
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
runtime.GC()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
for range max(runtime.GOMAXPROCS(-1)-1, 1) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
for range 5 {
|
||||
bt := new(T)
|
||||
wt := weak.Make(bt)
|
||||
bt = nil
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
bt = wt.Strong()
|
||||
if bt != nil {
|
||||
time.Sleep(4 * time.Millisecond)
|
||||
bt.t = bt
|
||||
bt.a = 12
|
||||
}
|
||||
runtime.KeepAlive(bt)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
154
src/os/copy_test.go
Normal file
154
src/os/copy_test.go
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright 2024 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 os_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"math/rand/v2"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/nettest"
|
||||
)
|
||||
|
||||
// Exercise sendfile/splice fast paths with a moderately large file.
|
||||
//
|
||||
// https://go.dev/issue/70000
|
||||
|
||||
func TestLargeCopyViaNetwork(t *testing.T) {
|
||||
const size = 10 * 1024 * 1024
|
||||
dir := t.TempDir()
|
||||
|
||||
src, err := os.Create(dir + "/src")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer src.Close()
|
||||
if _, err := io.CopyN(src, newRandReader(), size); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := src.Seek(0, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dst, err := os.Create(dir + "/dst")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
client, server := createSocketPair(t, "tcp")
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if n, err := io.Copy(dst, server); n != size || err != nil {
|
||||
t.Errorf("copy to destination = %v, %v; want %v, nil", n, err, size)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
defer client.Close()
|
||||
if n, err := io.Copy(client, src); n != size || err != nil {
|
||||
t.Errorf("copy from source = %v, %v; want %v, nil", n, err, size)
|
||||
}
|
||||
}()
|
||||
wg.Wait()
|
||||
|
||||
if _, err := dst.Seek(0, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := compareReaders(dst, io.LimitReader(newRandReader(), size)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func compareReaders(a, b io.Reader) error {
|
||||
bufa := make([]byte, 4096)
|
||||
bufb := make([]byte, 4096)
|
||||
for {
|
||||
na, erra := io.ReadFull(a, bufa)
|
||||
if erra != nil && erra != io.EOF {
|
||||
return erra
|
||||
}
|
||||
nb, errb := io.ReadFull(b, bufb)
|
||||
if errb != nil && errb != io.EOF {
|
||||
return errb
|
||||
}
|
||||
if !bytes.Equal(bufa[:na], bufb[:nb]) {
|
||||
return errors.New("contents mismatch")
|
||||
}
|
||||
if erra == io.EOF && errb == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type randReader struct {
|
||||
rand *rand.Rand
|
||||
}
|
||||
|
||||
func newRandReader() *randReader {
|
||||
return &randReader{rand.New(rand.NewPCG(0, 0))}
|
||||
}
|
||||
|
||||
func (r *randReader) Read(p []byte) (int, error) {
|
||||
var v uint64
|
||||
var n int
|
||||
for i := range p {
|
||||
if n == 0 {
|
||||
v = r.rand.Uint64()
|
||||
n = 8
|
||||
}
|
||||
p[i] = byte(v & 0xff)
|
||||
v >>= 8
|
||||
n--
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func createSocketPair(t *testing.T, proto string) (client, server net.Conn) {
|
||||
t.Helper()
|
||||
if !nettest.TestableNetwork(proto) {
|
||||
t.Skipf("%s does not support %q", runtime.GOOS, proto)
|
||||
}
|
||||
|
||||
ln, err := nettest.NewLocalListener(proto)
|
||||
if err != nil {
|
||||
t.Fatalf("NewLocalListener error: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if ln != nil {
|
||||
ln.Close()
|
||||
}
|
||||
if client != nil {
|
||||
client.Close()
|
||||
}
|
||||
if server != nil {
|
||||
server.Close()
|
||||
}
|
||||
})
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
var err error
|
||||
server, err = ln.Accept()
|
||||
if err != nil {
|
||||
t.Errorf("Accept new connection error: %v", err)
|
||||
}
|
||||
ch <- struct{}{}
|
||||
}()
|
||||
client, err = net.Dial(proto, ln.Addr().String())
|
||||
<-ch
|
||||
if err != nil {
|
||||
t.Fatalf("Dial new connection error: %v", err)
|
||||
}
|
||||
return client, server
|
||||
}
|
@ -132,15 +132,18 @@ func ReadDir(name string) ([]DirEntry, error) {
|
||||
// CopyFS copies the file system fsys into the directory dir,
|
||||
// creating dir if necessary.
|
||||
//
|
||||
// Newly created directories and files have their default modes
|
||||
// where any bits from the file in fsys that are not part of the
|
||||
// standard read, write, and execute permissions will be zeroed
|
||||
// out, and standard read and write permissions are set for owner,
|
||||
// group, and others while retaining any existing execute bits from
|
||||
// the file in fsys.
|
||||
// Files are created with mode 0o666 plus any execute permissions
|
||||
// from the source, and directories are created with mode 0o777
|
||||
// (before umask).
|
||||
//
|
||||
// Symbolic links in fsys are not supported, a *PathError with Err set
|
||||
// to ErrInvalid is returned on symlink.
|
||||
// CopyFS will not overwrite existing files. If a file name in fsys
|
||||
// already exists in the destination, CopyFS will return an error
|
||||
// such that errors.Is(err, fs.ErrExist) will be true.
|
||||
//
|
||||
// Symbolic links in fsys are not supported. A *PathError with Err set
|
||||
// to ErrInvalid is returned when copying from a symbolic link.
|
||||
//
|
||||
// Symbolic links in dir are followed.
|
||||
//
|
||||
// Copying stops at and returns the first error encountered.
|
||||
func CopyFS(dir string, fsys fs.FS) error {
|
||||
@ -174,7 +177,7 @@ func CopyFS(dir string, fsys fs.FS) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w, err := OpenFile(newPath, O_CREATE|O_TRUNC|O_WRONLY, 0666|info.Mode()&0777)
|
||||
w, err := OpenFile(newPath, O_CREATE|O_EXCL|O_WRONLY, 0666|info.Mode()&0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ func ExampleFileMode() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("permissions: %#o\n", fi.Mode().Perm()) // 0400, 0777, etc.
|
||||
fmt.Printf("permissions: %#o\n", fi.Mode().Perm()) // 0o400, 0o777, etc.
|
||||
switch mode := fi.Mode(); {
|
||||
case mode.IsRegular():
|
||||
fmt.Println("regular file")
|
||||
|
@ -35,10 +35,11 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
|
||||
}
|
||||
}
|
||||
|
||||
attrSys, shouldDupPidfd := ensurePidfd(attr.Sys)
|
||||
sysattr := &syscall.ProcAttr{
|
||||
Dir: attr.Dir,
|
||||
Env: attr.Env,
|
||||
Sys: ensurePidfd(attr.Sys),
|
||||
Sys: attrSys,
|
||||
}
|
||||
if sysattr.Env == nil {
|
||||
sysattr.Env, err = execenv.Default(sysattr.Sys)
|
||||
@ -63,7 +64,7 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
|
||||
// For Windows, syscall.StartProcess above already returned a process handle.
|
||||
if runtime.GOOS != "windows" {
|
||||
var ok bool
|
||||
h, ok = getPidfd(sysattr.Sys)
|
||||
h, ok = getPidfd(sysattr.Sys, shouldDupPidfd)
|
||||
if !ok {
|
||||
return newPIDProcess(pid), nil
|
||||
}
|
||||
|
@ -366,7 +366,7 @@ func Open(name string) (*File, error) {
|
||||
}
|
||||
|
||||
// Create creates or truncates the named file. If the file already exists,
|
||||
// it is truncated. If the file does not exist, it is created with mode 0666
|
||||
// it is truncated. If the file does not exist, it is created with mode 0o666
|
||||
// (before umask). If successful, methods on the returned File can
|
||||
// be used for I/O; the associated file descriptor has mode O_RDWR.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
@ -602,11 +602,11 @@ func UserHomeDir() (string, error) {
|
||||
// On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and
|
||||
// ModeSticky are used.
|
||||
//
|
||||
// On Windows, only the 0200 bit (owner writable) of mode is used; it
|
||||
// On Windows, only the 0o200 bit (owner writable) of mode is used; it
|
||||
// controls whether the file's read-only attribute is set or cleared.
|
||||
// The other bits are currently unused. For compatibility with Go 1.12
|
||||
// and earlier, use a non-zero mode. Use mode 0400 for a read-only
|
||||
// file and 0600 for a readable+writable file.
|
||||
// and earlier, use a non-zero mode. Use mode 0o400 for a read-only
|
||||
// file and 0o600 for a readable+writable file.
|
||||
//
|
||||
// On Plan 9, the mode's permission bits, ModeAppend, ModeExclusive,
|
||||
// and ModeTemporary are used.
|
||||
|
@ -1376,8 +1376,7 @@ func TestChtimes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f := newFile(t)
|
||||
|
||||
f.Write([]byte("hello, world\n"))
|
||||
// This should be an empty file (see #68687, #68663).
|
||||
f.Close()
|
||||
|
||||
testChtimes(t, f.Name())
|
||||
@ -1395,12 +1394,9 @@ func TestChtimesOmit(t *testing.T) {
|
||||
func testChtimesOmit(t *testing.T, omitAt, omitMt bool) {
|
||||
t.Logf("omit atime: %v, mtime: %v", omitAt, omitMt)
|
||||
file := newFile(t)
|
||||
_, err := file.Write([]byte("hello, world\n"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// This should be an empty file (see #68687, #68663).
|
||||
name := file.Name()
|
||||
err = file.Close()
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@ -3358,6 +3354,14 @@ func TestCopyFS(t *testing.T) {
|
||||
t.Fatal("comparing two directories:", err)
|
||||
}
|
||||
|
||||
// Test whether CopyFS disallows copying for disk filesystem when there is any
|
||||
// existing file in the destination directory.
|
||||
if err := CopyFS(tmpDir, fsys); !errors.Is(err, fs.ErrExist) {
|
||||
t.Errorf("CopyFS should have failed and returned error when there is"+
|
||||
"any existing file in the destination directory (in disk filesystem), "+
|
||||
"got: %v, expected any error that indicates <file exists>", err)
|
||||
}
|
||||
|
||||
// Test with memory filesystem.
|
||||
fsys = fstest.MapFS{
|
||||
"william": {Data: []byte("Shakespeare\n")},
|
||||
@ -3395,6 +3399,14 @@ func TestCopyFS(t *testing.T) {
|
||||
}); err != nil {
|
||||
t.Fatal("comparing two directories:", err)
|
||||
}
|
||||
|
||||
// Test whether CopyFS disallows copying for memory filesystem when there is any
|
||||
// existing file in the destination directory.
|
||||
if err := CopyFS(tmpDir, fsys); !errors.Is(err, fs.ErrExist) {
|
||||
t.Errorf("CopyFS should have failed and returned error when there is"+
|
||||
"any existing file in the destination directory (in memory filesystem), "+
|
||||
"got: %v, expected any error that indicates <file exists>", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyFSWithSymlinks(t *testing.T) {
|
||||
|
@ -8,20 +8,28 @@
|
||||
// v5.3: pidfd_open syscall, clone3 syscall;
|
||||
// v5.4: P_PIDFD idtype support for waitid syscall;
|
||||
// v5.6: pidfd_getfd syscall.
|
||||
//
|
||||
// N.B. Alternative Linux implementations may not follow this ordering. e.g.,
|
||||
// QEMU user mode 7.2 added pidfd_open, but CLONE_PIDFD was not added until
|
||||
// 8.0.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"internal/syscall/unix"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func ensurePidfd(sysAttr *syscall.SysProcAttr) *syscall.SysProcAttr {
|
||||
// ensurePidfd initializes the PidFD field in sysAttr if it is not already set.
|
||||
// It returns the original or modified SysProcAttr struct and a flag indicating
|
||||
// whether the PidFD should be duplicated before using.
|
||||
func ensurePidfd(sysAttr *syscall.SysProcAttr) (*syscall.SysProcAttr, bool) {
|
||||
if !pidfdWorks() {
|
||||
return sysAttr
|
||||
return sysAttr, false
|
||||
}
|
||||
|
||||
var pidfd int
|
||||
@ -29,23 +37,33 @@ func ensurePidfd(sysAttr *syscall.SysProcAttr) *syscall.SysProcAttr {
|
||||
if sysAttr == nil {
|
||||
return &syscall.SysProcAttr{
|
||||
PidFD: &pidfd,
|
||||
}
|
||||
}, false
|
||||
}
|
||||
if sysAttr.PidFD == nil {
|
||||
newSys := *sysAttr // copy
|
||||
newSys.PidFD = &pidfd
|
||||
return &newSys
|
||||
return &newSys, false
|
||||
}
|
||||
|
||||
return sysAttr
|
||||
return sysAttr, true
|
||||
}
|
||||
|
||||
func getPidfd(sysAttr *syscall.SysProcAttr) (uintptr, bool) {
|
||||
// getPidfd returns the value of sysAttr.PidFD (or its duplicate if needDup is
|
||||
// set) and a flag indicating whether the value can be used.
|
||||
func getPidfd(sysAttr *syscall.SysProcAttr, needDup bool) (uintptr, bool) {
|
||||
if !pidfdWorks() {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return uintptr(*sysAttr.PidFD), true
|
||||
h := *sysAttr.PidFD
|
||||
if needDup {
|
||||
dupH, e := unix.Fcntl(h, syscall.F_DUPFD_CLOEXEC, 0)
|
||||
if e != nil {
|
||||
return 0, false
|
||||
}
|
||||
h = dupH
|
||||
}
|
||||
return uintptr(h), true
|
||||
}
|
||||
|
||||
func pidfdFind(pid int) (uintptr, error) {
|
||||
@ -126,14 +144,21 @@ func pidfdWorks() bool {
|
||||
|
||||
var checkPidfdOnce = sync.OnceValue(checkPidfd)
|
||||
|
||||
// checkPidfd checks whether all required pidfd-related syscalls work.
|
||||
// This consists of pidfd_open and pidfd_send_signal syscalls, and waitid
|
||||
// syscall with idtype of P_PIDFD.
|
||||
// checkPidfd checks whether all required pidfd-related syscalls work. This
|
||||
// consists of pidfd_open and pidfd_send_signal syscalls, waitid syscall with
|
||||
// idtype of P_PIDFD, and clone(CLONE_PIDFD).
|
||||
//
|
||||
// Reasons for non-working pidfd syscalls include an older kernel and an
|
||||
// execution environment in which the above system calls are restricted by
|
||||
// seccomp or a similar technology.
|
||||
func checkPidfd() error {
|
||||
// In Android version < 12, pidfd-related system calls are not allowed
|
||||
// by seccomp and trigger the SIGSYS signal. See issue #69065.
|
||||
if runtime.GOOS == "android" {
|
||||
ignoreSIGSYS()
|
||||
defer restoreSIGSYS()
|
||||
}
|
||||
|
||||
// Get a pidfd of the current process (opening of "/proc/self" won't
|
||||
// work for waitid).
|
||||
fd, err := unix.PidFDOpen(syscall.Getpid(), 0)
|
||||
@ -159,5 +184,27 @@ func checkPidfd() error {
|
||||
return NewSyscallError("pidfd_send_signal", err)
|
||||
}
|
||||
|
||||
// Verify that clone(CLONE_PIDFD) works.
|
||||
//
|
||||
// This shouldn't be necessary since pidfd_open was added in Linux 5.3,
|
||||
// after CLONE_PIDFD in Linux 5.2, but some alternative Linux
|
||||
// implementations may not adhere to this ordering.
|
||||
if err := checkClonePidfd(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Provided by syscall.
|
||||
//
|
||||
//go:linkname checkClonePidfd
|
||||
func checkClonePidfd() error
|
||||
|
||||
// Provided by runtime.
|
||||
//
|
||||
//go:linkname ignoreSIGSYS
|
||||
func ignoreSIGSYS()
|
||||
|
||||
//go:linkname restoreSIGSYS
|
||||
func restoreSIGSYS()
|
||||
|
@ -6,8 +6,10 @@ package os_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"internal/syscall/unix"
|
||||
"internal/testenv"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
@ -57,3 +59,93 @@ func TestFindProcessViaPidfd(t *testing.T) {
|
||||
t.Fatalf("Release: got %v, want <nil>", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartProcessWithPidfd(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
t.Parallel()
|
||||
|
||||
if err := os.CheckPidfdOnce(); err != nil {
|
||||
// Non-pidfd code paths tested in exec_unix_test.go.
|
||||
t.Skipf("skipping: pidfd not available: %v", err)
|
||||
}
|
||||
|
||||
var pidfd int
|
||||
p, err := os.StartProcess(testenv.GoToolPath(t), []string{"go"}, &os.ProcAttr{
|
||||
Sys: &syscall.SysProcAttr{
|
||||
PidFD: &pidfd,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("starting test process: %v", err)
|
||||
}
|
||||
defer syscall.Close(pidfd)
|
||||
|
||||
if _, err := p.Wait(); err != nil {
|
||||
t.Fatalf("Wait: got %v, want <nil>", err)
|
||||
}
|
||||
|
||||
// Check the pidfd is still valid
|
||||
err = unix.PidFDSendSignal(uintptr(pidfd), syscall.Signal(0))
|
||||
if !errors.Is(err, syscall.ESRCH) {
|
||||
t.Errorf("SendSignal: got %v, want %v", err, syscall.ESRCH)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #69284
|
||||
func TestPidfdLeak(t *testing.T) {
|
||||
testenv.MustHaveExec(t)
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Find the next 10 descriptors.
|
||||
// We need to get more than one descriptor in practice;
|
||||
// the pidfd winds up not being the next descriptor.
|
||||
const count = 10
|
||||
want := make([]int, count)
|
||||
for i := range count {
|
||||
var err error
|
||||
want[i], err = syscall.Open(exe, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Close the descriptors.
|
||||
for _, d := range want {
|
||||
syscall.Close(d)
|
||||
}
|
||||
|
||||
// Start a process 10 times.
|
||||
for range 10 {
|
||||
// For testing purposes this has to be an absolute path.
|
||||
// Otherwise we will fail finding the executable
|
||||
// and won't start a process at all.
|
||||
cmd := exec.Command("/noSuchExecutable")
|
||||
cmd.Run()
|
||||
}
|
||||
|
||||
// Open the next 10 descriptors again.
|
||||
got := make([]int, count)
|
||||
for i := range count {
|
||||
var err error
|
||||
got[i], err = syscall.Open(exe, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Close the descriptors
|
||||
for _, d := range got {
|
||||
syscall.Close(d)
|
||||
}
|
||||
|
||||
t.Logf("got %v", got)
|
||||
t.Logf("want %v", want)
|
||||
|
||||
// Allow some slack for runtime epoll descriptors and the like.
|
||||
if got[count-1] > want[count-1]+5 {
|
||||
t.Errorf("got descriptor %d, want %d", got[count-1], want[count-1])
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,11 @@ package os
|
||||
|
||||
import "syscall"
|
||||
|
||||
func ensurePidfd(sysAttr *syscall.SysProcAttr) *syscall.SysProcAttr {
|
||||
return sysAttr
|
||||
func ensurePidfd(sysAttr *syscall.SysProcAttr) (*syscall.SysProcAttr, bool) {
|
||||
return sysAttr, false
|
||||
}
|
||||
|
||||
func getPidfd(_ *syscall.SysProcAttr) (uintptr, bool) {
|
||||
func getPidfd(_ *syscall.SysProcAttr, _ bool) (uintptr, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
|
@ -14,15 +14,12 @@ import (
|
||||
"net"
|
||||
. "os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/nettest"
|
||||
)
|
||||
|
||||
func TestCopyFileRange(t *testing.T) {
|
||||
@ -784,41 +781,3 @@ func testGetPollFDAndNetwork(t *testing.T, proto string) {
|
||||
t.Fatalf("server Control error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func createSocketPair(t *testing.T, proto string) (client, server net.Conn) {
|
||||
t.Helper()
|
||||
if !nettest.TestableNetwork(proto) {
|
||||
t.Skipf("%s does not support %q", runtime.GOOS, proto)
|
||||
}
|
||||
|
||||
ln, err := nettest.NewLocalListener(proto)
|
||||
if err != nil {
|
||||
t.Fatalf("NewLocalListener error: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if ln != nil {
|
||||
ln.Close()
|
||||
}
|
||||
if client != nil {
|
||||
client.Close()
|
||||
}
|
||||
if server != nil {
|
||||
server.Close()
|
||||
}
|
||||
})
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
var err error
|
||||
server, err = ln.Accept()
|
||||
if err != nil {
|
||||
t.Errorf("Accept new connection error: %v", err)
|
||||
}
|
||||
ch <- struct{}{}
|
||||
}()
|
||||
client, err = net.Dial(proto, ln.Addr().String())
|
||||
<-ch
|
||||
if err != nil {
|
||||
t.Fatalf("Dial new connection error: %v", err)
|
||||
}
|
||||
return client, server
|
||||
}
|
||||
|
@ -338,9 +338,14 @@ func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) {
|
||||
// stack. However, since we're returning to an earlier stack frame and
|
||||
// need to pair with the entersyscall() call made by cgocall, we must
|
||||
// save syscall* and let reentersyscall restore them.
|
||||
//
|
||||
// Note: savedsp and savedbp MUST be held in locals as an unsafe.Pointer.
|
||||
// When we call into Go, the stack is free to be moved. If these locals
|
||||
// aren't visible in the stack maps, they won't get updated properly,
|
||||
// and will end up being stale when restored by reentersyscall.
|
||||
savedsp := unsafe.Pointer(gp.syscallsp)
|
||||
savedpc := gp.syscallpc
|
||||
savedbp := gp.syscallbp
|
||||
savedbp := unsafe.Pointer(gp.syscallbp)
|
||||
exitsyscall() // coming out of cgo call
|
||||
gp.m.incgo = false
|
||||
if gp.m.isextra {
|
||||
@ -372,7 +377,7 @@ func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) {
|
||||
osPreemptExtEnter(gp.m)
|
||||
|
||||
// going back to cgo call
|
||||
reentersyscall(savedpc, uintptr(savedsp), savedbp)
|
||||
reentersyscall(savedpc, uintptr(savedsp), uintptr(savedbp))
|
||||
|
||||
gp.m.winsyscall = winsyscall
|
||||
}
|
||||
|
@ -208,6 +208,18 @@ func coroswitch_m(gp *g) {
|
||||
// directly if possible.
|
||||
setGNoWB(&mp.curg, gnext)
|
||||
setMNoWB(&gnext.m, mp)
|
||||
|
||||
// Synchronize with any out-standing goroutine profile. We're about to start
|
||||
// executing, and an invariant of the profiler is that we tryRecordGoroutineProfile
|
||||
// whenever a goroutine is about to start running.
|
||||
//
|
||||
// N.B. We must do this before transitioning to _Grunning but after installing gnext
|
||||
// in curg, so that we have a valid curg for allocation (tryRecordGoroutineProfile
|
||||
// may allocate).
|
||||
if goroutineProfile.active {
|
||||
tryRecordGoroutineProfile(gnext, nil, osyield)
|
||||
}
|
||||
|
||||
if !gnext.atomicstatus.CompareAndSwap(_Gwaiting, _Grunning) {
|
||||
// The CAS failed: use casgstatus, which will take care of
|
||||
// coordinating with the garbage collector about the state change.
|
||||
|
@ -1209,6 +1209,11 @@ func (h *hmap) sameSizeGrow() bool {
|
||||
return h.flags&sameSizeGrow != 0
|
||||
}
|
||||
|
||||
//go:linkname sameSizeGrowForIssue69110Test
|
||||
func sameSizeGrowForIssue69110Test(h *hmap) bool {
|
||||
return h.sameSizeGrow()
|
||||
}
|
||||
|
||||
// noldbuckets calculates the number of buckets prior to the current map growth.
|
||||
func (h *hmap) noldbuckets() uintptr {
|
||||
oldB := h.B
|
||||
@ -1668,7 +1673,16 @@ func moveToBmap(t *maptype, h *hmap, dst *bmap, pos int, src *bmap) (*bmap, int)
|
||||
}
|
||||
|
||||
func mapclone2(t *maptype, src *hmap) *hmap {
|
||||
dst := makemap(t, src.count, nil)
|
||||
hint := src.count
|
||||
if overLoadFactor(hint, src.B) {
|
||||
// Note: in rare cases (e.g. during a same-sized grow) the map
|
||||
// can be overloaded. Make sure we don't allocate a destination
|
||||
// bucket array larger than the source bucket array.
|
||||
// This will cause the cloned map to be overloaded also,
|
||||
// but that's better than crashing. See issue 69110.
|
||||
hint = int(loadFactorNum * (bucketShift(src.B) / loadFactorDen))
|
||||
}
|
||||
dst := makemap(t, hint, nil)
|
||||
dst.hash0 = src.hash0
|
||||
dst.nevacuate = 0
|
||||
// flags do not need to be copied here, just like a new map has no flags.
|
||||
|
@ -2073,7 +2073,22 @@ func internal_weak_runtime_makeStrongFromWeak(u unsafe.Pointer) unsafe.Pointer {
|
||||
// Even if we just swept some random span that doesn't contain this object, because
|
||||
// this object is long dead and its memory has since been reused, we'll just observe nil.
|
||||
ptr := unsafe.Pointer(handle.Load())
|
||||
|
||||
// This is responsible for maintaining the same GC-related
|
||||
// invariants as the Yuasa part of the write barrier. During
|
||||
// the mark phase, it's possible that we just created the only
|
||||
// valid pointer to the object pointed to by ptr. If it's only
|
||||
// ever referenced from our stack, and our stack is blackened
|
||||
// already, we could fail to mark it. So, mark it now.
|
||||
if gcphase != _GCoff {
|
||||
shade(uintptr(ptr))
|
||||
}
|
||||
releasem(mp)
|
||||
|
||||
// Explicitly keep ptr alive. This seems unnecessary since we return ptr,
|
||||
// but let's be explicit since it's important we keep ptr alive across the
|
||||
// call to shade.
|
||||
KeepAlive(ptr)
|
||||
return ptr
|
||||
}
|
||||
|
||||
@ -2081,6 +2096,9 @@ func internal_weak_runtime_makeStrongFromWeak(u unsafe.Pointer) unsafe.Pointer {
|
||||
func getOrAddWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
||||
// First try to retrieve without allocating.
|
||||
if handle := getWeakHandle(p); handle != nil {
|
||||
// Keep p alive for the duration of the function to ensure
|
||||
// that it cannot die while we're trying to do this.
|
||||
KeepAlive(p)
|
||||
return handle
|
||||
}
|
||||
|
||||
@ -2105,6 +2123,10 @@ func getOrAddWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
||||
scanblock(uintptr(unsafe.Pointer(&s.handle)), goarch.PtrSize, &oneptrmask[0], gcw, nil)
|
||||
releasem(mp)
|
||||
}
|
||||
|
||||
// Keep p alive for the duration of the function to ensure
|
||||
// that it cannot die while we're trying to do this.
|
||||
KeepAlive(p)
|
||||
return s.handle
|
||||
}
|
||||
|
||||
@ -2124,7 +2146,7 @@ func getOrAddWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
||||
}
|
||||
|
||||
// Keep p alive for the duration of the function to ensure
|
||||
// that it cannot die while we're trying to this.
|
||||
// that it cannot die while we're trying to do this.
|
||||
KeepAlive(p)
|
||||
return handle
|
||||
}
|
||||
@ -2154,6 +2176,9 @@ func getWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
||||
unlock(&span.speciallock)
|
||||
releasem(mp)
|
||||
|
||||
// Keep p alive for the duration of the function to ensure
|
||||
// that it cannot die while we're trying to do this.
|
||||
KeepAlive(p)
|
||||
return handle
|
||||
}
|
||||
|
||||
|
@ -1136,11 +1136,12 @@ func expandFrames(p []BlockProfileRecord) {
|
||||
for i := range p {
|
||||
cf := CallersFrames(p[i].Stack())
|
||||
j := 0
|
||||
for ; j < len(expandedStack); j++ {
|
||||
for j < len(expandedStack) {
|
||||
f, more := cf.Next()
|
||||
// f.PC is a "call PC", but later consumers will expect
|
||||
// "return PCs"
|
||||
expandedStack[j] = f.PC + 1
|
||||
j++
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
@ -1270,7 +1271,8 @@ func pprof_mutexProfileInternal(p []profilerecord.BlockProfileRecord) (n int, ok
|
||||
// of calling ThreadCreateProfile directly.
|
||||
func ThreadCreateProfile(p []StackRecord) (n int, ok bool) {
|
||||
return threadCreateProfileInternal(len(p), func(r profilerecord.StackRecord) {
|
||||
copy(p[0].Stack0[:], r.Stack)
|
||||
i := copy(p[0].Stack0[:], r.Stack)
|
||||
clear(p[0].Stack0[i:])
|
||||
p = p[1:]
|
||||
})
|
||||
}
|
||||
@ -1649,7 +1651,8 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
|
||||
return
|
||||
}
|
||||
for i, mr := range records[0:n] {
|
||||
copy(p[i].Stack0[:], mr.Stack)
|
||||
l := copy(p[i].Stack0[:], mr.Stack)
|
||||
clear(p[i].Stack0[l:])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -879,8 +879,9 @@ func runPerThreadSyscall() {
|
||||
}
|
||||
|
||||
const (
|
||||
_SI_USER = 0
|
||||
_SI_TKILL = -6
|
||||
_SI_USER = 0
|
||||
_SI_TKILL = -6
|
||||
_SYS_SECCOMP = 1
|
||||
)
|
||||
|
||||
// sigFromUser reports whether the signal was sent because of a call
|
||||
@ -892,6 +893,14 @@ func (c *sigctxt) sigFromUser() bool {
|
||||
return code == _SI_USER || code == _SI_TKILL
|
||||
}
|
||||
|
||||
// sigFromSeccomp reports whether the signal was sent from seccomp.
|
||||
//
|
||||
//go:nosplit
|
||||
func (c *sigctxt) sigFromSeccomp() bool {
|
||||
code := int32(c.sigcode())
|
||||
return code == _SYS_SECCOMP
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func mprotect(addr unsafe.Pointer, n uintptr, prot int32) (ret int32, errno int32) {
|
||||
r, _, err := syscall.Syscall6(syscall.SYS_MPROTECT, uintptr(addr), n, uintptr(prot), 0, 0, 0)
|
||||
|
@ -13,3 +13,10 @@ package runtime
|
||||
func (c *sigctxt) sigFromUser() bool {
|
||||
return c.sigcode() == _SI_USER
|
||||
}
|
||||
|
||||
// sigFromSeccomp reports whether the signal was sent from seccomp.
|
||||
//
|
||||
//go:nosplit
|
||||
func (c *sigctxt) sigFromSeccomp() bool {
|
||||
return false
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ func TestMemoryProfiler(t *testing.T) {
|
||||
}
|
||||
t.Logf("Profile = %v", p)
|
||||
|
||||
stks := stacks(p)
|
||||
stks := profileStacks(p)
|
||||
for _, test := range tests {
|
||||
if !containsStack(stks, test.stk) {
|
||||
t.Fatalf("No matching stack entry for %q\n\nProfile:\n%v\n", test.stk, p)
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"internal/syscall/unix"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"iter"
|
||||
"math"
|
||||
"math/big"
|
||||
"os"
|
||||
@ -981,7 +982,7 @@ func TestBlockProfile(t *testing.T) {
|
||||
t.Fatalf("invalid profile: %v", err)
|
||||
}
|
||||
|
||||
stks := stacks(p)
|
||||
stks := profileStacks(p)
|
||||
for _, test := range tests {
|
||||
if !containsStack(stks, test.stk) {
|
||||
t.Errorf("No matching stack entry for %v, want %+v", test.name, test.stk)
|
||||
@ -991,7 +992,7 @@ func TestBlockProfile(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func stacks(p *profile.Profile) (res [][]string) {
|
||||
func profileStacks(p *profile.Profile) (res [][]string) {
|
||||
for _, s := range p.Sample {
|
||||
var stk []string
|
||||
for _, l := range s.Location {
|
||||
@ -1004,6 +1005,22 @@ func stacks(p *profile.Profile) (res [][]string) {
|
||||
return res
|
||||
}
|
||||
|
||||
func blockRecordStacks(records []runtime.BlockProfileRecord) (res [][]string) {
|
||||
for _, record := range records {
|
||||
frames := runtime.CallersFrames(record.Stack())
|
||||
var stk []string
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
stk = append(stk, frame.Function)
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
res = append(res, stk)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func containsStack(got [][]string, want []string) bool {
|
||||
for _, stk := range got {
|
||||
if len(stk) < len(want) {
|
||||
@ -1288,7 +1305,7 @@ func TestMutexProfile(t *testing.T) {
|
||||
t.Fatalf("invalid profile: %v", err)
|
||||
}
|
||||
|
||||
stks := stacks(p)
|
||||
stks := profileStacks(p)
|
||||
for _, want := range [][]string{
|
||||
{"sync.(*Mutex).Unlock", "runtime/pprof.blockMutexN.func1"},
|
||||
} {
|
||||
@ -1328,6 +1345,28 @@ func TestMutexProfile(t *testing.T) {
|
||||
t.Fatalf("profile samples total %v, want within range [%v, %v] (target: %v)", d, lo, hi, N*D)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("records", func(t *testing.T) {
|
||||
// Record a mutex profile using the structured record API.
|
||||
var records []runtime.BlockProfileRecord
|
||||
for {
|
||||
n, ok := runtime.MutexProfile(records)
|
||||
if ok {
|
||||
records = records[:n]
|
||||
break
|
||||
}
|
||||
records = make([]runtime.BlockProfileRecord, n*2)
|
||||
}
|
||||
|
||||
// Check that we see the same stack trace as the proto profile. For
|
||||
// historical reason we expect a runtime.goexit root frame here that is
|
||||
// omitted in the proto profile.
|
||||
stks := blockRecordStacks(records)
|
||||
want := []string{"sync.(*Mutex).Unlock", "runtime/pprof.blockMutexN.func1", "runtime.goexit"}
|
||||
if !containsStack(stks, want) {
|
||||
t.Errorf("No matching stack entry for %+v", want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMutexProfileRateAdjust(t *testing.T) {
|
||||
@ -1754,6 +1793,50 @@ func TestGoroutineProfileConcurrency(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Regression test for #69998.
|
||||
func TestGoroutineProfileCoro(t *testing.T) {
|
||||
testenv.MustHaveParallelism(t)
|
||||
|
||||
goroutineProf := Lookup("goroutine")
|
||||
|
||||
// Set up a goroutine to just create and run coroutine goroutines all day.
|
||||
iterFunc := func() {
|
||||
p, stop := iter.Pull2(
|
||||
func(yield func(int, int) bool) {
|
||||
for i := 0; i < 10000; i++ {
|
||||
if !yield(i, i) {
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
defer stop()
|
||||
for {
|
||||
_, _, ok := p()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
done := make(chan struct{})
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
iterFunc()
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Take a goroutine profile. If the bug in #69998 is present, this will crash
|
||||
// with high probability. We don't care about the output for this bug.
|
||||
goroutineProf.WriteTo(io.Discard, 1)
|
||||
}
|
||||
|
||||
func BenchmarkGoroutine(b *testing.B) {
|
||||
withIdle := func(n int, fn func(b *testing.B)) func(b *testing.B) {
|
||||
return func(b *testing.B) {
|
||||
@ -2441,16 +2524,7 @@ func TestTimeVDSO(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProfilerStackDepth(t *testing.T) {
|
||||
// Disable sampling, otherwise it's difficult to assert anything.
|
||||
oldMemRate := runtime.MemProfileRate
|
||||
runtime.MemProfileRate = 1
|
||||
runtime.SetBlockProfileRate(1)
|
||||
oldMutexRate := runtime.SetMutexProfileFraction(1)
|
||||
t.Cleanup(func() {
|
||||
runtime.MemProfileRate = oldMemRate
|
||||
runtime.SetBlockProfileRate(0)
|
||||
runtime.SetMutexProfileFraction(oldMutexRate)
|
||||
})
|
||||
t.Cleanup(disableSampling())
|
||||
|
||||
const depth = 128
|
||||
go produceProfileEvents(t, depth)
|
||||
@ -2478,7 +2552,7 @@ func TestProfilerStackDepth(t *testing.T) {
|
||||
}
|
||||
t.Logf("Profile = %v", p)
|
||||
|
||||
stks := stacks(p)
|
||||
stks := profileStacks(p)
|
||||
var stk []string
|
||||
for _, s := range stks {
|
||||
if hasPrefix(s, test.prefix) {
|
||||
@ -2742,3 +2816,84 @@ runtime/pprof.inlineA`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProfileRecordNullPadding(t *testing.T) {
|
||||
// Produce events for the different profile types.
|
||||
t.Cleanup(disableSampling())
|
||||
memSink = make([]byte, 1) // MemProfile
|
||||
<-time.After(time.Millisecond) // BlockProfile
|
||||
blockMutex(t) // MutexProfile
|
||||
runtime.GC()
|
||||
|
||||
// Test that all profile records are null padded.
|
||||
testProfileRecordNullPadding(t, "MutexProfile", runtime.MutexProfile)
|
||||
testProfileRecordNullPadding(t, "GoroutineProfile", runtime.GoroutineProfile)
|
||||
testProfileRecordNullPadding(t, "BlockProfile", runtime.BlockProfile)
|
||||
testProfileRecordNullPadding(t, "MemProfile/inUseZero=true", func(p []runtime.MemProfileRecord) (int, bool) {
|
||||
return runtime.MemProfile(p, true)
|
||||
})
|
||||
testProfileRecordNullPadding(t, "MemProfile/inUseZero=false", func(p []runtime.MemProfileRecord) (int, bool) {
|
||||
return runtime.MemProfile(p, false)
|
||||
})
|
||||
// Not testing ThreadCreateProfile because it is broken, see issue 6104.
|
||||
}
|
||||
|
||||
func testProfileRecordNullPadding[T runtime.StackRecord | runtime.MemProfileRecord | runtime.BlockProfileRecord](t *testing.T, name string, fn func([]T) (int, bool)) {
|
||||
stack0 := func(sr *T) *[32]uintptr {
|
||||
switch t := any(sr).(type) {
|
||||
case *runtime.StackRecord:
|
||||
return &t.Stack0
|
||||
case *runtime.MemProfileRecord:
|
||||
return &t.Stack0
|
||||
case *runtime.BlockProfileRecord:
|
||||
return &t.Stack0
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected type %T", sr))
|
||||
}
|
||||
}
|
||||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
var p []T
|
||||
for {
|
||||
n, ok := fn(p)
|
||||
if ok {
|
||||
p = p[:n]
|
||||
break
|
||||
}
|
||||
p = make([]T, n*2)
|
||||
for i := range p {
|
||||
s0 := stack0(&p[i])
|
||||
for j := range s0 {
|
||||
// Poison the Stack0 array to identify lack of zero padding
|
||||
s0[j] = ^uintptr(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(p) == 0 {
|
||||
t.Fatal("no records found")
|
||||
}
|
||||
|
||||
for _, sr := range p {
|
||||
for i, v := range stack0(&sr) {
|
||||
if v == ^uintptr(0) {
|
||||
t.Fatalf("record p[%d].Stack0 is not null padded: %+v", i, sr)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// disableSampling configures the profilers to capture all events, otherwise
|
||||
// it's difficult to assert anything.
|
||||
func disableSampling() func() {
|
||||
oldMemRate := runtime.MemProfileRate
|
||||
runtime.MemProfileRate = 1
|
||||
runtime.SetBlockProfileRate(1)
|
||||
oldMutexRate := runtime.SetMutexProfileFraction(1)
|
||||
return func() {
|
||||
runtime.MemProfileRate = oldMemRate
|
||||
runtime.SetBlockProfileRate(0)
|
||||
runtime.SetMutexProfileFraction(oldMutexRate)
|
||||
}
|
||||
}
|
||||
|
@ -4415,7 +4415,13 @@ func reentersyscall(pc, sp, bp uintptr) {
|
||||
}
|
||||
if gp.syscallsp < gp.stack.lo || gp.stack.hi < gp.syscallsp {
|
||||
systemstack(func() {
|
||||
print("entersyscall inconsistent ", hex(gp.syscallsp), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n")
|
||||
print("entersyscall inconsistent sp ", hex(gp.syscallsp), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n")
|
||||
throw("entersyscall")
|
||||
})
|
||||
}
|
||||
if gp.syscallbp != 0 && gp.syscallbp < gp.stack.lo || gp.stack.hi < gp.syscallbp {
|
||||
systemstack(func() {
|
||||
print("entersyscall inconsistent bp ", hex(gp.syscallbp), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n")
|
||||
throw("entersyscall")
|
||||
})
|
||||
}
|
||||
@ -4553,14 +4559,20 @@ func entersyscallblock() {
|
||||
sp2 := gp.sched.sp
|
||||
sp3 := gp.syscallsp
|
||||
systemstack(func() {
|
||||
print("entersyscallblock inconsistent ", hex(sp1), " ", hex(sp2), " ", hex(sp3), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n")
|
||||
print("entersyscallblock inconsistent sp ", hex(sp1), " ", hex(sp2), " ", hex(sp3), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n")
|
||||
throw("entersyscallblock")
|
||||
})
|
||||
}
|
||||
casgstatus(gp, _Grunning, _Gsyscall)
|
||||
if gp.syscallsp < gp.stack.lo || gp.stack.hi < gp.syscallsp {
|
||||
systemstack(func() {
|
||||
print("entersyscallblock inconsistent ", hex(sp), " ", hex(gp.sched.sp), " ", hex(gp.syscallsp), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n")
|
||||
print("entersyscallblock inconsistent sp ", hex(sp), " ", hex(gp.sched.sp), " ", hex(gp.syscallsp), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n")
|
||||
throw("entersyscallblock")
|
||||
})
|
||||
}
|
||||
if gp.syscallbp != 0 && gp.syscallbp < gp.stack.lo || gp.stack.hi < gp.syscallbp {
|
||||
systemstack(func() {
|
||||
print("entersyscallblock inconsistent bp ", hex(bp), " ", hex(gp.sched.bp), " ", hex(gp.syscallbp), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n")
|
||||
throw("entersyscallblock")
|
||||
})
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ TEXT _main(SB),NOSPLIT,$-8
|
||||
MOVD R12, CTR
|
||||
BR (CTR)
|
||||
|
||||
// Paramater save space required to cross-call into _cgo_sys_thread_create
|
||||
#define PARAM_SPACE 16
|
||||
|
||||
TEXT _rt0_ppc64_aix_lib(SB),NOSPLIT,$-8
|
||||
// Start with standard C stack frame layout and linkage.
|
||||
@ -49,45 +51,45 @@ TEXT _rt0_ppc64_aix_lib(SB),NOSPLIT,$-8
|
||||
MOVW CR, R0 // Save CR in caller's frame
|
||||
MOVD R0, 8(R1)
|
||||
|
||||
MOVDU R1, -344(R1) // Allocate frame.
|
||||
MOVDU R1, -344-PARAM_SPACE(R1) // Allocate frame.
|
||||
|
||||
// Preserve callee-save registers.
|
||||
MOVD R14, 48(R1)
|
||||
MOVD R15, 56(R1)
|
||||
MOVD R16, 64(R1)
|
||||
MOVD R17, 72(R1)
|
||||
MOVD R18, 80(R1)
|
||||
MOVD R19, 88(R1)
|
||||
MOVD R20, 96(R1)
|
||||
MOVD R21,104(R1)
|
||||
MOVD R22, 112(R1)
|
||||
MOVD R23, 120(R1)
|
||||
MOVD R24, 128(R1)
|
||||
MOVD R25, 136(R1)
|
||||
MOVD R26, 144(R1)
|
||||
MOVD R27, 152(R1)
|
||||
MOVD R28, 160(R1)
|
||||
MOVD R29, 168(R1)
|
||||
MOVD g, 176(R1) // R30
|
||||
MOVD R31, 184(R1)
|
||||
FMOVD F14, 192(R1)
|
||||
FMOVD F15, 200(R1)
|
||||
FMOVD F16, 208(R1)
|
||||
FMOVD F17, 216(R1)
|
||||
FMOVD F18, 224(R1)
|
||||
FMOVD F19, 232(R1)
|
||||
FMOVD F20, 240(R1)
|
||||
FMOVD F21, 248(R1)
|
||||
FMOVD F22, 256(R1)
|
||||
FMOVD F23, 264(R1)
|
||||
FMOVD F24, 272(R1)
|
||||
FMOVD F25, 280(R1)
|
||||
FMOVD F26, 288(R1)
|
||||
FMOVD F27, 296(R1)
|
||||
FMOVD F28, 304(R1)
|
||||
FMOVD F29, 312(R1)
|
||||
FMOVD F30, 320(R1)
|
||||
FMOVD F31, 328(R1)
|
||||
MOVD R14, 48+PARAM_SPACE(R1)
|
||||
MOVD R15, 56+PARAM_SPACE(R1)
|
||||
MOVD R16, 64+PARAM_SPACE(R1)
|
||||
MOVD R17, 72+PARAM_SPACE(R1)
|
||||
MOVD R18, 80+PARAM_SPACE(R1)
|
||||
MOVD R19, 88+PARAM_SPACE(R1)
|
||||
MOVD R20, 96+PARAM_SPACE(R1)
|
||||
MOVD R21,104+PARAM_SPACE(R1)
|
||||
MOVD R22, 112+PARAM_SPACE(R1)
|
||||
MOVD R23, 120+PARAM_SPACE(R1)
|
||||
MOVD R24, 128+PARAM_SPACE(R1)
|
||||
MOVD R25, 136+PARAM_SPACE(R1)
|
||||
MOVD R26, 144+PARAM_SPACE(R1)
|
||||
MOVD R27, 152+PARAM_SPACE(R1)
|
||||
MOVD R28, 160+PARAM_SPACE(R1)
|
||||
MOVD R29, 168+PARAM_SPACE(R1)
|
||||
MOVD g, 176+PARAM_SPACE(R1) // R30
|
||||
MOVD R31, 184+PARAM_SPACE(R1)
|
||||
FMOVD F14, 192+PARAM_SPACE(R1)
|
||||
FMOVD F15, 200+PARAM_SPACE(R1)
|
||||
FMOVD F16, 208+PARAM_SPACE(R1)
|
||||
FMOVD F17, 216+PARAM_SPACE(R1)
|
||||
FMOVD F18, 224+PARAM_SPACE(R1)
|
||||
FMOVD F19, 232+PARAM_SPACE(R1)
|
||||
FMOVD F20, 240+PARAM_SPACE(R1)
|
||||
FMOVD F21, 248+PARAM_SPACE(R1)
|
||||
FMOVD F22, 256+PARAM_SPACE(R1)
|
||||
FMOVD F23, 264+PARAM_SPACE(R1)
|
||||
FMOVD F24, 272+PARAM_SPACE(R1)
|
||||
FMOVD F25, 280+PARAM_SPACE(R1)
|
||||
FMOVD F26, 288+PARAM_SPACE(R1)
|
||||
FMOVD F27, 296+PARAM_SPACE(R1)
|
||||
FMOVD F28, 304+PARAM_SPACE(R1)
|
||||
FMOVD F29, 312+PARAM_SPACE(R1)
|
||||
FMOVD F30, 320+PARAM_SPACE(R1)
|
||||
FMOVD F31, 328+PARAM_SPACE(R1)
|
||||
|
||||
// Synchronous initialization.
|
||||
MOVD $runtime·reginit(SB), R12
|
||||
@ -130,44 +132,44 @@ nocgo:
|
||||
|
||||
done:
|
||||
// Restore saved registers.
|
||||
MOVD 48(R1), R14
|
||||
MOVD 56(R1), R15
|
||||
MOVD 64(R1), R16
|
||||
MOVD 72(R1), R17
|
||||
MOVD 80(R1), R18
|
||||
MOVD 88(R1), R19
|
||||
MOVD 96(R1), R20
|
||||
MOVD 104(R1), R21
|
||||
MOVD 112(R1), R22
|
||||
MOVD 120(R1), R23
|
||||
MOVD 128(R1), R24
|
||||
MOVD 136(R1), R25
|
||||
MOVD 144(R1), R26
|
||||
MOVD 152(R1), R27
|
||||
MOVD 160(R1), R28
|
||||
MOVD 168(R1), R29
|
||||
MOVD 176(R1), g // R30
|
||||
MOVD 184(R1), R31
|
||||
FMOVD 196(R1), F14
|
||||
FMOVD 200(R1), F15
|
||||
FMOVD 208(R1), F16
|
||||
FMOVD 216(R1), F17
|
||||
FMOVD 224(R1), F18
|
||||
FMOVD 232(R1), F19
|
||||
FMOVD 240(R1), F20
|
||||
FMOVD 248(R1), F21
|
||||
FMOVD 256(R1), F22
|
||||
FMOVD 264(R1), F23
|
||||
FMOVD 272(R1), F24
|
||||
FMOVD 280(R1), F25
|
||||
FMOVD 288(R1), F26
|
||||
FMOVD 296(R1), F27
|
||||
FMOVD 304(R1), F28
|
||||
FMOVD 312(R1), F29
|
||||
FMOVD 320(R1), F30
|
||||
FMOVD 328(R1), F31
|
||||
MOVD 48+PARAM_SPACE(R1), R14
|
||||
MOVD 56+PARAM_SPACE(R1), R15
|
||||
MOVD 64+PARAM_SPACE(R1), R16
|
||||
MOVD 72+PARAM_SPACE(R1), R17
|
||||
MOVD 80+PARAM_SPACE(R1), R18
|
||||
MOVD 88+PARAM_SPACE(R1), R19
|
||||
MOVD 96+PARAM_SPACE(R1), R20
|
||||
MOVD 104+PARAM_SPACE(R1), R21
|
||||
MOVD 112+PARAM_SPACE(R1), R22
|
||||
MOVD 120+PARAM_SPACE(R1), R23
|
||||
MOVD 128+PARAM_SPACE(R1), R24
|
||||
MOVD 136+PARAM_SPACE(R1), R25
|
||||
MOVD 144+PARAM_SPACE(R1), R26
|
||||
MOVD 152+PARAM_SPACE(R1), R27
|
||||
MOVD 160+PARAM_SPACE(R1), R28
|
||||
MOVD 168+PARAM_SPACE(R1), R29
|
||||
MOVD 176+PARAM_SPACE(R1), g // R30
|
||||
MOVD 184+PARAM_SPACE(R1), R31
|
||||
FMOVD 196+PARAM_SPACE(R1), F14
|
||||
FMOVD 200+PARAM_SPACE(R1), F15
|
||||
FMOVD 208+PARAM_SPACE(R1), F16
|
||||
FMOVD 216+PARAM_SPACE(R1), F17
|
||||
FMOVD 224+PARAM_SPACE(R1), F18
|
||||
FMOVD 232+PARAM_SPACE(R1), F19
|
||||
FMOVD 240+PARAM_SPACE(R1), F20
|
||||
FMOVD 248+PARAM_SPACE(R1), F21
|
||||
FMOVD 256+PARAM_SPACE(R1), F22
|
||||
FMOVD 264+PARAM_SPACE(R1), F23
|
||||
FMOVD 272+PARAM_SPACE(R1), F24
|
||||
FMOVD 280+PARAM_SPACE(R1), F25
|
||||
FMOVD 288+PARAM_SPACE(R1), F26
|
||||
FMOVD 296+PARAM_SPACE(R1), F27
|
||||
FMOVD 304+PARAM_SPACE(R1), F28
|
||||
FMOVD 312+PARAM_SPACE(R1), F29
|
||||
FMOVD 320+PARAM_SPACE(R1), F30
|
||||
FMOVD 328+PARAM_SPACE(R1), F31
|
||||
|
||||
ADD $344, R1
|
||||
ADD $344+PARAM_SPACE, R1
|
||||
|
||||
MOVD 8(R1), R0
|
||||
MOVFL R0, $0xff
|
||||
|
@ -575,15 +575,15 @@ func TestGdbAutotmpTypes(t *testing.T) {
|
||||
|
||||
// Check that the backtrace matches the source code.
|
||||
types := []string{
|
||||
"[]main.astruct;",
|
||||
"bucket<string,main.astruct>;",
|
||||
"hash<string,main.astruct>;",
|
||||
"main.astruct;",
|
||||
"hash<string,main.astruct> * map[string]main.astruct;",
|
||||
"[]main.astruct",
|
||||
"bucket<string,main.astruct>",
|
||||
"hash<string,main.astruct>",
|
||||
"main.astruct",
|
||||
"hash<string,main.astruct> * map[string]main.astruct",
|
||||
}
|
||||
for _, name := range types {
|
||||
if !strings.Contains(sgot, name) {
|
||||
t.Fatalf("could not find %s in 'info typrs astruct' output", name)
|
||||
t.Fatalf("could not find %q in 'info typrs astruct' output", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -605,6 +605,19 @@ var crashing atomic.Int32
|
||||
var testSigtrap func(info *siginfo, ctxt *sigctxt, gp *g) bool
|
||||
var testSigusr1 func(gp *g) bool
|
||||
|
||||
// sigsysIgnored is non-zero if we are currently ignoring SIGSYS. See issue #69065.
|
||||
var sigsysIgnored uint32
|
||||
|
||||
//go:linkname ignoreSIGSYS os.ignoreSIGSYS
|
||||
func ignoreSIGSYS() {
|
||||
atomic.Store(&sigsysIgnored, 1)
|
||||
}
|
||||
|
||||
//go:linkname restoreSIGSYS os.restoreSIGSYS
|
||||
func restoreSIGSYS() {
|
||||
atomic.Store(&sigsysIgnored, 0)
|
||||
}
|
||||
|
||||
// sighandler is invoked when a signal occurs. The global g will be
|
||||
// set to a gsignal goroutine and we will be running on the alternate
|
||||
// signal stack. The parameter gp will be the value of the global g
|
||||
@ -715,6 +728,10 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
|
||||
return
|
||||
}
|
||||
|
||||
if sig == _SIGSYS && c.sigFromSeccomp() && atomic.Load(&sigsysIgnored) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if flags&_SigKill != 0 {
|
||||
dieFromSignal(sig)
|
||||
}
|
||||
|
@ -454,43 +454,37 @@ func syscall_getprocaddress(handle uintptr, procname *byte) (outhandle, err uint
|
||||
//go:linkname syscall_Syscall syscall.Syscall
|
||||
//go:nosplit
|
||||
func syscall_Syscall(fn, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
|
||||
args := [...]uintptr{a1, a2, a3}
|
||||
return syscall_SyscallN(fn, args[:nargs]...)
|
||||
return syscall_syscalln(fn, nargs, a1, a2, a3)
|
||||
}
|
||||
|
||||
//go:linkname syscall_Syscall6 syscall.Syscall6
|
||||
//go:nosplit
|
||||
func syscall_Syscall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
|
||||
args := [...]uintptr{a1, a2, a3, a4, a5, a6}
|
||||
return syscall_SyscallN(fn, args[:nargs]...)
|
||||
return syscall_syscalln(fn, nargs, a1, a2, a3, a4, a5, a6)
|
||||
}
|
||||
|
||||
//go:linkname syscall_Syscall9 syscall.Syscall9
|
||||
//go:nosplit
|
||||
func syscall_Syscall9(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) {
|
||||
args := [...]uintptr{a1, a2, a3, a4, a5, a6, a7, a8, a9}
|
||||
return syscall_SyscallN(fn, args[:nargs]...)
|
||||
return syscall_syscalln(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9)
|
||||
}
|
||||
|
||||
//go:linkname syscall_Syscall12 syscall.Syscall12
|
||||
//go:nosplit
|
||||
func syscall_Syscall12(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, err uintptr) {
|
||||
args := [...]uintptr{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12}
|
||||
return syscall_SyscallN(fn, args[:nargs]...)
|
||||
return syscall_syscalln(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)
|
||||
}
|
||||
|
||||
//go:linkname syscall_Syscall15 syscall.Syscall15
|
||||
//go:nosplit
|
||||
func syscall_Syscall15(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
|
||||
args := [...]uintptr{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15}
|
||||
return syscall_SyscallN(fn, args[:nargs]...)
|
||||
return syscall_syscalln(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
|
||||
}
|
||||
|
||||
//go:linkname syscall_Syscall18 syscall.Syscall18
|
||||
//go:nosplit
|
||||
func syscall_Syscall18(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2, err uintptr) {
|
||||
args := [...]uintptr{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18}
|
||||
return syscall_SyscallN(fn, args[:nargs]...)
|
||||
return syscall_syscalln(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18)
|
||||
}
|
||||
|
||||
// maxArgs should be divisible by 2, as Windows stack
|
||||
@ -503,7 +497,15 @@ const maxArgs = 42
|
||||
//go:linkname syscall_SyscallN syscall.SyscallN
|
||||
//go:nosplit
|
||||
func syscall_SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
|
||||
if len(args) > maxArgs {
|
||||
return syscall_syscalln(fn, uintptr(len(args)), args...)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func syscall_syscalln(fn, n uintptr, args ...uintptr) (r1, r2, err uintptr) {
|
||||
if n > uintptr(len(args)) {
|
||||
panic("syscall: n > len(args)") // should not be reachable from user code
|
||||
}
|
||||
if n > maxArgs {
|
||||
panic("runtime: SyscallN has too many arguments")
|
||||
}
|
||||
|
||||
@ -512,7 +514,7 @@ func syscall_SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
|
||||
// calls back into Go.
|
||||
c := &getg().m.winsyscall
|
||||
c.fn = fn
|
||||
c.n = uintptr(len(args))
|
||||
c.n = n
|
||||
if c.n != 0 {
|
||||
c.args = uintptr(noescape(unsafe.Pointer(&args[0])))
|
||||
}
|
||||
|
@ -1212,6 +1212,13 @@ func TestBigStackCallbackSyscall(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyscallStackUsage(t *testing.T) {
|
||||
// Test that the stack usage of a syscall doesn't exceed the limit.
|
||||
// See https://go.dev/issue/69813.
|
||||
syscall.Syscall15(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
syscall.Syscall18(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
var (
|
||||
modwinmm = syscall.NewLazyDLL("winmm.dll")
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
@ -26,10 +26,11 @@ type timer struct {
|
||||
// mu protects reads and writes to all fields, with exceptions noted below.
|
||||
mu mutex
|
||||
|
||||
astate atomic.Uint8 // atomic copy of state bits at last unlock
|
||||
state uint8 // state bits
|
||||
isChan bool // timer has a channel; immutable; can be read without lock
|
||||
blocked uint32 // number of goroutines blocked on timer's channel
|
||||
astate atomic.Uint8 // atomic copy of state bits at last unlock
|
||||
state uint8 // state bits
|
||||
isChan bool // timer has a channel; immutable; can be read without lock
|
||||
|
||||
blocked uint32 // number of goroutines blocked on timer's channel
|
||||
|
||||
// Timer wakes up at when, and then at when+period, ... (period > 0 only)
|
||||
// each time calling f(arg, seq, delay) in the timer goroutine, so f must be
|
||||
@ -68,6 +69,20 @@ type timer struct {
|
||||
// sendLock protects sends on the timer's channel.
|
||||
// Not used for async (pre-Go 1.23) behavior when debug.asynctimerchan.Load() != 0.
|
||||
sendLock mutex
|
||||
|
||||
// isSending is used to handle races between running a
|
||||
// channel timer and stopping or resetting the timer.
|
||||
// It is used only for channel timers (t.isChan == true).
|
||||
// It is not used for tickers.
|
||||
// The value is incremented when about to send a value on the channel,
|
||||
// and decremented after sending the value.
|
||||
// The stop/reset code uses this to detect whether it
|
||||
// stopped the channel send.
|
||||
//
|
||||
// isSending is incremented only when t.mu is held.
|
||||
// isSending is decremented only when t.sendLock is held.
|
||||
// isSending is read only when both t.mu and t.sendLock are held.
|
||||
isSending atomic.Int32
|
||||
}
|
||||
|
||||
// init initializes a newly allocated timer t.
|
||||
@ -431,6 +446,15 @@ func (t *timer) stop() bool {
|
||||
// Stop any future sends with stale values.
|
||||
// See timer.unlockAndRun.
|
||||
t.seq++
|
||||
|
||||
// If there is currently a send in progress,
|
||||
// incrementing seq is going to prevent that
|
||||
// send from actually happening. That means
|
||||
// that we should return true: the timer was
|
||||
// stopped, even though t.when may be zero.
|
||||
if t.period == 0 && t.isSending.Load() > 0 {
|
||||
pending = true
|
||||
}
|
||||
}
|
||||
t.unlock()
|
||||
if !async && t.isChan {
|
||||
@ -490,6 +514,7 @@ func (t *timer) modify(when, period int64, f func(arg any, seq uintptr, delay in
|
||||
t.maybeRunAsync()
|
||||
}
|
||||
t.trace("modify")
|
||||
oldPeriod := t.period
|
||||
t.period = period
|
||||
if f != nil {
|
||||
t.f = f
|
||||
@ -525,6 +550,15 @@ func (t *timer) modify(when, period int64, f func(arg any, seq uintptr, delay in
|
||||
// Stop any future sends with stale values.
|
||||
// See timer.unlockAndRun.
|
||||
t.seq++
|
||||
|
||||
// If there is currently a send in progress,
|
||||
// incrementing seq is going to prevent that
|
||||
// send from actually happening. That means
|
||||
// that we should return true: the timer was
|
||||
// stopped, even though t.when may be zero.
|
||||
if oldPeriod == 0 && t.isSending.Load() > 0 {
|
||||
pending = true
|
||||
}
|
||||
}
|
||||
t.unlock()
|
||||
if !async && t.isChan {
|
||||
@ -1013,6 +1047,15 @@ func (t *timer) unlockAndRun(now int64) {
|
||||
}
|
||||
t.updateHeap()
|
||||
}
|
||||
|
||||
async := debug.asynctimerchan.Load() != 0
|
||||
if !async && t.isChan && t.period == 0 {
|
||||
// Tell Stop/Reset that we are sending a value.
|
||||
if t.isSending.Add(1) < 0 {
|
||||
throw("too many concurrent timer firings")
|
||||
}
|
||||
}
|
||||
|
||||
t.unlock()
|
||||
|
||||
if raceenabled {
|
||||
@ -1028,7 +1071,6 @@ func (t *timer) unlockAndRun(now int64) {
|
||||
ts.unlock()
|
||||
}
|
||||
|
||||
async := debug.asynctimerchan.Load() != 0
|
||||
if !async && t.isChan {
|
||||
// For a timer channel, we want to make sure that no stale sends
|
||||
// happen after a t.stop or t.modify, but we cannot hold t.mu
|
||||
@ -1044,7 +1086,21 @@ func (t *timer) unlockAndRun(now int64) {
|
||||
// and double-check that t.seq is still the seq value we saw above.
|
||||
// If not, the timer has been updated and we should skip the send.
|
||||
// We skip the send by reassigning f to a no-op function.
|
||||
//
|
||||
// The isSending field tells t.stop or t.modify that we have
|
||||
// started to send the value. That lets them correctly return
|
||||
// true meaning that no value was sent.
|
||||
lock(&t.sendLock)
|
||||
|
||||
if t.period == 0 {
|
||||
// We are committed to possibly sending a value
|
||||
// based on seq, so no need to keep telling
|
||||
// stop/modify that we are sending.
|
||||
if t.isSending.Add(-1) < 0 {
|
||||
throw("mismatched isSending updates")
|
||||
}
|
||||
}
|
||||
|
||||
if t.seq != seq {
|
||||
f = func(any, uintptr, int64) {}
|
||||
}
|
||||
|
@ -293,3 +293,8 @@ childerror:
|
||||
RawSyscall(SYS_EXIT, 253, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// forkAndExecFailureCleanup cleans up after an exec failure.
|
||||
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
@ -317,3 +317,8 @@ childerror:
|
||||
RawSyscall(SYS_EXIT, 253, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// forkAndExecFailureCleanup cleans up after an exec failure.
|
||||
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
@ -314,6 +314,11 @@ childerror:
|
||||
}
|
||||
}
|
||||
|
||||
// forkAndExecFailureCleanup cleans up after an exec failure.
|
||||
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
func ioctlPtr(fd, req uintptr, arg unsafe.Pointer) (err Errno) {
|
||||
return ioctl(fd, req, uintptr(arg))
|
||||
}
|
||||
|
@ -289,3 +289,8 @@ childerror:
|
||||
rawSyscall(abi.FuncPCABI0(libc_exit_trampoline), 253, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// forkAndExecFailureCleanup cleans up after an exec failure.
|
||||
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
package syscall
|
||||
|
||||
import (
|
||||
errpkg "errors"
|
||||
"internal/itoa"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
@ -328,6 +329,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
|
||||
if clone3 != nil {
|
||||
pid, err1 = rawVforkSyscall(_SYS_clone3, uintptr(unsafe.Pointer(clone3)), unsafe.Sizeof(*clone3), 0)
|
||||
} else {
|
||||
// N.B. Keep in sync with doCheckClonePidfd.
|
||||
flags |= uintptr(SIGCHLD)
|
||||
if runtime.GOARCH == "s390x" {
|
||||
// On Linux/s390, the first two arguments of clone(2) are swapped.
|
||||
@ -735,3 +737,90 @@ func writeUidGidMappings(pid int, sys *SysProcAttr) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// forkAndExecFailureCleanup cleans up after an exec failure.
|
||||
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
|
||||
if sys.PidFD != nil && *sys.PidFD != -1 {
|
||||
Close(*sys.PidFD)
|
||||
*sys.PidFD = -1
|
||||
}
|
||||
}
|
||||
|
||||
// checkClonePidfd verifies that clone(CLONE_PIDFD) works by actually doing a
|
||||
// clone.
|
||||
//
|
||||
//go:linkname os_checkClonePidfd os.checkClonePidfd
|
||||
func os_checkClonePidfd() error {
|
||||
pidfd := int32(-1)
|
||||
pid, errno := doCheckClonePidfd(&pidfd)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
|
||||
if pidfd == -1 {
|
||||
// Bad: CLONE_PIDFD failed to provide a pidfd. Reap the process
|
||||
// before returning.
|
||||
|
||||
var err error
|
||||
for {
|
||||
var status WaitStatus
|
||||
_, err = Wait4(int(pid), &status, 0, nil)
|
||||
if err != EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return errpkg.New("clone(CLONE_PIDFD) failed to return pidfd")
|
||||
}
|
||||
|
||||
// Good: CLONE_PIDFD provided a pidfd. Reap the process and close the
|
||||
// pidfd.
|
||||
defer Close(int(pidfd))
|
||||
|
||||
for {
|
||||
const _P_PIDFD = 3
|
||||
_, _, errno = Syscall6(SYS_WAITID, _P_PIDFD, uintptr(pidfd), 0, WEXITED, 0, 0)
|
||||
if errno != EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// doCheckClonePidfd implements the actual clone call of os_checkClonePidfd and
|
||||
// child execution. This is a separate function so we can separate the child's
|
||||
// and parent's stack frames if we're using vfork.
|
||||
//
|
||||
// This is go:noinline because the point is to keep the stack frames of this
|
||||
// and os_checkClonePidfd separate.
|
||||
//
|
||||
//go:noinline
|
||||
func doCheckClonePidfd(pidfd *int32) (pid uintptr, errno Errno) {
|
||||
flags := uintptr(CLONE_VFORK|CLONE_VM|CLONE_PIDFD|SIGCHLD)
|
||||
if runtime.GOARCH == "s390x" {
|
||||
// On Linux/s390, the first two arguments of clone(2) are swapped.
|
||||
pid, errno = rawVforkSyscall(SYS_CLONE, 0, flags, uintptr(unsafe.Pointer(pidfd)))
|
||||
} else {
|
||||
pid, errno = rawVforkSyscall(SYS_CLONE, flags, 0, uintptr(unsafe.Pointer(pidfd)))
|
||||
}
|
||||
if errno != 0 || pid != 0 {
|
||||
// If we're in the parent, we must return immediately
|
||||
// so we're not in the same stack frame as the child.
|
||||
// This can at most use the return PC, which the child
|
||||
// will not modify, and the results of
|
||||
// rawVforkSyscall, which must have been written after
|
||||
// the child was replaced.
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
RawSyscall(SYS_EXIT_GROUP, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
@ -237,6 +237,10 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
|
||||
for err1 == EINTR {
|
||||
_, err1 = Wait4(pid, &wstatus, 0, nil)
|
||||
}
|
||||
|
||||
// OS-specific cleanup on failure.
|
||||
forkAndExecFailureCleanup(attr, sys)
|
||||
|
||||
return 0, err
|
||||
}
|
||||
|
||||
|
@ -785,6 +785,119 @@ func TestAdjustTimers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopResult(t *testing.T) {
|
||||
testStopResetResult(t, true)
|
||||
}
|
||||
|
||||
func TestResetResult(t *testing.T) {
|
||||
testStopResetResult(t, false)
|
||||
}
|
||||
|
||||
// Test that when racing between running a timer and stopping a timer Stop
|
||||
// consistently indicates whether a value can be read from the channel.
|
||||
// Issue #69312.
|
||||
func testStopResetResult(t *testing.T, testStop bool) {
|
||||
for _, name := range []string{"0", "1", "2"} {
|
||||
t.Run("asynctimerchan="+name, func(t *testing.T) {
|
||||
testStopResetResultGODEBUG(t, testStop, name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testStopResetResultGODEBUG(t *testing.T, testStop bool, godebug string) {
|
||||
t.Setenv("GODEBUG", "asynctimerchan="+godebug)
|
||||
|
||||
stopOrReset := func(timer *Timer) bool {
|
||||
if testStop {
|
||||
return timer.Stop()
|
||||
} else {
|
||||
return timer.Reset(1 * Hour)
|
||||
}
|
||||
}
|
||||
|
||||
start := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
const N = 1000
|
||||
wg.Add(N)
|
||||
for range N {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-start
|
||||
for j := 0; j < 100; j++ {
|
||||
timer1 := NewTimer(1 * Millisecond)
|
||||
timer2 := NewTimer(1 * Millisecond)
|
||||
select {
|
||||
case <-timer1.C:
|
||||
if !stopOrReset(timer2) {
|
||||
// The test fails if this
|
||||
// channel read times out.
|
||||
<-timer2.C
|
||||
}
|
||||
case <-timer2.C:
|
||||
if !stopOrReset(timer1) {
|
||||
// The test fails if this
|
||||
// channel read times out.
|
||||
<-timer1.C
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
close(start)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Test having a large number of goroutines wake up a ticker simultaneously.
|
||||
// This used to trigger a crash when run under x/tools/cmd/stress.
|
||||
func TestMultiWakeupTicker(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("-short")
|
||||
}
|
||||
|
||||
goroutines := runtime.GOMAXPROCS(0)
|
||||
timer := NewTicker(Microsecond)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(goroutines)
|
||||
for range goroutines {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for range 100000 {
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-After(Millisecond):
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Test having a large number of goroutines wake up a timer simultaneously.
|
||||
// This used to trigger a crash when run under x/tools/cmd/stress.
|
||||
func TestMultiWakeupTimer(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("-short")
|
||||
}
|
||||
|
||||
goroutines := runtime.GOMAXPROCS(0)
|
||||
timer := NewTimer(Nanosecond)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(goroutines)
|
||||
for range goroutines {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for range 10000 {
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
}
|
||||
timer.Reset(Nanosecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Benchmark timer latency when the thread that creates the timer is busy with
|
||||
// other work and the timers must be serviced by other threads.
|
||||
// https://golang.org/issue/38860
|
||||
|
@ -27,7 +27,7 @@ func cSeq(stringOffsets ...uintptr) cloneSeq {
|
||||
|
||||
func testCloneSeq[T any](t *testing.T, want cloneSeq) {
|
||||
typName := reflect.TypeFor[T]().Name()
|
||||
typ := abi.TypeOf(*new(T))
|
||||
typ := abi.TypeFor[T]()
|
||||
t.Run(typName, func(t *testing.T) {
|
||||
got := makeCloneSeq(typ)
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
|
@ -31,7 +31,7 @@ func (h Handle[T]) Value() T {
|
||||
// are equal if and only if the values used to produce them are equal.
|
||||
func Make[T comparable](value T) Handle[T] {
|
||||
// Find the map for type T.
|
||||
typ := abi.TypeOf(value)
|
||||
typ := abi.TypeFor[T]()
|
||||
ma, ok := uniqueMaps.Load(typ)
|
||||
if !ok {
|
||||
// This is a good time to initialize cleanup, since we must go through
|
||||
@ -50,13 +50,13 @@ func Make[T comparable](value T) Handle[T] {
|
||||
toInsert *T // Keep this around to keep it alive.
|
||||
toInsertWeak weak.Pointer[T]
|
||||
)
|
||||
newValue := func() weak.Pointer[T] {
|
||||
newValue := func() (T, weak.Pointer[T]) {
|
||||
if toInsert == nil {
|
||||
toInsert = new(T)
|
||||
*toInsert = clone(value, &m.cloneSeq)
|
||||
toInsertWeak = weak.Make(toInsert)
|
||||
}
|
||||
return toInsertWeak
|
||||
return *toInsert, toInsertWeak
|
||||
}
|
||||
var ptr *T
|
||||
for {
|
||||
@ -64,7 +64,8 @@ func Make[T comparable](value T) Handle[T] {
|
||||
wp, ok := m.Load(value)
|
||||
if !ok {
|
||||
// Try to insert a new value into the map.
|
||||
wp, _ = m.LoadOrStore(value, newValue())
|
||||
k, v := newValue()
|
||||
wp, _ = m.LoadOrStore(k, v)
|
||||
}
|
||||
// Now that we're sure there's a value in the map, let's
|
||||
// try to get the pointer we need out of it.
|
||||
|
@ -9,7 +9,10 @@ import (
|
||||
"internal/abi"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Set up special types. Because the internal maps are sharded by type,
|
||||
@ -41,6 +44,7 @@ func TestHandle(t *testing.T) {
|
||||
s: [2]testStringStruct{testStringStruct{"y"}, testStringStruct{"z"}},
|
||||
})
|
||||
testHandle[testStruct](t, testStruct{0.5, "184"})
|
||||
testHandle[testEface](t, testEface("hello"))
|
||||
}
|
||||
|
||||
func testHandle[T comparable](t *testing.T, value T) {
|
||||
@ -93,7 +97,7 @@ func drainMaps(t *testing.T) {
|
||||
|
||||
func checkMapsFor[T comparable](t *testing.T, value T) {
|
||||
// Manually load the value out of the map.
|
||||
typ := abi.TypeOf(value)
|
||||
typ := abi.TypeFor[T]()
|
||||
a, ok := uniqueMaps.Load(typ)
|
||||
if !ok {
|
||||
return
|
||||
@ -109,3 +113,22 @@ func checkMapsFor[T comparable](t *testing.T, value T) {
|
||||
}
|
||||
t.Errorf("failed to drain internal maps of %v", value)
|
||||
}
|
||||
|
||||
func TestMakeClonesStrings(t *testing.T) {
|
||||
s := strings.Clone("abcdefghijklmnopqrstuvwxyz") // N.B. Must be big enough to not be tiny-allocated.
|
||||
ran := make(chan bool)
|
||||
runtime.SetFinalizer(unsafe.StringData(s), func(_ *byte) {
|
||||
ran <- true
|
||||
})
|
||||
h := Make(s)
|
||||
|
||||
// Clean up s (hopefully) and run the finalizer.
|
||||
runtime.GC()
|
||||
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("string was improperly retained")
|
||||
case <-ran:
|
||||
}
|
||||
runtime.KeepAlive(h)
|
||||
}
|
||||
|
@ -12,22 +12,29 @@ import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
checkLinkOutput("", "-B argument must start with 0x")
|
||||
// The cannot open file error indicates that the parsing of -B flag
|
||||
// succeeded and it failed at a later step.
|
||||
checkLinkOutput("0", "-B argument must start with 0x")
|
||||
checkLinkOutput("0x", "usage")
|
||||
checkLinkOutput("0x", "cannot open file nonexistent.o")
|
||||
checkLinkOutput("0x0", "-B argument must have even number of digits")
|
||||
checkLinkOutput("0x00", "usage")
|
||||
checkLinkOutput("0x00", "cannot open file nonexistent.o")
|
||||
checkLinkOutput("0xYZ", "-B argument contains invalid hex digit")
|
||||
checkLinkOutput("0x"+strings.Repeat("00", 32), "usage")
|
||||
checkLinkOutput("0x"+strings.Repeat("00", 33), "-B option too long (max 32 digits)")
|
||||
|
||||
maxLen := 32
|
||||
if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
|
||||
maxLen = 16
|
||||
}
|
||||
checkLinkOutput("0x"+strings.Repeat("00", maxLen), "cannot open file nonexistent.o")
|
||||
checkLinkOutput("0x"+strings.Repeat("00", maxLen+1), "-B option too long")
|
||||
}
|
||||
|
||||
func checkLinkOutput(buildid string, message string) {
|
||||
cmd := exec.Command("go", "tool", "link", "-B", buildid)
|
||||
cmd := exec.Command("go", "tool", "link", "-B", buildid, "nonexistent.o")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
log.Fatalf("expected cmd/link to fail")
|
||||
@ -39,6 +46,6 @@ func checkLinkOutput(buildid string, message string) {
|
||||
}
|
||||
|
||||
if !strings.Contains(firstLine, message) {
|
||||
log.Fatalf("cmd/link output did not include expected message %q: %s", message, firstLine)
|
||||
log.Fatalf("%s: cmd/link output did not include expected message %q: %s", buildid, message, firstLine)
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,20 @@
|
||||
// errorcheck -lang=go1.21
|
||||
// errorcheck -lang=go1.22
|
||||
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.4
|
||||
// This file has been changed from its original version as
|
||||
// //go:build file versions below 1.21 set the language version to 1.21.
|
||||
// The original tested a -lang version of 1.21 with a file version of
|
||||
// go1.4 while this new version tests a -lang version of go1.22
|
||||
// with a file version of go1.21.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
package p
|
||||
|
||||
const c = 0o123 // ERROR "file declares //go:build go1.4"
|
||||
|
||||
// ERROR "file declares //go:build go1.4"
|
||||
|
||||
//line issue63489a.go:13:1
|
||||
const d = 0o124
|
||||
func f() {
|
||||
for _ = range 10 { // ERROR "file declares //go:build go1.21"
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,20 @@
|
||||
// errorcheck -lang=go1.4
|
||||
// errorcheck -lang=go1.21
|
||||
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.4
|
||||
// This file has been changed from its original version as
|
||||
// //go:build file versions below 1.21 set the language version to 1.21.
|
||||
// The original tested a -lang version of 1.4 with a file version of
|
||||
// go1.4 while this new version tests a -lang version of go1.1
|
||||
// with a file version of go1.21.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
package p
|
||||
|
||||
const c = 0o123 // ERROR "file declares //go:build go1.4"
|
||||
func f() {
|
||||
for _ = range 10 { // ERROR "file declares //go:build go1.21"
|
||||
}
|
||||
}
|
||||
|
15
test/fixedbugs/issue68580.go
Normal file
15
test/fixedbugs/issue68580.go
Normal file
@ -0,0 +1,15 @@
|
||||
// compile -goexperiment aliastypeparams
|
||||
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
type A[P any] = struct{ _ P }
|
||||
|
||||
type N[P any] A[P]
|
||||
|
||||
func f[P any](N[P]) {}
|
||||
|
||||
var _ = f[int]
|
57
test/fixedbugs/issue69110.go
Normal file
57
test/fixedbugs/issue69110.go
Normal file
@ -0,0 +1,57 @@
|
||||
// run
|
||||
|
||||
// Copyright 2024 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 (
|
||||
"maps"
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
const NB = 4
|
||||
|
||||
func f() {
|
||||
// Make a map with NB buckets, at max capacity.
|
||||
// 6.5 entries/bucket.
|
||||
ne := NB * 13 / 2
|
||||
m := map[int]int{}
|
||||
for i := 0; i < ne; i++ {
|
||||
m[i] = i
|
||||
}
|
||||
|
||||
// delete/insert a lot, to hopefully get lots of overflow buckets
|
||||
// and trigger a same-size grow.
|
||||
ssg := false
|
||||
for i := ne; i < ne+1000; i++ {
|
||||
delete(m, i-ne)
|
||||
m[i] = i
|
||||
if sameSizeGrow(m) {
|
||||
ssg = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ssg {
|
||||
return
|
||||
}
|
||||
|
||||
// Insert 1 more entry, which would ordinarily trigger a growth.
|
||||
// We can't grow while growing, so we instead go over our
|
||||
// target capacity.
|
||||
m[-1] = -1
|
||||
|
||||
// Cloning in this state will make a map with a destination bucket
|
||||
// array twice the size of the source.
|
||||
_ = maps.Clone(m)
|
||||
}
|
||||
|
||||
//go:linkname sameSizeGrow runtime.sameSizeGrowForIssue69110Test
|
||||
func sameSizeGrow(m map[int]int) bool
|
173
test/fixedbugs/issue69434.go
Normal file
173
test/fixedbugs/issue69434.go
Normal file
@ -0,0 +1,173 @@
|
||||
// run
|
||||
|
||||
// Copyright 2024 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 (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"iter"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// WordReader is the struct that implements io.Reader
|
||||
type WordReader struct {
|
||||
scanner *bufio.Scanner
|
||||
}
|
||||
|
||||
// NewWordReader creates a new WordReader from an io.Reader
|
||||
func NewWordReader(r io.Reader) *WordReader {
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner.Split(bufio.ScanWords)
|
||||
return &WordReader{
|
||||
scanner: scanner,
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads data from the input stream and returns a single lowercase word at a time
|
||||
func (wr *WordReader) Read(p []byte) (n int, err error) {
|
||||
if !wr.scanner.Scan() {
|
||||
if err := wr.scanner.Err(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return 0, io.EOF
|
||||
}
|
||||
word := wr.scanner.Text()
|
||||
cleanedWord := removeNonAlphabetic(word)
|
||||
if len(cleanedWord) == 0 {
|
||||
return wr.Read(p)
|
||||
}
|
||||
n = copy(p, []byte(cleanedWord))
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// All returns an iterator allowing the caller to iterate over the WordReader using for/range.
|
||||
func (wr *WordReader) All() iter.Seq[string] {
|
||||
word := make([]byte, 1024)
|
||||
return func(yield func(string) bool) {
|
||||
var err error
|
||||
var n int
|
||||
for n, err = wr.Read(word); err == nil; n, err = wr.Read(word) {
|
||||
if !yield(string(word[:n])) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != io.EOF {
|
||||
fmt.Fprintf(os.Stderr, "error reading word: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// removeNonAlphabetic removes non-alphabetic characters from a word using strings.Map
|
||||
func removeNonAlphabetic(word string) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
if unicode.IsLetter(r) {
|
||||
return unicode.ToLower(r)
|
||||
}
|
||||
return -1
|
||||
}, word)
|
||||
}
|
||||
|
||||
// ProbabilisticSkipper determines if an item should be retained with probability 1/(1<<n)
|
||||
type ProbabilisticSkipper struct {
|
||||
n int
|
||||
counter uint64
|
||||
bitmask uint64
|
||||
}
|
||||
|
||||
// NewProbabilisticSkipper initializes the ProbabilisticSkipper
|
||||
func NewProbabilisticSkipper(n int) *ProbabilisticSkipper {
|
||||
pr := &ProbabilisticSkipper{n: n}
|
||||
pr.refreshCounter()
|
||||
return pr
|
||||
}
|
||||
|
||||
// check panics if pr.n is not the expected value
|
||||
func (pr *ProbabilisticSkipper) check(n int) {
|
||||
if pr.n != n {
|
||||
panic(fmt.Sprintf("check: pr.n != n %d != %d", pr.n, n))
|
||||
}
|
||||
}
|
||||
|
||||
// refreshCounter refreshes the counter with a new random value
|
||||
func (pr *ProbabilisticSkipper) refreshCounter() {
|
||||
if pr.n == 0 {
|
||||
pr.bitmask = ^uint64(0) // All bits set to 1
|
||||
} else {
|
||||
pr.bitmask = rand.Uint64()
|
||||
for i := 0; i < pr.n-1; i++ {
|
||||
pr.bitmask &= rand.Uint64()
|
||||
}
|
||||
}
|
||||
pr.counter = 64
|
||||
}
|
||||
|
||||
// ShouldSkip returns true with probability 1/(1<<n)
|
||||
func (pr *ProbabilisticSkipper) ShouldSkip() bool {
|
||||
remove := pr.bitmask&1 == 0
|
||||
pr.bitmask >>= 1
|
||||
pr.counter--
|
||||
if pr.counter == 0 {
|
||||
pr.refreshCounter()
|
||||
}
|
||||
return remove
|
||||
}
|
||||
|
||||
// EstimateUniqueWordsIter estimates the number of unique words using a probabilistic counting method
|
||||
func EstimateUniqueWordsIter(reader io.Reader, memorySize int) int {
|
||||
wordReader := NewWordReader(reader)
|
||||
words := make(map[string]struct{}, memorySize)
|
||||
|
||||
rounds := 0
|
||||
roundRemover := NewProbabilisticSkipper(1)
|
||||
wordSkipper := NewProbabilisticSkipper(rounds)
|
||||
wordSkipper.check(rounds)
|
||||
|
||||
for word := range wordReader.All() {
|
||||
wordSkipper.check(rounds)
|
||||
if wordSkipper.ShouldSkip() {
|
||||
delete(words, word)
|
||||
} else {
|
||||
words[word] = struct{}{}
|
||||
|
||||
if len(words) >= memorySize {
|
||||
rounds++
|
||||
|
||||
wordSkipper = NewProbabilisticSkipper(rounds)
|
||||
for w := range words {
|
||||
if roundRemover.ShouldSkip() {
|
||||
delete(words, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
wordSkipper.check(rounds)
|
||||
}
|
||||
|
||||
if len(words) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
invProbability := 1 << rounds
|
||||
estimatedUniqueWords := len(words) * invProbability
|
||||
return estimatedUniqueWords
|
||||
}
|
||||
|
||||
func main() {
|
||||
input := "Hello, world! This is a test. Hello, world, hello!"
|
||||
expectedUniqueWords := 6 // "hello", "world", "this", "is", "a", "test" (but "hello" and "world" are repeated)
|
||||
memorySize := 6
|
||||
|
||||
reader := strings.NewReader(input)
|
||||
estimatedUniqueWords := EstimateUniqueWordsIter(reader, memorySize)
|
||||
if estimatedUniqueWords != expectedUniqueWords {
|
||||
// ...
|
||||
}
|
||||
}
|
133
test/fixedbugs/issue69507.go
Normal file
133
test/fixedbugs/issue69507.go
Normal file
@ -0,0 +1,133 @@
|
||||
// run
|
||||
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
methods := "AB"
|
||||
|
||||
type node struct {
|
||||
tag string
|
||||
choices []string
|
||||
}
|
||||
all := []node{
|
||||
{"000", permutations(methods)},
|
||||
}
|
||||
|
||||
next := 1
|
||||
for len(all) > 0 {
|
||||
cur := all[0]
|
||||
k := copy(all, all[1:])
|
||||
all = all[:k]
|
||||
|
||||
if len(cur.choices) == 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
var bestM map[byte][]string
|
||||
bMax := len(cur.choices) + 1
|
||||
bMin := -1
|
||||
for sel := range selections(methods) {
|
||||
m := make(map[byte][]string)
|
||||
for _, order := range cur.choices {
|
||||
x := findFirstMatch(order, sel)
|
||||
m[x] = append(m[x], order)
|
||||
}
|
||||
|
||||
min := len(cur.choices) + 1
|
||||
max := -1
|
||||
for _, v := range m {
|
||||
if len(v) < min {
|
||||
min = len(v)
|
||||
}
|
||||
if len(v) > max {
|
||||
max = len(v)
|
||||
}
|
||||
}
|
||||
if max < bMax || (max == bMax && min > bMin) {
|
||||
bestM = m
|
||||
bMin = min
|
||||
bMax = max
|
||||
}
|
||||
}
|
||||
|
||||
if bMax == len(cur.choices) {
|
||||
continue
|
||||
}
|
||||
|
||||
cc := Keys(bestM)
|
||||
for c := range cc {
|
||||
choices := bestM[c]
|
||||
next++
|
||||
|
||||
switch c {
|
||||
case 'A':
|
||||
case 'B':
|
||||
default:
|
||||
panic("unexpected selector type " + string(c))
|
||||
}
|
||||
all = append(all, node{"", choices})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func permutations(s string) []string {
|
||||
if len(s) <= 1 {
|
||||
return []string{s}
|
||||
}
|
||||
|
||||
var result []string
|
||||
for i, char := range s {
|
||||
rest := s[:i] + s[i+1:]
|
||||
for _, perm := range permutations(rest) {
|
||||
result = append(result, string(char)+perm)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type Seq[V any] func(yield func(V) bool)
|
||||
|
||||
func selections(s string) Seq[string] {
|
||||
return func(yield func(string) bool) {
|
||||
for bits := 1; bits < 1<<len(s); bits++ {
|
||||
var choice string
|
||||
for j, char := range s {
|
||||
if bits&(1<<j) != 0 {
|
||||
choice += string(char)
|
||||
}
|
||||
}
|
||||
if !yield(choice) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findFirstMatch(order, sel string) byte {
|
||||
for _, c := range order {
|
||||
return byte(c)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func Keys[Map ~map[K]V, K comparable, V any](m Map) Seq[K] {
|
||||
return func(yield func(K) bool) {
|
||||
for k := range m {
|
||||
if !yield(k) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user