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, TestDirectives []Directive #56986
|
||||||
pkg go/build, type Package struct, XTestDirectives []Directive #56986
|
pkg go/build, type Package struct, XTestDirectives []Directive #56986
|
||||||
pkg go/token, method (*File) Lines() []int #57708
|
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 = 12 #59584
|
||||||
pkg html/template, const ErrJSTemplate ErrorCode #59584
|
pkg html/template, const ErrJSTemplate ErrorCode #59584
|
||||||
pkg io/fs, func FormatDirEntry(DirEntry) string #54451
|
pkg io/fs, func FormatDirEntry(DirEntry) string #54451
|
||||||
|
@ -65,7 +65,7 @@ func (s *Slice) Col() int {
|
|||||||
// #define A #define B(x) x
|
// #define A #define B(x) x
|
||||||
// and
|
// and
|
||||||
// #define A #define B (x) x
|
// #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.
|
// text/scanner strip the blanks for us, this is extremely rare, hard to fix, and not worth it.
|
||||||
return s.pos
|
return s.pos
|
||||||
}
|
}
|
||||||
|
@ -836,11 +836,11 @@
|
|||||||
//
|
//
|
||||||
// Key:
|
// Key:
|
||||||
//
|
//
|
||||||
// [+ -](x * y) [+ -] z.
|
// [+ -](x * y [+ -] z).
|
||||||
// _ N A S
|
// _ N A S
|
||||||
// D U
|
// D U
|
||||||
// D B
|
// D B
|
||||||
//
|
//
|
||||||
// Note: multiplication commutativity handled by rule generator.
|
// 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)
|
(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]
|
v_0 := v.Args[0]
|
||||||
// match: (FMADDD neg:(FNEGD x) y z)
|
// match: (FMADDD neg:(FNEGD x) y z)
|
||||||
// cond: neg.Uses == 1
|
// cond: neg.Uses == 1
|
||||||
// result: (FNMADDD x y z)
|
// result: (FNMSUBD x y z)
|
||||||
for {
|
for {
|
||||||
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
||||||
neg := v_0
|
neg := v_0
|
||||||
@ -3335,7 +3335,7 @@ func rewriteValueRISCV64_OpRISCV64FMADDD(v *Value) bool {
|
|||||||
if !(neg.Uses == 1) {
|
if !(neg.Uses == 1) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v.reset(OpRISCV64FNMADDD)
|
v.reset(OpRISCV64FNMSUBD)
|
||||||
v.AddArg3(x, y, z)
|
v.AddArg3(x, y, z)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -3367,7 +3367,7 @@ func rewriteValueRISCV64_OpRISCV64FMSUBD(v *Value) bool {
|
|||||||
v_0 := v.Args[0]
|
v_0 := v.Args[0]
|
||||||
// match: (FMSUBD neg:(FNEGD x) y z)
|
// match: (FMSUBD neg:(FNEGD x) y z)
|
||||||
// cond: neg.Uses == 1
|
// cond: neg.Uses == 1
|
||||||
// result: (FNMSUBD x y z)
|
// result: (FNMADDD x y z)
|
||||||
for {
|
for {
|
||||||
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
||||||
neg := v_0
|
neg := v_0
|
||||||
@ -3380,7 +3380,7 @@ func rewriteValueRISCV64_OpRISCV64FMSUBD(v *Value) bool {
|
|||||||
if !(neg.Uses == 1) {
|
if !(neg.Uses == 1) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v.reset(OpRISCV64FNMSUBD)
|
v.reset(OpRISCV64FNMADDD)
|
||||||
v.AddArg3(x, y, z)
|
v.AddArg3(x, y, z)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -3412,7 +3412,7 @@ func rewriteValueRISCV64_OpRISCV64FNMADDD(v *Value) bool {
|
|||||||
v_0 := v.Args[0]
|
v_0 := v.Args[0]
|
||||||
// match: (FNMADDD neg:(FNEGD x) y z)
|
// match: (FNMADDD neg:(FNEGD x) y z)
|
||||||
// cond: neg.Uses == 1
|
// cond: neg.Uses == 1
|
||||||
// result: (FMADDD x y z)
|
// result: (FMSUBD x y z)
|
||||||
for {
|
for {
|
||||||
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
||||||
neg := v_0
|
neg := v_0
|
||||||
@ -3425,7 +3425,7 @@ func rewriteValueRISCV64_OpRISCV64FNMADDD(v *Value) bool {
|
|||||||
if !(neg.Uses == 1) {
|
if !(neg.Uses == 1) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v.reset(OpRISCV64FMADDD)
|
v.reset(OpRISCV64FMSUBD)
|
||||||
v.AddArg3(x, y, z)
|
v.AddArg3(x, y, z)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -3457,7 +3457,7 @@ func rewriteValueRISCV64_OpRISCV64FNMSUBD(v *Value) bool {
|
|||||||
v_0 := v.Args[0]
|
v_0 := v.Args[0]
|
||||||
// match: (FNMSUBD neg:(FNEGD x) y z)
|
// match: (FNMSUBD neg:(FNEGD x) y z)
|
||||||
// cond: neg.Uses == 1
|
// cond: neg.Uses == 1
|
||||||
// result: (FMSUBD x y z)
|
// result: (FMADDD x y z)
|
||||||
for {
|
for {
|
||||||
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
|
||||||
neg := v_0
|
neg := v_0
|
||||||
@ -3470,7 +3470,7 @@ func rewriteValueRISCV64_OpRISCV64FNMSUBD(v *Value) bool {
|
|||||||
if !(neg.Uses == 1) {
|
if !(neg.Uses == 1) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v.reset(OpRISCV64FMSUBD)
|
v.reset(OpRISCV64FMADDD)
|
||||||
v.AddArg3(x, y, z)
|
v.AddArg3(x, y, z)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
|
"internal/goversion"
|
||||||
. "internal/types/errors"
|
. "internal/types/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -231,19 +232,19 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
|
|||||||
info = new(Info)
|
info = new(Info)
|
||||||
}
|
}
|
||||||
|
|
||||||
version, err := parseGoVersion(conf.GoVersion)
|
// Note: clients may call NewChecker with the Unsafe package, which is
|
||||||
if err != nil {
|
// globally shared and must not be mutated. Therefore NewChecker must not
|
||||||
panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err))
|
// mutate *pkg.
|
||||||
}
|
//
|
||||||
|
// (previously, pkg.goVersion was mutated here: go.dev/issue/61212)
|
||||||
|
|
||||||
return &Checker{
|
return &Checker{
|
||||||
conf: conf,
|
conf: conf,
|
||||||
ctxt: conf.Context,
|
ctxt: conf.Context,
|
||||||
pkg: pkg,
|
pkg: pkg,
|
||||||
Info: info,
|
Info: info,
|
||||||
version: version,
|
objMap: make(map[Object]*declInfo),
|
||||||
objMap: make(map[Object]*declInfo),
|
impMap: make(map[importKey]*Package),
|
||||||
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")
|
var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")
|
||||||
|
|
||||||
func (check *Checker) checkFiles(files []*syntax.File) (err error) {
|
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 {
|
if check.conf.FakeImportC && check.conf.go115UsesCgo {
|
||||||
return errBadCgo
|
return errBadCgo
|
||||||
}
|
}
|
||||||
@ -377,6 +392,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
|
|||||||
check.monomorph()
|
check.monomorph()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check.pkg.goVersion = check.conf.GoVersion
|
||||||
check.pkg.complete = true
|
check.pkg.complete = true
|
||||||
|
|
||||||
// no longer needed - release memory
|
// no longer needed - release memory
|
||||||
|
@ -497,14 +497,14 @@ func TestIssue43088(t *testing.T) {
|
|||||||
// _ T2
|
// _ T2
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
n1 := NewTypeName(syntax.Pos{}, nil, "T1", nil)
|
n1 := NewTypeName(nopos, nil, "T1", nil)
|
||||||
T1 := NewNamed(n1, nil, nil)
|
T1 := NewNamed(n1, nil, nil)
|
||||||
n2 := NewTypeName(syntax.Pos{}, nil, "T2", nil)
|
n2 := NewTypeName(nopos, nil, "T2", nil)
|
||||||
T2 := NewNamed(n2, nil, 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)
|
T1.SetUnderlying(s1)
|
||||||
s2 := NewStruct([]*Var{NewField(syntax.Pos{}, nil, "_", T2, false)}, nil)
|
s2 := NewStruct([]*Var{NewField(nopos, nil, "_", T2, false)}, nil)
|
||||||
s3 := NewStruct([]*Var{NewField(syntax.Pos{}, nil, "_", s2, false)}, nil)
|
s3 := NewStruct([]*Var{NewField(nopos, nil, "_", s2, false)}, nil)
|
||||||
T2.SetUnderlying(s3)
|
T2.SetUnderlying(s3)
|
||||||
|
|
||||||
// These calls must terminate (no endless recursion).
|
// These calls must terminate (no endless recursion).
|
||||||
@ -535,38 +535,69 @@ func TestIssue44515(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue43124(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
|
// All involved packages have the same name (template). Error messages should
|
||||||
// disambiguate between text/template and html/template by printing the full
|
// disambiguate between text/template and html/template by printing the full
|
||||||
// path.
|
// path.
|
||||||
const (
|
const (
|
||||||
asrc = `package a; import "text/template"; func F(template.Template) {}; func G(int) {}`
|
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{}) }`
|
bsrc = `
|
||||||
csrc = `package c; import ("a"; "html/template"); func _() { a.G(template.Template{}) }`
|
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)
|
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
|
withImporter := func(cfg *Config) {
|
||||||
// error string itself.
|
cfg.Importer = imp
|
||||||
_, 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...and also when there is any ambiguity in reachable packages.
|
testFiles(t, []string{"b.go"}, [][]byte{[]byte(bsrc)}, 0, false, withImporter)
|
||||||
_, err = typecheck(csrc, &conf, nil)
|
testFiles(t, []string{"c.go"}, [][]byte{[]byte(csrc)}, 0, false, withImporter)
|
||||||
if err == nil {
|
testFiles(t, []string{"t.go"}, [][]byte{[]byte(tsrc)}, 0, false, withImporter)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue50646(t *testing.T) {
|
func TestIssue50646(t *testing.T) {
|
||||||
|
@ -10,13 +10,14 @@ import (
|
|||||||
|
|
||||||
// A Package describes a Go package.
|
// A Package describes a Go package.
|
||||||
type Package struct {
|
type Package struct {
|
||||||
path string
|
path string
|
||||||
name string
|
name string
|
||||||
scope *Scope
|
scope *Scope
|
||||||
imports []*Package
|
imports []*Package
|
||||||
complete bool
|
complete bool
|
||||||
fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
|
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
|
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.
|
// 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.
|
// SetName sets the package name.
|
||||||
func (pkg *Package) SetName(name string) { pkg.name = 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
|
// Scope returns the (complete or incomplete) package scope
|
||||||
// holding the objects declared at package level (TypeNames,
|
// holding the objects declared at package level (TypeNames,
|
||||||
// Consts, Vars, and Funcs).
|
// Consts, Vars, and Funcs).
|
||||||
|
@ -47,7 +47,7 @@ func TestSizeof(t *testing.T) {
|
|||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
{Scope{}, 60, 104},
|
{Scope{}, 60, 104},
|
||||||
{Package{}, 36, 72},
|
{Package{}, 44, 88},
|
||||||
{_TypeSet{}, 28, 56},
|
{_TypeSet{}, 28, 56},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ package types2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"cmd/compile/internal/syntax"
|
"cmd/compile/internal/syntax"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -44,23 +43,24 @@ var (
|
|||||||
go1_21 = version{1, 21}
|
go1_21 = version{1, 21}
|
||||||
)
|
)
|
||||||
|
|
||||||
var errVersionSyntax = errors.New("invalid Go version syntax")
|
|
||||||
|
|
||||||
// parseGoVersion parses a Go version string (such as "go1.12")
|
// parseGoVersion parses a Go version string (such as "go1.12")
|
||||||
// and returns the version, or an error. If s is the empty
|
// and returns the version, or an error. If s is the empty
|
||||||
// string, the version is 0.0.
|
// string, the version is 0.0.
|
||||||
func parseGoVersion(s string) (v version, err error) {
|
func parseGoVersion(s string) (v version, err error) {
|
||||||
|
bad := func() (version, error) {
|
||||||
|
return version{}, fmt.Errorf("invalid Go version syntax %q", s)
|
||||||
|
}
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(s, "go") {
|
if !strings.HasPrefix(s, "go") {
|
||||||
return version{}, errVersionSyntax
|
return bad()
|
||||||
}
|
}
|
||||||
s = s[len("go"):]
|
s = s[len("go"):]
|
||||||
i := 0
|
i := 0
|
||||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||||
if i >= 10 || i == 0 && s[i] == '0' {
|
if i >= 10 || i == 0 && s[i] == '0' {
|
||||||
return version{}, errVersionSyntax
|
return bad()
|
||||||
}
|
}
|
||||||
v.major = 10*v.major + int(s[i]) - '0'
|
v.major = 10*v.major + int(s[i]) - '0'
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ func parseGoVersion(s string) (v version, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if i == 0 || s[i] != '.' {
|
if i == 0 || s[i] != '.' {
|
||||||
return version{}, errVersionSyntax
|
return bad()
|
||||||
}
|
}
|
||||||
s = s[i+1:]
|
s = s[i+1:]
|
||||||
if s == "0" {
|
if s == "0" {
|
||||||
@ -81,14 +81,15 @@ func parseGoVersion(s string) (v version, err error) {
|
|||||||
i = 0
|
i = 0
|
||||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||||
if i >= 10 || i == 0 && s[i] == '0' {
|
if i >= 10 || i == 0 && s[i] == '0' {
|
||||||
return version{}, errVersionSyntax
|
return bad()
|
||||||
}
|
}
|
||||||
v.minor = 10*v.minor + int(s[i]) - '0'
|
v.minor = 10*v.minor + int(s[i]) - '0'
|
||||||
}
|
}
|
||||||
if i > 0 && i == len(s) {
|
// Accept any suffix after the minor number.
|
||||||
return
|
// We are only looking for the language version (major.minor)
|
||||||
}
|
// but want to accept any valid Go version, like go1.21.0
|
||||||
return version{}, errVersionSyntax
|
// and go1.21rc2.
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// langCompat reports an error if the representation of a numeric
|
// 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
|
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
|
VetxOnly bool // only compute vetx data; don't report detected problems
|
||||||
VetxOutput string // write vetx data to this output file
|
VetxOutput string // write vetx data to this output file
|
||||||
|
GoVersion string // Go version for package
|
||||||
|
|
||||||
SucceedOnTypecheckFailure bool // awful hack; see #18395 and below
|
SucceedOnTypecheckFailure bool // awful hack; see #18395 and below
|
||||||
}
|
}
|
||||||
@ -1149,6 +1150,13 @@ func buildVetConfig(a *Action, srcfiles []string) {
|
|||||||
PackageFile: make(map[string]string),
|
PackageFile: make(map[string]string),
|
||||||
Standard: make(map[string]bool),
|
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
|
a.vetCfg = vcfg
|
||||||
for i, raw := range a.Package.Internal.RawImports {
|
for i, raw := range a.Package.Internal.RawImports {
|
||||||
final := a.Package.Imports[i]
|
final := a.Package.Imports[i]
|
||||||
|
@ -85,19 +85,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
|
|||||||
if p.Module != nil {
|
if p.Module != nil {
|
||||||
v := p.Module.GoVersion
|
v := p.Module.GoVersion
|
||||||
if v == "" {
|
if v == "" {
|
||||||
// We started adding a 'go' directive to the go.mod file unconditionally
|
v = gover.DefaultGoModVersion
|
||||||
// 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"
|
|
||||||
}
|
}
|
||||||
if allowedVersion(v) {
|
if allowedVersion(v) {
|
||||||
defaultGcFlags = append(defaultGcFlags, "-lang=go"+gover.Lang(v))
|
defaultGcFlags = append(defaultGcFlags, "-lang=go"+gover.Lang(v))
|
||||||
|
@ -2944,15 +2944,17 @@ func (rs *Rows) initContextClose(ctx, txctx context.Context) {
|
|||||||
if bypassRowsAwaitDone {
|
if bypassRowsAwaitDone {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx, rs.cancel = context.WithCancel(ctx)
|
closectx, cancel := context.WithCancel(ctx)
|
||||||
go rs.awaitDone(ctx, txctx)
|
rs.cancel = cancel
|
||||||
|
go rs.awaitDone(ctx, txctx, closectx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// awaitDone blocks until either ctx or txctx is canceled. The ctx is provided
|
// awaitDone blocks until ctx, txctx, or closectx is canceled.
|
||||||
// from the query context and is canceled when the query Rows is closed.
|
// The ctx is provided from the query context.
|
||||||
// If the query was issued in a transaction, the transaction's 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.
|
// is also provided in txctx, to ensure Rows is closed if the Tx is closed.
|
||||||
func (rs *Rows) awaitDone(ctx, txctx context.Context) {
|
// The closectx is closed by an explicit call to rs.Close.
|
||||||
|
func (rs *Rows) awaitDone(ctx, txctx, closectx context.Context) {
|
||||||
var txctxDone <-chan struct{}
|
var txctxDone <-chan struct{}
|
||||||
if txctx != nil {
|
if txctx != nil {
|
||||||
txctxDone = txctx.Done()
|
txctxDone = txctx.Done()
|
||||||
@ -2964,6 +2966,9 @@ func (rs *Rows) awaitDone(ctx, txctx context.Context) {
|
|||||||
case <-txctxDone:
|
case <-txctxDone:
|
||||||
err := txctx.Err()
|
err := txctx.Err()
|
||||||
rs.contextDone.Store(&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())
|
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.
|
// badConn implements a bad driver.Conn, for TestBadDriver.
|
||||||
// The Exec method panics.
|
// The Exec method panics.
|
||||||
type badConn struct{}
|
type badConn struct{}
|
||||||
|
@ -286,7 +286,7 @@ var depsRules = `
|
|||||||
math/big, go/token
|
math/big, go/token
|
||||||
< go/constant;
|
< go/constant;
|
||||||
|
|
||||||
container/heap, go/constant, go/parser, internal/types/errors
|
container/heap, go/constant, go/parser, internal/goversion, internal/types/errors
|
||||||
< go/types;
|
< go/types;
|
||||||
|
|
||||||
# The vast majority of standard library packages should not be resorting to regexp.
|
# The vast majority of standard library packages should not be resorting to regexp.
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"go/ast"
|
"go/ast"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"internal/goversion"
|
||||||
. "internal/types/errors"
|
. "internal/types/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -233,20 +234,20 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
|
|||||||
info = new(Info)
|
info = new(Info)
|
||||||
}
|
}
|
||||||
|
|
||||||
version, err := parseGoVersion(conf.GoVersion)
|
// Note: clients may call NewChecker with the Unsafe package, which is
|
||||||
if err != nil {
|
// globally shared and must not be mutated. Therefore NewChecker must not
|
||||||
panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err))
|
// mutate *pkg.
|
||||||
}
|
//
|
||||||
|
// (previously, pkg.goVersion was mutated here: go.dev/issue/61212)
|
||||||
|
|
||||||
return &Checker{
|
return &Checker{
|
||||||
conf: conf,
|
conf: conf,
|
||||||
ctxt: conf.Context,
|
ctxt: conf.Context,
|
||||||
fset: fset,
|
fset: fset,
|
||||||
pkg: pkg,
|
pkg: pkg,
|
||||||
Info: info,
|
Info: info,
|
||||||
version: version,
|
objMap: make(map[Object]*declInfo),
|
||||||
objMap: make(map[Object]*declInfo),
|
impMap: make(map[importKey]*Package),
|
||||||
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")
|
var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")
|
||||||
|
|
||||||
func (check *Checker) checkFiles(files []*ast.File) (err error) {
|
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 {
|
if check.conf.FakeImportC && check.conf.go115UsesCgo {
|
||||||
return errBadCgo
|
return errBadCgo
|
||||||
}
|
}
|
||||||
@ -386,6 +401,7 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
|
|||||||
check.monomorph()
|
check.monomorph()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check.pkg.goVersion = check.conf.GoVersion
|
||||||
check.pkg.complete = true
|
check.pkg.complete = true
|
||||||
|
|
||||||
// no longer needed - release memory
|
// no longer needed - release memory
|
||||||
|
@ -141,6 +141,7 @@ var filemap = map[string]action{
|
|||||||
"universe.go": fixGlobalTypVarDecl,
|
"universe.go": fixGlobalTypVarDecl,
|
||||||
"util_test.go": fixTokenPos,
|
"util_test.go": fixTokenPos,
|
||||||
"validtype.go": nil,
|
"validtype.go": nil,
|
||||||
|
"version_test.go": nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(gri) We should be able to make these rewriters more configurable/composable.
|
// TODO(gri) We should be able to make these rewriters more configurable/composable.
|
||||||
|
@ -12,13 +12,14 @@ import (
|
|||||||
|
|
||||||
// A Package describes a Go package.
|
// A Package describes a Go package.
|
||||||
type Package struct {
|
type Package struct {
|
||||||
path string
|
path string
|
||||||
name string
|
name string
|
||||||
scope *Scope
|
scope *Scope
|
||||||
imports []*Package
|
imports []*Package
|
||||||
complete bool
|
complete bool
|
||||||
fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
|
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
|
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.
|
// 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.
|
// SetName sets the package name.
|
||||||
func (pkg *Package) SetName(name string) { pkg.name = 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
|
// Scope returns the (complete or incomplete) package scope
|
||||||
// holding the objects declared at package level (TypeNames,
|
// holding the objects declared at package level (TypeNames,
|
||||||
// Consts, Vars, and Funcs).
|
// Consts, Vars, and Funcs).
|
||||||
|
@ -46,7 +46,7 @@ func TestSizeof(t *testing.T) {
|
|||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
{Scope{}, 44, 88},
|
{Scope{}, 44, 88},
|
||||||
{Package{}, 36, 72},
|
{Package{}, 44, 88},
|
||||||
{_TypeSet{}, 28, 56},
|
{_TypeSet{}, 28, 56},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
@ -45,23 +44,24 @@ var (
|
|||||||
go1_21 = version{1, 21}
|
go1_21 = version{1, 21}
|
||||||
)
|
)
|
||||||
|
|
||||||
var errVersionSyntax = errors.New("invalid Go version syntax")
|
|
||||||
|
|
||||||
// parseGoVersion parses a Go version string (such as "go1.12")
|
// parseGoVersion parses a Go version string (such as "go1.12")
|
||||||
// and returns the version, or an error. If s is the empty
|
// and returns the version, or an error. If s is the empty
|
||||||
// string, the version is 0.0.
|
// string, the version is 0.0.
|
||||||
func parseGoVersion(s string) (v version, err error) {
|
func parseGoVersion(s string) (v version, err error) {
|
||||||
|
bad := func() (version, error) {
|
||||||
|
return version{}, fmt.Errorf("invalid Go version syntax %q", s)
|
||||||
|
}
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(s, "go") {
|
if !strings.HasPrefix(s, "go") {
|
||||||
return version{}, errVersionSyntax
|
return bad()
|
||||||
}
|
}
|
||||||
s = s[len("go"):]
|
s = s[len("go"):]
|
||||||
i := 0
|
i := 0
|
||||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||||
if i >= 10 || i == 0 && s[i] == '0' {
|
if i >= 10 || i == 0 && s[i] == '0' {
|
||||||
return version{}, errVersionSyntax
|
return bad()
|
||||||
}
|
}
|
||||||
v.major = 10*v.major + int(s[i]) - '0'
|
v.major = 10*v.major + int(s[i]) - '0'
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ func parseGoVersion(s string) (v version, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if i == 0 || s[i] != '.' {
|
if i == 0 || s[i] != '.' {
|
||||||
return version{}, errVersionSyntax
|
return bad()
|
||||||
}
|
}
|
||||||
s = s[i+1:]
|
s = s[i+1:]
|
||||||
if s == "0" {
|
if s == "0" {
|
||||||
@ -82,14 +82,15 @@ func parseGoVersion(s string) (v version, err error) {
|
|||||||
i = 0
|
i = 0
|
||||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||||
if i >= 10 || i == 0 && s[i] == '0' {
|
if i >= 10 || i == 0 && s[i] == '0' {
|
||||||
return version{}, errVersionSyntax
|
return bad()
|
||||||
}
|
}
|
||||||
v.minor = 10*v.minor + int(s[i]) - '0'
|
v.minor = 10*v.minor + int(s[i]) - '0'
|
||||||
}
|
}
|
||||||
if i > 0 && i == len(s) {
|
// Accept any suffix after the minor number.
|
||||||
return
|
// We are only looking for the language version (major.minor)
|
||||||
}
|
// but want to accept any valid Go version, like go1.21.0
|
||||||
return version{}, errVersionSyntax
|
// and go1.21rc2.
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// langCompat reports an error if the representation of a numeric
|
// 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 {
|
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()
|
h2 := h.clone()
|
||||||
// Pre-format the attributes as an optimization.
|
// Pre-format the attributes as an optimization.
|
||||||
state := h2.newHandleState((*buffer.Buffer)(&h2.preformattedAttrs), false, "")
|
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
|
// Attrs in Record -- unlike the built-in ones, they are in groups started
|
||||||
// from WithGroup.
|
// from WithGroup.
|
||||||
s.prefix.WriteString(s.h.groupPrefix)
|
// If the record has no Attrs, don't output any groups.
|
||||||
s.openGroups()
|
nOpenGroups := s.h.nOpenGroups
|
||||||
r.Attrs(func(a Attr) bool {
|
if r.NumAttrs() > 0 {
|
||||||
s.appendAttr(a)
|
s.prefix.WriteString(s.h.groupPrefix)
|
||||||
return true
|
s.openGroups()
|
||||||
})
|
nOpenGroups = len(s.h.groups)
|
||||||
|
r.Attrs(func(a Attr) bool {
|
||||||
|
s.appendAttr(a)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
if s.h.json {
|
if s.h.json {
|
||||||
// Close all open groups.
|
// Close all open groups.
|
||||||
for range s.h.groups {
|
for range s.h.groups[:nOpenGroups] {
|
||||||
s.buf.WriteByte('}')
|
s.buf.WriteByte('}')
|
||||||
}
|
}
|
||||||
// Close the top-level object.
|
// Close the top-level object.
|
||||||
|
@ -214,6 +214,28 @@ func TestJSONAndTextHandlers(t *testing.T) {
|
|||||||
wantText: "msg=message h.a=1",
|
wantText: "msg=message h.a=1",
|
||||||
wantJSON: `{"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",
|
name: "escapes",
|
||||||
replace: removeKeys(TimeKey, LevelKey),
|
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",
|
wantText: "msg=message p1=1 s1.s2.a=one s1.s2.b=2",
|
||||||
wantJSON: `{"msg":"message","p1":1,"s1":{"s2":{"a":"one","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",
|
name: "GroupValue as Attr value",
|
||||||
replace: removeKeys(TimeKey, LevelKey),
|
replace: removeKeys(TimeKey, LevelKey),
|
||||||
|
@ -106,7 +106,7 @@ func TestConnections(t *testing.T) {
|
|||||||
// log.Logger's output goes through the handler.
|
// log.Logger's output goes through the handler.
|
||||||
SetDefault(New(NewTextHandler(&slogbuf, &HandlerOptions{AddSource: true})))
|
SetDefault(New(NewTextHandler(&slogbuf, &HandlerOptions{AddSource: true})))
|
||||||
log.Print("msg2")
|
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.
|
// The default log.Logger always outputs at Info level.
|
||||||
slogbuf.Reset()
|
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.
|
// AddAttrs appends the given Attrs to the Record's list of Attrs.
|
||||||
|
// It omits empty groups.
|
||||||
func (r *Record) AddAttrs(attrs ...Attr) {
|
func (r *Record) AddAttrs(attrs ...Attr) {
|
||||||
n := copy(r.front[r.nFront:], attrs)
|
var i int
|
||||||
r.nFront += n
|
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
|
// Check if a copy was modified by slicing past the end
|
||||||
// and seeing if the Attr there is non-zero.
|
// and seeing if the Attr there is non-zero.
|
||||||
if cap(r.back) > len(r.back) {
|
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")
|
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],
|
// Add converts the args to Attrs as described in [Logger.Log],
|
||||||
// then appends the Attrs to the Record's list of Attrs.
|
// then appends the Attrs to the Record's list of Attrs.
|
||||||
|
// It omits empty groups.
|
||||||
func (r *Record) Add(args ...any) {
|
func (r *Record) Add(args ...any) {
|
||||||
var a Attr
|
var a Attr
|
||||||
for len(args) > 0 {
|
for len(args) > 0 {
|
||||||
a, args = argsToAttr(args)
|
a, args = argsToAttr(args)
|
||||||
|
if a.Value.isEmptyGroup() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if r.nFront < len(r.front) {
|
if r.nFront < len(r.front) {
|
||||||
r.front[r.nFront] = a
|
r.front[r.nFront] = a
|
||||||
r.nFront++
|
r.nFront++
|
||||||
|
@ -164,9 +164,32 @@ func DurationValue(v time.Duration) Value {
|
|||||||
// GroupValue returns a new Value for a list of Attrs.
|
// GroupValue returns a new Value for a list of Attrs.
|
||||||
// The caller must not subsequently mutate the argument slice.
|
// The caller must not subsequently mutate the argument slice.
|
||||||
func GroupValue(as ...Attr) Value {
|
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))}
|
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.
|
// AnyValue returns a Value for the supplied value.
|
||||||
//
|
//
|
||||||
// If the supplied value is of type Value, it is returned
|
// 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.
|
// append appends a text representation of v to dst.
|
||||||
// v is formatted as with fmt.Sprint.
|
// v is formatted as with fmt.Sprint.
|
||||||
func (v Value) append(dst []byte) []byte {
|
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 {
|
type replace struct {
|
||||||
v Value
|
v Value
|
||||||
}
|
}
|
||||||
|
@ -2059,6 +2059,9 @@ var fmaC = []struct{ x, y, z, want float64 }{
|
|||||||
|
|
||||||
// Special
|
// Special
|
||||||
{0, 0, 0, 0},
|
{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()},
|
{-1.1754226043408471e-38, NaN(), Inf(0), NaN()},
|
||||||
{0, 0, 2.22507385643494e-308, 2.22507385643494e-308},
|
{0, 0, 2.22507385643494e-308, 2.22507385643494e-308},
|
||||||
{-8.65697792e+09, NaN(), -7.516192799999999e+09, NaN()},
|
{-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},
|
{4.612811918325842e+18, 1.4901161193847641e-08, 2.6077032311277997e-08, 6.873625395187494e+10},
|
||||||
{-9.094947033611148e-13, 4.450691014249257e-308, 2.086006742350485e-308, 2.086006742346437e-308},
|
{-9.094947033611148e-13, 4.450691014249257e-308, 2.086006742350485e-308, 2.086006742346437e-308},
|
||||||
{-7.751454006381804e-05, 5.588653777189071e-308, -2.2207280111272877e-308, -2.2211612130544025e-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{
|
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
|
// Check that math functions of high angle values
|
||||||
// return accurate results. [Since (vf[i] + large) - large != vf[i],
|
// return accurate results. [Since (vf[i] + large) - large != vf[i],
|
||||||
// testing for Trig(vf[i] + large) == Trig(vf[i]), where large is
|
// 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
|
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
|
// Align significands
|
||||||
zm1, zm2 = shrcompress(zm1, zm2, uint(pe-ze))
|
zm1, zm2 = shrcompress(zm1, zm2, uint(pe-ze))
|
||||||
|
|
||||||
|
@ -3,12 +3,13 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// This file holds stub versions of the cgo functions called on Unix systems.
|
// 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
|
// We build this file:
|
||||||
// enabled and we are using a Unix system other than Darwin.
|
// - if using the netgo build tag on a Unix system
|
||||||
// Darwin is exempted because it always provides the cgo routines,
|
// - on a Unix system without the cgo resolver functions
|
||||||
// in cgo_unix_syscall.go.
|
// (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
|
package net
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !js && !wasip1
|
//go:build !js
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !js && !wasip1
|
//go:build !js
|
||||||
|
|
||||||
// DNS client: see RFC 1035.
|
// DNS client: see RFC 1035.
|
||||||
// Has to be linked into package net for Dial.
|
// Has to be linked into package net for Dial.
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// 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
|
// 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.
|
// Test that cmd/go doesn't link in the HTTP server.
|
||||||
//
|
//
|
||||||
// This catches accidental dependencies between the HTTP transport and
|
// This catches accidental dependencies between the HTTP transport and
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"mime"
|
"mime"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net"
|
|
||||||
"net/http/httptrace"
|
"net/http/httptrace"
|
||||||
"net/http/internal/ascii"
|
"net/http/internal/ascii"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
@ -27,6 +26,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/net/http/httpguts"
|
||||||
"golang.org/x/net/idna"
|
"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.
|
// is not given, use the host from the request URL.
|
||||||
//
|
//
|
||||||
// Clean the host, in case it arrives with unexpected stuff in it.
|
// Clean the host, in case it arrives with unexpected stuff in it.
|
||||||
host := cleanHost(r.Host)
|
host := r.Host
|
||||||
if host == "" {
|
if host == "" {
|
||||||
if r.URL == nil {
|
if r.URL == nil {
|
||||||
return errMissingHost
|
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
|
// 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)
|
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.
|
// removeZone removes IPv6 zone identifier from host.
|
||||||
// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
|
// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
|
||||||
func removeZone(host string) string {
|
func removeZone(host string) string {
|
||||||
|
@ -775,15 +775,8 @@ func TestRequestBadHost(t *testing.T) {
|
|||||||
}
|
}
|
||||||
req.Host = "foo.com with spaces"
|
req.Host = "foo.com with spaces"
|
||||||
req.URL.Host = "foo.com with spaces"
|
req.URL.Host = "foo.com with spaces"
|
||||||
req.Write(logWrites{t, &got})
|
if err := req.Write(logWrites{t, &got}); err == nil {
|
||||||
want := []string{
|
t.Errorf("Writing request with invalid Host: succeded, want error")
|
||||||
"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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6731,3 +6731,22 @@ func testHandlerAbortRacesBodyRead(t *testing.T, mode testMode) {
|
|||||||
}
|
}
|
||||||
wg.Wait()
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (js && wasm) || wasip1
|
//go:build js && wasm
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build unix
|
//go:build unix || wasip1
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var listenersMu sync.Mutex
|
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) {
|
func (fd *fakeNetFD) dup() (f *os.File, err error) {
|
||||||
return nil, syscall.ENOSYS
|
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
|
package net
|
||||||
|
|
||||||
import "internal/poll"
|
import (
|
||||||
|
"context"
|
||||||
|
"internal/poll"
|
||||||
|
|
||||||
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
|
)
|
||||||
|
|
||||||
// Network file descriptor.
|
// Network file descriptor.
|
||||||
type netFD struct {
|
type netFD struct {
|
||||||
@ -25,3 +30,7 @@ type netFD struct {
|
|||||||
pfd poll.FD
|
pfd poll.FD
|
||||||
isConnected bool // handshake completed or use of association with peer
|
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
|
if entptr == nil { // EOF
|
||||||
break
|
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 {
|
if dirent.Ino == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,11 @@ func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEn
|
|||||||
if !ok {
|
if !ok {
|
||||||
break
|
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
|
continue
|
||||||
}
|
}
|
||||||
const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
|
const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
|
||||||
|
@ -130,7 +130,7 @@ func TestClearenv(t *testing.T) {
|
|||||||
defer func(origEnv []string) {
|
defer func(origEnv []string) {
|
||||||
for _, pair := range origEnv {
|
for _, pair := range origEnv {
|
||||||
// Environment variables on Windows can begin with =
|
// 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
|
i := strings.Index(pair[1:], "=") + 1
|
||||||
if err := Setenv(pair[:i], pair[i+1:]); err != nil {
|
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)
|
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()
|
got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatalf("testprog failed: %s, output:\n%s", err, got)
|
||||||
}
|
}
|
||||||
fn := strings.TrimSpace(string(got))
|
fn := strings.TrimSpace(string(got))
|
||||||
defer os.Remove(fn)
|
defer os.Remove(fn)
|
||||||
|
@ -76,7 +76,7 @@ func TestCrashDumpsAllThreads(t *testing.T) {
|
|||||||
|
|
||||||
testenv.MustHaveGoBuild(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
|
// 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
|
// 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
|
// affect this test and this is itself a test of a debug mode, it's not
|
||||||
|
@ -339,9 +339,11 @@ var allDesc = []Description{
|
|||||||
Kind: KindUint64,
|
Kind: KindUint64,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "/memory/classes/heap/stacks:bytes",
|
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. " +
|
||||||
Kind: KindUint64,
|
"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,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "/memory/classes/heap/unused:bytes",
|
Name: "/memory/classes/heap/unused:bytes",
|
||||||
@ -374,9 +376,13 @@ var allDesc = []Description{
|
|||||||
Kind: KindUint64,
|
Kind: KindUint64,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "/memory/classes/os-stacks:bytes",
|
Name: "/memory/classes/os-stacks:bytes",
|
||||||
Description: "Stack memory allocated by the underlying operating system.",
|
Description: "Stack memory allocated by the underlying operating system. " +
|
||||||
Kind: KindUint64,
|
"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,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "/memory/classes/other:bytes",
|
Name: "/memory/classes/other:bytes",
|
||||||
|
@ -318,7 +318,10 @@ Below is the full list of supported metrics, ordered lexicographically.
|
|||||||
|
|
||||||
/memory/classes/heap/stacks:bytes
|
/memory/classes/heap/stacks:bytes
|
||||||
Memory allocated from the heap that is reserved for stack space,
|
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/classes/heap/unused:bytes
|
||||||
Memory that is reserved for heap objects but is not currently
|
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
|
/memory/classes/os-stacks:bytes
|
||||||
Stack memory allocated by the underlying operating system.
|
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/classes/other:bytes
|
||||||
Memory used by execution trace buffers, structures for debugging
|
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 bytes of stack memory obtained from the OS.
|
||||||
//
|
//
|
||||||
// StackSys is StackInuse, plus any memory obtained directly
|
// 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
|
StackSys uint64
|
||||||
|
|
||||||
// Off-heap memory statistics.
|
// Off-heap memory statistics.
|
||||||
@ -347,6 +357,7 @@ func init() {
|
|||||||
// which is a snapshot as of the most recently completed garbage
|
// which is a snapshot as of the most recently completed garbage
|
||||||
// collection cycle.
|
// collection cycle.
|
||||||
func ReadMemStats(m *MemStats) {
|
func ReadMemStats(m *MemStats) {
|
||||||
|
_ = m.Alloc // nil check test before we switch stacks, see issue 61158
|
||||||
stopTheWorld(stwReadMemStats)
|
stopTheWorld(stwReadMemStats)
|
||||||
|
|
||||||
systemstack(func() {
|
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.
|
// isExportedRuntime reports whether name is an exported runtime function.
|
||||||
// It is only for runtime functions, so ASCII A-Z is fine.
|
// 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 {
|
func isExportedRuntime(name string) bool {
|
||||||
const n = len("runtime.")
|
const n = len("runtime.")
|
||||||
return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z'
|
return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z'
|
||||||
|
@ -6,7 +6,10 @@
|
|||||||
|
|
||||||
package syscall
|
package syscall
|
||||||
|
|
||||||
import "unsafe"
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
// readInt returns the size-bytes unsigned integer in native byte order at offset off.
|
// 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) {
|
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 {
|
if !ok {
|
||||||
break
|
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
|
continue
|
||||||
}
|
}
|
||||||
const namoff = uint64(unsafe.Offsetof(Dirent{}.Name))
|
const namoff = uint64(unsafe.Offsetof(Dirent{}.Name))
|
||||||
|
@ -62,7 +62,7 @@ func Clearenv() {
|
|||||||
for _, s := range Environ() {
|
for _, s := range Environ() {
|
||||||
// Environment variables can begin with =
|
// Environment variables can begin with =
|
||||||
// so start looking for the separator = at j=1.
|
// 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++ {
|
for j := 1; j < len(s); j++ {
|
||||||
if s[j] == '=' {
|
if s[j] == '=' {
|
||||||
Unsetenv(s[0:j])
|
Unsetenv(s[0:j])
|
||||||
|
@ -478,3 +478,16 @@ func SetNonblock(fd int, nonblocking bool) error {
|
|||||||
errno := fd_fdstat_set_flags(int32(fd), flags)
|
errno := fd_fdstat_set_flags(int32(fd), flags)
|
||||||
return errnoErr(errno)
|
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"
|
// arm/7:"ADDD",-"MULD"
|
||||||
// arm64:"FADDD",-"FMULD"
|
// arm64:"FADDD",-"FMULD"
|
||||||
// ppc64x:"FADD",-"FMUL"
|
// ppc64x:"FADD",-"FMUL"
|
||||||
|
// riscv64:"FADDD",-"FMULD"
|
||||||
return f * 2.0
|
return f * 2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
|
|||||||
// arm/7:"MULD",-"DIVD"
|
// arm/7:"MULD",-"DIVD"
|
||||||
// arm64:"FMULD",-"FDIVD"
|
// arm64:"FMULD",-"FDIVD"
|
||||||
// ppc64x:"FMUL",-"FDIV"
|
// ppc64x:"FMUL",-"FDIV"
|
||||||
|
// riscv64:"FMULD",-"FDIVD"
|
||||||
x := f1 / 16.0
|
x := f1 / 16.0
|
||||||
|
|
||||||
// 386/sse2:"MULSD",-"DIVSD"
|
// 386/sse2:"MULSD",-"DIVSD"
|
||||||
@ -36,6 +38,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
|
|||||||
// arm/7:"MULD",-"DIVD"
|
// arm/7:"MULD",-"DIVD"
|
||||||
// arm64:"FMULD",-"FDIVD"
|
// arm64:"FMULD",-"FDIVD"
|
||||||
// ppc64x:"FMUL",-"FDIVD"
|
// ppc64x:"FMUL",-"FDIVD"
|
||||||
|
// riscv64:"FMULD",-"FDIVD"
|
||||||
y := f2 / 0.125
|
y := f2 / 0.125
|
||||||
|
|
||||||
// 386/sse2:"ADDSD",-"DIVSD",-"MULSD"
|
// 386/sse2:"ADDSD",-"DIVSD",-"MULSD"
|
||||||
@ -43,6 +46,7 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
|
|||||||
// arm/7:"ADDD",-"MULD",-"DIVD"
|
// arm/7:"ADDD",-"MULD",-"DIVD"
|
||||||
// arm64:"FADDD",-"FMULD",-"FDIVD"
|
// arm64:"FADDD",-"FMULD",-"FDIVD"
|
||||||
// ppc64x:"FADD",-"FMUL",-"FDIV"
|
// ppc64x:"FADD",-"FMUL",-"FDIV"
|
||||||
|
// riscv64:"FADDD",-"FMULD",-"FDIVD"
|
||||||
z := f3 / 0.5
|
z := f3 / 0.5
|
||||||
|
|
||||||
return x, y, z
|
return x, y, z
|
||||||
|
@ -143,13 +143,13 @@ func fms(x, y, z float64) float64 {
|
|||||||
return math.FMA(x, y, -z)
|
return math.FMA(x, y, -z)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnma(x, y, z float64) float64 {
|
func fnms(x, y, z float64) float64 {
|
||||||
// riscv64:"FNMADDD"
|
// riscv64:"FNMSUBD",-"FNMADDD"
|
||||||
return math.FMA(-x, y, z)
|
return math.FMA(-x, y, z)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fnms(x, y, z float64) float64 {
|
func fnma(x, y, z float64) float64 {
|
||||||
// riscv64:"FNMSUBD"
|
// riscv64:"FNMADDD",-"FNMSUBD"
|
||||||
return math.FMA(x, -y, -z)
|
return math.FMA(x, -y, -z)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user