mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
[dev.inline] merge with master at 894d24d617
Change-Id: I845eec08108c69228ebcba921f8a807a376d3fae
This commit is contained in:
commit
71aaa8bde1
@ -174,6 +174,7 @@ pkg go/build, type Package struct, Directives []Directive #56986
|
||||
pkg go/build, type Package struct, TestDirectives []Directive #56986
|
||||
pkg go/build, type Package struct, XTestDirectives []Directive #56986
|
||||
pkg go/token, method (*File) Lines() []int #57708
|
||||
pkg go/types, method (*Package) GoVersion() string #61175
|
||||
pkg html/template, const ErrJSTemplate = 12 #59584
|
||||
pkg html/template, const ErrJSTemplate ErrorCode #59584
|
||||
pkg io/fs, func FormatDirEntry(DirEntry) string #54451
|
||||
|
@ -65,7 +65,7 @@ func (s *Slice) Col() int {
|
||||
// #define A #define B(x) x
|
||||
// and
|
||||
// #define A #define B (x) x
|
||||
// The first has definition of B has an argument, the second doesn't. Because we let
|
||||
// The first definition of B has an argument, the second doesn't. Because we let
|
||||
// text/scanner strip the blanks for us, this is extremely rare, hard to fix, and not worth it.
|
||||
return s.pos
|
||||
}
|
||||
|
@ -836,11 +836,11 @@
|
||||
//
|
||||
// Key:
|
||||
//
|
||||
// [+ -](x * y) [+ -] z.
|
||||
// [+ -](x * y [+ -] z).
|
||||
// _ N A S
|
||||
// D U
|
||||
// D B
|
||||
//
|
||||
// Note: multiplication commutativity handled by rule generator.
|
||||
(F(MADD|NMADD|MSUB|NMSUB)D neg:(FNEGD x) y z) && neg.Uses == 1 => (F(NMADD|MADD|NMSUB|MSUB)D x y z)
|
||||
(F(MADD|NMADD|MSUB|NMSUB)D neg:(FNEGD x) y z) && neg.Uses == 1 => (F(NMSUB|MSUB|NMADD|MADD)D x y z)
|
||||
(F(MADD|NMADD|MSUB|NMSUB)D x y neg:(FNEGD z)) && neg.Uses == 1 => (F(MSUB|NMSUB|MADD|NMADD)D x y z)
|
||||
|
@ -3322,7 +3322,7 @@ func rewriteValueRISCV64_OpRISCV64FMADDD(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
// match: (FMADDD neg:(FNEGD x) y z)
|
||||
// cond: neg.Uses == 1
|
||||
// result: (FNMADDD x y z)
|
||||
// result: (FNMSUBD x y z)
|
||||
for {
|
||||
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
||||
neg := v_0
|
||||
@ -3335,7 +3335,7 @@ func rewriteValueRISCV64_OpRISCV64FMADDD(v *Value) bool {
|
||||
if !(neg.Uses == 1) {
|
||||
continue
|
||||
}
|
||||
v.reset(OpRISCV64FNMADDD)
|
||||
v.reset(OpRISCV64FNMSUBD)
|
||||
v.AddArg3(x, y, z)
|
||||
return true
|
||||
}
|
||||
@ -3367,7 +3367,7 @@ func rewriteValueRISCV64_OpRISCV64FMSUBD(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
// match: (FMSUBD neg:(FNEGD x) y z)
|
||||
// cond: neg.Uses == 1
|
||||
// result: (FNMSUBD x y z)
|
||||
// result: (FNMADDD x y z)
|
||||
for {
|
||||
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
||||
neg := v_0
|
||||
@ -3380,7 +3380,7 @@ func rewriteValueRISCV64_OpRISCV64FMSUBD(v *Value) bool {
|
||||
if !(neg.Uses == 1) {
|
||||
continue
|
||||
}
|
||||
v.reset(OpRISCV64FNMSUBD)
|
||||
v.reset(OpRISCV64FNMADDD)
|
||||
v.AddArg3(x, y, z)
|
||||
return true
|
||||
}
|
||||
@ -3412,7 +3412,7 @@ func rewriteValueRISCV64_OpRISCV64FNMADDD(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
// match: (FNMADDD neg:(FNEGD x) y z)
|
||||
// cond: neg.Uses == 1
|
||||
// result: (FMADDD x y z)
|
||||
// result: (FMSUBD x y z)
|
||||
for {
|
||||
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
||||
neg := v_0
|
||||
@ -3425,7 +3425,7 @@ func rewriteValueRISCV64_OpRISCV64FNMADDD(v *Value) bool {
|
||||
if !(neg.Uses == 1) {
|
||||
continue
|
||||
}
|
||||
v.reset(OpRISCV64FMADDD)
|
||||
v.reset(OpRISCV64FMSUBD)
|
||||
v.AddArg3(x, y, z)
|
||||
return true
|
||||
}
|
||||
@ -3457,7 +3457,7 @@ func rewriteValueRISCV64_OpRISCV64FNMSUBD(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
// match: (FNMSUBD neg:(FNEGD x) y z)
|
||||
// cond: neg.Uses == 1
|
||||
// result: (FMSUBD x y z)
|
||||
// result: (FMADDD x y z)
|
||||
for {
|
||||
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
||||
neg := v_0
|
||||
@ -3470,7 +3470,7 @@ func rewriteValueRISCV64_OpRISCV64FNMSUBD(v *Value) bool {
|
||||
if !(neg.Uses == 1) {
|
||||
continue
|
||||
}
|
||||
v.reset(OpRISCV64FMSUBD)
|
||||
v.reset(OpRISCV64FMADDD)
|
||||
v.AddArg3(x, y, z)
|
||||
return true
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"internal/goversion"
|
||||
. "internal/types/errors"
|
||||
)
|
||||
|
||||
@ -231,17 +232,17 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
|
||||
info = new(Info)
|
||||
}
|
||||
|
||||
version, err := parseGoVersion(conf.GoVersion)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err))
|
||||
}
|
||||
// Note: clients may call NewChecker with the Unsafe package, which is
|
||||
// globally shared and must not be mutated. Therefore NewChecker must not
|
||||
// mutate *pkg.
|
||||
//
|
||||
// (previously, pkg.goVersion was mutated here: go.dev/issue/61212)
|
||||
|
||||
return &Checker{
|
||||
conf: conf,
|
||||
ctxt: conf.Context,
|
||||
pkg: pkg,
|
||||
Info: info,
|
||||
version: version,
|
||||
objMap: make(map[Object]*declInfo),
|
||||
impMap: make(map[importKey]*Package),
|
||||
}
|
||||
@ -333,6 +334,20 @@ func (check *Checker) Files(files []*syntax.File) error { return check.checkFile
|
||||
var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")
|
||||
|
||||
func (check *Checker) checkFiles(files []*syntax.File) (err error) {
|
||||
if check.pkg == Unsafe {
|
||||
// Defensive handling for Unsafe, which cannot be type checked, and must
|
||||
// not be mutated. See https://go.dev/issue/61212 for an example of where
|
||||
// Unsafe is passed to NewChecker.
|
||||
return nil
|
||||
}
|
||||
|
||||
check.version, err = parseGoVersion(check.conf.GoVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if check.version.after(version{1, goversion.Version}) {
|
||||
return fmt.Errorf("package requires newer Go version %v", check.version)
|
||||
}
|
||||
if check.conf.FakeImportC && check.conf.go115UsesCgo {
|
||||
return errBadCgo
|
||||
}
|
||||
@ -377,6 +392,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
|
||||
check.monomorph()
|
||||
}
|
||||
|
||||
check.pkg.goVersion = check.conf.GoVersion
|
||||
check.pkg.complete = true
|
||||
|
||||
// no longer needed - release memory
|
||||
|
@ -497,14 +497,14 @@ func TestIssue43088(t *testing.T) {
|
||||
// _ T2
|
||||
// }
|
||||
// }
|
||||
n1 := NewTypeName(syntax.Pos{}, nil, "T1", nil)
|
||||
n1 := NewTypeName(nopos, nil, "T1", nil)
|
||||
T1 := NewNamed(n1, nil, nil)
|
||||
n2 := NewTypeName(syntax.Pos{}, nil, "T2", nil)
|
||||
n2 := NewTypeName(nopos, nil, "T2", nil)
|
||||
T2 := NewNamed(n2, nil, nil)
|
||||
s1 := NewStruct([]*Var{NewField(syntax.Pos{}, nil, "_", T2, false)}, nil)
|
||||
s1 := NewStruct([]*Var{NewField(nopos, nil, "_", T2, false)}, nil)
|
||||
T1.SetUnderlying(s1)
|
||||
s2 := NewStruct([]*Var{NewField(syntax.Pos{}, nil, "_", T2, false)}, nil)
|
||||
s3 := NewStruct([]*Var{NewField(syntax.Pos{}, nil, "_", s2, false)}, nil)
|
||||
s2 := NewStruct([]*Var{NewField(nopos, nil, "_", T2, false)}, nil)
|
||||
s3 := NewStruct([]*Var{NewField(nopos, nil, "_", s2, false)}, nil)
|
||||
T2.SetUnderlying(s3)
|
||||
|
||||
// These calls must terminate (no endless recursion).
|
||||
@ -535,38 +535,69 @@ func TestIssue44515(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssue43124(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
// TODO(rFindley) move this to testdata by enhancing support for importing.
|
||||
|
||||
testenv.MustHaveGoBuild(t) // The go command is needed for the importer to determine the locations of stdlib .a files.
|
||||
|
||||
// All involved packages have the same name (template). Error messages should
|
||||
// disambiguate between text/template and html/template by printing the full
|
||||
// path.
|
||||
const (
|
||||
asrc = `package a; import "text/template"; func F(template.Template) {}; func G(int) {}`
|
||||
bsrc = `package b; import ("a"; "html/template"); func _() { a.F(template.Template{}) }`
|
||||
csrc = `package c; import ("a"; "html/template"); func _() { a.G(template.Template{}) }`
|
||||
bsrc = `
|
||||
package b
|
||||
|
||||
import (
|
||||
"a"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
func _() {
|
||||
// Packages should be fully qualified when there is ambiguity within the
|
||||
// error string itself.
|
||||
a.F(template /* ERRORx "cannot use.*html/template.* as .*text/template" */ .Template{})
|
||||
}
|
||||
`
|
||||
csrc = `
|
||||
package c
|
||||
|
||||
import (
|
||||
"a"
|
||||
"fmt"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
// go.dev/issue/46905: make sure template is not the first package qualified.
|
||||
var _ fmt.Stringer = 1 // ERRORx "cannot use 1.*as fmt\\.Stringer"
|
||||
|
||||
// Packages should be fully qualified when there is ambiguity in reachable
|
||||
// packages. In this case both a (and for that matter html/template) import
|
||||
// text/template.
|
||||
func _() { a.G(template /* ERRORx "cannot use .*html/template.*Template" */ .Template{}) }
|
||||
`
|
||||
|
||||
tsrc = `
|
||||
package template
|
||||
|
||||
import "text/template"
|
||||
|
||||
type T int
|
||||
|
||||
// Verify that the current package name also causes disambiguation.
|
||||
var _ T = template /* ERRORx "cannot use.*text/template.* as T value" */.Template{}
|
||||
`
|
||||
)
|
||||
|
||||
a := mustTypecheck(asrc, nil, nil)
|
||||
conf := Config{Importer: importHelper{pkg: a, fallback: defaultImporter()}}
|
||||
imp := importHelper{pkg: a, fallback: defaultImporter()}
|
||||
|
||||
// Packages should be fully qualified when there is ambiguity within the
|
||||
// error string itself.
|
||||
_, err := typecheck(bsrc, &conf, nil)
|
||||
if err == nil {
|
||||
t.Fatal("package b had no errors")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "text/template") || !strings.Contains(err.Error(), "html/template") {
|
||||
t.Errorf("type checking error for b does not disambiguate package template: %q", err)
|
||||
withImporter := func(cfg *Config) {
|
||||
cfg.Importer = imp
|
||||
}
|
||||
|
||||
// ...and also when there is any ambiguity in reachable packages.
|
||||
_, err = typecheck(csrc, &conf, nil)
|
||||
if err == nil {
|
||||
t.Fatal("package c had no errors")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "html/template") {
|
||||
t.Errorf("type checking error for c does not disambiguate package template: %q", err)
|
||||
}
|
||||
testFiles(t, []string{"b.go"}, [][]byte{[]byte(bsrc)}, 0, false, withImporter)
|
||||
testFiles(t, []string{"c.go"}, [][]byte{[]byte(csrc)}, 0, false, withImporter)
|
||||
testFiles(t, []string{"t.go"}, [][]byte{[]byte(tsrc)}, 0, false, withImporter)
|
||||
}
|
||||
|
||||
func TestIssue50646(t *testing.T) {
|
||||
|
@ -17,6 +17,7 @@ type Package struct {
|
||||
complete bool
|
||||
fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
|
||||
cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go
|
||||
goVersion string // minimum Go version required for package (by Config.GoVersion, typically from go.mod)
|
||||
}
|
||||
|
||||
// NewPackage returns a new Package for the given package path and name.
|
||||
@ -35,6 +36,12 @@ func (pkg *Package) Name() string { return pkg.name }
|
||||
// SetName sets the package name.
|
||||
func (pkg *Package) SetName(name string) { pkg.name = name }
|
||||
|
||||
// GoVersion returns the minimum Go version required by this package.
|
||||
// If the minimum version is unknown, GoVersion returns the empty string.
|
||||
// Individual source files may specify a different minimum Go version,
|
||||
// as reported in the [go/ast.File.GoVersion] field.
|
||||
func (pkg *Package) GoVersion() string { return pkg.goVersion }
|
||||
|
||||
// Scope returns the (complete or incomplete) package scope
|
||||
// holding the objects declared at package level (TypeNames,
|
||||
// Consts, Vars, and Funcs).
|
||||
|
@ -47,7 +47,7 @@ func TestSizeof(t *testing.T) {
|
||||
|
||||
// Misc
|
||||
{Scope{}, 60, 104},
|
||||
{Package{}, 36, 72},
|
||||
{Package{}, 44, 88},
|
||||
{_TypeSet{}, 28, 56},
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@ package types2
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/syntax"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
@ -44,23 +43,24 @@ var (
|
||||
go1_21 = version{1, 21}
|
||||
)
|
||||
|
||||
var errVersionSyntax = errors.New("invalid Go version syntax")
|
||||
|
||||
// parseGoVersion parses a Go version string (such as "go1.12")
|
||||
// and returns the version, or an error. If s is the empty
|
||||
// string, the version is 0.0.
|
||||
func parseGoVersion(s string) (v version, err error) {
|
||||
bad := func() (version, error) {
|
||||
return version{}, fmt.Errorf("invalid Go version syntax %q", s)
|
||||
}
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
if !strings.HasPrefix(s, "go") {
|
||||
return version{}, errVersionSyntax
|
||||
return bad()
|
||||
}
|
||||
s = s[len("go"):]
|
||||
i := 0
|
||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
if i >= 10 || i == 0 && s[i] == '0' {
|
||||
return version{}, errVersionSyntax
|
||||
return bad()
|
||||
}
|
||||
v.major = 10*v.major + int(s[i]) - '0'
|
||||
}
|
||||
@ -68,7 +68,7 @@ func parseGoVersion(s string) (v version, err error) {
|
||||
return
|
||||
}
|
||||
if i == 0 || s[i] != '.' {
|
||||
return version{}, errVersionSyntax
|
||||
return bad()
|
||||
}
|
||||
s = s[i+1:]
|
||||
if s == "0" {
|
||||
@ -81,14 +81,15 @@ func parseGoVersion(s string) (v version, err error) {
|
||||
i = 0
|
||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
if i >= 10 || i == 0 && s[i] == '0' {
|
||||
return version{}, errVersionSyntax
|
||||
return bad()
|
||||
}
|
||||
v.minor = 10*v.minor + int(s[i]) - '0'
|
||||
}
|
||||
if i > 0 && i == len(s) {
|
||||
// Accept any suffix after the minor number.
|
||||
// We are only looking for the language version (major.minor)
|
||||
// but want to accept any valid Go version, like go1.21.0
|
||||
// and go1.21rc2.
|
||||
return
|
||||
}
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
|
||||
// langCompat reports an error if the representation of a numeric
|
||||
|
24
src/cmd/compile/internal/types2/version_test.go
Normal file
24
src/cmd/compile/internal/types2/version_test.go
Normal file
@ -0,0 +1,24 @@
|
||||
// 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.
|
||||
|
||||
package types2
|
||||
|
||||
import "testing"
|
||||
|
||||
var parseGoVersionTests = []struct {
|
||||
in string
|
||||
out version
|
||||
}{
|
||||
{"go1.21", version{1, 21}},
|
||||
{"go1.21.0", version{1, 21}},
|
||||
{"go1.21rc2", version{1, 21}},
|
||||
}
|
||||
|
||||
func TestParseGoVersion(t *testing.T) {
|
||||
for _, tt := range parseGoVersionTests {
|
||||
if out, err := parseGoVersion(tt.in); out != tt.out || err != nil {
|
||||
t.Errorf("parseGoVersion(%q) = %v, %v, want %v, nil", tt.in, out, err, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
@ -1115,6 +1115,7 @@ type vetConfig struct {
|
||||
PackageVetx map[string]string // map package path to vetx data from earlier vet run
|
||||
VetxOnly bool // only compute vetx data; don't report detected problems
|
||||
VetxOutput string // write vetx data to this output file
|
||||
GoVersion string // Go version for package
|
||||
|
||||
SucceedOnTypecheckFailure bool // awful hack; see #18395 and below
|
||||
}
|
||||
@ -1149,6 +1150,13 @@ func buildVetConfig(a *Action, srcfiles []string) {
|
||||
PackageFile: make(map[string]string),
|
||||
Standard: make(map[string]bool),
|
||||
}
|
||||
if a.Package.Module != nil {
|
||||
v := a.Package.Module.GoVersion
|
||||
if v == "" {
|
||||
v = gover.DefaultGoModVersion
|
||||
}
|
||||
vcfg.GoVersion = "go" + v
|
||||
}
|
||||
a.vetCfg = vcfg
|
||||
for i, raw := range a.Package.Internal.RawImports {
|
||||
final := a.Package.Imports[i]
|
||||
|
@ -85,19 +85,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
|
||||
if p.Module != nil {
|
||||
v := p.Module.GoVersion
|
||||
if v == "" {
|
||||
// We started adding a 'go' directive to the go.mod file unconditionally
|
||||
// as of Go 1.12, so any module that still lacks such a directive must
|
||||
// either have been authored before then, or have a hand-edited go.mod
|
||||
// file that hasn't been updated by cmd/go since that edit.
|
||||
//
|
||||
// Unfortunately, through at least Go 1.16 we didn't add versions to
|
||||
// vendor/modules.txt. So this could also be a vendored 1.16 dependency.
|
||||
//
|
||||
// Fortunately, there were no breaking changes to the language between Go
|
||||
// 1.11 and 1.16, so if we assume Go 1.16 semantics we will not introduce
|
||||
// any spurious errors — we will only mask errors, and not particularly
|
||||
// important ones at that.
|
||||
v = "1.16"
|
||||
v = gover.DefaultGoModVersion
|
||||
}
|
||||
if allowedVersion(v) {
|
||||
defaultGcFlags = append(defaultGcFlags, "-lang=go"+gover.Lang(v))
|
||||
|
@ -2944,15 +2944,17 @@ func (rs *Rows) initContextClose(ctx, txctx context.Context) {
|
||||
if bypassRowsAwaitDone {
|
||||
return
|
||||
}
|
||||
ctx, rs.cancel = context.WithCancel(ctx)
|
||||
go rs.awaitDone(ctx, txctx)
|
||||
closectx, cancel := context.WithCancel(ctx)
|
||||
rs.cancel = cancel
|
||||
go rs.awaitDone(ctx, txctx, closectx)
|
||||
}
|
||||
|
||||
// awaitDone blocks until either ctx or txctx is canceled. The ctx is provided
|
||||
// from the query context and is canceled when the query Rows is closed.
|
||||
// awaitDone blocks until ctx, txctx, or closectx is canceled.
|
||||
// The ctx is provided from the query context.
|
||||
// If the query was issued in a transaction, the transaction's context
|
||||
// is also provided in txctx to ensure Rows is closed if the Tx is closed.
|
||||
func (rs *Rows) awaitDone(ctx, txctx context.Context) {
|
||||
// is also provided in txctx, to ensure Rows is closed if the Tx is closed.
|
||||
// The closectx is closed by an explicit call to rs.Close.
|
||||
func (rs *Rows) awaitDone(ctx, txctx, closectx context.Context) {
|
||||
var txctxDone <-chan struct{}
|
||||
if txctx != nil {
|
||||
txctxDone = txctx.Done()
|
||||
@ -2964,6 +2966,9 @@ func (rs *Rows) awaitDone(ctx, txctx context.Context) {
|
||||
case <-txctxDone:
|
||||
err := txctx.Err()
|
||||
rs.contextDone.Store(&err)
|
||||
case <-closectx.Done():
|
||||
// rs.cancel was called via Close(); don't store this into contextDone
|
||||
// to ensure Err() is unaffected.
|
||||
}
|
||||
rs.close(ctx.Err())
|
||||
}
|
||||
|
@ -4493,6 +4493,31 @@ func TestContextCancelBetweenNextAndErr(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNilErrorAfterClose(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
|
||||
// This WithCancel is important; Rows contains an optimization to avoid
|
||||
// spawning a goroutine when the query/transaction context cannot be
|
||||
// canceled, but this test tests a bug which is caused by said goroutine.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
r, err := db.QueryContext(ctx, "SELECT|people|name|")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := r.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond) // increase odds of seeing failure
|
||||
if err := r.Err(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// badConn implements a bad driver.Conn, for TestBadDriver.
|
||||
// The Exec method panics.
|
||||
type badConn struct{}
|
||||
|
@ -286,7 +286,7 @@ var depsRules = `
|
||||
math/big, go/token
|
||||
< go/constant;
|
||||
|
||||
container/heap, go/constant, go/parser, internal/types/errors
|
||||
container/heap, go/constant, go/parser, internal/goversion, internal/types/errors
|
||||
< go/types;
|
||||
|
||||
# The vast majority of standard library packages should not be resorting to regexp.
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"internal/goversion"
|
||||
. "internal/types/errors"
|
||||
)
|
||||
|
||||
@ -233,10 +234,11 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
|
||||
info = new(Info)
|
||||
}
|
||||
|
||||
version, err := parseGoVersion(conf.GoVersion)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err))
|
||||
}
|
||||
// Note: clients may call NewChecker with the Unsafe package, which is
|
||||
// globally shared and must not be mutated. Therefore NewChecker must not
|
||||
// mutate *pkg.
|
||||
//
|
||||
// (previously, pkg.goVersion was mutated here: go.dev/issue/61212)
|
||||
|
||||
return &Checker{
|
||||
conf: conf,
|
||||
@ -244,7 +246,6 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
|
||||
fset: fset,
|
||||
pkg: pkg,
|
||||
Info: info,
|
||||
version: version,
|
||||
objMap: make(map[Object]*declInfo),
|
||||
impMap: make(map[importKey]*Package),
|
||||
}
|
||||
@ -342,6 +343,20 @@ func (check *Checker) Files(files []*ast.File) error { return check.checkFiles(f
|
||||
var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")
|
||||
|
||||
func (check *Checker) checkFiles(files []*ast.File) (err error) {
|
||||
if check.pkg == Unsafe {
|
||||
// Defensive handling for Unsafe, which cannot be type checked, and must
|
||||
// not be mutated. See https://go.dev/issue/61212 for an example of where
|
||||
// Unsafe is passed to NewChecker.
|
||||
return nil
|
||||
}
|
||||
|
||||
check.version, err = parseGoVersion(check.conf.GoVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if check.version.after(version{1, goversion.Version}) {
|
||||
return fmt.Errorf("package requires newer Go version %v", check.version)
|
||||
}
|
||||
if check.conf.FakeImportC && check.conf.go115UsesCgo {
|
||||
return errBadCgo
|
||||
}
|
||||
@ -386,6 +401,7 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
|
||||
check.monomorph()
|
||||
}
|
||||
|
||||
check.pkg.goVersion = check.conf.GoVersion
|
||||
check.pkg.complete = true
|
||||
|
||||
// no longer needed - release memory
|
||||
|
@ -141,6 +141,7 @@ var filemap = map[string]action{
|
||||
"universe.go": fixGlobalTypVarDecl,
|
||||
"util_test.go": fixTokenPos,
|
||||
"validtype.go": nil,
|
||||
"version_test.go": nil,
|
||||
}
|
||||
|
||||
// TODO(gri) We should be able to make these rewriters more configurable/composable.
|
||||
|
@ -19,6 +19,7 @@ type Package struct {
|
||||
complete bool
|
||||
fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
|
||||
cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go
|
||||
goVersion string // minimum Go version required for package (by Config.GoVersion, typically from go.mod)
|
||||
}
|
||||
|
||||
// NewPackage returns a new Package for the given package path and name.
|
||||
@ -37,6 +38,12 @@ func (pkg *Package) Name() string { return pkg.name }
|
||||
// SetName sets the package name.
|
||||
func (pkg *Package) SetName(name string) { pkg.name = name }
|
||||
|
||||
// GoVersion returns the minimum Go version required by this package.
|
||||
// If the minimum version is unknown, GoVersion returns the empty string.
|
||||
// Individual source files may specify a different minimum Go version,
|
||||
// as reported in the [go/ast.File.GoVersion] field.
|
||||
func (pkg *Package) GoVersion() string { return pkg.goVersion }
|
||||
|
||||
// Scope returns the (complete or incomplete) package scope
|
||||
// holding the objects declared at package level (TypeNames,
|
||||
// Consts, Vars, and Funcs).
|
||||
|
@ -46,7 +46,7 @@ func TestSizeof(t *testing.T) {
|
||||
|
||||
// Misc
|
||||
{Scope{}, 44, 88},
|
||||
{Package{}, 36, 72},
|
||||
{Package{}, 44, 88},
|
||||
{_TypeSet{}, 28, 56},
|
||||
}
|
||||
for _, test := range tests {
|
||||
|
@ -5,7 +5,6 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
@ -45,23 +44,24 @@ var (
|
||||
go1_21 = version{1, 21}
|
||||
)
|
||||
|
||||
var errVersionSyntax = errors.New("invalid Go version syntax")
|
||||
|
||||
// parseGoVersion parses a Go version string (such as "go1.12")
|
||||
// and returns the version, or an error. If s is the empty
|
||||
// string, the version is 0.0.
|
||||
func parseGoVersion(s string) (v version, err error) {
|
||||
bad := func() (version, error) {
|
||||
return version{}, fmt.Errorf("invalid Go version syntax %q", s)
|
||||
}
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
if !strings.HasPrefix(s, "go") {
|
||||
return version{}, errVersionSyntax
|
||||
return bad()
|
||||
}
|
||||
s = s[len("go"):]
|
||||
i := 0
|
||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
if i >= 10 || i == 0 && s[i] == '0' {
|
||||
return version{}, errVersionSyntax
|
||||
return bad()
|
||||
}
|
||||
v.major = 10*v.major + int(s[i]) - '0'
|
||||
}
|
||||
@ -69,7 +69,7 @@ func parseGoVersion(s string) (v version, err error) {
|
||||
return
|
||||
}
|
||||
if i == 0 || s[i] != '.' {
|
||||
return version{}, errVersionSyntax
|
||||
return bad()
|
||||
}
|
||||
s = s[i+1:]
|
||||
if s == "0" {
|
||||
@ -82,14 +82,15 @@ func parseGoVersion(s string) (v version, err error) {
|
||||
i = 0
|
||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
if i >= 10 || i == 0 && s[i] == '0' {
|
||||
return version{}, errVersionSyntax
|
||||
return bad()
|
||||
}
|
||||
v.minor = 10*v.minor + int(s[i]) - '0'
|
||||
}
|
||||
if i > 0 && i == len(s) {
|
||||
// Accept any suffix after the minor number.
|
||||
// We are only looking for the language version (major.minor)
|
||||
// but want to accept any valid Go version, like go1.21.0
|
||||
// and go1.21rc2.
|
||||
return
|
||||
}
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
|
||||
// langCompat reports an error if the representation of a numeric
|
||||
|
26
src/go/types/version_test.go
Normal file
26
src/go/types/version_test.go
Normal file
@ -0,0 +1,26 @@
|
||||
// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
|
||||
|
||||
// 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.
|
||||
|
||||
package types
|
||||
|
||||
import "testing"
|
||||
|
||||
var parseGoVersionTests = []struct {
|
||||
in string
|
||||
out version
|
||||
}{
|
||||
{"go1.21", version{1, 21}},
|
||||
{"go1.21.0", version{1, 21}},
|
||||
{"go1.21rc2", version{1, 21}},
|
||||
}
|
||||
|
||||
func TestParseGoVersion(t *testing.T) {
|
||||
for _, tt := range parseGoVersionTests {
|
||||
if out, err := parseGoVersion(tt.in); out != tt.out || err != nil {
|
||||
t.Errorf("parseGoVersion(%q) = %v, %v, want %v, nil", tt.in, out, err, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
@ -221,6 +221,11 @@ func (h *commonHandler) enabled(l Level) bool {
|
||||
}
|
||||
|
||||
func (h *commonHandler) withAttrs(as []Attr) *commonHandler {
|
||||
// We are going to ignore empty groups, so if the entire slice consists of
|
||||
// them, there is nothing to do.
|
||||
if countEmptyGroups(as) == len(as) {
|
||||
return h
|
||||
}
|
||||
h2 := h.clone()
|
||||
// Pre-format the attributes as an optimization.
|
||||
state := h2.newHandleState((*buffer.Buffer)(&h2.preformattedAttrs), false, "")
|
||||
@ -308,15 +313,20 @@ func (s *handleState) appendNonBuiltIns(r Record) {
|
||||
}
|
||||
// Attrs in Record -- unlike the built-in ones, they are in groups started
|
||||
// from WithGroup.
|
||||
// If the record has no Attrs, don't output any groups.
|
||||
nOpenGroups := s.h.nOpenGroups
|
||||
if r.NumAttrs() > 0 {
|
||||
s.prefix.WriteString(s.h.groupPrefix)
|
||||
s.openGroups()
|
||||
nOpenGroups = len(s.h.groups)
|
||||
r.Attrs(func(a Attr) bool {
|
||||
s.appendAttr(a)
|
||||
return true
|
||||
})
|
||||
}
|
||||
if s.h.json {
|
||||
// Close all open groups.
|
||||
for range s.h.groups {
|
||||
for range s.h.groups[:nOpenGroups] {
|
||||
s.buf.WriteByte('}')
|
||||
}
|
||||
// Close the top-level object.
|
||||
|
@ -214,6 +214,28 @@ func TestJSONAndTextHandlers(t *testing.T) {
|
||||
wantText: "msg=message h.a=1",
|
||||
wantJSON: `{"msg":"message","h":{"a":1}}`,
|
||||
},
|
||||
{
|
||||
name: "nested empty group",
|
||||
replace: removeKeys(TimeKey, LevelKey),
|
||||
attrs: []Attr{
|
||||
Group("g",
|
||||
Group("h",
|
||||
Group("i"), Group("j"))),
|
||||
},
|
||||
wantText: `msg=message`,
|
||||
wantJSON: `{"msg":"message"}`,
|
||||
},
|
||||
{
|
||||
name: "nested non-empty group",
|
||||
replace: removeKeys(TimeKey, LevelKey),
|
||||
attrs: []Attr{
|
||||
Group("g",
|
||||
Group("h",
|
||||
Group("i"), Group("j", Int("a", 1)))),
|
||||
},
|
||||
wantText: `msg=message g.h.j.a=1`,
|
||||
wantJSON: `{"msg":"message","g":{"h":{"j":{"a":1}}}}`,
|
||||
},
|
||||
{
|
||||
name: "escapes",
|
||||
replace: removeKeys(TimeKey, LevelKey),
|
||||
@ -281,6 +303,34 @@ func TestJSONAndTextHandlers(t *testing.T) {
|
||||
wantText: "msg=message p1=1 s1.s2.a=one s1.s2.b=2",
|
||||
wantJSON: `{"msg":"message","p1":1,"s1":{"s2":{"a":"one","b":2}}}`,
|
||||
},
|
||||
{
|
||||
name: "empty with-groups",
|
||||
replace: removeKeys(TimeKey, LevelKey),
|
||||
with: func(h Handler) Handler {
|
||||
return h.WithGroup("x").WithGroup("y")
|
||||
},
|
||||
wantText: "msg=message",
|
||||
wantJSON: `{"msg":"message"}`,
|
||||
},
|
||||
{
|
||||
name: "empty with-groups, no non-empty attrs",
|
||||
replace: removeKeys(TimeKey, LevelKey),
|
||||
with: func(h Handler) Handler {
|
||||
return h.WithGroup("x").WithAttrs([]Attr{Group("g")}).WithGroup("y")
|
||||
},
|
||||
wantText: "msg=message",
|
||||
wantJSON: `{"msg":"message"}`,
|
||||
},
|
||||
{
|
||||
name: "one empty with-group",
|
||||
replace: removeKeys(TimeKey, LevelKey),
|
||||
with: func(h Handler) Handler {
|
||||
return h.WithGroup("x").WithAttrs([]Attr{Int("a", 1)}).WithGroup("y")
|
||||
},
|
||||
attrs: []Attr{Group("g", Group("h"))},
|
||||
wantText: "msg=message x.a=1",
|
||||
wantJSON: `{"msg":"message","x":{"a":1}}`,
|
||||
},
|
||||
{
|
||||
name: "GroupValue as Attr value",
|
||||
replace: removeKeys(TimeKey, LevelKey),
|
||||
|
@ -106,7 +106,7 @@ func TestConnections(t *testing.T) {
|
||||
// log.Logger's output goes through the handler.
|
||||
SetDefault(New(NewTextHandler(&slogbuf, &HandlerOptions{AddSource: true})))
|
||||
log.Print("msg2")
|
||||
checkLogOutput(t, slogbuf.String(), "time="+timeRE+` level=INFO source=.*logger_test.go:\d{3} msg=msg2`)
|
||||
checkLogOutput(t, slogbuf.String(), "time="+timeRE+` level=INFO source=.*logger_test.go:\d{3}"? msg=msg2`)
|
||||
|
||||
// The default log.Logger always outputs at Info level.
|
||||
slogbuf.Reset()
|
||||
|
@ -93,9 +93,17 @@ func (r Record) Attrs(f func(Attr) bool) {
|
||||
}
|
||||
|
||||
// AddAttrs appends the given Attrs to the Record's list of Attrs.
|
||||
// It omits empty groups.
|
||||
func (r *Record) AddAttrs(attrs ...Attr) {
|
||||
n := copy(r.front[r.nFront:], attrs)
|
||||
r.nFront += n
|
||||
var i int
|
||||
for i = 0; i < len(attrs) && r.nFront < len(r.front); i++ {
|
||||
a := attrs[i]
|
||||
if a.Value.isEmptyGroup() {
|
||||
continue
|
||||
}
|
||||
r.front[r.nFront] = a
|
||||
r.nFront++
|
||||
}
|
||||
// Check if a copy was modified by slicing past the end
|
||||
// and seeing if the Attr there is non-zero.
|
||||
if cap(r.back) > len(r.back) {
|
||||
@ -104,15 +112,25 @@ func (r *Record) AddAttrs(attrs ...Attr) {
|
||||
panic("copies of a slog.Record were both modified")
|
||||
}
|
||||
}
|
||||
r.back = append(r.back, attrs[n:]...)
|
||||
ne := countEmptyGroups(attrs[i:])
|
||||
r.back = slices.Grow(r.back, len(attrs[i:])-ne)
|
||||
for _, a := range attrs[i:] {
|
||||
if !a.Value.isEmptyGroup() {
|
||||
r.back = append(r.back, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add converts the args to Attrs as described in [Logger.Log],
|
||||
// then appends the Attrs to the Record's list of Attrs.
|
||||
// It omits empty groups.
|
||||
func (r *Record) Add(args ...any) {
|
||||
var a Attr
|
||||
for len(args) > 0 {
|
||||
a, args = argsToAttr(args)
|
||||
if a.Value.isEmptyGroup() {
|
||||
continue
|
||||
}
|
||||
if r.nFront < len(r.front) {
|
||||
r.front[r.nFront] = a
|
||||
r.nFront++
|
||||
|
@ -164,9 +164,32 @@ func DurationValue(v time.Duration) Value {
|
||||
// GroupValue returns a new Value for a list of Attrs.
|
||||
// The caller must not subsequently mutate the argument slice.
|
||||
func GroupValue(as ...Attr) Value {
|
||||
// Remove empty groups.
|
||||
// It is simpler overall to do this at construction than
|
||||
// to check each Group recursively for emptiness.
|
||||
if n := countEmptyGroups(as); n > 0 {
|
||||
as2 := make([]Attr, 0, len(as)-n)
|
||||
for _, a := range as {
|
||||
if !a.Value.isEmptyGroup() {
|
||||
as2 = append(as2, a)
|
||||
}
|
||||
}
|
||||
as = as2
|
||||
}
|
||||
return Value{num: uint64(len(as)), any: groupptr(unsafe.SliceData(as))}
|
||||
}
|
||||
|
||||
// countEmptyGroups returns the number of empty group values in its argument.
|
||||
func countEmptyGroups(as []Attr) int {
|
||||
n := 0
|
||||
for _, a := range as {
|
||||
if a.Value.isEmptyGroup() {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// AnyValue returns a Value for the supplied value.
|
||||
//
|
||||
// If the supplied value is of type Value, it is returned
|
||||
@ -399,6 +422,17 @@ func (v Value) Equal(w Value) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// isEmptyGroup reports whether v is a group that has no attributes.
|
||||
func (v Value) isEmptyGroup() bool {
|
||||
if v.Kind() != KindGroup {
|
||||
return false
|
||||
}
|
||||
// We do not need to recursively examine the group's Attrs for emptiness,
|
||||
// because GroupValue removed them when the group was constructed, and
|
||||
// groups are immutable.
|
||||
return len(v.group()) == 0
|
||||
}
|
||||
|
||||
// append appends a text representation of v to dst.
|
||||
// v is formatted as with fmt.Sprint.
|
||||
func (v Value) append(dst []byte) []byte {
|
||||
|
@ -229,6 +229,18 @@ func TestZeroTime(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyGroup(t *testing.T) {
|
||||
g := GroupValue(
|
||||
Int("a", 1),
|
||||
Group("g1", Group("g2")),
|
||||
Group("g3", Group("g4", Int("b", 2))))
|
||||
got := g.Group()
|
||||
want := []Attr{Int("a", 1), Group("g3", Group("g4", Int("b", 2)))}
|
||||
if !attrsEqual(got, want) {
|
||||
t.Errorf("\ngot %v\nwant %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
type replace struct {
|
||||
v Value
|
||||
}
|
||||
|
@ -2059,6 +2059,9 @@ var fmaC = []struct{ x, y, z, want float64 }{
|
||||
|
||||
// Special
|
||||
{0, 0, 0, 0},
|
||||
{Copysign(0, -1), 0, 0, 0},
|
||||
{0, 0, Copysign(0, -1), 0},
|
||||
{Copysign(0, -1), 0, Copysign(0, -1), Copysign(0, -1)},
|
||||
{-1.1754226043408471e-38, NaN(), Inf(0), NaN()},
|
||||
{0, 0, 2.22507385643494e-308, 2.22507385643494e-308},
|
||||
{-8.65697792e+09, NaN(), -7.516192799999999e+09, NaN()},
|
||||
@ -2077,6 +2080,10 @@ var fmaC = []struct{ x, y, z, want float64 }{
|
||||
{4.612811918325842e+18, 1.4901161193847641e-08, 2.6077032311277997e-08, 6.873625395187494e+10},
|
||||
{-9.094947033611148e-13, 4.450691014249257e-308, 2.086006742350485e-308, 2.086006742346437e-308},
|
||||
{-7.751454006381804e-05, 5.588653777189071e-308, -2.2207280111272877e-308, -2.2211612130544025e-308},
|
||||
|
||||
// Issue #61130
|
||||
{-1, 1, 1, 0},
|
||||
{1, 1, -1, 0},
|
||||
}
|
||||
|
||||
var sqrt32 = []float32{
|
||||
@ -3099,6 +3106,45 @@ func TestFMA(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func fmsub(x, y, z float64) float64 {
|
||||
return FMA(x, y, -z)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func fnmsub(x, y, z float64) float64 {
|
||||
return FMA(-x, y, z)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func fnmadd(x, y, z float64) float64 {
|
||||
return FMA(-x, y, -z)
|
||||
}
|
||||
|
||||
func TestFMANegativeArgs(t *testing.T) {
|
||||
// Some architectures have instructions for fused multiply-subtract and
|
||||
// also negated variants of fused multiply-add and subtract. This test
|
||||
// aims to check that the optimizations that generate those instructions
|
||||
// are applied correctly, if they exist.
|
||||
for _, c := range fmaC {
|
||||
want := PortableFMA(c.x, c.y, -c.z)
|
||||
got := fmsub(c.x, c.y, c.z)
|
||||
if !alike(got, want) {
|
||||
t.Errorf("FMA(%g, %g, -(%g)) == %g, want %g", c.x, c.y, c.z, got, want)
|
||||
}
|
||||
want = PortableFMA(-c.x, c.y, c.z)
|
||||
got = fnmsub(c.x, c.y, c.z)
|
||||
if !alike(got, want) {
|
||||
t.Errorf("FMA(-(%g), %g, %g) == %g, want %g", c.x, c.y, c.z, got, want)
|
||||
}
|
||||
want = PortableFMA(-c.x, c.y, -c.z)
|
||||
got = fnmadd(c.x, c.y, c.z)
|
||||
if !alike(got, want) {
|
||||
t.Errorf("FMA(-(%g), %g, -(%g)) == %g, want %g", c.x, c.y, c.z, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that math functions of high angle values
|
||||
// return accurate results. [Since (vf[i] + large) - large != vf[i],
|
||||
// testing for Trig(vf[i] + large) == Trig(vf[i]), where large is
|
||||
|
@ -132,6 +132,11 @@ func FMA(x, y, z float64) float64 {
|
||||
ps, pe, pm1, pm2, zs, ze, zm1, zm2 = zs, ze, zm1, zm2, ps, pe, pm1, pm2
|
||||
}
|
||||
|
||||
// Special case: if p == -z the result is always +0 since neither operand is zero.
|
||||
if ps != zs && pe == ze && pm1 == zm1 && pm2 == zm2 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Align significands
|
||||
zm1, zm2 = shrcompress(zm1, zm2, uint(pe-ze))
|
||||
|
||||
|
@ -3,12 +3,13 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file holds stub versions of the cgo functions called on Unix systems.
|
||||
// We build this file if using the netgo build tag, or if cgo is not
|
||||
// enabled and we are using a Unix system other than Darwin.
|
||||
// Darwin is exempted because it always provides the cgo routines,
|
||||
// in cgo_unix_syscall.go.
|
||||
// We build this file:
|
||||
// - if using the netgo build tag on a Unix system
|
||||
// - on a Unix system without the cgo resolver functions
|
||||
// (Darwin always provides the cgo functions, in cgo_unix_syscall.go)
|
||||
// - on wasip1, where cgo is never available
|
||||
|
||||
//go:build netgo || (!cgo && unix && !darwin)
|
||||
//go:build (netgo && unix) || (unix && !cgo && !darwin) || wasip1
|
||||
|
||||
package net
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !js && !wasip1
|
||||
//go:build !js
|
||||
|
||||
package net
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !js && !wasip1
|
||||
//go:build !js
|
||||
|
||||
// DNS client: see RFC 1035.
|
||||
// Has to be linked into package net for Dial.
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !js && !wasip1 && !windows
|
||||
//go:build !js && !windows
|
||||
|
||||
// Read system DNS config from /etc/resolv.conf
|
||||
|
||||
|
@ -48,35 +48,6 @@ func TestForeachHeaderElement(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanHost(t *testing.T) {
|
||||
tests := []struct {
|
||||
in, want string
|
||||
}{
|
||||
{"www.google.com", "www.google.com"},
|
||||
{"www.google.com foo", "www.google.com"},
|
||||
{"www.google.com/foo", "www.google.com"},
|
||||
{" first character is a space", ""},
|
||||
{"[1::6]:8080", "[1::6]:8080"},
|
||||
|
||||
// Punycode:
|
||||
{"гофер.рф/foo", "xn--c1ae0ajs.xn--p1ai"},
|
||||
{"bücher.de", "xn--bcher-kva.de"},
|
||||
{"bücher.de:8080", "xn--bcher-kva.de:8080"},
|
||||
// Verify we convert to lowercase before punycode:
|
||||
{"BÜCHER.de", "xn--bcher-kva.de"},
|
||||
{"BÜCHER.de:8080", "xn--bcher-kva.de:8080"},
|
||||
// Verify we normalize to NFC before punycode:
|
||||
{"gophér.nfc", "xn--gophr-esa.nfc"}, // NFC input; no work needed
|
||||
{"goph\u0065\u0301r.nfd", "xn--gophr-esa.nfd"}, // NFD input
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := cleanHost(tt.in)
|
||||
if tt.want != got {
|
||||
t.Errorf("cleanHost(%q) = %q, want %q", tt.in, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that cmd/go doesn't link in the HTTP server.
|
||||
//
|
||||
// This catches accidental dependencies between the HTTP transport and
|
||||
|
@ -17,7 +17,6 @@ import (
|
||||
"io"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"net/http/httptrace"
|
||||
"net/http/internal/ascii"
|
||||
"net/textproto"
|
||||
@ -27,6 +26,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
@ -580,12 +580,19 @@ func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitF
|
||||
// is not given, use the host from the request URL.
|
||||
//
|
||||
// Clean the host, in case it arrives with unexpected stuff in it.
|
||||
host := cleanHost(r.Host)
|
||||
host := r.Host
|
||||
if host == "" {
|
||||
if r.URL == nil {
|
||||
return errMissingHost
|
||||
}
|
||||
host = cleanHost(r.URL.Host)
|
||||
host = r.URL.Host
|
||||
}
|
||||
host, err = httpguts.PunycodeHostPort(host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !httpguts.ValidHostHeader(host) {
|
||||
return errors.New("http: invalid Host header")
|
||||
}
|
||||
|
||||
// According to RFC 6874, an HTTP client, proxy, or other
|
||||
@ -742,40 +749,6 @@ func idnaASCII(v string) (string, error) {
|
||||
return idna.Lookup.ToASCII(v)
|
||||
}
|
||||
|
||||
// cleanHost cleans up the host sent in request's Host header.
|
||||
//
|
||||
// It both strips anything after '/' or ' ', and puts the value
|
||||
// into Punycode form, if necessary.
|
||||
//
|
||||
// Ideally we'd clean the Host header according to the spec:
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc7230#section-5.4 (Host = uri-host [ ":" port ]")
|
||||
// https://tools.ietf.org/html/rfc7230#section-2.7 (uri-host -> rfc3986's host)
|
||||
// https://tools.ietf.org/html/rfc3986#section-3.2.2 (definition of host)
|
||||
//
|
||||
// But practically, what we are trying to avoid is the situation in
|
||||
// issue 11206, where a malformed Host header used in the proxy context
|
||||
// would create a bad request. So it is enough to just truncate at the
|
||||
// first offending character.
|
||||
func cleanHost(in string) string {
|
||||
if i := strings.IndexAny(in, " /"); i != -1 {
|
||||
in = in[:i]
|
||||
}
|
||||
host, port, err := net.SplitHostPort(in)
|
||||
if err != nil { // input was just a host
|
||||
a, err := idnaASCII(in)
|
||||
if err != nil {
|
||||
return in // garbage in, garbage out
|
||||
}
|
||||
return a
|
||||
}
|
||||
a, err := idnaASCII(host)
|
||||
if err != nil {
|
||||
return in // garbage in, garbage out
|
||||
}
|
||||
return net.JoinHostPort(a, port)
|
||||
}
|
||||
|
||||
// removeZone removes IPv6 zone identifier from host.
|
||||
// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
|
||||
func removeZone(host string) string {
|
||||
|
@ -775,15 +775,8 @@ func TestRequestBadHost(t *testing.T) {
|
||||
}
|
||||
req.Host = "foo.com with spaces"
|
||||
req.URL.Host = "foo.com with spaces"
|
||||
req.Write(logWrites{t, &got})
|
||||
want := []string{
|
||||
"GET /after HTTP/1.1\r\n",
|
||||
"Host: foo.com\r\n",
|
||||
"User-Agent: " + DefaultUserAgent + "\r\n",
|
||||
"\r\n",
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Writes = %q\n Want = %q", got, want)
|
||||
if err := req.Write(logWrites{t, &got}); err == nil {
|
||||
t.Errorf("Writing request with invalid Host: succeded, want error")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6731,3 +6731,22 @@ func testHandlerAbortRacesBodyRead(t *testing.T, mode testMode) {
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestRequestSanitization(t *testing.T) { run(t, testRequestSanitization) }
|
||||
func testRequestSanitization(t *testing.T, mode testMode) {
|
||||
if mode == http2Mode {
|
||||
// Remove this after updating x/net.
|
||||
t.Skip("https://go.dev/issue/60374 test fails when run with HTTP/2")
|
||||
}
|
||||
ts := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
|
||||
if h, ok := req.Header["X-Evil"]; ok {
|
||||
t.Errorf("request has X-Evil header: %q", h)
|
||||
}
|
||||
})).ts
|
||||
req, _ := NewRequest("GET", ts.URL, nil)
|
||||
req.Host = "go.dev\r\nX-Evil:evil"
|
||||
resp, _ := ts.Client().Do(req)
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (js && wasm) || wasip1
|
||||
//go:build js && wasm
|
||||
|
||||
package net
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build unix
|
||||
//go:build unix || wasip1
|
||||
|
||||
package net
|
||||
|
||||
|
@ -15,8 +15,6 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
var listenersMu sync.Mutex
|
||||
@ -406,7 +404,3 @@ func (fd *fakeNetFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int,
|
||||
func (fd *fakeNetFD) dup() (f *os.File, err error) {
|
||||
return nil, syscall.ENOSYS
|
||||
}
|
||||
|
||||
func (r *Resolver) lookup(ctx context.Context, name string, qtype dnsmessage.Type, conf *dnsConfig) (dnsmessage.Parser, string, error) {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
@ -8,7 +8,12 @@
|
||||
|
||||
package net
|
||||
|
||||
import "internal/poll"
|
||||
import (
|
||||
"context"
|
||||
"internal/poll"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
// Network file descriptor.
|
||||
type netFD struct {
|
||||
@ -25,3 +30,7 @@ type netFD struct {
|
||||
pfd poll.FD
|
||||
isConnected bool // handshake completed or use of association with peer
|
||||
}
|
||||
|
||||
func (r *Resolver) lookup(ctx context.Context, name string, qtype dnsmessage.Type, conf *dnsConfig) (dnsmessage.Parser, string, error) {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
@ -54,6 +54,15 @@ func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEn
|
||||
if entptr == nil { // EOF
|
||||
break
|
||||
}
|
||||
// Darwin may return a zero inode when a directory entry has been
|
||||
// deleted but not yet removed from the directory. The man page for
|
||||
// getdirentries(2) states that programs are responsible for skipping
|
||||
// those entries:
|
||||
//
|
||||
// Users of getdirentries() should skip entries with d_fileno = 0,
|
||||
// as such entries represent files which have been deleted but not
|
||||
// yet removed from the directory entry.
|
||||
//
|
||||
if dirent.Ino == 0 {
|
||||
continue
|
||||
}
|
||||
|
@ -89,7 +89,11 @@ func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEn
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if ino == 0 {
|
||||
// When building to wasip1, the host runtime might be running on Windows
|
||||
// or might expose a remote file system which does not have the concept
|
||||
// of inodes. Therefore, we cannot make the assumption that it is safe
|
||||
// to skip entries with zero inodes.
|
||||
if ino == 0 && runtime.GOOS != "wasip1" {
|
||||
continue
|
||||
}
|
||||
const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
|
||||
|
@ -130,7 +130,7 @@ func TestClearenv(t *testing.T) {
|
||||
defer func(origEnv []string) {
|
||||
for _, pair := range origEnv {
|
||||
// Environment variables on Windows can begin with =
|
||||
// https://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx
|
||||
// https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
|
||||
i := strings.Index(pair[1:], "=") + 1
|
||||
if err := Setenv(pair[:i], pair[i+1:]); err != nil {
|
||||
t.Errorf("Setenv(%q, %q) failed during reset: %v", pair[:i], pair[i+1:], err)
|
||||
|
@ -534,7 +534,7 @@ func TestMemPprof(t *testing.T) {
|
||||
|
||||
got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("testprog failed: %s, output:\n%s", err, got)
|
||||
}
|
||||
fn := strings.TrimSpace(string(got))
|
||||
defer os.Remove(fn)
|
||||
|
@ -76,7 +76,7 @@ func TestCrashDumpsAllThreads(t *testing.T) {
|
||||
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
if strings.Contains(os.Getenv("GCFLAGS"), "mayMoreStackPreempt") {
|
||||
if strings.Contains(os.Getenv("GOFLAGS"), "mayMoreStackPreempt") {
|
||||
// This test occasionally times out in this debug mode. This is probably
|
||||
// revealing a real bug in the scheduler, but since it seems to only
|
||||
// affect this test and this is itself a test of a debug mode, it's not
|
||||
|
@ -340,7 +340,9 @@ var allDesc = []Description{
|
||||
},
|
||||
{
|
||||
Name: "/memory/classes/heap/stacks:bytes",
|
||||
Description: "Memory allocated from the heap that is reserved for stack space, whether or not it is currently in-use.",
|
||||
Description: "Memory allocated from the heap that is reserved for stack space, whether or not it is currently in-use. " +
|
||||
"Currently, this represents all stack memory for goroutines. It also includes all OS thread stacks in non-cgo programs. " +
|
||||
"Note that stacks may be allocated differently in the future, and this may change.",
|
||||
Kind: KindUint64,
|
||||
},
|
||||
{
|
||||
@ -375,7 +377,11 @@ var allDesc = []Description{
|
||||
},
|
||||
{
|
||||
Name: "/memory/classes/os-stacks:bytes",
|
||||
Description: "Stack memory allocated by the underlying operating system.",
|
||||
Description: "Stack memory allocated by the underlying operating system. " +
|
||||
"In non-cgo programs this metric is currently zero. This may change in the future." +
|
||||
"In cgo programs this metric includes OS thread stacks allocated directly from the OS. " +
|
||||
"Currently, this only accounts for one stack in c-shared and c-archive build modes, " +
|
||||
"and other sources of stacks from the OS are not measured. This too may change in the future.",
|
||||
Kind: KindUint64,
|
||||
},
|
||||
{
|
||||
|
@ -318,7 +318,10 @@ Below is the full list of supported metrics, ordered lexicographically.
|
||||
|
||||
/memory/classes/heap/stacks:bytes
|
||||
Memory allocated from the heap that is reserved for stack space,
|
||||
whether or not it is currently in-use.
|
||||
whether or not it is currently in-use. Currently, this
|
||||
represents all stack memory for goroutines. It also includes all
|
||||
OS thread stacks in non-cgo programs. Note that stacks may be
|
||||
allocated differently in the future, and this may change.
|
||||
|
||||
/memory/classes/heap/unused:bytes
|
||||
Memory that is reserved for heap objects but is not currently
|
||||
@ -345,6 +348,12 @@ Below is the full list of supported metrics, ordered lexicographically.
|
||||
|
||||
/memory/classes/os-stacks:bytes
|
||||
Stack memory allocated by the underlying operating system.
|
||||
In non-cgo programs this metric is currently zero. This may
|
||||
change in the future.In cgo programs this metric includes
|
||||
OS thread stacks allocated directly from the OS. Currently,
|
||||
this only accounts for one stack in c-shared and c-archive build
|
||||
modes, and other sources of stacks from the OS are not measured.
|
||||
This too may change in the future.
|
||||
|
||||
/memory/classes/other:bytes
|
||||
Memory used by execution trace buffers, structures for debugging
|
||||
|
@ -199,7 +199,17 @@ type MemStats struct {
|
||||
// StackSys is bytes of stack memory obtained from the OS.
|
||||
//
|
||||
// StackSys is StackInuse, plus any memory obtained directly
|
||||
// from the OS for OS thread stacks (which should be minimal).
|
||||
// from the OS for OS thread stacks.
|
||||
//
|
||||
// In non-cgo programs this metric is currently equal to StackInuse
|
||||
// (but this should not be relied upon, and the value may change in
|
||||
// the future).
|
||||
//
|
||||
// In cgo programs this metric includes OS thread stacks allocated
|
||||
// directly from the OS. Currently, this only accounts for one stack in
|
||||
// c-shared and c-archive build modes and other sources of stacks from
|
||||
// the OS (notably, any allocated by C code) are not currently measured.
|
||||
// Note this too may change in the future.
|
||||
StackSys uint64
|
||||
|
||||
// Off-heap memory statistics.
|
||||
@ -347,6 +357,7 @@ func init() {
|
||||
// which is a snapshot as of the most recently completed garbage
|
||||
// collection cycle.
|
||||
func ReadMemStats(m *MemStats) {
|
||||
_ = m.Alloc // nil check test before we switch stacks, see issue 61158
|
||||
stopTheWorld(stwReadMemStats)
|
||||
|
||||
systemstack(func() {
|
||||
|
@ -1147,6 +1147,7 @@ func showfuncinfo(sf srcFunc, firstFrame bool, calleeID abi.FuncID) bool {
|
||||
|
||||
// isExportedRuntime reports whether name is an exported runtime function.
|
||||
// It is only for runtime functions, so ASCII A-Z is fine.
|
||||
// TODO: this handles exported functions but not exported methods.
|
||||
func isExportedRuntime(name string) bool {
|
||||
const n = len("runtime.")
|
||||
return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z'
|
||||
|
@ -6,7 +6,10 @@
|
||||
|
||||
package syscall
|
||||
|
||||
import "unsafe"
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// readInt returns the size-bytes unsigned integer in native byte order at offset off.
|
||||
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
|
||||
@ -75,7 +78,9 @@ func ParseDirent(buf []byte, max int, names []string) (consumed int, count int,
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if ino == 0 { // File absent in directory.
|
||||
// See src/os/dir_unix.go for the reason why this condition is
|
||||
// excluded on wasip1.
|
||||
if ino == 0 && runtime.GOOS != "wasip1" { // File absent in directory.
|
||||
continue
|
||||
}
|
||||
const namoff = uint64(unsafe.Offsetof(Dirent{}.Name))
|
||||
|
@ -62,7 +62,7 @@ func Clearenv() {
|
||||
for _, s := range Environ() {
|
||||
// Environment variables can begin with =
|
||||
// so start looking for the separator = at j=1.
|
||||
// https://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx
|
||||
// https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
|
||||
for j := 1; j < len(s); j++ {
|
||||
if s[j] == '=' {
|
||||
Unsetenv(s[0:j])
|
||||
|
@ -478,3 +478,16 @@ func SetNonblock(fd int, nonblocking bool) error {
|
||||
errno := fd_fdstat_set_flags(int32(fd), flags)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
type Rlimit struct {
|
||||
Cur uint64
|
||||
Max uint64
|
||||
}
|
||||
|
||||
const (
|
||||
RLIMIT_NOFILE = iota
|
||||
)
|
||||
|
||||
func Getrlimit(which int, lim *Rlimit) error {
|
||||
return ENOSYS
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ func Mul2(f float64) float64 {
|
||||
// arm/7:"ADDD",-"MULD"
|
||||
// arm64:"FADDD",-"FMULD"
|
||||
// ppc64x:"FADD",-"FMUL"
|
||||
// riscv64:"FADDD",-"FMULD"
|
||||
return f * 2.0
|
||||
}
|
||||
|
||||
@ -29,6 +30,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
|
||||
// arm/7:"MULD",-"DIVD"
|
||||
// arm64:"FMULD",-"FDIVD"
|
||||
// ppc64x:"FMUL",-"FDIV"
|
||||
// riscv64:"FMULD",-"FDIVD"
|
||||
x := f1 / 16.0
|
||||
|
||||
// 386/sse2:"MULSD",-"DIVSD"
|
||||
@ -36,6 +38,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
|
||||
// arm/7:"MULD",-"DIVD"
|
||||
// arm64:"FMULD",-"FDIVD"
|
||||
// ppc64x:"FMUL",-"FDIVD"
|
||||
// riscv64:"FMULD",-"FDIVD"
|
||||
y := f2 / 0.125
|
||||
|
||||
// 386/sse2:"ADDSD",-"DIVSD",-"MULSD"
|
||||
@ -43,6 +46,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
|
||||
// arm/7:"ADDD",-"MULD",-"DIVD"
|
||||
// arm64:"FADDD",-"FMULD",-"FDIVD"
|
||||
// ppc64x:"FADD",-"FMUL",-"FDIV"
|
||||
// riscv64:"FADDD",-"FMULD",-"FDIVD"
|
||||
z := f3 / 0.5
|
||||
|
||||
return x, y, z
|
||||
|
@ -143,13 +143,13 @@ func fms(x, y, z float64) float64 {
|
||||
return math.FMA(x, y, -z)
|
||||
}
|
||||
|
||||
func fnma(x, y, z float64) float64 {
|
||||
// riscv64:"FNMADDD"
|
||||
func fnms(x, y, z float64) float64 {
|
||||
// riscv64:"FNMSUBD",-"FNMADDD"
|
||||
return math.FMA(-x, y, z)
|
||||
}
|
||||
|
||||
func fnms(x, y, z float64) float64 {
|
||||
// riscv64:"FNMSUBD"
|
||||
func fnma(x, y, z float64) float64 {
|
||||
// riscv64:"FNMADDD",-"FNMSUBD"
|
||||
return math.FMA(x, -y, -z)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user