diff --git a/src/cmd/compile/internal/staticinit/sched.go b/src/cmd/compile/internal/staticinit/sched.go index 62331392a58d81..42a2276414177e 100644 --- a/src/cmd/compile/internal/staticinit/sched.go +++ b/src/cmd/compile/internal/staticinit/sched.go @@ -546,7 +546,7 @@ func (s *Schedule) initplan(n ir.Node) { if a.Sym().IsBlank() { continue } - s.addvalue(p, a.Field.Offset, a.Value) + s.addvalue(p, typecheck.FieldOffset(n.Type(), a.Field), a.Value) } case ir.OMAPLIT: diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index 3b22d260bf4faf..27943f2902c85c 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -790,3 +790,21 @@ var slist []symlink type symlink struct { field *types.Field } + +// FieldOffset returns the offset of field f in t, +// including any implicit offsets from embedded fields. +func FieldOffset(t *types.Type, f *types.Field) int64 { + if f.Sym == nil { + return f.Offset + } + path, ambig := dotpath(f.Sym, t, nil, false) + if path == nil || ambig { + return f.Offset + } + var offset int64 + for _, d := range path { + offset += d.field.Offset + } + offset += f.Offset + return offset +} diff --git a/src/crypto/internal/cryptotest/wycheproof/_schema/schema_gen.go b/src/crypto/internal/cryptotest/wycheproof/_schema/schema_gen.go index 5ad885fb8b72dc..e9d26ed262e023 100644 --- a/src/crypto/internal/cryptotest/wycheproof/_schema/schema_gen.go +++ b/src/crypto/internal/cryptotest/wycheproof/_schema/schema_gen.go @@ -12,10 +12,13 @@ package main import ( + "bufio" + "fmt" "io/fs" "log" "os" "path/filepath" + "strings" "github.com/atombender/go-jsonschema/pkg/generator" "github.com/c2sp/wycheproof" @@ -88,4 +91,61 @@ func main() { if err := os.WriteFile(outFile, content, 0644); err != nil { log.Fatalf("error writing file %s: %v\n", outFile, err) } + + // Write a sibling file recording the Wycheproof module version that the + // generated schema.go was produced against. The wycheproof package uses + // this constant to fetch matching test vectors at runtime. We avoid reading + // go.sum at test time because it might not be available (e.g. if the test + // binary was copied to a remote machine). + version, err := wycheproofVersionFromSum("go.sum") + if err != nil { + log.Fatalf("extracting wycheproof version: %v", err) + } + const versionFile = "../schemaversion.go" + versionSrc := fmt.Sprintf(`// 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. + +// Code generated by _schema/schema_gen.go, DO NOT EDIT. + +package wycheproof + +// wycheproofVersion is the github.com/c2sp/wycheproof module version that +// schema.go was generated against. +const wycheproofVersion = %q +`, version) + if err := os.WriteFile(versionFile, []byte(versionSrc), 0644); err != nil { + log.Fatalf("writing %s: %v", versionFile, err) + } +} + +// wycheproofVersionFromSum returns the github.com/c2sp/wycheproof module +// version recorded in the given go.sum file. +func wycheproofVersionFromSum(path string) (string, error) { + f, err := os.Open(path) + if err != nil { + return "", err + } + defer f.Close() + + var version string + found := 0 + scanner := bufio.NewScanner(f) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + if len(fields) == 3 && fields[0] == "github.com/c2sp/wycheproof" { + version = strings.TrimSuffix(fields[1], "/go.mod") + found++ + } + } + if err := scanner.Err(); err != nil { + return "", err + } + switch { + case found == 0: + return "", fmt.Errorf("%s: missing github.com/c2sp/wycheproof entry", path) + case found > 2: + return "", fmt.Errorf("%s: %d github.com/c2sp/wycheproof entries (run 'go mod tidy')", path, found) + } + return version, nil } diff --git a/src/crypto/internal/cryptotest/wycheproof/schemaversion.go b/src/crypto/internal/cryptotest/wycheproof/schemaversion.go new file mode 100644 index 00000000000000..12088f043a228a --- /dev/null +++ b/src/crypto/internal/cryptotest/wycheproof/schemaversion.go @@ -0,0 +1,11 @@ +// 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. + +// Code generated by _schema/schema_gen.go, DO NOT EDIT. + +package wycheproof + +// wycheproofVersion is the github.com/c2sp/wycheproof module version that +// schema.go was generated against. +const wycheproofVersion = "v0.0.0-20260428174413-4d535535851f" diff --git a/src/crypto/internal/cryptotest/wycheproof/wycheproof.go b/src/crypto/internal/cryptotest/wycheproof/wycheproof.go index 93b83806a6986f..e75c91f13d0f76 100644 --- a/src/crypto/internal/cryptotest/wycheproof/wycheproof.go +++ b/src/crypto/internal/cryptotest/wycheproof/wycheproof.go @@ -8,7 +8,6 @@ package wycheproof import ( - "bufio" "crypto" "crypto/internal/cryptotest" "encoding/hex" @@ -18,7 +17,6 @@ import ( "os" "path" "reflect" - "strings" "testing" ) @@ -34,9 +32,11 @@ func LoadVectorFile(t *testing.T, filename string, value any) { // We want to avoid a dependency on c2sp/wycheproof or the schema generator // in this stdlib code, so we fetch the module at runtime and read the - // vector JSON from that module clone. + // vector JSON from that module clone. The version is pinned to whatever + // the _schema generator was last run against (see schemaversion.go), so + // the vectors match the generated schema.go. wycheproofDir := cryptotest.FetchModule( - t, "github.com/c2sp/wycheproof", findVersionFromSum(t)) + t, "github.com/c2sp/wycheproof", wycheproofVersion) content, err := os.ReadFile(path.Join(wycheproofDir, "testvectors_v1", filename)) if err != nil { @@ -49,44 +49,6 @@ func LoadVectorFile(t *testing.T, filename string, value any) { } } -// To make sure this code fetches the same module version as we used to -// generate the vendored schema.go we parse the _schema Go module's -// go.sum to find the Wycheproof version used. -func findVersionFromSum(t *testing.T) string { - testenv.MustHaveSource(t) - - goSumPath := path.Join( - testenv.GOROOT(t), - "src/crypto/internal/cryptotest/wycheproof/_schema/go.sum") - f, err := os.Open(goSumPath) - if err != nil { - t.Fatalf("_schema module go.sum read failed: %v", err) - } - defer f.Close() - - var version string - found := 0 - scanner := bufio.NewScanner(f) - for scanner.Scan() { - fields := strings.Fields(scanner.Text()) - if len(fields) == 3 && fields[0] == "github.com/c2sp/wycheproof" { - version = strings.TrimSuffix(fields[1], "/go.mod") - found++ - } - } - - // We expect 2 entries for a tidied sum file. - if found > 2 { - t.Fatalf( - "_schema module requires 'go tidy' - found %d wycheproof occurrences in go.sum", - found) - } else if found == 0 { - t.Fatal("_schema module go.sum missing wycheproof dependency") - } - - return version -} - // ShouldPass returns true if a test should pass informed by expected result // and flags. // diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index 8c5ec383f7b2f8..c13a9a6cc6a9bc 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -1076,17 +1076,29 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { return unsafe.Pointer(&zerobase) } - if sizeSpecializedMallocEnabled && size < uintptr(len(mallocNoScanTable)) { - if typ == nil || !typ.Pointers() { - if size >= maxTinySize { - return mallocNoScanTable[size](size, typ, needzero) + if sizeSpecializedMallocEnabled { + if size < uintptr(len(mallocNoScanTable)) { + if typ == nil || !typ.Pointers() { + if size >= maxTinySize { + return mallocNoScanTable[size](size, typ, needzero) + } + return mallocgcTinySC2(size, typ, needzero) + } else { + if !needzero { + throw("objects with pointers must be zeroed") + } + return mallocScanTable[size](size, typ, needzero) } - return mallocgcTinySC2(size, typ, needzero) - } else { - if !needzero { - throw("objects with pointers must be zeroed") + } + if heapBitsInSpan(size) { + noscan := typ == nil || !typ.Pointers() + sc := gc.SizeToSizeClass8[divRoundUp(size, gc.SmallSizeDiv)] + elemsize := uintptr(gc.SizeClassToSize[sc]) + spc := makeSpanClass(sc, noscan) + if noscan { + return mallocgcSmallNoScanSlowPath(size, typ, needzero, spc, elemsize) } - return mallocScanTable[size](size, typ, needzero) + return mallocgcSmallScanSlowPath(size, typ, needzero, spc, elemsize) } } @@ -1127,11 +1139,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { if !needzero { throw("objects with pointers must be zeroed") } - if heapBitsInSpan(size) { - x, elemsize = mallocgcSmallScanNoHeader(size, typ) - } else { - x, elemsize = mallocgcSmallScanHeader(size, typ) - } + x, elemsize = mallocgcSmallScanHeader(size, typ) } } else { x, elemsize = mallocgcLarge(size, typ, needzero) diff --git a/src/runtime/trace/subscribe.go b/src/runtime/trace/subscribe.go index a4d653dcaece92..90a7eac070ecf4 100644 --- a/src/runtime/trace/subscribe.go +++ b/src/runtime/trace/subscribe.go @@ -199,6 +199,3 @@ func (t *traceMultiplexer) startLocked() error { t.enabled.Store(true) return nil } - -//go:linkname runtime_readTrace -func runtime_readTrace() (buf []byte) diff --git a/test/complit2.go b/test/complit2.go index 43b23c023d0cba..bc1d915d171415 100644 --- a/test/complit2.go +++ b/test/complit2.go @@ -28,6 +28,7 @@ type C struct { func main() { eq(A{1, B{b: "foo"}}, A{a: 1, b: "foo"}) eq(A{B: B{C: C{c: "foo"}}}, A{c: "foo"}) + eq(x, A{B: B{b: "foo"}}) } func eq(x, y any) { @@ -35,3 +36,6 @@ func eq(x, y any) { panic(fmt.Sprintf("%v != %v", x, y)) } } + +// test global initializer +var x = A{b: "foo"} diff --git a/test/fixedbugs/issue18149.go b/test/fixedbugs/issue18149.go index 4b6eea6fd0998e..d8b3de86802c8b 100644 --- a/test/fixedbugs/issue18149.go +++ b/test/fixedbugs/issue18149.go @@ -17,7 +17,7 @@ import ( ) // Since go.dev/issue/70478, the compiler resolves a relative filename in a -// //line directive against the directory of the source file, so the expected +// line directive against the directory of the source file, so the expected // path may be a suffix of the actual filename rather than equal to it. // Accept either an exact match or the file as a path-component suffix. // Compiler-emitted paths are always slash-normalized (cmd/internal/objabi.AbsFile). @@ -36,7 +36,7 @@ func check(file string, line int) { func main() { //line /foo/bar.go:123 - check(`/foo/bar.go`, 123) + check("/foo/bar.go", 123) //line c:/foo/bar.go:987 - check(`c:/foo/bar.go`, 987) + check("c:/foo/bar.go", 987) } diff --git a/test/fixedbugs/issue22660.go b/test/fixedbugs/issue22660.go index 1d89805e9efb6d..3e495a29c44798 100644 --- a/test/fixedbugs/issue22660.go +++ b/test/fixedbugs/issue22660.go @@ -11,7 +11,6 @@ package main import ( "bytes" "fmt" - "io/ioutil" "log" "os" "os/exec" @@ -20,33 +19,33 @@ import ( ) func main() { - f, err := ioutil.TempFile("", "issue22660.go") + f, err := os.CreateTemp("", "issue22660.go") if err != nil { log.Fatal(err) } f.Close() defer os.Remove(f.Name()) - // path components from the //line directive must survive -trimpath and - // appear in error messages (since go.dev/issue/70478, as a path-component - // suffix of the resolved path, not as the bare prefix). + // path must appear in error messages even if we strip them with -trimpath path := filepath.Join("users", "xxx", "go") + filename := filepath.Join(path, "foo.go") var src bytes.Buffer - fmt.Fprintf(&src, "//line %s:1\n", filepath.Join(path, "foo.go")) - - if err := ioutil.WriteFile(f.Name(), src.Bytes(), 0660); err != nil { + fmt.Fprintf(&src, "//line %s:1\n", filename) + if err := os.WriteFile(f.Name(), src.Bytes(), 0660); err != nil { log.Fatal(err) } out, err := exec.Command("go", "tool", "compile", "-p=p", fmt.Sprintf("-trimpath=%s", path), f.Name()).CombinedOutput() if err == nil { - log.Fatalf("expected compiling %s to fail", f.Name()) + // The file only contains a line directive, w/o a package clause. + log.Fatalf("expected compiling %s to fail with syntax error", f.Name()) } - // After #70478 the resolved path is /users/xxx/go/foo.go, - // so the directive's components must appear as a path suffix. - want := filepath.Join(path, "foo.go") - if !strings.Contains(string(out), string(filepath.Separator)+want) { - log.Fatalf("expected path component (%s) in error message, got:\n%s", want, out) + // The error message position depends on the line directive. + // The resolved path is /filename, so the directive's components + // must appear as a path suffix in the error message's error position + // (go.dev/issue/70478). + if !strings.Contains(string(out), string(filepath.Separator)+filename) { + log.Fatalf("expected full path and filename (%s) in error message, got:\n%s", filename, out) } } diff --git a/test/fixedbugs/issue22662.go b/test/fixedbugs/issue22662.go index 16e8abfa0e0d02..d732485424f79c 100644 --- a/test/fixedbugs/issue22662.go +++ b/test/fixedbugs/issue22662.go @@ -16,7 +16,7 @@ import ( ) // Since go.dev/issue/70478, the compiler resolves a relative filename in a -// //line directive against the directory of the source file, so the expected +// line directive against the directory of the source file, so the expected // path may be a suffix of the actual filename rather than equal to it. // Accept either an exact match or the file as a path-component suffix. // Compiler-emitted paths are always slash-normalized (cmd/internal/objabi.AbsFile). diff --git a/test/fixedbugs/issue22662b.go b/test/fixedbugs/issue22662b.go index 15c28ec95ece59..eca486d451b217 100644 --- a/test/fixedbugs/issue22662b.go +++ b/test/fixedbugs/issue22662b.go @@ -11,7 +11,6 @@ package main import ( - "io/ioutil" "log" "os" "os/exec" @@ -38,7 +37,7 @@ var tests = []struct { } func main() { - f, err := ioutil.TempFile("", "issue22662b.go") + f, err := os.CreateTemp("", "issue22662b.go") if err != nil { log.Fatal(err) } @@ -46,7 +45,7 @@ func main() { defer os.Remove(f.Name()) for _, test := range tests { - if err := ioutil.WriteFile(f.Name(), []byte(test.src), 0660); err != nil { + if err := os.WriteFile(f.Name(), []byte(test.src), 0660); err != nil { log.Fatal(err) }