src/bytes/buffer.go | 6 +++--- src/cmd/cgo/internal/testfortran/fortran_test.go | 2 +- src/cmd/compile/internal/amd64/simdssa.go | 4 ++-- src/cmd/compile/internal/amd64/ssa.go | 17 +++++++++++++++++ src/cmd/compile/internal/base/debug.go | 1 + src/cmd/compile/internal/importer/gcimporter_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cmd/compile/internal/importer/genmeth_test.go | 86 ----------------------------------------------------- src/cmd/compile/internal/ir/class_string.go | 5 +++-- src/cmd/compile/internal/ir/op_string.go | 5 +++-- src/cmd/compile/internal/midway/analysis.go | 14 +++++++++++--- src/cmd/compile/internal/midway/deepcopy.go | 9 ++++++++- src/cmd/compile/internal/midway/rewrite.go | 52 +++++++++++++++++++++++++++++++--------------------- src/cmd/compile/internal/noder/unified.go | 10 ++-------- src/cmd/compile/internal/ssa/_gen/AMD64.rules | 8 ++++---- src/cmd/compile/internal/ssa/rewriteAMD64.go | 32 ++++++++++++++++---------------- src/cmd/compile/internal/ssagen/intrinsics.go | 24 ++++++++++++------------ src/cmd/compile/internal/ssagen/simdAMD64intrinsics.go | 40 ++++++++++++++++++++-------------------- src/cmd/compile/internal/syntax/operator_string.go | 8 ++++---- src/cmd/compile/internal/syntax/token_string.go | 8 ++++---- src/cmd/compile/internal/types/algkind_string.go | 5 +++-- src/cmd/compile/internal/types/kind_string.go | 5 +++-- src/cmd/compile/internal/types2/builtins.go | 8 +++++++- src/cmd/compile/internal/types2/cycles.go | 43 +++++++++++++++++++++++++++---------------- src/cmd/compile/internal/types2/infer.go | 2 +- src/cmd/compile/internal/types2/instantiate.go | 35 +++++++++++++++++++++-------------- src/cmd/compile/internal/types2/issues_test.go | 31 +++++++++++++++++++++++++++++++ src/cmd/compile/internal/types2/unify.go | 13 ++++++++++--- src/cmd/dist/test.go | 17 ++++++++++++++--- src/cmd/go/internal/doc/pkgsite.go | 12 +++++++++++- src/cmd/go/internal/load/pkg.go | 4 ++-- src/cmd/go/internal/modindex/read.go | 3 ++- src/cmd/go/internal/modload/init.go | 2 +- src/cmd/go/testdata/script/godebug_default.txt | 6 +++--- src/cmd/go/testdata/script/godebug_removed.txt | 36 ++++++++++++++++++++++++++---------- src/cmd/go/testdata/script/mod_removed_godebug.txt | 2 +- src/cmd/internal/obj/abi_string.go | 5 +++-- src/cmd/internal/obj/addrtype_string.go | 5 +++-- src/cmd/internal/script/scripttest/run.go | 33 +++++++++++++++++++++++++-------- src/cmd/link/internal/sym/symkind_string.go | 5 +++-- src/crypto/tls/ech.go | 4 ++-- src/crypto/tls/handshake_server.go | 8 ++++---- src/crypto/tls/tls_test.go | 8 ++++++++ src/database/sql/driver/driver.go | 3 +++ src/debug/dwarf/class_string.go | 5 +++-- src/debug/macho/reloctype_string.go | 20 ++++++++++++-------- src/encoding/json/decode_test.go | 26 +++++++++++++------------- src/encoding/json/v2/arshal_default.go | 20 ++++++++++---------- src/encoding/json/v2/arshal_inlined.go => src/encoding/json/v2/arshal_embedded.go | 26 +++++++++++++------------- src/encoding/json/v2/arshal_test.go | 842 +++++++++++++++++++++++++++--------------------------- src/encoding/json/v2/doc.go | 18 +++++++++--------- src/encoding/json/v2/example_test.go | 18 +++++++++--------- src/encoding/json/v2/fields.go | 86 +++++++++++++++++++++++++++--------------------------- src/encoding/json/v2/fields_test.go | 194 +++++++++++++++++++++++++++--------------------------- src/encoding/json/v2/options.go | 2 +- src/encoding/json/v2_decode_test.go | 26 +++++++++++++------------- src/go/constant/kind_string.go | 5 +++-- src/go/internal/gcimporter/gcimporter_test.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/go/internal/gcimporter/genmeth_test.go | 92 ----------------------------------------------------- src/go/types/builtins.go | 8 +++++++- src/go/types/cycles.go | 43 +++++++++++++++++++++++++++---------------- src/go/types/generate_test.go | 2 +- src/go/types/infer.go | 2 +- src/go/types/instantiate.go | 35 +++++++++++++++++++++-------------- src/go/types/issues_test.go | 31 +++++++++++++++++++++++++++++++ src/go/types/unify.go | 13 ++++++++++--- src/html/template/attr_string.go | 5 +++-- src/html/template/delim_string.go | 5 +++-- src/html/template/element_string.go | 5 +++-- src/html/template/jsctx_string.go | 5 +++-- src/html/template/state_string.go | 5 +++-- src/html/template/urlpart_string.go | 5 +++-- src/internal/buildcfg/exp.go | 1 - src/internal/filepathlite/path_windows.go | 47 ++++++++++++++++++++++++++++++----------------- src/internal/goexperiment/exp_genericmethods_off.go | 8 -------- src/internal/goexperiment/exp_genericmethods_on.go | 8 -------- src/internal/goexperiment/flags.go | 3 --- src/internal/pkgbits/syncmarker_string.go | 8 ++++---- src/internal/types/testdata/fixedbugs/issue77905.go | 30 ++++++++++++++++++++++++++++++ src/internal/types/testdata/fixedbugs/issue79265.go | 17 +++++++++++++++++ src/math/big/accuracy_string.go | 8 ++++---- src/math/big/roundingmode_string.go | 5 +++-- src/math/rand/v2/n.go | 20 -------------------- src/math/rand/v2/rand.go | 15 +++++++++++---- src/net/http/internal/http2/clientconn_test.go | 16 ++++++++-------- src/net/http/internal/http2/config_test.go | 17 +++++++++-------- src/net/http/internal/http2/connframes_test.go | 4 ++-- src/net/http/internal/http2/server_push_test.go | 28 ++++++++++++++-------------- src/net/http/internal/http2/server_test.go | 557 +++++++++++++++++++++++++++++------------------------ src/net/http/internal/http2/synctest_test.go | 12 ++---------- src/net/http/internal/http2/transport.go | 12 +++--------- src/net/http/internal/http2/transport_test.go | 333 +++++++++++++++++++++++++++++++---------------------- src/path/filepath/path_test.go | 26 +++++++++++++++++++++++--- src/runtime/panic.go | 2 +- src/runtime/panic_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/runtime/proc.go | 7 +++++++ src/runtime/runtime1.go | 26 ++++++++++++++++++++++++++ src/runtime/slice.go | 5 +++-- src/simd/archsimd/_gen/midway/intersect_simd_ops.go | 195 ++++++++++++++++++++++++++++++----------------------- src/simd/archsimd/_gen/simdgen/arch.go | 2 ++ src/simd/archsimd/_gen/simdgen/ops/Moves/go_amd64.yaml | 146 +++++++++++++++++++++++++++++++++++++++++++++-------- src/simd/archsimd/internal/simd_test/simd_test.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/simd/endianness_test.go | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/simd/example_test.go | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/simd/internal/bridge/import_hook.go | 13 +++++++++++++ src/simd/internal/bridge/simd_emulated.go | 24 +++++++++++++++--------- src/simd/ip_test.go | 309 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/simd/simd.go => src/simd/simd_stubs.go | 89 ----------------------------------------------------- src/simd/simd_emulated.go | 111 +++++++---------------------------------------------- src/simd/simd_types.go | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/simd/sizeof_test.go | 33 +++++++++++++++++++++++++++++++++ src/simd/testdata/mains/compiles.go | 26 ++++++++++++++++++++++++++ src/simd/testdata/mains/errors.go | 25 +++++++++++++++++++++++++ src/simd/testdata/simd/size.go | 22 ++++++++++++++++++++++ src/simd/testdata/v.go | 21 +++++++++++++++++++++ src/strings/builder.go | 2 +- src/syscall/exec_libc2.go | 5 +++-- src/syscall/syscall_darwin.go | 22 ++++++++++++++++++++++ test/codegen/append.go | 7 ++++--- test/codegen/append_freegc.go | 5 ++++- test/fixedbugs/issue19658.go | 2 +- test/fixedbugs/issue80004.go | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++ test/genmeth.go | 2 +- test/genmeth1.go | 2 +- test/genmeth2.go | 2 +- diff --git a/src/bytes/buffer.go b/src/bytes/buffer.go index 6cb4d6a8f66ebf556eb4543f495ec8d537889fd9..03f17d624daa239308b448de9db0e9146c18c8e9 100644 --- a/src/bytes/buffer.go +++ b/src/bytes/buffer.go @@ -311,9 +311,9 @@ return nil } // WriteRune appends the UTF-8 encoding of Unicode code point r to the -// buffer, returning its length and an error, which is always nil but is -// included to match [bufio.Writer]'s WriteRune. The buffer is grown as needed; -// if it becomes too large, WriteRune will panic with [ErrTooLarge]. +// buffer, returning the number of bytes written and a nil error. The nil +// error is included to match [bufio.Writer]'s WriteRune. The buffer is grown +// as needed; if it becomes too large, WriteRune will panic with [ErrTooLarge]. func (b *Buffer) WriteRune(r rune) (n int, err error) { // Compare as uint32 to correctly handle negative runes. if uint32(r) < utf8.RuneSelf { diff --git a/src/cmd/cgo/internal/testfortran/fortran_test.go b/src/cmd/cgo/internal/testfortran/fortran_test.go index 1decd138bc4a316937492a936bbc4d6c1d5eda28..771590e34adaa507dd53fd87cb457bbeabd8aad3 100644 --- a/src/cmd/cgo/internal/testfortran/fortran_test.go +++ b/src/cmd/cgo/internal/testfortran/fortran_test.go @@ -47,7 +47,7 @@ libExt := "so" switch runtime.GOOS { case "darwin": libExt = "dylib" - case "aix", "openbsd": + case "aix", "openbsd", "windows": libExt = "a" } libName := "libgfortran." + libExt diff --git a/src/cmd/compile/internal/amd64/simdssa.go b/src/cmd/compile/internal/amd64/simdssa.go index f74cd4c28422cf138495e0601623b840f71046af..12f20f15ea4628769220ec9781c3fecb7975e4ff 100644 --- a/src/cmd/compile/internal/amd64/simdssa.go +++ b/src/cmd/compile/internal/amd64/simdssa.go @@ -1717,13 +1717,13 @@ case ssa.OpAMD64VPINSRD128, ssa.OpAMD64VPINSRQ128, ssa.OpAMD64VPINSRB128, ssa.OpAMD64VPINSRW128: - p = simdVgpvImm8(s, v) + p = simdVgpvImm(s, v) case ssa.OpAMD64VPEXTRD128, ssa.OpAMD64VPEXTRQ128, ssa.OpAMD64VPEXTRB128, ssa.OpAMD64VPEXTRW128: - p = simdVgpImm8(s, v) + p = simdVgpImm(s, v) case ssa.OpAMD64VPALIGNRMasked256, ssa.OpAMD64VPALIGNRMasked512, diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go index 201463e65c788b1af67b4fb34d2cca0a52e879c3..cd5a2be7057c59254d71f19eb54e630e03e49da4 100644 --- a/src/cmd/compile/internal/amd64/ssa.go +++ b/src/cmd/compile/internal/amd64/ssa.go @@ -2093,6 +2093,15 @@ p.To.Type = obj.TYPE_REG p.To.Reg = simdReg(v) return p } +func simdVgpvImm(s *ssagen.State, v *ssa.Value) *obj.Prog { + // within simdgen, the choice of intrinsic shape and the output + // intruction format are linked. In the case of VgpImm, there is + // a difference in the intrinsic, but no difference in the + // instruction, it is just like VgpvImm8. + // + // See also, simdVgpImm and simdVgpImm8 + return simdVgpvImm8(s, v) +} // Example instruction: VPCMPD $1, Z1, Z2, K1 func simdV2kImm8(s *ssagen.State, v *ssa.Value) *obj.Prog { @@ -2193,6 +2202,14 @@ p.AddRestSourceReg(simdReg(v.Args[0])) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() return p +} + +func simdVgpImm(s *ssagen.State, v *ssa.Value) *obj.Prog { + // within simdgen, the choice of intrinsic shape and the output + // intruction format are linked. In the case of VgpImm, there is + // a difference in the intrinsic, but no difference in the + // instruction, it is just like VgpImm8. + return simdVgpImm8(s, v) } // Currently unused diff --git a/src/cmd/compile/internal/base/debug.go b/src/cmd/compile/internal/base/debug.go index 0b5f9d2cc02fbe77b5e8d9b82bbb39c57d0f18f3..a4cbe724e88c2f99f251f268726733ea0730ddef 100644 --- a/src/cmd/compile/internal/base/debug.go +++ b/src/cmd/compile/internal/base/debug.go @@ -68,6 +68,7 @@ PCTab string `help:"print named pc-value table\nOne of: pctospadj, pctofile, pctoline, pctoinline, pctopcdata"` Panic int `help:"show all compiler panics"` Reshape int `help:"print information about expression reshaping"` Shapify int `help:"print information about shaping recursive types"` + Simd int `help:"print information about simd analysis and code transformation" concurrent:"ok"` Slice int `help:"print information about slice compilation"` SoftFloat int `help:"force compiler to emit soft-float code" concurrent:"ok"` StaticCopy int `help:"print information about missed static copies" concurrent:"ok"` diff --git a/src/cmd/compile/internal/importer/gcimporter_test.go b/src/cmd/compile/internal/importer/gcimporter_test.go index 11e4ee6b583b6af839dd7e4938253a4891994c98..e81a71f266eb90b9349f1ced4b2395db9930099a 100644 --- a/src/cmd/compile/internal/importer/gcimporter_test.go +++ b/src/cmd/compile/internal/importer/gcimporter_test.go @@ -720,3 +720,71 @@ if _, err := check("p", pSrc, importer); err != nil { t.Errorf("Check failed: %v", err) } } + +func TestGenMeth(t *testing.T) { + testenv.MustHaveGoBuild(t) + + // This package only handles gc export data. + if runtime.Compiler != "gc" { + t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) + } + + tmpdir := t.TempDir() + testoutdir := filepath.Join(tmpdir, "testdata") + if err := os.Mkdir(testoutdir, 0700); err != nil { + t.Fatalf("making output dir: %v", err) + } + + compile(t, "testdata", "genmeth.go", testoutdir, nil) + + genmeth, err := Import(make(map[string]*types2.Package), "./testdata/genmeth", tmpdir, nil) + if err != nil { + t.Fatal(err) + } + + check := func(pkgname, src string, imports importMap) (*types2.Package, error) { + f, err := syntax.Parse(syntax.NewFileBase(pkgname), strings.NewReader(src), nil, nil, 0) + if err != nil { + return nil, err + } + config := &types2.Config{ + Importer: imports, + } + return config.Check(pkgname, []*syntax.File{f}, nil) + } + + const pSrc = `package p + +import "genmeth" + +func _() { + var ex func(int) genmeth.List[int] + var fl func(genmeth.List[int]) genmeth.List[int] + + var l genmeth.List[int] + l = l.Map(ex).FlatMap(fl) + + var bl genmeth.BiList[int, any] + bl = bl.MapKeys(ex).Flip().FlatMapValues(fl).Flip() + + var id func(int) int + + var op genmeth.Option[int] + var _ int = op.MapIfPresent(id).Get() + + var ol genmeth.OrderedList[int] + var _ int = ol.Min().Get() + + var b genmeth.Box[int] + b.Set(42) + var _ int = b.Get() +} +` + + importer := importMap{ + "genmeth": genmeth, + } + if _, err := check("p", pSrc, importer); err != nil { + t.Errorf("Check failed: %v", err) + } +} diff --git a/src/cmd/compile/internal/importer/genmeth_test.go b/src/cmd/compile/internal/importer/genmeth_test.go deleted file mode 100644 index fa827a7adf204c1ac4f4c1e51a6a3c21438a1b9f..0000000000000000000000000000000000000000 --- a/src/cmd/compile/internal/importer/genmeth_test.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2026 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build goexperiment.genericmethods - -package importer - -import ( - "cmd/compile/internal/syntax" - "cmd/compile/internal/types2" - "internal/testenv" - "os" - "path/filepath" - "runtime" - "strings" - "testing" -) - -func TestGenMeth(t *testing.T) { - testenv.MustHaveGoBuild(t) - - // This package only handles gc export data. - if runtime.Compiler != "gc" { - t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) - } - - tmpdir := t.TempDir() - testoutdir := filepath.Join(tmpdir, "testdata") - if err := os.Mkdir(testoutdir, 0700); err != nil { - t.Fatalf("making output dir: %v", err) - } - - compile(t, "testdata", "genmeth.go", testoutdir, nil) - - genmeth, err := Import(make(map[string]*types2.Package), "./testdata/genmeth", tmpdir, nil) - if err != nil { - t.Fatal(err) - } - - check := func(pkgname, src string, imports importMap) (*types2.Package, error) { - f, err := syntax.Parse(syntax.NewFileBase(pkgname), strings.NewReader(src), nil, nil, 0) - if err != nil { - return nil, err - } - config := &types2.Config{ - Importer: imports, - } - return config.Check(pkgname, []*syntax.File{f}, nil) - } - - const pSrc = `package p - -import "genmeth" - -func _() { - var ex func(int) genmeth.List[int] - var fl func(genmeth.List[int]) genmeth.List[int] - - var l genmeth.List[int] - l = l.Map(ex).FlatMap(fl) - - var bl genmeth.BiList[int, any] - bl = bl.MapKeys(ex).Flip().FlatMapValues(fl).Flip() - - var id func(int) int - - var op genmeth.Option[int] - var _ int = op.MapIfPresent(id).Get() - - var ol genmeth.OrderedList[int] - var _ int = ol.Min().Get() - - var b genmeth.Box[int] - b.Set(42) - var _ int = b.Get() -} -` - - importer := importMap{ - "genmeth": genmeth, - } - if _, err := check("p", pSrc, importer); err != nil { - t.Errorf("Check failed: %v", err) - } -} diff --git a/src/cmd/compile/internal/ir/class_string.go b/src/cmd/compile/internal/ir/class_string.go index 11a94c004701ba4f6238217b145b2972abbea1a7..bd14c417b2b639375083d8b73bd8e168bf2cb58f 100644 --- a/src/cmd/compile/internal/ir/class_string.go +++ b/src/cmd/compile/internal/ir/class_string.go @@ -23,8 +23,9 @@ var _Class_index = [...]uint8{0, 4, 11, 16, 25, 31, 40, 50, 55} func (i Class) String() string { - if i >= Class(len(_Class_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_Class_index)-1 { return "Class(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Class_name[_Class_index[i]:_Class_index[i+1]] + return _Class_name[_Class_index[idx]:_Class_index[idx+1]] } diff --git a/src/cmd/compile/internal/ir/op_string.go b/src/cmd/compile/internal/ir/op_string.go index f042ad84a405d0ebbfce7733dcc01279957a5055..6c1c99a35d840f5ac1d697fbd0f3a0359f4523f2 100644 --- a/src/cmd/compile/internal/ir/op_string.go +++ b/src/cmd/compile/internal/ir/op_string.go @@ -166,8 +166,9 @@ var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 129, 141, 143, 146, 156, 163, 170, 177, 181, 185, 193, 201, 210, 213, 218, 223, 230, 237, 243, 252, 260, 268, 274, 278, 287, 294, 298, 301, 308, 314, 317, 323, 330, 338, 342, 349, 357, 359, 361, 363, 365, 367, 369, 374, 379, 387, 390, 399, 402, 406, 414, 421, 430, 443, 446, 449, 452, 455, 458, 461, 467, 470, 473, 479, 483, 486, 490, 495, 500, 507, 512, 516, 521, 529, 537, 543, 552, 563, 575, 582, 586, 593, 601, 604, 607, 611, 615, 622, 631, 642, 657, 669, 685, 693, 702, 707, 712, 716, 724, 729, 733, 736, 740, 742, 747, 749, 754, 760, 766, 772, 778, 785, 793, 797, 802, 806, 811, 819, 825, 832, 845, 854, 869, 878, 892, 907, 918, 926, 930, 941, 944} func (i Op) String() string { - if i >= Op(len(_Op_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_Op_index)-1 { return "Op(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Op_name[_Op_index[i]:_Op_index[i+1]] + return _Op_name[_Op_index[idx]:_Op_index[idx+1]] } diff --git a/src/cmd/compile/internal/midway/analysis.go b/src/cmd/compile/internal/midway/analysis.go index d9779571a78b20f79ea2f329f6da0d4d07f68a39..30b4288d10238e5b78c6772597111d29c5e17ecf 100644 --- a/src/cmd/compile/internal/midway/analysis.go +++ b/src/cmd/compile/internal/midway/analysis.go @@ -5,6 +5,7 @@ package midway import ( + "cmd/compile/internal/base" "cmd/compile/internal/syntax" "cmd/compile/internal/types2" ) @@ -74,11 +75,9 @@ if fn.Body == nil { return false } // Walk the body and check identifiers + // This will also note any variable references that are dependent. found := false syntax.Inspect(fn.Body, func(n syntax.Node) bool { - if found { - return false - } if id, ok := n.(*syntax.Name); ok { obj := a.info.Uses[id] if obj == nil { @@ -98,6 +97,12 @@ return false } } if a.isDependentType(obj.Type()) { + // Whatever this is, it makes the outer object dependent. + // If this is a variable with dependent type, mark the variable as + // dependent, so that references to it become dependent. + if obj, ok := obj.(*types2.Var); ok { + a.dependentObj[obj] = true + } found = true return false } @@ -144,6 +149,9 @@ isDep = true } if isDep { + if base.Debug.Simd > 0 { + base.Warn("%v is simd-dependent", obj) + } a.dependentObj[obj] = true } return isDep diff --git a/src/cmd/compile/internal/midway/deepcopy.go b/src/cmd/compile/internal/midway/deepcopy.go index 40f41582d93a18a1d4fe51c50178089b7fd4fe94..ba00742cc8a749823404be60bde8ef2abc4a5474 100644 --- a/src/cmd/compile/internal/midway/deepcopy.go +++ b/src/cmd/compile/internal/midway/deepcopy.go @@ -8,6 +8,7 @@ import ( "fmt" "strings" + "cmd/compile/internal/base" "cmd/compile/internal/syntax" "cmd/compile/internal/types2" ) @@ -75,6 +76,9 @@ if c.analyzer.dependentObj[obj] || isBaseSimdTypeObj(obj) { newId := syntax.NewName(id.Pos(), id.Value+c.suffix) // Object link will be handled manually in deepcopier Use/Def mapper + if base.Debug.Simd > 0 { + base.Warn("Rewriting name %s to %s", id.Value, newId.Value) + } return newId } return nil @@ -94,7 +98,7 @@ if isBaseSimdTypeObj(obj) { // if it is a name, that means that this is in the simd package, // and the name must be replaced with a selector referencing - // the architecture-dependent packages.. + // the architecture-dependent packages. name := id.Value width := nameToElemBitWidth(name) if width > 0 { @@ -125,6 +129,9 @@ if c.analyzer.dependentObj[obj] { newId := syntax.NewName(id.Pos(), id.Value+c.suffix) // Object link will be handled manually in deepcopier Use/Def mapper + if base.Debug.Simd > 0 { + base.Warn("Rewriting name %s to %s", id.Value, newId.Value) + } return newId } return nil diff --git a/src/cmd/compile/internal/midway/rewrite.go b/src/cmd/compile/internal/midway/rewrite.go index 34269e621b5ea2d707b726e4aa36c5b0b94b15b7..796635c708925d16e2b432c3a9b910e3ee990638 100644 --- a/src/cmd/compile/internal/midway/rewrite.go +++ b/src/cmd/compile/internal/midway/rewrite.go @@ -93,7 +93,12 @@ continue } if r.analyzer.HasDependentSignature(sig) { - // Drop dependent signatures entirely + if o := r.info.Defs[d.Name]; o != nil && !o.Exported() { + // Drop unexported dependent signatures entirely + continue + } + d.Body = r.blockOf(d.Pos(), r.panicStmt(d.Pos(), "unexpected call of original function rewritten to specialized SIMD")) + newDecls = append(newDecls, d) continue } @@ -102,17 +107,9 @@ d.Body = r.createDispatcherBody(d, sig) newDecls = append(newDecls, d) case *syntax.VarDecl: - // Filter specs conceptually based on dependents - keep := false - for _, name := range d.NameList { - if !r.analyzer.dependentObj[r.info.Defs[name]] { - keep = true - break // Keep entire var decl if any name is clean, else drop - } - } - if keep { - newDecls = append(newDecls, d) - } + // Keep var decls even if rewritten, so that pre-rewrite code parses correctly. + // TODO figure out how to deal with side-effects in initializers. + newDecls = append(newDecls, d) case *syntax.TypeDecl: if !r.analyzer.dependentObj[r.info.Defs[d.Name]] || r.analyzer.inSimd { newDecls = append(newDecls, d) @@ -285,20 +282,33 @@ caseClause.SetPos(d.Pos()) switchStmt.Body = append(switchStmt.Body, caseClause) } + panicStmt := r.panicStmt(d.Pos(), "unsupported vector size in simd-rewritten code") + return r.blockOf(d.Pos(), switchStmt, panicStmt) +} + +func (r *Rewriter) blockOf(p syntax.Pos, stmts ...syntax.Stmt) *syntax.BlockStmt { + for _, s := range stmts { + s.SetPos(p) + } + blockStmt := &syntax.BlockStmt{List: stmts} + blockStmt.SetPos(p) + return blockStmt +} + +func (r *Rewriter) panicStmt(p syntax.Pos, unquotedMessage string) *syntax.ExprStmt { + pe := func(e syntax.Expr) syntax.Expr { + e.SetPos(p) + return e + } fnName := "panic" - fnIdent := pe(syntax.NewName(d.Pos(), fnName)) - + fnIdent := pe(syntax.NewName(p, fnName)) callExpr := pe(&syntax.CallExpr{ Fun: fnIdent, - ArgList: []syntax.Expr{pe(&syntax.BasicLit{Value: "\"unsupported vector size in simd-rewritten code\"", Kind: syntax.StringLit})}, + ArgList: []syntax.Expr{pe(&syntax.BasicLit{Value: `"` + unquotedMessage + `"`, Kind: syntax.StringLit})}, }) - panicStmt := &syntax.ExprStmt{X: callExpr} - blockStmt := &syntax.BlockStmt{List: []syntax.Stmt{ps(switchStmt), ps(panicStmt)}} - - blockStmt.SetPos(d.Pos()) - - return blockStmt + panicStmt.SetPos(p) + return panicStmt } func (r *Rewriter) generateForSize(fileAST *syntax.File, k int, newDecls []syntax.Decl) []syntax.Decl { diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index ca601ef9cc1a55c85aad660082def8766fa373d3..320fb3c75ac9adfea2ac035792716d6f64829ab7 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.go @@ -7,7 +7,6 @@ import ( "cmp" "fmt" - "internal/buildcfg" "internal/pkgbits" "internal/types/errors" "io" @@ -26,13 +25,8 @@ "cmd/internal/src" ) // uirVersion is the unified IR version to use for encoding/decoding. -// Use V4 for generic methods if the GOEXPERIMENT is enabled. -var uirVersion = func() pkgbits.Version { - if buildcfg.Experiment.GenericMethods { - return pkgbits.V4 - } - return pkgbits.V3 -}() +// Use V4 for generic methods. +const uirVersion = pkgbits.V4 // localPkgReader holds the package reader used for reading the local // package. It exists so the unified IR linker can refer back to it diff --git a/src/cmd/compile/internal/ssa/_gen/AMD64.rules b/src/cmd/compile/internal/ssa/_gen/AMD64.rules index bdf0230de0aa9236ea03036f9cee6d9447e215d8..a58f5acb0d337f2184258edd712939eafa7a5c49 100644 --- a/src/cmd/compile/internal/ssa/_gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/_gen/AMD64.rules @@ -1875,10 +1875,10 @@ (EQ (VPTEST x:(VPANDN(D|Q)512 j k) y) yes no) && x == y && x.Uses == 2 => (ULT (VPTEST k j) yes no) // AndNot has swapped its operand order // remove flags → bool → flags roundtrip // Only do it if the flag generating instruction is local otherwise the likelihood flagalloc won't undo this optimization and makes things worse are slim. -(NE t:(TESTQ x:(MOVBQZX s:(SET(EQ|NE|L|G|LE|GE|A|B|AE|BE|EQF|NEF|GF|GEF) flags)) x) yes no) && t.Block == s.Block => ((EQ|NE|LT|GT|LE|GE|UGT|ULT|UGE|ULE|EQF|NEF|UGE|UGT) flags yes no) -(NE t:(TESTL x:(MOVBQZX s:(SET(EQ|NE|L|G|LE|GE|A|B|AE|BE|EQF|NEF|GF|GEF) flags)) x) yes no) && t.Block == s.Block => ((EQ|NE|LT|GT|LE|GE|UGT|ULT|UGE|ULE|EQF|NEF|UGE|UGT) flags yes no) -(NE t:(TESTW x:(MOVBQZX s:(SET(EQ|NE|L|G|LE|GE|A|B|AE|BE|EQF|NEF|GF|GEF) flags)) x) yes no) && t.Block == s.Block => ((EQ|NE|LT|GT|LE|GE|UGT|ULT|UGE|ULE|EQF|NEF|UGE|UGT) flags yes no) -(NE t:(TESTB s:(SET(EQ|NE|L|G|LE|GE|A|B|AE|BE|EQF|NEF|GF|GEF) flags) s) yes no) && t.Block == s.Block => ((EQ|NE|LT|GT|LE|GE|UGT|ULT|UGE|ULE|EQF|NEF|UGE|UGT) flags yes no) +(NE t:(TESTQ x:(MOVBQZX s:(SET(EQ|NE|L|G|LE|GE|A|B|AE|BE|EQF|NEF|GF|GEF) flags)) x) yes no) && t.Block == s.Block => ((EQ|NE|LT|GT|LE|GE|UGT|ULT|UGE|ULE|EQF|NEF|UGT|UGE) flags yes no) +(NE t:(TESTL x:(MOVBQZX s:(SET(EQ|NE|L|G|LE|GE|A|B|AE|BE|EQF|NEF|GF|GEF) flags)) x) yes no) && t.Block == s.Block => ((EQ|NE|LT|GT|LE|GE|UGT|ULT|UGE|ULE|EQF|NEF|UGT|UGE) flags yes no) +(NE t:(TESTW x:(MOVBQZX s:(SET(EQ|NE|L|G|LE|GE|A|B|AE|BE|EQF|NEF|GF|GEF) flags)) x) yes no) && t.Block == s.Block => ((EQ|NE|LT|GT|LE|GE|UGT|ULT|UGE|ULE|EQF|NEF|UGT|UGE) flags yes no) +(NE t:(TESTB s:(SET(EQ|NE|L|G|LE|GE|A|B|AE|BE|EQF|NEF|GF|GEF) flags) s) yes no) && t.Block == s.Block => ((EQ|NE|LT|GT|LE|GE|UGT|ULT|UGE|ULE|EQF|NEF|UGT|UGE) flags yes no) (CMOVQNE yes no t:(TESTQ x:(MOVBQZX s:(SET(EQ|NE|L|G|LE|GE|A|B|AE|BE|EQF|NEF|GF|GEF) flags)) x)) && t.Block == s.Block => (CMOVQ(EQ|NE|LT|GT|LE|GE|HI|CS|CC|LS|EQF|NEF|GTF|GEF) yes no flags) (CMOVQNE yes no t:(TESTL x:(MOVBQZX s:(SET(EQ|NE|L|G|LE|GE|A|B|AE|BE|EQF|NEF|GF|GEF) flags)) x)) && t.Block == s.Block => (CMOVQ(EQ|NE|LT|GT|LE|GE|HI|CS|CC|LS|EQF|NEF|GTF|GEF) yes no flags) diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index c083fd8c61f9703db25e4f1945e6e3992df6c7db..ce7b6b003b7f99303fd851cd7ca1407f7b70d9ef 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -108727,7 +108727,7 @@ break } // match: (NE t:(TESTQ x:(MOVBQZX s:(SETGF flags)) x) yes no) // cond: t.Block == s.Block - // result: (UGE flags yes no) + // result: (UGT flags yes no) for b.Controls[0].Op == OpAMD64TESTQ { t := b.Controls[0] _ = t.Args[1] @@ -108746,14 +108746,14 @@ flags := s.Args[0] if x != t_1 || !(t.Block == s.Block) { continue } - b.resetWithControl(BlockAMD64UGE, flags) + b.resetWithControl(BlockAMD64UGT, flags) return true } break } // match: (NE t:(TESTQ x:(MOVBQZX s:(SETGEF flags)) x) yes no) // cond: t.Block == s.Block - // result: (UGT flags yes no) + // result: (UGE flags yes no) for b.Controls[0].Op == OpAMD64TESTQ { t := b.Controls[0] _ = t.Args[1] @@ -108772,7 +108772,7 @@ flags := s.Args[0] if x != t_1 || !(t.Block == s.Block) { continue } - b.resetWithControl(BlockAMD64UGT, flags) + b.resetWithControl(BlockAMD64UGE, flags) return true } break @@ -109091,7 +109091,7 @@ break } // match: (NE t:(TESTL x:(MOVBQZX s:(SETGF flags)) x) yes no) // cond: t.Block == s.Block - // result: (UGE flags yes no) + // result: (UGT flags yes no) for b.Controls[0].Op == OpAMD64TESTL { t := b.Controls[0] _ = t.Args[1] @@ -109110,14 +109110,14 @@ flags := s.Args[0] if x != t_1 || !(t.Block == s.Block) { continue } - b.resetWithControl(BlockAMD64UGE, flags) + b.resetWithControl(BlockAMD64UGT, flags) return true } break } // match: (NE t:(TESTL x:(MOVBQZX s:(SETGEF flags)) x) yes no) // cond: t.Block == s.Block - // result: (UGT flags yes no) + // result: (UGE flags yes no) for b.Controls[0].Op == OpAMD64TESTL { t := b.Controls[0] _ = t.Args[1] @@ -109136,7 +109136,7 @@ flags := s.Args[0] if x != t_1 || !(t.Block == s.Block) { continue } - b.resetWithControl(BlockAMD64UGT, flags) + b.resetWithControl(BlockAMD64UGE, flags) return true } break @@ -109455,7 +109455,7 @@ break } // match: (NE t:(TESTW x:(MOVBQZX s:(SETGF flags)) x) yes no) // cond: t.Block == s.Block - // result: (UGE flags yes no) + // result: (UGT flags yes no) for b.Controls[0].Op == OpAMD64TESTW { t := b.Controls[0] _ = t.Args[1] @@ -109474,14 +109474,14 @@ flags := s.Args[0] if x != t_1 || !(t.Block == s.Block) { continue } - b.resetWithControl(BlockAMD64UGE, flags) + b.resetWithControl(BlockAMD64UGT, flags) return true } break } // match: (NE t:(TESTW x:(MOVBQZX s:(SETGEF flags)) x) yes no) // cond: t.Block == s.Block - // result: (UGT flags yes no) + // result: (UGE flags yes no) for b.Controls[0].Op == OpAMD64TESTW { t := b.Controls[0] _ = t.Args[1] @@ -109500,7 +109500,7 @@ flags := s.Args[0] if x != t_1 || !(t.Block == s.Block) { continue } - b.resetWithControl(BlockAMD64UGT, flags) + b.resetWithControl(BlockAMD64UGE, flags) return true } break @@ -109771,7 +109771,7 @@ break } // match: (NE t:(TESTB s:(SETGF flags) s) yes no) // cond: t.Block == s.Block - // result: (UGE flags yes no) + // result: (UGT flags yes no) for b.Controls[0].Op == OpAMD64TESTB { t := b.Controls[0] _ = t.Args[1] @@ -109786,14 +109786,14 @@ flags := s.Args[0] if s != t_1 || !(t.Block == s.Block) { continue } - b.resetWithControl(BlockAMD64UGE, flags) + b.resetWithControl(BlockAMD64UGT, flags) return true } break } // match: (NE t:(TESTB s:(SETGEF flags) s) yes no) // cond: t.Block == s.Block - // result: (UGT flags yes no) + // result: (UGE flags yes no) for b.Controls[0].Op == OpAMD64TESTB { t := b.Controls[0] _ = t.Args[1] @@ -109808,7 +109808,7 @@ flags := s.Args[0] if s != t_1 || !(t.Block == s.Block) { continue } - b.resetWithControl(BlockAMD64UGT, flags) + b.resetWithControl(BlockAMD64UGE, flags) return true } break diff --git a/src/cmd/compile/internal/ssagen/intrinsics.go b/src/cmd/compile/internal/ssagen/intrinsics.go index 8b7be728096ba90ef83fd30ff50fa0c8dc2c7d73..f3f1c5663ba527c426dd4086b4d94d85fdbd4b1f 100644 --- a/src/cmd/compile/internal/ssagen/intrinsics.go +++ b/src/cmd/compile/internal/ssagen/intrinsics.go @@ -2241,48 +2241,48 @@ }) } } -func opLen1Imm(op ssa.Op, t *types.Type, offset int, immMax int) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { +func opLen1Imm(op ssa.Op, t *types.Type, offset int, immMax uint64) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { - if args[1].Op == ssa.OpConst8 || args[1].Op == ssa.OpConst64 { + if (args[1].Op == ssa.OpConst8 || args[1].Op == ssa.OpConst64) && uint64(args[1].AuxInt) <= immMax { return s.newValue1I(op, t, int64(int8(args[1].AuxInt<= Operator(len(_Operator_index)-1) { - return "Operator(" + strconv.FormatInt(int64(i+1), 10) + ")" + idx := int(i) - 1 + if i < 1 || idx >= len(_Operator_index)-1 { + return "Operator(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Operator_name[_Operator_index[i]:_Operator_index[i+1]] + return _Operator_name[_Operator_index[idx]:_Operator_index[idx+1]] } diff --git a/src/cmd/compile/internal/syntax/token_string.go b/src/cmd/compile/internal/syntax/token_string.go index ef295eb24b2bc9acb4b0d44ca9893f3bcb5067c2..eaad90c09724310b13b55a50968e9ee31c6de4e1 100644 --- a/src/cmd/compile/internal/syntax/token_string.go +++ b/src/cmd/compile/internal/syntax/token_string.go @@ -62,9 +62,9 @@ var _token_index = [...]uint8{0, 3, 7, 14, 16, 19, 23, 24, 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 42, 47, 51, 55, 60, 68, 75, 80, 84, 95, 98, 102, 104, 108, 110, 116, 125, 128, 135, 140, 146, 152, 158, 164, 168, 171, 171} func (i token) String() string { - i -= 1 - if i >= token(len(_token_index)-1) { - return "token(" + strconv.FormatInt(int64(i+1), 10) + ")" + idx := int(i) - 1 + if i < 1 || idx >= len(_token_index)-1 { + return "token(" + strconv.FormatInt(int64(i), 10) + ")" } - return _token_name[_token_index[i]:_token_index[i+1]] + return _token_name[_token_index[idx]:_token_index[idx+1]] } diff --git a/src/cmd/compile/internal/types/algkind_string.go b/src/cmd/compile/internal/types/algkind_string.go index ca65a72c29acd4f4008d5f9c2ffdeaf3fcd14d02..d48940dfc1ab182a0d0dfdb3c79d5f7d0d350795 100644 --- a/src/cmd/compile/internal/types/algkind_string.go +++ b/src/cmd/compile/internal/types/algkind_string.go @@ -33,8 +33,9 @@ var _AlgKind_index = [...]uint8{0, 3, 7, 12, 15, 19, 23, 28, 33, 38, 44, 50, 55, 63, 70, 77, 83, 90, 97} func (i AlgKind) String() string { - if i < 0 || i >= AlgKind(len(_AlgKind_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_AlgKind_index)-1 { return "AlgKind(" + strconv.FormatInt(int64(i), 10) + ")" } - return _AlgKind_name[_AlgKind_index[i]:_AlgKind_index[i+1]] + return _AlgKind_name[_AlgKind_index[idx]:_AlgKind_index[idx+1]] } diff --git a/src/cmd/compile/internal/types/kind_string.go b/src/cmd/compile/internal/types/kind_string.go index 1e1e84624080b08e614821c3e936c5c58daa4f01..7bd78b3919613c5cd5ff488b65576bc2e15a830d 100644 --- a/src/cmd/compile/internal/types/kind_string.go +++ b/src/cmd/compile/internal/types/kind_string.go @@ -53,8 +53,9 @@ var _Kind_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 158, 161, 166, 174, 182, 185, 190, 197, 202} func (i Kind) String() string { - if i >= Kind(len(_Kind_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_Kind_index)-1 { return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Kind_name[_Kind_index[i]:_Kind_index[i+1]] + return _Kind_name[_Kind_index[idx]:_Kind_index[idx+1]] } diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go index 82c6e9c15bf3539d22f22d5b01391775a7588923..e9cfcf541d76fefdf0715af924a2b5275d7d8f30 100644 --- a/src/cmd/compile/internal/types2/builtins.go +++ b/src/cmd/compile/internal/types2/builtins.go @@ -1027,12 +1027,18 @@ check.cycleError(cycle, firstInSrc(cycle)) return true } - check.push(t.obj) + obj := t.obj + check.push(obj) defer check.pop() // Careful, we're inspecting t.fromRHS, so we need to unpack first. t.unpack() varSize := check.hasVarSize(t.rhs()) + + // Special case for portable simd types that rewrite to unknown sizes. + if pkg := obj.Pkg(); pkg != nil && pkg.Path() == "simd" && obj.Name() == "_simd" { + varSize = true + } t.mu.Lock() defer t.mu.Unlock() diff --git a/src/cmd/compile/internal/types2/cycles.go b/src/cmd/compile/internal/types2/cycles.go index 14bd7f2630bea0c432d78ff877291406c17634d3..c0541be52d9a50e51bc63c8a8bb697b490534fd6 100644 --- a/src/cmd/compile/internal/types2/cycles.go +++ b/src/cmd/compile/internal/types2/cycles.go @@ -106,23 +106,34 @@ // isComplete returns whether a type is complete (i.e. up to having an underlying type). // Incomplete types will panic if [Type.Underlying] is called on them. func (check *Checker) isComplete(t Type) bool { - if n, ok := Unalias(t).(*Named); ok { - if i, found := check.objPathIdx[n.obj]; found { - cycle := check.objPath[i:] - check.cycleError(cycle, firstInSrc(cycle)) - return false - } + var ( + obj Object + rhs Type + ) + switch t := t.(type) { + case *Alias: + obj = t.obj + rhs = t.fromRHS + case *Named: + obj = t.obj + rhs = t.fromRHS + default: + return true + } - // We must walk through names because we permit certain cycles of names. - // Consider: - // - // type A B - // type B [unsafe.Sizeof(A{})]int - // - // starting at B. At the site of A{}, A has no underlying type, and so a - // cycle must be reported. - return check.isComplete(n.fromRHS) + if i, found := check.objPathIdx[obj]; found { + cycle := check.objPath[i:] + check.cycleError(cycle, firstInSrc(cycle)) + return false } - return true + // We must walk through names because we permit certain cycles of names. + // Consider: + // + // type A B + // type B [unsafe.Sizeof(A{})]int + // + // starting at B. At the site of A{}, A has no underlying type, and so a + // cycle must be reported. + return check.isComplete(rhs) } diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index 8f4336fb187274b0659b8efea8bb250e86fd4b4d..1529d111714f7d7c02cbb24d374ef6957322bc84 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go @@ -111,7 +111,7 @@ // Unify parameter and argument types for generic parameters with typed arguments // and collect the indices of generic parameters with untyped arguments. // Terminology: generic parameter = function parameter with a type-parameterized type - u := newUnifier(tparams, targs, check.allowVersion(go1_21)) + u := newUnifier(check, tparams, targs, check.allowVersion(go1_21)) errorf := func(tpar, targ Type, arg *operand) { // provide a better error message if we can diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index c3372e9d99477aeab63895a84f88a01d96b9f0f1..daa6b506eb43eb4520c9cf5e68f1f2dbc763ee16 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -21,7 +21,7 @@ TypeParams() *TypeParamList } // Instantiate instantiates the type orig with the given type arguments targs. -// orig must be an *Alias, *Named, or *Signature type. If there is no error, +// orig must be a generic *Alias, *Named, or *Signature type. If there is no error, // the resulting Type is an instantiated type of the same kind (*Alias, *Named // or *Signature, respectively). // @@ -36,30 +36,37 @@ // equivalent, so that instantiating distinct (but possibly identical) // signatures will yield different instances. The use of a shared context does // not guarantee that identical instances are deduplicated in all cases. // -// If validate is set, Instantiate verifies that the number of type arguments -// and parameters match, and that the type arguments satisfy their respective -// type constraints. If verification fails, the resulting error may wrap an -// *ArgumentError indicating which type argument did not satisfy its type parameter -// constraint, and why. +// If validate is set, Instantiate verifies that the type orig is in fact generic, +// that the number of type arguments and parameters match, and that the type arguments +// satisfy their respective type constraints. +// If verification fails, the resulting error may wrap an *ArgumentError indicating +// which type argument did not satisfy its type parameter constraint, and why. // -// If validate is not set, Instantiate does not verify the type argument count -// or whether the type arguments satisfy their constraints. Instantiate is -// guaranteed to not return an error, but may panic. Specifically, for -// *Signature types, Instantiate will panic immediately if the type argument +// If validate is not set, Instantiate does not check if orig is generic, verify the +// type argument count, or check whether the type arguments satisfy their constraints. +// Instantiate is guaranteed to not return an error, but may panic. Specifically, +// for *Signature types, Instantiate will panic immediately if the type argument // count is incorrect; for *Named types, a panic may occur later inside the // *Named API. func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) { - assert(len(targs) > 0) if ctxt == nil { ctxt = NewContext() } - orig_ := orig.(genericType) // signature of Instantiate must not change for backward-compatibility + orig_, ok := orig.(genericType) // signature of Instantiate must not change for backward-compatibility + if !ok { + panic(sprintf(nil, false, "cannot instantiate non-generic %s: expected *Named, *Alias, or *Signature", orig)) + } + if len(targs) == 0 { + panic(sprintf(nil, false, "cannot instantiate %s: empty type argument list", orig)) + } if validate { tparams := orig_.TypeParams().list() - assert(len(tparams) > 0) + if len(tparams) == 0 { + return nil, fmt.Errorf("cannot instantiate non-generic %s: has no type parameters", orig) + } if len(targs) != len(tparams) { - return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams)) + return nil, fmt.Errorf("cannot instantiate %s: got %d type arguments but have %d type parameters", orig, len(targs), len(tparams)) } if i, err := (*Checker)(nil).verify(nopos, tparams, targs, ctxt); err != nil { return nil, &ArgumentError{i, err} diff --git a/src/cmd/compile/internal/types2/issues_test.go b/src/cmd/compile/internal/types2/issues_test.go index ef5f535290052858bd85b32a08dc585b7fb1f695..c51543b40df870b6501989a2f9a94bd73e2340c6 100644 --- a/src/cmd/compile/internal/types2/issues_test.go +++ b/src/cmd/compile/internal/types2/issues_test.go @@ -1191,3 +1191,34 @@ if tv.Type != Typ[Invalid] { t.Fatalf("unexpected type for {x}: %s", tv.Type) } } + +func TestIssue72978(t *testing.T) { + const src = ` +package p + +type ( + genericG[T, U any] struct { x T; y U } + G1 genericG[int, string] + G2 = G1 +) + +func genericF[T, U any]() {} +var f = genericF[string, float64] +` + + pkg := mustTypecheck(src, nil, nil) + for _, name := range []string{"G1", "G2", "f"} { + func() { + typ := pkg.Scope().Lookup(name).Type() + _, err := Instantiate(nil, typ, []Type{Typ[Bool], Typ[Int]}, true) + if err == nil { + t.Errorf("%s[bool, int]: got no error", name) + return + } + want := fmt.Sprintf("cannot instantiate non-generic %s: has no type parameters", typ) + if err.Error() != want { + t.Errorf("%s[bool, int]: got %q, want %q", name, err.Error(), want) + } + }() + } +} diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go index 9e90c5fc7d3f03acff6b420b24cf2030bf5b0de2..e554e77a5dfad7fa204d568232d498d15524567e 100644 --- a/src/cmd/compile/internal/types2/unify.go +++ b/src/cmd/compile/internal/types2/unify.go @@ -67,6 +67,7 @@ // A unifier maintains a list of type parameters and // corresponding types inferred for each type parameter. // A unifier is created by calling newUnifier. type unifier struct { + check *Checker // handles maps each type parameter to its inferred type through // an indirection *Type called (inferred type) "handle". // Initially, each type parameter has its own, separate handle, @@ -85,7 +86,7 @@ // newUnifier returns a new unifier initialized with the given type parameter // and corresponding type argument lists. The type argument list may be shorter // than the type parameter list, and it may contain nil types. Matching type // parameters and arguments must have the same index. -func newUnifier(tparams []*TypeParam, targs []Type, enableInterfaceInference bool) *unifier { +func newUnifier(check *Checker, tparams []*TypeParam, targs []Type, enableInterfaceInference bool) *unifier { assert(len(tparams) >= len(targs)) handles := make(map[*TypeParam]*Type, len(tparams)) // Allocate all handles up-front: in a correct program, all type parameters @@ -99,7 +100,7 @@ t = targs[i] } handles[x] = &t } - return &unifier{handles, 0, enableInterfaceInference} + return &unifier{check, handles, 0, enableInterfaceInference} } // unifyMode controls the behavior of the unifier. @@ -142,6 +143,7 @@ return u.nify(x, y, mode, nil) } func (u *unifier) tracef(format string, args ...any) { + // TODO(gri) consider adjusting this to use Checker.trace fmt.Println(strings.Repeat(". ", u.depth) + sprintf(nil, true, format, args...)) } @@ -539,7 +541,12 @@ // than unify ym's own type parameter into an inference variable. xmethods := xi.typeSet().methods for _, xm := range xmethods { obj, _, _ := LookupFieldOrMethod(y, false, xm.pkg, xm.name) - if ym, _ := obj.(*Func); ym == nil || ym.Signature().TypeParams().Len() > 0 || !u.nify(xm.typ, ym.typ, exact, p) { + ym, _ := obj.(*Func) + if ym == nil { + return false + } + u.check.objDecl(ym) // ensure fully set-up signature + if ym.Signature().TypeParams() != nil || !u.nify(xm.typ, ym.typ, exact, p) { return false } } diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 1d258fe6dbbb1df9051605712d37baa820ea276c..43ac003c7907c7d006ebd57a17cc34a2342e9d59 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -78,7 +78,7 @@ lastHeading string // last dir heading printed tests []distTest // use addTest to extend testNames map[string]bool - timeoutScale int + timeoutScale int // a non-negative integer factor to scale test timeout by; defaults to 1 worklist []*work } @@ -573,9 +573,20 @@ ranGoTest = true timeoutSec := 180 * time.Second for _, pkg := range stdMatches { - if pkg == "cmd/go" { + switch pkg { + case "cmd/go": timeoutSec *= 3 - break + case "cmd/cgo/internal/testshared": + // This package can take 2-3 minutes to test, so 3 min timeout causes + // flaky failures, like https://ci.chromium.org/b/8679277370961616529. + // Use the default timeout for it rather than the custom 3 minute one. + timeoutSec = 0 + case "internal/godebugs": + // This package can take 5-6 minutes to test when the asan mode is on. + // The asan modifier scales the timeout by 2, but even 3*2 minutes is + // sometimes not enough. See go.dev/issue/78392. + // Use the default timeout for it rather than the custom 3 minute one. + timeoutSec = 0 } } return (&goTest{ diff --git a/src/cmd/go/internal/doc/pkgsite.go b/src/cmd/go/internal/doc/pkgsite.go index ff1caf8e654b9a85aff6a33ab592a27f4c2bd84d..8bfae93029bdc555eba430b334e61fbb6871c5de 100644 --- a/src/cmd/go/internal/doc/pkgsite.go +++ b/src/cmd/go/internal/doc/pkgsite.go @@ -24,6 +24,16 @@ "cmd/go/internal/modload" "cmd/go/internal/work" ) +// pkgsiteCmdInternalDocVersion controls the version of the golang.org/x/pkgsite/cmd/internal/doc +// module to build in buildPkgsite. +// +// This version is maintained, like other golang.org/x dependencies, via the go.dev/issue/36905 process. +// +// To make that process easier, the exact name and file location of this constant are known +// to the [golang.org/x/build/cmd/updatestd] command. If this constant needs to be renamed +// or moved to another .go file, update that code too. +const pkgsiteCmdInternalDocVersion = "v0.0.0-20260605201217-deb78785c3ce" + // pickUnusedPort finds an unused port by trying to listen on port 0 // and letting the OS pick a port, then closing that connection and // returning that port number. @@ -59,7 +69,7 @@ base.Fatal(err) } }() - version := "v0.0.0-20260605201217-deb78785c3ce" + version := pkgsiteCmdInternalDocVersion if os.Getenv("TEST_GODOC_BUILD_ONLY") != "" { version = "v0.1.0" } diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index 1a2adcf4bf7c65ccb1c13894532406dd2df83a17..2985cff67cec0bde02e247f2e28cf1a72b60ad87 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -941,10 +941,10 @@ } else { buildContext.GOPATH = "" // Clear GOPATH so packages are imported as pure module packages } modroot := modload.PackageModRoot(ld, ctx, r.path) - if modroot == "" && str.HasPathPrefix(r.dir, cfg.GOROOTsrc) { + if modroot == "" && str.HasFilePathPrefix(r.dir, cfg.GOROOTsrc) { modroot = cfg.GOROOTsrc gorootSrcCmd := filepath.Join(cfg.GOROOTsrc, "cmd") - if str.HasPathPrefix(r.dir, gorootSrcCmd) { + if str.HasFilePathPrefix(r.dir, gorootSrcCmd) { modroot = gorootSrcCmd } } diff --git a/src/cmd/go/internal/modindex/read.go b/src/cmd/go/internal/modindex/read.go index a31e09d233209cd1aff1147680bf246f67a17e36..529c26ea057019cb565bc9258c8d8cd51c546bf4 100644 --- a/src/cmd/go/internal/modindex/read.go +++ b/src/cmd/go/internal/modindex/read.go @@ -138,7 +138,7 @@ } if !errors.Is(err, errNotFromModuleCache) { return nil, err } - if cfg.BuildContext.Compiler == "gccgo" && str.HasPathPrefix(modroot, cfg.GOROOTsrc) { + if cfg.BuildContext.Compiler == "gccgo" && str.HasFilePathPrefix(modroot, cfg.GOROOTsrc) { return nil, err // gccgo has no sources for GOROOT packages. } // The pkgdir for fips140 has been replaced in the fsys overlay, @@ -403,6 +403,7 @@ } // goroot and gopath inTestdata := func(sub string) bool { + sub = filepath.ToSlash(sub) return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || str.HasPathPrefix(sub, "testdata") } var pkga string diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index 28b824572d0e57a645c375ff1ba1fe27e46c7672..6e3f9251be9b1ed8bed42893a4797cc3b00e6bbe 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -2305,7 +2305,7 @@ } for _, info := range godebugs.Removed { if info.Name == k { if info.Old(v) { - return fmt.Errorf("use of removed %s %q with old value %q (see https://go.dev/doc/godebug#go-1%v)", verb, k, v, info.Removed) + return fmt.Errorf("removed GODEBUG %q set to old value %q (https://go.dev/doc/godebug#go-1%v)", k, v, info.Removed) } // Using a removed GODEBUG setting with a non-old value is ok (see go.dev/issue/76163). return nil diff --git a/src/cmd/go/testdata/script/godebug_default.txt b/src/cmd/go/testdata/script/godebug_default.txt index 22f8fe622d914103eecd566b708aec9174a59643..fdde0c41e91c5486ed798047791ece9b9aece4b2 100644 --- a/src/cmd/go/testdata/script/godebug_default.txt +++ b/src/cmd/go/testdata/script/godebug_default.txt @@ -79,10 +79,10 @@ # Programs in Go 1.21 work module should trigger run-time error. cp go.mod.21 go.mod ! go run . -stderr 'panic: panic called with nil argument' +stderr 'runtime error: panic called with nil argument' ! go run rsc.io/panicnil -stderr 'panic: panic called with nil argument' +stderr 'runtime error: panic called with nil argument' # Programs in Go 1.20 work module use old panic nil behavior. cp go.mod.20 go.mod @@ -99,7 +99,7 @@ stderr 'panic: nil' rm go.mod ! go run rsc.io/panicnil@v1.1.0 -stderr 'panic: panic called with nil argument' +stderr 'runtime error: panic called with nil argument' -- go.work.21 -- go 1.21 diff --git a/src/cmd/go/testdata/script/godebug_removed.txt b/src/cmd/go/testdata/script/godebug_removed.txt index 115e9011810133e88f2ce458396015cee14df658..71a358b581975fea8d9ca43cf9cda8c491b1b769 100644 --- a/src/cmd/go/testdata/script/godebug_removed.txt +++ b/src/cmd/go/testdata/script/godebug_removed.txt @@ -5,7 +5,7 @@ cp go.mod.20 go.mod # Go 1.20 must accept the removed GODEBUG gotypesalias with default value go build -n godebugok.go -! stderr 'use of removed' +! stderr 'removed GODEBUG "gotypesalias" set to old value "0"' # This also applies to go vet go vet godebugok.go @@ -13,40 +13,45 @@ ! stderr . # Go 1.20 must not accept the removed GODEBUG gotypesalias with old value ! go build -n godebugold.go -stderr 'use of removed' +stderr 'removed GODEBUG "gotypesalias" set to old value "0"' ! go vet godebugold.go -stderr 'use of removed' +stderr 'removed GODEBUG "gotypesalias" set to old value "0"' # If the GODEBUG setting is valid in the .mod file the build succeeds. cp go.mod.20.godebugok go.mod go build -n main.go -! stderr 'use of removed' +! stderr 'removed GODEBUG "gotypesalias" set to old value "0"' # In this case, setting an invalid value in the .go file leads to build failure. ! go build -n godebugold.go -stderr 'use of removed' +stderr 'removed GODEBUG "gotypesalias" set to old value "0"' # If the GODEBUG setting is invalid in the .mod file the build fails. cp go.mod.20.godebugold go.mod ! go build -n main.go -stderr 'use of removed' +stderr 'removed GODEBUG "gotypesalias" set to old value "0"' # In this case, setting a valid value in the .go file does not remedy the failure. ! go build -n godebugok.go -#stderr 'use of removed' +stderr 'removed GODEBUG "gotypesalias" set to old value "0"' [short] skip -# GODEBUGs set in the environment are simply passed along by the go command. +# Using a removed GODEBUG with old value in the environment causes panic at startup. cp go.mod.20 go.mod env GODEBUG=gotypesalias=0 -go run printgodebug.go -stdout gotypesalias=0 +! go run printgodebug.go +stderr 'removed GODEBUG "gotypesalias" set to old value "0"' +# Using a removed GODEBUG with default value in the environment is ok. env GODEBUG=gotypesalias=1 go run printgodebug.go stdout gotypesalias=1 + +# Setting a removed GODEBUG with old value in the environment during runtime is not guarded against. +go run setgodebug.go +stdout gotypesalias=0 -- go.mod.20 -- go 1.20 @@ -87,3 +92,14 @@ func main() { fmt.Println(os.Getenv("GODEBUG")) } + +-- setgodebug.go -- +package main + +import "fmt" +import "os" + +func main() { + os.Setenv("GODEBUG", "gotypesalias=0") + fmt.Println(os.Getenv("GODEBUG")) +} diff --git a/src/cmd/go/testdata/script/mod_removed_godebug.txt b/src/cmd/go/testdata/script/mod_removed_godebug.txt index df4a47a111d1d9fca2dd0ee3a7e29e3114052bed..cf8f04736bd03928f0b91c1b80b16dc8086c5076 100644 --- a/src/cmd/go/testdata/script/mod_removed_godebug.txt +++ b/src/cmd/go/testdata/script/mod_removed_godebug.txt @@ -3,7 +3,7 @@ # instead of the generic "unknown godebug" error message # for removed GODEBUGs. ! go list -stderr '^go.mod:3: use of removed godebug "x509sha1" with old value "1" \(see https://go.dev/doc/godebug#go-124\)' +stderr '^go.mod:3: removed GODEBUG "x509sha1" set to old value "1" \(https://go.dev/doc/godebug#go-124\)' -- go.mod -- module example.com/bar diff --git a/src/cmd/internal/obj/abi_string.go b/src/cmd/internal/obj/abi_string.go index 77868eeac0da407eb549b4f03ef9df1278d05589..babb4bc6f66df0f7045bfb18721515493e19ac29 100644 --- a/src/cmd/internal/obj/abi_string.go +++ b/src/cmd/internal/obj/abi_string.go @@ -18,8 +18,9 @@ var _ABI_index = [...]uint8{0, 4, 15, 23} func (i ABI) String() string { - if i >= ABI(len(_ABI_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_ABI_index)-1 { return "ABI(" + strconv.FormatInt(int64(i), 10) + ")" } - return _ABI_name[_ABI_index[i]:_ABI_index[i+1]] + return _ABI_name[_ABI_index[idx]:_ABI_index[idx+1]] } diff --git a/src/cmd/internal/obj/addrtype_string.go b/src/cmd/internal/obj/addrtype_string.go index e6277d39b012425ca223d685ef612a1e0728a4a0..6fac40562498f507ceea0583bbdf9a889bfad5f6 100644 --- a/src/cmd/internal/obj/addrtype_string.go +++ b/src/cmd/internal/obj/addrtype_string.go @@ -30,8 +30,9 @@ var _AddrType_index = [...]uint8{0, 9, 20, 33, 41, 51, 62, 73, 81, 90, 100, 111, 123, 133, 145, 157} func (i AddrType) String() string { - if i >= AddrType(len(_AddrType_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_AddrType_index)-1 { return "AddrType(" + strconv.FormatInt(int64(i), 10) + ")" } - return _AddrType_name[_AddrType_index[i]:_AddrType_index[i+1]] + return _AddrType_name[_AddrType_index[idx]:_AddrType_index[idx+1]] } diff --git a/src/cmd/internal/script/scripttest/run.go b/src/cmd/internal/script/scripttest/run.go index d235246a114360eb1a58af9c7a8e1a4d629546b8..76956a51c4ac70948f10c3e0a3b6c73957445de9 100644 --- a/src/cmd/internal/script/scripttest/run.go +++ b/src/cmd/internal/script/scripttest/run.go @@ -103,9 +103,17 @@ return env } interrupt := func(cmd *exec.Cmd) error { + // TODO(thepudds): currently cmd/go/script_test.go uses a platform-specific cancel + // that we could consider emulating here. return cmd.Process.Signal(os.Interrupt) } - gracePeriod := 60 * time.Second // arbitrary + + // Customize the subprocess termination grace period to reduce flakes on busy builders (#76685). + // The grace period is the max of 100ms or 5% of the time remaining until any t.Deadline. + gracePeriod := subprocessGracePeriod(t.Deadline()) + + cmdExec := script.Exec(interrupt, gracePeriod) + cmds["exec"] = cmdExec // Set up an alternate go root for running script tests, since it // is possible that we might want to replace one of the installed @@ -123,7 +131,6 @@ // Add in commands for "go" and "cc". testgo := filepath.Join(tgr, "bin", "go") gocmd := script.Program(testgo, interrupt, gracePeriod) addcmd("go", gocmd) - cmdExec := cmds["exec"] addcmd("cc", scriptCC(cmdExec, goEnv("CC"))) // Add various helpful conditions related to builds and toolchain use. @@ -199,19 +206,29 @@ if !ok { return ctx } - gracePeriod := 100 * time.Millisecond - timeout := time.Until(deadline) - - // If time allows, increase the termination grace period to 5% of the - // remaining time. - gracePeriod = max(gracePeriod, timeout/20) + gracePeriod := subprocessGracePeriod(deadline, ok) // Reserve two grace periods to clean up + timeout := time.Until(deadline) timeout -= 2 * gracePeriod ctx, cancel := context.WithTimeout(ctx, timeout) t.Cleanup(cancel) return ctx +} + +// subprocessGracePeriod returns a grace period for terminating subprocesses +// created by the commands of a script test. +func subprocessGracePeriod(deadline time.Time, hasDeadline bool) time.Duration { + gracePeriod := 100 * time.Millisecond // arbitrary + if !hasDeadline { + return gracePeriod + } + + // If time allows, increase the termination grace period to 5% of the + // remaining time. + timeout := time.Until(deadline) + return max(gracePeriod, timeout/20) } // RunTests kicks off one or more script-based tests using the diff --git a/src/cmd/link/internal/sym/symkind_string.go b/src/cmd/link/internal/sym/symkind_string.go index 2499c5b435c63d2e21d36a8b926e04a3922c2b58..5f5da3bee879107cd06f5c60cf5154ccebc1a2ad 100644 --- a/src/cmd/link/internal/sym/symkind_string.go +++ b/src/cmd/link/internal/sym/symkind_string.go @@ -88,8 +88,9 @@ var _SymKind_index = [...]uint16{0, 4, 9, 23, 32, 44, 52, 62, 71, 78, 87, 94, 101, 117, 128, 142, 152, 160, 170, 182, 187, 194, 207, 222, 236, 246, 255, 263, 269, 277, 288, 295, 304, 314, 333, 347, 364, 377, 385, 390, 404, 413, 425, 433, 442, 446, 455, 462, 485, 502, 518, 525, 542, 547, 559, 571, 588, 605, 615, 623, 632, 642, 654, 665, 674, 686, 696, 705, 716, 725, 736, 746, 760, 768} func (i SymKind) String() string { - if i >= SymKind(len(_SymKind_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_SymKind_index)-1 { return "SymKind(" + strconv.FormatInt(int64(i), 10) + ")" } - return _SymKind_name[_SymKind_index[i]:_SymKind_index[i+1]] + return _SymKind_name[_SymKind_index[idx]:_SymKind_index[idx+1]] } diff --git a/src/crypto/tls/ech.go b/src/crypto/tls/ech.go index ca9cddcacc7d856d0b7241ece2592a2f63bb668b..91aea409196e77dfbe90088364a3bf4916d2741f 100644 --- a/src/crypto/tls/ech.go +++ b/src/crypto/tls/ech.go @@ -55,7 +55,7 @@ } func parseECHConfig(enc []byte) (skip bool, ec echConfig, err error) { s := cryptobyte.String(enc) - ec.raw = []byte(enc) + ec.raw = enc if !s.ReadUint16(&ec.Version) { return false, echConfig{}, &echConfigErr{"version"} } @@ -119,7 +119,7 @@ return false, ec, nil } -// parseECHConfigList parses a draft-ietf-tls-esni-18 ECHConfigList, returning a +// parseECHConfigList parses a RFC 9849 ECHConfigList, returning a // slice of parsed ECHConfigs, in the same order they were parsed, or an error // if the list is malformed. func parseECHConfigList(data []byte) ([]echConfig, error) { diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index a4544a13051f5166fc2bd0636fdb1c38b32dc84e..b62feef1a0d2a3213e59d9e40709d291eb1d260a 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -280,10 +280,6 @@ } return err } - if hs.cert != nil { - hs.c.localCertificate = hs.cert.Certificate - } - if hs.clientHello.scts { hs.hello.scts = hs.cert.SignedCertificateTimestamps } @@ -618,6 +614,10 @@ } certMsg := new(certificateMsg) certMsg.certificates = hs.cert.Certificate + // Set localCertificate here, rather than at certificate selection time, so + // that it is only populated when a certificate is actually presented to the + // peer, and not on resumed connections. + c.localCertificate = hs.cert.Certificate if _, err := hs.c.writeHandshakeRecord(certMsg, &hs.finishedHash); err != nil { return err } diff --git a/src/crypto/tls/tls_test.go b/src/crypto/tls/tls_test.go index 2bfb645e2c8e60312107797ee017f7d6ea321a98..d1f229b0c77127ee821ed1a8d2881805f41d8624 100644 --- a/src/crypto/tls/tls_test.go +++ b/src/crypto/tls/tls_test.go @@ -2952,6 +2952,10 @@ func testLocalCertificate(t *testing.T, version uint16, callback bool) { clientConfig, serverConfig := testConfigClient.Clone(), testConfigServer.Clone() clientConfig.MinVersion, serverConfig.MinVersion = version, version + clientConfig.MaxVersion, serverConfig.MaxVersion = version, version + if version < VersionTLS12 { + skipFIPS(t) + } serverConfig.ClientAuth = RequestClientCert serverCert, clientCert := testConfigServer.Certificates[0], testConfigClient.Certificates[0] @@ -3007,6 +3011,10 @@ func testLocalCertificateResumption(t *testing.T, version uint16, callback bool) { clientConfig, serverConfig := testConfigClient.Clone(), testConfigServer.Clone() clientConfig.MinVersion, serverConfig.MinVersion = version, version + clientConfig.MaxVersion, serverConfig.MaxVersion = version, version + if version < VersionTLS12 { + skipFIPS(t) + } clientConfig.ClientSessionCache = NewLRUClientSessionCache(1) serverConfig.ClientAuth = RequestClientCert diff --git a/src/database/sql/driver/driver.go b/src/database/sql/driver/driver.go index 264fd67b8c236dc72ba6ef5f949ba50826820ca9..dc28a115250ceeb4449d8ed45ffe1fd12cefc928 100644 --- a/src/database/sql/driver/driver.go +++ b/src/database/sql/driver/driver.go @@ -452,6 +452,9 @@ // RowsColumnScanner extends the [Rows] interface by providing a way for the driver // to scan directly into the user-provided destination. // // RowsColumnScanner supersedes the [Rows.Next] method. +// +// As of Go 1.27, database/sql will not call Next if a Rows implements RowsColumnScanner. +// Rows implementations may still implement the Next method to support older versions of Go. type RowsColumnScanner interface { Rows diff --git a/src/debug/dwarf/class_string.go b/src/debug/dwarf/class_string.go index 163bed712a7e0497621b14f77c85ca843be38016..8d077087f264d8690c3d4fc181b2ad7edd06c64a 100644 --- a/src/debug/dwarf/class_string.go +++ b/src/debug/dwarf/class_string.go @@ -35,8 +35,9 @@ var _Class_index = [...]uint16{0, 12, 24, 34, 47, 59, 68, 80, 95, 106, 123, 137, 154, 165, 182, 196, 208, 220, 232, 248, 266} func (i Class) String() string { - if i < 0 || i >= Class(len(_Class_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_Class_index)-1 { return "Class(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Class_name[_Class_index[i]:_Class_index[i+1]] + return _Class_name[_Class_index[idx]:_Class_index[idx+1]] } diff --git a/src/debug/macho/reloctype_string.go b/src/debug/macho/reloctype_string.go index cb31627e7e370b810b1a2a9f736a9a179bfc461a..edba4f018c86ab37b33f95139e6c5d318c0da9f6 100644 --- a/src/debug/macho/reloctype_string.go +++ b/src/debug/macho/reloctype_string.go @@ -21,10 +21,11 @@ var _RelocTypeGeneric_index = [...]uint8{0, 21, 39, 61, 84, 112, 129} func (i RelocTypeGeneric) String() string { - if i < 0 || i >= RelocTypeGeneric(len(_RelocTypeGeneric_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_RelocTypeGeneric_index)-1 { return "RelocTypeGeneric(" + strconv.FormatInt(int64(i), 10) + ")" } - return _RelocTypeGeneric_name[_RelocTypeGeneric_index[i]:_RelocTypeGeneric_index[i+1]] + return _RelocTypeGeneric_name[_RelocTypeGeneric_index[idx]:_RelocTypeGeneric_index[idx+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. @@ -47,10 +48,11 @@ var _RelocTypeX86_64_index = [...]uint8{0, 21, 40, 59, 80, 96, 119, 140, 161, 182, 198} func (i RelocTypeX86_64) String() string { - if i < 0 || i >= RelocTypeX86_64(len(_RelocTypeX86_64_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_RelocTypeX86_64_index)-1 { return "RelocTypeX86_64(" + strconv.FormatInt(int64(i), 10) + ")" } - return _RelocTypeX86_64_name[_RelocTypeX86_64_index[i]:_RelocTypeX86_64_index[i+1]] + return _RelocTypeX86_64_name[_RelocTypeX86_64_index[idx]:_RelocTypeX86_64_index[idx+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. @@ -73,10 +75,11 @@ var _RelocTypeARM_index = [...]uint8{0, 17, 31, 49, 73, 92, 106, 126, 148, 162, 185} func (i RelocTypeARM) String() string { - if i < 0 || i >= RelocTypeARM(len(_RelocTypeARM_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_RelocTypeARM_index)-1 { return "RelocTypeARM(" + strconv.FormatInt(int64(i), 10) + ")" } - return _RelocTypeARM_name[_RelocTypeARM_index[i]:_RelocTypeARM_index[i+1]] + return _RelocTypeARM_name[_RelocTypeARM_index[idx]:_RelocTypeARM_index[idx+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. @@ -100,8 +103,9 @@ var _RelocTypeARM64_index = [...]uint16{0, 20, 42, 62, 80, 101, 128, 158, 184, 212, 243, 261} func (i RelocTypeARM64) String() string { - if i < 0 || i >= RelocTypeARM64(len(_RelocTypeARM64_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_RelocTypeARM64_index)-1 { return "RelocTypeARM64(" + strconv.FormatInt(int64(i), 10) + ")" } - return _RelocTypeARM64_name[_RelocTypeARM64_index[i]:_RelocTypeARM64_index[i+1]] + return _RelocTypeARM64_name[_RelocTypeARM64_index[idx]:_RelocTypeARM64_index[idx+1]] } diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go index 2e696c5918df6f1352c05c57c4b938a24ba77ffe..806a1c5bb2ae4a4f3add9c0bcff6a447bee7b27d 100644 --- a/src/encoding/json/decode_test.go +++ b/src/encoding/json/decode_test.go @@ -1734,7 +1734,7 @@ Interface any PInterface *any - InvalidInline int `json:",inline"` // issue #79921: invalid `inline` tag option should be ignored + InvalidEmbed int `json:",embed"` // issue #79921: invalid `embed` tag option should be ignored unexported int } @@ -1771,16 +1771,16 @@ MapP: map[string]*Small{ "19": {Tag: "tag19"}, "20": nil, }, - EmptyMap: map[string]Small{}, - Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}}, - SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}}, - EmptySlice: []Small{}, - StringSlice: []string{"str24", "str25", "str26"}, - ByteSlice: []byte{27, 28, 29}, - Small: Small{Tag: "tag30"}, - PSmall: &Small{Tag: "tag31"}, - Interface: 5.2, - InvalidInline: 123, + EmptyMap: map[string]Small{}, + Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}}, + SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}}, + EmptySlice: []Small{}, + StringSlice: []string{"str24", "str25", "str26"}, + ByteSlice: []byte{27, 28, 29}, + Small: Small{Tag: "tag30"}, + PSmall: &Small{Tag: "tag31"}, + Interface: 5.2, + InvalidEmbed: 123, } var pallValue = All{ @@ -1896,7 +1896,7 @@ }, "PPSmall": null, "Interface": 5.2, "PInterface": null, - "InvalidInline": 123 + "InvalidEmbed": 123 }` var allValueCompact = stripWhitespace(allValueIndent) @@ -1986,7 +1986,7 @@ "Tag": "tag31" }, "Interface": null, "PInterface": 5.2, - "InvalidInline": 0 + "InvalidEmbed": 0 }` var pallValueCompact = stripWhitespace(pallValueIndent) diff --git a/src/encoding/json/v2/arshal_default.go b/src/encoding/json/v2/arshal_default.go index af79674f4f0f8ab020cdc8eb646b6b25422714db..5760ae7b6890a8f794baa88064729bee01e30616 100644 --- a/src/encoding/json/v2/arshal_default.go +++ b/src/encoding/json/v2/arshal_default.go @@ -1145,7 +1145,7 @@ v := addressableValue{va.Field(f.index0), va.forcedAddr} // addressable if struct value is addressable if len(f.index) > 0 { v = v.fieldByIndex(f.index, false) if !v.IsValid() { - continue // implies a nil inlined field + continue // implies a nil embedded field } } @@ -1245,17 +1245,17 @@ } // Remember the previous written object member. // The set of seen fields only needs to be updated to detect - // duplicate names with those from the inlined fallback. - if !mo.Flags.Get(jsonflags.AllowDuplicateNames) && fields.inlinedFallback != nil { + // duplicate names with those from the embedded fallback. + if !mo.Flags.Get(jsonflags.AllowDuplicateNames) && fields.embeddedFallback != nil { seenIdxs.insert(uint(f.id)) } prevIdx = f.id } - if fields.inlinedFallback != nil { + if fields.embeddedFallback != nil { var insertUnquotedName func([]byte) bool if !mo.Flags.Get(jsonflags.AllowDuplicateNames) { insertUnquotedName = func(name []byte) bool { - // Check that the name from inlined fallback does not match + // Check that the name from embedded fallback does not match // one of the previously marshaled names from known fields. if foldedFields := fields.lookupByFoldedName(name); len(foldedFields) > 0 { if f := fields.byActualName[string(name)]; f != nil { @@ -1269,11 +1269,11 @@ } } // Check that the name does not match any other name - // previously marshaled from the inlined fallback. + // previously marshaled from the embedded fallback. return xe.Namespaces.Last().InsertUnquoted(name) } } - if err := marshalInlinedFallbackAll(enc, va, mo, fields.inlinedFallback, insertUnquotedName); err != nil { + if err := marshalEmbeddedFallbackAll(enc, va, mo, fields.embeddedFallback, insertUnquotedName); err != nil { return err } } @@ -1331,7 +1331,7 @@ break } } if f == nil { - if uo.Flags.Get(jsonflags.RejectUnknownMembers) && fields.inlinedFallback == nil { + if uo.Flags.Get(jsonflags.RejectUnknownMembers) && fields.embeddedFallback == nil { err := newUnmarshalErrorAfter(dec, t, ErrUnknownName) if !uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { return err @@ -1343,14 +1343,14 @@ // TODO: Unread the object name. return newDuplicateNameError(dec.StackPointer(), nil, dec.InputOffset()-len64(val)) } - if fields.inlinedFallback == nil { + if fields.embeddedFallback == nil { // Skip unknown value since we have no place to store it. if err := dec.SkipValue(); err != nil { return err } } else { // Unmarshal into a value capable of storing arbitrary object members. - if err := unmarshalInlinedFallbackNext(dec, va, uo, fields.inlinedFallback, val, name); err != nil { + if err := unmarshalEmbeddedFallbackNext(dec, va, uo, fields.embeddedFallback, val, name); err != nil { if isFatalError(err, uo.Flags) { return err } diff --git a/src/encoding/json/v2/arshal_inlined.go b/src/encoding/json/v2/arshal_embedded.go rename from src/encoding/json/v2/arshal_inlined.go rename to src/encoding/json/v2/arshal_embedded.go index e3596aaa82d89a9b4a5a4d07f223cfdb32b876f0..c29323e56a028c993d71e51cf14623ce0014f0fd 100644 --- a/src/encoding/json/v2/arshal_inlined.go +++ b/src/encoding/json/v2/arshal_embedded.go @@ -19,30 +19,30 @@ "encoding/json/internal/jsonwire" "encoding/json/jsontext" ) -// This package supports "inlining" a Go struct field, where the contents +// This package supports "embedding" a Go struct field, where the contents // of the serialized field (which must be a JSON object) are treated as if // they are part of the parent Go struct (which represents a JSON object). // -// Generally, inlined fields are of a Go struct type, where the fields of the +// Generally, embedded fields are of a Go struct type, where the fields of the // nested struct are virtually hoisted up to the parent struct using rules // similar to how Go embedding works (but operating within the JSON namespace). // -// However, inlined fields may also be of a Go map type with a string key or -// a jsontext.Value. Such inlined fields are called "fallback" fields since they +// However, embedded fields may also be of a Go map type with a string key or +// a jsontext.Value. Such embedded fields are called "fallback" fields since they // represent any arbitrary JSON object member. Explicitly named fields take -// precedence over the inlined fallback. Only one inlined fallback is allowed. +// precedence over the embedded fallback. Only one embedded fallback is allowed. -var errRawInlinedNotObject = errors.New("inlined raw value must be a JSON object") +var errRawEmbedNotObject = errors.New("embedded raw value must be a JSON object") var jsontextValueType = reflect.TypeFor[jsontext.Value]() -// marshalInlinedFallbackAll marshals all the members in an inlined fallback. -func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct, f *structField, insertUnquotedName func([]byte) bool) error { +// marshalEmbeddedFallbackAll marshals all the members in an embedded fallback. +func marshalEmbeddedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct, f *structField, insertUnquotedName func([]byte) bool) error { v := addressableValue{va.Field(f.index0), va.forcedAddr} // addressable if struct value is addressable if len(f.index) > 0 { v = v.fieldByIndex(f.index, false) if !v.IsValid() { - return nil // implies a nil inlined field + return nil // implies a nil embedded field } } v = v.indirect(false) @@ -69,7 +69,7 @@ } return newMarshalErrorBefore(enc, v.Type(), err) } if tok.Kind() != '{' { - return newMarshalErrorBefore(enc, v.Type(), errRawInlinedNotObject) + return newMarshalErrorBefore(enc, v.Type(), errRawEmbedNotObject) } for dec.PeekKind() != '}' { // Parse the JSON object name. @@ -165,8 +165,8 @@ return nil } } -// unmarshalInlinedFallbackNext unmarshals only the next member in an inlined fallback. -func unmarshalInlinedFallbackNext(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct, f *structField, quotedName, unquotedName []byte) error { +// unmarshalEmbeddedFallbackNext unmarshals only the next member in an embedded fallback. +func unmarshalEmbeddedFallbackNext(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct, f *structField, quotedName, unquotedName []byte) error { v := addressableValue{va.Field(f.index0), va.forcedAddr} // addressable if struct value is addressable if len(f.index) > 0 { v = v.fieldByIndex(f.index, true) @@ -188,7 +188,7 @@ if !jsonwire.HasSuffixByte(*b, ',') && !jsonwire.HasSuffixByte(*b, '{') { *b = append(*b, ',') } } else { - return newUnmarshalErrorAfterWithSkipping(dec, v.Type(), errRawInlinedNotObject) + return newUnmarshalErrorAfterWithSkipping(dec, v.Type(), errRawEmbedNotObject) } } *b = append(*b, quotedName...) diff --git a/src/encoding/json/v2/arshal_test.go b/src/encoding/json/v2/arshal_test.go index d344287367d3d869db05da7a34c88315efa28165..6fd6a8b7d52b8dd90d3e55c2047e0d3f01483d19 100644 --- a/src/encoding/json/v2/arshal_test.go +++ b/src/encoding/json/v2/arshal_test.go @@ -440,84 +440,84 @@ } structTimeFormatStringInvalid struct { T time.Time `json:",string,format:RFC3339"` } - structInlined struct { - X structInlinedL1 `json:",inline"` - *StructEmbed2 // implicit inline + structEmbedded struct { + X structEmbeddedL1 `json:",embed"` + *StructEmbed2 // implicit embed } - structInlinedL1 struct { - X *structInlinedL2 `json:",inline"` - StructEmbed1 `json:",inline"` + structEmbeddedL1 struct { + X *structEmbeddedL2 `json:",embed"` + StructEmbed1 `json:",embed"` } - structInlinedL2 struct{ A, B, C string } - StructEmbed1 struct{ C, D, E string } - StructEmbed2 struct{ E, F, G string } - structInlineTextValue struct { + structEmbeddedL2 struct{ A, B, C string } + StructEmbed1 struct{ C, D, E string } + StructEmbed2 struct{ E, F, G string } + structEmbedTextValue struct { A int `json:",omitzero"` - X jsontext.Value `json:",inline"` + X jsontext.Value `json:",embed"` B int `json:",omitzero"` } - structInlinePointerTextValue struct { + structEmbedPointerTextValue struct { A int `json:",omitzero"` - X *jsontext.Value `json:",inline"` + X *jsontext.Value `json:",embed"` B int `json:",omitzero"` } - structInlinePointerInlineTextValue struct { + structEmbedPointerEmbedTextValue struct { X *struct { A int - X jsontext.Value `json:",inline"` - } `json:",inline"` + X jsontext.Value `json:",embed"` + } `json:",embed"` } - structInlineInlinePointerTextValue struct { + structEmbedEmbedPointerTextValue struct { X struct { - X *jsontext.Value `json:",inline"` - } `json:",inline"` + X *jsontext.Value `json:",embed"` + } `json:",embed"` } - structInlineMapStringAny struct { + structEmbedMapStringAny struct { A int `json:",omitzero"` - X jsonObject `json:",inline"` + X jsonObject `json:",embed"` B int `json:",omitzero"` } - structInlinePointerMapStringAny struct { + structEmbedPointerMapStringAny struct { A int `json:",omitzero"` - X *jsonObject `json:",inline"` + X *jsonObject `json:",embed"` B int `json:",omitzero"` } - structInlinePointerInlineMapStringAny struct { + structEmbedPointerEmbedMapStringAny struct { X *struct { A int - X jsonObject `json:",inline"` - } `json:",inline"` + X jsonObject `json:",embed"` + } `json:",embed"` } - structInlineInlinePointerMapStringAny struct { + structEmbedEmbedPointerMapStringAny struct { X struct { - X *jsonObject `json:",inline"` - } `json:",inline"` + X *jsonObject `json:",embed"` + } `json:",embed"` } - structInlineMapStringInt struct { - X map[string]int `json:",inline"` + structEmbedMapStringInt struct { + X map[string]int `json:",embed"` } - structInlineMapNamedStringInt struct { - X map[namedString]int `json:",inline"` + structEmbedMapNamedStringInt struct { + X map[namedString]int `json:",embed"` } - structInlineMapNamedStringAny struct { + structEmbedMapNamedStringAny struct { A int `json:",omitzero"` - X map[namedString]any `json:",inline"` + X map[namedString]any `json:",embed"` B int `json:",omitzero"` } - structNoCaseInlineTextValue struct { + structNoCaseEmbedTextValue struct { AAA string `json:",omitempty,case:strict"` AA_b string `json:",omitempty"` AaA string `json:",omitempty,case:ignore"` AAa string `json:",omitempty,case:ignore"` Aaa string `json:",omitempty"` - X jsontext.Value `json:",inline"` + X jsontext.Value `json:",embed"` } - structNoCaseInlineMapStringAny struct { + structNoCaseEmbedMapStringAny struct { AAA string `json:",omitempty"` AaA string `json:",omitempty,case:ignore"` AAa string `json:",omitempty,case:ignore"` Aaa string `json:",omitempty"` - X jsonObject `json:",inline"` + X jsonObject `json:",embed"` } allMethods struct { @@ -595,12 +595,12 @@ valueStringer struct{} pointerStringer struct{} cyclicA struct { - B1 cyclicB `json:",inline"` - B2 cyclicB `json:",inline"` + B1 cyclicB `json:",embed"` + B2 cyclicB `json:",embed"` } cyclicB struct { F int - A *cyclicA `json:",inline"` + A *cyclicA `json:",embed"` } ) @@ -2542,234 +2542,234 @@ in: structFormatInvalid{Interface: "anything"}, want: `{"Interface"`, wantErr: EM(errInvalidFormatFlag).withPos(`{"Interface":`, "/Interface").withType(0, T[any]()), }, { - name: jsontest.Name("Structs/Inline/Zero"), - in: structInlined{}, + name: jsontest.Name("Structs/Embed/Zero"), + in: structEmbedded{}, want: `{"D":""}`, }, { - name: jsontest.Name("Structs/Inline/Alloc"), - in: structInlined{ - X: structInlinedL1{ - X: &structInlinedL2{}, + name: jsontest.Name("Structs/Embed/Alloc"), + in: structEmbedded{ + X: structEmbeddedL1{ + X: &structEmbeddedL2{}, StructEmbed1: StructEmbed1{}, }, StructEmbed2: &StructEmbed2{}, }, want: `{"A":"","B":"","D":"","E":"","F":"","G":""}`, }, { - name: jsontest.Name("Structs/Inline/NonZero"), - in: structInlined{ - X: structInlinedL1{ - X: &structInlinedL2{A: "A1", B: "B1", C: "C1"}, + name: jsontest.Name("Structs/Embed/NonZero"), + in: structEmbedded{ + X: structEmbeddedL1{ + X: &structEmbeddedL2{A: "A1", B: "B1", C: "C1"}, StructEmbed1: StructEmbed1{C: "C2", D: "D2", E: "E2"}, }, StructEmbed2: &StructEmbed2{E: "E3", F: "F3", G: "G3"}, }, want: `{"A":"A1","B":"B1","D":"D2","E":"E3","F":"F3","G":"G3"}`, }, { - name: jsontest.Name("Structs/Inline/DualCycle"), + name: jsontest.Name("Structs/Embed/DualCycle"), in: cyclicA{ B1: cyclicB{F: 1}, // B1.F ignored since it conflicts with B2.F B2: cyclicB{F: 2}, // B2.F ignored since it conflicts with B1.F }, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/Nil"), - in: structInlineTextValue{X: jsontext.Value(nil)}, + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/Nil"), + in: structEmbedTextValue{X: jsontext.Value(nil)}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/Empty"), - in: structInlineTextValue{X: jsontext.Value("")}, + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/Empty"), + in: structEmbedTextValue{X: jsontext.Value("")}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/NonEmptyN1"), - in: structInlineTextValue{X: jsontext.Value(` { "fizz" : "buzz" } `)}, + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/NonEmptyN1"), + in: structEmbedTextValue{X: jsontext.Value(` { "fizz" : "buzz" } `)}, want: `{"fizz":"buzz"}`, }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/NonEmptyN2"), - in: structInlineTextValue{X: jsontext.Value(` { "fizz" : "buzz" , "foo" : "bar" } `)}, + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/NonEmptyN2"), + in: structEmbedTextValue{X: jsontext.Value(` { "fizz" : "buzz" , "foo" : "bar" } `)}, want: `{"fizz":"buzz","foo":"bar"}`, }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/NonEmptyWithOthers"), - in: structInlineTextValue{ + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/NonEmptyWithOthers"), + in: structEmbedTextValue{ A: 1, X: jsontext.Value(` { "fizz" : "buzz" , "foo" : "bar" } `), B: 2, }, - // NOTE: Inlined fallback fields are always serialized last. + // NOTE: Embedded fallback fields are always serialized last. want: `{"A":1,"B":2,"fizz":"buzz","foo":"bar"}`, }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/RejectDuplicateNames"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/RejectDuplicateNames"), opts: []Options{jsontext.AllowDuplicateNames(false)}, - in: structInlineTextValue{X: jsontext.Value(` { "fizz" : "buzz" , "fizz" : "buzz" } `)}, + in: structEmbedTextValue{X: jsontext.Value(` { "fizz" : "buzz" , "fizz" : "buzz" } `)}, want: `{"fizz":"buzz"`, wantErr: newDuplicateNameError("/fizz", nil, len64(`{"fizz":"buzz"`)), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/AllowDuplicateNames"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/AllowDuplicateNames"), opts: []Options{jsontext.AllowDuplicateNames(true)}, - in: structInlineTextValue{X: jsontext.Value(` { "fizz" : "buzz" , "fizz" : "buzz" } `)}, + in: structEmbedTextValue{X: jsontext.Value(` { "fizz" : "buzz" , "fizz" : "buzz" } `)}, want: `{"fizz":"buzz","fizz":"buzz"}`, }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/RejectInvalidUTF8"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/RejectInvalidUTF8"), opts: []Options{jsontext.AllowInvalidUTF8(false)}, - in: structInlineTextValue{X: jsontext.Value(`{"` + "\xde\xad\xbe\xef" + `":"value"}`)}, + in: structEmbedTextValue{X: jsontext.Value(`{"` + "\xde\xad\xbe\xef" + `":"value"}`)}, want: `{`, wantErr: newInvalidUTF8Error(len64(`{"`+"\xde\xad"), ""), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/AllowInvalidUTF8"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/AllowInvalidUTF8"), opts: []Options{jsontext.AllowInvalidUTF8(true)}, - in: structInlineTextValue{X: jsontext.Value(`{"` + "\xde\xad\xbe\xef" + `":"value"}`)}, + in: structEmbedTextValue{X: jsontext.Value(`{"` + "\xde\xad\xbe\xef" + `":"value"}`)}, want: `{"ޭ��":"value"}`, }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/InvalidWhitespace"), - in: structInlineTextValue{X: jsontext.Value("\n\r\t ")}, + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/InvalidWhitespace"), + in: structEmbedTextValue{X: jsontext.Value("\n\r\t ")}, want: `{`, wantErr: EM(io.ErrUnexpectedEOF).withPos(`{`, "").withType(0, T[jsontext.Value]()), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/InvalidObject"), - in: structInlineTextValue{X: jsontext.Value(` true `)}, + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/InvalidObject"), + in: structEmbedTextValue{X: jsontext.Value(` true `)}, want: `{`, - wantErr: EM(errRawInlinedNotObject).withPos(`{`, "").withType(0, T[jsontext.Value]()), + wantErr: EM(errRawEmbedNotObject).withPos(`{`, "").withType(0, T[jsontext.Value]()), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/InvalidObjectName"), - in: structInlineTextValue{X: jsontext.Value(` { true : false } `)}, + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/InvalidObjectName"), + in: structEmbedTextValue{X: jsontext.Value(` { true : false } `)}, want: `{`, wantErr: EM(newNonStringNameError(len64(" { "), "")).withPos(`{`, "").withType(0, T[jsontext.Value]()), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/InvalidEndObject"), - in: structInlineTextValue{X: jsontext.Value(` { "name" : false , } `)}, + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/InvalidEndObject"), + in: structEmbedTextValue{X: jsontext.Value(` { "name" : false , } `)}, want: `{"name":false`, wantErr: EM(newInvalidCharacterError(",", "at start of value", len64(` { "name" : false `), "")).withPos(`{"name":false,`, "").withType(0, T[jsontext.Value]()), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/InvalidDualObject"), - in: structInlineTextValue{X: jsontext.Value(`{}{}`)}, + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/InvalidDualObject"), + in: structEmbedTextValue{X: jsontext.Value(`{}{}`)}, want: `{`, wantErr: EM(newInvalidCharacterError("{", "after top-level value", len64(`{}`), "")).withPos(`{`, "").withType(0, T[jsontext.Value]()), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/Nested/Nil"), - in: structInlinePointerInlineTextValue{}, + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/Nested/Nil"), + in: structEmbedPointerEmbedTextValue{}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/PointerTextValue/Nil"), - in: structInlinePointerTextValue{}, + name: jsontest.Name("Structs/EmbeddedFallback/PointerTextValue/Nil"), + in: structEmbedPointerTextValue{}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/PointerTextValue/NonEmpty"), - in: structInlinePointerTextValue{X: addr(jsontext.Value(` { "fizz" : "buzz" } `))}, + name: jsontest.Name("Structs/EmbeddedFallback/PointerTextValue/NonEmpty"), + in: structEmbedPointerTextValue{X: addr(jsontext.Value(` { "fizz" : "buzz" } `))}, want: `{"fizz":"buzz"}`, }, { - name: jsontest.Name("Structs/InlinedFallback/PointerTextValue/Nested/Nil"), - in: structInlineInlinePointerTextValue{}, + name: jsontest.Name("Structs/EmbeddedFallback/PointerTextValue/Nested/Nil"), + in: structEmbedEmbedPointerTextValue{}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/Nil"), - in: structInlineMapStringAny{X: nil}, + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/Nil"), + in: structEmbedMapStringAny{X: nil}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/Empty"), - in: structInlineMapStringAny{X: make(jsonObject)}, + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/Empty"), + in: structEmbedMapStringAny{X: make(jsonObject)}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/NonEmptyN1"), - in: structInlineMapStringAny{X: jsonObject{"fizz": nil}}, + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/NonEmptyN1"), + in: structEmbedMapStringAny{X: jsonObject{"fizz": nil}}, want: `{"fizz":null}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/NonEmptyN2"), - in: structInlineMapStringAny{X: jsonObject{"fizz": time.Time{}, "buzz": math.Pi}}, + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/NonEmptyN2"), + in: structEmbedMapStringAny{X: jsonObject{"fizz": time.Time{}, "buzz": math.Pi}}, want: `{"buzz":3.141592653589793,"fizz":"0001-01-01T00:00:00Z"}`, canonicalize: true, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/NonEmptyWithOthers"), - in: structInlineMapStringAny{ + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/NonEmptyWithOthers"), + in: structEmbedMapStringAny{ A: 1, X: jsonObject{"fizz": nil}, B: 2, }, - // NOTE: Inlined fallback fields are always serialized last. + // NOTE: Embedded fallback fields are always serialized last. want: `{"A":1,"B":2,"fizz":null}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/RejectInvalidUTF8"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/RejectInvalidUTF8"), opts: []Options{jsontext.AllowInvalidUTF8(false)}, - in: structInlineMapStringAny{X: jsonObject{"\xde\xad\xbe\xef": nil}}, + in: structEmbedMapStringAny{X: jsonObject{"\xde\xad\xbe\xef": nil}}, want: `{`, wantErr: EM(jsonwire.ErrInvalidUTF8).withPos(`{`, "").withType(0, stringType), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/AllowInvalidUTF8"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/AllowInvalidUTF8"), opts: []Options{jsontext.AllowInvalidUTF8(true)}, - in: structInlineMapStringAny{X: jsonObject{"\xde\xad\xbe\xef": nil}}, + in: structEmbedMapStringAny{X: jsonObject{"\xde\xad\xbe\xef": nil}}, want: `{"ޭ��":null}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/InvalidValue"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/InvalidValue"), opts: []Options{jsontext.AllowInvalidUTF8(true)}, - in: structInlineMapStringAny{X: jsonObject{"name": make(chan string)}}, + in: structEmbedMapStringAny{X: jsonObject{"name": make(chan string)}}, want: `{"name"`, wantErr: EM(nil).withPos(`{"name":`, "/name").withType(0, T[chan string]()), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/Nested/Nil"), - in: structInlinePointerInlineMapStringAny{}, + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/Nested/Nil"), + in: structEmbedPointerEmbedMapStringAny{}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/MarshalFunc"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/MarshalFunc"), opts: []Options{ WithMarshalers(MarshalFunc(func(v float64) ([]byte, error) { return []byte(fmt.Sprintf(`"%v"`, v)), nil })), }, - in: structInlineMapStringAny{X: jsonObject{"fizz": 3.14159}}, + in: structEmbedMapStringAny{X: jsonObject{"fizz": 3.14159}}, want: `{"fizz":"3.14159"}`, }, { - name: jsontest.Name("Structs/InlinedFallback/PointerMapStringAny/Nil"), - in: structInlinePointerMapStringAny{X: nil}, + name: jsontest.Name("Structs/EmbeddedFallback/PointerMapStringAny/Nil"), + in: structEmbedPointerMapStringAny{X: nil}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/PointerMapStringAny/NonEmpty"), - in: structInlinePointerMapStringAny{X: addr(jsonObject{"name": "value"})}, + name: jsontest.Name("Structs/EmbeddedFallback/PointerMapStringAny/NonEmpty"), + in: structEmbedPointerMapStringAny{X: addr(jsonObject{"name": "value"})}, want: `{"name":"value"}`, }, { - name: jsontest.Name("Structs/InlinedFallback/PointerMapStringAny/Nested/Nil"), - in: structInlineInlinePointerMapStringAny{}, + name: jsontest.Name("Structs/EmbeddedFallback/PointerMapStringAny/Nested/Nil"), + in: structEmbedEmbedPointerMapStringAny{}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt"), - in: structInlineMapStringInt{ + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt"), + in: structEmbedMapStringInt{ X: map[string]int{"zero": 0, "one": 1, "two": 2}, }, want: `{"one":1,"two":2,"zero":0}`, canonicalize: true, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt/Deterministic"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt/Deterministic"), opts: []Options{Deterministic(true)}, - in: structInlineMapStringInt{ + in: structEmbedMapStringInt{ X: map[string]int{"zero": 0, "one": 1, "two": 2}, }, want: `{"one":1,"two":2,"zero":0}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt/Deterministic+AllowInvalidUTF8+RejectDuplicateNames"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt/Deterministic+AllowInvalidUTF8+RejectDuplicateNames"), opts: []Options{Deterministic(true), jsontext.AllowInvalidUTF8(true), jsontext.AllowDuplicateNames(false)}, - in: structInlineMapStringInt{ + in: structEmbedMapStringInt{ X: map[string]int{"\xff": 0, "\xfe": 1}, }, want: `{"�":1`, wantErr: newDuplicateNameError("", []byte(`"�"`), len64(`{"�":1`)), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt/Deterministic+AllowInvalidUTF8+AllowDuplicateNames"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt/Deterministic+AllowInvalidUTF8+AllowDuplicateNames"), opts: []Options{Deterministic(true), jsontext.AllowInvalidUTF8(true), jsontext.AllowDuplicateNames(true)}, - in: structInlineMapStringInt{ + in: structEmbedMapStringInt{ X: map[string]int{"\xff": 0, "\xfe": 1}, }, want: `{"�":1,"�":0}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt/StringifiedNumbers"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt/StringifiedNumbers"), opts: []Options{StringifyNumbers(true)}, - in: structInlineMapStringInt{ + in: structEmbedMapStringInt{ X: map[string]int{"zero": 0, "one": 1, "two": 2}, }, want: `{"one":"1","two":"2","zero":"0"}`, canonicalize: true, }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt/MarshalFunc"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt/MarshalFunc"), opts: []Options{ WithMarshalers(JoinMarshalers( - // Marshalers do not affect the string key of inlined maps. + // Marshalers do not affect the string key of embedded maps. MarshalFunc(func(v string) ([]byte, error) { return []byte(fmt.Sprintf(`"%q"`, strings.ToUpper(v))), nil }), @@ -2778,136 +2778,136 @@ return []byte(fmt.Sprintf(`"%v"`, v)), nil }), )), }, - in: structInlineMapStringInt{ + in: structEmbedMapStringInt{ X: map[string]int{"zero": 0, "one": 1, "two": 2}, }, want: `{"one":"1","two":"2","zero":"0"}`, canonicalize: true, }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringInt"), - in: structInlineMapNamedStringInt{ + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringInt"), + in: structEmbedMapNamedStringInt{ X: map[namedString]int{"zero": 0, "one": 1, "two": 2}, }, want: `{"one":1,"two":2,"zero":0}`, canonicalize: true, }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringInt/Deterministic"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringInt/Deterministic"), opts: []Options{Deterministic(true)}, - in: structInlineMapNamedStringInt{ + in: structEmbedMapNamedStringInt{ X: map[namedString]int{"zero": 0, "one": 1, "two": 2}, }, want: `{"one":1,"two":2,"zero":0}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/Nil"), - in: structInlineMapNamedStringAny{X: nil}, + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/Nil"), + in: structEmbedMapNamedStringAny{X: nil}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/Empty"), - in: structInlineMapNamedStringAny{X: make(map[namedString]any)}, + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/Empty"), + in: structEmbedMapNamedStringAny{X: make(map[namedString]any)}, want: `{}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/NonEmptyN1"), - in: structInlineMapNamedStringAny{X: map[namedString]any{"fizz": nil}}, + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/NonEmptyN1"), + in: structEmbedMapNamedStringAny{X: map[namedString]any{"fizz": nil}}, want: `{"fizz":null}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/NonEmptyN2"), - in: structInlineMapNamedStringAny{X: map[namedString]any{"fizz": time.Time{}, "buzz": math.Pi}}, + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/NonEmptyN2"), + in: structEmbedMapNamedStringAny{X: map[namedString]any{"fizz": time.Time{}, "buzz": math.Pi}}, want: `{"buzz":3.141592653589793,"fizz":"0001-01-01T00:00:00Z"}`, canonicalize: true, }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/NonEmptyWithOthers"), - in: structInlineMapNamedStringAny{ + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/NonEmptyWithOthers"), + in: structEmbedMapNamedStringAny{ A: 1, X: map[namedString]any{"fizz": nil}, B: 2, }, - // NOTE: Inlined fallback fields are always serialized last. + // NOTE: Embedded fallback fields are always serialized last. want: `{"A":1,"B":2,"fizz":null}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/RejectInvalidUTF8"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/RejectInvalidUTF8"), opts: []Options{jsontext.AllowInvalidUTF8(false)}, - in: structInlineMapNamedStringAny{X: map[namedString]any{"\xde\xad\xbe\xef": nil}}, + in: structEmbedMapNamedStringAny{X: map[namedString]any{"\xde\xad\xbe\xef": nil}}, want: `{`, wantErr: EM(jsonwire.ErrInvalidUTF8).withPos(`{`, "").withType(0, T[namedString]()), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/AllowInvalidUTF8"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/AllowInvalidUTF8"), opts: []Options{jsontext.AllowInvalidUTF8(true)}, - in: structInlineMapNamedStringAny{X: map[namedString]any{"\xde\xad\xbe\xef": nil}}, + in: structEmbedMapNamedStringAny{X: map[namedString]any{"\xde\xad\xbe\xef": nil}}, want: `{"ޭ��":null}`, }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/InvalidValue"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/InvalidValue"), opts: []Options{jsontext.AllowInvalidUTF8(true)}, - in: structInlineMapNamedStringAny{X: map[namedString]any{"name": make(chan string)}}, + in: structEmbedMapNamedStringAny{X: map[namedString]any{"name": make(chan string)}}, want: `{"name"`, wantErr: EM(nil).withPos(`{"name":`, "/name").withType(0, T[chan string]()), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/MarshalFunc"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/MarshalFunc"), opts: []Options{ WithMarshalers(MarshalFunc(func(v float64) ([]byte, error) { return []byte(fmt.Sprintf(`"%v"`, v)), nil })), }, - in: structInlineMapNamedStringAny{X: map[namedString]any{"fizz": 3.14159}}, + in: structEmbedMapNamedStringAny{X: map[namedString]any{"fizz": 3.14159}}, want: `{"fizz":"3.14159"}`, }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineTextValue/Other"), - in: structNoCaseInlineTextValue{ + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedTextValue/Other"), + in: structNoCaseEmbedTextValue{ X: jsontext.Value(`{"dupe":"","dupe":""}`), }, want: `{"dupe":""`, wantErr: newDuplicateNameError("", []byte(`"dupe"`), len64(`{"dupe":""`)), }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineTextValue/Other/AllowDuplicateNames"), + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedTextValue/Other/AllowDuplicateNames"), opts: []Options{jsontext.AllowDuplicateNames(true)}, - in: structNoCaseInlineTextValue{ + in: structNoCaseEmbedTextValue{ X: jsontext.Value(`{"dupe": "", "dupe": ""}`), }, want: `{"dupe":"","dupe":""}`, }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineTextValue/ExactDifferent"), - in: structNoCaseInlineTextValue{ + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedTextValue/ExactDifferent"), + in: structNoCaseEmbedTextValue{ X: jsontext.Value(`{"Aaa": "", "AaA": "", "AAa": "", "AAA": ""}`), }, want: `{"Aaa":"","AaA":"","AAa":"","AAA":""}`, }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineTextValue/ExactConflict"), - in: structNoCaseInlineTextValue{ + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedTextValue/ExactConflict"), + in: structNoCaseEmbedTextValue{ X: jsontext.Value(`{"Aaa": "", "Aaa": ""}`), }, want: `{"Aaa":""`, wantErr: newDuplicateNameError("", []byte(`"Aaa"`), len64(`{"Aaa":""`)), }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineTextValue/ExactConflict/AllowDuplicateNames"), + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedTextValue/ExactConflict/AllowDuplicateNames"), opts: []Options{jsontext.AllowDuplicateNames(true)}, - in: structNoCaseInlineTextValue{ + in: structNoCaseEmbedTextValue{ X: jsontext.Value(`{"Aaa": "", "Aaa": ""}`), }, want: `{"Aaa":"","Aaa":""}`, }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineTextValue/NoCaseConflict"), - in: structNoCaseInlineTextValue{ + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedTextValue/NoCaseConflict"), + in: structNoCaseEmbedTextValue{ X: jsontext.Value(`{"Aaa": "", "AaA": "", "aaa": ""}`), }, want: `{"Aaa":"","AaA":""`, wantErr: newDuplicateNameError("", []byte(`"aaa"`), len64(`{"Aaa":"","AaA":""`)), }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineTextValue/NoCaseConflict/AllowDuplicateNames"), + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedTextValue/NoCaseConflict/AllowDuplicateNames"), opts: []Options{jsontext.AllowDuplicateNames(true)}, - in: structNoCaseInlineTextValue{ + in: structNoCaseEmbedTextValue{ X: jsontext.Value(`{"Aaa": "", "AaA": "", "aaa": ""}`), }, want: `{"Aaa":"","AaA":"","aaa":""}`, }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineTextValue/ExactDifferentWithField"), - in: structNoCaseInlineTextValue{ + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedTextValue/ExactDifferentWithField"), + in: structNoCaseEmbedTextValue{ AAA: "x", AaA: "x", X: jsontext.Value(`{"Aaa": ""}`), }, want: `{"AAA":"x","AaA":"x","Aaa":""}`, }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineTextValue/ExactConflictWithField"), - in: structNoCaseInlineTextValue{ + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedTextValue/ExactConflictWithField"), + in: structNoCaseEmbedTextValue{ AAA: "x", AaA: "x", X: jsontext.Value(`{"AAA": ""}`), @@ -2915,8 +2915,8 @@ }, want: `{"AAA":"x","AaA":"x"`, wantErr: newDuplicateNameError("", []byte(`"AAA"`), len64(`{"AAA":"x","AaA":"x"`)), }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineTextValue/NoCaseConflictWithField"), - in: structNoCaseInlineTextValue{ + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedTextValue/NoCaseConflictWithField"), + in: structNoCaseEmbedTextValue{ AAA: "x", AaA: "x", X: jsontext.Value(`{"aaa": ""}`), @@ -2925,7 +2925,7 @@ want: `{"AAA":"x","AaA":"x"`, wantErr: newDuplicateNameError("", []byte(`"aaa"`), len64(`{"AAA":"x","AaA":"x"`)), }, { name: jsontest.Name("Structs/DuplicateName/MatchCaseInsensitiveDelimiter"), - in: structNoCaseInlineTextValue{ + in: structNoCaseEmbedTextValue{ AaA: "x", X: jsontext.Value(`{"aa_a": ""}`), }, @@ -2934,7 +2934,7 @@ wantErr: newDuplicateNameError("", []byte(`"aa_a"`), len64(`{"AaA":"x"`)), }, { name: jsontest.Name("Structs/DuplicateName/MatchCaseSensitiveDelimiter"), opts: []Options{jsonflags.MatchCaseSensitiveDelimiter | 1}, - in: structNoCaseInlineTextValue{ + in: structNoCaseEmbedTextValue{ AaA: "x", X: jsontext.Value(`{"aa_a": ""}`), }, @@ -2942,7 +2942,7 @@ want: `{"AaA":"x","aa_a":""}`, }, { name: jsontest.Name("Structs/DuplicateName/MatchCaseInsensitiveNames+MatchCaseSensitiveDelimiter"), opts: []Options{MatchCaseInsensitiveNames(true), jsonflags.MatchCaseSensitiveDelimiter | 1}, - in: structNoCaseInlineTextValue{ + in: structNoCaseEmbedTextValue{ AaA: "x", X: jsontext.Value(`{"aa_a": ""}`), }, @@ -2950,30 +2950,30 @@ want: `{"AaA":"x","aa_a":""}`, }, { name: jsontest.Name("Structs/DuplicateName/MatchCaseInsensitiveNames+MatchCaseSensitiveDelimiter"), opts: []Options{MatchCaseInsensitiveNames(true), jsonflags.MatchCaseSensitiveDelimiter | 1}, - in: structNoCaseInlineTextValue{ + in: structNoCaseEmbedTextValue{ AA_b: "x", X: jsontext.Value(`{"aa_b": ""}`), }, want: `{"AA_b":"x"`, wantErr: newDuplicateNameError("", []byte(`"aa_b"`), len64(`{"AA_b":"x"`)), }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineMapStringAny/ExactDifferent"), - in: structNoCaseInlineMapStringAny{ + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedMapStringAny/ExactDifferent"), + in: structNoCaseEmbedMapStringAny{ X: jsonObject{"Aaa": "", "AaA": "", "AAa": "", "AAA": ""}, }, want: `{"AAA":"","AAa":"","AaA":"","Aaa":""}`, canonicalize: true, }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineMapStringAny/ExactDifferentWithField"), - in: structNoCaseInlineMapStringAny{ + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedMapStringAny/ExactDifferentWithField"), + in: structNoCaseEmbedMapStringAny{ AAA: "x", AaA: "x", X: jsonObject{"Aaa": ""}, }, want: `{"AAA":"x","AaA":"x","Aaa":""}`, }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineMapStringAny/ExactConflictWithField"), - in: structNoCaseInlineMapStringAny{ + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedMapStringAny/ExactConflictWithField"), + in: structNoCaseEmbedMapStringAny{ AAA: "x", AaA: "x", X: jsonObject{"AAA": ""}, @@ -2981,8 +2981,8 @@ }, want: `{"AAA":"x","AaA":"x"`, wantErr: newDuplicateNameError("", []byte(`"AAA"`), len64(`{"AAA":"x","AaA":"x"`)), }, { - name: jsontest.Name("Structs/DuplicateName/NoCaseInlineMapStringAny/NoCaseConflictWithField"), - in: structNoCaseInlineMapStringAny{ + name: jsontest.Name("Structs/DuplicateName/NoCaseEmbedMapStringAny/NoCaseConflictWithField"), + in: structNoCaseEmbedMapStringAny{ AAA: "x", AaA: "x", X: jsonObject{"aaa": ""}, @@ -4014,10 +4014,10 @@ }, in: structOmitZeroAll{}, want: `{}`, }, { - name: jsontest.Name("Functions/Struct/Inlined"), + name: jsontest.Name("Functions/Struct/Embedded"), opts: []Options{ WithMarshalers(JoinMarshalers( - MarshalFunc(func(v structInlinedL1) ([]byte, error) { + MarshalFunc(func(v structEmbeddedL1) ([]byte, error) { panic("should not be called") }), MarshalToFunc(func(enc *jsontext.Encoder, v *StructEmbed2) error { @@ -4025,7 +4025,7 @@ panic("should not be called") }), )), }, - in: structInlined{}, + in: structEmbedded{}, want: `{"D":""}`, }, { name: jsontest.Name("Functions/Slice/Elem"), @@ -6731,265 +6731,265 @@ inBuf: `{"Interface": "anything"}`, inVal: new(structFormatInvalid), wantErr: EU(errInvalidFormatFlag).withPos(`{"Interface": `, "/Interface").withType(0, T[any]()), }, { - name: jsontest.Name("Structs/Inline/Zero"), + name: jsontest.Name("Structs/Embed/Zero"), inBuf: `{"D":""}`, - inVal: new(structInlined), - want: new(structInlined), + inVal: new(structEmbedded), + want: new(structEmbedded), }, { - name: jsontest.Name("Structs/Inline/Alloc"), + name: jsontest.Name("Structs/Embed/Alloc"), inBuf: `{"E":"","F":"","G":"","A":"","B":"","D":""}`, - inVal: new(structInlined), - want: addr(structInlined{ - X: structInlinedL1{ - X: &structInlinedL2{}, + inVal: new(structEmbedded), + want: addr(structEmbedded{ + X: structEmbeddedL1{ + X: &structEmbeddedL2{}, StructEmbed1: StructEmbed1{}, }, StructEmbed2: &StructEmbed2{}, }), }, { - name: jsontest.Name("Structs/Inline/NonZero"), + name: jsontest.Name("Structs/Embed/NonZero"), inBuf: `{"E":"E3","F":"F3","G":"G3","A":"A1","B":"B1","D":"D2"}`, - inVal: new(structInlined), - want: addr(structInlined{ - X: structInlinedL1{ - X: &structInlinedL2{A: "A1", B: "B1" /* C: "C1" */}, + inVal: new(structEmbedded), + want: addr(structEmbedded{ + X: structEmbeddedL1{ + X: &structEmbeddedL2{A: "A1", B: "B1" /* C: "C1" */}, StructEmbed1: StructEmbed1{ /* C: "C2" */ D: "D2" /* E: "E2" */}, }, StructEmbed2: &StructEmbed2{E: "E3", F: "F3", G: "G3"}, }), }, { - name: jsontest.Name("Structs/Inline/Merge"), + name: jsontest.Name("Structs/Embed/Merge"), inBuf: `{"E":"E3","F":"F3","G":"G3","A":"A1","B":"B1","D":"D2"}`, - inVal: addr(structInlined{ - X: structInlinedL1{ - X: &structInlinedL2{B: "##", C: "C1"}, + inVal: addr(structEmbedded{ + X: structEmbeddedL1{ + X: &structEmbeddedL2{B: "##", C: "C1"}, StructEmbed1: StructEmbed1{C: "C2", E: "E2"}, }, StructEmbed2: &StructEmbed2{E: "##", G: "G3"}, }), - want: addr(structInlined{ - X: structInlinedL1{ - X: &structInlinedL2{A: "A1", B: "B1", C: "C1"}, + want: addr(structEmbedded{ + X: structEmbeddedL1{ + X: &structEmbeddedL2{A: "A1", B: "B1", C: "C1"}, StructEmbed1: StructEmbed1{C: "C2", D: "D2", E: "E2"}, }, StructEmbed2: &StructEmbed2{E: "E3", F: "F3", G: "G3"}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/Noop"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/Noop"), inBuf: `{"A":1,"B":2}`, - inVal: new(structInlineTextValue), - want: addr(structInlineTextValue{A: 1, X: jsontext.Value(nil), B: 2}), + inVal: new(structEmbedTextValue), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value(nil), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/MergeN1/Nil"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/MergeN1/Nil"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: new(structInlineTextValue), - want: addr(structInlineTextValue{A: 1, X: jsontext.Value(`{"fizz":"buzz"}`), B: 2}), + inVal: new(structEmbedTextValue), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value(`{"fizz":"buzz"}`), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/MergeN1/Empty"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/MergeN1/Empty"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: addr(structInlineTextValue{X: jsontext.Value{}}), - want: addr(structInlineTextValue{A: 1, X: jsontext.Value(`{"fizz":"buzz"}`), B: 2}), + inVal: addr(structEmbedTextValue{X: jsontext.Value{}}), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value(`{"fizz":"buzz"}`), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/MergeN1/Whitespace"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/MergeN1/Whitespace"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: addr(structInlineTextValue{X: jsontext.Value("\n\r\t ")}), - want: addr(structInlineTextValue{A: 1, X: jsontext.Value("")}), - wantErr: EU(errRawInlinedNotObject).withPos(`{"A":1,`, "/fizz").withType('"', T[jsontext.Value]()), + inVal: addr(structEmbedTextValue{X: jsontext.Value("\n\r\t ")}), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value("")}), + wantErr: EU(errRawEmbedNotObject).withPos(`{"A":1,`, "/fizz").withType('"', T[jsontext.Value]()), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/MergeN1/Null"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/MergeN1/Null"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: addr(structInlineTextValue{X: jsontext.Value("null")}), - want: addr(structInlineTextValue{A: 1, X: jsontext.Value("null")}), - wantErr: EU(errRawInlinedNotObject).withPos(`{"A":1,`, "/fizz").withType('"', T[jsontext.Value]()), + inVal: addr(structEmbedTextValue{X: jsontext.Value("null")}), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value("null")}), + wantErr: EU(errRawEmbedNotObject).withPos(`{"A":1,`, "/fizz").withType('"', T[jsontext.Value]()), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/MergeN1/ObjectN0"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/MergeN1/ObjectN0"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: addr(structInlineTextValue{X: jsontext.Value(` { } `)}), - want: addr(structInlineTextValue{A: 1, X: jsontext.Value(` {"fizz":"buzz"}`), B: 2}), + inVal: addr(structEmbedTextValue{X: jsontext.Value(` { } `)}), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value(` {"fizz":"buzz"}`), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/MergeN2/ObjectN1"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/MergeN2/ObjectN1"), inBuf: `{"A":1,"fizz":"buzz","B":2,"foo": [ 1 , 2 , 3 ]}`, - inVal: addr(structInlineTextValue{X: jsontext.Value(` { "fizz" : "buzz" } `)}), - want: addr(structInlineTextValue{A: 1, X: jsontext.Value(` { "fizz" : "buzz","fizz":"buzz","foo":[ 1 , 2 , 3 ]}`), B: 2}), + inVal: addr(structEmbedTextValue{X: jsontext.Value(` { "fizz" : "buzz" } `)}), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value(` { "fizz" : "buzz","fizz":"buzz","foo":[ 1 , 2 , 3 ]}`), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/Merge/EndObject"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/Merge/EndObject"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: addr(structInlineTextValue{X: jsontext.Value(` } `)}), + inVal: addr(structEmbedTextValue{X: jsontext.Value(` } `)}), // NOTE: This produces invalid output, // but the value being merged into is already invalid. - want: addr(structInlineTextValue{A: 1, X: jsontext.Value(`,"fizz":"buzz"}`), B: 2}), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value(`,"fizz":"buzz"}`), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/MergeInvalidValue"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/MergeInvalidValue"), inBuf: `{"A":1,"fizz":nil,"B":2}`, - inVal: new(structInlineTextValue), - want: addr(structInlineTextValue{A: 1, X: jsontext.Value(`{"fizz":`)}), + inVal: new(structEmbedTextValue), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value(`{"fizz":`)}), wantErr: newInvalidCharacterError("i", "in literal null (expecting 'u')", len64(`{"A":1,"fizz":n`), "/fizz"), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/CaseSensitive"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/CaseSensitive"), inBuf: `{"A":1,"fizz":"buzz","B":2,"a":3}`, - inVal: new(structInlineTextValue), - want: addr(structInlineTextValue{A: 1, X: jsontext.Value(`{"fizz":"buzz","a":3}`), B: 2}), + inVal: new(structEmbedTextValue), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value(`{"fizz":"buzz","a":3}`), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/RejectDuplicateNames"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/RejectDuplicateNames"), opts: []Options{jsontext.AllowDuplicateNames(false)}, inBuf: `{"A":1,"fizz":"buzz","B":2,"fizz":"buzz"}`, - inVal: new(structInlineTextValue), - want: addr(structInlineTextValue{A: 1, X: jsontext.Value(`{"fizz":"buzz"}`), B: 2}), + inVal: new(structEmbedTextValue), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value(`{"fizz":"buzz"}`), B: 2}), wantErr: newDuplicateNameError("", []byte(`"fizz"`), len64(`{"A":1,"fizz":"buzz","B":2,`)), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/AllowDuplicateNames"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/AllowDuplicateNames"), opts: []Options{jsontext.AllowDuplicateNames(true)}, inBuf: `{"A":1,"fizz":"buzz","B":2,"fizz":"buzz"}`, - inVal: new(structInlineTextValue), - want: addr(structInlineTextValue{A: 1, X: jsontext.Value(`{"fizz":"buzz","fizz":"buzz"}`), B: 2}), + inVal: new(structEmbedTextValue), + want: addr(structEmbedTextValue{A: 1, X: jsontext.Value(`{"fizz":"buzz","fizz":"buzz"}`), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/Nested/Noop"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/Nested/Noop"), inBuf: `{}`, - inVal: new(structInlinePointerInlineTextValue), - want: new(structInlinePointerInlineTextValue), + inVal: new(structEmbedPointerEmbedTextValue), + want: new(structEmbedPointerEmbedTextValue), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/Nested/Alloc"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/Nested/Alloc"), inBuf: `{"A":1,"fizz":"buzz"}`, - inVal: new(structInlinePointerInlineTextValue), - want: addr(structInlinePointerInlineTextValue{ + inVal: new(structEmbedPointerEmbedTextValue), + want: addr(structEmbedPointerEmbedTextValue{ X: &struct { A int - X jsontext.Value `json:",inline"` + X jsontext.Value `json:",embed"` }{A: 1, X: jsontext.Value(`{"fizz":"buzz"}`)}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/TextValue/Nested/Merge"), + name: jsontest.Name("Structs/EmbeddedFallback/TextValue/Nested/Merge"), inBuf: `{"fizz":"buzz"}`, - inVal: addr(structInlinePointerInlineTextValue{ + inVal: addr(structEmbedPointerEmbedTextValue{ X: &struct { A int - X jsontext.Value `json:",inline"` + X jsontext.Value `json:",embed"` }{A: 1}, }), - want: addr(structInlinePointerInlineTextValue{ + want: addr(structEmbedPointerEmbedTextValue{ X: &struct { A int - X jsontext.Value `json:",inline"` + X jsontext.Value `json:",embed"` }{A: 1, X: jsontext.Value(`{"fizz":"buzz"}`)}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/PointerTextValue/Noop"), + name: jsontest.Name("Structs/EmbeddedFallback/PointerTextValue/Noop"), inBuf: `{"A":1,"B":2}`, - inVal: new(structInlinePointerTextValue), - want: addr(structInlinePointerTextValue{A: 1, X: nil, B: 2}), + inVal: new(structEmbedPointerTextValue), + want: addr(structEmbedPointerTextValue{A: 1, X: nil, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/PointerTextValue/Alloc"), + name: jsontest.Name("Structs/EmbeddedFallback/PointerTextValue/Alloc"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: new(structInlinePointerTextValue), - want: addr(structInlinePointerTextValue{A: 1, X: addr(jsontext.Value(`{"fizz":"buzz"}`)), B: 2}), + inVal: new(structEmbedPointerTextValue), + want: addr(structEmbedPointerTextValue{A: 1, X: addr(jsontext.Value(`{"fizz":"buzz"}`)), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/PointerTextValue/Merge"), + name: jsontest.Name("Structs/EmbeddedFallback/PointerTextValue/Merge"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: addr(structInlinePointerTextValue{X: addr(jsontext.Value(`{"fizz":"buzz"}`))}), - want: addr(structInlinePointerTextValue{A: 1, X: addr(jsontext.Value(`{"fizz":"buzz","fizz":"buzz"}`)), B: 2}), + inVal: addr(structEmbedPointerTextValue{X: addr(jsontext.Value(`{"fizz":"buzz"}`))}), + want: addr(structEmbedPointerTextValue{A: 1, X: addr(jsontext.Value(`{"fizz":"buzz","fizz":"buzz"}`)), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/PointerTextValue/Nested/Nil"), + name: jsontest.Name("Structs/EmbeddedFallback/PointerTextValue/Nested/Nil"), inBuf: `{"fizz":"buzz"}`, - inVal: new(structInlineInlinePointerTextValue), - want: addr(structInlineInlinePointerTextValue{ + inVal: new(structEmbedEmbedPointerTextValue), + want: addr(structEmbedEmbedPointerTextValue{ X: struct { - X *jsontext.Value `json:",inline"` + X *jsontext.Value `json:",embed"` }{X: addr(jsontext.Value(`{"fizz":"buzz"}`))}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/Noop"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/Noop"), inBuf: `{"A":1,"B":2}`, - inVal: new(structInlineMapStringAny), - want: addr(structInlineMapStringAny{A: 1, X: nil, B: 2}), + inVal: new(structEmbedMapStringAny), + want: addr(structEmbedMapStringAny{A: 1, X: nil, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/MergeN1/Nil"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/MergeN1/Nil"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: new(structInlineMapStringAny), - want: addr(structInlineMapStringAny{A: 1, X: jsonObject{"fizz": "buzz"}, B: 2}), + inVal: new(structEmbedMapStringAny), + want: addr(structEmbedMapStringAny{A: 1, X: jsonObject{"fizz": "buzz"}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/MergeN1/Empty"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/MergeN1/Empty"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: addr(structInlineMapStringAny{X: jsonObject{}}), - want: addr(structInlineMapStringAny{A: 1, X: jsonObject{"fizz": "buzz"}, B: 2}), + inVal: addr(structEmbedMapStringAny{X: jsonObject{}}), + want: addr(structEmbedMapStringAny{A: 1, X: jsonObject{"fizz": "buzz"}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/MergeN1/ObjectN1"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/MergeN1/ObjectN1"), inBuf: `{"A":1,"fizz":{"charlie":"DELTA","echo":"foxtrot"},"B":2}`, - inVal: addr(structInlineMapStringAny{X: jsonObject{"fizz": jsonObject{ + inVal: addr(structEmbedMapStringAny{X: jsonObject{"fizz": jsonObject{ "alpha": "bravo", "charlie": "delta", }}}), - want: addr(structInlineMapStringAny{A: 1, X: jsonObject{"fizz": jsonObject{ + want: addr(structEmbedMapStringAny{A: 1, X: jsonObject{"fizz": jsonObject{ "alpha": "bravo", "charlie": "DELTA", "echo": "foxtrot", }}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/MergeN2/ObjectN1"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/MergeN2/ObjectN1"), inBuf: `{"A":1,"fizz":"buzz","B":2,"foo": [ 1 , 2 , 3 ]}`, - inVal: addr(structInlineMapStringAny{X: jsonObject{"fizz": "wuzz"}}), - want: addr(structInlineMapStringAny{A: 1, X: jsonObject{"fizz": "buzz", "foo": jsonArray{1.0, 2.0, 3.0}}, B: 2}), + inVal: addr(structEmbedMapStringAny{X: jsonObject{"fizz": "wuzz"}}), + want: addr(structEmbedMapStringAny{A: 1, X: jsonObject{"fizz": "buzz", "foo": jsonArray{1.0, 2.0, 3.0}}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/MergeInvalidValue"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/MergeInvalidValue"), inBuf: `{"A":1,"fizz":nil,"B":2}`, - inVal: new(structInlineMapStringAny), - want: addr(structInlineMapStringAny{A: 1, X: jsonObject{"fizz": nil}}), + inVal: new(structEmbedMapStringAny), + want: addr(structEmbedMapStringAny{A: 1, X: jsonObject{"fizz": nil}}), wantErr: newInvalidCharacterError("i", "in literal null (expecting 'u')", len64(`{"A":1,"fizz":n`), "/fizz"), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/MergeInvalidValue/Existing"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/MergeInvalidValue/Existing"), inBuf: `{"A":1,"fizz":nil,"B":2}`, - inVal: addr(structInlineMapStringAny{A: 1, X: jsonObject{"fizz": true}}), - want: addr(structInlineMapStringAny{A: 1, X: jsonObject{"fizz": true}}), + inVal: addr(structEmbedMapStringAny{A: 1, X: jsonObject{"fizz": true}}), + want: addr(structEmbedMapStringAny{A: 1, X: jsonObject{"fizz": true}}), wantErr: newInvalidCharacterError("i", "in literal null (expecting 'u')", len64(`{"A":1,"fizz":n`), "/fizz"), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/CaseSensitive"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/CaseSensitive"), inBuf: `{"A":1,"fizz":"buzz","B":2,"a":3}`, - inVal: new(structInlineMapStringAny), - want: addr(structInlineMapStringAny{A: 1, X: jsonObject{"fizz": "buzz", "a": 3.0}, B: 2}), + inVal: new(structEmbedMapStringAny), + want: addr(structEmbedMapStringAny{A: 1, X: jsonObject{"fizz": "buzz", "a": 3.0}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/RejectDuplicateNames"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/RejectDuplicateNames"), opts: []Options{jsontext.AllowDuplicateNames(false)}, inBuf: `{"A":1,"fizz":"buzz","B":2,"fizz":"buzz"}`, - inVal: new(structInlineMapStringAny), - want: addr(structInlineMapStringAny{A: 1, X: jsonObject{"fizz": "buzz"}, B: 2}), + inVal: new(structEmbedMapStringAny), + want: addr(structEmbedMapStringAny{A: 1, X: jsonObject{"fizz": "buzz"}, B: 2}), wantErr: newDuplicateNameError("", []byte(`"fizz"`), len64(`{"A":1,"fizz":"buzz","B":2,`)), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/AllowDuplicateNames"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/AllowDuplicateNames"), opts: []Options{jsontext.AllowDuplicateNames(true)}, inBuf: `{"A":1,"fizz":{"one":1,"two":-2},"B":2,"fizz":{"two":2,"three":3}}`, - inVal: new(structInlineMapStringAny), - want: addr(structInlineMapStringAny{A: 1, X: jsonObject{"fizz": jsonObject{"one": 1.0, "two": 2.0, "three": 3.0}}, B: 2}), + inVal: new(structEmbedMapStringAny), + want: addr(structEmbedMapStringAny{A: 1, X: jsonObject{"fizz": jsonObject{"one": 1.0, "two": 2.0, "three": 3.0}}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/Nested/Noop"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/Nested/Noop"), inBuf: `{}`, - inVal: new(structInlinePointerInlineMapStringAny), - want: new(structInlinePointerInlineMapStringAny), + inVal: new(structEmbedPointerEmbedMapStringAny), + want: new(structEmbedPointerEmbedMapStringAny), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/Nested/Alloc"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/Nested/Alloc"), inBuf: `{"A":1,"fizz":"buzz"}`, - inVal: new(structInlinePointerInlineMapStringAny), - want: addr(structInlinePointerInlineMapStringAny{ + inVal: new(structEmbedPointerEmbedMapStringAny), + want: addr(structEmbedPointerEmbedMapStringAny{ X: &struct { A int - X jsonObject `json:",inline"` + X jsonObject `json:",embed"` }{A: 1, X: jsonObject{"fizz": "buzz"}}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringAny/Nested/Merge"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringAny/Nested/Merge"), inBuf: `{"fizz":"buzz"}`, - inVal: addr(structInlinePointerInlineMapStringAny{ + inVal: addr(structEmbedPointerEmbedMapStringAny{ X: &struct { A int - X jsonObject `json:",inline"` + X jsonObject `json:",embed"` }{A: 1}, }), - want: addr(structInlinePointerInlineMapStringAny{ + want: addr(structEmbedPointerEmbedMapStringAny{ X: &struct { A int - X jsonObject `json:",inline"` + X jsonObject `json:",embed"` }{A: 1, X: jsonObject{"fizz": "buzz"}}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt/UnmarshalFunc"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt/UnmarshalFunc"), opts: []Options{ WithUnmarshalers(UnmarshalFunc(func(b []byte, v *any) error { var err error @@ -6998,64 +6998,64 @@ return err })), }, inBuf: `{"D":"1.1","E":"2.2","F":"3.3"}`, - inVal: new(structInlineMapStringAny), - want: addr(structInlineMapStringAny{X: jsonObject{"D": 1.1, "E": 2.2, "F": 3.3}}), + inVal: new(structEmbedMapStringAny), + want: addr(structEmbedMapStringAny{X: jsonObject{"D": 1.1, "E": 2.2, "F": 3.3}}), }, { - name: jsontest.Name("Structs/InlinedFallback/PointerMapStringAny/Noop"), + name: jsontest.Name("Structs/EmbeddedFallback/PointerMapStringAny/Noop"), inBuf: `{"A":1,"B":2}`, - inVal: new(structInlinePointerMapStringAny), - want: addr(structInlinePointerMapStringAny{A: 1, X: nil, B: 2}), + inVal: new(structEmbedPointerMapStringAny), + want: addr(structEmbedPointerMapStringAny{A: 1, X: nil, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/PointerMapStringAny/Alloc"), + name: jsontest.Name("Structs/EmbeddedFallback/PointerMapStringAny/Alloc"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: new(structInlinePointerMapStringAny), - want: addr(structInlinePointerMapStringAny{A: 1, X: addr(jsonObject{"fizz": "buzz"}), B: 2}), + inVal: new(structEmbedPointerMapStringAny), + want: addr(structEmbedPointerMapStringAny{A: 1, X: addr(jsonObject{"fizz": "buzz"}), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/PointerMapStringAny/Merge"), + name: jsontest.Name("Structs/EmbeddedFallback/PointerMapStringAny/Merge"), inBuf: `{"A":1,"fizz":"wuzz","B":2}`, - inVal: addr(structInlinePointerMapStringAny{X: addr(jsonObject{"fizz": "buzz"})}), - want: addr(structInlinePointerMapStringAny{A: 1, X: addr(jsonObject{"fizz": "wuzz"}), B: 2}), + inVal: addr(structEmbedPointerMapStringAny{X: addr(jsonObject{"fizz": "buzz"})}), + want: addr(structEmbedPointerMapStringAny{A: 1, X: addr(jsonObject{"fizz": "wuzz"}), B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/PointerMapStringAny/Nested/Nil"), + name: jsontest.Name("Structs/EmbeddedFallback/PointerMapStringAny/Nested/Nil"), inBuf: `{"fizz":"buzz"}`, - inVal: new(structInlineInlinePointerMapStringAny), - want: addr(structInlineInlinePointerMapStringAny{ + inVal: new(structEmbedEmbedPointerMapStringAny), + want: addr(structEmbedEmbedPointerMapStringAny{ X: struct { - X *jsonObject `json:",inline"` + X *jsonObject `json:",embed"` }{X: addr(jsonObject{"fizz": "buzz"})}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt"), inBuf: `{"zero": 0, "one": 1, "two": 2}`, - inVal: new(structInlineMapStringInt), - want: addr(structInlineMapStringInt{ + inVal: new(structEmbedMapStringInt), + want: addr(structEmbedMapStringInt{ X: map[string]int{"zero": 0, "one": 1, "two": 2}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt/Null"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt/Null"), inBuf: `{"zero": 0, "one": null, "two": 2}`, - inVal: new(structInlineMapStringInt), - want: addr(structInlineMapStringInt{ + inVal: new(structEmbedMapStringInt), + want: addr(structEmbedMapStringInt{ X: map[string]int{"zero": 0, "one": 0, "two": 2}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt/Invalid"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt/Invalid"), inBuf: `{"zero": 0, "one": {}, "two": 2}`, - inVal: new(structInlineMapStringInt), - want: addr(structInlineMapStringInt{ + inVal: new(structEmbedMapStringInt), + want: addr(structEmbedMapStringInt{ X: map[string]int{"zero": 0, "one": 0}, }), wantErr: EU(nil).withPos(`{"zero": 0, "one": `, "/one").withType('{', T[int]()), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt/StringifiedNumbers"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt/StringifiedNumbers"), opts: []Options{StringifyNumbers(true)}, inBuf: `{"zero": "0", "one": "1", "two": "2"}`, - inVal: new(structInlineMapStringInt), - want: addr(structInlineMapStringInt{ + inVal: new(structEmbedMapStringInt), + want: addr(structEmbedMapStringInt{ X: map[string]int{"zero": 0, "one": 1, "two": 2}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/MapStringInt/UnmarshalFunc"), + name: jsontest.Name("Structs/EmbeddedFallback/MapStringInt/UnmarshalFunc"), opts: []Options{ WithUnmarshalers(UnmarshalFunc(func(b []byte, v *int) error { i, err := strconv.ParseInt(string(bytes.Trim(b, `"`)), 10, 64) @@ -7067,43 +7067,43 @@ return nil })), }, inBuf: `{"zero": "0", "one": "1", "two": "2"}`, - inVal: new(structInlineMapStringInt), - want: addr(structInlineMapStringInt{ + inVal: new(structEmbedMapStringInt), + want: addr(structEmbedMapStringInt{ X: map[string]int{"zero": 0, "one": 1, "two": 2}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringInt"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringInt"), inBuf: `{"zero": 0, "one": 1, "two": 2}`, - inVal: new(structInlineMapNamedStringInt), - want: addr(structInlineMapNamedStringInt{ + inVal: new(structEmbedMapNamedStringInt), + want: addr(structEmbedMapNamedStringInt{ X: map[namedString]int{"zero": 0, "one": 1, "two": 2}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringInt/Null"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringInt/Null"), inBuf: `{"zero": 0, "one": null, "two": 2}`, - inVal: new(structInlineMapNamedStringInt), - want: addr(structInlineMapNamedStringInt{ + inVal: new(structEmbedMapNamedStringInt), + want: addr(structEmbedMapNamedStringInt{ X: map[namedString]int{"zero": 0, "one": 0, "two": 2}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringInt/Invalid"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringInt/Invalid"), inBuf: `{"zero": 0, "one": {}, "two": 2}`, - inVal: new(structInlineMapNamedStringInt), - want: addr(structInlineMapNamedStringInt{ + inVal: new(structEmbedMapNamedStringInt), + want: addr(structEmbedMapNamedStringInt{ X: map[namedString]int{"zero": 0, "one": 0}, }), wantErr: EU(nil).withPos(`{"zero": 0, "one": `, "/one").withType('{', T[int]()), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringInt/StringifiedNumbers"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringInt/StringifiedNumbers"), opts: []Options{StringifyNumbers(true)}, inBuf: `{"zero": "0", "one": 1, "two": "2"}`, - inVal: new(structInlineMapNamedStringInt), - want: addr(structInlineMapNamedStringInt{ + inVal: new(structEmbedMapNamedStringInt), + want: addr(structEmbedMapNamedStringInt{ X: map[namedString]int{"zero": 0, "one": 0}, }), wantErr: EU(nil).withPos(`{"zero": "0", "one": `, "/one").withType('0', T[int]()), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringInt/UnmarshalFunc"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringInt/UnmarshalFunc"), opts: []Options{ WithUnmarshalers(UnmarshalFunc(func(b []byte, v *int) error { i, err := strconv.ParseInt(string(bytes.Trim(b, `"`)), 10, 64) @@ -7115,78 +7115,78 @@ return nil })), }, inBuf: `{"zero": "0", "one": "1", "two": "2"}`, - inVal: new(structInlineMapNamedStringInt), - want: addr(structInlineMapNamedStringInt{ + inVal: new(structEmbedMapNamedStringInt), + want: addr(structEmbedMapNamedStringInt{ X: map[namedString]int{"zero": 0, "one": 1, "two": 2}, }), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/Noop"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/Noop"), inBuf: `{"A":1,"B":2}`, - inVal: new(structInlineMapNamedStringAny), - want: addr(structInlineMapNamedStringAny{A: 1, X: nil, B: 2}), + inVal: new(structEmbedMapNamedStringAny), + want: addr(structEmbedMapNamedStringAny{A: 1, X: nil, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/MergeN1/Nil"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/MergeN1/Nil"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: new(structInlineMapNamedStringAny), - want: addr(structInlineMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": "buzz"}, B: 2}), + inVal: new(structEmbedMapNamedStringAny), + want: addr(structEmbedMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": "buzz"}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/MergeN1/Empty"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/MergeN1/Empty"), inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: addr(structInlineMapNamedStringAny{X: map[namedString]any{}}), - want: addr(structInlineMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": "buzz"}, B: 2}), + inVal: addr(structEmbedMapNamedStringAny{X: map[namedString]any{}}), + want: addr(structEmbedMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": "buzz"}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/MergeN1/ObjectN1"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/MergeN1/ObjectN1"), inBuf: `{"A":1,"fizz":{"charlie":"DELTA","echo":"foxtrot"},"B":2}`, - inVal: addr(structInlineMapNamedStringAny{X: map[namedString]any{"fizz": jsonObject{ + inVal: addr(structEmbedMapNamedStringAny{X: map[namedString]any{"fizz": jsonObject{ "alpha": "bravo", "charlie": "delta", }}}), - want: addr(structInlineMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": jsonObject{ + want: addr(structEmbedMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": jsonObject{ "alpha": "bravo", "charlie": "DELTA", "echo": "foxtrot", }}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/MergeN2/ObjectN1"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/MergeN2/ObjectN1"), inBuf: `{"A":1,"fizz":"buzz","B":2,"foo": [ 1 , 2 , 3 ]}`, - inVal: addr(structInlineMapNamedStringAny{X: map[namedString]any{"fizz": "wuzz"}}), - want: addr(structInlineMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": "buzz", "foo": jsonArray{1.0, 2.0, 3.0}}, B: 2}), + inVal: addr(structEmbedMapNamedStringAny{X: map[namedString]any{"fizz": "wuzz"}}), + want: addr(structEmbedMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": "buzz", "foo": jsonArray{1.0, 2.0, 3.0}}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/MergeInvalidValue"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/MergeInvalidValue"), inBuf: `{"A":1,"fizz":nil,"B":2}`, - inVal: new(structInlineMapNamedStringAny), - want: addr(structInlineMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": nil}}), + inVal: new(structEmbedMapNamedStringAny), + want: addr(structEmbedMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": nil}}), wantErr: newInvalidCharacterError("i", "in literal null (expecting 'u')", len64(`{"A":1,"fizz":n`), "/fizz"), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/MergeInvalidValue/Existing"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/MergeInvalidValue/Existing"), inBuf: `{"A":1,"fizz":nil,"B":2}`, - inVal: addr(structInlineMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": true}}), - want: addr(structInlineMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": true}}), + inVal: addr(structEmbedMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": true}}), + want: addr(structEmbedMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": true}}), wantErr: newInvalidCharacterError("i", "in literal null (expecting 'u')", len64(`{"A":1,"fizz":n`), "/fizz"), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/CaseSensitive"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/CaseSensitive"), inBuf: `{"A":1,"fizz":"buzz","B":2,"a":3}`, - inVal: new(structInlineMapNamedStringAny), - want: addr(structInlineMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": "buzz", "a": 3.0}, B: 2}), + inVal: new(structEmbedMapNamedStringAny), + want: addr(structEmbedMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": "buzz", "a": 3.0}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/RejectDuplicateNames"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/RejectDuplicateNames"), opts: []Options{jsontext.AllowDuplicateNames(false)}, inBuf: `{"A":1,"fizz":"buzz","B":2,"fizz":"buzz"}`, - inVal: new(structInlineMapNamedStringAny), - want: addr(structInlineMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": "buzz"}, B: 2}), + inVal: new(structEmbedMapNamedStringAny), + want: addr(structEmbedMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": "buzz"}, B: 2}), wantErr: newDuplicateNameError("", []byte(`"fizz"`), len64(`{"A":1,"fizz":"buzz","B":2,`)), }, { - name: jsontest.Name("Structs/InlinedFallback/MapNamedStringAny/AllowDuplicateNames"), + name: jsontest.Name("Structs/EmbeddedFallback/MapNamedStringAny/AllowDuplicateNames"), opts: []Options{jsontext.AllowDuplicateNames(true)}, inBuf: `{"A":1,"fizz":{"one":1,"two":-2},"B":2,"fizz":{"two":2,"three":3}}`, - inVal: new(structInlineMapNamedStringAny), - want: addr(structInlineMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": map[string]any{"one": 1.0, "two": 2.0, "three": 3.0}}, B: 2}), + inVal: new(structEmbedMapNamedStringAny), + want: addr(structEmbedMapNamedStringAny{A: 1, X: map[namedString]any{"fizz": map[string]any{"one": 1.0, "two": 2.0, "three": 3.0}}, B: 2}), }, { - name: jsontest.Name("Structs/InlinedFallback/RejectUnknownMembers"), + name: jsontest.Name("Structs/EmbeddedFallback/RejectUnknownMembers"), opts: []Options{RejectUnknownMembers(true)}, inBuf: `{"A":1,"fizz":"buzz","B":2}`, - inVal: new(structInlineTextValue), - want: addr(structInlineTextValue{ + inVal: new(structEmbedTextValue), + want: addr(structEmbedTextValue{ A: 1, X: jsontext.Value(`{"fizz":"buzz"}`), B: 2, @@ -7257,51 +7257,51 @@ want: addr(structScalars{}), }, { name: jsontest.Name("Structs/DuplicateName/NoCase/ExactDifferent"), inBuf: `{"AAA":"AAA","AaA":"AaA","AAa":"AAa","Aaa":"Aaa"}`, - inVal: addr(structNoCaseInlineTextValue{}), - want: addr(structNoCaseInlineTextValue{AAA: "AAA", AaA: "AaA", AAa: "AAa", Aaa: "Aaa"}), + inVal: addr(structNoCaseEmbedTextValue{}), + want: addr(structNoCaseEmbedTextValue{AAA: "AAA", AaA: "AaA", AAa: "AAa", Aaa: "Aaa"}), }, { name: jsontest.Name("Structs/DuplicateName/NoCase/ExactConflict"), inBuf: `{"AAA":"AAA","AAA":"AAA"}`, - inVal: addr(structNoCaseInlineTextValue{}), - want: addr(structNoCaseInlineTextValue{AAA: "AAA"}), + inVal: addr(structNoCaseEmbedTextValue{}), + want: addr(structNoCaseEmbedTextValue{AAA: "AAA"}), wantErr: newDuplicateNameError("", []byte(`"AAA"`), len64(`{"AAA":"AAA",`)), }, { name: jsontest.Name("Structs/DuplicateName/NoCase/OverwriteExact"), inBuf: `{"AAA":"after"}`, - inVal: addr(structNoCaseInlineTextValue{AAA: "before"}), - want: addr(structNoCaseInlineTextValue{AAA: "after"}), + inVal: addr(structNoCaseEmbedTextValue{AAA: "before"}), + want: addr(structNoCaseEmbedTextValue{AAA: "after"}), }, { name: jsontest.Name("Structs/DuplicateName/NoCase/NoCaseConflict"), inBuf: `{"aaa":"aaa","aaA":"aaA"}`, - inVal: addr(structNoCaseInlineTextValue{}), - want: addr(structNoCaseInlineTextValue{AaA: "aaa"}), + inVal: addr(structNoCaseEmbedTextValue{}), + want: addr(structNoCaseEmbedTextValue{AaA: "aaa"}), wantErr: newDuplicateNameError("", []byte(`"aaA"`), len64(`{"aaa":"aaa",`)), }, { name: jsontest.Name("Structs/DuplicateName/NoCase/OverwriteNoCase"), inBuf: `{"aaa":"aaa","aaA":"aaA"}`, - inVal: addr(structNoCaseInlineTextValue{}), - want: addr(structNoCaseInlineTextValue{AaA: "aaa"}), + inVal: addr(structNoCaseEmbedTextValue{}), + want: addr(structNoCaseEmbedTextValue{AaA: "aaa"}), wantErr: newDuplicateNameError("", []byte(`"aaA"`), len64(`{"aaa":"aaa",`)), }, { - name: jsontest.Name("Structs/DuplicateName/Inline/Unknown"), + name: jsontest.Name("Structs/DuplicateName/Embed/Unknown"), inBuf: `{"unknown":""}`, - inVal: addr(structNoCaseInlineTextValue{}), - want: addr(structNoCaseInlineTextValue{X: jsontext.Value(`{"unknown":""}`)}), + inVal: addr(structNoCaseEmbedTextValue{}), + want: addr(structNoCaseEmbedTextValue{X: jsontext.Value(`{"unknown":""}`)}), }, { - name: jsontest.Name("Structs/DuplicateName/Inline/UnknownMerge"), + name: jsontest.Name("Structs/DuplicateName/Embed/UnknownMerge"), inBuf: `{"unknown":""}`, - inVal: addr(structNoCaseInlineTextValue{X: jsontext.Value(`{"unknown":""}`)}), - want: addr(structNoCaseInlineTextValue{X: jsontext.Value(`{"unknown":"","unknown":""}`)}), + inVal: addr(structNoCaseEmbedTextValue{X: jsontext.Value(`{"unknown":""}`)}), + want: addr(structNoCaseEmbedTextValue{X: jsontext.Value(`{"unknown":"","unknown":""}`)}), }, { - name: jsontest.Name("Structs/DuplicateName/Inline/NoCaseOkay"), + name: jsontest.Name("Structs/DuplicateName/Embed/NoCaseOkay"), inBuf: `{"b":"","B":""}`, - inVal: addr(structNoCaseInlineTextValue{}), - want: addr(structNoCaseInlineTextValue{X: jsontext.Value(`{"b":"","B":""}`)}), + inVal: addr(structNoCaseEmbedTextValue{}), + want: addr(structNoCaseEmbedTextValue{X: jsontext.Value(`{"b":"","B":""}`)}), }, { - name: jsontest.Name("Structs/DuplicateName/Inline/ExactConflict"), + name: jsontest.Name("Structs/DuplicateName/Embed/ExactConflict"), inBuf: `{"b":"","b":""}`, - inVal: addr(structNoCaseInlineTextValue{}), - want: addr(structNoCaseInlineTextValue{X: jsontext.Value(`{"b":""}`)}), + inVal: addr(structNoCaseEmbedTextValue{}), + want: addr(structNoCaseEmbedTextValue{X: jsontext.Value(`{"b":""}`)}), wantErr: newDuplicateNameError("", []byte(`"b"`), len64(`{"b":"",`)), }, { name: jsontest.Name("Structs/Invalid/ErrUnexpectedEOF"), @@ -8443,10 +8443,10 @@ inBuf: `{"Bool":"called1","String":"called2","Bytes":"called3","Int":"called4","Uint":456,"Float":789}`, inVal: addr(structScalars{}), want: addr(structScalars{Bool: true, String: "called2", Bytes: []byte("called3"), Int: 123, Uint: 456, Float: 789}), }, { - name: jsontest.Name("Functions/Struct/Inlined"), + name: jsontest.Name("Functions/Struct/Embedded"), opts: []Options{ WithUnmarshalers(JoinUnmarshalers( - UnmarshalFunc(func([]byte, *structInlinedL1) error { + UnmarshalFunc(func([]byte, *structEmbeddedL1) error { panic("should not be called") }), UnmarshalFromFunc(func(dec *jsontext.Decoder, v *StructEmbed2) error { @@ -8455,10 +8455,10 @@ }), )), }, inBuf: `{"E":"E3","F":"F3","G":"G3","A":"A1","B":"B1","D":"D2"}`, - inVal: new(structInlined), - want: addr(structInlined{ - X: structInlinedL1{ - X: &structInlinedL2{A: "A1", B: "B1" /* C: "C1" */}, + inVal: new(structEmbedded), + want: addr(structEmbedded{ + X: structEmbeddedL1{ + X: &structEmbeddedL2{A: "A1", B: "B1" /* C: "C1" */}, StructEmbed1: StructEmbed1{ /* C: "C2" */ D: "D2" /* E: "E2" */}, }, StructEmbed2: &StructEmbed2{E: "E3", F: "F3", G: "G3"}, diff --git a/src/encoding/json/v2/doc.go b/src/encoding/json/v2/doc.go index 9441960d419404fec84930396d1d68f77ecd0345..b6c8448d46c520ccd040a8cab1d414ddee9b0a73 100644 --- a/src/encoding/json/v2/doc.go +++ b/src/encoding/json/v2/doc.go @@ -101,19 +101,19 @@ // the first declared field in breadth-first order takes precedence. // The 'strict' value specifies that matching is case-sensitive. // This takes precedence over the [MatchCaseInsensitiveNames] option. // -// - inline: The "inline" option specifies that +// - embed: The "embed" option specifies that // the JSON representable content of this field type is to be promoted // as if they were specified in the parent struct. // It is the JSON equivalent of Go struct embedding. -// A Go embedded field is implicitly inlined unless an explicit JSON name -// is specified. The inlined field must be a Go struct +// A Go embedded field is implicitly JSON embedded unless +// an explicit JSON name is specified. The embedded field must be a Go struct // (that does not implement any JSON methods), [jsontext.Value], // map[~string]T, or an unnamed pointer to such types. When marshaling, -// inlined fields from a pointer type are omitted if it is nil. -// Inlined fields of type [jsontext.Value] and map[~string]T are called -// “inlined fallbacks” as they can represent all possible +// embedded fields from a pointer type are omitted if it is nil. +// Embedded fields of type [jsontext.Value] and map[~string]T are called +// “embedded fallbacks” as they can represent all possible // JSON object members not directly handled by the parent struct. -// Only one inlined fallback field may be specified in a struct, +// Only one embedded fallback field may be specified in a struct, // while many non-fallback fields may be specified. This option // must not be specified with any other option (including the JSON name). // @@ -129,8 +129,8 @@ // // Every Go struct corresponds to a list of JSON representable fields // which is constructed by performing a breadth-first search over // all struct fields (excluding unexported or ignored fields), -// where the search recursively descends into inlined structs. -// The set of non-inlined fields in a struct must have unique JSON names. +// where the search recursively descends into embedded structs. +// The set of non-embedded fields in a struct must have unique JSON names. // If multiple fields all have the same JSON name, then the one // at shallowest depth takes precedence and the other fields at deeper depths // are excluded from the list of JSON representable fields. diff --git a/src/encoding/json/v2/example_test.go b/src/encoding/json/v2/example_test.go index c27d15e55fcaae352e3689f007663c7fff094284..2596a10abfde1d3826e356ad82bc54404f06e367 100644 --- a/src/encoding/json/v2/example_test.go +++ b/src/encoding/json/v2/example_test.go @@ -269,38 +269,38 @@ // "Time": "0001-01-01T00:00:00Z" // } } -// JSON objects can be inlined within a parent object similar to +// JSON objects can be embedded within a parent object similar to // how Go structs can be embedded within a parent struct. -// The inlining rules are similar to those of Go embedding, +// The JSON embedding rules are similar to those of Go embedding, // but operates upon the JSON namespace. -func Example_inlinedFields() { +func Example_embeddedFields() { // Base is embedded within Container. type Base struct { // ID is promoted into the JSON object for Container. ID string // Type is ignored due to presence of Container.Type. Type string - // Time cancels out with Container.Inlined.Time. + // Time cancels out with Container.Embed.Time. Time time.Time } // Other is embedded within Container. type Other struct{ Cost float64 } // Container embeds Base and Other. type Container struct { - // Base is an embedded struct and is implicitly JSON inlined. + // Base is an embedded struct and is implicitly JSON embedded. Base // Type takes precedence over Base.Type. Type int - // Inlined is a named Go field, but is explicitly JSON inlined. - Inlined struct { + // Embed is a named Go field, but is explicitly JSON embedded. + Embed struct { // User is promoted into the JSON object for Container. User string // Time cancels out with Base.Time. Time string - } `json:",inline"` + } `json:",embed"` // ID does not conflict with Base.ID since the JSON name is different. ID string `json:"uuid"` - // Other is not JSON inlined since it has an explicit JSON name. + // Other is not JSON embedded since it has an explicit JSON name. Other `json:"other"` } diff --git a/src/encoding/json/v2/fields.go b/src/encoding/json/v2/fields.go index 68fe4c452a9b64a4f5886cea74b7ab0bb43b0590..f67a9ecb08f2374750efa784b007fbb3d97f8deb 100644 --- a/src/encoding/json/v2/fields.go +++ b/src/encoding/json/v2/fields.go @@ -29,10 +29,10 @@ var isZeroerType = reflect.TypeFor[isZeroer]() type structFields struct { - flattened []structField // listed in depth-first ordering - byActualName map[string]*structField - byFoldedName map[string][]*structField - inlinedFallback *structField + flattened []structField // listed in depth-first ordering + byActualName map[string]*structField + byFoldedName map[string][]*structField + embeddedFallback *structField errUnsupportedFormat *SemanticError } @@ -53,8 +53,8 @@ } for i := range sf.flattened { reindex(&sf.flattened[i]) } - if sf.inlinedFallback != nil { - reindex(sf.inlinedFallback) + if sf.embeddedFallback != nil { + reindex(sf.embeddedFallback) } } @@ -87,20 +87,20 @@ var queueIndex int type queueEntry struct { typ reflect.Type index []int - visitChildren bool // whether to recursively visit inlined field in this struct + visitChildren bool // whether to recursively visit embedded field in this struct } queue := []queueEntry{{root, nil, true}} seen := map[reflect.Type]bool{root: true} // Perform a breadth-first search over all reachable fields. // This ensures that len(f.index) will be monotonically increasing. - var allFields, inlinedFallbacks []structField + var allFields, embeddedFallbacks []structField for queueIndex < len(queue) { qe := queue[queueIndex] queueIndex++ t := qe.typ - inlinedFallbackIndex := -1 // index of last inlined fallback field in current struct + embeddedFallbackIndex := -1 // index of last embedded fallback field in current struct namesIndex := make(map[string]int) // index of each field with a given JSON object name in current struct var hasAnyJSONTag bool // whether any Go struct field has a `json` tag var hasAnyJSONField bool // whether any JSON serializable fields exist in current struct @@ -128,32 +128,32 @@ if sf.Anonymous && !f.hasName { if indirectType(f.typ).Kind() != reflect.Struct { serr = orErrorf(serr, t, "embedded Go struct field %s of non-struct type must be explicitly given a JSON name", sf.Name) } else { - f.inline = true // implied by use of Go embedding without an explicit name + f.embed = true // implied by use of Go embedding without an explicit name } } - var handleInline, handleField func() - handleInline = func() { - // Handle an inlined field that serializes to/from + var handleEmbed, handleField func() + handleEmbed = func() { + // Handle an embedded field that serializes to/from // zero or more JSON object members. - if f.fieldOptions != (fieldOptions{name: f.name, quotedName: f.quotedName, inline: true}) { - serr = orErrorf(serr, t, "Go struct field %s cannot have any options other than `inline` specified", sf.Name) + if f.fieldOptions != (fieldOptions{name: f.name, quotedName: f.quotedName, embed: true}) { + serr = orErrorf(serr, t, "Go struct field %s cannot have any options other than `embed` specified", sf.Name) if f.hasName { handleField() - return // invalid inlined field; treat as regular field + return // invalid embedded field; treat as regular field } - f.fieldOptions = fieldOptions{name: f.name, quotedName: f.quotedName, inline: f.inline} + f.fieldOptions = fieldOptions{name: f.name, quotedName: f.quotedName, embed: f.embed} } // Reject any types with custom serialization otherwise - // it becomes impossible to know what sub-fields to inline. + // it becomes impossible to know what sub-fields to embed. tf := indirectType(f.typ) if implementsAny(tf, allMethodTypes...) && tf != jsontextValueType { - serr = orErrorf(serr, t, "inlined Go struct field %s of type %s must not implement marshal or unmarshal methods", sf.Name, tf) + serr = orErrorf(serr, t, "embedded Go struct field %s of type %s must not implement marshal or unmarshal methods", sf.Name, tf) } - // Handle an inlined field that serializes to/from + // Handle an embedded field that serializes to/from // a finite number of JSON object members backed by a Go struct. if tf.Kind() == reflect.Struct { if qe.visitChildren { @@ -162,37 +162,37 @@ } seen[tf] = true return } else if !sf.IsExported() { - serr = orErrorf(serr, t, "inlined Go struct field %s is not exported", sf.Name) - return // invalid inlined field; treat as ignored + serr = orErrorf(serr, t, "embedded Go struct field %s is not exported", sf.Name) + return // invalid embedded field; treat as ignored } - // Handle an inlined field that serializes to/from any number of + // Handle an embedded field that serializes to/from any number of // JSON object members back by a Go map or jsontext.Value. switch { case tf == jsontextValueType: - f.fncs = nil // specially handled in arshal_inlined.go + f.fncs = nil // specially handled in arshal_embedded.go case tf.Kind() == reflect.Map && tf.Key().Kind() == reflect.String: if implementsAny(tf.Key(), allMethodTypes...) { - serr = orErrorf(serr, t, "inlined map field %s of type %s must have a string key that does not implement marshal or unmarshal methods", sf.Name, tf) + serr = orErrorf(serr, t, "embedded map field %s of type %s must have a string key that does not implement marshal or unmarshal methods", sf.Name, tf) handleField() - return // invalid inlined field; treat as regular field + return // invalid embedded field; treat as regular field } f.fncs = lookupArshaler(tf.Elem()) default: - serr = orErrorf(serr, t, "inlined Go struct field %s of type %s must be a Go struct, Go map of string key, or jsontext.Value", sf.Name, tf) + serr = orErrorf(serr, t, "embedded Go struct field %s of type %s must be a Go struct, Go map of string key, or jsontext.Value", sf.Name, tf) handleField() - return // invalid inlined field; treat as regular field + return // invalid embedded field; treat as regular field } - // Reject multiple inlined fallback fields within the same struct. - if inlinedFallbackIndex >= 0 { - serr = orErrorf(serr, t, "inlined Go struct fields %s and %s cannot both be a Go map or jsontext.Value", t.Field(inlinedFallbackIndex).Name, sf.Name) - // Still append f to inlinedFallbacks as there is still a - // check for a dominant inlined fallback before returning. + // Reject multiple embedded fallback fields within the same struct. + if embeddedFallbackIndex >= 0 { + serr = orErrorf(serr, t, "embedded Go struct fields %s and %s cannot both be a Go map or jsontext.Value", t.Field(embeddedFallbackIndex).Name, sf.Name) + // Still append f to embeddedFallbacks as there is still a + // check for a dominant embedded fallback before returning. } - inlinedFallbackIndex = i + embeddedFallbackIndex = i - inlinedFallbacks = append(inlinedFallbacks, f) + embeddedFallbacks = append(embeddedFallbacks, f) } handleField = func() { // Handle normal Go struct field that serializes to/from @@ -260,8 +260,8 @@ fs.errUnsupportedFormat = &SemanticError{GoType: t, Err: fmt.Errorf("Go struct field %s has unsupported `format` tag option", sf.Name)} } } - if f.inline { - handleInline() + if f.embed { + handleEmbed() } else { handleField() } @@ -347,8 +347,8 @@ }) fs.byFoldedName[foldedName] = fields } } - if n := len(inlinedFallbacks); n == 1 || (n > 1 && len(inlinedFallbacks[0].index) != len(inlinedFallbacks[1].index)) { - fs.inlinedFallback = &inlinedFallbacks[0] // dominant inlined fallback field + if n := len(embeddedFallbacks); n == 1 || (n > 1 && len(embeddedFallbacks[0].index) != len(embeddedFallbacks[1].index)) { + fs.embeddedFallback = &embeddedFallbacks[0] // dominant embedded fallback field } fs.reindex() return fs, serr @@ -397,7 +397,7 @@ quotedName string // quoted name per RFC 8785, section 3.2.2.2. hasName bool nameNeedEscape bool casing int8 // either 0, caseIgnore, or caseStrict - inline bool + embed bool omitzero bool omitempty bool string bool @@ -518,8 +518,8 @@ out.casing |= caseStrict default: err = cmp.Or(err, fmt.Errorf("Go struct field %s has unknown `case:%s` tag value", sf.Name, rawOpt)) } - case "inline": - out.inline = true + case "embed": + out.embed = true case "omitzero": out.omitzero = true case "omitempty": @@ -548,7 +548,7 @@ // Reject keys that resemble one of the supported options. // This catches invalid mutants such as "omitEmpty" or "omit_empty". normOpt := strings.ReplaceAll(strings.ToLower(opt), "_", "") switch normOpt { - case "case", "inline", "omitzero", "omitempty", "string", "format": + case "case", "embed", "omitzero", "omitempty", "string", "format": err = cmp.Or(err, fmt.Errorf("Go struct field %s has invalid appearance of `%s` tag option; specify `%s` instead", sf.Name, opt, normOpt)) } diff --git a/src/encoding/json/v2/fields_test.go b/src/encoding/json/v2/fields_test.go index 0141bed1887f3b00a60f87035ba903bdcec97fad..21ec4e0cb2e5b31815e73946402d517809986a69 100644 --- a/src/encoding/json/v2/fields_test.go +++ b/src/encoding/json/v2/fields_test.go @@ -24,7 +24,7 @@ Foo string } type Recursive struct { A string - *Recursive `json:",inline"` + *Recursive `json:",embed"` B string } type MapStringAny map[string]any @@ -57,17 +57,17 @@ L1B struct { L2A string L2B struct { L3A string - } `json:",inline"` + } `json:",embed"` L2C string - } `json:",inline"` + } `json:",embed"` L1C string L1D struct { L2D string L2E struct { L3B string - } `json:",inline"` + } `json:",embed"` L2F string - } `json:",inline"` + } `json:",embed"` L1E string }{}, want: structFields{ @@ -91,15 +91,15 @@ X struct { A string // loses in precedence to A B string // cancels out with X2.X.B D string // loses in precedence to D - } `json:",inline"` - } `json:",inline"` + } `json:",embed"` + } `json:",embed"` X2 struct { X struct { B string // cancels out with X1.X.B C string D string // loses in precedence to D - } `json:",inline"` - } `json:",inline"` + } `json:",embed"` + } `json:",embed"` A string // takes precedence over X1.X.A D string // takes precedence over X1.X.D and X2.X.D }{}, @@ -115,13 +115,13 @@ name: jsontest.Name("NameResolution/ExplicitNameUniquePrecedence"), in: struct { X1 struct { A string // loses in precedence to X2.A - } `json:",inline"` + } `json:",embed"` X2 struct { A string `json:"A"` - } `json:",inline"` + } `json:",embed"` X3 struct { A string // loses in precedence to X2.A - } `json:",inline"` + } `json:",embed"` }{}, want: structFields{ flattened: []structField{ @@ -133,13 +133,13 @@ name: jsontest.Name("NameResolution/ExplicitNameCancelsOut"), in: struct { X1 struct { A string // loses in precedence to X2.A or X3.A - } `json:",inline"` + } `json:",embed"` X2 struct { A string `json:"A"` // cancels out with X3.A - } `json:",inline"` + } `json:",embed"` X3 struct { A string `json:"A"` // cancels out with X2.A - } `json:",inline"` + } `json:",embed"` }{}, want: structFields{flattened: []structField{}}, }, { @@ -155,7 +155,7 @@ }, }, { name: jsontest.Name("Embed/Explicit"), in: struct { - Embed `json:",inline"` + Embed `json:",embed"` }{}, want: structFields{ flattened: []structField{ @@ -166,7 +166,7 @@ }, { name: jsontest.Name("Recursive"), in: struct { A string - Recursive `json:",inline"` + Recursive `json:",embed"` C string }{}, want: structFields{ @@ -177,32 +177,32 @@ {id: 1, index: []int{2}, typ: stringType, fieldOptions: fieldOptions{name: "C", quotedName: `"C"`}}, }, }, }, { - name: jsontest.Name("InlinedFallback/Cancelation"), + name: jsontest.Name("EmbeddedFallback/Cancelation"), in: struct { X1 struct { - X jsontext.Value `json:",inline"` - } `json:",inline"` + X jsontext.Value `json:",embed"` + } `json:",embed"` X2 struct { - X map[string]any `json:",inline"` - } `json:",inline"` + X map[string]any `json:",embed"` + } `json:",embed"` }{}, want: structFields{}, }, { - name: jsontest.Name("InlinedFallback/Precedence"), + name: jsontest.Name("EmbeddedFallback/Precedence"), in: struct { X1 struct { - X jsontext.Value `json:",inline"` - } `json:",inline"` + X jsontext.Value `json:",embed"` + } `json:",embed"` X2 struct { - X map[string]any `json:",inline"` - } `json:",inline"` - X map[string]jsontext.Value `json:",inline"` + X map[string]any `json:",embed"` + } `json:",embed"` + X map[string]jsontext.Value `json:",embed"` }{}, want: structFields{ - inlinedFallback: &structField{id: 0, index: []int{2}, typ: T[map[string]jsontext.Value](), fieldOptions: fieldOptions{name: "X", quotedName: `"X"`, inline: true}}, + embeddedFallback: &structField{id: 0, index: []int{2}, typ: T[map[string]jsontext.Value](), fieldOptions: fieldOptions{name: "X", quotedName: `"X"`, embed: true}}, }, }, { - name: jsontest.Name("InlinedFallback/InvalidImplicit"), + name: jsontest.Name("EmbeddedFallback/InvalidImplicit"), in: struct { MapStringAny }{}, @@ -221,30 +221,30 @@ }{}, want: structFields{flattened: []structField{}}, wantErr: errors.New(`Go struct fields A and B conflict over JSON object name "same"`), }, { - name: jsontest.Name("InlineWithOptions"), + name: jsontest.Name("EmbedWithOptions"), in: struct { - A struct{} `json:",inline,omitempty"` + A struct{} `json:",embed,omitempty"` }{}, - wantErr: errors.New("Go struct field A cannot have any options other than `inline` specified"), + wantErr: errors.New("Go struct field A cannot have any options other than `embed` specified"), }, { name: jsontest.Name("UnknownWithOptions"), in: struct { - A map[string]any `json:",inline,omitempty"` + A map[string]any `json:",embed,omitempty"` }{}, - want: structFields{inlinedFallback: &structField{ + want: structFields{embeddedFallback: &structField{ index: []int{0}, typ: reflect.TypeFor[map[string]any](), fieldOptions: fieldOptions{ name: "A", quotedName: `"A"`, - inline: true, + embed: true, }, }}, - wantErr: errors.New("Go struct field A cannot have any options other than `inline` specified"), + wantErr: errors.New("Go struct field A cannot have any options other than `embed` specified"), }, { - name: jsontest.Name("InlineTextMarshaler"), + name: jsontest.Name("EmbedTextMarshaler"), in: struct { - A struct{ encoding.TextMarshaler } `json:",inline"` + A struct{ encoding.TextMarshaler } `json:",embed"` }{}, want: structFields{flattened: []structField{{ index: []int{0, 0}, @@ -254,11 +254,11 @@ name: "TextMarshaler", quotedName: `"TextMarshaler"`, }, }}}, - wantErr: errors.New(`inlined Go struct field A of type struct { encoding.TextMarshaler } must not implement marshal or unmarshal methods`), + wantErr: errors.New(`embedded Go struct field A of type struct { encoding.TextMarshaler } must not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("InlineTextAppender"), + name: jsontest.Name("EmbedTextAppender"), in: struct { - A struct{ encoding.TextAppender } `json:",inline"` + A struct{ encoding.TextAppender } `json:",embed"` }{}, want: structFields{flattened: []structField{{ index: []int{0, 0}, @@ -268,11 +268,11 @@ name: "TextAppender", quotedName: `"TextAppender"`, }, }}}, - wantErr: errors.New(`inlined Go struct field A of type struct { encoding.TextAppender } must not implement marshal or unmarshal methods`), + wantErr: errors.New(`embedded Go struct field A of type struct { encoding.TextAppender } must not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("InlineJSONMarshaler"), + name: jsontest.Name("EmbedJSONMarshaler"), in: struct { - A struct{ Marshaler } `json:",inline"` + A struct{ Marshaler } `json:",embed"` }{}, want: structFields{flattened: []structField{{ index: []int{0, 0}, @@ -282,11 +282,11 @@ name: "Marshaler", quotedName: `"Marshaler"`, }, }}}, - wantErr: errors.New(`inlined Go struct field A of type struct { json.Marshaler } must not implement marshal or unmarshal methods`), + wantErr: errors.New(`embedded Go struct field A of type struct { json.Marshaler } must not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("InlineJSONMarshalerTo"), + name: jsontest.Name("EmbedJSONMarshalerTo"), in: struct { - A struct{ MarshalerTo } `json:",inline"` + A struct{ MarshalerTo } `json:",embed"` }{}, want: structFields{flattened: []structField{{ index: []int{0, 0}, @@ -296,11 +296,11 @@ name: "MarshalerTo", quotedName: `"MarshalerTo"`, }, }}}, - wantErr: errors.New(`inlined Go struct field A of type struct { json.MarshalerTo } must not implement marshal or unmarshal methods`), + wantErr: errors.New(`embedded Go struct field A of type struct { json.MarshalerTo } must not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("InlineTextUnmarshaler"), + name: jsontest.Name("EmbedTextUnmarshaler"), in: struct { - A *struct{ encoding.TextUnmarshaler } `json:",inline"` + A *struct{ encoding.TextUnmarshaler } `json:",embed"` }{}, want: structFields{flattened: []structField{{ index: []int{0, 0}, @@ -310,11 +310,11 @@ name: "TextUnmarshaler", quotedName: `"TextUnmarshaler"`, }, }}}, - wantErr: errors.New(`inlined Go struct field A of type struct { encoding.TextUnmarshaler } must not implement marshal or unmarshal methods`), + wantErr: errors.New(`embedded Go struct field A of type struct { encoding.TextUnmarshaler } must not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("InlineJSONUnmarshaler"), + name: jsontest.Name("EmbedJSONUnmarshaler"), in: struct { - A *struct{ Unmarshaler } `json:",inline"` + A *struct{ Unmarshaler } `json:",embed"` }{}, want: structFields{flattened: []structField{{ index: []int{0, 0}, @@ -324,11 +324,11 @@ name: "Unmarshaler", quotedName: `"Unmarshaler"`, }, }}}, - wantErr: errors.New(`inlined Go struct field A of type struct { json.Unmarshaler } must not implement marshal or unmarshal methods`), + wantErr: errors.New(`embedded Go struct field A of type struct { json.Unmarshaler } must not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("InlineJSONUnmarshalerFrom"), + name: jsontest.Name("EmbedJSONUnmarshalerFrom"), in: struct { - A struct{ UnmarshalerFrom } `json:",inline"` + A struct{ UnmarshalerFrom } `json:",embed"` }{}, want: structFields{flattened: []structField{{ index: []int{0, 0}, @@ -338,65 +338,65 @@ name: "UnmarshalerFrom", quotedName: `"UnmarshalerFrom"`, }, }}}, - wantErr: errors.New(`inlined Go struct field A of type struct { json.UnmarshalerFrom } must not implement marshal or unmarshal methods`), + wantErr: errors.New(`embedded Go struct field A of type struct { json.UnmarshalerFrom } must not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("InlineUnsupported/MapIntKey"), + name: jsontest.Name("EmbedUnsupported/MapIntKey"), in: struct { - A map[int]any `json:",inline"` + A map[int]any `json:",embed"` }{}, want: structFields{flattened: []structField{{ index: []int{0}, typ: reflect.TypeFor[map[int]any](), - fieldOptions: fieldOptions{name: "A", quotedName: `"A"`, inline: true}, + fieldOptions: fieldOptions{name: "A", quotedName: `"A"`, embed: true}, }}}, - wantErr: errors.New(`inlined Go struct field A of type map[int]interface {} must be a Go struct, Go map of string key, or jsontext.Value`), + wantErr: errors.New(`embedded Go struct field A of type map[int]interface {} must be a Go struct, Go map of string key, or jsontext.Value`), }, { - name: jsontest.Name("InlineUnsupported/MapTextMarshalerStringKey"), + name: jsontest.Name("EmbedUnsupported/MapTextMarshalerStringKey"), in: struct { - A map[nocaseString]any `json:",inline"` + A map[nocaseString]any `json:",embed"` }{}, want: structFields{flattened: []structField{{ index: []int{0}, typ: reflect.TypeFor[map[nocaseString]any](), - fieldOptions: fieldOptions{name: "A", quotedName: `"A"`, inline: true}, + fieldOptions: fieldOptions{name: "A", quotedName: `"A"`, embed: true}, }}}, - wantErr: errors.New(`inlined map field A of type map[json.nocaseString]interface {} must have a string key that does not implement marshal or unmarshal methods`), + wantErr: errors.New(`embedded map field A of type map[json.nocaseString]interface {} must have a string key that does not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("InlineUnsupported/MapMarshalerStringKey"), + name: jsontest.Name("EmbedUnsupported/MapMarshalerStringKey"), in: struct { - A map[stringMarshalEmpty]any `json:",inline"` + A map[stringMarshalEmpty]any `json:",embed"` }{}, want: structFields{flattened: []structField{{ index: []int{0}, typ: reflect.TypeFor[map[stringMarshalEmpty]any](), - fieldOptions: fieldOptions{name: "A", quotedName: `"A"`, inline: true}, + fieldOptions: fieldOptions{name: "A", quotedName: `"A"`, embed: true}, }}}, - wantErr: errors.New(`inlined map field A of type map[json.stringMarshalEmpty]interface {} must have a string key that does not implement marshal or unmarshal methods`), + wantErr: errors.New(`embedded map field A of type map[json.stringMarshalEmpty]interface {} must have a string key that does not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("InlineUnsupported/DoublePointer"), + name: jsontest.Name("EmbedUnsupported/DoublePointer"), in: struct { - A **struct{} `json:",inline"` + A **struct{} `json:",embed"` }{}, want: structFields{flattened: []structField{{ index: []int{0}, typ: reflect.TypeFor[**struct{}](), - fieldOptions: fieldOptions{name: "A", quotedName: `"A"`, inline: true}, + fieldOptions: fieldOptions{name: "A", quotedName: `"A"`, embed: true}, }}}, - wantErr: errors.New(`inlined Go struct field A of type *struct {} must be a Go struct, Go map of string key, or jsontext.Value`), + wantErr: errors.New(`embedded Go struct field A of type *struct {} must be a Go struct, Go map of string key, or jsontext.Value`), }, { - name: jsontest.Name("DuplicateInline"), + name: jsontest.Name("DuplicateEmbed"), in: struct { - A map[string]any `json:",inline"` - B jsontext.Value `json:",inline"` + A map[string]any `json:",embed"` + B jsontext.Value `json:",embed"` }{}, - wantErr: errors.New(`inlined Go struct fields A and B cannot both be a Go map or jsontext.Value`), + wantErr: errors.New(`embedded Go struct fields A and B cannot both be a Go map or jsontext.Value`), }, { - name: jsontest.Name("DuplicateEmbedInline"), + name: jsontest.Name("DuplicateEmbedEmbed"), in: struct { - A MapStringAny `json:",inline"` - B jsontext.Value `json:",inline"` + A MapStringAny `json:",embed"` + B jsontext.Value `json:",embed"` }{}, - wantErr: errors.New(`inlined Go struct fields A and B cannot both be a Go map or jsontext.Value`), + wantErr: errors.New(`embedded Go struct fields A and B cannot both be a Go map or jsontext.Value`), }} for _, tt := range tests { @@ -426,9 +426,9 @@ for i := range got.flattened { got.flattened[i].fncs = nil got.flattened[i].isEmpty = nil } - if got.inlinedFallback != nil { - got.inlinedFallback.fncs = nil - got.inlinedFallback.isEmpty = nil + if got.embeddedFallback != nil { + got.embeddedFallback.fncs = nil + got.embeddedFallback.isEmpty = nil } // Reproduce maps in want. @@ -473,9 +473,9 @@ wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`}, }, { name: jsontest.Name("GoNameWithOptions"), in: struct { - FieldName int `json:",inline"` + FieldName int `json:",embed"` }{}, - wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`, inline: true}, + wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`, embed: true}, }, { name: jsontest.Name("Empty"), in: struct { @@ -579,9 +579,9 @@ wantErr: errors.New("Go struct field V has malformed `json` tag: invalid trailing ',' character"), }, { name: jsontest.Name("SuperfluousCommas"), in: struct { - V int `json:",,,,\"\",,inline,,,,,"` + V int `json:",,,,\"\",,embed,,,,,"` }{}, - wantOpts: fieldOptions{name: "V", quotedName: `"V"`, inline: true}, + wantOpts: fieldOptions{name: "V", quotedName: `"V"`, embed: true}, wantErr: errors.New("Go struct field V has malformed `json` tag: invalid character ',' at start of option (expecting Unicode letter)"), }, { name: jsontest.Name("CaseAloneOption"), @@ -624,11 +624,11 @@ }{}, wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`, casing: caseIgnore | caseStrict}, wantErr: errors.New("Go struct field FieldName cannot have both `case:ignore` and `case:strict` tag options"), }, { - name: jsontest.Name("InlineOption"), + name: jsontest.Name("EmbedOption"), in: struct { - FieldName int `json:",inline"` + FieldName int `json:",embed"` }{}, - wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`, inline: true}, + wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`, embed: true}, }, { name: jsontest.Name("OmitZeroOption"), in: struct { @@ -683,13 +683,13 @@ wantErr: errors.New("Go struct field FieldName has `format` tag option that was not specified last"), }, { name: jsontest.Name("AllOptions"), in: struct { - FieldName int `json:",case:ignore,inline,omitzero,omitempty,string,format:format"` + FieldName int `json:",case:ignore,embed,omitzero,omitempty,string,format:format"` }{}, wantOpts: fieldOptions{ name: "FieldName", quotedName: `"FieldName"`, casing: caseIgnore, - inline: true, + embed: true, omitzero: true, omitempty: true, string: true, @@ -705,16 +705,16 @@ wantErr: errors.New("Go struct field FieldName has invalid appearance of `CASE` tag option; specify `case` instead"), }, { name: jsontest.Name("AllOptionsSpaceSensitive"), in: struct { - FieldName int `json:", case:ignore , inline , omitzero , omitempty , string , format:format "` + FieldName int `json:", case:ignore , embed , omitzero , omitempty , string , format:format "` }{}, wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`}, wantErr: errors.New("Go struct field FieldName has malformed `json` tag: invalid character ' ' at start of option (expecting Unicode letter)"), }, { name: jsontest.Name("UnknownTagOption"), in: struct { - FieldName int `json:",inline,whoknows,string"` + FieldName int `json:",embed,whoknows,string"` }{}, - wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`, inline: true, string: true}, + wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`, embed: true, string: true}, }, { name: jsontest.Name("MisnamedTag"), in: struct { diff --git a/src/encoding/json/v2/options.go b/src/encoding/json/v2/options.go index 0e47ca2a8b4feb5782d6419d15a29b55149dba87..f6d132a9ce9747442fa9333d096529aa10ac00ea 100644 --- a/src/encoding/json/v2/options.go +++ b/src/encoding/json/v2/options.go @@ -194,7 +194,7 @@ // regardless of the value of this option. // // This affects either marshaling or unmarshaling. // For marshaling, this option may alter the detection of duplicate names -// (assuming [jsontext.AllowDuplicateNames] is false) from inlined fields +// (assuming [jsontext.AllowDuplicateNames] is false) from embedded fields // if it matches one of the declared fields in the Go struct. func MatchCaseInsensitiveNames(v bool) Options { if v { diff --git a/src/encoding/json/v2_decode_test.go b/src/encoding/json/v2_decode_test.go index 4178642b358cd42029e679eddc927575cd6a522a..9fe05cbbef4327cef499fc793898e14cdfafdd12 100644 --- a/src/encoding/json/v2_decode_test.go +++ b/src/encoding/json/v2_decode_test.go @@ -1735,7 +1735,7 @@ Interface any PInterface *any - InvalidInline int `json:",inline"` // issue #79921: invalid `inline` tag option should be ignored + InvalidEmbed int `json:",embed"` // issue #79921: invalid `embed` tag option should be ignored unexported int } @@ -1772,16 +1772,16 @@ MapP: map[string]*Small{ "19": {Tag: "tag19"}, "20": nil, }, - EmptyMap: map[string]Small{}, - Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}}, - SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}}, - EmptySlice: []Small{}, - StringSlice: []string{"str24", "str25", "str26"}, - ByteSlice: []byte{27, 28, 29}, - Small: Small{Tag: "tag30"}, - PSmall: &Small{Tag: "tag31"}, - Interface: 5.2, - InvalidInline: 123, + EmptyMap: map[string]Small{}, + Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}}, + SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}}, + EmptySlice: []Small{}, + StringSlice: []string{"str24", "str25", "str26"}, + ByteSlice: []byte{27, 28, 29}, + Small: Small{Tag: "tag30"}, + PSmall: &Small{Tag: "tag31"}, + Interface: 5.2, + InvalidEmbed: 123, } var pallValue = All{ @@ -1897,7 +1897,7 @@ }, "PPSmall": null, "Interface": 5.2, "PInterface": null, - "InvalidInline": 123 + "InvalidEmbed": 123 }` var allValueCompact = stripWhitespace(allValueIndent) @@ -1987,7 +1987,7 @@ "Tag": "tag31" }, "Interface": null, "PInterface": 5.2, - "InvalidInline": 0 + "InvalidEmbed": 0 }` var pallValueCompact = stripWhitespace(pallValueIndent) diff --git a/src/go/constant/kind_string.go b/src/go/constant/kind_string.go index 700332511d8b7c2e4af13609a171f21974e72dbd..2e6720eae2110141fa65d6df7c2c8d2191fe9250 100644 --- a/src/go/constant/kind_string.go +++ b/src/go/constant/kind_string.go @@ -21,8 +21,9 @@ var _Kind_index = [...]uint8{0, 7, 11, 17, 20, 25, 32} func (i Kind) String() string { - if i < 0 || i >= Kind(len(_Kind_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_Kind_index)-1 { return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Kind_name[_Kind_index[i]:_Kind_index[i+1]] + return _Kind_name[_Kind_index[idx]:_Kind_index[idx+1]] } diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index 17f0893e1aaf14a9866e75d382b45ad43eadbf47..480554608903312a1e5b8d2e4a367c9aaa062eed 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -827,3 +827,73 @@ }() } wg.Wait() } + +func TestGenMeth(t *testing.T) { + testenv.MustHaveGoBuild(t) + + // This package only handles gc export data. + if runtime.Compiler != "gc" { + t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) + } + + tmpdir := t.TempDir() + testoutdir := filepath.Join(tmpdir, "testdata") + if err := os.Mkdir(testoutdir, 0700); err != nil { + t.Fatalf("making output dir: %v", err) + } + + compile(t, "testdata", "genmeth.go", testoutdir, nil) + + fset := token.NewFileSet() + + genmeth, err := Import(fset, make(map[string]*types.Package), "./testdata/genmeth", tmpdir, nil) + if err != nil { + t.Fatal(err) + } + + check := func(pkgname, src string, imports importMap) (*types.Package, error) { + f, err := parser.ParseFile(fset, "genmeth.go", src, parser.SkipObjectResolution) + if err != nil { + return nil, err + } + config := &types.Config{ + Importer: imports, + } + return config.Check(pkgname, fset, []*ast.File{f}, nil) + } + + const pSrc = `package p + +import "genmeth" + +func _() { + var ex func(int) genmeth.List[int] + var fl func(genmeth.List[int]) genmeth.List[int] + + var l genmeth.List[int] + l = l.Map(ex).FlatMap(fl) + + var bl genmeth.BiList[int, any] + bl = bl.MapKeys(ex).Flip().FlatMapValues(fl).Flip() + + var id func(int) int + + var op genmeth.Option[int] + var _ int = op.MapIfPresent(id).Get() + + var ol genmeth.OrderedList[int] + var _ int = ol.Min().Get() + + var b genmeth.Box[int] + b.Set(42) + var _ int = b.Get() +} +` + + importer := importMap{ + "genmeth": genmeth, + } + if _, err := check("p", pSrc, importer); err != nil { + t.Errorf("Check failed: %v", err) + } +} diff --git a/src/go/internal/gcimporter/genmeth_test.go b/src/go/internal/gcimporter/genmeth_test.go deleted file mode 100644 index 2bbb7bde26391ccccf156a4e5ac328a0bec9fc14..0000000000000000000000000000000000000000 --- a/src/go/internal/gcimporter/genmeth_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2026 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build goexperiment.genericmethods - -package gcimporter_test - -import ( - "internal/testenv" - "os" - "path/filepath" - "runtime" - "testing" - - "go/ast" - "go/parser" - "go/token" - "go/types" - - . "go/internal/gcimporter" -) - -func TestGenMeth(t *testing.T) { - testenv.MustHaveGoBuild(t) - - // This package only handles gc export data. - if runtime.Compiler != "gc" { - t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) - } - - tmpdir := t.TempDir() - testoutdir := filepath.Join(tmpdir, "testdata") - if err := os.Mkdir(testoutdir, 0700); err != nil { - t.Fatalf("making output dir: %v", err) - } - - compile(t, "testdata", "genmeth.go", testoutdir, nil) - - fset := token.NewFileSet() - - genmeth, err := Import(fset, make(map[string]*types.Package), "./testdata/genmeth", tmpdir, nil) - if err != nil { - t.Fatal(err) - } - - check := func(pkgname, src string, imports importMap) (*types.Package, error) { - f, err := parser.ParseFile(fset, "genmeth.go", src, parser.SkipObjectResolution) - if err != nil { - return nil, err - } - config := &types.Config{ - Importer: imports, - } - return config.Check(pkgname, fset, []*ast.File{f}, nil) - } - - const pSrc = `package p - -import "genmeth" - -func _() { - var ex func(int) genmeth.List[int] - var fl func(genmeth.List[int]) genmeth.List[int] - - var l genmeth.List[int] - l = l.Map(ex).FlatMap(fl) - - var bl genmeth.BiList[int, any] - bl = bl.MapKeys(ex).Flip().FlatMapValues(fl).Flip() - - var id func(int) int - - var op genmeth.Option[int] - var _ int = op.MapIfPresent(id).Get() - - var ol genmeth.OrderedList[int] - var _ int = ol.Min().Get() - - var b genmeth.Box[int] - b.Set(42) - var _ int = b.Get() -} -` - - importer := importMap{ - "genmeth": genmeth, - } - if _, err := check("p", pSrc, importer); err != nil { - t.Errorf("Check failed: %v", err) - } -} diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index 6630cd2be6eb42110d71cd070163a3ad66cdcacb..ed2c1b4568ede572fe5a5f5c056c20b2b85c0762 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -1030,12 +1030,18 @@ check.cycleError(cycle, firstInSrc(cycle)) return true } - check.push(t.obj) + obj := t.obj + check.push(obj) defer check.pop() // Careful, we're inspecting t.fromRHS, so we need to unpack first. t.unpack() varSize := check.hasVarSize(t.rhs()) + + // Special case for portable simd types that rewrite to unknown sizes. + if pkg := obj.Pkg(); pkg != nil && pkg.Path() == "simd" && obj.Name() == "_simd" { + varSize = true + } t.mu.Lock() defer t.mu.Unlock() diff --git a/src/go/types/cycles.go b/src/go/types/cycles.go index 957370cd9fcf6ff8ccb9fbc9d3ff46f7104e39bb..294eaf62deae4cbe0935b79b972146c82e7ec592 100644 --- a/src/go/types/cycles.go +++ b/src/go/types/cycles.go @@ -109,23 +109,34 @@ // isComplete returns whether a type is complete (i.e. up to having an underlying type). // Incomplete types will panic if [Type.Underlying] is called on them. func (check *Checker) isComplete(t Type) bool { - if n, ok := Unalias(t).(*Named); ok { - if i, found := check.objPathIdx[n.obj]; found { - cycle := check.objPath[i:] - check.cycleError(cycle, firstInSrc(cycle)) - return false - } + var ( + obj Object + rhs Type + ) + switch t := t.(type) { + case *Alias: + obj = t.obj + rhs = t.fromRHS + case *Named: + obj = t.obj + rhs = t.fromRHS + default: + return true + } - // We must walk through names because we permit certain cycles of names. - // Consider: - // - // type A B - // type B [unsafe.Sizeof(A{})]int - // - // starting at B. At the site of A{}, A has no underlying type, and so a - // cycle must be reported. - return check.isComplete(n.fromRHS) + if i, found := check.objPathIdx[obj]; found { + cycle := check.objPath[i:] + check.cycleError(cycle, firstInSrc(cycle)) + return false } - return true + // We must walk through names because we permit certain cycles of names. + // Consider: + // + // type A B + // type B [unsafe.Sizeof(A{})]int + // + // starting at B. At the site of A{}, A has no underlying type, and so a + // cycle must be reported. + return check.isComplete(rhs) } diff --git a/src/go/types/generate_test.go b/src/go/types/generate_test.go index 2b3cdab88fd9d558c101288fe3e8ce7964c5cfa4..d1ffd99a85152d66ee276861b3ba1dfb2c081768 100644 --- a/src/go/types/generate_test.go +++ b/src/go/types/generate_test.go @@ -143,7 +143,7 @@ "hilbert_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"->"go/types"`) }, "infer.go": func(f *ast.File) { fixTokenPos(f); fixInferSig(f) }, "initorder.go": nil, // "initorder.go": fixErrErrorfCall, // disabled for now due to unresolved error_ use implications for gopls - "instantiate.go": func(f *ast.File) { fixTokenPos(f); fixCheckErrorfCall(f) }, + "instantiate.go": func(f *ast.File) { fixTokenPos(f); fixCheckErrorfCall(f); fixSprintf(f) }, "instantiate_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"->"go/types"`) }, "literals.go": func(f *ast.File) { insertImportPath(f, `"go/token"`) diff --git a/src/go/types/infer.go b/src/go/types/infer.go index d18e19e247f0a8cf054edb6fed81d790f237d1a5..de873cfc774c602d44302f7b0fc33e550807e043 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -114,7 +114,7 @@ // Unify parameter and argument types for generic parameters with typed arguments // and collect the indices of generic parameters with untyped arguments. // Terminology: generic parameter = function parameter with a type-parameterized type - u := newUnifier(tparams, targs, check.allowVersion(go1_21)) + u := newUnifier(check, tparams, targs, check.allowVersion(go1_21)) errorf := func(tpar, targ Type, arg *operand) { // provide a better error message if we can diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index 4851a6f7d56c3fad38bcb4036073652c215edeb5..018998485d6459dfb8b1ae9fc96bec6b472227cd 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -24,7 +24,7 @@ TypeParams() *TypeParamList } // Instantiate instantiates the type orig with the given type arguments targs. -// orig must be an *Alias, *Named, or *Signature type. If there is no error, +// orig must be a generic *Alias, *Named, or *Signature type. If there is no error, // the resulting Type is an instantiated type of the same kind (*Alias, *Named // or *Signature, respectively). // @@ -39,30 +39,37 @@ // equivalent, so that instantiating distinct (but possibly identical) // signatures will yield different instances. The use of a shared context does // not guarantee that identical instances are deduplicated in all cases. // -// If validate is set, Instantiate verifies that the number of type arguments -// and parameters match, and that the type arguments satisfy their respective -// type constraints. If verification fails, the resulting error may wrap an -// *ArgumentError indicating which type argument did not satisfy its type parameter -// constraint, and why. +// If validate is set, Instantiate verifies that the type orig is in fact generic, +// that the number of type arguments and parameters match, and that the type arguments +// satisfy their respective type constraints. +// If verification fails, the resulting error may wrap an *ArgumentError indicating +// which type argument did not satisfy its type parameter constraint, and why. // -// If validate is not set, Instantiate does not verify the type argument count -// or whether the type arguments satisfy their constraints. Instantiate is -// guaranteed to not return an error, but may panic. Specifically, for -// *Signature types, Instantiate will panic immediately if the type argument +// If validate is not set, Instantiate does not check if orig is generic, verify the +// type argument count, or check whether the type arguments satisfy their constraints. +// Instantiate is guaranteed to not return an error, but may panic. Specifically, +// for *Signature types, Instantiate will panic immediately if the type argument // count is incorrect; for *Named types, a panic may occur later inside the // *Named API. func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) { - assert(len(targs) > 0) if ctxt == nil { ctxt = NewContext() } - orig_ := orig.(genericType) // signature of Instantiate must not change for backward-compatibility + orig_, ok := orig.(genericType) // signature of Instantiate must not change for backward-compatibility + if !ok { + panic(sprintf(nil, nil, false, "cannot instantiate non-generic %s: expected *Named, *Alias, or *Signature", orig)) + } + if len(targs) == 0 { + panic(sprintf(nil, nil, false, "cannot instantiate %s: empty type argument list", orig)) + } if validate { tparams := orig_.TypeParams().list() - assert(len(tparams) > 0) + if len(tparams) == 0 { + return nil, fmt.Errorf("cannot instantiate non-generic %s: has no type parameters", orig) + } if len(targs) != len(tparams) { - return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams)) + return nil, fmt.Errorf("cannot instantiate %s: got %d type arguments but have %d type parameters", orig, len(targs), len(tparams)) } if i, err := (*Checker)(nil).verify(nopos, tparams, targs, ctxt); err != nil { return nil, &ArgumentError{i, err} diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go index 2403d403262bf4e2645146031719881ab2b3352e..89d244f18c1e0465d8967aedcc9d81f498300df7 100644 --- a/src/go/types/issues_test.go +++ b/src/go/types/issues_test.go @@ -1205,3 +1205,34 @@ if tv.Type != Typ[Invalid] { t.Fatalf("unexpected type for {x}: %s", tv.Type) } } + +func TestIssue72978(t *testing.T) { + const src = ` +package p + +type ( + genericG[T, U any] struct { x T; y U } + G1 genericG[int, string] + G2 = G1 +) + +func genericF[T, U any]() {} +var f = genericF[string, float64] +` + + pkg := mustTypecheck(src, nil, nil) + for _, name := range []string{"G1", "G2", "f"} { + func() { + typ := pkg.Scope().Lookup(name).Type() + _, err := Instantiate(nil, typ, []Type{Typ[Bool], Typ[Int]}, true) + if err == nil { + t.Errorf("%s[bool, int]: got no error", name) + return + } + want := fmt.Sprintf("cannot instantiate non-generic %s: has no type parameters", typ) + if err.Error() != want { + t.Errorf("%s[bool, int]: got %q, want %q", name, err.Error(), want) + } + }() + } +} diff --git a/src/go/types/unify.go b/src/go/types/unify.go index 070f9d448255e7aec9938e7aa75f6cc330bedd8a..00c93a20a9d9664406ef157844b26b90dbda2f1a 100644 --- a/src/go/types/unify.go +++ b/src/go/types/unify.go @@ -70,6 +70,7 @@ // A unifier maintains a list of type parameters and // corresponding types inferred for each type parameter. // A unifier is created by calling newUnifier. type unifier struct { + check *Checker // handles maps each type parameter to its inferred type through // an indirection *Type called (inferred type) "handle". // Initially, each type parameter has its own, separate handle, @@ -88,7 +89,7 @@ // newUnifier returns a new unifier initialized with the given type parameter // and corresponding type argument lists. The type argument list may be shorter // than the type parameter list, and it may contain nil types. Matching type // parameters and arguments must have the same index. -func newUnifier(tparams []*TypeParam, targs []Type, enableInterfaceInference bool) *unifier { +func newUnifier(check *Checker, tparams []*TypeParam, targs []Type, enableInterfaceInference bool) *unifier { assert(len(tparams) >= len(targs)) handles := make(map[*TypeParam]*Type, len(tparams)) // Allocate all handles up-front: in a correct program, all type parameters @@ -102,7 +103,7 @@ t = targs[i] } handles[x] = &t } - return &unifier{handles, 0, enableInterfaceInference} + return &unifier{check, handles, 0, enableInterfaceInference} } // unifyMode controls the behavior of the unifier. @@ -145,6 +146,7 @@ return u.nify(x, y, mode, nil) } func (u *unifier) tracef(format string, args ...any) { + // TODO(gri) consider adjusting this to use Checker.trace fmt.Println(strings.Repeat(". ", u.depth) + sprintf(nil, nil, true, format, args...)) } @@ -542,7 +544,12 @@ // than unify ym's own type parameter into an inference variable. xmethods := xi.typeSet().methods for _, xm := range xmethods { obj, _, _ := LookupFieldOrMethod(y, false, xm.pkg, xm.name) - if ym, _ := obj.(*Func); ym == nil || ym.Signature().TypeParams().Len() > 0 || !u.nify(xm.typ, ym.typ, exact, p) { + ym, _ := obj.(*Func) + if ym == nil { + return false + } + u.check.objDecl(ym) // ensure fully set-up signature + if ym.Signature().TypeParams() != nil || !u.nify(xm.typ, ym.typ, exact, p) { return false } } diff --git a/src/html/template/attr_string.go b/src/html/template/attr_string.go index 7159fa9cbaa15a0b78c6c98324ad0133d180b05b..009458f429959fbe57e9c8d24c4bc3dfcacd3b68 100644 --- a/src/html/template/attr_string.go +++ b/src/html/template/attr_string.go @@ -22,8 +22,9 @@ var _attr_index = [...]uint8{0, 8, 18, 32, 41, 48, 58, 73} func (i attr) String() string { - if i >= attr(len(_attr_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_attr_index)-1 { return "attr(" + strconv.FormatInt(int64(i), 10) + ")" } - return _attr_name[_attr_index[i]:_attr_index[i+1]] + return _attr_name[_attr_index[idx]:_attr_index[idx+1]] } diff --git a/src/html/template/delim_string.go b/src/html/template/delim_string.go index 8d82850223c920f48768641cf6613b5ad33c1255..eb0c3922e3012951adb896fce3513d81f5e9d66b 100644 --- a/src/html/template/delim_string.go +++ b/src/html/template/delim_string.go @@ -19,8 +19,9 @@ var _delim_index = [...]uint8{0, 9, 25, 41, 59} func (i delim) String() string { - if i >= delim(len(_delim_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_delim_index)-1 { return "delim(" + strconv.FormatInt(int64(i), 10) + ")" } - return _delim_name[_delim_index[i]:_delim_index[i+1]] + return _delim_name[_delim_index[idx]:_delim_index[idx+1]] } diff --git a/src/html/template/element_string.go b/src/html/template/element_string.go index bdf9da7b9d53e665e4f4fc684db8117e59ef57dd..3ccaebcdfd8f35e2657443c01d84fe2e4e96ef4a 100644 --- a/src/html/template/element_string.go +++ b/src/html/template/element_string.go @@ -21,8 +21,9 @@ var _element_index = [...]uint8{0, 11, 24, 36, 51, 63, 74} func (i element) String() string { - if i >= element(len(_element_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_element_index)-1 { return "element(" + strconv.FormatInt(int64(i), 10) + ")" } - return _element_name[_element_index[i]:_element_index[i+1]] + return _element_name[_element_index[idx]:_element_index[idx+1]] } diff --git a/src/html/template/jsctx_string.go b/src/html/template/jsctx_string.go index 23948934c950a10fb9e3533ac58f63eeb147bea0..103d5559f68ae954059e5941832405c644b382ce 100644 --- a/src/html/template/jsctx_string.go +++ b/src/html/template/jsctx_string.go @@ -18,8 +18,9 @@ var _jsCtx_index = [...]uint8{0, 11, 21, 33} func (i jsCtx) String() string { - if i >= jsCtx(len(_jsCtx_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_jsCtx_index)-1 { return "jsCtx(" + strconv.FormatInt(int64(i), 10) + ")" } - return _jsCtx_name[_jsCtx_index[i]:_jsCtx_index[i+1]] + return _jsCtx_name[_jsCtx_index[idx]:_jsCtx_index[idx+1]] } diff --git a/src/html/template/state_string.go b/src/html/template/state_string.go index f5a70b2231c2da6065827496114523eb97c7612d..407064e8d76aec9bf22f1bf1956ca6c50392f314 100644 --- a/src/html/template/state_string.go +++ b/src/html/template/state_string.go @@ -46,8 +46,9 @@ var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 156, 169, 184, 198, 216, 235, 243, 256, 269, 282, 295, 306, 322, 337, 347, 363, 382, 391} func (i state) String() string { - if i >= state(len(_state_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_state_index)-1 { return "state(" + strconv.FormatInt(int64(i), 10) + ")" } - return _state_name[_state_index[i]:_state_index[i+1]] + return _state_name[_state_index[idx]:_state_index[idx+1]] } diff --git a/src/html/template/urlpart_string.go b/src/html/template/urlpart_string.go index 7bc957e81d5703241e8d4ead9ffc2f2c5fa2996b..3fa17e7bd8a71d07a09ab0cecafa89aba14d14ee 100644 --- a/src/html/template/urlpart_string.go +++ b/src/html/template/urlpart_string.go @@ -19,8 +19,9 @@ var _urlPart_index = [...]uint8{0, 11, 26, 44, 58} func (i urlPart) String() string { - if i >= urlPart(len(_urlPart_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_urlPart_index)-1 { return "urlPart(" + strconv.FormatInt(int64(i), 10) + ")" } - return _urlPart_name[_urlPart_index[i]:_urlPart_index[i+1]] + return _urlPart_name[_urlPart_index[idx]:_urlPart_index[idx+1]] } diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go index 8eeef17c1f470e8a49d071070b54a4ea775b24f2..bf411a8dbbfcde9bbf027ab4798fb423313457a7 100644 --- a/src/internal/buildcfg/exp.go +++ b/src/internal/buildcfg/exp.go @@ -85,7 +85,6 @@ Dwarf5: dwarf5Supported, RandomizedHeapBase64: true, GreenTeaGC: true, JSONv2: true, - GenericMethods: true, SizeSpecializedMalloc: true, } flags := &ExperimentFlags{ diff --git a/src/internal/filepathlite/path_windows.go b/src/internal/filepathlite/path_windows.go index 011baa96f0243248a742e29fc79468338f6415d2..2fcde82fa1fd55cd3a5b95ddff5ef68cc7603237 100644 --- a/src/internal/filepathlite/path_windows.go +++ b/src/internal/filepathlite/path_windows.go @@ -219,36 +219,49 @@ case len(path) == 0 || !IsPathSeparator(path[0]): // Path does not have a volume component. return 0 - case pathHasPrefixFold(path, `\\.\UNC`): - // We're going to treat the UNC host and share as part of the volume - // prefix for historical reasons, but this isn't really principled; - // Windows's own GetFullPathName will happily remove the first - // component of the path in this space, converting - // \\.\unc\a\b\..\c into \\.\unc\a\c. - return uncLen(path, len(`\\.\UNC\`)) - case pathHasPrefixFold(path, `\\.`) || pathHasPrefixFold(path, `\\?`) || pathHasPrefixFold(path, `\??`): - // Path starts with \\.\, and is a Local Device path; or - // path starts with \\?\ or \??\ and is a Root Local Device path. + // Path starts with a device prefix: \\.\ for Local Device paths, + // or \\?\ or \??\ for Root Local Device paths. + switch { + case len(path) == 3: + return 3 // exactly \\., \\?, or \?? + case pathHasPrefixFold(path[4:], `UNC`): + // We're going to treat the UNC host and share as part of the volume + // prefix for historical reasons, but this isn't really principled; + // Windows's own GetFullPathName will happily remove the first + // component of the path in this space, converting + // \\.\unc\a\b\..\c into \\.\unc\a\c. + return validVolumeNameLen(path, uncLen(path, len(`\\.\UNC\`))) + } // - // We treat the next component after the \\.\ prefix as + // We treat the next component after the device prefix as // part of the volume name, which means Clean(`\\?\c:\`) // won't remove the trailing \. (See #64028.) - if len(path) == 3 { - return 3 // exactly \\. - } _, rest, ok := cutPath(path[4:]) if !ok { - return len(path) + return validVolumeNameLen(path, len(path)) } - return len(path) - len(rest) - 1 + return validVolumeNameLen(path, len(path)-len(rest)-1) case len(path) >= 2 && IsPathSeparator(path[1]): // Path starts with \\, and is a UNC path. - return uncLen(path, 2) + return validVolumeNameLen(path, uncLen(path, 2)) } return 0 +} + +// validVolumeNameLen returns n if path[:n] is a valid Windows volume name. +// If the volume name contains a ".." path component, it returns 0. +func validVolumeNameLen(path string, n int) int { + for p := path[:n]; p != ""; { + var part string + part, p, _ = cutPath(p) + if part == ".." { + return 0 + } + } + return n } // pathHasPrefixFold tests whether the path s begins with prefix, diff --git a/src/internal/goexperiment/exp_genericmethods_off.go b/src/internal/goexperiment/exp_genericmethods_off.go deleted file mode 100644 index a66e47a0c3fb3611647d284a28cb06a87c7916fa..0000000000000000000000000000000000000000 --- a/src/internal/goexperiment/exp_genericmethods_off.go +++ /dev/null @@ -1,8 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build !goexperiment.genericmethods - -package goexperiment - -const GenericMethods = false -const GenericMethodsInt = 0 diff --git a/src/internal/goexperiment/exp_genericmethods_on.go b/src/internal/goexperiment/exp_genericmethods_on.go deleted file mode 100644 index e50e758c5babebdd60ea33b39385c2db620203c2..0000000000000000000000000000000000000000 --- a/src/internal/goexperiment/exp_genericmethods_on.go +++ /dev/null @@ -1,8 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build goexperiment.genericmethods - -package goexperiment - -const GenericMethods = true -const GenericMethodsInt = 1 diff --git a/src/internal/goexperiment/flags.go b/src/internal/goexperiment/flags.go index 75cf68a752348112cd75fbed659417069e7e4f61..48f3c8f4060be8e07f03d223504670d125f54c3c 100644 --- a/src/internal/goexperiment/flags.go +++ b/src/internal/goexperiment/flags.go @@ -130,7 +130,4 @@ // MapSplitGroup changes the internal representation of map groups // from interleaved key/elem slots (KVKVKVKV) to split key and elem // arrays (KKKKVVVV). MapSplitGroup bool - - // GenericMethods enables use of generic methods. - GenericMethods bool } diff --git a/src/internal/pkgbits/syncmarker_string.go b/src/internal/pkgbits/syncmarker_string.go index 582ad56d3e09f3dfd7c4390312a0e9141aec50ab..86ed5f61be22e98187763fd865ff143fb32e3eab 100644 --- a/src/internal/pkgbits/syncmarker_string.go +++ b/src/internal/pkgbits/syncmarker_string.go @@ -84,9 +84,9 @@ var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458, 467, 472, 480} func (i SyncMarker) String() string { - i -= 1 - if i < 0 || i >= SyncMarker(len(_SyncMarker_index)-1) { - return "SyncMarker(" + strconv.FormatInt(int64(i+1), 10) + ")" + idx := int(i) - 1 + if i < 1 || idx >= len(_SyncMarker_index)-1 { + return "SyncMarker(" + strconv.FormatInt(int64(i), 10) + ")" } - return _SyncMarker_name[_SyncMarker_index[i]:_SyncMarker_index[i+1]] + return _SyncMarker_name[_SyncMarker_index[idx]:_SyncMarker_index[idx+1]] } diff --git a/src/internal/types/testdata/fixedbugs/issue77905.go b/src/internal/types/testdata/fixedbugs/issue77905.go new file mode 100644 index 0000000000000000000000000000000000000000..6288a7c4906f62ef4d3135dfd1a050bfe6756195 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue77905.go @@ -0,0 +1,30 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type M[T any] interface { + m() T +} + +func f[T any](x interface{ m() T }) T { return x.m() } +func g[T any](x M[T]) T { return x.m() } + +type S struct{} + +// inference must work here even though m is declared only afterwards +// (inference must type-check m as needed) +var _ = f(S{}) +var _ = g(S{}) + +func _() { + var s S + var _ = f(s) + var _ = g(s) +} + +func (S) m() int { return 0 } + +var _ = f(S{}) +var _ = g(S{}) diff --git a/src/internal/types/testdata/fixedbugs/issue79265.go b/src/internal/types/testdata/fixedbugs/issue79265.go new file mode 100644 index 0000000000000000000000000000000000000000..b225a2d80d54e119a1eed0c4e1b7ecabc4357f6c --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue79265.go @@ -0,0 +1,17 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "unsafe" + +type _ A // force type-checking of A first; order is relevant +type A /* ERROR "invalid recursive type A" */ = T +type T [unsafe.Sizeof(A{})]int + +// same as above, but through a chain of aliases +type _ A1 +type A1 /* ERROR "invalid recursive type A1" */ = A2 +type A2 = U +type U [unsafe.Sizeof(A1{})]int diff --git a/src/math/big/accuracy_string.go b/src/math/big/accuracy_string.go index aae923829df220894c61df64aa51723c2b369440..32c65fde0a86f8044db2d9f6e1470e88a32231b3 100644 --- a/src/math/big/accuracy_string.go +++ b/src/math/big/accuracy_string.go @@ -18,9 +18,9 @@ var _Accuracy_index = [...]uint8{0, 5, 10, 15} func (i Accuracy) String() string { - i -= -1 - if i < 0 || i >= Accuracy(len(_Accuracy_index)-1) { - return "Accuracy(" + strconv.FormatInt(int64(i+-1), 10) + ")" + idx := int(i) - -1 + if i < -1 || idx >= len(_Accuracy_index)-1 { + return "Accuracy(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Accuracy_name[_Accuracy_index[i]:_Accuracy_index[i+1]] + return _Accuracy_name[_Accuracy_index[idx]:_Accuracy_index[idx+1]] } diff --git a/src/math/big/roundingmode_string.go b/src/math/big/roundingmode_string.go index e2f13a63b7b535b6b27ac5acd096f5552a4a6cba..3b2853349308e5b8dd87660b72513b35f431fa5d 100644 --- a/src/math/big/roundingmode_string.go +++ b/src/math/big/roundingmode_string.go @@ -21,8 +21,9 @@ var _RoundingMode_index = [...]uint8{0, 13, 26, 32, 44, 57, 70} func (i RoundingMode) String() string { - if i >= RoundingMode(len(_RoundingMode_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_RoundingMode_index)-1 { return "RoundingMode(" + strconv.FormatInt(int64(i), 10) + ")" } - return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]] + return _RoundingMode_name[_RoundingMode_index[idx]:_RoundingMode_index[idx+1]] } diff --git a/src/math/rand/v2/n.go b/src/math/rand/v2/n.go deleted file mode 100644 index a8fa575011edf2a1f8117927d12a35d597664b0f..0000000000000000000000000000000000000000 --- a/src/math/rand/v2/n.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2026 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. - -// TODO: When we drop support for nogenericmethods, merge this into -// rand.go and rewrite the package-level N to "return globalRand.N(n)" - -//go:build goexperiment.genericmethods - -package rand - -// N returns a pseudo-random number in the half-open interval [0,n). -// The type parameter Int can be any integer type. -// It panics if n <= 0. -func (r *Rand) N[Int intType](n Int) Int { - if n <= 0 { - panic("invalid argument to N") - } - return Int(r.uint64n(uint64(n))) -} diff --git a/src/math/rand/v2/rand.go b/src/math/rand/v2/rand.go index f6b2e4754fa773b83d1375c863bea9ae1a9be56e..662e84fd70f5f0f313299e4389490f4fed18f8ea 100644 --- a/src/math/rand/v2/rand.go +++ b/src/math/rand/v2/rand.go @@ -204,6 +204,16 @@ } return uint(r.uint64n(uint64(n))) } +// N returns a pseudo-random number in the half-open interval [0,n). +// The type parameter Int can be any integer type. +// It panics if n <= 0. +func (r *Rand) N[Int intType](n Int) Int { + if n <= 0 { + panic("invalid argument to N") + } + return Int(r.uint64n(uint64(n))) +} + // Float64 returns, as a float64, a pseudo-random number in the half-open interval [0.0,1.0). func (r *Rand) Float64() float64 { // There are exactly 1<<53 float64s in [0,1). Use Intn(1<<53) / (1<<53). @@ -321,10 +331,7 @@ // N returns a pseudo-random number in the half-open interval [0,n) from the default Source. // The type parameter Int can be any integer type. // It panics if n <= 0. func N[Int intType](n Int) Int { - if n <= 0 { - panic("invalid argument to N") - } - return Int(globalRand.uint64n(uint64(n))) + return globalRand.N(n) } type intType interface { diff --git a/src/net/http/internal/http2/clientconn_test.go b/src/net/http/internal/http2/clientconn_test.go index d34a35e02a95cdca6e840ffdc3df24e37abfd9ea..a99ab784a720dbe0e204a463b5ed13db84e4e7a6 100644 --- a/src/net/http/internal/http2/clientconn_test.go +++ b/src/net/http/internal/http2/clientconn_test.go @@ -28,8 +28,8 @@ "golang.org/x/net/http2/hpack" ) // TestTestClientConn demonstrates usage of testClientConn. -func TestTestClientConn(t *testing.T) { synctestTest(t, testTestClientConn) } -func testTestClientConn(t testing.TB) { +func TestTestClientConn(t *testing.T) { synctest.Test(t, testTestClientConn) } +func testTestClientConn(t *testing.T) { // newTestClientConn creates a *ClientConn and surrounding test infrastructure. tc := newTestClientConn(t) @@ -96,7 +96,7 @@ // // testClientConn manages synchronization, so tests can generally be written as // a linear sequence of actions and validations without additional synchronization. type testClientConn struct { - t testing.TB + t *testing.T tr *Transport fr *Framer @@ -111,7 +111,7 @@ netconn *synctestNetConn } -func newTestClientConnFromClientConn(t testing.TB, tr *Transport, cc *ClientConn) *testClientConn { +func newTestClientConnFromClientConn(t *testing.T, tr *Transport, cc *ClientConn) *testClientConn { tc := &testClientConn{ t: t, tr: tr, @@ -153,7 +153,7 @@ tc.t.Fatalf("client preface: %q, want %q", buf, ClientPreface) } } -func newTestClientConn(t testing.TB, opts ...any) *testClientConn { +func newTestClientConn(t *testing.T, opts ...any) *testClientConn { t.Helper() tt := newTestTransport(t, opts...) @@ -388,7 +388,7 @@ } // testRoundTrip manages a RoundTrip in progress. type testRoundTrip struct { - t testing.TB + t *testing.T resp *http.Response respErr error donec chan struct{} @@ -519,13 +519,13 @@ // A testTransport allows testing Transport.RoundTrip against fake servers. // Tests that aren't specifically exercising RoundTrip's retry loop or connection pooling // should use testClientConn instead. type testTransport struct { - t testing.TB + t *testing.T tr1 *http.Transport ccs []*testClientConn } -func newTestTransport(t testing.TB, opts ...any) *testTransport { +func newTestTransport(t *testing.T, opts ...any) *testTransport { t.Helper() tt := &testTransport{ t: t, diff --git a/src/net/http/internal/http2/config_test.go b/src/net/http/internal/http2/config_test.go index 37ae011db6bf6eb24bd876ae9a48ef1f0bfcbd91..bdb42cdcd3be86c98d1a330833717a0d739f93e3 100644 --- a/src/net/http/internal/http2/config_test.go +++ b/src/net/http/internal/http2/config_test.go @@ -7,13 +7,14 @@ import ( "net/http" "testing" + "testing/synctest" "time" . "net/http/internal/http2" ) -func TestConfigServerSettings(t *testing.T) { synctestTest(t, testConfigServerSettings) } -func testConfigServerSettings(t testing.TB) { +func TestConfigServerSettings(t *testing.T) { synctest.Test(t, testConfigServerSettings) } +func testConfigServerSettings(t *testing.T) { config := &http.HTTP2Config{ MaxConcurrentStreams: 1, MaxDecoderHeaderTableSize: 1<<20 + 2, @@ -38,8 +39,8 @@ SettingMaxHeaderListSize: maxHeaderBytes + (32 * 10), }) } -func TestConfigTransportSettings(t *testing.T) { synctestTest(t, testConfigTransportSettings) } -func testConfigTransportSettings(t testing.TB) { +func TestConfigTransportSettings(t *testing.T) { synctest.Test(t, testConfigTransportSettings) } +func testConfigTransportSettings(t *testing.T) { config := &http.HTTP2Config{ MaxConcurrentStreams: 1, // ignored by Transport MaxDecoderHeaderTableSize: 1<<20 + 2, @@ -62,8 +63,8 @@ }) tc.wantWindowUpdate(0, uint32(config.MaxReceiveBufferPerConnection)) } -func TestConfigPingTimeoutServer(t *testing.T) { synctestTest(t, testConfigPingTimeoutServer) } -func testConfigPingTimeoutServer(t testing.TB) { +func TestConfigPingTimeoutServer(t *testing.T) { synctest.Test(t, testConfigPingTimeoutServer) } +func testConfigPingTimeoutServer(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { }, func(h2 *http.HTTP2Config) { h2.SendPingTimeout = 2 * time.Second @@ -77,8 +78,8 @@ time.Sleep(3 * time.Second) st.wantClosed() } -func TestConfigPingTimeoutTransport(t *testing.T) { synctestTest(t, testConfigPingTimeoutTransport) } -func testConfigPingTimeoutTransport(t testing.TB) { +func TestConfigPingTimeoutTransport(t *testing.T) { synctest.Test(t, testConfigPingTimeoutTransport) } +func testConfigPingTimeoutTransport(t *testing.T) { tc := newTestClientConn(t, func(h2 *http.HTTP2Config) { h2.SendPingTimeout = 2 * time.Second h2.PingTimeout = 3 * time.Second diff --git a/src/net/http/internal/http2/connframes_test.go b/src/net/http/internal/http2/connframes_test.go index 0590384f13cd587fea88cee5a1cce532d2b01d15..90920ee6e13e80eb437bef7a63e933ca455adb7a 100644 --- a/src/net/http/internal/http2/connframes_test.go +++ b/src/net/http/internal/http2/connframes_test.go @@ -20,7 +20,7 @@ "golang.org/x/net/http2/hpack" ) type testConnFramer struct { - t testing.TB + t *testing.T fr *Framer dec *hpack.Decoder } @@ -45,7 +45,7 @@ readFrame() Frame } // readFrame reads a frame of a specific type. -func readFrame[T any](t testing.TB, framer readFramer) T { +func readFrame[T any](t *testing.T, framer readFramer) T { t.Helper() var v T fr := framer.readFrame() diff --git a/src/net/http/internal/http2/server_push_test.go b/src/net/http/internal/http2/server_push_test.go index 956fadb7bf78f4c70f9f0804d6956d91dec4300b..eb6098ba931234deebf0b34cef9f2eb5ee20753f 100644 --- a/src/net/http/internal/http2/server_push_test.go +++ b/src/net/http/internal/http2/server_push_test.go @@ -18,8 +18,8 @@ . "net/http/internal/http2" ) -func TestServer_Push_Success(t *testing.T) { synctestTest(t, testServer_Push_Success) } -func testServer_Push_Success(t testing.TB) { +func TestServer_Push_Success(t *testing.T) { synctest.Test(t, testServer_Push_Success) } +func testServer_Push_Success(t *testing.T) { const ( mainBody = "index page" pushedBody = "pushed page" @@ -244,8 +244,8 @@ consumed[id]++ } } -func TestServer_Push_SuccessNoRace(t *testing.T) { synctestTest(t, testServer_Push_SuccessNoRace) } -func testServer_Push_SuccessNoRace(t testing.TB) { +func TestServer_Push_SuccessNoRace(t *testing.T) { synctest.Test(t, testServer_Push_SuccessNoRace) } +func testServer_Push_SuccessNoRace(t *testing.T) { // Regression test for issue #18326. Ensure the request handler can mutate // pushed request headers without racing with the PUSH_PROMISE write. errc := make(chan error, 2) @@ -290,9 +290,9 @@ } } func TestServer_Push_RejectRecursivePush(t *testing.T) { - synctestTest(t, testServer_Push_RejectRecursivePush) + synctest.Test(t, testServer_Push_RejectRecursivePush) } -func testServer_Push_RejectRecursivePush(t testing.TB) { +func testServer_Push_RejectRecursivePush(t *testing.T) { // Expect two requests, but might get three if there's a bug and the second push succeeds. errc := make(chan error, 3) handler := func(w http.ResponseWriter, r *http.Request) error { @@ -329,11 +329,11 @@ } } func testServer_Push_RejectSingleRequest(t *testing.T, doPush func(http.Pusher, *http.Request) error, settings ...Setting) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testServer_Push_RejectSingleRequest_Bubble(t, doPush, settings...) }) } -func testServer_Push_RejectSingleRequest_Bubble(t testing.TB, doPush func(http.Pusher, *http.Request) error, settings ...Setting) { +func testServer_Push_RejectSingleRequest_Bubble(t *testing.T, doPush func(http.Pusher, *http.Request) error, settings ...Setting) { // Expect one request, but might get two if there's a bug and the push succeeds. errc := make(chan error, 2) st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { @@ -437,9 +437,9 @@ }) } func TestServer_Push_StateTransitions(t *testing.T) { - synctestTest(t, testServer_Push_StateTransitions) + synctest.Test(t, testServer_Push_StateTransitions) } -func testServer_Push_StateTransitions(t testing.TB) { +func testServer_Push_StateTransitions(t *testing.T) { const body = "foo" gotPromise := make(chan bool) @@ -493,9 +493,9 @@ close(finishedPush) } func TestServer_Push_RejectAfterGoAway(t *testing.T) { - synctestTest(t, testServer_Push_RejectAfterGoAway) + synctest.Test(t, testServer_Push_RejectAfterGoAway) } -func testServer_Push_RejectAfterGoAway(t testing.TB) { +func testServer_Push_RejectAfterGoAway(t *testing.T) { ready := make(chan struct{}) errc := make(chan error, 2) st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { @@ -518,8 +518,8 @@ t.Error(err) } } -func TestServer_Push_Underflow(t *testing.T) { synctestTest(t, testServer_Push_Underflow) } -func testServer_Push_Underflow(t testing.TB) { +func TestServer_Push_Underflow(t *testing.T) { synctest.Test(t, testServer_Push_Underflow) } +func testServer_Push_Underflow(t *testing.T) { // Test for #63511: Send several requests which generate PUSH_PROMISE responses, // verify they all complete successfully. st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { diff --git a/src/net/http/internal/http2/server_test.go b/src/net/http/internal/http2/server_test.go index d83efbf3610bd6930c1168ac8911e5442ef5b2f2..3d42cd4a4ac4043dfdc7da59c1a70962811bf8e4 100644 --- a/src/net/http/internal/http2/server_test.go +++ b/src/net/http/internal/http2/server_test.go @@ -74,7 +74,7 @@ } type serverTester struct { cc net.Conn // client conn - t testing.TB + t *testing.T h1server *http.Server h2server *Server serverLogBuf safeBuffer // logger for httptest.Server @@ -173,7 +173,7 @@ var optQuiet = func(server *http.Server) { server.ErrorLog = log.New(io.Discard, "", 0) } -func newServerTester(t testing.TB, handler http.HandlerFunc, opts ...any) *serverTester { +func newServerTester(t *testing.T, handler http.HandlerFunc, opts ...any) *serverTester { t.Helper() h1server := &http.Server{} @@ -584,8 +584,8 @@ st.t.Errorf("connection flow control consumed: %v, want %v", got, want) } } -func TestServer(t *testing.T) { synctestTest(t, testServer) } -func testServer(t testing.TB) { +func TestServer(t *testing.T) { synctest.Test(t, testServer) } +func testServer(t *testing.T) { gotReq := make(chan bool, 1) st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Foo", "Bar") @@ -604,8 +604,8 @@ <-gotReq } -func TestServer_Request_Get(t *testing.T) { synctestTest(t, testServer_Request_Get) } -func testServer_Request_Get(t testing.TB) { +func TestServer_Request_Get(t *testing.T) { synctest.Test(t, testServer_Request_Get) } +func testServer_Request_Get(t *testing.T) { testServerRequest(t, func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ StreamID: 1, // clients send odd numbers @@ -645,9 +645,9 @@ }) } func TestServer_Request_Get_PathSlashes(t *testing.T) { - synctestTest(t, testServer_Request_Get_PathSlashes) + synctest.Test(t, testServer_Request_Get_PathSlashes) } -func testServer_Request_Get_PathSlashes(t testing.TB) { +func testServer_Request_Get_PathSlashes(t *testing.T) { testServerRequest(t, func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ StreamID: 1, // clients send odd numbers @@ -670,9 +670,9 @@ // Content-Length anyway. Should we just omit it and force it to // zero? func TestServer_Request_Post_NoContentLength_EndStream(t *testing.T) { - synctestTest(t, testServer_Request_Post_NoContentLength_EndStream) + synctest.Test(t, testServer_Request_Post_NoContentLength_EndStream) } -func testServer_Request_Post_NoContentLength_EndStream(t testing.TB) { +func testServer_Request_Post_NoContentLength_EndStream(t *testing.T) { testServerRequest(t, func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ StreamID: 1, // clients send odd numbers @@ -694,9 +694,9 @@ }) } func TestServer_Request_Post_Body_ImmediateEOF(t *testing.T) { - synctestTest(t, testServer_Request_Post_Body_ImmediateEOF) + synctest.Test(t, testServer_Request_Post_Body_ImmediateEOF) } -func testServer_Request_Post_Body_ImmediateEOF(t testing.TB) { +func testServer_Request_Post_Body_ImmediateEOF(t *testing.T) { testBodyContents(t, -1, "", func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ StreamID: 1, // clients send odd numbers @@ -709,9 +709,9 @@ }) } func TestServer_Request_Post_Body_OneData(t *testing.T) { - synctestTest(t, testServer_Request_Post_Body_OneData) + synctest.Test(t, testServer_Request_Post_Body_OneData) } -func testServer_Request_Post_Body_OneData(t testing.TB) { +func testServer_Request_Post_Body_OneData(t *testing.T) { const content = "Some content" testBodyContents(t, -1, content, func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ @@ -725,9 +725,9 @@ }) } func TestServer_Request_Post_Body_TwoData(t *testing.T) { - synctestTest(t, testServer_Request_Post_Body_TwoData) + synctest.Test(t, testServer_Request_Post_Body_TwoData) } -func testServer_Request_Post_Body_TwoData(t testing.TB) { +func testServer_Request_Post_Body_TwoData(t *testing.T) { const content = "Some content" testBodyContents(t, -1, content, func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ @@ -742,9 +742,9 @@ }) } func TestServer_Request_Post_Body_ContentLength_Correct(t *testing.T) { - synctestTest(t, testServer_Request_Post_Body_ContentLength_Correct) + synctest.Test(t, testServer_Request_Post_Body_ContentLength_Correct) } -func testServer_Request_Post_Body_ContentLength_Correct(t testing.TB) { +func testServer_Request_Post_Body_ContentLength_Correct(t *testing.T) { const content = "Some content" testBodyContents(t, int64(len(content)), content, func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ @@ -761,9 +761,9 @@ }) } func TestServer_Request_Post_Body_ContentLength_TooLarge(t *testing.T) { - synctestTest(t, testServer_Request_Post_Body_ContentLength_TooLarge) + synctest.Test(t, testServer_Request_Post_Body_ContentLength_TooLarge) } -func testServer_Request_Post_Body_ContentLength_TooLarge(t testing.TB) { +func testServer_Request_Post_Body_ContentLength_TooLarge(t *testing.T) { testBodyContentsFail(t, 3, "request declared a Content-Length of 3 but only wrote 2 bytes", func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ @@ -794,9 +794,9 @@ }) } func TestServer_Request_Post_Body_ContentLength_TooSmall(t *testing.T) { - synctestTest(t, testServer_Request_Post_Body_ContentLength_TooSmall) + synctest.Test(t, testServer_Request_Post_Body_ContentLength_TooSmall) } -func testServer_Request_Post_Body_ContentLength_TooSmall(t testing.TB) { +func testServer_Request_Post_Body_ContentLength_TooSmall(t *testing.T) { testBodyContentsFail(t, 4, "sender tried to send more than declared Content-Length of 4 bytes", func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ @@ -816,7 +816,7 @@ st.wantConnFlowControlConsumed(0) }) } -func testBodyContents(t testing.TB, wantContentLength int64, wantBody string, write func(st *serverTester)) { +func testBodyContents(t *testing.T, wantContentLength int64, wantBody string, write func(st *serverTester)) { testServerRequest(t, write, func(r *http.Request) { if r.Method != "POST" { t.Errorf("Method = %q; want POST", r.Method) @@ -837,7 +837,7 @@ } }) } -func testBodyContentsFail(t testing.TB, wantContentLength int64, wantReadError string, write func(st *serverTester)) { +func testBodyContentsFail(t *testing.T, wantContentLength int64, wantReadError string, write func(st *serverTester)) { testServerRequest(t, write, func(r *http.Request) { if r.Method != "POST" { t.Errorf("Method = %q; want POST", r.Method) @@ -860,8 +860,8 @@ }) } // Using a Host header, instead of :authority -func TestServer_Request_Get_Host(t *testing.T) { synctestTest(t, testServer_Request_Get_Host) } -func testServer_Request_Get_Host(t testing.TB) { +func TestServer_Request_Get_Host(t *testing.T) { synctest.Test(t, testServer_Request_Get_Host) } +func testServer_Request_Get_Host(t *testing.T) { const host = "example.com" testServerRequest(t, func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ @@ -879,9 +879,9 @@ } // Using an :authority pseudo-header, instead of Host func TestServer_Request_Get_Authority(t *testing.T) { - synctestTest(t, testServer_Request_Get_Authority) + synctest.Test(t, testServer_Request_Get_Authority) } -func testServer_Request_Get_Authority(t testing.TB) { +func testServer_Request_Get_Authority(t *testing.T) { const host = "example.com" testServerRequest(t, func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ @@ -898,9 +898,9 @@ }) } func TestServer_Request_WithContinuation(t *testing.T) { - synctestTest(t, testServer_Request_WithContinuation) + synctest.Test(t, testServer_Request_WithContinuation) } -func testServer_Request_WithContinuation(t testing.TB) { +func testServer_Request_WithContinuation(t *testing.T) { wantHeader := http.Header{ "Foo-One": []string{"value-one"}, "Foo-Two": []string{"value-two"}, @@ -948,8 +948,8 @@ }) } // Concatenated cookie headers. ("8.1.2.5 Compressing the Cookie Header Field") -func TestServer_Request_CookieConcat(t *testing.T) { synctestTest(t, testServer_Request_CookieConcat) } -func testServer_Request_CookieConcat(t testing.TB) { +func TestServer_Request_CookieConcat(t *testing.T) { synctest.Test(t, testServer_Request_CookieConcat) } +func testServer_Request_CookieConcat(t *testing.T) { const host = "example.com" testServerRequest(t, func(st *serverTester) { st.bodylessReq1( @@ -1071,7 +1071,7 @@ }) } func testRejectRequest(t *testing.T, send func(*serverTester)) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { t.Error("server request made it to handler; should've been rejected") }) @@ -1083,7 +1083,7 @@ st.wantRSTStream(1, ErrCodeProtocol) }) } -func newServerTesterForError(t testing.TB) *serverTester { +func newServerTesterForError(t *testing.T) *serverTester { t.Helper() st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { t.Error("server request made it to handler; should've been rejected") @@ -1096,28 +1096,28 @@ // Section 5.1, on idle connections: "Receiving any frame other than // HEADERS or PRIORITY on a stream in this state MUST be treated as a // connection error (Section 5.4.1) of type PROTOCOL_ERROR." func TestRejectFrameOnIdle_WindowUpdate(t *testing.T) { - synctestTest(t, testRejectFrameOnIdle_WindowUpdate) + synctest.Test(t, testRejectFrameOnIdle_WindowUpdate) } -func testRejectFrameOnIdle_WindowUpdate(t testing.TB) { +func testRejectFrameOnIdle_WindowUpdate(t *testing.T) { st := newServerTesterForError(t) st.fr.WriteWindowUpdate(123, 456) st.wantGoAway(123, ErrCodeProtocol) } -func TestRejectFrameOnIdle_Data(t *testing.T) { synctestTest(t, testRejectFrameOnIdle_Data) } -func testRejectFrameOnIdle_Data(t testing.TB) { +func TestRejectFrameOnIdle_Data(t *testing.T) { synctest.Test(t, testRejectFrameOnIdle_Data) } +func testRejectFrameOnIdle_Data(t *testing.T) { st := newServerTesterForError(t) st.fr.WriteData(123, true, nil) st.wantGoAway(123, ErrCodeProtocol) } -func TestRejectFrameOnIdle_RSTStream(t *testing.T) { synctestTest(t, testRejectFrameOnIdle_RSTStream) } -func testRejectFrameOnIdle_RSTStream(t testing.TB) { +func TestRejectFrameOnIdle_RSTStream(t *testing.T) { synctest.Test(t, testRejectFrameOnIdle_RSTStream) } +func testRejectFrameOnIdle_RSTStream(t *testing.T) { st := newServerTesterForError(t) st.fr.WriteRSTStream(123, ErrCodeCancel) st.wantGoAway(123, ErrCodeProtocol) } -func TestServer_Request_Connect(t *testing.T) { synctestTest(t, testServer_Request_Connect) } -func testServer_Request_Connect(t testing.TB) { +func TestServer_Request_Connect(t *testing.T) { synctest.Test(t, testServer_Request_Connect) } +func testServer_Request_Connect(t *testing.T) { testServerRequest(t, func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ StreamID: 1, @@ -1142,9 +1142,9 @@ }) } func TestServer_Request_Connect_InvalidPath(t *testing.T) { - synctestTest(t, testServer_Request_Connect_InvalidPath) + synctest.Test(t, testServer_Request_Connect_InvalidPath) } -func testServer_Request_Connect_InvalidPath(t testing.TB) { +func testServer_Request_Connect_InvalidPath(t *testing.T) { testServerRejectsStream(t, ErrCodeProtocol, func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ StreamID: 1, @@ -1160,9 +1160,9 @@ }) } func TestServer_Request_Connect_InvalidScheme(t *testing.T) { - synctestTest(t, testServer_Request_Connect_InvalidScheme) + synctest.Test(t, testServer_Request_Connect_InvalidScheme) } -func testServer_Request_Connect_InvalidScheme(t testing.TB) { +func testServer_Request_Connect_InvalidScheme(t *testing.T) { testServerRejectsStream(t, ErrCodeProtocol, func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ StreamID: 1, @@ -1177,8 +1177,8 @@ }) }) } -func TestServer_Ping(t *testing.T) { synctestTest(t, testServer_Ping) } -func testServer_Ping(t testing.TB) { +func TestServer_Ping(t *testing.T) { synctest.Test(t, testServer_Ping) } +func testServer_Ping(t *testing.T) { st := newServerTester(t, nil) defer st.Close() st.greet() @@ -1218,9 +1218,9 @@ return l.accept(c) } func TestServer_MaxQueuedControlFrames(t *testing.T) { - synctestTest(t, testServer_MaxQueuedControlFrames) + synctest.Test(t, testServer_MaxQueuedControlFrames) } -func testServer_MaxQueuedControlFrames(t testing.TB) { +func testServer_MaxQueuedControlFrames(t *testing.T) { // Goroutine debugging makes this test very slow. DisableGoroutineTracking(t) @@ -1252,8 +1252,8 @@ } st.wantClosed() } -func TestServer_RejectsLargeFrames(t *testing.T) { synctestTest(t, testServer_RejectsLargeFrames) } -func testServer_RejectsLargeFrames(t testing.TB) { +func TestServer_RejectsLargeFrames(t *testing.T) { synctest.Test(t, testServer_RejectsLargeFrames) } +func testServer_RejectsLargeFrames(t *testing.T) { if runtime.GOOS == "windows" || runtime.GOOS == "plan9" || runtime.GOOS == "zos" { t.Skip("see golang.org/issue/13434, golang.org/issue/37321") } @@ -1272,9 +1272,9 @@ st.wantClosed() } func TestServer_Handler_Sends_WindowUpdate(t *testing.T) { - synctestTest(t, testServer_Handler_Sends_WindowUpdate) + synctest.Test(t, testServer_Handler_Sends_WindowUpdate) } -func testServer_Handler_Sends_WindowUpdate(t testing.TB) { +func testServer_Handler_Sends_WindowUpdate(t *testing.T) { // Need to set this to at least twice the initial window size, // or st.greet gets stuck waiting for a WINDOW_UPDATE. // @@ -1317,9 +1317,9 @@ // the version of the TestServer_Handler_Sends_WindowUpdate with padding. // See golang.org/issue/16556 func TestServer_Handler_Sends_WindowUpdate_Padding(t *testing.T) { - synctestTest(t, testServer_Handler_Sends_WindowUpdate_Padding) + synctest.Test(t, testServer_Handler_Sends_WindowUpdate_Padding) } -func testServer_Handler_Sends_WindowUpdate_Padding(t testing.TB) { +func testServer_Handler_Sends_WindowUpdate_Padding(t *testing.T) { const windowSize = 65535 * 2 st := newServerTester(t, nil, func(h2 *http.HTTP2Config) { h2.MaxReceiveBufferPerConnection = windowSize @@ -1352,9 +1352,9 @@ st.wantWindowUpdate(1, uint32(len(data)+1+len(pad))) } func TestServer_Send_GoAway_After_Bogus_WindowUpdate(t *testing.T) { - synctestTest(t, testServer_Send_GoAway_After_Bogus_WindowUpdate) + synctest.Test(t, testServer_Send_GoAway_After_Bogus_WindowUpdate) } -func testServer_Send_GoAway_After_Bogus_WindowUpdate(t testing.TB) { +func testServer_Send_GoAway_After_Bogus_WindowUpdate(t *testing.T) { st := newServerTester(t, nil) defer st.Close() st.greet() @@ -1365,9 +1365,9 @@ st.wantGoAway(0, ErrCodeFlowControl) } func TestServer_Send_RstStream_After_Bogus_WindowUpdate(t *testing.T) { - synctestTest(t, testServer_Send_RstStream_After_Bogus_WindowUpdate) + synctest.Test(t, testServer_Send_RstStream_After_Bogus_WindowUpdate) } -func testServer_Send_RstStream_After_Bogus_WindowUpdate(t testing.TB) { +func testServer_Send_RstStream_After_Bogus_WindowUpdate(t *testing.T) { inHandler := make(chan bool) blockHandler := make(chan bool) st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { @@ -1394,7 +1394,7 @@ // testServerPostUnblock sends a hanging POST with unsent data to handler, // then runs fn once in the handler, and verifies that the error returned from // handler is acceptable. It fails if takes over 5 seconds for handler to exit. -func testServerPostUnblock(t testing.TB, +func testServerPostUnblock(t *testing.T, handler func(http.ResponseWriter, *http.Request) error, fn func(*serverTester), checkErr func(error), @@ -1422,9 +1422,9 @@ } } func TestServer_RSTStream_Unblocks_Read(t *testing.T) { - synctestTest(t, testServer_RSTStream_Unblocks_Read) + synctest.Test(t, testServer_RSTStream_Unblocks_Read) } -func testServer_RSTStream_Unblocks_Read(t testing.TB) { +func testServer_RSTStream_Unblocks_Read(t *testing.T) { testServerPostUnblock(t, func(w http.ResponseWriter, r *http.Request) (err error) { _, err = r.Body.Read(make([]byte, 1)) @@ -1452,11 +1452,11 @@ if testing.Short() { n = 5 } for i := 0; i < n; i++ { - synctestTest(t, testServer_RSTStream_Unblocks_Header_Write) + synctest.Test(t, testServer_RSTStream_Unblocks_Header_Write) } } -func testServer_RSTStream_Unblocks_Header_Write(t testing.TB) { +func testServer_RSTStream_Unblocks_Header_Write(t *testing.T) { inHandler := make(chan bool, 1) unblockHandler := make(chan bool, 1) headerWritten := make(chan bool, 1) @@ -1491,9 +1491,9 @@ unblockHandler <- true } func TestServer_DeadConn_Unblocks_Read(t *testing.T) { - synctestTest(t, testServer_DeadConn_Unblocks_Read) + synctest.Test(t, testServer_DeadConn_Unblocks_Read) } -func testServer_DeadConn_Unblocks_Read(t testing.TB) { +func testServer_DeadConn_Unblocks_Read(t *testing.T) { testServerPostUnblock(t, func(w http.ResponseWriter, r *http.Request) (err error) { _, err = r.Body.Read(make([]byte, 1)) @@ -1514,9 +1514,9 @@ return nil } func TestServer_CloseNotify_After_RSTStream(t *testing.T) { - synctestTest(t, testServer_CloseNotify_After_RSTStream) + synctest.Test(t, testServer_CloseNotify_After_RSTStream) } -func testServer_CloseNotify_After_RSTStream(t testing.TB) { +func testServer_CloseNotify_After_RSTStream(t *testing.T) { testServerPostUnblock(t, blockUntilClosed, func(st *serverTester) { if err := st.fr.WriteRSTStream(1, ErrCodeCancel); err != nil { t.Fatal(err) @@ -1525,9 +1525,9 @@ }, nil) } func TestServer_CloseNotify_After_ConnClose(t *testing.T) { - synctestTest(t, testServer_CloseNotify_After_ConnClose) + synctest.Test(t, testServer_CloseNotify_After_ConnClose) } -func testServer_CloseNotify_After_ConnClose(t testing.TB) { +func testServer_CloseNotify_After_ConnClose(t *testing.T) { testServerPostUnblock(t, blockUntilClosed, func(st *serverTester) { st.cc.Close() }, nil) } @@ -1535,17 +1535,17 @@ // that CloseNotify unblocks after a stream error due to the client's // problem that's unrelated to them explicitly canceling it (which is // TestServer_CloseNotify_After_RSTStream above) func TestServer_CloseNotify_After_StreamError(t *testing.T) { - synctestTest(t, testServer_CloseNotify_After_StreamError) + synctest.Test(t, testServer_CloseNotify_After_StreamError) } -func testServer_CloseNotify_After_StreamError(t testing.TB) { +func testServer_CloseNotify_After_StreamError(t *testing.T) { testServerPostUnblock(t, blockUntilClosed, func(st *serverTester) { // data longer than declared Content-Length => stream error st.writeData(1, true, []byte("1234")) }, nil, "content-length", "3") } -func TestServer_StateTransitions(t *testing.T) { synctestTest(t, testServer_StateTransitions) } -func testServer_StateTransitions(t testing.TB) { +func TestServer_StateTransitions(t *testing.T) { synctest.Test(t, testServer_StateTransitions) } +func testServer_StateTransitions(t *testing.T) { var st *serverTester inHandler := make(chan bool) writeData := make(chan bool) @@ -1602,9 +1602,9 @@ } // test HEADERS w/o EndHeaders + another HEADERS (should get rejected) func TestServer_Rejects_HeadersNoEnd_Then_Headers(t *testing.T) { - synctestTest(t, testServer_Rejects_HeadersNoEnd_Then_Headers) + synctest.Test(t, testServer_Rejects_HeadersNoEnd_Then_Headers) } -func testServer_Rejects_HeadersNoEnd_Then_Headers(t testing.TB) { +func testServer_Rejects_HeadersNoEnd_Then_Headers(t *testing.T) { st := newServerTesterForError(t) st.writeHeaders(HeadersFrameParam{ StreamID: 1, @@ -1623,9 +1623,9 @@ } // test HEADERS w/o EndHeaders + PING (should get rejected) func TestServer_Rejects_HeadersNoEnd_Then_Ping(t *testing.T) { - synctestTest(t, testServer_Rejects_HeadersNoEnd_Then_Ping) + synctest.Test(t, testServer_Rejects_HeadersNoEnd_Then_Ping) } -func testServer_Rejects_HeadersNoEnd_Then_Ping(t testing.TB) { +func testServer_Rejects_HeadersNoEnd_Then_Ping(t *testing.T) { st := newServerTesterForError(t) st.writeHeaders(HeadersFrameParam{ StreamID: 1, @@ -1641,9 +1641,9 @@ } // test HEADERS w/ EndHeaders + a continuation HEADERS (should get rejected) func TestServer_Rejects_HeadersEnd_Then_Continuation(t *testing.T) { - synctestTest(t, testServer_Rejects_HeadersEnd_Then_Continuation) + synctest.Test(t, testServer_Rejects_HeadersEnd_Then_Continuation) } -func testServer_Rejects_HeadersEnd_Then_Continuation(t testing.TB) { +func testServer_Rejects_HeadersEnd_Then_Continuation(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {}, optQuiet) st.greet() st.writeHeaders(HeadersFrameParam{ @@ -1664,9 +1664,9 @@ } // test HEADERS w/o EndHeaders + a continuation HEADERS on wrong stream ID func TestServer_Rejects_HeadersNoEnd_Then_ContinuationWrongStream(t *testing.T) { - synctestTest(t, testServer_Rejects_HeadersNoEnd_Then_ContinuationWrongStream) + synctest.Test(t, testServer_Rejects_HeadersNoEnd_Then_ContinuationWrongStream) } -func testServer_Rejects_HeadersNoEnd_Then_ContinuationWrongStream(t testing.TB) { +func testServer_Rejects_HeadersNoEnd_Then_ContinuationWrongStream(t *testing.T) { st := newServerTesterForError(t) st.writeHeaders(HeadersFrameParam{ StreamID: 1, @@ -1681,8 +1681,8 @@ st.wantGoAway(0, ErrCodeProtocol) } // No HEADERS on stream 0. -func TestServer_Rejects_Headers0(t *testing.T) { synctestTest(t, testServer_Rejects_Headers0) } -func testServer_Rejects_Headers0(t testing.TB) { +func TestServer_Rejects_Headers0(t *testing.T) { synctest.Test(t, testServer_Rejects_Headers0) } +func testServer_Rejects_Headers0(t *testing.T) { st := newServerTesterForError(t) st.fr.AllowIllegalWrites = true st.writeHeaders(HeadersFrameParam{ @@ -1696,9 +1696,9 @@ } // No CONTINUATION on stream 0. func TestServer_Rejects_Continuation0(t *testing.T) { - synctestTest(t, testServer_Rejects_Continuation0) + synctest.Test(t, testServer_Rejects_Continuation0) } -func testServer_Rejects_Continuation0(t testing.TB) { +func testServer_Rejects_Continuation0(t *testing.T) { st := newServerTesterForError(t) st.fr.AllowIllegalWrites = true if err := st.fr.WriteContinuation(0, true, st.encodeHeader()); err != nil { @@ -1708,8 +1708,8 @@ st.wantGoAway(0, ErrCodeProtocol) } // No PRIORITY on stream 0. -func TestServer_Rejects_Priority0(t *testing.T) { synctestTest(t, testServer_Rejects_Priority0) } -func testServer_Rejects_Priority0(t testing.TB) { +func TestServer_Rejects_Priority0(t *testing.T) { synctest.Test(t, testServer_Rejects_Priority0) } +func testServer_Rejects_Priority0(t *testing.T) { st := newServerTesterForError(t) st.fr.AllowIllegalWrites = true st.writePriority(0, PriorityParam{StreamDep: 1}) @@ -1719,9 +1719,9 @@ // PRIORITY_UPDATE only accepts non-zero ID for the prioritized stream ID in // its payload. func TestServer_Rejects_PriorityUpdate0(t *testing.T) { - synctestTest(t, testServer_Rejects_PriorityUpdate0) + synctest.Test(t, testServer_Rejects_PriorityUpdate0) } -func testServer_Rejects_PriorityUpdate0(t testing.TB) { +func testServer_Rejects_PriorityUpdate0(t *testing.T) { st := newServerTesterForError(t) st.fr.AllowIllegalWrites = true st.writePriorityUpdate(0, "") @@ -1730,9 +1730,9 @@ } // PRIORITY_UPDATE with unparsable priority parameters may be rejected. func TestServer_Rejects_PriorityUpdateUnparsable(t *testing.T) { - synctestTest(t, testServer_Rejects_PriorityUnparsable) + synctest.Test(t, testServer_Rejects_PriorityUnparsable) } -func testServer_Rejects_PriorityUnparsable(t testing.TB) { +func testServer_Rejects_PriorityUnparsable(t *testing.T) { st := newServerTester(t, nil) defer st.Close() st.greet() @@ -1742,9 +1742,9 @@ } // No HEADERS frame with a self-dependence. func TestServer_Rejects_HeadersSelfDependence(t *testing.T) { - synctestTest(t, testServer_Rejects_HeadersSelfDependence) + synctest.Test(t, testServer_Rejects_HeadersSelfDependence) } -func testServer_Rejects_HeadersSelfDependence(t testing.TB) { +func testServer_Rejects_HeadersSelfDependence(t *testing.T) { testServerRejectsStream(t, ErrCodeProtocol, func(st *serverTester) { st.fr.AllowIllegalWrites = true st.writeHeaders(HeadersFrameParam{ @@ -1759,17 +1759,17 @@ } // No PRIORITY frame with a self-dependence. func TestServer_Rejects_PrioritySelfDependence(t *testing.T) { - synctestTest(t, testServer_Rejects_PrioritySelfDependence) + synctest.Test(t, testServer_Rejects_PrioritySelfDependence) } -func testServer_Rejects_PrioritySelfDependence(t testing.TB) { +func testServer_Rejects_PrioritySelfDependence(t *testing.T) { testServerRejectsStream(t, ErrCodeProtocol, func(st *serverTester) { st.fr.AllowIllegalWrites = true st.writePriority(1, PriorityParam{StreamDep: 1}) }) } -func TestServer_Rejects_PushPromise(t *testing.T) { synctestTest(t, testServer_Rejects_PushPromise) } -func testServer_Rejects_PushPromise(t testing.TB) { +func TestServer_Rejects_PushPromise(t *testing.T) { synctest.Test(t, testServer_Rejects_PushPromise) } +func testServer_Rejects_PushPromise(t *testing.T) { st := newServerTesterForError(t) pp := PushPromiseParam{ StreamID: 1, @@ -1783,7 +1783,7 @@ } // testServerRejectsStream tests that the server sends a RST_STREAM with the provided // error code after a client sends a bogus request. -func testServerRejectsStream(t testing.TB, code ErrCode, writeReq func(*serverTester)) { +func testServerRejectsStream(t *testing.T, code ErrCode, writeReq func(*serverTester)) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {}) defer st.Close() st.greet() @@ -1794,7 +1794,7 @@ // testServerRequest sets up an idle HTTP/2 connection and lets you // write a single request with writeReq, and then verify that the // *http.Request is built correctly in checkReq. -func testServerRequest(t testing.TB, writeReq func(*serverTester), checkReq func(*http.Request)) { +func testServerRequest(t *testing.T, writeReq func(*serverTester), checkReq func(*http.Request)) { gotReq := make(chan bool, 1) st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { if r.Body == nil { @@ -1812,8 +1812,8 @@ } func getSlash(st *serverTester) { st.bodylessReq1() } -func TestServer_Response_NoData(t *testing.T) { synctestTest(t, testServer_Response_NoData) } -func testServer_Response_NoData(t testing.TB) { +func TestServer_Response_NoData(t *testing.T) { synctest.Test(t, testServer_Response_NoData) } +func testServer_Response_NoData(t *testing.T) { testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { // Nothing. return nil @@ -1827,9 +1827,9 @@ }) } func TestServer_Response_NoData_Header_FooBar(t *testing.T) { - synctestTest(t, testServer_Response_NoData_Header_FooBar) + synctest.Test(t, testServer_Response_NoData_Header_FooBar) } -func testServer_Response_NoData_Header_FooBar(t testing.TB) { +func testServer_Response_NoData_Header_FooBar(t *testing.T) { testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { w.Header().Set("Foo-Bar", "some-value") return nil @@ -1850,9 +1850,9 @@ // Reject content-length headers containing a sign. // See https://golang.org/issue/39017 func TestServerIgnoresContentLengthSignWhenWritingChunks(t *testing.T) { - synctestTest(t, testServerIgnoresContentLengthSignWhenWritingChunks) + synctest.Test(t, testServerIgnoresContentLengthSignWhenWritingChunks) } -func testServerIgnoresContentLengthSignWhenWritingChunks(t testing.TB) { +func testServerIgnoresContentLengthSignWhenWritingChunks(t *testing.T) { tests := []struct { name string cl string @@ -1939,7 +1939,7 @@ }, } for _, tt := range tests { - synctestSubtest(t, tt.name, func(t testing.TB) { + synctestSubtest(t, tt.name, func(t *testing.T) { writeReq := func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ StreamID: 1, // clients send odd numbers @@ -1987,7 +1987,7 @@ wantOk: false, }, } for _, tt := range tests { - synctestSubtest(t, tt.name, func(t testing.TB) { + synctestSubtest(t, tt.name, func(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }) @@ -2018,9 +2018,9 @@ } } func TestServer_Response_Data_Sniff_DoesntOverride(t *testing.T) { - synctestTest(t, testServer_Response_Data_Sniff_DoesntOverride) + synctest.Test(t, testServer_Response_Data_Sniff_DoesntOverride) } -func testServer_Response_Data_Sniff_DoesntOverride(t testing.TB) { +func testServer_Response_Data_Sniff_DoesntOverride(t *testing.T) { const msg = "this is HTML." testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { w.Header().Set("Content-Type", "foo/bar") @@ -2046,9 +2046,9 @@ }) } func TestServer_Response_TransferEncoding_chunked(t *testing.T) { - synctestTest(t, testServer_Response_TransferEncoding_chunked) + synctest.Test(t, testServer_Response_TransferEncoding_chunked) } -func testServer_Response_TransferEncoding_chunked(t testing.TB) { +func testServer_Response_TransferEncoding_chunked(t *testing.T) { const msg = "hi" testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { w.Header().Set("Transfer-Encoding", "chunked") // should be stripped @@ -2070,9 +2070,9 @@ } // Header accessed only after the initial write. func TestServer_Response_Data_IgnoreHeaderAfterWrite_After(t *testing.T) { - synctestTest(t, testServer_Response_Data_IgnoreHeaderAfterWrite_After) + synctest.Test(t, testServer_Response_Data_IgnoreHeaderAfterWrite_After) } -func testServer_Response_Data_IgnoreHeaderAfterWrite_After(t testing.TB) { +func testServer_Response_Data_IgnoreHeaderAfterWrite_After(t *testing.T) { const msg = "this is HTML." testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { io.WriteString(w, msg) @@ -2094,9 +2094,9 @@ } // Header accessed before the initial write and later mutated. func TestServer_Response_Data_IgnoreHeaderAfterWrite_Overwrite(t *testing.T) { - synctestTest(t, testServer_Response_Data_IgnoreHeaderAfterWrite_Overwrite) + synctest.Test(t, testServer_Response_Data_IgnoreHeaderAfterWrite_Overwrite) } -func testServer_Response_Data_IgnoreHeaderAfterWrite_Overwrite(t testing.TB) { +func testServer_Response_Data_IgnoreHeaderAfterWrite_Overwrite(t *testing.T) { const msg = "this is HTML." testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { w.Header().Set("foo", "proper value") @@ -2119,9 +2119,9 @@ }) } func TestServer_Response_Data_SniffLenType(t *testing.T) { - synctestTest(t, testServer_Response_Data_SniffLenType) + synctest.Test(t, testServer_Response_Data_SniffLenType) } -func testServer_Response_Data_SniffLenType(t testing.TB) { +func testServer_Response_Data_SniffLenType(t *testing.T) { const msg = "this is HTML." testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { io.WriteString(w, msg) @@ -2146,9 +2146,9 @@ }) } func TestServer_Response_Header_Flush_MidWrite(t *testing.T) { - synctestTest(t, testServer_Response_Header_Flush_MidWrite) + synctest.Test(t, testServer_Response_Header_Flush_MidWrite) } -func testServer_Response_Header_Flush_MidWrite(t testing.TB) { +func testServer_Response_Header_Flush_MidWrite(t *testing.T) { const msg = "this is HTML" const msg2 = ", and this is the next chunk" testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { @@ -2180,8 +2180,8 @@ }) }) } -func TestServer_Response_LargeWrite(t *testing.T) { synctestTest(t, testServer_Response_LargeWrite) } -func testServer_Response_LargeWrite(t testing.TB) { +func TestServer_Response_LargeWrite(t *testing.T) { synctest.Test(t, testServer_Response_LargeWrite) } +func testServer_Response_LargeWrite(t *testing.T) { const size = 1 << 20 const maxFrameSize = 16 << 10 testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { @@ -2247,9 +2247,9 @@ } // Test that the handler can't write more than the client allows func TestServer_Response_LargeWrite_FlowControlled(t *testing.T) { - synctestTest(t, testServer_Response_LargeWrite_FlowControlled) + synctest.Test(t, testServer_Response_LargeWrite_FlowControlled) } -func testServer_Response_LargeWrite_FlowControlled(t testing.TB) { +func testServer_Response_LargeWrite_FlowControlled(t *testing.T) { // Make these reads. Before each read, the client adds exactly enough // flow-control to satisfy the read. Numbers chosen arbitrarily. reads := []int{123, 1, 13, 127} @@ -2304,9 +2304,9 @@ } // Test that the handler blocked in a Write is unblocked if the server sends a RST_STREAM. func TestServer_Response_RST_Unblocks_LargeWrite(t *testing.T) { - synctestTest(t, testServer_Response_RST_Unblocks_LargeWrite) + synctest.Test(t, testServer_Response_RST_Unblocks_LargeWrite) } -func testServer_Response_RST_Unblocks_LargeWrite(t testing.TB) { +func testServer_Response_RST_Unblocks_LargeWrite(t *testing.T) { const size = 1 << 20 const maxFrameSize = 16 << 10 testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { @@ -2339,9 +2339,9 @@ }) } func TestServer_Response_Empty_Data_Not_FlowControlled(t *testing.T) { - synctestTest(t, testServer_Response_Empty_Data_Not_FlowControlled) + synctest.Test(t, testServer_Response_Empty_Data_Not_FlowControlled) } -func testServer_Response_Empty_Data_Not_FlowControlled(t testing.TB) { +func testServer_Response_Empty_Data_Not_FlowControlled(t *testing.T) { testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { w.(http.Flusher).Flush() // Nothing; send empty DATA @@ -2369,9 +2369,9 @@ }) } func TestServer_Response_Automatic100Continue(t *testing.T) { - synctestTest(t, testServer_Response_Automatic100Continue) + synctest.Test(t, testServer_Response_Automatic100Continue) } -func testServer_Response_Automatic100Continue(t testing.TB) { +func testServer_Response_Automatic100Continue(t *testing.T) { const msg = "foo" const reply = "bar" testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { @@ -2423,9 +2423,9 @@ }) } func TestServer_HandlerWriteErrorOnDisconnect(t *testing.T) { - synctestTest(t, testServer_HandlerWriteErrorOnDisconnect) + synctest.Test(t, testServer_HandlerWriteErrorOnDisconnect) } -func testServer_HandlerWriteErrorOnDisconnect(t testing.TB) { +func testServer_HandlerWriteErrorOnDisconnect(t *testing.T) { errc := make(chan error, 1) testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { p := []byte("some data.\n") @@ -2454,9 +2454,9 @@ }) } func TestServer_Rejects_Too_Many_Streams(t *testing.T) { - synctestTest(t, testServer_Rejects_Too_Many_Streams) + synctest.Test(t, testServer_Rejects_Too_Many_Streams) } -func testServer_Rejects_Too_Many_Streams(t testing.TB) { +func testServer_Rejects_Too_Many_Streams(t *testing.T) { st := newServerTester(t, nil) st.greet() nextStreamID := uint32(1) @@ -2517,9 +2517,9 @@ } // So many response headers that the server needs to use CONTINUATION frames: func TestServer_Response_ManyHeaders_With_Continuation(t *testing.T) { - synctestTest(t, testServer_Response_ManyHeaders_With_Continuation) + synctest.Test(t, testServer_Response_ManyHeaders_With_Continuation) } -func testServer_Response_ManyHeaders_With_Continuation(t testing.TB) { +func testServer_Response_ManyHeaders_With_Continuation(t *testing.T) { testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { h := w.Header() for i := range 5000 { @@ -2554,9 +2554,9 @@ // invariant check later when we tried to remove that stream (via // defer sc.closeAllStreamsOnConnClose) when the serverConn serve loop // ended. func TestServer_NoCrash_HandlerClose_Then_ClientClose(t *testing.T) { - synctestTest(t, testServer_NoCrash_HandlerClose_Then_ClientClose) + synctest.Test(t, testServer_NoCrash_HandlerClose_Then_ClientClose) } -func testServer_NoCrash_HandlerClose_Then_ClientClose(t testing.TB) { +func testServer_NoCrash_HandlerClose_Then_ClientClose(t *testing.T) { testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { // nothing return nil @@ -2624,7 +2624,7 @@ func TestServer_Rejects_TLS10(t *testing.T) { testRejectTLS(t, tls.VersionTLS10) } func TestServer_Rejects_TLS11(t *testing.T) { testRejectTLS(t, tls.VersionTLS11) } func testRejectTLS(t *testing.T, version uint16) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { st := newServerTester(t, nil, func(state *tls.ConnectionState) { // As of 1.18 the default minimum Go TLS version is // 1.2. In order to test rejection of lower versions, @@ -2636,8 +2636,8 @@ st.wantGoAway(0, ErrCodeInadequateSecurity) }) } -func TestServer_Rejects_TLSBadCipher(t *testing.T) { synctestTest(t, testServer_Rejects_TLSBadCipher) } -func testServer_Rejects_TLSBadCipher(t testing.TB) { +func TestServer_Rejects_TLSBadCipher(t *testing.T) { synctest.Test(t, testServer_Rejects_TLSBadCipher) } +func testServer_Rejects_TLSBadCipher(t *testing.T) { st := newServerTester(t, nil, func(state *tls.ConnectionState) { state.Version = tls.VersionTLS12 state.CipherSuite = tls.TLS_RSA_WITH_RC4_128_SHA @@ -2647,9 +2647,9 @@ st.wantGoAway(0, ErrCodeInadequateSecurity) } func TestServer_Advertises_Common_Cipher(t *testing.T) { - synctestTest(t, testServer_Advertises_Common_Cipher) + synctest.Test(t, testServer_Advertises_Common_Cipher) } -func testServer_Advertises_Common_Cipher(t testing.TB) { +func testServer_Advertises_Common_Cipher(t *testing.T) { ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { }, func(srv *http.Server) { // Have the server configured with no specific cipher suites. @@ -2681,7 +2681,7 @@ } // testServerResponse sets up an idle HTTP/2 connection. The client function should // write a single request that must be handled by the handler. -func testServerResponse(t testing.TB, +func testServerResponse(t *testing.T, handler func(http.ResponseWriter, *http.Request) error, client func(*serverTester), ) { @@ -2710,7 +2710,7 @@ // readBodyHandler returns an http Handler func that reads len(want) // bytes from r.Body and fails t if the contents read were not // the value of want. -func readBodyHandler(t testing.TB, want string) func(w http.ResponseWriter, r *http.Request) { +func readBodyHandler(t *testing.T, want string) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { buf := make([]byte, len(want)) _, err := io.ReadFull(r.Body, buf) @@ -2725,9 +2725,9 @@ } } func TestServer_MaxDecoderHeaderTableSize(t *testing.T) { - synctestTest(t, testServer_MaxDecoderHeaderTableSize) + synctest.Test(t, testServer_MaxDecoderHeaderTableSize) } -func testServer_MaxDecoderHeaderTableSize(t testing.TB) { +func testServer_MaxDecoderHeaderTableSize(t *testing.T) { wantHeaderTableSize := uint32(InitialHeaderTableSize * 2) st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {}, func(h2 *http.HTTP2Config) { h2.MaxDecoderHeaderTableSize = int(wantHeaderTableSize) @@ -2751,9 +2751,9 @@ } } func TestServer_MaxEncoderHeaderTableSize(t *testing.T) { - synctestTest(t, testServer_MaxEncoderHeaderTableSize) + synctest.Test(t, testServer_MaxEncoderHeaderTableSize) } -func testServer_MaxEncoderHeaderTableSize(t testing.TB) { +func testServer_MaxEncoderHeaderTableSize(t *testing.T) { wantHeaderTableSize := uint32(InitialHeaderTableSize / 2) st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {}, func(h2 *http.HTTP2Config) { h2.MaxEncoderHeaderTableSize = int(wantHeaderTableSize) @@ -2768,8 +2768,8 @@ } } // Issue 12843 -func TestServerDoS_MaxHeaderListSize(t *testing.T) { synctestTest(t, testServerDoS_MaxHeaderListSize) } -func testServerDoS_MaxHeaderListSize(t testing.TB) { +func TestServerDoS_MaxHeaderListSize(t *testing.T) { synctest.Test(t, testServerDoS_MaxHeaderListSize) } +func testServerDoS_MaxHeaderListSize(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {}) defer st.Close() @@ -2839,9 +2839,9 @@ }) } func TestServer_Response_Stream_With_Missing_Trailer(t *testing.T) { - synctestTest(t, testServer_Response_Stream_With_Missing_Trailer) + synctest.Test(t, testServer_Response_Stream_With_Missing_Trailer) } -func testServer_Response_Stream_With_Missing_Trailer(t testing.TB) { +func testServer_Response_Stream_With_Missing_Trailer(t *testing.T) { testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { w.Header().Set("Trailer", "test-trailer") return nil @@ -2859,8 +2859,8 @@ }) }) } -func TestCompressionErrorOnWrite(t *testing.T) { synctestTest(t, testCompressionErrorOnWrite) } -func testCompressionErrorOnWrite(t testing.TB) { +func TestCompressionErrorOnWrite(t *testing.T) { synctest.Test(t, testCompressionErrorOnWrite) } +func testCompressionErrorOnWrite(t *testing.T) { const maxStrLen = 8 << 10 var serverConfig *http.Server st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { @@ -2922,8 +2922,8 @@ }) st.wantGoAway(3, ErrCodeCompression) } -func TestCompressionErrorOnClose(t *testing.T) { synctestTest(t, testCompressionErrorOnClose) } -func testCompressionErrorOnClose(t testing.TB) { +func TestCompressionErrorOnClose(t *testing.T) { synctest.Test(t, testCompressionErrorOnClose) } +func testCompressionErrorOnClose(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { // No response body. }) @@ -2943,8 +2943,8 @@ st.wantGoAway(1, ErrCodeCompression) } // test that a server handler can read trailers from a client -func TestServerReadsTrailers(t *testing.T) { synctestTest(t, testServerReadsTrailers) } -func testServerReadsTrailers(t testing.TB) { +func TestServerReadsTrailers(t *testing.T) { synctest.Test(t, testServerReadsTrailers) } +func testServerReadsTrailers(t *testing.T) { const testBody = "some test body" writeReq := func(st *serverTester) { st.writeHeaders(HeadersFrameParam{ @@ -2996,17 +2996,17 @@ } // test that a server handler can send trailers func TestServerWritesTrailers_WithFlush(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testServerWritesTrailers(t, true) }) } func TestServerWritesTrailers_WithoutFlush(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testServerWritesTrailers(t, false) }) } -func testServerWritesTrailers(t testing.TB, withFlush bool) { +func testServerWritesTrailers(t *testing.T, withFlush bool) { // See https://httpwg.github.io/specs/rfc7540.html#rfc.section.8.1.3 testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B") @@ -3074,9 +3074,9 @@ }) } func TestServerWritesUndeclaredTrailers(t *testing.T) { - synctestTest(t, testServerWritesUndeclaredTrailers) + synctest.Test(t, testServerWritesUndeclaredTrailers) } -func testServerWritesUndeclaredTrailers(t testing.TB) { +func testServerWritesUndeclaredTrailers(t *testing.T) { const trailer = "Trailer-Header" const value = "hi1" ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { @@ -3105,9 +3105,9 @@ // validate transmitted header field names & values // golang.org/issue/14048 func TestServerDoesntWriteInvalidHeaders(t *testing.T) { - synctestTest(t, testServerDoesntWriteInvalidHeaders) + synctest.Test(t, testServerDoesntWriteInvalidHeaders) } -func testServerDoesntWriteInvalidHeaders(t testing.TB) { +func testServerDoesntWriteInvalidHeaders(t *testing.T) { testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { w.Header().Add("OK1", "x") w.Header().Add("Bad:Colon", "x") // colon (non-token byte) in key @@ -3128,8 +3128,8 @@ }) }) } -func TestIssue53(t *testing.T) { synctestTest(t, testIssue53) } -func testIssue53(t testing.TB) { +func TestIssue53(t *testing.T) { synctest.Test(t, testIssue53) } +func testIssue53(t *testing.T) { const data = "PRI * HTTP/2.0\r\n\r\nSM" + "\r\n\r\n\x00\x00\x00\x01\ainfinfin\ad" st := newServerTester(t, func(w http.ResponseWriter, req *http.Request) { @@ -3225,9 +3225,9 @@ func (li errListener) Close() error { return nil } func (li errListener) Addr() net.Addr { return nil } func TestServerNoAutoContentLengthOnHead(t *testing.T) { - synctestTest(t, testServerNoAutoContentLengthOnHead) + synctest.Test(t, testServerNoAutoContentLengthOnHead) } -func testServerNoAutoContentLengthOnHead(t testing.TB) { +func testServerNoAutoContentLengthOnHead(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { // No response body. (or smaller than one frame) }) @@ -3250,9 +3250,9 @@ } // golang.org/issue/13495 func TestServerNoDuplicateContentType(t *testing.T) { - synctestTest(t, testServerNoDuplicateContentType) + synctest.Test(t, testServerNoDuplicateContentType) } -func testServerNoDuplicateContentType(t testing.TB) { +func testServerNoDuplicateContentType(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { w.Header()["Content-Type"] = []string{""} fmt.Fprintf(w, "hi") @@ -3277,9 +3277,9 @@ }) } func TestServerContentLengthCanBeDisabled(t *testing.T) { - synctestTest(t, testServerContentLengthCanBeDisabled) + synctest.Test(t, testServerContentLengthCanBeDisabled) } -func testServerContentLengthCanBeDisabled(t testing.TB) { +func testServerContentLengthCanBeDisabled(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { w.Header()["Content-Length"] = nil fmt.Fprintf(w, "OK") @@ -3303,8 +3303,8 @@ }) } // golang.org/issue/14214 -func TestServer_Rejects_ConnHeaders(t *testing.T) { synctestTest(t, testServer_Rejects_ConnHeaders) } -func testServer_Rejects_ConnHeaders(t testing.TB) { +func TestServer_Rejects_ConnHeaders(t *testing.T) { synctest.Test(t, testServer_Rejects_ConnHeaders) } +func testServer_Rejects_ConnHeaders(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { t.Error("should not get to Handler") }) @@ -3328,7 +3328,7 @@ enc *hpack.Encoder buf bytes.Buffer } -func (he *hpackEncoder) encodeHeaderRaw(t testing.TB, headers ...string) []byte { +func (he *hpackEncoder) encodeHeaderRaw(t *testing.T, headers ...string) []byte { if len(headers)%2 == 1 { panic("odd number of kv args") } @@ -3349,9 +3349,9 @@ } // golang.org/issue/14030 func TestExpect100ContinueAfterHandlerWrites(t *testing.T) { - synctestTest(t, testExpect100ContinueAfterHandlerWrites) + synctest.Test(t, testExpect100ContinueAfterHandlerWrites) } -func testExpect100ContinueAfterHandlerWrites(t testing.TB) { +func testExpect100ContinueAfterHandlerWrites(t *testing.T) { const msg = "Hello" const msg2 = "World" @@ -3432,7 +3432,7 @@ r.Body.Close() }, }, } { - synctestSubtest(t, tt.name, func(t testing.TB) { + synctestSubtest(t, tt.name, func(t *testing.T) { unblock := make(chan bool, 1) defer close(unblock) @@ -3475,9 +3475,9 @@ } } func TestServerReturnsStreamAndConnFlowControlOnBodyClose(t *testing.T) { - synctestTest(t, testServerReturnsStreamAndConnFlowControlOnBodyClose) + synctest.Test(t, testServerReturnsStreamAndConnFlowControlOnBodyClose) } -func testServerReturnsStreamAndConnFlowControlOnBodyClose(t testing.TB) { +func testServerReturnsStreamAndConnFlowControlOnBodyClose(t *testing.T) { unblockHandler := make(chan struct{}) defer close(unblockHandler) @@ -3509,8 +3509,8 @@ endStream: true, }) } -func TestServerIdleTimeout(t *testing.T) { synctestTest(t, testServerIdleTimeout) } -func testServerIdleTimeout(t testing.TB) { +func TestServerIdleTimeout(t *testing.T) { synctest.Test(t, testServerIdleTimeout) } +func testServerIdleTimeout(t *testing.T) { if testing.Short() { t.Skip("skipping in short mode") } @@ -3527,9 +3527,9 @@ st.wantGoAway(0, ErrCodeNo) } func TestServerIdleTimeout_AfterRequest(t *testing.T) { - synctestTest(t, testServerIdleTimeout_AfterRequest) + synctest.Test(t, testServerIdleTimeout_AfterRequest) } -func testServerIdleTimeout_AfterRequest(t testing.TB) { +func testServerIdleTimeout_AfterRequest(t *testing.T) { if testing.Short() { t.Skip("skipping in short mode") } @@ -3566,8 +3566,8 @@ // grpc-go closes the Request.Body currently with a Read. // Verify that it doesn't race. // See https://github.com/grpc/grpc-go/pull/938 -func TestRequestBodyReadCloseRace(t *testing.T) { synctestTest(t, testRequestBodyReadCloseRace) } -func testRequestBodyReadCloseRace(t testing.TB) { +func TestRequestBodyReadCloseRace(t *testing.T) { synctest.Test(t, testRequestBodyReadCloseRace) } +func testRequestBodyReadCloseRace(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { go r.Body.Close() io.Copy(io.Discard, r.Body) @@ -3604,8 +3604,8 @@ } } } -func TestIssue20704Race(t *testing.T) { synctestTest(t, testIssue20704Race) } -func testIssue20704Race(t testing.TB) { +func TestIssue20704Race(t *testing.T) { synctest.Test(t, testIssue20704Race) } +func testIssue20704Race(t *testing.T) { if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" { t.Skip("skipping in short mode") } @@ -3641,8 +3641,8 @@ resp.Body.Close() } } -func TestServer_Rejects_TooSmall(t *testing.T) { synctestTest(t, testServer_Rejects_TooSmall) } -func testServer_Rejects_TooSmall(t testing.TB) { +func TestServer_Rejects_TooSmall(t *testing.T) { synctest.Test(t, testServer_Rejects_TooSmall) } +func testServer_Rejects_TooSmall(t *testing.T) { testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { io.ReadAll(r.Body) return nil @@ -3665,9 +3665,9 @@ // Tests that a handler setting "Connection: close" results in a GOAWAY being sent, // and the connection still completing. func TestServerHandlerConnectionClose(t *testing.T) { - synctestTest(t, testServerHandlerConnectionClose) + synctest.Test(t, testServerHandlerConnectionClose) } -func testServerHandlerConnectionClose(t testing.TB) { +func testServerHandlerConnectionClose(t *testing.T) { unblockHandler := make(chan bool, 1) testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { w.Header().Set("Connection", "close") @@ -3761,9 +3761,9 @@ }) } func TestServer_Headers_HalfCloseRemote(t *testing.T) { - synctestTest(t, testServer_Headers_HalfCloseRemote) + synctest.Test(t, testServer_Headers_HalfCloseRemote) } -func testServer_Headers_HalfCloseRemote(t testing.TB) { +func testServer_Headers_HalfCloseRemote(t *testing.T) { var st *serverTester writeData := make(chan bool) writeHeaders := make(chan bool) @@ -3811,8 +3811,8 @@ st.wantRSTStream(1, ErrCodeStreamClosed) } -func TestServerGracefulShutdown(t *testing.T) { synctestTest(t, testServerGracefulShutdown) } -func testServerGracefulShutdown(t testing.TB) { +func TestServerGracefulShutdown(t *testing.T) { synctest.Test(t, testServerGracefulShutdown) } +func testServerGracefulShutdown(t *testing.T) { handlerDone := make(chan struct{}) st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { <-handlerDone @@ -3917,7 +3917,7 @@ }, } for _, tt := range resps { - synctestSubtest(t, tt.name, func(t testing.TB) { + synctestSubtest(t, tt.name, func(t *testing.T) { ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { if tt.contentEncoding != nil { w.Header().Set("Content-Encoding", tt.contentEncoding.(string)) @@ -3959,9 +3959,9 @@ } } func TestServerWindowUpdateOnBodyClose(t *testing.T) { - synctestTest(t, testServerWindowUpdateOnBodyClose) + synctest.Test(t, testServerWindowUpdateOnBodyClose) } -func testServerWindowUpdateOnBodyClose(t testing.TB) { +func testServerWindowUpdateOnBodyClose(t *testing.T) { const windowSize = 65535 * 2 content := make([]byte, windowSize) errc := make(chan error) @@ -4020,9 +4020,9 @@ st.wantWindowUpdate(0, windowSize/2) } func TestNoErrorLoggedOnPostAfterGOAWAY(t *testing.T) { - synctestTest(t, testNoErrorLoggedOnPostAfterGOAWAY) + synctest.Test(t, testNoErrorLoggedOnPostAfterGOAWAY) } -func testNoErrorLoggedOnPostAfterGOAWAY(t testing.TB) { +func testNoErrorLoggedOnPostAfterGOAWAY(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {}) defer st.Close() @@ -4055,8 +4055,8 @@ t.Error("got protocol error") } } -func TestServerSendsProcessing(t *testing.T) { synctestTest(t, testServerSendsProcessing) } -func testServerSendsProcessing(t testing.TB) { +func TestServerSendsProcessing(t *testing.T) { synctest.Test(t, testServerSendsProcessing) } +func testServerSendsProcessing(t *testing.T) { testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { w.WriteHeader(http.StatusProcessing) w.Write([]byte("stuff")) @@ -4083,8 +4083,8 @@ }) }) } -func TestServerSendsEarlyHints(t *testing.T) { synctestTest(t, testServerSendsEarlyHints) } -func testServerSendsEarlyHints(t testing.TB) { +func TestServerSendsEarlyHints(t *testing.T) { synctest.Test(t, testServerSendsEarlyHints) } +func testServerSendsEarlyHints(t *testing.T) { testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { h := w.Header() h.Add("Content-Length", "123") @@ -4140,8 +4140,8 @@ }) }) } -func TestProtocolErrorAfterGoAway(t *testing.T) { synctestTest(t, testProtocolErrorAfterGoAway) } -func testProtocolErrorAfterGoAway(t testing.TB) { +func TestProtocolErrorAfterGoAway(t *testing.T) { synctest.Test(t, testProtocolErrorAfterGoAway) } +func testProtocolErrorAfterGoAway(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { io.Copy(io.Discard, r.Body) }) @@ -4186,7 +4186,7 @@ // below half of the maximum. Perhaps it would be nice to // test this case, but we currently do not. 65535 * 2, } { - synctestSubtest(t, fmt.Sprint(want), func(t testing.TB) { + synctestSubtest(t, fmt.Sprint(want), func(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { }, func(h2 *http.HTTP2Config) { @@ -4233,9 +4233,9 @@ // // Terminating the request stream on the client causes Write to return. // We should not access the slice after this point. func TestServerWriteDoesNotRetainBufferAfterReturn(t *testing.T) { - synctestTest(t, testServerWriteDoesNotRetainBufferAfterReturn) + synctest.Test(t, testServerWriteDoesNotRetainBufferAfterReturn) } -func testServerWriteDoesNotRetainBufferAfterReturn(t testing.TB) { +func testServerWriteDoesNotRetainBufferAfterReturn(t *testing.T) { donec := make(chan struct{}) ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { defer close(donec) @@ -4274,9 +4274,9 @@ // // Shutting down the Server causes Write to return. // We should not access the slice after this point. func TestServerWriteDoesNotRetainBufferAfterServerClose(t *testing.T) { - synctestTest(t, testServerWriteDoesNotRetainBufferAfterServerClose) + synctest.Test(t, testServerWriteDoesNotRetainBufferAfterServerClose) } -func testServerWriteDoesNotRetainBufferAfterServerClose(t testing.TB) { +func testServerWriteDoesNotRetainBufferAfterServerClose(t *testing.T) { donec := make(chan struct{}, 1) ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { donec <- struct{}{} @@ -4312,8 +4312,8 @@ ts.Config.Close() <-donec } -func TestServerMaxHandlerGoroutines(t *testing.T) { synctestTest(t, testServerMaxHandlerGoroutines) } -func testServerMaxHandlerGoroutines(t testing.TB) { +func TestServerMaxHandlerGoroutines(t *testing.T) { synctest.Test(t, testServerMaxHandlerGoroutines) } +func testServerMaxHandlerGoroutines(t *testing.T) { const maxHandlers = 10 handlerc := make(chan chan bool) donec := make(chan struct{}) @@ -4413,8 +4413,8 @@ close(s) } } -func TestServerContinuationFlood(t *testing.T) { synctestTest(t, testServerContinuationFlood) } -func testServerContinuationFlood(t testing.TB) { +func TestServerContinuationFlood(t *testing.T) { synctest.Test(t, testServerContinuationFlood) } +func testServerContinuationFlood(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { fmt.Println(r.Header) }, func(s *http.Server) { @@ -4467,9 +4467,9 @@ // is that the connection is closed, don't check for the GOAWAY having been sent. } func TestServerContinuationAfterInvalidHeader(t *testing.T) { - synctestTest(t, testServerContinuationAfterInvalidHeader) + synctest.Test(t, testServerContinuationAfterInvalidHeader) } -func testServerContinuationAfterInvalidHeader(t testing.TB) { +func testServerContinuationAfterInvalidHeader(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { fmt.Println(r.Header) }) @@ -4508,8 +4508,8 @@ } } // Issue 67036: A stream error should result in the handler's request context being canceled. -func TestServerRequestCancelOnError(t *testing.T) { synctestTest(t, testServerRequestCancelOnError) } -func testServerRequestCancelOnError(t testing.TB) { +func TestServerRequestCancelOnError(t *testing.T) { synctest.Test(t, testServerRequestCancelOnError) } +func testServerRequestCancelOnError(t *testing.T) { recvc := make(chan struct{}) // handler has started donec := make(chan struct{}) // handler has finished st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { @@ -4543,9 +4543,9 @@ <-donec } func TestServerSetReadWriteDeadlineRace(t *testing.T) { - synctestTest(t, testServerSetReadWriteDeadlineRace) + synctest.Test(t, testServerSetReadWriteDeadlineRace) } -func testServerSetReadWriteDeadlineRace(t testing.TB) { +func testServerSetReadWriteDeadlineRace(t *testing.T) { ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { ctl := http.NewResponseController(w) ctl.SetReadDeadline(time.Now().Add(3600 * time.Second)) @@ -4558,8 +4558,8 @@ } resp.Body.Close() } -func TestServerWriteByteTimeout(t *testing.T) { synctestTest(t, testServerWriteByteTimeout) } -func testServerWriteByteTimeout(t testing.TB) { +func TestServerWriteByteTimeout(t *testing.T) { synctest.Test(t, testServerWriteByteTimeout) } +func testServerWriteByteTimeout(t *testing.T) { const timeout = 1 * time.Second st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { w.Write(make([]byte, 100)) @@ -4596,8 +4596,8 @@ st.advance(1 * time.Second) // timeout after failing to write any more bytes st.wantClosed() } -func TestServerPingSent(t *testing.T) { synctestTest(t, testServerPingSent) } -func testServerPingSent(t testing.TB) { +func TestServerPingSent(t *testing.T) { synctest.Test(t, testServerPingSent) } +func testServerPingSent(t *testing.T) { const sendPingTimeout = 15 * time.Second st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { }, func(h2 *http.HTTP2Config) { @@ -4617,8 +4617,8 @@ st.advance(1 * time.Second) st.wantClosed() } -func TestServerPingResponded(t *testing.T) { synctestTest(t, testServerPingResponded) } -func testServerPingResponded(t testing.TB) { +func TestServerPingResponded(t *testing.T) { synctest.Test(t, testServerPingResponded) } +func testServerPingResponded(t *testing.T) { const sendPingTimeout = 15 * time.Second st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { }, func(h2 *http.HTTP2Config) { @@ -4646,9 +4646,9 @@ // body doesn't terminate the stream to the peer. (It just stops // readability from the handler's side, and eventually the client // runs out of flow control tokens) func TestServerSendDataAfterRequestBodyClose(t *testing.T) { - synctestTest(t, testServerSendDataAfterRequestBodyClose) + synctest.Test(t, testServerSendDataAfterRequestBodyClose) } -func testServerSendDataAfterRequestBodyClose(t testing.TB) { +func testServerSendDataAfterRequestBodyClose(t *testing.T) { st := newServerTester(t, nil) st.greet() @@ -4699,9 +4699,9 @@ st.wantIdle() } func TestServerSettingNoRFC7540Priorities(t *testing.T) { - synctestTest(t, testServerSettingNoRFC7540Priorities) + synctest.Test(t, testServerSettingNoRFC7540Priorities) } -func testServerSettingNoRFC7540Priorities(t testing.TB) { +func testServerSettingNoRFC7540Priorities(t *testing.T) { const wantNoRFC7540Setting = true st := newServerTester(t, nil) defer st.Close() @@ -4720,9 +4720,9 @@ } } func TestServerSettingNoRFC7540PrioritiesInvalid(t *testing.T) { - synctestTest(t, testServerSettingNoRFC7540PrioritiesInvalid) + synctest.Test(t, testServerSettingNoRFC7540PrioritiesInvalid) } -func testServerSettingNoRFC7540PrioritiesInvalid(t testing.TB) { +func testServerSettingNoRFC7540PrioritiesInvalid(t *testing.T) { st := newServerTester(t, nil) defer st.Close() @@ -4737,9 +4737,9 @@ // This test documents current behavior, rather than ideal behavior that we // would necessarily like to see. Refer to go.dev/issues/75936 for details. func TestServerRFC9218PrioritySmallPayload(t *testing.T) { - synctestTest(t, testServerRFC9218PrioritySmallPayload) + synctest.Test(t, testServerRFC9218PrioritySmallPayload) } -func testServerRFC9218PrioritySmallPayload(t testing.TB) { +func testServerRFC9218PrioritySmallPayload(t *testing.T) { endTest := false st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { for !endTest { @@ -4800,9 +4800,9 @@ } } func TestServerRFC9218Priority(t *testing.T) { - synctestTest(t, testServerRFC9218Priority) + synctest.Test(t, testServerRFC9218Priority) } -func testServerRFC9218Priority(t testing.TB) { +func testServerRFC9218Priority(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { w.Write(slices.Repeat([]byte("a"), 16<<20)) if f, ok := w.(http.Flusher); ok { @@ -4855,9 +4855,9 @@ } } func TestServerRFC9218PriorityIgnoredWhenProxied(t *testing.T) { - synctestTest(t, testServerRFC9218PriorityIgnoredWhenProxied) + synctest.Test(t, testServerRFC9218PriorityIgnoredWhenProxied) } -func testServerRFC9218PriorityIgnoredWhenProxied(t testing.TB) { +func testServerRFC9218PriorityIgnoredWhenProxied(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { w.Write(slices.Repeat([]byte("a"), 16<<20)) if f, ok := w.(http.Flusher); ok { @@ -4904,9 +4904,9 @@ } } func TestServerRFC9218PriorityAware(t *testing.T) { - synctestTest(t, testServerRFC9218PriorityAware) + synctest.Test(t, testServerRFC9218PriorityAware) } -func testServerRFC9218PriorityAware(t testing.TB) { +func testServerRFC9218PriorityAware(t *testing.T) { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { w.Write(slices.Repeat([]byte("a"), 16<<20)) if f, ok := w.(http.Flusher); ok { @@ -4980,9 +4980,9 @@ } } func TestServerInvalidPathHeader(t *testing.T) { - synctestTest(t, testServerInvalidPathHeader) + synctest.Test(t, testServerInvalidPathHeader) } -func testServerInvalidPathHeader(t testing.TB) { +func testServerInvalidPathHeader(t *testing.T) { for _, path := range []string{ "", "\x00", @@ -5003,9 +5003,9 @@ } } func TestServerPathInitialSlashes(t *testing.T) { - synctestTest(t, testServerPathInitialSlashes) + synctest.Test(t, testServerPathInitialSlashes) } -func testServerPathInitialSlashes(t testing.TB) { +func testServerPathInitialSlashes(t *testing.T) { st := newServerTester(t, nil) st.greet() @@ -5028,6 +5028,65 @@ } if got, want := call.req.URL.Path, path; got != want { t.Errorf("got req.URL.Path %q, want %q", got, want) } +} + +// "An endpoint MUST treat a change to SETTINGS_INITIAL_WINDOW_SIZE +// that causes any flow-control window to exceed the maximum size as +// a connection error (Section 5.4.1) of type FLOW_CONTROL_ERROR." +// -- https://www.rfc-editor.org/rfc/rfc9113.html#section-6.9.2-7 +func TestServerSettingsFlowControlUpdateBeyondLimit(t *testing.T) { + synctest.Test(t, testServerSettingsFlowControlUpdateBeyondLimit) +} +func testServerSettingsFlowControlUpdateBeyondLimit(t *testing.T) { + st := newServerTester(t, nil) + st.greet() + + st.writeHeaders(HeadersFrameParam{ + StreamID: 1, // clients send odd numbers + BlockFragment: st.encodeHeader(":method", "POST"), + EndStream: false, // data coming + EndHeaders: true, + }) + + // Give this stream some additional flow control. + const windowIncrease = 1000 + st.writeWindowUpdate(1, windowIncrease) + st.wantIdle() + + // Adjust the initial flow control window. The stream is now over the limit. + const maxWindowSize = (1 << 31) - 1 // RFC 9113, 6.9.1 + const maxInitialWindowSize = maxWindowSize - windowIncrease + st.writeSettings(Setting{SettingInitialWindowSize, maxInitialWindowSize + 1}) + st.wantGoAway(1, ErrCodeFlowControl) +} + +// Counterpart to TestServerSettingsFlowControlUpdateBeyondLimit: +// A SETTINGS update which doesn't quite put a stream over the flow control limit. +func TestServerSettingsFlowControlUpdateWithinLimit(t *testing.T) { + synctest.Test(t, testServerSettingsFlowControlUpdateWithinLimit) +} +func testServerSettingsFlowControlUpdateWithinLimit(t *testing.T) { + st := newServerTester(t, nil) + st.greet() + + st.writeHeaders(HeadersFrameParam{ + StreamID: 1, // clients send odd numbers + BlockFragment: st.encodeHeader(":method", "POST"), + EndStream: false, // data coming + EndHeaders: true, + }) + + // Give this stream some additional flow control. + const windowIncrease = 1000 + st.writeWindowUpdate(1, windowIncrease) + st.wantIdle() + + // Adjust the initial flow control window. The stream is just within the limit. + const maxWindowSize = (1 << 31) - 1 // RFC 9113, 6.9.1 + const maxInitialWindowSize = maxWindowSize - windowIncrease + st.writeSettings(Setting{SettingInitialWindowSize, maxInitialWindowSize}) + st.wantSettingsAck() + st.wantIdle() } func TestConsistentConstants(t *testing.T) { diff --git a/src/net/http/internal/http2/synctest_test.go b/src/net/http/internal/http2/synctest_test.go index fec7ad6512dca5fb9c17e6c0322a9ba8f88971e5..f98ce7a02eb6d09d783f935ffa7157af1d5c4fb8 100644 --- a/src/net/http/internal/http2/synctest_test.go +++ b/src/net/http/internal/http2/synctest_test.go @@ -9,18 +9,10 @@ "testing" "testing/synctest" ) -func synctestTest(t *testing.T, f func(t testing.TB)) { - t.Helper() - synctest.Test(t, func(t *testing.T) { - t.Helper() - f(t) - }) -} - // synctestSubtest starts a subtest and runs f in a synctest bubble within it. -func synctestSubtest(t *testing.T, name string, f func(testing.TB)) { +func synctestSubtest(t *testing.T, name string, f func(*testing.T)) { t.Helper() t.Run(name, func(t *testing.T) { - synctestTest(t, f) + synctest.Test(t, f) }) } diff --git a/src/net/http/internal/http2/transport.go b/src/net/http/internal/http2/transport.go index d98e0f8ccae7673a9fee5b0e4d4458e040dc9cfe..beab55f706757ba8ef24d8b3515d331e4c98a6fc 100644 --- a/src/net/http/internal/http2/transport.go +++ b/src/net/http/internal/http2/transport.go @@ -2700,20 +2700,14 @@ seenMaxConcurrentStreams = true case SettingMaxHeaderListSize: cc.peerMaxHeaderListSize = uint64(s.Val) case SettingInitialWindowSize: - // Values above the maximum flow-control - // window size of 2^31-1 MUST be treated as a - // connection error (Section 5.4.1) of type - // FLOW_CONTROL_ERROR. - if s.Val > math.MaxInt32 { - return ConnectionError(ErrCodeFlowControl) - } - // Adjust flow control of currently-open // frames by the difference of the old initial // window size and this one. delta := int32(s.Val) - int32(cc.initialWindowSize) for _, cs := range cc.streams { - cs.flow.add(delta) + if !cs.flow.add(delta) { + return ConnectionError(ErrCodeFlowControl) + } } cc.cond.Broadcast() diff --git a/src/net/http/internal/http2/transport_test.go b/src/net/http/internal/http2/transport_test.go index 4863054a019ad997fd43afc82aaf972766381c00..3f45206b2f288e0c8c7194d4b370c004fa2b8166 100644 --- a/src/net/http/internal/http2/transport_test.go +++ b/src/net/http/internal/http2/transport_test.go @@ -118,7 +118,7 @@ tr1.IdleConnTimeout = 2 * time.Second }), wantNewConn: false, }} { - synctestSubtest(t, test.name, func(t testing.TB) { + synctestSubtest(t, test.name, func(t *testing.T) { tt := newTestTransport(t, func(tr *http.Transport) { tr.IdleConnTimeout = test.idleConnTimeout }) @@ -661,17 +661,17 @@ return ln } func TestTransportReqBodyAfterResponse_200(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportReqBodyAfterResponse(t, 200) }) } func TestTransportReqBodyAfterResponse_403(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportReqBodyAfterResponse(t, 403) }) } -func testTransportReqBodyAfterResponse(t testing.TB, status int) { +func testTransportReqBodyAfterResponse(t *testing.T, status int) { const bodySize = 1 << 10 tc := newTestClientConn(t) @@ -900,11 +900,11 @@ func TestTransportResPattern_c2h2d1t1(t *testing.T) { testTransportResPattern(t, f2, f2, d1, f1) } func TestTransportResPattern_c2h2d1t2(t *testing.T) { testTransportResPattern(t, f2, f2, d1, f2) } func testTransportResPattern(t *testing.T, expect100Continue, resHeader headerType, withData bool, trailers headerType) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportResPatternBubble(t, expect100Continue, resHeader, withData, trailers) }) } -func testTransportResPatternBubble(t testing.TB, expect100Continue, resHeader headerType, withData bool, trailers headerType) { +func testTransportResPatternBubble(t *testing.T, expect100Continue, resHeader headerType, withData bool, trailers headerType) { const reqBody = "some request body" const resBody = "some response body" @@ -985,8 +985,8 @@ } } // Issue 26189, Issue 17739: ignore unknown 1xx responses -func TestTransportUnknown1xx(t *testing.T) { synctestTest(t, testTransportUnknown1xx) } -func testTransportUnknown1xx(t testing.TB) { +func TestTransportUnknown1xx(t *testing.T) { synctest.Test(t, testTransportUnknown1xx) } +func testTransportUnknown1xx(t *testing.T) { var buf bytes.Buffer SetTestHookGot1xx(t, func(code int, header textproto.MIMEHeader) error { fmt.Fprintf(&buf, "code=%d header=%v\n", code, header) @@ -1035,9 +1035,9 @@ } } func TestTransportReceiveUndeclaredTrailer(t *testing.T) { - synctestTest(t, testTransportReceiveUndeclaredTrailer) + synctest.Test(t, testTransportReceiveUndeclaredTrailer) } -func testTransportReceiveUndeclaredTrailer(t testing.TB) { +func testTransportReceiveUndeclaredTrailer(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -1105,11 +1105,11 @@ ) } func testInvalidTrailer(t *testing.T, mode headerType, wantErr error, trailers ...string) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testInvalidTrailerBubble(t, mode, wantErr, trailers...) }) } -func testInvalidTrailerBubble(t testing.TB, mode headerType, wantErr error, trailers ...string) { +func testInvalidTrailerBubble(t *testing.T, mode headerType, wantErr error, trailers ...string) { tc := newTestClientConn(t) tc.greet() @@ -1164,7 +1164,7 @@ // call t.Fatal if asked to do so. PadHeaders first reserves enough // space for an empty "Pad-Headers" key, then adds as many copies of // filler as possible. Any remaining bytes necessary to push the // header list size up to limit are added to h["Pad-Headers"]. -func padHeaders(t testing.TB, h http.Header, limit uint64, filler string) { +func padHeaders(t *testing.T, h http.Header, limit uint64, filler string) { if limit > 0xffffffff { t.Fatalf("padHeaders: refusing to pad to more than 2^32-1 bytes. limit = %v", limit) } @@ -1257,9 +1257,9 @@ } } func TestTransportChecksRequestHeaderListSize(t *testing.T) { - synctestTest(t, testTransportChecksRequestHeaderListSize) + synctest.Test(t, testTransportChecksRequestHeaderListSize) } -func testTransportChecksRequestHeaderListSize(t testing.TB) { +func testTransportChecksRequestHeaderListSize(t *testing.T) { const peerSize = 16 << 10 tc := newTestClientConn(t) @@ -1360,9 +1360,9 @@ checkRoundTrip(req, ErrRequestHeaderListSize, "Single large trailer") } func TestTransportChecksResponseHeaderListSize(t *testing.T) { - synctestTest(t, testTransportChecksResponseHeaderListSize) + synctest.Test(t, testTransportChecksResponseHeaderListSize) } -func testTransportChecksResponseHeaderListSize(t testing.TB) { +func testTransportChecksResponseHeaderListSize(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -1408,8 +1408,8 @@ t.Fatalf("RoundTrip Error = %v (and %d bytes of response headers); want errResponseHeaderListSize", err, size) } } -func TestTransportCookieHeaderSplit(t *testing.T) { synctestTest(t, testTransportCookieHeaderSplit) } -func testTransportCookieHeaderSplit(t testing.TB) { +func TestTransportCookieHeaderSplit(t *testing.T) { synctest.Test(t, testTransportCookieHeaderSplit) } +func testTransportCookieHeaderSplit(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -1626,17 +1626,17 @@ } // Test that the http1 Transport.ResponseHeaderTimeout option and cancel is sent. func TestTransportResponseHeaderTimeout_NoBody(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportResponseHeaderTimeout(t, false) }) } func TestTransportResponseHeaderTimeout_Body(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportResponseHeaderTimeout(t, true) }) } -func testTransportResponseHeaderTimeout(t testing.TB, body bool) { +func testTransportResponseHeaderTimeout(t *testing.T, body bool) { const bodySize = 4 << 20 tc := newTestClientConn(t, func(t1 *http.Transport) { t1.ResponseHeaderTimeout = 5 * time.Millisecond @@ -1681,11 +1681,64 @@ t.Fatalf("RoundTrip error: %v; want timeout error", err) } } +// "An endpoint MUST treat a change to SETTINGS_INITIAL_WINDOW_SIZE +// that causes any flow-control window to exceed the maximum size as +// a connection error (Section 5.4.1) of type FLOW_CONTROL_ERROR." +// -- https://www.rfc-editor.org/rfc/rfc9113.html#section-6.9.2-7 +func TestTransportSettingsFlowControlUpdateBeyondLimit(t *testing.T) { + synctest.Test(t, testTransportSettingsFlowControlUpdateBeyondLimit) +} +func testTransportSettingsFlowControlUpdateBeyondLimit(t *testing.T) { + tc := newTestClientConn(t) + tc.greet() + + req, _ := http.NewRequest("GET", "https://dummy.tld/", nil) + rt := tc.roundTrip(req) + tc.wantFrameType(FrameHeaders) + + // Give this stream some additional flow control. + const windowIncrease = 1000 + tc.writeWindowUpdate(rt.streamID(), windowIncrease) + tc.wantIdle() + + // Adjust the initial flow control window. The stream is now over the limit. + const maxWindowSize = (1 << 31) - 1 // RFC 9113, 6.9.1 + const maxInitialWindowSize = maxWindowSize - windowIncrease + tc.writeSettings(Setting{SettingInitialWindowSize, maxInitialWindowSize + 1}) + tc.wantGoAway(0, ErrCodeFlowControl) +} + +// Counterpart to TestTransportSettingsFlowControlUpdateBeyondLimit: +// A SETTINGS update which doesn't quite put a stream over the flow control limit. +func TestTransportSettingsFlowControlUpdateWithinLimit(t *testing.T) { + synctest.Test(t, testTransportSettingsFlowControlUpdateWithinLimit) +} +func testTransportSettingsFlowControlUpdateWithinLimit(t *testing.T) { + tc := newTestClientConn(t) + tc.greet() + + req, _ := http.NewRequest("GET", "https://dummy.tld/", nil) + rt := tc.roundTrip(req) + tc.wantFrameType(FrameHeaders) + + // Give this stream some additional flow control. + const windowIncrease = 1000 + tc.writeWindowUpdate(rt.streamID(), windowIncrease) + tc.wantIdle() + + // Adjust the initial flow control window. The stream is just within the limit. + const maxWindowSize = (1 << 31) - 1 // RFC 9113, 6.9.1 + const maxInitialWindowSize = maxWindowSize - windowIncrease + tc.writeSettings(Setting{SettingInitialWindowSize, maxInitialWindowSize}) + tc.wantSettingsAck() + tc.wantIdle() +} + // https://go.dev/issue/77331 func TestTransportWindowUpdateBeyondLimit(t *testing.T) { - synctestTest(t, testTransportWindowUpdateBeyondLimit) + synctest.Test(t, testTransportWindowUpdateBeyondLimit) } -func testTransportWindowUpdateBeyondLimit(t testing.TB) { +func testTransportWindowUpdateBeyondLimit(t *testing.T) { const windowIncrease uint32 = (1 << 31) - 1 // Will cause window to exceed limit of 2^31-1. tc := newTestClientConn(t) tc.greet() @@ -1978,8 +2031,8 @@ // The Google GFE responds to HEAD requests with a HEADERS frame // without END_STREAM, followed by a 0-length DATA frame with // END_STREAM. Make sure we don't get confused by that. (We did.) -func TestTransportReadHeadResponse(t *testing.T) { synctestTest(t, testTransportReadHeadResponse) } -func testTransportReadHeadResponse(t testing.TB) { +func TestTransportReadHeadResponse(t *testing.T) { synctest.Test(t, testTransportReadHeadResponse) } +func testTransportReadHeadResponse(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -2006,9 +2059,9 @@ rt.wantBody(nil) } func TestTransportReadHeadResponseWithBody(t *testing.T) { - synctestTest(t, testTransportReadHeadResponseWithBody) + synctest.Test(t, testTransportReadHeadResponseWithBody) } -func testTransportReadHeadResponseWithBody(t testing.TB) { +func testTransportReadHeadResponseWithBody(t *testing.T) { // This test uses an invalid response format. // Discard logger output to not spam tests output. log.SetOutput(io.Discard) @@ -2052,9 +2105,9 @@ // #15425: Transport goroutine leak while the transport is still trying to // write its body after the stream has completed. func TestTransportStreamEndsWhileBodyIsBeingWritten(t *testing.T) { - synctestTest(t, testTransportStreamEndsWhileBodyIsBeingWritten) + synctest.Test(t, testTransportStreamEndsWhileBodyIsBeingWritten) } -func testTransportStreamEndsWhileBodyIsBeingWritten(t testing.TB) { +func testTransportStreamEndsWhileBodyIsBeingWritten(t *testing.T) { body := "this is the client request body" const windowSize = 10 // less than len(body) @@ -2083,8 +2136,8 @@ }) rt.wantStatus(413) } -func TestTransportFlowControl(t *testing.T) { synctestTest(t, testTransportFlowControl) } -func testTransportFlowControl(t testing.TB) { +func TestTransportFlowControl(t *testing.T) { synctest.Test(t, testTransportFlowControl) } +func testTransportFlowControl(t *testing.T) { const maxBuffer = 64 << 10 // 64KiB tc := newTestClientConn(t, func(tr *http.Transport) { tr.HTTP2 = &http.HTTP2Config{ @@ -2154,18 +2207,18 @@ // RoundTrip or request body reads) if needed (e.g. if the server // proceeds to close the TCP connection before the client gets its // response) func TestTransportUsesGoAwayDebugError_RoundTrip(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportUsesGoAwayDebugError(t, false) }) } func TestTransportUsesGoAwayDebugError_Body(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportUsesGoAwayDebugError(t, true) }) } -func testTransportUsesGoAwayDebugError(t testing.TB, failMidBody bool) { +func testTransportUsesGoAwayDebugError(t *testing.T, failMidBody bool) { tc := newTestClientConn(t) tc.greet() @@ -2217,8 +2270,8 @@ } // https://go.dev/issue/68440 -- receiving a GoAway when there are no outstanding requests // should immediately close the connection. -func TestTransportGoAwayWithNoConns(t *testing.T) { synctestTest(t, testTransportGoAwayWithNoConns) } -func testTransportGoAwayWithNoConns(t testing.TB) { +func TestTransportGoAwayWithNoConns(t *testing.T) { synctest.Test(t, testTransportGoAwayWithNoConns) } +func testTransportGoAwayWithNoConns(t *testing.T) { tt := newTestTransportWithUnusedConn(t) tc := tt.getConn() tc.greet() @@ -2226,7 +2279,7 @@ tc.writeGoAway(1, ErrCodeNo, nil) tc.wantClosed() } -func testTransportReturnsUnusedFlowControl(t testing.TB, oneDataFrame bool) { +func testTransportReturnsUnusedFlowControl(t *testing.T, oneDataFrame bool) { tc := newTestClientConn(t) tc.greet() @@ -2300,22 +2353,22 @@ } // See golang.org/issue/16481 func TestTransportReturnsUnusedFlowControlSingleWrite(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportReturnsUnusedFlowControl(t, true) }) } // See golang.org/issue/20469 func TestTransportReturnsUnusedFlowControlMultipleWrites(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportReturnsUnusedFlowControl(t, false) }) } // Issue 16612: adjust flow control on open streams when transport // receives SETTINGS with INITIAL_WINDOW_SIZE from server. -func TestTransportAdjustsFlowControl(t *testing.T) { synctestTest(t, testTransportAdjustsFlowControl) } -func testTransportAdjustsFlowControl(t testing.TB) { +func TestTransportAdjustsFlowControl(t *testing.T) { synctest.Test(t, testTransportAdjustsFlowControl) } +func testTransportAdjustsFlowControl(t *testing.T) { const bodySize = 1 << 20 tc := newTestClientConn(t) @@ -2372,9 +2425,9 @@ } // See golang.org/issue/16556 func TestTransportReturnsDataPaddingFlowControl(t *testing.T) { - synctestTest(t, testTransportReturnsDataPaddingFlowControl) + synctest.Test(t, testTransportReturnsDataPaddingFlowControl) } -func testTransportReturnsDataPaddingFlowControl(t testing.TB) { +func testTransportReturnsDataPaddingFlowControl(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -2411,9 +2464,9 @@ // golang.org/issue/16572 -- RoundTrip shouldn't hang when it gets a // StreamError as a result of the response HEADERS func TestTransportReturnsErrorOnBadResponseHeaders(t *testing.T) { - synctestTest(t, testTransportReturnsErrorOnBadResponseHeaders) + synctest.Test(t, testTransportReturnsErrorOnBadResponseHeaders) } -func testTransportReturnsErrorOnBadResponseHeaders(t testing.TB) { +func testTransportReturnsErrorOnBadResponseHeaders(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -2465,9 +2518,9 @@ // Notably, to trigger this, the requests need to use a Request.Body // which returns (non-0, io.EOF) and also needs to set the ContentLength // explicitly. func TestTransportBodyDoubleEndStream(t *testing.T) { - synctestTest(t, testTransportBodyDoubleEndStream) + synctest.Test(t, testTransportBodyDoubleEndStream) } -func testTransportBodyDoubleEndStream(t testing.TB) { +func testTransportBodyDoubleEndStream(t *testing.T) { ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { // Nothing. }) @@ -2629,9 +2682,9 @@ // golang.org/issue/17071 -- don't sniff the first byte of the request body // before we've determined that the ClientConn is usable. func TestRoundTripDoesntConsumeRequestBodyEarly(t *testing.T) { - synctestTest(t, testRoundTripDoesntConsumeRequestBodyEarly) + synctest.Test(t, testRoundTripDoesntConsumeRequestBodyEarly) } -func testRoundTripDoesntConsumeRequestBodyEarly(t testing.TB) { +func testRoundTripDoesntConsumeRequestBodyEarly(t *testing.T) { tc := newTestClientConn(t) tc.greet() tc.closeWrite() @@ -2732,8 +2785,8 @@ // This access of req.Header should not race with code in the transport. req.Header = http.Header{} } -func TestTransportCloseAfterLostPing(t *testing.T) { synctestTest(t, testTransportCloseAfterLostPing) } -func testTransportCloseAfterLostPing(t testing.TB) { +func TestTransportCloseAfterLostPing(t *testing.T) { synctest.Test(t, testTransportCloseAfterLostPing) } +func testTransportCloseAfterLostPing(t *testing.T) { tc := newTestClientConn(t, func(h2 *http.HTTP2Config) { h2.PingTimeout = 1 * time.Second h2.SendPingTimeout = 1 * time.Second @@ -2783,9 +2836,9 @@ } } func TestTransportPingWhenReadingMultiplePings(t *testing.T) { - synctestTest(t, testTransportPingWhenReadingMultiplePings) + synctest.Test(t, testTransportPingWhenReadingMultiplePings) } -func testTransportPingWhenReadingMultiplePings(t testing.TB) { +func testTransportPingWhenReadingMultiplePings(t *testing.T) { tc := newTestClientConn(t, func(h2 *http.HTTP2Config) { h2.SendPingTimeout = 1000 * time.Millisecond }) @@ -2830,9 +2883,9 @@ } } func TestTransportPingWhenReadingPingDisabled(t *testing.T) { - synctestTest(t, testTransportPingWhenReadingPingDisabled) + synctest.Test(t, testTransportPingWhenReadingPingDisabled) } -func testTransportPingWhenReadingPingDisabled(t testing.TB) { +func testTransportPingWhenReadingPingDisabled(t *testing.T) { tc := newTestClientConn(t, func(h2 *http.HTTP2Config) { h2.SendPingTimeout = 0 // PINGs disabled }) @@ -2859,9 +2912,9 @@ } } func TestTransportRetryAfterGOAWAYNoRetry(t *testing.T) { - synctestTest(t, testTransportRetryAfterGOAWAYNoRetry) + synctest.Test(t, testTransportRetryAfterGOAWAYNoRetry) } -func testTransportRetryAfterGOAWAYNoRetry(t testing.TB) { +func testTransportRetryAfterGOAWAYNoRetry(t *testing.T) { tt := newTestTransport(t) req, _ := http.NewRequest("GET", "https://dummy.tld/", nil) @@ -2886,9 +2939,9 @@ } } func TestTransportRetryAfterGOAWAYRetry(t *testing.T) { - synctestTest(t, testTransportRetryAfterGOAWAYRetry) + synctest.Test(t, testTransportRetryAfterGOAWAYRetry) } -func testTransportRetryAfterGOAWAYRetry(t testing.TB) { +func testTransportRetryAfterGOAWAYRetry(t *testing.T) { tt := newTestTransport(t) req, _ := http.NewRequest("GET", "https://dummy.tld/", nil) @@ -2933,9 +2986,9 @@ rt.wantStatus(200) } func TestTransportRetryAfterGOAWAYSecondRequest(t *testing.T) { - synctestTest(t, testTransportRetryAfterGOAWAYSecondRequest) + synctest.Test(t, testTransportRetryAfterGOAWAYSecondRequest) } -func testTransportRetryAfterGOAWAYSecondRequest(t testing.TB) { +func testTransportRetryAfterGOAWAYSecondRequest(t *testing.T) { tt := newTestTransport(t) // First request succeeds. @@ -2999,9 +3052,9 @@ rt2.wantStatus(200) } func TestTransportRetryAfterRefusedStream(t *testing.T) { - synctestTest(t, testTransportRetryAfterRefusedStream) + synctest.Test(t, testTransportRetryAfterRefusedStream) } -func testTransportRetryAfterRefusedStream(t testing.TB) { +func testTransportRetryAfterRefusedStream(t *testing.T) { tt := newTestTransport(t) req, _ := http.NewRequest("GET", "https://dummy.tld/", nil) @@ -3040,8 +3093,8 @@ rt.wantStatus(204) } -func TestTransportRetryHasLimit(t *testing.T) { synctestTest(t, testTransportRetryHasLimit) } -func testTransportRetryHasLimit(t testing.TB) { +func TestTransportRetryHasLimit(t *testing.T) { synctest.Test(t, testTransportRetryHasLimit) } +func testTransportRetryHasLimit(t *testing.T) { tt := newTestTransport(t) req, _ := http.NewRequest("GET", "https://dummy.tld/", nil) @@ -3080,9 +3133,9 @@ } } func TestTransportResponseDataBeforeHeaders(t *testing.T) { - synctestTest(t, testTransportResponseDataBeforeHeaders) + synctest.Test(t, testTransportResponseDataBeforeHeaders) } -func testTransportResponseDataBeforeHeaders(t testing.TB) { +func testTransportResponseDataBeforeHeaders(t *testing.T) { // Discard log output complaining about protocol error. log.SetOutput(io.Discard) t.Cleanup(func() { log.SetOutput(os.Stderr) }) // after other cleanup is done @@ -3132,7 +3185,7 @@ // This test's expectation changed when the http2 package moved into // net/http, since the configuration field set changed. want: DefaultMaxReadFrameSize, }} { - synctestSubtest(t, fmt.Sprint(test.maxReadFrameSize), func(t testing.TB) { + synctestSubtest(t, fmt.Sprint(test.maxReadFrameSize), func(t *testing.T) { tc := newTestClientConn(t, func(h2 *http.HTTP2Config) { h2.MaxReadFrameSize = int(test.maxReadFrameSize) }) @@ -3276,9 +3329,9 @@ rts[0].wantStatus(200) } func TestTransportMaxDecoderHeaderTableSize(t *testing.T) { - synctestTest(t, testTransportMaxDecoderHeaderTableSize) + synctest.Test(t, testTransportMaxDecoderHeaderTableSize) } -func testTransportMaxDecoderHeaderTableSize(t testing.TB) { +func testTransportMaxDecoderHeaderTableSize(t *testing.T) { var reqSize, resSize uint32 = 8192, 16384 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) { h2.MaxDecoderHeaderTableSize = int(reqSize) @@ -3299,9 +3352,9 @@ } } func TestTransportMaxEncoderHeaderTableSize(t *testing.T) { - synctestTest(t, testTransportMaxEncoderHeaderTableSize) + synctest.Test(t, testTransportMaxEncoderHeaderTableSize) } -func testTransportMaxEncoderHeaderTableSize(t testing.TB) { +func testTransportMaxEncoderHeaderTableSize(t *testing.T) { var peerAdvertisedMaxHeaderTableSize uint32 = 16384 const wantMaxEncoderHeaderTableSize = 8192 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) { @@ -3317,9 +3370,9 @@ // Issue 20448: stop allocating for DATA frames' payload after // Response.Body.Close is called. func TestTransportAllocationsAfterResponseBodyClose(t *testing.T) { - synctestTest(t, testTransportAllocationsAfterResponseBodyClose) + synctest.Test(t, testTransportAllocationsAfterResponseBodyClose) } -func testTransportAllocationsAfterResponseBodyClose(t testing.TB) { +func testTransportAllocationsAfterResponseBodyClose(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -3361,8 +3414,8 @@ } // Issue 18891: make sure Request.Body == NoBody means no DATA frame // is ever sent, even if empty. -func TestTransportNoBodyMeansNoDATA(t *testing.T) { synctestTest(t, testTransportNoBodyMeansNoDATA) } -func testTransportNoBodyMeansNoDATA(t testing.TB) { +func TestTransportNoBodyMeansNoDATA(t *testing.T) { synctest.Test(t, testTransportNoBodyMeansNoDATA) } +func testTransportNoBodyMeansNoDATA(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -3454,9 +3507,9 @@ // Verify transport doesn't crash when receiving bogus response lacking a :status header. // Issue 22880. func TestTransportHandlesInvalidStatuslessResponse(t *testing.T) { - synctestTest(t, testTransportHandlesInvalidStatuslessResponse) + synctest.Test(t, testTransportHandlesInvalidStatuslessResponse) } -func testTransportHandlesInvalidStatuslessResponse(t testing.TB) { +func testTransportHandlesInvalidStatuslessResponse(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -3596,8 +3649,8 @@ // The client closes the connection just after the server got the client's HEADERS // frame, but before the server sends its HEADERS response back. The expected // result is an error on RoundTrip explaining the client closed the connection. -func TestClientConnCloseAtHeaders(t *testing.T) { synctestTest(t, testClientConnCloseAtHeaders) } -func testClientConnCloseAtHeaders(t testing.TB) { +func TestClientConnCloseAtHeaders(t *testing.T) { synctest.Test(t, testClientConnCloseAtHeaders) } +func testClientConnCloseAtHeaders(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -3614,8 +3667,8 @@ } // The client closes the connection while reading the response. // The expected behavior is a response body io read error on the client. -func TestClientConnCloseAtBody(t *testing.T) { synctestTest(t, testClientConnCloseAtBody) } -func testClientConnCloseAtBody(t testing.TB) { +func TestClientConnCloseAtBody(t *testing.T) { synctest.Test(t, testClientConnCloseAtBody) } +func testClientConnCloseAtBody(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -3643,8 +3696,8 @@ } // The client sends a GOAWAY frame before the server finished processing a request. // We expect the connection not to close until the request is completed. -func TestClientConnShutdown(t *testing.T) { synctestTest(t, testClientConnShutdown) } -func testClientConnShutdown(t testing.TB) { +func TestClientConnShutdown(t *testing.T) { synctest.Test(t, testClientConnShutdown) } +func testClientConnShutdown(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -3678,8 +3731,8 @@ // The client sends a GOAWAY frame before the server finishes processing a request, // but cancels the passed context before the request is completed. The expected // behavior is the client closing the connection after the context is canceled. -func TestClientConnShutdownCancel(t *testing.T) { synctestTest(t, testClientConnShutdownCancel) } -func testClientConnShutdownCancel(t testing.TB) { +func TestClientConnShutdownCancel(t *testing.T) { synctest.Test(t, testClientConnShutdownCancel) } +func testClientConnShutdownCancel(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -3733,11 +3786,11 @@ return 0, r.err } func testTransportBodyReadError(t *testing.T, body []byte) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportBodyReadErrorBubble(t, body) }) } -func testTransportBodyReadErrorBubble(t testing.TB, body []byte) { +func testTransportBodyReadErrorBubble(t *testing.T, body []byte) { tc := newTestClientConn(t) tc.greet() @@ -3778,8 +3831,8 @@ // Issue 32254: verify that the client sends END_STREAM flag eagerly with the last // (or in this test-case the only one) request body data frame, and does not send // extra zero-len data frames. -func TestTransportBodyEagerEndStream(t *testing.T) { synctestTest(t, testTransportBodyEagerEndStream) } -func testTransportBodyEagerEndStream(t testing.TB) { +func TestTransportBodyEagerEndStream(t *testing.T) { synctest.Test(t, testTransportBodyEagerEndStream) } +func testTransportBodyEagerEndStream(t *testing.T) { const reqBody = "some request body" const resBody = "some response body" @@ -3823,7 +3876,7 @@ body := &chunkReader{[][]byte{ []byte("123"), []byte("456"), }} - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportBodyLargerThanSpecifiedContentLength(t, body, 3) }) } @@ -3832,12 +3885,12 @@ func TestTransportBodyLargerThanSpecifiedContentLength_len2(t *testing.T) { body := &chunkReader{[][]byte{ []byte("123"), }} - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportBodyLargerThanSpecifiedContentLength(t, body, 2) }) } -func testTransportBodyLargerThanSpecifiedContentLength(t testing.TB, body *chunkReader, contentLen int64) { +func testTransportBodyLargerThanSpecifiedContentLength(t *testing.T, body *chunkReader, contentLen int64) { ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { r.Body.Read(make([]byte, 6)) }) @@ -3854,9 +3907,9 @@ } // issue 39337: close the connection on a failed write func TestTransportNewClientConnCloseOnWriteError(t *testing.T) { - synctestTest(t, testTransportNewClientConnCloseOnWriteError) + synctest.Test(t, testTransportNewClientConnCloseOnWriteError) } -func testTransportNewClientConnCloseOnWriteError(t testing.TB) { +func testTransportNewClientConnCloseOnWriteError(t *testing.T) { // The original version of this test verifies that we close a connection // if we fail to write the client preface, SETTINGS, and WINDOW_UPDATE. // @@ -3887,9 +3940,9 @@ } } func TestTransportRoundtripCloseOnWriteError(t *testing.T) { - synctestTest(t, testTransportRoundtripCloseOnWriteError) + synctest.Test(t, testTransportRoundtripCloseOnWriteError) } -func testTransportRoundtripCloseOnWriteError(t testing.TB) { +func testTransportRoundtripCloseOnWriteError(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -4232,13 +4285,13 @@ return req }(), }} { t.Run(test.name, func(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportBlockingRequestWrite(t, test.req) }) }) } } -func testTransportBlockingRequestWrite(t testing.TB, req2 *http.Request) { +func testTransportBlockingRequestWrite(t *testing.T, req2 *http.Request) { tt := newTestTransport(t) smallReq := func() *http.Request { @@ -4335,9 +4388,9 @@ } } func TestTransportNoRetryOnStreamProtocolError(t *testing.T) { - synctestTest(t, testTransportNoRetryOnStreamProtocolError) + synctest.Test(t, testTransportNoRetryOnStreamProtocolError) } -func testTransportNoRetryOnStreamProtocolError(t testing.TB) { +func testTransportNoRetryOnStreamProtocolError(t *testing.T) { // This test verifies that: // - a request that fails with ErrCodeProtocol is not retried. See // go.dev/issue/77843. @@ -4395,8 +4448,8 @@ }) rt1.wantStatus(200) } -func TestClientConnReservations(t *testing.T) { synctestTest(t, testClientConnReservations) } -func testClientConnReservations(t testing.TB) { +func TestClientConnReservations(t *testing.T) { synctest.Test(t, testClientConnReservations) } +func testClientConnReservations(t *testing.T) { tc := newTestClientConn(t) tc.greet( Setting{ID: SettingMaxConcurrentStreams, Val: InitialMaxConcurrentStreams}, @@ -4447,8 +4500,8 @@ t.Errorf("after reset, reservations = %v; want %v", n2, n) } } -func TestTransportTimeoutServerHangs(t *testing.T) { synctestTest(t, testTransportTimeoutServerHangs) } -func testTransportTimeoutServerHangs(t testing.TB) { +func TestTransportTimeoutServerHangs(t *testing.T) { synctest.Test(t, testTransportTimeoutServerHangs) } +func testTransportTimeoutServerHangs(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -4493,7 +4546,7 @@ wantErr: nil, wantContentLength: 0, }, } { - synctestSubtest(t, test.name, func(t testing.TB) { + synctestSubtest(t, test.name, func(t *testing.T) { contentLength := "" ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Length", contentLength) @@ -4524,9 +4577,9 @@ } } func TestTransportCloseResponseBodyWhileRequestBodyHangs(t *testing.T) { - synctestTest(t, testTransportCloseResponseBodyWhileRequestBodyHangs) + synctest.Test(t, testTransportCloseResponseBodyWhileRequestBodyHangs) } -func testTransportCloseResponseBodyWhileRequestBodyHangs(t testing.TB) { +func testTransportCloseResponseBodyWhileRequestBodyHangs(t *testing.T) { ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) w.(http.Flusher).Flush() @@ -4549,8 +4602,8 @@ res.Body.Close() pw.Close() } -func TestTransport300ResponseBody(t *testing.T) { synctestTest(t, testTransport300ResponseBody) } -func testTransport300ResponseBody(t testing.TB) { +func TestTransport300ResponseBody(t *testing.T) { synctest.Test(t, testTransport300ResponseBody) } +func testTransport300ResponseBody(t *testing.T) { reqc := make(chan struct{}) body := []byte("response body") ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { @@ -4624,8 +4677,8 @@ } return c.Conn.Write(b) } -func TestTransportSlowWrites(t *testing.T) { synctestTest(t, testTransportSlowWrites) } -func testTransportSlowWrites(t testing.TB) { +func TestTransportSlowWrites(t *testing.T) { synctest.Test(t, testTransportSlowWrites) } +func testTransportSlowWrites(t *testing.T) { ts := newTestServer(t, nil, func(s *http.Server) { s.Protocols = protocols("h2c") }) @@ -4647,12 +4700,12 @@ resp.Body.Close() } func TestTransportClosesConnAfterGoAwayNoStreams(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportClosesConnAfterGoAway(t, 0) }) } func TestTransportClosesConnAfterGoAwayLastStream(t *testing.T) { - synctestTest(t, func(t testing.TB) { + synctest.Test(t, func(t *testing.T) { testTransportClosesConnAfterGoAway(t, 1) }) } @@ -4663,7 +4716,7 @@ // // lastStream is the last stream ID in the GOAWAY frame. // When 0, the transport (unsuccessfully) retries the request (stream 1); // when 1, the transport reads the response after receiving the GOAWAY. -func testTransportClosesConnAfterGoAway(t testing.TB, lastStream uint32) { +func testTransportClosesConnAfterGoAway(t *testing.T, lastStream uint32) { tc := newTestClientConn(t) tc.greet() @@ -4886,8 +4939,8 @@ case <-successCh: } } -func TestTransportDataAfter1xxHeader(t *testing.T) { synctestTest(t, testTransportDataAfter1xxHeader) } -func testTransportDataAfter1xxHeader(t testing.TB) { +func TestTransportDataAfter1xxHeader(t *testing.T) { synctest.Test(t, testTransportDataAfter1xxHeader) } +func testTransportDataAfter1xxHeader(t *testing.T) { // Discard logger output to avoid spamming stderr. log.SetOutput(io.Discard) defer log.SetOutput(os.Stderr) @@ -5011,7 +5064,7 @@ }, hcount: 20, limited: false, }} { - synctestSubtest(t, test.name, func(t testing.TB) { + synctestSubtest(t, test.name, func(t *testing.T) { tc := newTestClientConn(t, test.opt) tc.greet() @@ -5048,8 +5101,8 @@ } // TestTransportSendPingWithReset verifies that when a request to an unresponsive server // is canceled, it continues to consume a concurrency slot until the server responds to a PING. -func TestTransportSendPingWithReset(t *testing.T) { synctestTest(t, testTransportSendPingWithReset) } -func testTransportSendPingWithReset(t testing.TB) { +func TestTransportSendPingWithReset(t *testing.T) { synctest.Test(t, testTransportSendPingWithReset) } +func testTransportSendPingWithReset(t *testing.T) { tc := newTestClientConn(t, func(h2 *http.HTTP2Config) { h2.StrictMaxConcurrentRequests = true }) @@ -5093,9 +5146,9 @@ // server is canceled (specifically: when frames have been received from the server // in the time since the request was first sent), the request is immediately canceled and // does not continue to consume a concurrency slot. func TestTransportNoPingAfterResetWithFrames(t *testing.T) { - synctestTest(t, testTransportNoPingAfterResetWithFrames) + synctest.Test(t, testTransportNoPingAfterResetWithFrames) } -func testTransportNoPingAfterResetWithFrames(t testing.TB) { +func testTransportNoPingAfterResetWithFrames(t *testing.T) { tc := newTestClientConn(t, func(h2 *http.HTTP2Config) { h2.StrictMaxConcurrentRequests = true }) @@ -5140,9 +5193,9 @@ // Issue #70505: gRPC gets upset if we send more than 2 pings per HEADERS/DATA frame // sent by the server. func TestTransportSendNoMoreThanOnePingWithReset(t *testing.T) { - synctestTest(t, testTransportSendNoMoreThanOnePingWithReset) + synctest.Test(t, testTransportSendNoMoreThanOnePingWithReset) } -func testTransportSendNoMoreThanOnePingWithReset(t testing.TB) { +func testTransportSendNoMoreThanOnePingWithReset(t *testing.T) { tc := newTestClientConn(t) tc.greet() @@ -5215,9 +5268,9 @@ tc.wantFrameType(FramePing) } func TestTransportConnBecomesUnresponsive(t *testing.T) { - synctestTest(t, testTransportConnBecomesUnresponsive) + synctest.Test(t, testTransportConnBecomesUnresponsive) } -func testTransportConnBecomesUnresponsive(t testing.TB) { +func testTransportConnBecomesUnresponsive(t *testing.T) { // We send a number of requests in series to an unresponsive connection. // Each request is canceled or times out without a response. // Eventually, we open a new connection rather than trying to use the old one. @@ -5292,7 +5345,7 @@ // sends a request on the Transport, // and then cancels the request before the resulting dial completes. // It then waits for the dial to finish // and returns the Transport with an unused conn in its pool. -func newTestTransportWithUnusedConn(t testing.TB, opts ...any) *testTransport { +func newTestTransportWithUnusedConn(t *testing.T, opts ...any) *testTransport { tt := newTestTransport(t, opts...) waitc := make(chan struct{}) @@ -5315,8 +5368,8 @@ return tt } // Test that the Transport can use a conn created for one request, but never used by it. -func TestTransportUnusedConnOK(t *testing.T) { synctestTest(t, testTransportUnusedConnOK) } -func testTransportUnusedConnOK(t testing.TB) { +func TestTransportUnusedConnOK(t *testing.T) { synctest.Test(t, testTransportUnusedConnOK) } +func testTransportUnusedConnOK(t *testing.T) { tt := newTestTransportWithUnusedConn(t) req := Must(http.NewRequest("GET", "https://dummy.tld/", nil)) @@ -5355,9 +5408,9 @@ } // Test the case where an unused conn immediately encounters an error. func TestTransportUnusedConnImmediateFailureUsed(t *testing.T) { - synctestTest(t, testTransportUnusedConnImmediateFailureUsed) + synctest.Test(t, testTransportUnusedConnImmediateFailureUsed) } -func testTransportUnusedConnImmediateFailureUsed(t testing.TB) { +func testTransportUnusedConnImmediateFailureUsed(t *testing.T) { tt := newTestTransportWithUnusedConn(t) // The connection encounters an error before we send a request that uses it. @@ -5385,9 +5438,9 @@ } // Test the case where an unused conn is closed for idleness before we use it. func TestTransportUnusedConnIdleTimoutBeforeUse(t *testing.T) { - synctestTest(t, testTransportUnusedConnIdleTimoutBeforeUse) + synctest.Test(t, testTransportUnusedConnIdleTimoutBeforeUse) } -func testTransportUnusedConnIdleTimoutBeforeUse(t testing.TB) { +func testTransportUnusedConnIdleTimoutBeforeUse(t *testing.T) { tt := newTestTransportWithUnusedConn(t, func(t1 *http.Transport) { t1.IdleConnTimeout = 1 * time.Second }) @@ -5413,9 +5466,9 @@ // Test the case where a conn provided via a TLSNextProto hook immediately encounters an error, // but no requests are sent which would use the bad connection. func TestTransportTLSNextProtoConnImmediateFailureUnused(t *testing.T) { - synctestTest(t, testTransportTLSNextProtoConnImmediateFailureUnused) + synctest.Test(t, testTransportTLSNextProtoConnImmediateFailureUnused) } -func testTransportTLSNextProtoConnImmediateFailureUnused(t testing.TB) { +func testTransportTLSNextProtoConnImmediateFailureUnused(t *testing.T) { tt := newTestTransportWithUnusedConn(t, func(t1 *http.Transport) { t1.IdleConnTimeout = 1 * time.Second }) @@ -5440,9 +5493,9 @@ tc2.wantFrameType(FrameHeaders) } func TestTransportDoNotHangOnZeroMaxFrameSize(t *testing.T) { - synctestTest(t, testTransportDoNotHangOnZeroMaxFrameSize) + synctest.Test(t, testTransportDoNotHangOnZeroMaxFrameSize) } -func testTransportDoNotHangOnZeroMaxFrameSize(t testing.TB) { +func testTransportDoNotHangOnZeroMaxFrameSize(t *testing.T) { tc := newTestClientConn(t) tc.writeSettings(Setting{ID: SettingMaxFrameSize, Val: 0}) tc.wantFrameType(FrameSettings) @@ -5517,9 +5570,9 @@ // Issue #70658: Make sure extended CONNECT requests don't get stuck if a // connection fails early in its lifetime. func TestExtendedConnectReadFrameError(t *testing.T) { - synctestTest(t, testExtendedConnectReadFrameError) + synctest.Test(t, testExtendedConnectReadFrameError) } -func testExtendedConnectReadFrameError(t testing.TB) { +func testExtendedConnectReadFrameError(t *testing.T) { t.Skip("https://go.dev/issue/53208 -- net/http needs to support the :protocol header") tc := newTestClientConn(t) tc.wantFrameType(FrameSettings) diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go index ad99f70287f74caa2b2a99c68def095b2cd305da..281edfe99934ccf1397003ab946d23ba46546313 100644 --- a/src/path/filepath/path_test.go +++ b/src/path/filepath/path_test.go @@ -92,12 +92,16 @@ {`c:..\abc`, `c:..\abc`}, {`c:\b:\..\..\..\d`, `c:\d`}, {`\`, `\`}, {`/`, `\`}, - {`\\i\..\c$`, `\\i\..\c$`}, - {`\\i\..\i\c$`, `\\i\..\i\c$`}, - {`\\i\..\I\c$`, `\\i\..\I\c$`}, + {`\\i\..\c$`, `\c$`}, + {`\\i\..\i\c$`, `\i\c$`}, + {`\\i\..\I\c$`, `\I\c$`}, + {`\\..\..\a`, `\a`}, + {`//../../a`, `\a`}, {`\\host\share\foo\..\bar`, `\\host\share\bar`}, {`//host/share/foo/../baz`, `\\host\share\baz`}, {`\\host\share\foo\..\..\..\..\bar`, `\\host\share\bar`}, + {`\\?\UNC\host\share\foo\..\..\..\..\bar`, `\\?\UNC\host\share\bar`}, + {`\??\UNC\host\share\foo\..\..\..\..\bar`, `\??\UNC\host\share\bar`}, {`\\.\C:\a\..\..\..\..\bar`, `\\.\C:\bar`}, {`\\.\C:\\\\a`, `\\.\C:\a`}, {`\\a\b\..\c`, `\\a\b\c`}, @@ -1085,6 +1089,11 @@ {`\\host\share`, true}, {`\\host\share\`, true}, {`\\host\share\foo`, true}, {`//host/share/foo/bar`, true}, + {`\\..\..\a`, false}, + {`//../../a`, false}, + {`\\i\..\c$`, false}, + {`//?/../x`, false}, + {`//./../x`, false}, {`\\?\a\b\c`, true}, {`\??\a\b\c`, true}, } @@ -1579,6 +1588,15 @@ {`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`}, {`//host/share//foo///bar////baz`, `\\host\share`}, {`\\host\share\foo\..\bar`, `\\host\share`}, {`//host/share/foo/../bar`, `\\host\share`}, + {`\\..\..\a`, ``}, + {`//../../a`, ``}, + {`\\i\..\c$`, ``}, + {`//./UNC/../share`, ``}, + {`//?/../x`, ``}, + {`//./../x`, ``}, + {`//.../share`, `\\...\share`}, + {`//host/...`, `\\host\...`}, + {`//?/..x`, `\\?\..x`}, {`//.`, `\\.`}, {`//./`, `\\.\`}, {`//./NUL`, `\\.\NUL`}, @@ -1593,6 +1611,8 @@ {`//./C:`, `\\.\C:`}, {`//./C:/`, `\\.\C:`}, {`//./C:/a/b/c`, `\\.\C:`}, {`//./UNC/host/share/a/b/c`, `\\.\UNC\host\share`}, + {`//?/UNC/host/share/a/b/c`, `\\?\UNC\host\share`}, + {`/??/UNC/host/share/a/b/c`, `\??\UNC\host\share`}, {`//./UNC/host`, `\\.\UNC\host`}, {`//./UNC/host\`, `\\.\UNC\host\`}, {`//./UNC`, `\\.\UNC`}, diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 678fe20c25aeeb49b32865e20ec9f364c589ba66..f28807d0d8c93882bdbc6ece55c87ebd9ff592de 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -791,7 +791,7 @@ // like happened in go.dev/issue/56603. _ [0]*PanicNilError } -func (*PanicNilError) Error() string { return "panic called with nil argument" } +func (*PanicNilError) Error() string { return "runtime error: panic called with nil argument" } func (*PanicNilError) RuntimeError() {} var panicnil = &godebugInc{name: "panicnil"} diff --git a/src/runtime/panic_test.go b/src/runtime/panic_test.go index 5a4b6ae633c0b5f4a2e2f4b385a1d41529b7f5b5..889b33f032ec1813bb03b0a3cd2cdb402f91d981 100644 --- a/src/runtime/panic_test.go +++ b/src/runtime/panic_test.go @@ -5,6 +5,7 @@ package runtime_test import ( + "runtime" "slices" "strings" "testing" @@ -98,3 +99,59 @@ if m > 3.5 { t.Errorf("more than 2x time increase: %v", m) } } + +// Test that panics with nil arguments produce the prefix +// "runtime error:" per https://golang.org/issues/63813. +func TestPanicNilErrorPrefix(t *testing.T) { + tests := []struct { + name string + wantPanic string + fn func() + }{ + { + name: "panic(nil)", + wantPanic: "runtime error: panic with nil argument", + fn: func() { + panic(nil) + }, + }, + { + name: "panic((any)(nil))", + wantPanic: "runtime error: panic with nil argument", + fn: func() { + var foo any = nil + panic(foo) + }, + }, + { + name: "panic((error)(nil))", + wantPanic: "runtime error: panic with nil argument", + fn: func() { + var err error + panic(err) + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + defer func() { + r := recover() + if r == nil { + t.Fatal("expected a panic") + } + + re, ok := r.(runtime.Error) + if !ok { + t.Fatalf("wrong panic type: got %T, want runtime.Error", r) + } + if !strings.Contains(re.Error(), "runtime error: panic called with nil argument") { + t.Fatalf("mismatched message, missing `runtime error: panic with nil`, : got:\n%s", re.Error()) + } + }() + + tt.fn() + }) + } +} diff --git a/src/runtime/proc.go b/src/runtime/proc.go index beb91432e0cfd795cf14cee7e2e686eb1093862f..b8d4d15041a54e7f60df460c58ce87582b831227 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -217,6 +217,13 @@ gcenable() defaultGOMAXPROCSUpdateEnable() // don't STW before runtime initialized. + // If we encountered a removed GODEBUG during startup we can panic now. + if k := invalidGODEBUG.key; k != "" { + v := invalidGODEBUG.value + r := strconv.Itoa(invalidGODEBUG.removed) + fatal(`removed GODEBUG "` + k + `" set to old value "` + v + `" in environment (https://go.dev/doc/godebug#go-1` + r + `)`) + } + mainInitDoneChan = make(chan bool) if iscgo { if _cgo_pthread_key_created == nil { diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index 0cc653b0a3d1560676a9f3f369cc837ac76d06fe..fc26e9b3f8226fa0be78ff36dac50002945a0325 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -8,6 +8,7 @@ import ( "internal/abi" "internal/bytealg" "internal/goarch" + "internal/godebugs" "internal/runtime/atomic" "internal/strconv" "unsafe" @@ -470,6 +471,14 @@ } } } +// If an invalid GODEBUG setting is found during startup time, +// invalidGODEBUG is set to that setting so it can be reported +// when initialization has progressed sufficiently. +var invalidGODEBUG struct { + key, value string + removed int +} + // parsegodebug parses the godebug string, updating variables listed in dbgvars. // If seen == nil, this is startup time and we process the string left to right // overwriting older settings with newer ones. @@ -508,6 +517,23 @@ if i < 0 { continue } key, value := field[:i], field[i+1:] + + // Setting a removed GODEBUG is ok unless it's set to an old value. + // We only check at startup time per go.dev/issue/76163. + if seen == nil { + for _, info := range godebugs.Removed { + if info.Name == key { + if info.Old(value) { + invalidGODEBUG.key = key + invalidGODEBUG.value = value + invalidGODEBUG.removed = info.Removed + return // this skips the cgocheck below but we're about to fatal anyway + } + break + } + } + } + if seen[key] { continue } diff --git a/src/runtime/slice.go b/src/runtime/slice.go index 2a44297754288815bdee82cf3db6f533937f403a..9f5561feaf04a82c512574064b88fceb2e13c488 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -480,10 +480,11 @@ return old, 0, 0 } lenmem := uintptr(len) * et.Size_ capmem := roundupsize(lenmem, false) - new := mallocgc(capmem, et, true) + cap := capmem / et.Size_ + new := mallocgc(cap*et.Size_, et, true) bulkBarrierPreWriteSrcOnly(uintptr(new), uintptr(old), lenmem, et) memmove(new, old, lenmem) - return new, len, int(capmem / et.Size_) + return new, len, int(cap) } // moveSliceNoCapNoScan is a combination of moveSliceNoScan and moveSliceNoCap. diff --git a/src/simd/archsimd/_gen/midway/intersect_simd_ops.go b/src/simd/archsimd/_gen/midway/intersect_simd_ops.go index 99048c8fa229d2a0f2ecc2507d2f68f17c7aab25..e91c52763c45b1a4dc4f33ea7525e7ada44b3012 100644 --- a/src/simd/archsimd/_gen/midway/intersect_simd_ops.go +++ b/src/simd/archsimd/_gen/midway/intersect_simd_ops.go @@ -218,6 +218,7 @@ } return recvType } + // Record existing emulated methods emulated := make(map[TypeMethod]bool) f, err := parser.ParseFile(fset, emulatedFile, nil, parser.ParseComments) if err != nil { @@ -327,109 +328,135 @@ } } } + type ElemMethod struct { + e, m string + } + intersectionByElem := make(map[string][]string) + signatureByElemMethod := make(map[ElemMethod]*ast.FuncDecl) - doWrites := func(w io.Writer) { + // elems is a slice of stems of vector types. + elems := []string{"Int8", "Int16", "Int32", "Int64", "Uint8", "Uint16", "Uint32", "Uint64", "Float32", "Float64", "Mask8", "Mask16", "Mask32", "Mask64"} - p := func(s ...any) { fmt.Fprint(w, s...) } - pf := func(f string, s ...any) { fmt.Fprintf(w, f, s...) } - nl := func() { fmt.Fprintln(w) } + for _, elem := range elems { + type128 := map128[elem] + type256 := map256[elem] + type512 := map512[elem] - // elems is a slice of stems of vector types. - elems := []string{"Int8", "Int16", "Int32", "Int64", "Uint8", "Uint16", "Uint32", "Uint64", "Float32", "Float64", "Mask8", "Mask16", "Mask32", "Mask64"} + methods128w := methodsByType[combine("wasm", type128)] + methods128n := methodsByType[combine("arm64", type128)] + methods128 := methodsByType[combine("amd64", type128)] + methods256 := methodsByType[combine("amd64", type256)] + methods512 := methodsByType[combine("amd64", type512)] - fmt.Fprintln(w, - `// Code generated by 'go run -C $GOROOT/src/simd/archsimd/_gen/midway'; DO NOT EDIT. + var intersection []string + var missingNames []string + for m := range allMethodNames { + if wasm128, arm128, amd128, amd256, amd512 := + methods128w[m] == nil, methods128n[m] == nil, methods128[m] == nil, methods256[m] == nil, methods512[m] == nil; !wasm128 && !arm128 && !amd128 && !amd256 && !amd512 { + intersection = append(intersection, m) + signatureByElemMethod[ElemMethod{elem, m}] = methods512[m] // Use 512-bit signature (arbitrary choice, they should match) + } else if !(wasm128 && arm128 && amd128 && amd256 && amd512) { + missing[m] = whyMissing{wasm128, arm128, amd128, amd256, amd512} + missingNames = append(missingNames, m) + } + } + sort.Strings(missingNames) -//go:build goexperiment.simd && (amd64 || wasm || arm64) + for _, m := range missingNames { + pv("Missing implementation for %ss.%s on %s\n", elem, m, missing[m].String()) + } -// Computed intersection of methods for supported SIMD architectures and vector widths + sort.Strings(intersection) -package simd`) + intersectionByElem[elem] = intersection + } - if c := comments.Types["_simd"]; c != "" { - pf("// %s\n", c) - } - p("type _simd struct {\n\t_ [0]func(*_simd) *_simd\n}\n") - - sigForMethod := make(map[string]*ast.FuncDecl) - - // xlateType translates a type by replacing instances of types with keys in knownReceivers with their values, - // and generates the string representation of the resulting type. E.g., []Int8x32 -> []Int8s - // (because Int8x32 -> Int8s in knownReceivers - var xlateType func(ast.Expr) string - xlateType = func(e ast.Expr) string { - switch t := e.(type) { - case *ast.Ident: - if mapped, ok := knownReceivers[t.Name]; ok { - return mapped - } - return t.Name - case *ast.StarExpr: - return "*" + xlateType(t.X) - case *ast.ArrayType: - lenStr := "" - if t.Len != nil { - var buf strings.Builder - format.Node(&buf, token.NewFileSet(), t.Len) - lenStr = buf.String() - } - return "[" + lenStr + "]" + xlateType(t.Elt) - case *ast.SelectorExpr: - return xlateType(t.X) + "." + t.Sel.Name - case *ast.Ellipsis: - return "..." + xlateType(t.Elt) - default: + // xlateType translates a type by replacing instances of types with keys in knownReceivers with their values, + // and generates the string representation of the resulting type. E.g., []Int8x32 -> []Int8s + // (because Int8x32 -> Int8s in knownReceivers + var xlateType func(ast.Expr) string + xlateType = func(e ast.Expr) string { + switch t := e.(type) { + case *ast.Ident: + if mapped, ok := knownReceivers[t.Name]; ok { + return mapped + } + return t.Name + case *ast.StarExpr: + return "*" + xlateType(t.X) + case *ast.ArrayType: + lenStr := "" + if t.Len != nil { var buf strings.Builder - format.Node(&buf, token.NewFileSet(), t) - return buf.String() + format.Node(&buf, token.NewFileSet(), t.Len) + lenStr = buf.String() } + return "[" + lenStr + "]" + xlateType(t.Elt) + case *ast.SelectorExpr: + return xlateType(t.X) + "." + t.Sel.Name + case *ast.Ellipsis: + return "..." + xlateType(t.Elt) + default: + var buf strings.Builder + format.Node(&buf, token.NewFileSet(), t) + return buf.String() } + } - toScalar := func(s string) string { - if strings.HasPrefix(s, "Mask") { - return "int" + s[4:] - } - return strings.ToLower(s) + toScalar := func(s string) string { + if strings.HasPrefix(s, "Mask") { + return "int" + s[4:] } + return strings.ToLower(s) + } - for _, elem := range elems { - type128 := map128[elem] - type256 := map256[elem] - type512 := map512[elem] + doTypes := func(w io.Writer) { + + pf := func(f string, s ...any) { fmt.Fprintf(w, f, s...) } - methods128w := methodsByType[combine("wasm", type128)] - methods128n := methodsByType[combine("arm64", type128)] - methods128 := methodsByType[combine("amd64", type128)] - methods256 := methodsByType[combine("amd64", type256)] - methods512 := methodsByType[combine("amd64", type512)] + fmt.Fprintln(w, + `// Code generated by 'go run -C $GOROOT/src/simd/archsimd/_gen/midway'; DO NOT EDIT. - var intersection []string - var missingNames []string - for m := range allMethodNames { - if wasm128, arm128, amd128, amd256, amd512 := - methods128w[m] == nil, methods128n[m] == nil, methods128[m] == nil, methods256[m] == nil, methods512[m] == nil; !wasm128 && !arm128 && !amd128 && !amd256 && !amd512 { - intersection = append(intersection, m) - sigForMethod[m] = methods512[m] // Use 512-bit signature (arbitrary choice, they should match) - } else if !(wasm128 && arm128 && amd128 && amd256 && amd512) { - missing[m] = whyMissing{wasm128, arm128, amd128, amd256, amd512} - missingNames = append(missingNames, m) - } - } - sort.Strings(missingNames) +//go:build goexperiment.simd + +// Scalable vector types for rewriting and emulation - for _, m := range missingNames { - pv("Missing implementation for %ss.%s on %s\n", elem, m, missing[m].String()) - } +package simd - sort.Strings(intersection) +import "simd/internal/bridge" - intersectionByElem[elem] = intersection +// internal SIMD marker, and hard dependence on simd/internal/bridge +type _simd bridge.ZeroSized +`) + for _, elem := range elems { if c := comments.Types[elem+"s"]; c != "" { pf("// %s\n", c) } - pf("type %ss struct {\n\t_ _simd\n\tatLeast [2]uint64 // the actual vector size may be larger.\n}\n", elem) + pf("type %ss struct {\n\t_ _simd\n\ta, b uint64 // the actual vector size may be larger.\n}\n", elem) + } + } + + doMethods := func(w io.Writer) { + + p := func(s ...any) { fmt.Fprint(w, s...) } + pf := func(f string, s ...any) { fmt.Fprintf(w, f, s...) } + nl := func() { fmt.Fprintln(w) } + + fmt.Fprintln(w, + `// Code generated by 'go run -C $GOROOT/src/simd/archsimd/_gen/midway'; DO NOT EDIT. + +//go:build goexperiment.simd && (amd64 || wasm || arm64) + +// Computed intersection of methods for supported SIMD architectures and vector widths + +package simd + +`) + + for _, elem := range elems { + intersection := intersectionByElem[elem] if elem[0] != 'M' { // cannot load masks @@ -460,11 +487,10 @@ if broadcastComment != "" { pf("// %s\n", broadcastComment) } pf("func Broadcast%ss(%s) %ss\n", elem, toScalar(elem), elem) - } for _, m := range intersection { - fd := sigForMethod[m] + fd := signatureByElemMethod[ElemMethod{elem, m}] elems := elem + "s" methodComment := "" if typeMethods, ok := comments.Methods[elem+"s"]; ok { @@ -531,7 +557,10 @@ nl() } } } - formatAndWrite(*goRoot+"/src/simd/simd.go", doWrites) + + formatAndWrite(*goRoot+"/src/simd/simd_types.go", doTypes) + formatAndWrite(*goRoot+"/src/simd/simd_stubs.go", doMethods) + var extraMocks []TypeMethod for x := range emulated { extraMocks = append(extraMocks, x) @@ -546,8 +575,6 @@ for _, x := range extraMocks { pw("%s contains %s.%s missing from intersected methods\n", emulatedFile, x.t, x.m) } - - elems := []string{"Int8", "Int16", "Int32", "Int64", "Uint8", "Uint16", "Uint32", "Uint64", "Float32", "Float64", "Mask8", "Mask16", "Mask32", "Mask64"} for _, aaf := range archAndFiles { arch := aaf.arch diff --git a/src/simd/archsimd/_gen/simdgen/arch.go b/src/simd/archsimd/_gen/simdgen/arch.go index 5575810a839bc7daa0aaaf069093e2e30358ca94..a6498bdfd26d5d8c0d22f7ab9ac74a9c7fd28824 100644 --- a/src/simd/archsimd/_gen/simdgen/arch.go +++ b/src/simd/archsimd/_gen/simdgen/arch.go @@ -37,7 +37,9 @@ "v3kvResultInArg0", "vfpv", "vfpkv", "vgpvImm8", + "vgpvImm", "vgpImm8", + "vgpImm", "v2kvImm8", "vkvload", "v21load", diff --git a/src/simd/archsimd/_gen/simdgen/ops/Moves/go_amd64.yaml b/src/simd/archsimd/_gen/simdgen/ops/Moves/go_amd64.yaml index ae64e4e398552b6b2c8caf0a7edf2686b65fcb3e..aeb99b675ab13d5c46a4bb4ebdec7de876db8c9e 100644 --- a/src/simd/archsimd/_gen/simdgen/ops/Moves/go_amd64.yaml +++ b/src/simd/archsimd/_gen/simdgen/ops/Moves/go_amd64.yaml @@ -1,6 +1,51 @@ !sum - go: SetElem - asm: "VPINSR[BWDQ]" + asm: "VPINSRB" + in: + - &t + class: vreg + base: $b + - class: greg + base: $b + lanes: 1 # Scalar, darn it! + - class: immediate + immOffset: 0 + immMax: 15 + name: index + out: + - *t +- go: SetElem + asm: "VPINSRW" + in: + - &t + class: vreg + base: $b + - class: greg + base: $b + lanes: 1 # Scalar, darn it! + - class: immediate + immOffset: 0 + immMax: 7 + name: index + out: + - *t +- go: SetElem + asm: "VPINSRD" + in: + - &t + class: vreg + base: $b + - class: greg + base: $b + lanes: 1 # Scalar, darn it! + - class: immediate + immOffset: 0 + immMax: 3 + name: index + out: + - *t +- go: SetElem + asm: "VPINSRQ" in: - &t class: vreg @@ -8,15 +53,15 @@ base: $b - class: greg base: $b lanes: 1 # Scalar, darn it! - - &imm - class: immediate + - class: immediate immOffset: 0 + immMax: 1 name: index out: - *t - go: SetElem - asm: "VPINSR[DQ]" + asm: "VPINSRD" in: - &t class: vreg @@ -26,9 +71,26 @@ - class: greg base: int OverwriteBase: float lanes: 1 # Scalar, darn it! - - &imm - class: immediate + - class: immediate immOffset: 0 + immMax: 3 + name: index + out: + - *t +- go: SetElem + asm: "VPINSRQ" + in: + - &t + class: vreg + base: int + OverwriteBase: float + - class: greg + base: int + OverwriteBase: float + lanes: 1 # Scalar, darn it! + - class: immediate + immOffset: 0 + immMax: 1 name: index out: - *t @@ -36,11 +98,14 @@ - go: GetElem # VPEXTRB 128 bit asm: VPEXTRB in: - - bits: 128 - base: $b - class: vreg - elemBits: 8 - - *imm + - bits: 128 + base: $b + class: vreg + elemBits: 8 + - class: immediate + immOffset: 0 + immMax: 15 + name: index out: - bits: 32 base: $b @@ -50,11 +115,14 @@ - go: GetElem # VPEXTRW 128 bit asm: VPEXTRW in: - - bits: 128 - base: $b - class: vreg - elemBits: 16 - - *imm + - bits: 128 + base: $b + class: vreg + elemBits: 16 + - class: immediate + immOffset: 0 + immMax: 7 + name: index out: - bits: 32 base: $b @@ -62,25 +130,63 @@ class: greg overwriteBits: 16 # The XED data specification is wrong, correct it to the right bits - go: GetElem - asm: "VPEXTR[DQ]" + asm: "VPEXTRD" + in: + - class: vreg + base: $b + elemBits: $e + - class: immediate + immOffset: 0 + immMax: 3 + name: index + out: + - class: greg + base: $b + bits: $e + +- go: GetElem + asm: "VPEXTRQ" in: - class: vreg base: $b elemBits: $e - - *imm + - class: immediate + immOffset: 0 + immMax: 1 + name: index out: - class: greg base: $b bits: $e - go: GetElem - asm: "VPEXTR[DQ]" + asm: "VPEXTRD" in: - class: vreg base: int elemBits: $e OverwriteBase: float - - *imm + - class: immediate + immOffset: 0 + immMax: 3 + name: index + out: + - class: greg + base: int + bits: $e + OverwriteBase: float + +- go: GetElem + asm: "VPEXTRQ" + in: + - class: vreg + base: int + elemBits: $e + OverwriteBase: float + - class: immediate + immOffset: 0 + immMax: 1 + name: index out: - class: greg base: int diff --git a/src/simd/archsimd/internal/simd_test/simd_test.go b/src/simd/archsimd/internal/simd_test/simd_test.go index e1b67dbcb87026dea977155ac71a0d78dbf60df5..b23ba1341872058d4895f23aa17107a26b155493 100644 --- a/src/simd/archsimd/internal/simd_test/simd_test.go +++ b/src/simd/archsimd/internal/simd_test/simd_test.go @@ -244,6 +244,82 @@ } } +var seventeen = uint8(17) + +func TestSlicesInt8GetElem16(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Logf("Saw EXPECTED panic %v", r) + } else { + t.Errorf("Did not see expected panic") + } + }() + a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + v := archsimd.LoadInt8x16(a) + e := v.GetElem(seventeen - 1) + t.Errorf("Should have panicked, e=%v", e) +} + +func TestSlicesInt8GetElem16const(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Logf("Saw EXPECTED panic %v", r) + } else { + t.Errorf("Did not see expected panic") + } + }() + a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + v := archsimd.LoadInt8x16(a) + e := v.GetElem(16) + t.Errorf("Should have panicked, e=%v", e) +} + +func TestSlicesInt8GetElem15(t *testing.T) { + a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + v := archsimd.LoadInt8x16(a) + e := v.GetElem(seventeen - 2) + if e != a[15] { + t.Errorf("GetElem(15) = %d != a[15] = %d", e, a[15]) + } +} + +func TestSlicesInt8GetElem15const(t *testing.T) { + a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + v := archsimd.LoadInt8x16(a) + e := v.GetElem(15) + if e != a[15] { + t.Errorf("GetElem(15) = %d != a[15] = %d", e, a[15]) + } +} + +func TestSlicesInt8SetElem17(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Logf("Saw EXPECTED panic %v", r) + } else { + t.Errorf("Did not see expected panic") + } + }() + a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + v := archsimd.LoadInt8x16(a) + e := v.SetElem(seventeen, 18).GetElem(2) + t.Errorf("Should have panicked, e=%v", e) +} + +func TestSlicesInt8SetElem17const(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Logf("Saw EXPECTED panic %v", r) + } else { + t.Errorf("Did not see expected panic") + } + }() + a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + v := archsimd.LoadInt8x16(a) + e := v.SetElem(17, 18).GetElem(2) + t.Errorf("Should have panicked, e=%v", e) +} + func TestSlicesInt8TooShortLoad(t *testing.T) { if !archsimd.X86.AVX2() { t.Skip("Test requires X86.AVX2, not available on this hardware") diff --git a/src/simd/endianness_test.go b/src/simd/endianness_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f1e7030e5354fb6402552de2c0c493d30ba1912e --- /dev/null +++ b/src/simd/endianness_test.go @@ -0,0 +1,128 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.simd + +package simd_test + +import ( + "simd" + "testing" +) + +func assertEQ[T number](t *testing.T, x, y T, what string) { + t.Helper() + if x != y { + t.Errorf("%v != %v, %s", x, y, what) + } +} + +func makeSlice[T number](l int) []T { + x := make([]T, l) + var i T + for ; int(i) < len(x); i++ { + x[int(i)] = i + 1 + } + return x +} + +func TestLen(t *testing.T) { + var U64s simd.Uint64s + var U32s simd.Uint32s + var U16s simd.Uint16s + var U8s simd.Uint8s + + assertEQ(t, 2*U64s.Len(), U32s.Len(), "2*U64s.Len()==U32s.Len()") + assertEQ(t, 2*U32s.Len(), U16s.Len(), "2*U32.Len()==U16s.Len()") + assertEQ(t, 2*U16s.Len(), U8s.Len(), "2*U16s.Len()==U8s.Len()") +} + +func getElem64(x simd.Uint64s, i int) uint64 { + s := make([]uint64, x.Len()) + x.Store(s) + return s[i] +} + +func getElem32(x simd.Uint32s, i int) uint32 { + s := make([]uint32, x.Len()) + x.Store(s) + return s[i] +} + +func getElem16(x simd.Uint16s, i int) uint16 { + s := make([]uint16, x.Len()) + x.Store(s) + return s[i] +} + +func getElem8(x simd.Uint8s, i int) uint8 { + s := make([]uint8, x.Len()) + x.Store(s) + return s[i] +} + +func TestEndian(t *testing.T) { + var U64s simd.Uint64s + var U32s simd.Uint32s + var U16s simd.Uint16s + var U8s simd.Uint8s + + u64s := makeSlice[uint64](U64s.Len()) + u32s := makeSlice[uint32](U32s.Len()) + u16s := makeSlice[uint16](U16s.Len()) + u8s := makeSlice[uint8](U8s.Len()) + + U64s = simd.LoadUint64s(u64s) + U32s = simd.LoadUint32s(u32s) + U16s = simd.LoadUint16s(u16s) + U8s = simd.LoadUint8s(u8s) + + assertEQ(t, getElem64(U64s, 1), 2, "U64s[1] == 2") + assertEQ(t, getElem32(U32s, 1), 2, "U32s[1] == 2") + assertEQ(t, getElem16(U16s, 1), 2, "U16s[1] == 2") + assertEQ(t, getElem8(U8s, 1), 2, "U8s[1] == 2") +} + +func TestReshape(t *testing.T) { + var U64s simd.Uint64s + var U32s simd.Uint32s + var U16s simd.Uint16s + var U8s simd.Uint8s + + u64s := makeSlice[uint64](U64s.Len()) + u32s := makeSlice[uint32](U32s.Len()) + u16s := makeSlice[uint16](U16s.Len()) + u8s := makeSlice[uint8](U8s.Len()) + + U64s = simd.LoadUint64s(u64s) + U32s = simd.LoadUint32s(u32s) + U16s = simd.LoadUint16s(u16s) + U8s = simd.LoadUint8s(u8s) + + assertEQ(t, getElem8(U8s, 0), 1, "U8s[0] == 1") + assertEQ(t, getElem8(U16s.ReshapeToUint8s(), 0), 1, "U16s.ReshapeToUint8s()[0] == 1") + assertEQ(t, getElem8(U32s.ReshapeToUint8s(), 0), 1, "U32s.ReshapeToUint8s()[0] == 1") + assertEQ(t, getElem8(U64s.ReshapeToUint8s(), 0), 1, "U64s.ReshapeToUint8s()[0] == 1") + + assertEQ(t, getElem16(U8s.ReshapeToUint16s(), 0), 0x0201, "U8s.ReshapeToUint16s()[0] == 0x0201") + assertEQ(t, getElem16(U16s, 0), 1, "U16s[0] == 1") + assertEQ(t, getElem16(U32s.ReshapeToUint16s(), 0), 1, "U32s.ReshapeToUint16s()[0] == 1") + assertEQ(t, getElem16(U64s.ReshapeToUint16s(), 0), 1, "U64s.ReshapeToUint16s()[0] == 1") + + assertEQ(t, getElem32(U8s.ReshapeToUint32s(), 0), 0x04030201, "U8s.ReshapeToUint16s()[0] == 0x04030201") + assertEQ(t, getElem32(U16s.ReshapeToUint32s(), 0), 0x00020001, "U16s.ReshapeToUint32s()[0] == 0x00020001") + assertEQ(t, getElem32(U32s, 0), 1, "U32s[0] == 1") + assertEQ(t, getElem32(U64s.ReshapeToUint32s(), 0), 1, "U64s.ReshapeToUint32s()[0] == 1") + + assertEQ(t, getElem64(U8s.ReshapeToUint64s(), 0), 0x0807060504030201, "U8s.ReshapeToUint64s()[0] == 0x0807060504030201") + assertEQ(t, getElem64(U16s.ReshapeToUint64s(), 0), 0x0004000300020001, "U16s.ReshapeToUint64s()[0] == 0x0004000300020001") + assertEQ(t, getElem64(U32s.ReshapeToUint64s(), 0), 0x0000000200000001, "U32s.ReshapeToUint64s()[0] == 0x0000000200000001") + assertEQ(t, getElem64(U64s, 0), 1, "U64s[0] == 1") + + t.Logf("U8s=%v", U8s) + t.Logf("U16s=%v", U16s) + t.Logf("U32s=%v", U32s) + t.Logf("U64s=%v", U64s) + +} diff --git a/src/simd/example_test.go b/src/simd/example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b0530fc59bcb7e6cb60e021d6d637ec40c5919ab --- /dev/null +++ b/src/simd/example_test.go @@ -0,0 +1,158 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.simd + +package simd_test + +import ( + "fmt" + "simd" +) + +func ExampleInt8s_Add() { + // Initialize slice of 64 int8s (max vector size under AVX512). + in1 := []int8{ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + } + in2 := []int8{ + 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 100, 10, 10, 10, 16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + } + + // Load slices into vectors. + v1 := simd.LoadInt8s(in1) + v2 := simd.LoadInt8s(in2) + + // Add the vectors. + sum := v1.Add(v2) + + // Store the result back to a slice. + out := make([]int8, sum.Len()) + sum.Store(out) + + // Print the first 16 elements (minimum vector width across architectures). + fmt.Println(out[:16]) + // Output: [11 22 33 44 55 66 77 88 99 110 121 112 23 24 25 32] +} + +func ExampleInt8s_Masked() { + // Load vectors of 64 elements. + v1 := simd.LoadInt8s([]int8{ + 1, -2, 3, -4, 5, -6, 7, -8, 9, -10, 11, -12, 13, -14, 15, -16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }) + var v2 simd.Int8s // zero value + + // Create a mask where elements in v1 are greater than zero. + mask := v1.Greater(v2) + + // Keep elements of v1 where the mask is true, zero out elsewhere. + res := v1.Masked(mask) + + out := make([]int8, res.Len()) + res.Store(out) + + // Print the first 16 elements. + fmt.Println(out[:16]) + // Output: [1 0 3 0 5 0 7 0 9 0 11 0 13 0 15 0] +} + +func ExampleLoadInt8sPart() { + // Slice smaller than the full vector length. + s := []int8{1, 2, 3, 4, 5} + + // Load partial slice. + v, n := simd.LoadInt8sPart(s) + fmt.Printf("Loaded %d elements\n", n) + + // Store only the loaded elements. + out := make([]int8, n) + v.StorePart(out) + fmt.Println(out) + + // Output: + // Loaded 5 elements + // [1 2 3 4 5] +} + +func ExampleFloat32s_MulAdd() { + // Float32s on 512-bit vector has 16 elements. + v1 := simd.LoadFloat32s([]float32{ + 1.5, 2.5, 3.5, 4.5, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + }) + v2 := simd.LoadFloat32s([]float32{ + 2.0, 2.0, 2.0, 2.0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + }) + v3 := simd.LoadFloat32s([]float32{ + 1.0, 2.0, 3.0, 4.0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + }) + + // Perform element-wise v1 * v2 + v3. + res := v1.MulAdd(v2, v3) + + out := make([]float32, res.Len()) + res.Store(out) + + // Print the first 4 elements. + fmt.Println(out[:4]) + // Output: [4 7 10 13] +} + +func ExampleInt16s_ShiftAllLeft() { + // Int16s on 512-bit vector has 32 elements. + in := []int16{ + 1, 2, 4, 8, 16, 32, 64, 128, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + } + v := simd.LoadInt16s(in) + + // Shift all elements left by 2 bits. + res := v.ShiftAllLeft(2) + + out := make([]int16, res.Len()) + res.Store(out) + + // Print the first 8 elements. + fmt.Println(out[:8]) + // Output: [4 8 16 32 64 128 256 512] +} + +func ExampleInt16s_RotateAllLeft() { + // Int16s on 512-bit vector has 32 elements. + in := []int16{ + 0x00f0, 0x1234, 0, 0, 0, 0, 0, 0x7000, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + } + v := simd.LoadInt16s(in) + + // Rotate all elements left by 4 bits. + res := v.RotateAllLeft(4) + + out := make([]int16, res.Len()) + res.Store(out) + + fmt.Printf("%#04x\n", out[:8]) + // Output: [0x0f00 0x2341 0x0000 0x0000 0x0000 0x0000 0x0000 0x0007] +} diff --git a/src/simd/internal/bridge/import_hook.go b/src/simd/internal/bridge/import_hook.go new file mode 100644 index 0000000000000000000000000000000000000000..7d0b293d0b01c800ce90ca0c47575100a41a6013 --- /dev/null +++ b/src/simd/internal/bridge/import_hook.go @@ -0,0 +1,13 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.simd + +package bridge + +// ZeroSized is used as the definition for type _simd in package simd, to create +// a hard dependence between the two packages before code transformation. +type ZeroSized struct { + _ [0]func(*ZeroSized) *ZeroSized +} diff --git a/src/simd/internal/bridge/simd_emulated.go b/src/simd/internal/bridge/simd_emulated.go index 41b6417d16716d7879c7538b2e72192fc9ca2983..4a82d166c9956a4af2bc4ea3c0189e2b5969bf7c 100644 --- a/src/simd/internal/bridge/simd_emulated.go +++ b/src/simd/internal/bridge/simd_emulated.go @@ -2537,9 +2537,10 @@ // Add returns the element-wise sum of x and y. func (x Float32s) Add(y Float32s) Float32s { var res Float32s - for i := 0; i < 4; i++ { - res.set(i, x.get(i)+y.get(i)) - } + res.set(0, x.get(0)+y.get(0)) + res.set(1, x.get(1)+y.get(1)) + res.set(2, x.get(2)+y.get(2)) + res.set(3, x.get(3)+y.get(3)) return res } @@ -2667,18 +2668,23 @@ // Mul returns the element-wise product of x and y. func (x Float32s) Mul(y Float32s) Float32s { var res Float32s - for i := 0; i < 4; i++ { - res.set(i, x.get(i)*y.get(i)) - } + res.set(0, x.get(0)*y.get(0)) + res.set(1, x.get(1)*y.get(1)) + res.set(2, x.get(2)*y.get(2)) + res.set(3, x.get(3)*y.get(3)) + return res } // MulAdd returns x * y + z element-wise. func (x Float32s) MulAdd(y, z Float32s) Float32s { var res Float32s - for i := 0; i < 4; i++ { - res.set(i, x.get(i)*y.get(i)+z.get(i)) - } + + res.set(0, x.get(0)*y.get(0)+z.get(0)) + res.set(1, x.get(1)*y.get(1)+z.get(1)) + res.set(2, x.get(2)*y.get(2)+z.get(2)) + res.set(3, x.get(3)*y.get(3)+z.get(3)) + return res } diff --git a/src/simd/ip_test.go b/src/simd/ip_test.go index 90aea587dabd81c378165bf2d8322da0fb5ca644..b88c499cadcbb1be4ca539a37eed7b067650f125 100644 --- a/src/simd/ip_test.go +++ b/src/simd/ip_test.go @@ -8,10 +8,62 @@ package simd_test import ( "fmt" + "math/rand/v2" "simd" "testing" ) +func fill(x, y []float32) { + for i := range x { + x[i] = 2*rand.Float32() - 1 + y[i] = 2*rand.Float32() - 1 + } +} + +func checkErrors(b *testing.B, errors int) { + b.Helper() + if errors > 0 { + b.Logf("errors = %d", errors) + } +} + +// BenchmarkIPFMA is simd vector inner product computing using FMA. +func BenchmarkIPFMA(b *testing.B) { + x := make([]float32, ipBenchLen) + y := make([]float32, ipBenchLen) + + fill(x, y) + + ip0, _, _ := ipFMA(x, y) + + var errors int + for b.Loop() { + z, _, _ := ipFMA(x, y) + if z != ip0 { + errors++ + } + } + checkErrors(b, errors) +} + +func ipFMA(x, y []float32) (float32, int, bool) { + var a simd.Float32s + sumWidth := a.Len() * 32 + emulated := simd.Emulated() + var i int + for i = 0; i < len(x)-a.Len()+1; i += a.Len() { + u := simd.LoadFloat32s(x[i : i+a.Len()]) + v := simd.LoadFloat32s(y[i : i+a.Len()]) + a = u.MulAdd(v, a) + } + if i < len(x) { + a = first(simd.LoadFloat32sPart(x[i:])).MulAdd( + first(simd.LoadFloat32sPart(y[i:])), a) + } + + return sum(a), sumWidth, emulated +} + func TestIP(t *testing.T) { var a, b [50]float32 @@ -48,12 +100,269 @@ func first[T, U any](t T, u U) T { return t } +const ipBenchLen = 300000 + +// BenchmarkIP is simd vector inner product, vanilla transcription. +func BenchmarkIP(b *testing.B) { + x := make([]float32, ipBenchLen) + y := make([]float32, ipBenchLen) + + fill(x, y) + + ip0, _, _ := ip(x, y) + + var errors int + for b.Loop() { + z, _, _ := ip(x, y) + if z != ip0 { + errors++ + } + } + checkErrors(b, errors) +} + +// BenchmarkIPUnroll is simd vector inner product, unrolled 4x vector ops. +func BenchmarkIPUnroll(b *testing.B) { + x := make([]float32, ipBenchLen) + y := make([]float32, ipBenchLen) + + fill(x, y) + + ip0, _, _ := ipU(x, y) + + var errors int + for b.Loop() { + z, _, _ := ipU(x, y) + if z != ip0 { + errors++ + } + } + checkErrors(b, errors) +} + +// BenchmarkIPUnrollMore is simd vector inner product, unrolled 5x vector ops +func BenchmarkIPUnrollMore(b *testing.B) { + x := make([]float32, ipBenchLen) + y := make([]float32, ipBenchLen) + + fill(x, y) + + ip0, _, _ := ipUmore(x, y) + + var errors int + for b.Loop() { + z, _, _ := ipUmore(x, y) + if z != ip0 { + errors++ + } + } + checkErrors(b, errors) +} + +// ipNosimd computes inner product with serial +// addition order of the terms (to make the) +// check comparison turn out right. +func ipNosimd(x, y []float32) float32 { + var z float32 + for i, a := range x { + z += a * y[i] + } + return z +} + +// BenchmarkIPnosimd1 is serial, just a vanilla inner product. +func BenchmarkIPnosimd0(b *testing.B) { + x := make([]float32, ipBenchLen) + y := make([]float32, ipBenchLen) + + fill(x, y) + + ip0 := ipNosimd(x, y) + + var errors int + for b.Loop() { + var z float32 + for i, a := range x { + z += a * y[i] + } + if z != ip0 { + errors++ + } + } + checkErrors(b, errors) +} + +// BenchmarkIPnosimd1 is serial, but with a no-op subslice that +// makes it clear that x and y have the same length. +func BenchmarkIPnosimd1(b *testing.B) { + x := make([]float32, ipBenchLen) + y := make([]float32, ipBenchLen) + + fill(x, y) + + ip0 := ipNosimd(x, y) + + var errors int + for b.Loop() { + var z float32 + yy := y[:(len(x))] + for i, a := range x { + z += a * yy[i] + } + if z != ip0 { + errors++ + } + } + checkErrors(b, errors) +} + +// BenchmarkIPnosimdA is serial, rewritten to use arrays instead of slices, +// so no bounds checking, gosh darn it to heck. +func BenchmarkIPnosimdA(b *testing.B) { + var x, y [ipBenchLen]float32 + + fill(x[:], y[:]) + + ip0 := ipNosimd(x[:], y[:]) + + var errors int + for b.Loop() { + var z float32 + for i, a := range x { + z += a * y[i] + } + if z != ip0 { + errors++ + } + } + checkErrors(b, errors) +} + +var x, y [ipBenchLen]float32 +var ip0 float32 + +func initIp0() { + fill(x[:], y[:]) + ip0 = ipNosimd(x[:], y[:]) +} + +// BenchmarkIPnosimdAnotBloop is serial, rewritten to use arrays instead of slices, +// and using a classic iterated loop to see if b.Loop affects subscript inference, +// so no bounds checking, gosh darn it to heck, this time, for sure. +func BenchmarkIPnosimdAnotBloop(b *testing.B) { + if ip0 == 0 { + initIp0() + } + + var errors int + for range b.N { + var z float32 + for i, a := range x { + z += a * y[i] + } + if z != ip0 { + errors++ + } + } + checkErrors(b, errors) +} + func ip(x, y []float32) (float32, int, bool) { var a simd.Float32s sumWidth := a.Len() * 32 emulated := simd.Emulated() var i int for i = 0; i < len(x)-a.Len()+1; i += a.Len() { + u := simd.LoadFloat32s(x[i : i+a.Len()]) + v := simd.LoadFloat32s(y[i : i+a.Len()]) + a = a.Add(u.Mul(v)) + } + if i < len(x) { + a = a.Add(first(simd.LoadFloat32sPart(x[i:])). + Mul(first(simd.LoadFloat32sPart(y[i:])))) + } + + return sum(a), sumWidth, emulated +} + +func ipU(x, y []float32) (float32, int, bool) { + const U = 4 + var a, a0, a1, a2, a3 simd.Float32s + sumWidth := a.Len() * 32 + emulated := simd.Emulated() + var i int + for i = 0; i < len(x)-U*a.Len()+1; i += U * a.Len() { + i0 := i + i1 := i + a.Len() + i2 := i + 2*a.Len() + i3 := i + 3*a.Len() + + u := simd.LoadFloat32s(x[i0 : i0+a.Len()]) + v := simd.LoadFloat32s(y[i0 : i0+a.Len()]) + a0 = a0.Add(u.Mul(v)) + + u = simd.LoadFloat32s(x[i1 : i1+a.Len()]) + v = simd.LoadFloat32s(y[i1 : i1+a.Len()]) + a1 = a1.Add(u.Mul(v)) + + u = simd.LoadFloat32s(x[i2 : i2+a.Len()]) + v = simd.LoadFloat32s(y[i2 : i2+a.Len()]) + a2 = a2.Add(u.Mul(v)) + + u = simd.LoadFloat32s(x[i3 : i3+a.Len()]) + v = simd.LoadFloat32s(y[i3 : i3+a.Len()]) + a3 = a3.Add(u.Mul(v)) + } + a = a0.Add(a1).Add(a2.Add(a3)) + for ; i < len(x)-a.Len()+1; i += a.Len() { + u := simd.LoadFloat32s(x[i : i+a.Len()]) + v := simd.LoadFloat32s(y[i : i+a.Len()]) + a = a.Add(u.Mul(v)) + } + if i < len(x) { + a = a.Add(first(simd.LoadFloat32sPart(x[i:])). + Mul(first(simd.LoadFloat32sPart(y[i:])))) + } + + return sum(a), sumWidth, emulated +} + +func ipUmore(x, y []float32) (float32, int, bool) { + const U = 5 + var a, a0, a1, a2, a3, a4 simd.Float32s + sumWidth := a.Len() * 32 + emulated := simd.Emulated() + var i int + for i = 0; i < len(x)-U*a.Len()+1; i += U * a.Len() { + i0 := i + i1 := i + a.Len() + i2 := i + 2*a.Len() + i3 := i + 3*a.Len() + i4 := i + 4*a.Len() + + u := simd.LoadFloat32s(x[i0 : i0+a.Len()]) + v := simd.LoadFloat32s(y[i0 : i0+a.Len()]) + a0 = a0.Add(u.Mul(v)) + + u = simd.LoadFloat32s(x[i1 : i1+a.Len()]) + v = simd.LoadFloat32s(y[i1 : i1+a.Len()]) + a1 = a1.Add(u.Mul(v)) + + u = simd.LoadFloat32s(x[i2 : i2+a.Len()]) + v = simd.LoadFloat32s(y[i2 : i2+a.Len()]) + a2 = a2.Add(u.Mul(v)) + + u = simd.LoadFloat32s(x[i3 : i3+a.Len()]) + v = simd.LoadFloat32s(y[i3 : i3+a.Len()]) + a3 = a3.Add(u.Mul(v)) + + u = simd.LoadFloat32s(x[i4 : i4+a.Len()]) + v = simd.LoadFloat32s(y[i4 : i4+a.Len()]) + a4 = a4.Add(u.Mul(v)) + } + a = a0.Add(a1).Add(a2.Add(a3)).Add(a4) + + for ; i < len(x)-a.Len()+1; i += a.Len() { u := simd.LoadFloat32s(x[i : i+a.Len()]) v := simd.LoadFloat32s(y[i : i+a.Len()]) a = a.Add(u.Mul(v)) diff --git a/src/simd/simd.go b/src/simd/simd_stubs.go rename from src/simd/simd.go rename to src/simd/simd_stubs.go index 745c0a05682d4b5d295c112c1324c1713411b414..6cb59214fe8b4bf3a5131a82a3f3c188e41e7579 100644 --- a/src/simd/simd.go +++ b/src/simd/simd_stubs.go @@ -6,17 +6,6 @@ // Computed intersection of methods for supported SIMD architectures and vector widths package simd -// internal SIMD marker. -type _simd struct { - _ [0]func(*_simd) *_simd -} - -// Int8s represents a vector of 8-bit signed integers. -type Int8s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} - // LoadInt8 loads a slice of int8 into an Int8s vector. func LoadInt8s([]int8) Int8s @@ -113,12 +102,6 @@ // Xor returns the bitwise XOR of x and y. func (x Int8s) Xor(y Int8s) Int8s -// Int16s represents a vector of 16-bit signed integers. -type Int16s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} - // LoadInt16 loads a slice of int16 into an Int16s vector. func LoadInt16s([]int16) Int16s @@ -226,12 +209,6 @@ func (x Int16s) ToMask() (to Mask16s) // Xor returns the bitwise XOR of x and y. func (x Int16s) Xor(y Int16s) Int16s - -// Int32s represents a vector of 32-bit signed integers. -type Int32s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} // LoadInt32 loads a slice of int32 into an Int32s vector. func LoadInt32s([]int32) Int32s @@ -338,12 +315,6 @@ // Xor returns the bitwise XOR of x and y. func (x Int32s) Xor(y Int32s) Int32s -// Int64s represents a vector of 64-bit signed integers. -type Int64s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} - // LoadInt64 loads a slice of int64 into an Int64s vector. func LoadInt64s([]int64) Int64s @@ -431,12 +402,6 @@ // Xor returns the bitwise XOR of x and y. func (x Int64s) Xor(y Int64s) Int64s -// Uint8s represents a vector of 8-bit unsigned integers. -type Uint8s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} - // LoadUint8 loads a slice of uint8 into an Uint8s vector. func LoadUint8s([]uint8) Uint8s @@ -523,12 +488,6 @@ func (x Uint8s) SubSaturated(y Uint8s) Uint8s // Xor returns the bitwise XOR of x and y. func (x Uint8s) Xor(y Uint8s) Uint8s - -// Uint16s represents a vector of 16-bit unsigned integers. -type Uint16s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} // LoadUint16 loads a slice of uint16 into an Uint16s vector. func LoadUint16s([]uint16) Uint16s @@ -641,12 +600,6 @@ // Xor returns the bitwise XOR of x and y. func (x Uint16s) Xor(y Uint16s) Uint16s -// Uint32s represents a vector of 32-bit unsigned integers. -type Uint32s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} - // LoadUint32 loads a slice of uint32 into an Uint32s vector. func LoadUint32s([]uint32) Uint32s @@ -751,12 +704,6 @@ func (x Uint32s) Sub(y Uint32s) Uint32s // Xor returns the bitwise XOR of x and y. func (x Uint32s) Xor(y Uint32s) Uint32s - -// Uint64s represents a vector of 64-bit unsigned integers. -type Uint64s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} // LoadUint64 loads a slice of uint64 into an Uint64s vector. func LoadUint64s([]uint64) Uint64s @@ -886,12 +833,6 @@ // Xor returns the bitwise XOR of x and y. func (x Uint64s) Xor(y Uint64s) Uint64s -// Float32s represents a vector of 32-bit floating-point numbers. -type Float32s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} - // LoadFloat32 loads a slice of float32 into an Float32s vector. func LoadFloat32s([]float32) Float32s @@ -973,12 +914,6 @@ // ToBits reinterprets the vector bits as an unsigned integer vector. func (x Float32s) ToBits() Uint32s -// Float64s represents a vector of 64-bit floating-point numbers. -type Float64s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} - // LoadFloat64 loads a slice of float64 into an Float64s vector. func LoadFloat64s([]float64) Float64s @@ -1057,12 +992,6 @@ // ToBits reinterprets the vector bits as an unsigned integer vector. func (x Float64s) ToBits() Uint64s -// Mask8s represents a boolean mask for Int8s/Uint8s vectors. -type Mask8s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} - // And returns the bitwise AND of x and y. func (x Mask8s) And(y Mask8s) Mask8s @@ -1075,12 +1004,6 @@ // ToInt8s converts the mask to an Int8s vector. func (x Mask8s) ToInt8s() (to Int8s) -// Mask16s represents a boolean mask for Int16s/Uint16s vectors. -type Mask16s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} - // And returns the bitwise AND of x and y. func (x Mask16s) And(y Mask16s) Mask16s @@ -1093,12 +1016,6 @@ // ToInt16s converts the mask to an Int16s vector. func (x Mask16s) ToInt16s() (to Int16s) -// Mask32s represents a boolean mask for Int32s/Uint32s vectors. -type Mask32s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} - // And returns the bitwise AND of x and y. func (x Mask32s) And(y Mask32s) Mask32s @@ -1110,12 +1027,6 @@ func (x Mask32s) String() string // ToInt32s converts the mask to an Int32s vector. func (x Mask32s) ToInt32s() (to Int32s) - -// Mask64s represents a boolean mask for Int64s/Uint64s vectors. -type Mask64s struct { - _ _simd - atLeast [2]uint64 // the actual vector size may be larger. -} // And returns the bitwise AND of x and y. func (x Mask64s) And(y Mask64s) Mask64s diff --git a/src/simd/simd_emulated.go b/src/simd/simd_emulated.go index 1ad562ee7c0b66e0c8c2e028fcf08278b265be7e..d6d560794b2b6a21de61c5768c9689ba5ea05f3e 100644 --- a/src/simd/simd_emulated.go +++ b/src/simd/simd_emulated.go @@ -32,16 +32,6 @@ func HasHardwareCarrylessMultiply() bool { return false } -type _simd struct { - _ [0]func(*_simd) *_simd -} - -// Int8s represents a 128-bit vector of 16 int8 elements. -type Int8s struct { - _ _simd - a, b uint64 -} - // LoadInt8s loads a slice of int8 into an Int8s vector. func LoadInt8s(s []int8) Int8s { var a, b uint64 @@ -356,12 +346,6 @@ func (x Int8s) ToBits() Uint8s { return Uint8s{a: x.a, b: x.b} } -// Int16s represents a 128-bit vector of 8 int16 elements. -type Int16s struct { - _ _simd - a, b uint64 -} - // LoadInt16s loads a slice of int16 into an Int16s vector. func LoadInt16s(s []int16) Int16s { var a, b uint64 @@ -718,12 +702,6 @@ func (x Int16s) ToBits() Uint16s { return Uint16s{a: x.a, b: x.b} } -// Int32s represents a 128-bit vector of 4 int32 elements. -type Int32s struct { - _ _simd - a, b uint64 -} - // LoadInt32s loads a slice of int32 into an Int32s vector. func LoadInt32s(s []int32) Int32s { var a, b uint64 @@ -1055,12 +1033,6 @@ // ToBits reinterprets the vector bits as a Uint32s vector. func (x Int32s) ToBits() Uint32s { return Uint32s{a: x.a, b: x.b} -} - -// Int64s represents a 128-bit vector of 2 int64 elements. -type Int64s struct { - _ _simd - a, b uint64 } // LoadInt64s loads a slice of int64 into an Int64s vector. @@ -1294,12 +1266,6 @@ func (x Int64s) ToBits() Uint64s { return Uint64s{a: x.a, b: x.b} } -// Uint8s represents a 128-bit vector of 16 uint8 elements. -type Uint8s struct { - _ _simd - a, b uint64 -} - // LoadUint8s loads a slice of uint8 into an Uint8s vector. func LoadUint8s(s []uint8) Uint8s { var a, b uint64 @@ -1555,12 +1521,6 @@ // ReshapeToUint64s reinterprets the vector bits as a Uint64s vector. func (x Uint8s) ReshapeToUint64s() Uint64s { return Uint64s{a: x.a, b: x.b} -} - -// Uint16s represents a 128-bit vector of 8 uint16 elements. -type Uint16s struct { - _ _simd - a, b uint64 } // LoadUint16s loads a slice of uint16 into an Uint16s vector. @@ -1906,12 +1866,6 @@ func (x Uint16s) ReshapeToUint8s() Uint8s { return Uint8s{a: x.a, b: x.b} } -// Uint32s represents a 128-bit vector of 4 uint32 elements. -type Uint32s struct { - _ _simd - a, b uint64 -} - // LoadUint32s loads a slice of uint32 into an Uint32s vector. func LoadUint32s(s []uint32) Uint32s { var a, b uint64 @@ -2222,12 +2176,6 @@ func (x Uint32s) ReshapeToUint8s() Uint8s { return Uint8s{a: x.a, b: x.b} } -// Uint64s represents a 128-bit vector of 2 uint64 elements. -type Uint64s struct { - _ _simd - a, b uint64 -} - // LoadUint64s loads a slice of uint64 into an Uint64s vector. func LoadUint64s(s []uint64) Uint64s { var a, b uint64 @@ -2464,12 +2412,6 @@ func (x Uint64s) ReshapeToUint8s() Uint8s { return Uint8s{a: x.a, b: x.b} } -// Float32s represents a 128-bit vector of 4 float32 elements. -type Float32s struct { - _ _simd - a, b uint64 -} - // LoadFloat32s loads a slice of float32 into an Float32s vector. func LoadFloat32s(s []float32) Float32s { var a, b uint64 @@ -2537,9 +2479,10 @@ // Add returns the element-wise sum of x and y. func (x Float32s) Add(y Float32s) Float32s { var res Float32s - for i := 0; i < 4; i++ { - res.set(i, x.get(i)+y.get(i)) - } + res.set(0, x.get(0)+y.get(0)) + res.set(1, x.get(1)+y.get(1)) + res.set(2, x.get(2)+y.get(2)) + res.set(3, x.get(3)+y.get(3)) return res } @@ -2667,18 +2610,22 @@ // Mul returns the element-wise product of x and y. func (x Float32s) Mul(y Float32s) Float32s { var res Float32s - for i := 0; i < 4; i++ { - res.set(i, x.get(i)*y.get(i)) - } + res.set(0, x.get(0)*y.get(0)) + res.set(1, x.get(1)*y.get(1)) + res.set(2, x.get(2)*y.get(2)) + res.set(3, x.get(3)*y.get(3)) + return res } // MulAdd returns x * y + z element-wise. func (x Float32s) MulAdd(y, z Float32s) Float32s { var res Float32s - for i := 0; i < 4; i++ { - res.set(i, x.get(i)*y.get(i)+z.get(i)) - } + + res.set(0, x.get(0)*y.get(0)+z.get(0)) + res.set(1, x.get(1)*y.get(1)+z.get(1)) + res.set(2, x.get(2)*y.get(2)+z.get(2)) + res.set(3, x.get(3)*y.get(3)+z.get(3)) return res } @@ -2747,12 +2694,6 @@ func (x Float32s) ToBits() Uint32s { return Uint32s{a: x.a, b: x.b} } -// Float64s represents a 128-bit vector of 2 float64 elements. -type Float64s struct { - _ _simd - a, b uint64 -} - // LoadFloat64s loads a slice of float64 into an Float64s vector. func LoadFloat64s(s []float64) Float64s { var a, b uint64 @@ -3016,12 +2957,6 @@ func (x Float64s) ToBits() Uint64s { return Uint64s{a: x.a, b: x.b} } -// Mask8s represents a 128-bit mask vector for 16 int8/uint8 elements. -type Mask8s struct { - _ _simd - a, b uint64 -} - func (x *Mask8s) set(i int, v bool) { if v { if i < 8 { @@ -3052,12 +2987,6 @@ // ToInt8s converts the mask to an Int8s vector. func (x Mask8s) ToInt8s() Int8s { return Int8s{a: x.a, b: x.b} -} - -// Mask16s represents a 128-bit mask vector for 8 int16/uint16 elements. -type Mask16s struct { - _ _simd - a, b uint64 } func (x *Mask16s) set(i int, v bool) { @@ -3092,12 +3021,6 @@ func (x Mask16s) ToInt16s() Int16s { return Int16s{a: x.a, b: x.b} } -// Mask32s represents a 128-bit mask vector for 4 int32/uint32/float32 elements. -type Mask32s struct { - _ _simd - a, b uint64 -} - func (x *Mask32s) set(i int, v bool) { if v { if i < 2 { @@ -3128,12 +3051,6 @@ // ToInt32s converts the mask to an Int32s vector. func (x Mask32s) ToInt32s() Int32s { return Int32s{a: x.a, b: x.b} -} - -// Mask64s represents a 128-bit mask vector for 2 int64/uint64/float64 elements. -type Mask64s struct { - _ _simd - a, b uint64 } func (x *Mask64s) set(i int, v bool) { diff --git a/src/simd/simd_types.go b/src/simd/simd_types.go new file mode 100644 index 0000000000000000000000000000000000000000..fccaa2aed3807e76f5ede4ef9c7d96141ce736d7 --- /dev/null +++ b/src/simd/simd_types.go @@ -0,0 +1,96 @@ +// Code generated by 'go run -C $GOROOT/src/simd/archsimd/_gen/midway'; DO NOT EDIT. + +//go:build goexperiment.simd + +// Scalable vector types for rewriting and emulation + +package simd + +import "simd/internal/bridge" + +// internal SIMD marker, and hard dependence on simd/internal/bridge +type _simd bridge.ZeroSized + +// Int8s represents a vector of 8-bit signed integers. +type Int8s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Int16s represents a vector of 16-bit signed integers. +type Int16s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Int32s represents a vector of 32-bit signed integers. +type Int32s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Int64s represents a vector of 64-bit signed integers. +type Int64s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Uint8s represents a vector of 8-bit unsigned integers. +type Uint8s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Uint16s represents a vector of 16-bit unsigned integers. +type Uint16s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Uint32s represents a vector of 32-bit unsigned integers. +type Uint32s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Uint64s represents a vector of 64-bit unsigned integers. +type Uint64s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Float32s represents a vector of 32-bit floating-point numbers. +type Float32s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Float64s represents a vector of 64-bit floating-point numbers. +type Float64s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Mask8s represents a boolean mask for Int8s/Uint8s vectors. +type Mask8s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Mask16s represents a boolean mask for Int16s/Uint16s vectors. +type Mask16s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Mask32s represents a boolean mask for Int32s/Uint32s vectors. +type Mask32s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} + +// Mask64s represents a boolean mask for Int64s/Uint64s vectors. +type Mask64s struct { + _ _simd + a, b uint64 // the actual vector size may be larger. +} diff --git a/src/simd/sizeof_test.go b/src/simd/sizeof_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4202dc2939203483d702f354a50505fb18ff1e50 --- /dev/null +++ b/src/simd/sizeof_test.go @@ -0,0 +1,33 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.simd + +package simd_test + +import ( + "simd" + "simd/testdata" + "testing" + "unsafe" +) + +var v simd.Float32s + +func TestSizeof(t *testing.T) { + var f float32 + sv0 := int(unsafe.Sizeof(v)) + sv1 := v.Len() * int(unsafe.Sizeof(f)) + sV := int(unsafe.Sizeof(testdata.V)) + sF := int(unsafe.Sizeof(testdata.F())) + if sv0 != sv1 { + t.Errorf("sv0=%d and sv1=%d should be equal but are not", sv0, sv1) + } + if sF != sv1 { + t.Errorf("sF=%d and sv1=%d should be equal but are not", sF, sv1) + } + if sV != sv1 { + t.Errorf("sV=%d and sv1=%d should be equal but are not", sV, sv1) + } +} diff --git a/src/simd/testdata/mains/compiles.go b/src/simd/testdata/mains/compiles.go new file mode 100644 index 0000000000000000000000000000000000000000..1ce17c0f67fe219b4a44f5e2cfc0e1191b0e0a74 --- /dev/null +++ b/src/simd/testdata/mains/compiles.go @@ -0,0 +1,26 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.simd + +package main + +// For testing purposes, this SHOULD compile, because +// the "simd" type whose unsafe.Sizeof is used in a +// constant context, is from some other package whose +// name (but not path) happens to be "simd". + +import ( + "simd/testdata/simd" + "unsafe" +) + +var v [1]simd.HasConstantSize24 +var u [unsafe.Sizeof(v)]byte + +func main() { + if len(u) != 24 { + println("FAIL") + } +} diff --git a/src/simd/testdata/mains/errors.go b/src/simd/testdata/mains/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..9e72119f6eecbfe5c259267e1f81c7f70b252b33 --- /dev/null +++ b/src/simd/testdata/mains/errors.go @@ -0,0 +1,25 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.simd + +package main + +// For testing purposes, this should NOT compile, because +// it uses the unsafe.Sizeof of a "simd" type in a constant +// context (as an array size). + +import ( + "simd" + "unsafe" +) + +var v [1]simd.Int8s +var u [unsafe.Sizeof(v)]byte + +func main() { + if len(u) != 16 { + println("FAIL") + } +} diff --git a/src/simd/testdata/simd/size.go b/src/simd/testdata/simd/size.go new file mode 100644 index 0000000000000000000000000000000000000000..293c0149eeb85292ec9208ebc2cd8c5793226ac0 --- /dev/null +++ b/src/simd/testdata/simd/size.go @@ -0,0 +1,22 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.simd + +package simd + +// This is "simd/testdata/simd", not really "simd", hence not subject +// to the sizeof restrictions on simd types despite matching in every +// other detail. + +// _simd mimics the same type definition in the top-level "simd" package +// to test the non-constant-sizeof test. +type _simd struct { + _ [0]func(*_simd) *_simd +} + +type HasConstantSize24 struct { + _ _simd + a, b, c uint64 +} diff --git a/src/simd/testdata/v.go b/src/simd/testdata/v.go new file mode 100644 index 0000000000000000000000000000000000000000..1c6f7ddbb60fa2d6905cf7a982dcf3d1ef14a569 --- /dev/null +++ b/src/simd/testdata/v.go @@ -0,0 +1,21 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.simd + +package testdata + +// For testing purposes, F and V are exported simd types, +// and should have the proper (variable) unsafe.Sizeof + +import ( + "simd" +) + +var V simd.Float32s + +func F() simd.Float32s { + var x simd.Float32s + return x +} diff --git a/src/strings/builder.go b/src/strings/builder.go index 7ecef3176b2ce5755d63460001aac6ab9b583c4d..7f785661334ff9a252fec7e88d4a1d99ed77bfb6 100644 --- a/src/strings/builder.go +++ b/src/strings/builder.go @@ -99,7 +99,7 @@ return nil } // WriteRune appends the UTF-8 encoding of Unicode code point r to b's buffer. -// It returns the length of r and a nil error. +// It returns the number of bytes written and a nil error. func (b *Builder) WriteRune(r rune) (int, error) { b.copyCheck() n := len(b.buf) diff --git a/src/syscall/exec_libc2.go b/src/syscall/exec_libc2.go index 3de2c94c99b1c5760e5b071dd244d76148f2681c..50f8d42046bc27647463756597096cf3cd926cb0 100644 --- a/src/syscall/exec_libc2.go +++ b/src/syscall/exec_libc2.go @@ -48,8 +48,9 @@ // In the child, this function must not acquire any locks, because // they might have been locked at the time of the fork. This means // no rescheduling, no malloc calls, and no new stack segments. // For the same reason compiler does not race instrument it. -// The calls to rawSyscall are okay because they are assembly -// functions that do not grow the stack. +// The calls to rawSyscall are okay because they are nosplit +// functions that do not grow the stack and are not race +// instrumented (go:norace). // //go:norace func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err1 Errno) { diff --git a/src/syscall/syscall_darwin.go b/src/syscall/syscall_darwin.go index ca76cc2962b098399bcd5e5500e0d9edb59cd232..b65f69f978d81b06f3be2b907ecef89e530904a9 100644 --- a/src/syscall/syscall_darwin.go +++ b/src/syscall/syscall_darwin.go @@ -326,6 +326,7 @@ // errno return e if int32(r) is -1, else it returns 0. // //go:nosplit +//go:norace func errno(r uintptr, e Errno) Errno { if int32(r) == -1 { return e @@ -336,6 +337,7 @@ // errnoX return e if r is -1, else it returns 0. // //go:nosplit +//go:norace func errnoX(r uintptr, e Errno) Errno { if r == ^uintptr(0) { return e @@ -346,6 +348,7 @@ // errnoPtr return e if r is 0, else it returns 0. // //go:nosplit +//go:norace func errnoPtr(r uintptr, e Errno) Errno { if r == 0 { return e @@ -358,6 +361,22 @@ // golang.org/x/sys linknames the following syscalls. // Do not remove or change the type signature. +// N.B. For the Syscall functions below: +// +// //go:uintptrkeepalive because the uintptr argument may be converted pointers +// that need to be kept alive in the caller. +// +// //go:nosplit because stack copying does not account for uintptrkeepalive, so +// the stack must not grow. Stack copying cannot blindly assume that all +// uintptr arguments are pointers, because some values may look like pointers, +// but not really be pointers, and adjusting their value would break the call. +// +// //go:norace, on RawSyscall, to avoid race instrumentation if RawSyscall is +// called after fork, or from a signal handler. +// +// //go:linkname to ensure ABI wrappers are generated for external callers +// (notably x/sys/unix assembly). + //go:linkname syscall //go:nosplit //go:uintptrkeepalive @@ -409,6 +428,7 @@ } //go:linkname rawSyscall //go:nosplit +//go:norace //go:uintptrkeepalive func rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { r1, r2, err = rawsyscalln(fn, a1, a2, a3) @@ -417,6 +437,7 @@ } //go:linkname rawSyscall6 //go:nosplit +//go:norace //go:uintptrkeepalive func rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { r1, r2, err = rawsyscalln(fn, a1, a2, a3, a4, a5, a6) @@ -425,6 +446,7 @@ } //go:linkname rawSyscall9 //go:nosplit +//go:norace //go:uintptrkeepalive func rawSyscall9(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) { r1, r2, err = rawsyscalln(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9) diff --git a/test/codegen/append.go b/test/codegen/append.go index 8d0d1f02c81531162f8c908c9473185ea9071e14..2d4205c1a47be74a0f8ccef933657c9f7904186c 100644 --- a/test/codegen/append.go +++ b/test/codegen/append.go @@ -204,9 +204,10 @@ } for i, x := range r { println(i, x) } - // "range r" keeps pointer to backing store of r, there - // should not be `moveSliceNoCapNoScan` here, see #79909. - // amd64:-`.*moveSliceNoCapNoScan\b` + // "range r" keeps pointer to backing store of r. Our analysis + // is not yet precise enough to know it is OK in this case, so + // currently no `moveSliceNoCapNoScan` here. See #79909. + // amd64:-`.*moveSlice` return r } diff --git a/test/codegen/append_freegc.go b/test/codegen/append_freegc.go index 91f0901e357fa47d3cb1012ab49e1da15c9e7fc7..c222855648bc8adf2cc290e8e006f60ae378f51e 100644 --- a/test/codegen/append_freegc.go +++ b/test/codegen/append_freegc.go @@ -208,7 +208,10 @@ } for i, x := range r { println(i, x) } - // amd64:`.*moveSliceNoCapNoScan\b` + // "range r" keeps pointer to backing store of r. Our analysis + // is not yet precise enough to know it is OK in this case, so + // currently no `moveSliceNoCapNoScan` here. See #79909. + // amd64:-`.*moveSlice` return r } diff --git a/test/fixedbugs/issue19658.go b/test/fixedbugs/issue19658.go index 1ad575c269e021cb7681c27bbdaad04b800b524e..ff61a14f6a167b0a512a9c15744c44d0c66d04b8 100644 --- a/test/fixedbugs/issue19658.go +++ b/test/fixedbugs/issue19658.go @@ -47,7 +47,7 @@ Type string Input string Expect string }{ - {"", "nil", "panic: panic called with nil argument"}, + {"", "nil", "panic: runtime error: panic called with nil argument"}, {"errors.New", `"test"`, "panic: test"}, {"S", "S{}", "panic: s-stringer"}, {"byte", "8", "panic: 8"}, diff --git a/test/fixedbugs/issue80004.go b/test/fixedbugs/issue80004.go new file mode 100644 index 0000000000000000000000000000000000000000..59ec7f22c6edeeee928ccf38a6a14ac3be8057cd --- /dev/null +++ b/test/fixedbugs/issue80004.go @@ -0,0 +1,82 @@ +// run + +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "runtime" + "unsafe" +) + +// 1 of these is 28 bytes. +// When allocating 1 of them in a 32-byte size class, +// we accidentally write 2 pointer bitmasks, which +// marks the first 6 fields of the following object +// as pointers. +type E struct { + a [7]*byte +} + +// 32 bytes, and has pointers. +// First field is not a pointer, but will contain +// a badPtr value. +type Victim struct { + a uintptr + b [6]int32 + c *byte +} + +//go:noinline +func f(n int) []E { + var r []E + for range n { + r = append(r, E{}) + } + return r +} + +//go:noinline +func newVictim() *Victim { + return &Victim{a: badPtr} +} + +var badPtr uintptr + +func init() { + x := make([]byte, 1<<18-8) + sink = &x[0] + badPtr = uintptr(unsafe.Pointer(&x[len(x)-1])) + 5 +} + +func main() { + fs := make([]*Victim, 1000) + + // Allocate a bunch of Victims. + for i := range fs { + fs[i] = newVictim() + } + // Deallocate every other one. + for i := range fs { + if i%2 == 1 { + fs[i] = nil + } + } + runtime.GC() + + // Allocate Es in the deallocated slots. + // Those allocations will incorrectly set the + // pointer bit for the first field of all the + // Victims we allocated. + for range len(fs) / 2 { + _ = f(1) + } + runtime.GC() + + // Keep fs alive. + sink = &fs[0] +} + +var sink any diff --git a/test/genmeth.go b/test/genmeth.go index 85c6402e7c2be275bdf7890e2adadddd96845793..c37ea60d430132ceeb9baeb303cbcfb9259088a7 100644 --- a/test/genmeth.go +++ b/test/genmeth.go @@ -1,4 +1,4 @@ -// run -goexperiment genericmethods +// run // Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/genmeth1.go b/test/genmeth1.go index 2a2207b8df146bb527905aff2ce90ce005a7c31f..1963f5b4acdb5475481d9d54e4ed88b00fbbe086 100644 --- a/test/genmeth1.go +++ b/test/genmeth1.go @@ -1,4 +1,4 @@ -// run -goexperiment genericmethods +// run // Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/genmeth2.go b/test/genmeth2.go index db3118859c4c272040f4174fc6ab1583f6385fb6..a67181b275f8365084cb1a0d1f6e053534847365 100644 --- a/test/genmeth2.go +++ b/test/genmeth2.go @@ -1,4 +1,4 @@ -// run -goexperiment genericmethods +// run // Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style