aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorCherry Mui <cherryyz@google.com>2025-09-25 13:33:58 -0400
committerCherry Mui <cherryyz@google.com>2025-09-25 13:33:59 -0400
commita693ae1e9aebac896f6634583dbdd1cd319f3983 (patch)
tree44ef04e84afe5ef8652222c5500ab6c779d09650 /src/cmd
parent5a78e1a4a1c79185e86b5c18efffba2a9b9d3739 (diff)
parentd70ad4e740e24b4b76961c4b56d698fa23668aa2 (diff)
downloadgo-a693ae1e9aebac896f6634583dbdd1cd319f3983.tar.xz
[dev.simd] all: merge master (d70ad4e) into dev.simd
Conflicts: - src/cmd/compile/internal/types2/stdlib_test.go - src/go/types/stdlib_test.go Merge List: + 2025-09-25 d70ad4e740 sync/atomic: correct Uintptr.Or return doc + 2025-09-25 d7abfe4f0d runtime: acquire/release C TSAN lock when calling cgo symbolizer/tracebacker + 2025-09-25 393d91aea0 cmd/fix: remove all functionality + 2025-09-25 6dceff8bad cmd/link: handle -w flag in external linking mode + 2025-09-25 76d088eb74 cmd/internal/obj/riscv: remove ACFLWSP/ACFSWSP and ACFLW/ACFSW + 2025-09-25 5225e9dc49 doc/next: document new image/jpeg DCT in release notes + 2025-09-25 81a83bba21 cmd: update x/tools@4df13e3 + 2025-09-25 6b32c613ca go/types: make typeset return an iterator + 2025-09-25 fbba930271 image/jpeg: replace fdct.go and idct.go with new implementation in dct.go + 2025-09-25 92e093467f image/jpeg: correct and test reference slowFDCT and slowIDCT + 2025-09-25 27c7bbc51c image/jpeg: prepare for new FDCT/IDCT implementations + 2025-09-24 f15cd63ec4 cmd/compile: don't rely on loop info when there are irreducible loops + 2025-09-24 371c1d2fcb cmd/internal/obj/riscv: add support for vector unit-stride fault-only-first load instructions + 2025-09-23 411c250d64 runtime: add specialized malloc functions for sizes up to 512 bytes + 2025-09-23 d7a38adf4c runtime: eliminate global span queue [green tea] + 2025-09-23 7bc1935db5 cmd/compile/internal: support new(expr) + 2025-09-23 eb78f13c9f doc/go_spec.html: document new(expr) + 2025-09-23 74cc463f9e go/token: add TestRemovedFileFileReturnsNil test + 2025-09-23 902dc27ae9 go/token: clear cache after grabbing the mutex in RemoveFile + 2025-09-23 a13d085a5b cmd/cgo: don't hardcode section name in TestNumberOfExportedFunctions + 2025-09-23 61bf26a9ee cmd/link: fix Macho-O X86_64_RELOC_SUBTRACTOR in internal linking + 2025-09-23 4b787c8c2b reflect: remove stale comment in unpackEface + 2025-09-23 3df27cd21a cmd/compile: fix typo in comment + 2025-09-23 684e8d3363 reflect: allocate memory in TypeAssert[I] only when the assertion succeeds + 2025-09-23 a5866ebe40 cmd/compile: prevent shapifying of pointer shape type + 2025-09-23 a27261c42f go/types,types2: allow new(expr) + 2025-09-23 e93f439ac4 runtime/cgo: retry when CreateThread fails with ERROR_ACCESS_DENIED + 2025-09-23 69e74b0aac runtime: deduplicate pMask resize code + 2025-09-23 fde10c4ce7 runtime: split gcMarkWorkAvailable into two separate conditions + 2025-09-23 5d040df092 runtime: use scan kernels in scanSpan [green tea] + 2025-09-23 7e0251bf58 runtime: don't report non-blocked goroutines as "(durable)" in stacks + 2025-09-23 22ac328856 cmd/link: make -w behavior consistent on Windows Change-Id: Id76b5a30a3b6f6669437f97e3320c9bca65a1e96
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/asm/internal/asm/testdata/riscv64.s10
-rw-r--r--src/cmd/asm/internal/asm/testdata/riscv64error.s1
-rw-r--r--src/cmd/asm/internal/asm/testdata/riscv64validation.s2
-rw-r--r--src/cmd/cgo/internal/testcshared/cshared_test.go116
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/main.go78
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/tracebackctxt_c.c70
-rw-r--r--src/cmd/cgo/internal/testsanitizers/tsan_test.go3
-rw-r--r--src/cmd/compile/internal/ir/node.go2
-rw-r--r--src/cmd/compile/internal/ir/type.go4
-rw-r--r--src/cmd/compile/internal/noder/doc.go6
-rw-r--r--src/cmd/compile/internal/noder/reader.go59
-rw-r--r--src/cmd/compile/internal/noder/writer.go8
-rw-r--r--src/cmd/compile/internal/ssa/tighten.go27
-rw-r--r--src/cmd/compile/internal/types2/builtins.go74
-rw-r--r--src/cmd/compile/internal/types2/index.go12
-rw-r--r--src/cmd/compile/internal/types2/signature.go7
-rw-r--r--src/cmd/compile/internal/types2/stdlib_test.go1
-rw-r--r--src/cmd/compile/internal/types2/typeparam.go10
-rw-r--r--src/cmd/compile/internal/types2/typeset.go16
-rw-r--r--src/cmd/compile/internal/types2/under.go57
-rw-r--r--src/cmd/compile/internal/types2/version.go1
-rw-r--r--src/cmd/compile/testdata/script/issue75461.txt78
-rw-r--r--src/cmd/fix/buildtag.go52
-rw-r--r--src/cmd/fix/buildtag_test.go34
-rw-r--r--src/cmd/fix/cftype.go25
-rw-r--r--src/cmd/fix/context.go17
-rw-r--r--src/cmd/fix/doc.go29
-rw-r--r--src/cmd/fix/egltype.go26
-rw-r--r--src/cmd/fix/fix.go552
-rw-r--r--src/cmd/fix/gotypes.go16
-rw-r--r--src/cmd/fix/import_test.go458
-rw-r--r--src/cmd/fix/jnitype.go17
-rw-r--r--src/cmd/fix/main.go246
-rw-r--r--src/cmd/fix/main_test.go166
-rw-r--r--src/cmd/fix/netipv6zone.go19
-rw-r--r--src/cmd/fix/printerconfig.go16
-rw-r--r--src/cmd/fix/typecheck.go814
-rw-r--r--src/cmd/go.mod2
-rw-r--r--src/cmd/go.sum4
-rw-r--r--src/cmd/go/go_test.go33
-rw-r--r--src/cmd/internal/obj/riscv/anames.go4
-rw-r--r--src/cmd/internal/obj/riscv/cpu.go4
-rw-r--r--src/cmd/internal/obj/riscv/obj.go8
-rw-r--r--src/cmd/link/dwarf_test.go62
-rw-r--r--src/cmd/link/internal/amd64/asm.go2
-rw-r--r--src/cmd/link/internal/ld/lib.go2
-rw-r--r--src/cmd/link/internal/ld/pe.go3
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go5
-rw-r--r--src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go38
-rw-r--r--src/cmd/vendor/modules.txt2
50 files changed, 579 insertions, 2719 deletions
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s
index 07a898465f..702b82223b 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64.s
@@ -549,6 +549,16 @@ start:
VSOXEI64V V3, V2, (X10) // a771250e
VSOXEI64V V3, V2, V0, (X10) // a771250c
+ // 31.7.7: Unit-stride Fault-Only-First Loads
+ VLE8FFV (X10), V8 // 07040503
+ VLE16FFV (X10), V8 // 07540503
+ VLE32FFV (X10), V8 // 07640503
+ VLE64FFV (X10), V8 // 07740503
+ VLE8FFV (X10), V0, V8 // 07040501
+ VLE16FFV (X10), V0, V8 // 07540501
+ VLE32FFV (X10), V0, V8 // 07640501
+ VLE64FFV (X10), V0, V8 // 07740501
+
// 31.7.8: Vector Load/Store Segment Instructions
// 31.7.8.1: Vector Unit-Stride Segment Loads and Stores
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64error.s b/src/cmd/asm/internal/asm/testdata/riscv64error.s
index 113b4ad2d6..3c09770d2a 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64error.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64error.s
@@ -73,6 +73,7 @@ TEXT errors(SB),$0
//
VSETIVLI X10, E32, M2, TA, MA, X12 // ERROR "expected immediate value"
VLE8V (X10), V1, V3 // ERROR "invalid vector mask register"
+ VLE8FFV (X10), V1, V3 // ERROR "invalid vector mask register"
VSE8V V3, V1, (X10) // ERROR "invalid vector mask register"
VLSE8V (X10), X10, V1, V3 // ERROR "invalid vector mask register"
VSSE8V V3, X11, V1, (X10) // ERROR "invalid vector mask register"
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64validation.s b/src/cmd/asm/internal/asm/testdata/riscv64validation.s
index eac1a992c3..6549765916 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64validation.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64validation.s
@@ -20,6 +20,8 @@ TEXT validation(SB),$0
VSETVL X10, X11 // ERROR "expected integer register in rs1 position"
VLE8V (X10), X10 // ERROR "expected vector register in vd position"
VLE8V (V1), V3 // ERROR "expected integer register in rs1 position"
+ VLE8FFV (X10), X10 // ERROR "expected vector register in vd position"
+ VLE8FFV (V1), V3 // ERROR "expected integer register in rs1 position"
VSE8V X10, (X10) // ERROR "expected vector register in vs1 position"
VSE8V V3, (V1) // ERROR "expected integer register in rd position"
VLSE8V (X10), V3 // ERROR "expected integer register in rs2 position"
diff --git a/src/cmd/cgo/internal/testcshared/cshared_test.go b/src/cmd/cgo/internal/testcshared/cshared_test.go
index 2c4d33f599..f1c30f8f9a 100644
--- a/src/cmd/cgo/internal/testcshared/cshared_test.go
+++ b/src/cmd/cgo/internal/testcshared/cshared_test.go
@@ -375,26 +375,7 @@ func TestExportedSymbols(t *testing.T) {
}
}
-func checkNumberOfExportedFunctionsWindows(t *testing.T, exportAllSymbols bool) {
- const prog = `
-package main
-
-import "C"
-
-//export GoFunc
-func GoFunc() {
- println(42)
-}
-
-//export GoFunc2
-func GoFunc2() {
- println(24)
-}
-
-func main() {
-}
-`
-
+func checkNumberOfExportedFunctionsWindows(t *testing.T, prog string, exportedFunctions int, wantAll bool) {
tmpdir := t.TempDir()
srcfile := filepath.Join(tmpdir, "test.go")
@@ -403,7 +384,7 @@ func main() {
t.Fatal(err)
}
argv := []string{"build", "-buildmode=c-shared"}
- if exportAllSymbols {
+ if wantAll {
argv = append(argv, "-ldflags", "-extldflags=-Wl,--export-all-symbols")
}
argv = append(argv, "-o", objfile, srcfile)
@@ -417,10 +398,36 @@ func main() {
t.Fatalf("pe.Open failed: %v", err)
}
defer f.Close()
- section := f.Section(".edata")
+
+ _, pe64 := f.OptionalHeader.(*pe.OptionalHeader64)
+ // grab the export data directory entry
+ var idd pe.DataDirectory
+ if pe64 {
+ idd = f.OptionalHeader.(*pe.OptionalHeader64).DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT]
+ } else {
+ idd = f.OptionalHeader.(*pe.OptionalHeader32).DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT]
+ }
+
+ // figure out which section contains the import directory table
+ var section *pe.Section
+ for _, s := range f.Sections {
+ if s.Offset == 0 {
+ continue
+ }
+ if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress-s.VirtualAddress < s.VirtualSize {
+ section = s
+ break
+ }
+ }
if section == nil {
- t.Skip(".edata section is not present")
+ t.Fatal("no section contains export directory")
}
+ d, err := section.Data()
+ if err != nil {
+ t.Fatal(err)
+ }
+ // seek to the virtual address specified in the export data directory
+ d = d[idd.VirtualAddress-section.VirtualAddress:]
// TODO: deduplicate this struct from cmd/link/internal/ld/pe.go
type IMAGE_EXPORT_DIRECTORY struct {
@@ -432,26 +439,22 @@ func main() {
_ [3]uint32
}
var e IMAGE_EXPORT_DIRECTORY
- if err := binary.Read(section.Open(), binary.LittleEndian, &e); err != nil {
+ if err := binary.Read(bytes.NewReader(d), binary.LittleEndian, &e); err != nil {
t.Fatalf("binary.Read failed: %v", err)
}
- // Only the two exported functions and _cgo_dummy_export should be exported
- expectedNumber := uint32(3)
-
- if exportAllSymbols {
- if e.NumberOfFunctions <= expectedNumber {
- t.Fatalf("missing exported functions: %v", e.NumberOfFunctions)
- }
- if e.NumberOfNames <= expectedNumber {
- t.Fatalf("missing exported names: %v", e.NumberOfNames)
+ // Only the two exported functions and _cgo_dummy_export should be exported.
+ // NumberOfNames is the number of functions exported with a unique name.
+ // NumberOfFunctions can be higher than that because it also counts
+ // functions exported only by ordinal, a unique number asigned by the linker,
+ // and linkers might add an unknown number of their own ordinal-only functions.
+ if wantAll {
+ if e.NumberOfNames <= uint32(exportedFunctions) {
+ t.Errorf("got %d exported names, want > %d", e.NumberOfNames, exportedFunctions)
}
} else {
- if e.NumberOfFunctions != expectedNumber {
- t.Fatalf("got %d exported functions; want %d", e.NumberOfFunctions, expectedNumber)
- }
- if e.NumberOfNames != expectedNumber {
- t.Fatalf("got %d exported names; want %d", e.NumberOfNames, expectedNumber)
+ if e.NumberOfNames > uint32(exportedFunctions) {
+ t.Errorf("got %d exported names, want <= %d", e.NumberOfNames, exportedFunctions)
}
}
}
@@ -467,11 +470,42 @@ func TestNumberOfExportedFunctions(t *testing.T) {
t.Parallel()
- t.Run("OnlyExported", func(t *testing.T) {
- checkNumberOfExportedFunctionsWindows(t, false)
+ const prog0 = `
+package main
+
+import "C"
+
+func main() {
+}
+`
+
+ const prog2 = `
+package main
+
+import "C"
+
+//export GoFunc
+func GoFunc() {
+ println(42)
+}
+
+//export GoFunc2
+func GoFunc2() {
+ println(24)
+}
+
+func main() {
+}
+`
+ // All programs export _cgo_dummy_export, so add 1 to the expected counts.
+ t.Run("OnlyExported/0", func(t *testing.T) {
+ checkNumberOfExportedFunctionsWindows(t, prog0, 0+1, false)
+ })
+ t.Run("OnlyExported/2", func(t *testing.T) {
+ checkNumberOfExportedFunctionsWindows(t, prog2, 2+1, false)
})
t.Run("All", func(t *testing.T) {
- checkNumberOfExportedFunctionsWindows(t, true)
+ checkNumberOfExportedFunctionsWindows(t, prog2, 2+1, true)
})
}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/main.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/main.go
new file mode 100644
index 0000000000..998a08ca53
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/main.go
@@ -0,0 +1,78 @@
+// Copyright 2025 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
+
+/*
+// Defined in tracebackctxt_c.c.
+extern void C1(void);
+extern void C2(void);
+extern void tcContext(void*);
+extern void tcTraceback(void*);
+extern void tcSymbolizer(void*);
+*/
+import "C"
+
+import (
+ "fmt"
+ "runtime"
+ "sync"
+ "unsafe"
+)
+
+// Regression test for https://go.dev/issue/73949. TSAN should not report races
+// on writes to the argument passed to the symbolizer function.
+//
+// Triggering this race requires calls to the symbolizer function with the same
+// argument pointer on multiple threads. The runtime passes a stack variable to
+// this function, so that means we need to get a single goroutine to execute on
+// two threads, calling the symbolizer function on each.
+//
+// runtime.CallersFrames / Next will call the symbolizer function (if there are
+// C frames). So the approach here is, with GOMAXPROCS=2, have 2 goroutines
+// that use CallersFrames over and over, both frequently calling Gosched in an
+// attempt to get picked up by the other P.
+
+var tracebackOK bool
+
+func main() {
+ runtime.GOMAXPROCS(2)
+ runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContext), unsafe.Pointer(C.tcSymbolizer))
+ C.C1()
+ if tracebackOK {
+ fmt.Println("OK")
+ }
+}
+
+//export G1
+func G1() {
+ C.C2()
+}
+
+//export G2
+func G2() {
+ pc := make([]uintptr, 32)
+ n := runtime.Callers(0, pc)
+
+ var wg sync.WaitGroup
+ for range 2 {
+ wg.Go(func() {
+ for range 1000 {
+ cf := runtime.CallersFrames(pc[:n])
+ var frames []runtime.Frame
+ for {
+ frame, more := cf.Next()
+ frames = append(frames, frame)
+ if !more {
+ break
+ }
+ }
+ runtime.Gosched()
+ }
+ })
+ }
+ wg.Wait()
+
+ tracebackOK = true
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/tracebackctxt_c.c b/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/tracebackctxt_c.c
new file mode 100644
index 0000000000..9ddaa4aaf2
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/tracebackctxt_c.c
@@ -0,0 +1,70 @@
+// Copyright 2025 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.
+
+// The C definitions for tracebackctxt.go. That file uses //export so
+// it can't put function definitions in the "C" import comment.
+
+#include <stdint.h>
+#include <stdio.h>
+
+// Functions exported from Go.
+extern void G1(void);
+extern void G2(void);
+
+void C1() {
+ G1();
+}
+
+void C2() {
+ G2();
+}
+
+struct cgoContextArg {
+ uintptr_t context;
+};
+
+struct cgoTracebackArg {
+ uintptr_t context;
+ uintptr_t sigContext;
+ uintptr_t* buf;
+ uintptr_t max;
+};
+
+struct cgoSymbolizerArg {
+ uintptr_t pc;
+ const char* file;
+ uintptr_t lineno;
+ const char* func;
+ uintptr_t entry;
+ uintptr_t more;
+ uintptr_t data;
+};
+
+void tcContext(void* parg) {
+ struct cgoContextArg* arg = (struct cgoContextArg*)(parg);
+ if (arg->context == 0) {
+ arg->context = 1;
+ }
+}
+
+void tcTraceback(void* parg) {
+ int base, i;
+ struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg);
+ if (arg->max < 1) {
+ return;
+ }
+ arg->buf[0] = 6; // Chosen by fair dice roll.
+}
+
+void tcSymbolizer(void *parg) {
+ struct cgoSymbolizerArg* arg = (struct cgoSymbolizerArg*)(parg);
+ if (arg->pc == 0) {
+ return;
+ }
+ // Report two lines per PC returned by traceback, to test more handling.
+ arg->more = arg->file == NULL;
+ arg->file = "tracebackctxt.go";
+ arg->func = "cFunction";
+ arg->lineno = arg->pc + (arg->more << 16);
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/tsan_test.go b/src/cmd/cgo/internal/testsanitizers/tsan_test.go
index 265c5e3605..589db2e6bc 100644
--- a/src/cmd/cgo/internal/testsanitizers/tsan_test.go
+++ b/src/cmd/cgo/internal/testsanitizers/tsan_test.go
@@ -56,6 +56,7 @@ func TestTSAN(t *testing.T) {
{src: "tsan13.go", needsRuntime: true},
{src: "tsan14.go", needsRuntime: true},
{src: "tsan15.go", needsRuntime: true},
+ {src: "tsan_tracebackctxt", needsRuntime: true}, // Subdirectory
}
for _, tc := range cases {
tc := tc
@@ -67,7 +68,7 @@ func TestTSAN(t *testing.T) {
defer dir.RemoveAll(t)
outPath := dir.Join(name)
- mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src)))
+ mustRun(t, config.goCmd("build", "-o", outPath, "./"+srcPath(tc.src)))
cmdArgs := []string{outPath}
if goos == "linux" {
diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go
index 003ec15de1..8c61bb6ed5 100644
--- a/src/cmd/compile/internal/ir/node.go
+++ b/src/cmd/compile/internal/ir/node.go
@@ -215,7 +215,7 @@ const (
ORSH // X >> Y
OAND // X & Y
OANDNOT // X &^ Y
- ONEW // new(X); corresponds to calls to new in source code
+ ONEW // new(X); corresponds to calls to new(T) in source code
ONOT // !X
OBITNOT // ^X
OPLUS // +X
diff --git a/src/cmd/compile/internal/ir/type.go b/src/cmd/compile/internal/ir/type.go
index 6daca856a6..0f44cf8d04 100644
--- a/src/cmd/compile/internal/ir/type.go
+++ b/src/cmd/compile/internal/ir/type.go
@@ -42,6 +42,10 @@ func TypeNode(t *types.Type) Node {
// A DynamicType represents a type expression whose exact type must be
// computed dynamically.
+//
+// TODO(adonovan): I think "dynamic" is a misnomer here; it's really a
+// type with free type parameters that needs to be instantiated to obtain
+// a ground type for which an rtype can exist.
type DynamicType struct {
miniExpr
diff --git a/src/cmd/compile/internal/noder/doc.go b/src/cmd/compile/internal/noder/doc.go
index a5d5533168..8eb67e92f0 100644
--- a/src/cmd/compile/internal/noder/doc.go
+++ b/src/cmd/compile/internal/noder/doc.go
@@ -87,7 +87,7 @@ constant for file bases and hence not encoded.
[ Sync ]
StringRef // the (absolute) file name for the base
Bool // true if a file base, else a line base
- // The below is ommitted for file bases.
+ // The below is omitted for file bases.
[ Pos
Uint64 // line
Uint64 ] // column
@@ -99,7 +99,7 @@ without a PosBase have no line or column.
Pos = [ Sync ]
Bool // true if the position has a base
- // The below is ommitted if the position has no base.
+ // The below is omitted if the position has no base.
[ Ref[PosBase]
Uint64 // line
Uint64 ] // column
@@ -125,7 +125,7 @@ packages. The below package paths have special meaning.
Pkg = RefTable
[ Sync ]
StringRef // path
- // The below is ommitted for the special package paths
+ // The below is omitted for the special package paths
// "builtin" and "unsafe".
[ StringRef // name
Imports ]
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index 3cbc7989a7..ca7c6bf151 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -49,9 +49,6 @@ type pkgReader struct {
// but bitwise inverted so we can detect if we're missing the entry
// or not.
newindex []index
-
- // indicates whether the data is reading during reshaping.
- reshaping bool
}
func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader {
@@ -119,10 +116,6 @@ type reader struct {
// find parameters/results.
funarghack bool
- // reshaping is used during reading exprReshape code, preventing
- // the reader from shapifying the re-shaped type.
- reshaping bool
-
// methodSym is the name of method's name, if reading a method.
// It's nil if reading a normal function or closure body.
methodSym *types.Sym
@@ -937,8 +930,19 @@ func shapify(targ *types.Type, basic bool) *types.Type {
// types, and discarding struct field names and tags. However, we'll
// need to start tracking how type parameters are actually used to
// implement some of these optimizations.
+ pointerShaping := basic && targ.IsPtr() && !targ.Elem().NotInHeap()
+ // The exception is when the type parameter is a pointer to a type
+ // which `Type.HasShape()` returns true, but `Type.IsShape()` returns
+ // false, like `*[]go.shape.T`. This is because the type parameter is
+ // used to instantiate a generic function inside another generic function.
+ // In this case, we want to keep the targ as-is, otherwise, we may lose the
+ // original type after `*[]go.shape.T` is shapified to `*go.shape.uint8`.
+ // See issue #54535, #71184.
+ if pointerShaping && !targ.Elem().IsShape() && targ.Elem().HasShape() {
+ return targ
+ }
under := targ.Underlying()
- if basic && targ.IsPtr() && !targ.Elem().NotInHeap() {
+ if pointerShaping {
under = types.NewPtr(types.Types[types.TUINT8])
}
@@ -1014,25 +1018,7 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx index, implicits, explicits
// arguments.
for i, targ := range dict.targs {
basic := r.Bool()
- isPointerShape := basic && targ.IsPtr() && !targ.Elem().NotInHeap()
- // We should not do shapify during the reshaping process, see #71184.
- // However, this only matters for shapify a pointer type, which will
- // lose the original underlying type.
- //
- // Example with a pointer type:
- //
- // - First, shapifying *[]T -> *uint8
- // - During the reshaping process, *uint8 is shapified to *go.shape.uint8
- // - This ends up with a different type with the original *[]T
- //
- // For a non-pointer type:
- //
- // - int -> go.shape.int
- // - go.shape.int -> go.shape.int
- //
- // We always end up with the identical type.
- canShapify := !pr.reshaping || !isPointerShape
- if dict.shaped && canShapify {
+ if dict.shaped {
dict.targs[i] = shapify(targ, basic)
}
}
@@ -2445,8 +2431,16 @@ func (r *reader) expr() (res ir.Node) {
case exprNew:
pos := r.pos()
- typ := r.exprType()
- return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ))
+ if r.Bool() {
+ // new(expr) -> tmp := expr; &tmp
+ x := r.expr()
+ var init ir.Nodes
+ addr := ir.NewAddrExpr(pos, r.tempCopy(pos, x, &init))
+ addr.SetInit(init)
+ return typecheck.Expr(addr)
+ }
+ // new(T)
+ return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, r.exprType()))
case exprSizeof:
return ir.NewUintptr(r.pos(), r.typ().Size())
@@ -2470,10 +2464,7 @@ func (r *reader) expr() (res ir.Node) {
case exprReshape:
typ := r.typ()
- old := r.reshaping
- r.reshaping = true
x := r.expr()
- r.reshaping = old
if types.IdenticalStrict(x.Type(), typ) {
return x
@@ -2596,10 +2587,7 @@ func (r *reader) funcInst(pos src.XPos) (wrapperFn, baseFn, dictPtr ir.Node) {
info := r.dict.subdicts[idx]
explicits := r.p.typListIdx(info.explicits, r.dict)
- old := r.p.reshaping
- r.p.reshaping = r.reshaping
baseFn = r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
- r.p.reshaping = old
// TODO(mdempsky): Is there a more robust way to get the
// dictionary pointer type here?
@@ -3259,6 +3247,7 @@ func (r *reader) exprType() ir.Node {
var rtype, itab ir.Node
if r.Bool() {
+ // non-empty interface
typ, rtype, _, _, itab = r.itab(pos)
if !typ.IsInterface() {
rtype = nil // TODO(mdempsky): Leave set?
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go
index 54e5f1ea5f..9c90d221c2 100644
--- a/src/cmd/compile/internal/noder/writer.go
+++ b/src/cmd/compile/internal/noder/writer.go
@@ -2035,10 +2035,16 @@ func (w *writer) expr(expr syntax.Expr) {
case "new":
assert(len(expr.ArgList) == 1)
assert(!expr.HasDots)
+ arg := expr.ArgList[0]
w.Code(exprNew)
w.pos(expr)
- w.exprType(nil, expr.ArgList[0])
+ tv := w.p.typeAndValue(arg)
+ if w.Bool(!tv.IsType()) {
+ w.expr(arg) // new(expr), go1.26
+ } else {
+ w.exprType(nil, arg) // new(T)
+ }
return
case "Sizeof":
diff --git a/src/cmd/compile/internal/ssa/tighten.go b/src/cmd/compile/internal/ssa/tighten.go
index 48efdb5609..b1f787e03b 100644
--- a/src/cmd/compile/internal/ssa/tighten.go
+++ b/src/cmd/compile/internal/ssa/tighten.go
@@ -123,18 +123,21 @@ func tighten(f *Func) {
// If the target location is inside a loop,
// move the target location up to just before the loop head.
- for _, b := range f.Blocks {
- origloop := loops.b2l[b.ID]
- for _, v := range b.Values {
- t := target[v.ID]
- if t == nil {
- continue
- }
- targetloop := loops.b2l[t.ID]
- for targetloop != nil && (origloop == nil || targetloop.depth > origloop.depth) {
- t = idom[targetloop.header.ID]
- target[v.ID] = t
- targetloop = loops.b2l[t.ID]
+ if !loops.hasIrreducible {
+ // Loop info might not be correct for irreducible loops. See issue 75569.
+ for _, b := range f.Blocks {
+ origloop := loops.b2l[b.ID]
+ for _, v := range b.Values {
+ t := target[v.ID]
+ if t == nil {
+ continue
+ }
+ targetloop := loops.b2l[t.ID]
+ for targetloop != nil && (origloop == nil || targetloop.depth > origloop.depth) {
+ t = idom[targetloop.header.ID]
+ target[v.ID] = t
+ targetloop = loops.b2l[t.ID]
+ }
}
}
}
diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go
index 4bb2135755..df207a2746 100644
--- a/src/cmd/compile/internal/types2/builtins.go
+++ b/src/cmd/compile/internal/types2/builtins.go
@@ -98,17 +98,17 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
y := args[1]
hasString := false
- typeset(y.typ, func(_, u Type) bool {
+ for _, u := range typeset(y.typ) {
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
- return true
- }
- if isString(u) {
+ // typeset ⊇ {[]byte}
+ } else if isString(u) {
+ // typeset ⊇ {string}
hasString = true
- return true
+ } else {
+ y = nil
+ break
}
- y = nil
- return false
- })
+ }
if y != nil && hasString {
// setting the signature also signals that we're done
sig = makeSig(x.typ, x.typ, y.typ)
@@ -368,16 +368,16 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
var special bool
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
special = true
- typeset(y.typ, func(_, u Type) bool {
+ for _, u := range typeset(y.typ) {
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
- return true
- }
- if isString(u) {
- return true
+ // typeset ⊇ {[]byte}
+ } else if isString(u) {
+ // typeset ⊇ {string}
+ } else {
+ special = false
+ break
}
- special = false
- return false
- })
+ }
}
// general case
@@ -636,11 +636,30 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
}
case _New:
- // new(T)
+ // new(T) or new(expr)
// (no argument evaluated yet)
- T := check.varType(argList[0])
- if !isValid(T) {
- return
+ arg := argList[0]
+ check.exprOrType(x, arg, true)
+ var T Type
+ switch x.mode {
+ case builtin:
+ check.errorf(x, UncalledBuiltin, "%s must be called", x)
+ x.mode = invalid
+ case typexpr:
+ // new(T)
+ T = x.typ
+ if !isValid(T) {
+ return
+ }
+ default:
+ // new(expr)
+ check.verifyVersionf(call.Fun, go1_26, "new(expr)")
+ T = Default(x.typ)
+ if T != x.typ {
+ // untyped constant: check for overflow.
+ check.assignment(x, T, "argument to new")
+ }
+ check.validVarType(arg, T)
}
x.mode = value
@@ -961,29 +980,22 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
// or a type error if x is not a slice (or a type set of slices).
func sliceElem(x *operand) (Type, *typeError) {
var E Type
- var err *typeError
- typeset(x.typ, func(_, u Type) bool {
+ for _, u := range typeset(x.typ) {
s, _ := u.(*Slice)
if s == nil {
if x.isNil() {
// Printing x in this case would just print "nil".
// Special case this so we can emphasize "untyped".
- err = typeErrorf("argument must be a slice; have untyped nil")
+ return nil, typeErrorf("argument must be a slice; have untyped nil")
} else {
- err = typeErrorf("argument must be a slice; have %s", x)
+ return nil, typeErrorf("argument must be a slice; have %s", x)
}
- return false
}
if E == nil {
E = s.elem
} else if !Identical(E, s.elem) {
- err = typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
- return false
+ return nil, typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
}
- return true
- })
- if err != nil {
- return nil, err
}
return E, nil
}
diff --git a/src/cmd/compile/internal/types2/index.go b/src/cmd/compile/internal/types2/index.go
index 80e8514168..7e16a87332 100644
--- a/src/cmd/compile/internal/types2/index.go
+++ b/src/cmd/compile/internal/types2/index.go
@@ -216,11 +216,11 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
// determine common underlying type cu
var ct, cu Type // type and respective common underlying type
var hasString bool
- typeset(x.typ, func(t, u Type) bool {
+ for t, u := range typeset(x.typ) {
if u == nil {
check.errorf(x, NonSliceableOperand, "cannot slice %s: no specific type in %s", x, x.typ)
cu = nil
- return false
+ break
}
// Treat strings like byte slices but remember that we saw a string.
@@ -232,18 +232,16 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
// If this is the first type we're seeing, we're done.
if cu == nil {
ct, cu = t, u
- return true
+ continue
}
// Otherwise, the current type must have the same underlying type as all previous types.
if !Identical(cu, u) {
check.errorf(x, NonSliceableOperand, "cannot slice %s: %s and %s have different underlying types", x, ct, t)
cu = nil
- return false
+ break
}
-
- return true
- })
+ }
if hasString {
// If we saw a string, proceed with string type,
// but don't go from untyped string to string.
diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go
index eaecb77af5..ea1cfd88cc 100644
--- a/src/cmd/compile/internal/types2/signature.go
+++ b/src/cmd/compile/internal/types2/signature.go
@@ -49,7 +49,7 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
}
last := params.At(n - 1).typ
var S *Slice
- typeset(last, func(t, _ Type) bool {
+ for t := range typeset(last) {
var s *Slice
if isString(t) {
s = NewSlice(universeByte)
@@ -60,10 +60,9 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
S = s
} else if !Identical(S, s) {
S = nil
- return false
+ break
}
- return true
- })
+ }
if S == nil {
panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
}
diff --git a/src/cmd/compile/internal/types2/stdlib_test.go b/src/cmd/compile/internal/types2/stdlib_test.go
index fc67397503..26d2eb23ab 100644
--- a/src/cmd/compile/internal/types2/stdlib_test.go
+++ b/src/cmd/compile/internal/types2/stdlib_test.go
@@ -360,6 +360,7 @@ func TestStdKen(t *testing.T) {
var excluded = map[string]bool{
"builtin": true,
"cmd/compile/internal/ssa/_gen": true,
+ "runtime/_mkmalloc": true,
"simd/_gen/simdgen": true,
"simd/_gen/unify": true,
}
diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go
index a04f928908..c60b5eb417 100644
--- a/src/cmd/compile/internal/types2/typeparam.go
+++ b/src/cmd/compile/internal/types2/typeparam.go
@@ -155,10 +155,10 @@ func (t *TypeParam) is(f func(*term) bool) bool {
return t.iface().typeSet().is(f)
}
-// typeset is an iterator over the (type/underlying type) pairs of the
+// typeset reports whether f(t, y) is true for all (type/underlying type) pairs of the
// specific type terms of t's constraint.
-// If there are no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (t *TypeParam) typeset(yield func(t, u Type) bool) {
- t.iface().typeSet().typeset(yield)
+// If there are no specific terms, typeset returns f(nil, nil).
+// In any case, typeset is guaranteed to call f at least once.
+func (t *TypeParam) typeset(f func(t, u Type) bool) bool {
+ return t.iface().typeSet().all(f)
}
diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go
index 74436952f2..ce487e74f7 100644
--- a/src/cmd/compile/internal/types2/typeset.go
+++ b/src/cmd/compile/internal/types2/typeset.go
@@ -104,13 +104,12 @@ func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll
// subsetOf reports whether s1 ⊆ s2.
func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
-// typeset is an iterator over the (type/underlying type) pairs in s.
-// If s has no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
+// all reports whether f(t, u) is true for each (type/underlying type) pairs in s.
+// If s has no specific terms, all calls f(nil, nil).
+// In any case, all is guaranteed to call f at least once.
+func (s *_TypeSet) all(f func(t, u Type) bool) bool {
if !s.hasTerms() {
- yield(nil, nil)
- return
+ return f(nil, nil)
}
for _, t := range s.terms {
@@ -123,10 +122,11 @@ func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
if debug {
assert(Identical(u, under(u)))
}
- if !yield(t.typ, u) {
- break
+ if !f(t.typ, u) {
+ return false
}
}
+ return true
}
// is calls f with the specific type terms of s and reports whether
diff --git a/src/cmd/compile/internal/types2/under.go b/src/cmd/compile/internal/types2/under.go
index 9e5334b724..078ba9ab17 100644
--- a/src/cmd/compile/internal/types2/under.go
+++ b/src/cmd/compile/internal/types2/under.go
@@ -4,6 +4,8 @@
package types2
+import "iter"
+
// under returns the true expanded underlying type.
// If it doesn't exist, the result is Typ[Invalid].
// under must only be called when a type is known
@@ -18,12 +20,18 @@ func under(t Type) Type {
// If typ is a type parameter, underIs returns the result of typ.underIs(f).
// Otherwise, underIs returns the result of f(under(typ)).
func underIs(typ Type, f func(Type) bool) bool {
- var ok bool
- typeset(typ, func(_, u Type) bool {
- ok = f(u)
- return ok
+ return all(typ, func(_, u Type) bool {
+ return f(u)
})
- return ok
+}
+
+// all reports whether f(t, u) is true for all (type/underlying type)
+// pairs in the typeset of t. See [typeset] for details of sequence.
+func all(t Type, f func(t, u Type) bool) bool {
+ if p, _ := Unalias(t).(*TypeParam); p != nil {
+ return p.typeset(f)
+ }
+ return f(t, under(t))
}
// typeset is an iterator over the (type/underlying type) pairs of the
@@ -32,12 +40,10 @@ func underIs(typ Type, f func(Type) bool) bool {
// In that case, if there are no specific terms, typeset calls yield with (nil, nil).
// If t is not a type parameter, the implied type set consists of just t.
// In any case, typeset is guaranteed to call yield at least once.
-func typeset(t Type, yield func(t, u Type) bool) {
- if p, _ := Unalias(t).(*TypeParam); p != nil {
- p.typeset(yield)
- return
+func typeset(t Type) iter.Seq2[Type, Type] {
+ return func(yield func(t, u Type) bool) {
+ _ = all(t, yield)
}
- yield(t, under(t))
}
// A typeError describes a type error.
@@ -80,35 +86,28 @@ func (err *typeError) format(check *Checker) string {
// with the single type t in its type set.
func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
var ct, cu Type // type and respective common underlying type
- var err *typeError
-
- bad := func(format string, args ...any) bool {
- err = typeErrorf(format, args...)
- return false
- }
-
- typeset(t, func(t, u Type) bool {
+ for t, u := range typeset(t) {
if cond != nil {
- if err = cond(t, u); err != nil {
- return false
+ if err := cond(t, u); err != nil {
+ return nil, err
}
}
if u == nil {
- return bad("no specific type")
+ return nil, typeErrorf("no specific type")
}
// If this is the first type we're seeing, we're done.
if cu == nil {
ct, cu = t, u
- return true
+ continue
}
// If we've seen a channel before, and we have a channel now, they must be compatible.
if chu, _ := cu.(*Chan); chu != nil {
if ch, _ := u.(*Chan); ch != nil {
if !Identical(chu.elem, ch.elem) {
- return bad("channels %s and %s have different element types", ct, t)
+ return nil, typeErrorf("channels %s and %s have different element types", ct, t)
}
// If we have different channel directions, keep the restricted one
// and complain if they conflict.
@@ -118,22 +117,16 @@ func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
case chu.dir == SendRecv:
ct, cu = t, u // switch to restricted channel
case ch.dir != SendRecv:
- return bad("channels %s and %s have conflicting directions", ct, t)
+ return nil, typeErrorf("channels %s and %s have conflicting directions", ct, t)
}
- return true
+ continue
}
}
// Otherwise, the current type must have the same underlying type as all previous types.
if !Identical(cu, u) {
- return bad("%s and %s have different underlying types", ct, t)
+ return nil, typeErrorf("%s and %s have different underlying types", ct, t)
}
-
- return true
- })
-
- if err != nil {
- return nil, err
}
return cu, nil
}
diff --git a/src/cmd/compile/internal/types2/version.go b/src/cmd/compile/internal/types2/version.go
index b555f398da..765b0f7e9a 100644
--- a/src/cmd/compile/internal/types2/version.go
+++ b/src/cmd/compile/internal/types2/version.go
@@ -43,6 +43,7 @@ var (
go1_21 = asGoVersion("go1.21")
go1_22 = asGoVersion("go1.22")
go1_23 = asGoVersion("go1.23")
+ go1_26 = asGoVersion("go1.26")
// current (deployed) Go version
go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version))
diff --git a/src/cmd/compile/testdata/script/issue75461.txt b/src/cmd/compile/testdata/script/issue75461.txt
new file mode 100644
index 0000000000..05f0fd4cfa
--- /dev/null
+++ b/src/cmd/compile/testdata/script/issue75461.txt
@@ -0,0 +1,78 @@
+go build main.go
+! stdout .
+! stderr .
+
+-- main.go --
+package main
+
+import (
+ "demo/registry"
+)
+
+func main() {
+ _ = registry.NewUserRegistry()
+}
+
+-- go.mod --
+module demo
+
+go 1.24
+
+-- model/user.go --
+package model
+
+type User struct {
+ ID int
+}
+
+func (c *User) String() string {
+ return ""
+}
+
+-- ordered/map.go --
+package ordered
+
+type OrderedMap[K comparable, V any] struct {
+ m map[K]V
+}
+
+func New[K comparable, V any](options ...any) *OrderedMap[K, V] {
+ orderedMap := &OrderedMap[K, V]{}
+ return orderedMap
+}
+
+-- registry/user.go --
+package registry
+
+import (
+ "demo/model"
+ "demo/ordered"
+)
+
+type baseRegistry = Registry[model.User, *model.User]
+
+type UserRegistry struct {
+ *baseRegistry
+}
+
+type Registry[T any, P PStringer[T]] struct {
+ m *ordered.OrderedMap[string, P]
+}
+
+type PStringer[T any] interface {
+ *T
+ String() string
+}
+
+func NewRegistry[T any, P PStringer[T]]() *Registry[T, P] {
+ r := &Registry[T, P]{
+ m: ordered.New[string, P](),
+ }
+ return r
+}
+
+func NewUserRegistry() *UserRegistry {
+ return &UserRegistry{
+ baseRegistry: NewRegistry[model.User](),
+ }
+}
diff --git a/src/cmd/fix/buildtag.go b/src/cmd/fix/buildtag.go
deleted file mode 100644
index 6b706c4cb5..0000000000
--- a/src/cmd/fix/buildtag.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2020 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 (
- "go/ast"
- "go/version"
- "strings"
-)
-
-func init() {
- register(buildtagFix)
-}
-
-const buildtagGoVersionCutoff = "go1.18"
-
-var buildtagFix = fix{
- name: "buildtag",
- date: "2021-08-25",
- f: buildtag,
- desc: `Remove +build comments from modules using Go 1.18 or later`,
-}
-
-func buildtag(f *ast.File) bool {
- if version.Compare(*goVersion, buildtagGoVersionCutoff) < 0 {
- return false
- }
-
- // File is already gofmt-ed, so we know that if there are +build lines,
- // they are in a comment group that starts with a //go:build line followed
- // by a blank line. While we cannot delete comments from an AST and
- // expect consistent output in general, this specific case - deleting only
- // some lines from a comment block - does format correctly.
- fixed := false
- for _, g := range f.Comments {
- sawGoBuild := false
- for i, c := range g.List {
- if strings.HasPrefix(c.Text, "//go:build ") {
- sawGoBuild = true
- }
- if sawGoBuild && strings.HasPrefix(c.Text, "// +build ") {
- g.List = g.List[:i]
- fixed = true
- break
- }
- }
- }
-
- return fixed
-}
diff --git a/src/cmd/fix/buildtag_test.go b/src/cmd/fix/buildtag_test.go
deleted file mode 100644
index e5997043c2..0000000000
--- a/src/cmd/fix/buildtag_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-func init() {
- addTestCases(buildtagTests, buildtag)
-}
-
-var buildtagTests = []testCase{
- {
- Name: "buildtag.oldGo",
- Version: "go1.10",
- In: `//go:build yes
-// +build yes
-
-package main
-`,
- },
- {
- Name: "buildtag.new",
- Version: "go1.99",
- In: `//go:build yes
-// +build yes
-
-package main
-`,
- Out: `//go:build yes
-
-package main
-`,
- },
-}
diff --git a/src/cmd/fix/cftype.go b/src/cmd/fix/cftype.go
deleted file mode 100644
index 3e9f4c5a35..0000000000
--- a/src/cmd/fix/cftype.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 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 (
- "go/ast"
-)
-
-func init() {
- register(cftypeFix)
-}
-
-var cftypeFix = fix{
- name: "cftype",
- date: "2017-09-27",
- f: noop,
- desc: `Fixes initializers and casts of C.*Ref and JNI types (removed)`,
- disabled: false,
-}
-
-func noop(f *ast.File) bool {
- return false
-}
diff --git a/src/cmd/fix/context.go b/src/cmd/fix/context.go
deleted file mode 100644
index fe2e095052..0000000000
--- a/src/cmd/fix/context.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-func init() {
- register(contextFix)
-}
-
-var contextFix = fix{
- name: "context",
- date: "2016-09-09",
- f: noop,
- desc: `Change imports of golang.org/x/net/context to context (removed)`,
- disabled: false,
-}
diff --git a/src/cmd/fix/doc.go b/src/cmd/fix/doc.go
index 062eb79285..b3d6914471 100644
--- a/src/cmd/fix/doc.go
+++ b/src/cmd/fix/doc.go
@@ -9,29 +9,12 @@ the necessary changes to your programs.
Usage:
- go tool fix [-r name,...] [path ...]
+ go tool fix [ignored...]
-Without an explicit path, fix reads standard input and writes the
-result to standard output.
-
-If the named path is a file, fix rewrites the named files in place.
-If the named path is a directory, fix rewrites all .go files in that
-directory tree. When fix rewrites a file, it prints a line to standard
-error giving the name of the file and the rewrite applied.
-
-If the -diff flag is set, no files are rewritten. Instead fix prints
-the differences a rewrite would introduce.
-
-The -r flag restricts the set of rewrites considered to those in the
-named list. By default fix considers all known rewrites. Fix's
-rewrites are idempotent, so that it is safe to apply fix to updated
-or partially updated code even without using the -r flag.
-
-Fix prints the full list of fixes it can apply in its help output;
-to see them, run go tool fix -help.
-
-Fix does not make backup copies of the files that it edits.
-Instead, use a version control system's “diff” functionality to inspect
-the changes that fix makes before committing them.
+This tool is currently in transition. All its historical fixers were
+long obsolete and have been removed, so it is currently a no-op. In
+due course the tool will integrate with the Go analysis framework
+(golang.org/x/tools/go/analysis) and run a modern suite of fix
+algorithms; see https://go.dev/issue/71859.
*/
package main
diff --git a/src/cmd/fix/egltype.go b/src/cmd/fix/egltype.go
deleted file mode 100644
index 8ba66efb06..0000000000
--- a/src/cmd/fix/egltype.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-func init() {
- register(eglFixDisplay)
- register(eglFixConfig)
-}
-
-var eglFixDisplay = fix{
- name: "egl",
- date: "2018-12-15",
- f: noop,
- desc: `Fixes initializers of EGLDisplay (removed)`,
- disabled: false,
-}
-
-var eglFixConfig = fix{
- name: "eglconf",
- date: "2020-05-30",
- f: noop,
- desc: `Fixes initializers of EGLConfig (removed)`,
- disabled: false,
-}
diff --git a/src/cmd/fix/fix.go b/src/cmd/fix/fix.go
deleted file mode 100644
index 26adae41ee..0000000000
--- a/src/cmd/fix/fix.go
+++ /dev/null
@@ -1,552 +0,0 @@
-// Copyright 2011 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 (
- "fmt"
- "go/ast"
- "go/token"
- "path"
- "strconv"
-)
-
-type fix struct {
- name string
- date string // date that fix was introduced, in YYYY-MM-DD format
- f func(*ast.File) bool
- desc string
- disabled bool // whether this fix should be disabled by default
-}
-
-var fixes []fix
-
-func register(f fix) {
- fixes = append(fixes, f)
-}
-
-// walk traverses the AST x, calling visit(y) for each node y in the tree but
-// also with a pointer to each ast.Expr, ast.Stmt, and *ast.BlockStmt,
-// in a bottom-up traversal.
-func walk(x any, visit func(any)) {
- walkBeforeAfter(x, nop, visit)
-}
-
-func nop(any) {}
-
-// walkBeforeAfter is like walk but calls before(x) before traversing
-// x's children and after(x) afterward.
-func walkBeforeAfter(x any, before, after func(any)) {
- before(x)
-
- switch n := x.(type) {
- default:
- panic(fmt.Errorf("unexpected type %T in walkBeforeAfter", x))
-
- case nil:
-
- // pointers to interfaces
- case *ast.Decl:
- walkBeforeAfter(*n, before, after)
- case *ast.Expr:
- walkBeforeAfter(*n, before, after)
- case *ast.Spec:
- walkBeforeAfter(*n, before, after)
- case *ast.Stmt:
- walkBeforeAfter(*n, before, after)
-
- // pointers to struct pointers
- case **ast.BlockStmt:
- walkBeforeAfter(*n, before, after)
- case **ast.CallExpr:
- walkBeforeAfter(*n, before, after)
- case **ast.FieldList:
- walkBeforeAfter(*n, before, after)
- case **ast.FuncType:
- walkBeforeAfter(*n, before, after)
- case **ast.Ident:
- walkBeforeAfter(*n, before, after)
- case **ast.BasicLit:
- walkBeforeAfter(*n, before, after)
-
- // pointers to slices
- case *[]ast.Decl:
- walkBeforeAfter(*n, before, after)
- case *[]ast.Expr:
- walkBeforeAfter(*n, before, after)
- case *[]*ast.File:
- walkBeforeAfter(*n, before, after)
- case *[]*ast.Ident:
- walkBeforeAfter(*n, before, after)
- case *[]ast.Spec:
- walkBeforeAfter(*n, before, after)
- case *[]ast.Stmt:
- walkBeforeAfter(*n, before, after)
-
- // These are ordered and grouped to match ../../go/ast/ast.go
- case *ast.Field:
- walkBeforeAfter(&n.Names, before, after)
- walkBeforeAfter(&n.Type, before, after)
- walkBeforeAfter(&n.Tag, before, after)
- case *ast.FieldList:
- for _, field := range n.List {
- walkBeforeAfter(field, before, after)
- }
- case *ast.BadExpr:
- case *ast.Ident:
- case *ast.Ellipsis:
- walkBeforeAfter(&n.Elt, before, after)
- case *ast.BasicLit:
- case *ast.FuncLit:
- walkBeforeAfter(&n.Type, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.CompositeLit:
- walkBeforeAfter(&n.Type, before, after)
- walkBeforeAfter(&n.Elts, before, after)
- case *ast.ParenExpr:
- walkBeforeAfter(&n.X, before, after)
- case *ast.SelectorExpr:
- walkBeforeAfter(&n.X, before, after)
- case *ast.IndexExpr:
- walkBeforeAfter(&n.X, before, after)
- walkBeforeAfter(&n.Index, before, after)
- case *ast.IndexListExpr:
- walkBeforeAfter(&n.X, before, after)
- walkBeforeAfter(&n.Indices, before, after)
- case *ast.SliceExpr:
- walkBeforeAfter(&n.X, before, after)
- if n.Low != nil {
- walkBeforeAfter(&n.Low, before, after)
- }
- if n.High != nil {
- walkBeforeAfter(&n.High, before, after)
- }
- case *ast.TypeAssertExpr:
- walkBeforeAfter(&n.X, before, after)
- walkBeforeAfter(&n.Type, before, after)
- case *ast.CallExpr:
- walkBeforeAfter(&n.Fun, before, after)
- walkBeforeAfter(&n.Args, before, after)
- case *ast.StarExpr:
- walkBeforeAfter(&n.X, before, after)
- case *ast.UnaryExpr:
- walkBeforeAfter(&n.X, before, after)
- case *ast.BinaryExpr:
- walkBeforeAfter(&n.X, before, after)
- walkBeforeAfter(&n.Y, before, after)
- case *ast.KeyValueExpr:
- walkBeforeAfter(&n.Key, before, after)
- walkBeforeAfter(&n.Value, before, after)
-
- case *ast.ArrayType:
- walkBeforeAfter(&n.Len, before, after)
- walkBeforeAfter(&n.Elt, before, after)
- case *ast.StructType:
- walkBeforeAfter(&n.Fields, before, after)
- case *ast.FuncType:
- if n.TypeParams != nil {
- walkBeforeAfter(&n.TypeParams, before, after)
- }
- walkBeforeAfter(&n.Params, before, after)
- if n.Results != nil {
- walkBeforeAfter(&n.Results, before, after)
- }
- case *ast.InterfaceType:
- walkBeforeAfter(&n.Methods, before, after)
- case *ast.MapType:
- walkBeforeAfter(&n.Key, before, after)
- walkBeforeAfter(&n.Value, before, after)
- case *ast.ChanType:
- walkBeforeAfter(&n.Value, before, after)
-
- case *ast.BadStmt:
- case *ast.DeclStmt:
- walkBeforeAfter(&n.Decl, before, after)
- case *ast.EmptyStmt:
- case *ast.LabeledStmt:
- walkBeforeAfter(&n.Stmt, before, after)
- case *ast.ExprStmt:
- walkBeforeAfter(&n.X, before, after)
- case *ast.SendStmt:
- walkBeforeAfter(&n.Chan, before, after)
- walkBeforeAfter(&n.Value, before, after)
- case *ast.IncDecStmt:
- walkBeforeAfter(&n.X, before, after)
- case *ast.AssignStmt:
- walkBeforeAfter(&n.Lhs, before, after)
- walkBeforeAfter(&n.Rhs, before, after)
- case *ast.GoStmt:
- walkBeforeAfter(&n.Call, before, after)
- case *ast.DeferStmt:
- walkBeforeAfter(&n.Call, before, after)
- case *ast.ReturnStmt:
- walkBeforeAfter(&n.Results, before, after)
- case *ast.BranchStmt:
- case *ast.BlockStmt:
- walkBeforeAfter(&n.List, before, after)
- case *ast.IfStmt:
- walkBeforeAfter(&n.Init, before, after)
- walkBeforeAfter(&n.Cond, before, after)
- walkBeforeAfter(&n.Body, before, after)
- walkBeforeAfter(&n.Else, before, after)
- case *ast.CaseClause:
- walkBeforeAfter(&n.List, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.SwitchStmt:
- walkBeforeAfter(&n.Init, before, after)
- walkBeforeAfter(&n.Tag, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.TypeSwitchStmt:
- walkBeforeAfter(&n.Init, before, after)
- walkBeforeAfter(&n.Assign, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.CommClause:
- walkBeforeAfter(&n.Comm, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.SelectStmt:
- walkBeforeAfter(&n.Body, before, after)
- case *ast.ForStmt:
- walkBeforeAfter(&n.Init, before, after)
- walkBeforeAfter(&n.Cond, before, after)
- walkBeforeAfter(&n.Post, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.RangeStmt:
- walkBeforeAfter(&n.Key, before, after)
- walkBeforeAfter(&n.Value, before, after)
- walkBeforeAfter(&n.X, before, after)
- walkBeforeAfter(&n.Body, before, after)
-
- case *ast.ImportSpec:
- case *ast.ValueSpec:
- walkBeforeAfter(&n.Type, before, after)
- walkBeforeAfter(&n.Values, before, after)
- walkBeforeAfter(&n.Names, before, after)
- case *ast.TypeSpec:
- if n.TypeParams != nil {
- walkBeforeAfter(&n.TypeParams, before, after)
- }
- walkBeforeAfter(&n.Type, before, after)
-
- case *ast.BadDecl:
- case *ast.GenDecl:
- walkBeforeAfter(&n.Specs, before, after)
- case *ast.FuncDecl:
- if n.Recv != nil {
- walkBeforeAfter(&n.Recv, before, after)
- }
- walkBeforeAfter(&n.Type, before, after)
- if n.Body != nil {
- walkBeforeAfter(&n.Body, before, after)
- }
-
- case *ast.File:
- walkBeforeAfter(&n.Decls, before, after)
-
- case *ast.Package:
- walkBeforeAfter(&n.Files, before, after)
-
- case []*ast.File:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- case []ast.Decl:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- case []ast.Expr:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- case []*ast.Ident:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- case []ast.Stmt:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- case []ast.Spec:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- }
- after(x)
-}
-
-// imports reports whether f imports path.
-func imports(f *ast.File, path string) bool {
- return importSpec(f, path) != nil
-}
-
-// importSpec returns the import spec if f imports path,
-// or nil otherwise.
-func importSpec(f *ast.File, path string) *ast.ImportSpec {
- for _, s := range f.Imports {
- if importPath(s) == path {
- return s
- }
- }
- return nil
-}
-
-// importPath returns the unquoted import path of s,
-// or "" if the path is not properly quoted.
-func importPath(s *ast.ImportSpec) string {
- t, err := strconv.Unquote(s.Path.Value)
- if err == nil {
- return t
- }
- return ""
-}
-
-// declImports reports whether gen contains an import of path.
-func declImports(gen *ast.GenDecl, path string) bool {
- if gen.Tok != token.IMPORT {
- return false
- }
- for _, spec := range gen.Specs {
- impspec := spec.(*ast.ImportSpec)
- if importPath(impspec) == path {
- return true
- }
- }
- return false
-}
-
-// isTopName reports whether n is a top-level unresolved identifier with the given name.
-func isTopName(n ast.Expr, name string) bool {
- id, ok := n.(*ast.Ident)
- return ok && id.Name == name && id.Obj == nil
-}
-
-// renameTop renames all references to the top-level name old.
-// It reports whether it makes any changes.
-func renameTop(f *ast.File, old, new string) bool {
- var fixed bool
-
- // Rename any conflicting imports
- // (assuming package name is last element of path).
- for _, s := range f.Imports {
- if s.Name != nil {
- if s.Name.Name == old {
- s.Name.Name = new
- fixed = true
- }
- } else {
- _, thisName := path.Split(importPath(s))
- if thisName == old {
- s.Name = ast.NewIdent(new)
- fixed = true
- }
- }
- }
-
- // Rename any top-level declarations.
- for _, d := range f.Decls {
- switch d := d.(type) {
- case *ast.FuncDecl:
- if d.Recv == nil && d.Name.Name == old {
- d.Name.Name = new
- d.Name.Obj.Name = new
- fixed = true
- }
- case *ast.GenDecl:
- for _, s := range d.Specs {
- switch s := s.(type) {
- case *ast.TypeSpec:
- if s.Name.Name == old {
- s.Name.Name = new
- s.Name.Obj.Name = new
- fixed = true
- }
- case *ast.ValueSpec:
- for _, n := range s.Names {
- if n.Name == old {
- n.Name = new
- n.Obj.Name = new
- fixed = true
- }
- }
- }
- }
- }
- }
-
- // Rename top-level old to new, both unresolved names
- // (probably defined in another file) and names that resolve
- // to a declaration we renamed.
- walk(f, func(n any) {
- id, ok := n.(*ast.Ident)
- if ok && isTopName(id, old) {
- id.Name = new
- fixed = true
- }
- if ok && id.Obj != nil && id.Name == old && id.Obj.Name == new {
- id.Name = id.Obj.Name
- fixed = true
- }
- })
-
- return fixed
-}
-
-// matchLen returns the length of the longest prefix shared by x and y.
-func matchLen(x, y string) int {
- i := 0
- for i < len(x) && i < len(y) && x[i] == y[i] {
- i++
- }
- return i
-}
-
-// addImport adds the import path to the file f, if absent.
-func addImport(f *ast.File, ipath string) (added bool) {
- if imports(f, ipath) {
- return false
- }
-
- // Determine name of import.
- // Assume added imports follow convention of using last element.
- _, name := path.Split(ipath)
-
- // Rename any conflicting top-level references from name to name_.
- renameTop(f, name, name+"_")
-
- newImport := &ast.ImportSpec{
- Path: &ast.BasicLit{
- Kind: token.STRING,
- Value: strconv.Quote(ipath),
- },
- }
-
- // Find an import decl to add to.
- var (
- bestMatch = -1
- lastImport = -1
- impDecl *ast.GenDecl
- impIndex = -1
- )
- for i, decl := range f.Decls {
- gen, ok := decl.(*ast.GenDecl)
- if ok && gen.Tok == token.IMPORT {
- lastImport = i
- // Do not add to import "C", to avoid disrupting the
- // association with its doc comment, breaking cgo.
- if declImports(gen, "C") {
- continue
- }
-
- // Compute longest shared prefix with imports in this block.
- for j, spec := range gen.Specs {
- impspec := spec.(*ast.ImportSpec)
- n := matchLen(importPath(impspec), ipath)
- if n > bestMatch {
- bestMatch = n
- impDecl = gen
- impIndex = j
- }
- }
- }
- }
-
- // If no import decl found, add one after the last import.
- if impDecl == nil {
- impDecl = &ast.GenDecl{
- Tok: token.IMPORT,
- }
- f.Decls = append(f.Decls, nil)
- copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:])
- f.Decls[lastImport+1] = impDecl
- }
-
- // Ensure the import decl has parentheses, if needed.
- if len(impDecl.Specs) > 0 && !impDecl.Lparen.IsValid() {
- impDecl.Lparen = impDecl.Pos()
- }
-
- insertAt := impIndex + 1
- if insertAt == 0 {
- insertAt = len(impDecl.Specs)
- }
- impDecl.Specs = append(impDecl.Specs, nil)
- copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:])
- impDecl.Specs[insertAt] = newImport
- if insertAt > 0 {
- // Assign same position as the previous import,
- // so that the sorter sees it as being in the same block.
- prev := impDecl.Specs[insertAt-1]
- newImport.Path.ValuePos = prev.Pos()
- newImport.EndPos = prev.Pos()
- }
-
- f.Imports = append(f.Imports, newImport)
- return true
-}
-
-// deleteImport deletes the import path from the file f, if present.
-func deleteImport(f *ast.File, path string) (deleted bool) {
- oldImport := importSpec(f, path)
-
- // Find the import node that imports path, if any.
- for i, decl := range f.Decls {
- gen, ok := decl.(*ast.GenDecl)
- if !ok || gen.Tok != token.IMPORT {
- continue
- }
- for j, spec := range gen.Specs {
- impspec := spec.(*ast.ImportSpec)
- if oldImport != impspec {
- continue
- }
-
- // We found an import spec that imports path.
- // Delete it.
- deleted = true
- copy(gen.Specs[j:], gen.Specs[j+1:])
- gen.Specs = gen.Specs[:len(gen.Specs)-1]
-
- // If this was the last import spec in this decl,
- // delete the decl, too.
- if len(gen.Specs) == 0 {
- copy(f.Decls[i:], f.Decls[i+1:])
- f.Decls = f.Decls[:len(f.Decls)-1]
- } else if len(gen.Specs) == 1 {
- gen.Lparen = token.NoPos // drop parens
- }
- if j > 0 {
- // We deleted an entry but now there will be
- // a blank line-sized hole where the import was.
- // Close the hole by making the previous
- // import appear to "end" where this one did.
- gen.Specs[j-1].(*ast.ImportSpec).EndPos = impspec.End()
- }
- break
- }
- }
-
- // Delete it from f.Imports.
- for i, imp := range f.Imports {
- if imp == oldImport {
- copy(f.Imports[i:], f.Imports[i+1:])
- f.Imports = f.Imports[:len(f.Imports)-1]
- break
- }
- }
-
- return
-}
-
-// rewriteImport rewrites any import of path oldPath to path newPath.
-func rewriteImport(f *ast.File, oldPath, newPath string) (rewrote bool) {
- for _, imp := range f.Imports {
- if importPath(imp) == oldPath {
- rewrote = true
- // record old End, because the default is to compute
- // it using the length of imp.Path.Value.
- imp.EndPos = imp.End()
- imp.Path.Value = strconv.Quote(newPath)
- }
- }
- return
-}
diff --git a/src/cmd/fix/gotypes.go b/src/cmd/fix/gotypes.go
deleted file mode 100644
index 987dab5d02..0000000000
--- a/src/cmd/fix/gotypes.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-func init() {
- register(gotypesFix)
-}
-
-var gotypesFix = fix{
- name: "gotypes",
- date: "2015-07-16",
- f: noop,
- desc: `Change imports of golang.org/x/tools/go/{exact,types} to go/{constant,types} (removed)`,
-}
diff --git a/src/cmd/fix/import_test.go b/src/cmd/fix/import_test.go
deleted file mode 100644
index 8644e28f85..0000000000
--- a/src/cmd/fix/import_test.go
+++ /dev/null
@@ -1,458 +0,0 @@
-// Copyright 2011 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 "go/ast"
-
-func init() {
- addTestCases(importTests, nil)
-}
-
-var importTests = []testCase{
- {
- Name: "import.0",
- Fn: addImportFn("os"),
- In: `package main
-
-import (
- "os"
-)
-`,
- Out: `package main
-
-import (
- "os"
-)
-`,
- },
- {
- Name: "import.1",
- Fn: addImportFn("os"),
- In: `package main
-`,
- Out: `package main
-
-import "os"
-`,
- },
- {
- Name: "import.2",
- Fn: addImportFn("os"),
- In: `package main
-
-// Comment
-import "C"
-`,
- Out: `package main
-
-// Comment
-import "C"
-import "os"
-`,
- },
- {
- Name: "import.3",
- Fn: addImportFn("os"),
- In: `package main
-
-// Comment
-import "C"
-
-import (
- "io"
- "utf8"
-)
-`,
- Out: `package main
-
-// Comment
-import "C"
-
-import (
- "io"
- "os"
- "utf8"
-)
-`,
- },
- {
- Name: "import.4",
- Fn: deleteImportFn("os"),
- In: `package main
-
-import (
- "os"
-)
-`,
- Out: `package main
-`,
- },
- {
- Name: "import.5",
- Fn: deleteImportFn("os"),
- In: `package main
-
-// Comment
-import "C"
-import "os"
-`,
- Out: `package main
-
-// Comment
-import "C"
-`,
- },
- {
- Name: "import.6",
- Fn: deleteImportFn("os"),
- In: `package main
-
-// Comment
-import "C"
-
-import (
- "io"
- "os"
- "utf8"
-)
-`,
- Out: `package main
-
-// Comment
-import "C"
-
-import (
- "io"
- "utf8"
-)
-`,
- },
- {
- Name: "import.7",
- Fn: deleteImportFn("io"),
- In: `package main
-
-import (
- "io" // a
- "os" // b
- "utf8" // c
-)
-`,
- Out: `package main
-
-import (
- // a
- "os" // b
- "utf8" // c
-)
-`,
- },
- {
- Name: "import.8",
- Fn: deleteImportFn("os"),
- In: `package main
-
-import (
- "io" // a
- "os" // b
- "utf8" // c
-)
-`,
- Out: `package main
-
-import (
- "io" // a
- // b
- "utf8" // c
-)
-`,
- },
- {
- Name: "import.9",
- Fn: deleteImportFn("utf8"),
- In: `package main
-
-import (
- "io" // a
- "os" // b
- "utf8" // c
-)
-`,
- Out: `package main
-
-import (
- "io" // a
- "os" // b
- // c
-)
-`,
- },
- {
- Name: "import.10",
- Fn: deleteImportFn("io"),
- In: `package main
-
-import (
- "io"
- "os"
- "utf8"
-)
-`,
- Out: `package main
-
-import (
- "os"
- "utf8"
-)
-`,
- },
- {
- Name: "import.11",
- Fn: deleteImportFn("os"),
- In: `package main
-
-import (
- "io"
- "os"
- "utf8"
-)
-`,
- Out: `package main
-
-import (
- "io"
- "utf8"
-)
-`,
- },
- {
- Name: "import.12",
- Fn: deleteImportFn("utf8"),
- In: `package main
-
-import (
- "io"
- "os"
- "utf8"
-)
-`,
- Out: `package main
-
-import (
- "io"
- "os"
-)
-`,
- },
- {
- Name: "import.13",
- Fn: rewriteImportFn("utf8", "encoding/utf8"),
- In: `package main
-
-import (
- "io"
- "os"
- "utf8" // thanks ken
-)
-`,
- Out: `package main
-
-import (
- "encoding/utf8" // thanks ken
- "io"
- "os"
-)
-`,
- },
- {
- Name: "import.14",
- Fn: rewriteImportFn("asn1", "encoding/asn1"),
- In: `package main
-
-import (
- "asn1"
- "crypto"
- "crypto/rsa"
- _ "crypto/sha1"
- "crypto/x509"
- "crypto/x509/pkix"
- "time"
-)
-
-var x = 1
-`,
- Out: `package main
-
-import (
- "crypto"
- "crypto/rsa"
- _ "crypto/sha1"
- "crypto/x509"
- "crypto/x509/pkix"
- "encoding/asn1"
- "time"
-)
-
-var x = 1
-`,
- },
- {
- Name: "import.15",
- Fn: rewriteImportFn("url", "net/url"),
- In: `package main
-
-import (
- "bufio"
- "net"
- "path"
- "url"
-)
-
-var x = 1 // comment on x, not on url
-`,
- Out: `package main
-
-import (
- "bufio"
- "net"
- "net/url"
- "path"
-)
-
-var x = 1 // comment on x, not on url
-`,
- },
- {
- Name: "import.16",
- Fn: rewriteImportFn("http", "net/http", "template", "text/template"),
- In: `package main
-
-import (
- "flag"
- "http"
- "log"
- "template"
-)
-
-var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
-`,
- Out: `package main
-
-import (
- "flag"
- "log"
- "net/http"
- "text/template"
-)
-
-var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
-`,
- },
- {
- Name: "import.17",
- Fn: addImportFn("x/y/z", "x/a/c"),
- In: `package main
-
-// Comment
-import "C"
-
-import (
- "a"
- "b"
-
- "x/w"
-
- "d/f"
-)
-`,
- Out: `package main
-
-// Comment
-import "C"
-
-import (
- "a"
- "b"
-
- "x/a/c"
- "x/w"
- "x/y/z"
-
- "d/f"
-)
-`,
- },
- {
- Name: "import.18",
- Fn: addDelImportFn("e", "o"),
- In: `package main
-
-import (
- "f"
- "o"
- "z"
-)
-`,
- Out: `package main
-
-import (
- "e"
- "f"
- "z"
-)
-`,
- },
-}
-
-func addImportFn(path ...string) func(*ast.File) bool {
- return func(f *ast.File) bool {
- fixed := false
- for _, p := range path {
- if !imports(f, p) {
- addImport(f, p)
- fixed = true
- }
- }
- return fixed
- }
-}
-
-func deleteImportFn(path string) func(*ast.File) bool {
- return func(f *ast.File) bool {
- if imports(f, path) {
- deleteImport(f, path)
- return true
- }
- return false
- }
-}
-
-func addDelImportFn(p1 string, p2 string) func(*ast.File) bool {
- return func(f *ast.File) bool {
- fixed := false
- if !imports(f, p1) {
- addImport(f, p1)
- fixed = true
- }
- if imports(f, p2) {
- deleteImport(f, p2)
- fixed = true
- }
- return fixed
- }
-}
-
-func rewriteImportFn(oldnew ...string) func(*ast.File) bool {
- return func(f *ast.File) bool {
- fixed := false
- for i := 0; i < len(oldnew); i += 2 {
- if imports(f, oldnew[i]) {
- rewriteImport(f, oldnew[i], oldnew[i+1])
- fixed = true
- }
- }
- return fixed
- }
-}
diff --git a/src/cmd/fix/jnitype.go b/src/cmd/fix/jnitype.go
deleted file mode 100644
index bee38e6720..0000000000
--- a/src/cmd/fix/jnitype.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2017 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-func init() {
- register(jniFix)
-}
-
-var jniFix = fix{
- name: "jni",
- date: "2017-12-04",
- f: noop,
- desc: `Fixes initializers of JNI's jobject and subtypes (removed)`,
- disabled: false,
-}
diff --git a/src/cmd/fix/main.go b/src/cmd/fix/main.go
index 933c32bcd9..87cc0d6414 100644
--- a/src/cmd/fix/main.go
+++ b/src/cmd/fix/main.go
@@ -5,261 +5,27 @@
package main
import (
- "bytes"
"flag"
"fmt"
- "go/ast"
- "go/format"
- "go/parser"
- "go/scanner"
- "go/token"
- "go/version"
- "internal/diff"
- "io"
- "io/fs"
"os"
- "path/filepath"
- "slices"
- "strings"
-
- "cmd/internal/telemetry/counter"
)
var (
- fset = token.NewFileSet()
- exitCode = 0
-)
-
-var allowedRewrites = flag.String("r", "",
- "restrict the rewrites to this comma-separated list")
-
-var forceRewrites = flag.String("force", "",
- "force these fixes to run even if the code looks updated")
-
-var allowed, force map[string]bool
-
-var (
- doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files")
- goVersion = flag.String("go", "", "go language version for files")
+ _ = flag.Bool("diff", false, "obsolete, no effect")
+ _ = flag.String("go", "", "obsolete, no effect")
+ _ = flag.String("r", "", "obsolete, no effect")
+ _ = flag.String("force", "", "obsolete, no effect")
)
-// enable for debugging fix failures
-const debug = false // display incorrectly reformatted source and exit
-
func usage() {
- fmt.Fprintf(os.Stderr, "usage: go tool fix [-diff] [-r fixname,...] [-force fixname,...] [path ...]\n")
+ fmt.Fprintf(os.Stderr, "usage: go tool fix [-diff] [-r ignored] [-force ignored] ...\n")
flag.PrintDefaults()
- fmt.Fprintf(os.Stderr, "\nAvailable rewrites are:\n")
- slices.SortFunc(fixes, func(a, b fix) int {
- return strings.Compare(a.name, b.name)
- })
- for _, f := range fixes {
- if f.disabled {
- fmt.Fprintf(os.Stderr, "\n%s (disabled)\n", f.name)
- } else {
- fmt.Fprintf(os.Stderr, "\n%s\n", f.name)
- }
- desc := strings.TrimSpace(f.desc)
- desc = strings.ReplaceAll(desc, "\n", "\n\t")
- fmt.Fprintf(os.Stderr, "\t%s\n", desc)
- }
os.Exit(2)
}
func main() {
- counter.Open()
flag.Usage = usage
flag.Parse()
- counter.Inc("fix/invocations")
- counter.CountFlags("fix/flag:", *flag.CommandLine)
-
- if !version.IsValid(*goVersion) {
- report(fmt.Errorf("invalid -go=%s", *goVersion))
- os.Exit(exitCode)
- }
-
- slices.SortFunc(fixes, func(a, b fix) int {
- return strings.Compare(a.date, b.date)
- })
-
- if *allowedRewrites != "" {
- allowed = make(map[string]bool)
- for f := range strings.SplitSeq(*allowedRewrites, ",") {
- allowed[f] = true
- }
- }
-
- if *forceRewrites != "" {
- force = make(map[string]bool)
- for f := range strings.SplitSeq(*forceRewrites, ",") {
- force[f] = true
- }
- }
-
- if flag.NArg() == 0 {
- if err := processFile("standard input", true); err != nil {
- report(err)
- }
- os.Exit(exitCode)
- }
-
- for i := 0; i < flag.NArg(); i++ {
- path := flag.Arg(i)
- switch dir, err := os.Stat(path); {
- case err != nil:
- report(err)
- case dir.IsDir():
- walkDir(path)
- default:
- if err := processFile(path, false); err != nil {
- report(err)
- }
- }
- }
-
- os.Exit(exitCode)
-}
-
-const parserMode = parser.ParseComments
-
-func gofmtFile(f *ast.File) ([]byte, error) {
- var buf bytes.Buffer
- if err := format.Node(&buf, fset, f); err != nil {
- return nil, err
- }
- return buf.Bytes(), nil
-}
-
-func processFile(filename string, useStdin bool) error {
- var f *os.File
- var err error
- var fixlog strings.Builder
-
- if useStdin {
- f = os.Stdin
- } else {
- f, err = os.Open(filename)
- if err != nil {
- return err
- }
- defer f.Close()
- }
-
- src, err := io.ReadAll(f)
- if err != nil {
- return err
- }
-
- file, err := parser.ParseFile(fset, filename, src, parserMode)
- if err != nil {
- return err
- }
-
- // Make sure file is in canonical format.
- // This "fmt" pseudo-fix cannot be disabled.
- newSrc, err := gofmtFile(file)
- if err != nil {
- return err
- }
- if !bytes.Equal(newSrc, src) {
- newFile, err := parser.ParseFile(fset, filename, newSrc, parserMode)
- if err != nil {
- return err
- }
- file = newFile
- fmt.Fprintf(&fixlog, " fmt")
- }
-
- // Apply all fixes to file.
- newFile := file
- fixed := false
- for _, fix := range fixes {
- if allowed != nil && !allowed[fix.name] {
- continue
- }
- if fix.disabled && !force[fix.name] {
- continue
- }
- if fix.f(newFile) {
- fixed = true
- fmt.Fprintf(&fixlog, " %s", fix.name)
-
- // AST changed.
- // Print and parse, to update any missing scoping
- // or position information for subsequent fixers.
- newSrc, err := gofmtFile(newFile)
- if err != nil {
- return err
- }
- newFile, err = parser.ParseFile(fset, filename, newSrc, parserMode)
- if err != nil {
- if debug {
- fmt.Printf("%s", newSrc)
- report(err)
- os.Exit(exitCode)
- }
- return err
- }
- }
- }
- if !fixed {
- return nil
- }
- fmt.Fprintf(os.Stderr, "%s: fixed %s\n", filename, fixlog.String()[1:])
-
- // Print AST. We did that after each fix, so this appears
- // redundant, but it is necessary to generate gofmt-compatible
- // source code in a few cases. The official gofmt style is the
- // output of the printer run on a standard AST generated by the parser,
- // but the source we generated inside the loop above is the
- // output of the printer run on a mangled AST generated by a fixer.
- newSrc, err = gofmtFile(newFile)
- if err != nil {
- return err
- }
-
- if *doDiff {
- os.Stdout.Write(diff.Diff(filename, src, "fixed/"+filename, newSrc))
- return nil
- }
-
- if useStdin {
- os.Stdout.Write(newSrc)
- return nil
- }
-
- return os.WriteFile(f.Name(), newSrc, 0)
-}
-
-func gofmt(n any) string {
- var gofmtBuf strings.Builder
- if err := format.Node(&gofmtBuf, fset, n); err != nil {
- return "<" + err.Error() + ">"
- }
- return gofmtBuf.String()
-}
-
-func report(err error) {
- scanner.PrintError(os.Stderr, err)
- exitCode = 2
-}
-
-func walkDir(path string) {
- filepath.WalkDir(path, visitFile)
-}
-
-func visitFile(path string, f fs.DirEntry, err error) error {
- if err == nil && isGoFile(f) {
- err = processFile(path, false)
- }
- if err != nil {
- report(err)
- }
- return nil
-}
-func isGoFile(f fs.DirEntry) bool {
- // ignore non-Go files
- name := f.Name()
- return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
+ os.Exit(0)
}
diff --git a/src/cmd/fix/main_test.go b/src/cmd/fix/main_test.go
deleted file mode 100644
index 8d841b101f..0000000000
--- a/src/cmd/fix/main_test.go
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2011 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 (
- "fmt"
- "go/ast"
- "go/parser"
- "internal/diff"
- "internal/testenv"
- "strings"
- "testing"
-)
-
-type testCase struct {
- Name string
- Fn func(*ast.File) bool
- Version string
- In string
- Out string
-}
-
-var testCases []testCase
-
-func addTestCases(t []testCase, fn func(*ast.File) bool) {
- // Fill in fn to avoid repetition in definitions.
- if fn != nil {
- for i := range t {
- if t[i].Fn == nil {
- t[i].Fn = fn
- }
- }
- }
- testCases = append(testCases, t...)
-}
-
-func fnop(*ast.File) bool { return false }
-
-func parseFixPrint(t *testing.T, fn func(*ast.File) bool, desc, in string, mustBeGofmt bool) (out string, fixed, ok bool) {
- file, err := parser.ParseFile(fset, desc, in, parserMode)
- if err != nil {
- t.Errorf("parsing: %v", err)
- return
- }
-
- outb, err := gofmtFile(file)
- if err != nil {
- t.Errorf("printing: %v", err)
- return
- }
- if s := string(outb); in != s && mustBeGofmt {
- t.Errorf("not gofmt-formatted.\n--- %s\n%s\n--- %s | gofmt\n%s",
- desc, in, desc, s)
- tdiff(t, "want", in, "have", s)
- return
- }
-
- if fn == nil {
- for _, fix := range fixes {
- if fix.f(file) {
- fixed = true
- }
- }
- } else {
- fixed = fn(file)
- }
-
- outb, err = gofmtFile(file)
- if err != nil {
- t.Errorf("printing: %v", err)
- return
- }
-
- return string(outb), fixed, true
-}
-
-func TestRewrite(t *testing.T) {
- // If cgo is enabled, enforce that cgo commands invoked by cmd/fix
- // do not fail during testing.
- if testenv.HasCGO() {
- testenv.MustHaveGoBuild(t) // Really just 'go tool cgo', but close enough.
-
- // The reportCgoError hook is global, so we can't set it per-test
- // if we want to be able to run those tests in parallel.
- // Instead, simply set it to panic on error: the goroutine dump
- // from the panic should help us determine which test failed.
- prevReportCgoError := reportCgoError
- reportCgoError = func(err error) {
- panic(fmt.Sprintf("unexpected cgo error: %v", err))
- }
- t.Cleanup(func() { reportCgoError = prevReportCgoError })
- }
-
- for _, tt := range testCases {
- tt := tt
- t.Run(tt.Name, func(t *testing.T) {
- if tt.Version == "" {
- if testing.Verbose() {
- // Don't run in parallel: cmd/fix sometimes writes directly to stderr,
- // and since -v prints which test is currently running we want that
- // information to accurately correlate with the stderr output.
- } else {
- t.Parallel()
- }
- } else {
- old := *goVersion
- *goVersion = tt.Version
- defer func() {
- *goVersion = old
- }()
- }
-
- // Apply fix: should get tt.Out.
- out, fixed, ok := parseFixPrint(t, tt.Fn, tt.Name, tt.In, true)
- if !ok {
- return
- }
-
- // reformat to get printing right
- out, _, ok = parseFixPrint(t, fnop, tt.Name, out, false)
- if !ok {
- return
- }
-
- if tt.Out == "" {
- tt.Out = tt.In
- }
- if out != tt.Out {
- t.Errorf("incorrect output.\n")
- if !strings.HasPrefix(tt.Name, "testdata/") {
- t.Errorf("--- have\n%s\n--- want\n%s", out, tt.Out)
- }
- tdiff(t, "have", out, "want", tt.Out)
- return
- }
-
- if changed := out != tt.In; changed != fixed {
- t.Errorf("changed=%v != fixed=%v", changed, fixed)
- return
- }
-
- // Should not change if run again.
- out2, fixed2, ok := parseFixPrint(t, tt.Fn, tt.Name+" output", out, true)
- if !ok {
- return
- }
-
- if fixed2 {
- t.Errorf("applied fixes during second round")
- return
- }
-
- if out2 != out {
- t.Errorf("changed output after second round of fixes.\n--- output after first round\n%s\n--- output after second round\n%s",
- out, out2)
- tdiff(t, "first", out, "second", out2)
- }
- })
- }
-}
-
-func tdiff(t *testing.T, aname, a, bname, b string) {
- t.Errorf("%s", diff.Diff(aname, []byte(a), bname, []byte(b)))
-}
diff --git a/src/cmd/fix/netipv6zone.go b/src/cmd/fix/netipv6zone.go
deleted file mode 100644
index 75d2150e43..0000000000
--- a/src/cmd/fix/netipv6zone.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-func init() {
- register(netipv6zoneFix)
-}
-
-var netipv6zoneFix = fix{
- name: "netipv6zone",
- date: "2012-11-26",
- f: noop,
- desc: `Adapt element key to IPAddr, UDPAddr or TCPAddr composite literals (removed).
-
-https://codereview.appspot.com/6849045/
-`,
-}
diff --git a/src/cmd/fix/printerconfig.go b/src/cmd/fix/printerconfig.go
deleted file mode 100644
index f9e49d7c0b..0000000000
--- a/src/cmd/fix/printerconfig.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-func init() {
- register(printerconfigFix)
-}
-
-var printerconfigFix = fix{
- name: "printerconfig",
- date: "2012-12-11",
- f: noop,
- desc: `Add element keys to Config composite literals (removed).`,
-}
diff --git a/src/cmd/fix/typecheck.go b/src/cmd/fix/typecheck.go
deleted file mode 100644
index be21582fce..0000000000
--- a/src/cmd/fix/typecheck.go
+++ /dev/null
@@ -1,814 +0,0 @@
-// Copyright 2011 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 (
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "maps"
- "os"
- "os/exec"
- "path/filepath"
- "reflect"
- "runtime"
- "strings"
-)
-
-// Partial type checker.
-//
-// The fact that it is partial is very important: the input is
-// an AST and a description of some type information to
-// assume about one or more packages, but not all the
-// packages that the program imports. The checker is
-// expected to do as much as it can with what it has been
-// given. There is not enough information supplied to do
-// a full type check, but the type checker is expected to
-// apply information that can be derived from variable
-// declarations, function and method returns, and type switches
-// as far as it can, so that the caller can still tell the types
-// of expression relevant to a particular fix.
-//
-// TODO(rsc,gri): Replace with go/typechecker.
-// Doing that could be an interesting test case for go/typechecker:
-// the constraints about working with partial information will
-// likely exercise it in interesting ways. The ideal interface would
-// be to pass typecheck a map from importpath to package API text
-// (Go source code), but for now we use data structures (TypeConfig, Type).
-//
-// The strings mostly use gofmt form.
-//
-// A Field or FieldList has as its type a comma-separated list
-// of the types of the fields. For example, the field list
-// x, y, z int
-// has type "int, int, int".
-
-// The prefix "type " is the type of a type.
-// For example, given
-// var x int
-// type T int
-// x's type is "int" but T's type is "type int".
-// mkType inserts the "type " prefix.
-// getType removes it.
-// isType tests for it.
-
-func mkType(t string) string {
- return "type " + t
-}
-
-func getType(t string) string {
- if !isType(t) {
- return ""
- }
- return t[len("type "):]
-}
-
-func isType(t string) bool {
- return strings.HasPrefix(t, "type ")
-}
-
-// TypeConfig describes the universe of relevant types.
-// For ease of creation, the types are all referred to by string
-// name (e.g., "reflect.Value"). TypeByName is the only place
-// where the strings are resolved.
-
-type TypeConfig struct {
- Type map[string]*Type
- Var map[string]string
- Func map[string]string
-
- // External maps from a name to its type.
- // It provides additional typings not present in the Go source itself.
- // For now, the only additional typings are those generated by cgo.
- External map[string]string
-}
-
-// typeof returns the type of the given name, which may be of
-// the form "x" or "p.X".
-func (cfg *TypeConfig) typeof(name string) string {
- if cfg.Var != nil {
- if t := cfg.Var[name]; t != "" {
- return t
- }
- }
- if cfg.Func != nil {
- if t := cfg.Func[name]; t != "" {
- return "func()" + t
- }
- }
- return ""
-}
-
-// Type describes the Fields and Methods of a type.
-// If the field or method cannot be found there, it is next
-// looked for in the Embed list.
-type Type struct {
- Field map[string]string // map field name to type
- Method map[string]string // map method name to comma-separated return types (should start with "func ")
- Embed []string // list of types this type embeds (for extra methods)
- Def string // definition of named type
-}
-
-// dot returns the type of "typ.name", making its decision
-// using the type information in cfg.
-func (typ *Type) dot(cfg *TypeConfig, name string) string {
- if typ.Field != nil {
- if t := typ.Field[name]; t != "" {
- return t
- }
- }
- if typ.Method != nil {
- if t := typ.Method[name]; t != "" {
- return t
- }
- }
-
- for _, e := range typ.Embed {
- etyp := cfg.Type[e]
- if etyp != nil {
- if t := etyp.dot(cfg, name); t != "" {
- return t
- }
- }
- }
-
- return ""
-}
-
-// typecheck type checks the AST f assuming the information in cfg.
-// It returns two maps with type information:
-// typeof maps AST nodes to type information in gofmt string form.
-// assign maps type strings to lists of expressions that were assigned
-// to values of another type that were assigned to that type.
-func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[any]string, assign map[string][]any) {
- typeof = make(map[any]string)
- assign = make(map[string][]any)
- cfg1 := &TypeConfig{}
- *cfg1 = *cfg // make copy so we can add locally
- copied := false
-
- // If we import "C", add types of cgo objects.
- cfg.External = map[string]string{}
- cfg1.External = cfg.External
- if imports(f, "C") {
- // Run cgo on gofmtFile(f)
- // Parse, extract decls from _cgo_gotypes.go
- // Map _Ctype_* types to C.* types.
- err := func() error {
- txt, err := gofmtFile(f)
- if err != nil {
- return err
- }
- dir, err := os.MkdirTemp(os.TempDir(), "fix_cgo_typecheck")
- if err != nil {
- return err
- }
- defer os.RemoveAll(dir)
- err = os.WriteFile(filepath.Join(dir, "in.go"), txt, 0600)
- if err != nil {
- return err
- }
- goCmd := "go"
- if goroot := runtime.GOROOT(); goroot != "" {
- goCmd = filepath.Join(goroot, "bin", "go")
- }
- cmd := exec.Command(goCmd, "tool", "cgo", "-objdir", dir, "-srcdir", dir, "in.go")
- if reportCgoError != nil {
- // Since cgo command errors will be reported, also forward the error
- // output from the command for debugging.
- cmd.Stderr = os.Stderr
- }
- err = cmd.Run()
- if err != nil {
- return err
- }
- out, err := os.ReadFile(filepath.Join(dir, "_cgo_gotypes.go"))
- if err != nil {
- return err
- }
- cgo, err := parser.ParseFile(token.NewFileSet(), "cgo.go", out, 0)
- if err != nil {
- return err
- }
- for _, decl := range cgo.Decls {
- fn, ok := decl.(*ast.FuncDecl)
- if !ok {
- continue
- }
- if strings.HasPrefix(fn.Name.Name, "_Cfunc_") {
- var params, results []string
- for _, p := range fn.Type.Params.List {
- t := gofmt(p.Type)
- t = strings.ReplaceAll(t, "_Ctype_", "C.")
- params = append(params, t)
- }
- for _, r := range fn.Type.Results.List {
- t := gofmt(r.Type)
- t = strings.ReplaceAll(t, "_Ctype_", "C.")
- results = append(results, t)
- }
- cfg.External["C."+fn.Name.Name[7:]] = joinFunc(params, results)
- }
- }
- return nil
- }()
- if err != nil {
- if reportCgoError == nil {
- fmt.Fprintf(os.Stderr, "go fix: warning: no cgo types: %s\n", err)
- } else {
- reportCgoError(err)
- }
- }
- }
-
- // gather function declarations
- for _, decl := range f.Decls {
- fn, ok := decl.(*ast.FuncDecl)
- if !ok {
- continue
- }
- typecheck1(cfg, fn.Type, typeof, assign)
- t := typeof[fn.Type]
- if fn.Recv != nil {
- // The receiver must be a type.
- rcvr := typeof[fn.Recv]
- if !isType(rcvr) {
- if len(fn.Recv.List) != 1 {
- continue
- }
- rcvr = mkType(gofmt(fn.Recv.List[0].Type))
- typeof[fn.Recv.List[0].Type] = rcvr
- }
- rcvr = getType(rcvr)
- if rcvr != "" && rcvr[0] == '*' {
- rcvr = rcvr[1:]
- }
- typeof[rcvr+"."+fn.Name.Name] = t
- } else {
- if isType(t) {
- t = getType(t)
- } else {
- t = gofmt(fn.Type)
- }
- typeof[fn.Name] = t
-
- // Record typeof[fn.Name.Obj] for future references to fn.Name.
- typeof[fn.Name.Obj] = t
- }
- }
-
- // gather struct declarations
- for _, decl := range f.Decls {
- d, ok := decl.(*ast.GenDecl)
- if ok {
- for _, s := range d.Specs {
- switch s := s.(type) {
- case *ast.TypeSpec:
- if cfg1.Type[s.Name.Name] != nil {
- break
- }
- if !copied {
- copied = true
- // Copy map lazily: it's time.
- cfg1.Type = maps.Clone(cfg.Type)
- if cfg1.Type == nil {
- cfg1.Type = make(map[string]*Type)
- }
- }
- t := &Type{Field: map[string]string{}}
- cfg1.Type[s.Name.Name] = t
- switch st := s.Type.(type) {
- case *ast.StructType:
- for _, f := range st.Fields.List {
- for _, n := range f.Names {
- t.Field[n.Name] = gofmt(f.Type)
- }
- }
- case *ast.ArrayType, *ast.StarExpr, *ast.MapType:
- t.Def = gofmt(st)
- }
- }
- }
- }
- }
-
- typecheck1(cfg1, f, typeof, assign)
- return typeof, assign
-}
-
-// reportCgoError, if non-nil, reports a non-nil error from running the "cgo"
-// tool. (Set to a non-nil hook during testing if cgo is expected to work.)
-var reportCgoError func(err error)
-
-func makeExprList(a []*ast.Ident) []ast.Expr {
- var b []ast.Expr
- for _, x := range a {
- b = append(b, x)
- }
- return b
-}
-
-// typecheck1 is the recursive form of typecheck.
-// It is like typecheck but adds to the information in typeof
-// instead of allocating a new map.
-func typecheck1(cfg *TypeConfig, f any, typeof map[any]string, assign map[string][]any) {
- // set sets the type of n to typ.
- // If isDecl is true, n is being declared.
- set := func(n ast.Expr, typ string, isDecl bool) {
- if typeof[n] != "" || typ == "" {
- if typeof[n] != typ {
- assign[typ] = append(assign[typ], n)
- }
- return
- }
- typeof[n] = typ
-
- // If we obtained typ from the declaration of x
- // propagate the type to all the uses.
- // The !isDecl case is a cheat here, but it makes
- // up in some cases for not paying attention to
- // struct fields. The real type checker will be
- // more accurate so we won't need the cheat.
- if id, ok := n.(*ast.Ident); ok && id.Obj != nil && (isDecl || typeof[id.Obj] == "") {
- typeof[id.Obj] = typ
- }
- }
-
- // Type-check an assignment lhs = rhs.
- // If isDecl is true, this is := so we can update
- // the types of the objects that lhs refers to.
- typecheckAssign := func(lhs, rhs []ast.Expr, isDecl bool) {
- if len(lhs) > 1 && len(rhs) == 1 {
- if _, ok := rhs[0].(*ast.CallExpr); ok {
- t := split(typeof[rhs[0]])
- // Lists should have same length but may not; pair what can be paired.
- for i := 0; i < len(lhs) && i < len(t); i++ {
- set(lhs[i], t[i], isDecl)
- }
- return
- }
- }
- if len(lhs) == 1 && len(rhs) == 2 {
- // x = y, ok
- rhs = rhs[:1]
- } else if len(lhs) == 2 && len(rhs) == 1 {
- // x, ok = y
- lhs = lhs[:1]
- }
-
- // Match as much as we can.
- for i := 0; i < len(lhs) && i < len(rhs); i++ {
- x, y := lhs[i], rhs[i]
- if typeof[y] != "" {
- set(x, typeof[y], isDecl)
- } else {
- set(y, typeof[x], false)
- }
- }
- }
-
- expand := func(s string) string {
- typ := cfg.Type[s]
- if typ != nil && typ.Def != "" {
- return typ.Def
- }
- return s
- }
-
- // The main type check is a recursive algorithm implemented
- // by walkBeforeAfter(n, before, after).
- // Most of it is bottom-up, but in a few places we need
- // to know the type of the function we are checking.
- // The before function records that information on
- // the curfn stack.
- var curfn []*ast.FuncType
-
- before := func(n any) {
- // push function type on stack
- switch n := n.(type) {
- case *ast.FuncDecl:
- curfn = append(curfn, n.Type)
- case *ast.FuncLit:
- curfn = append(curfn, n.Type)
- }
- }
-
- // After is the real type checker.
- after := func(n any) {
- if n == nil {
- return
- }
- if false && reflect.TypeOf(n).Kind() == reflect.Pointer { // debugging trace
- defer func() {
- if t := typeof[n]; t != "" {
- pos := fset.Position(n.(ast.Node).Pos())
- fmt.Fprintf(os.Stderr, "%s: typeof[%s] = %s\n", pos, gofmt(n), t)
- }
- }()
- }
-
- switch n := n.(type) {
- case *ast.FuncDecl, *ast.FuncLit:
- // pop function type off stack
- curfn = curfn[:len(curfn)-1]
-
- case *ast.FuncType:
- typeof[n] = mkType(joinFunc(split(typeof[n.Params]), split(typeof[n.Results])))
-
- case *ast.FieldList:
- // Field list is concatenation of sub-lists.
- t := ""
- for _, field := range n.List {
- if t != "" {
- t += ", "
- }
- t += typeof[field]
- }
- typeof[n] = t
-
- case *ast.Field:
- // Field is one instance of the type per name.
- all := ""
- t := typeof[n.Type]
- if !isType(t) {
- // Create a type, because it is typically *T or *p.T
- // and we might care about that type.
- t = mkType(gofmt(n.Type))
- typeof[n.Type] = t
- }
- t = getType(t)
- if len(n.Names) == 0 {
- all = t
- } else {
- for _, id := range n.Names {
- if all != "" {
- all += ", "
- }
- all += t
- typeof[id.Obj] = t
- typeof[id] = t
- }
- }
- typeof[n] = all
-
- case *ast.ValueSpec:
- // var declaration. Use type if present.
- if n.Type != nil {
- t := typeof[n.Type]
- if !isType(t) {
- t = mkType(gofmt(n.Type))
- typeof[n.Type] = t
- }
- t = getType(t)
- for _, id := range n.Names {
- set(id, t, true)
- }
- }
- // Now treat same as assignment.
- typecheckAssign(makeExprList(n.Names), n.Values, true)
-
- case *ast.AssignStmt:
- typecheckAssign(n.Lhs, n.Rhs, n.Tok == token.DEFINE)
-
- case *ast.Ident:
- // Identifier can take its type from underlying object.
- if t := typeof[n.Obj]; t != "" {
- typeof[n] = t
- }
-
- case *ast.SelectorExpr:
- // Field or method.
- name := n.Sel.Name
- if t := typeof[n.X]; t != "" {
- t = strings.TrimPrefix(t, "*") // implicit *
- if typ := cfg.Type[t]; typ != nil {
- if t := typ.dot(cfg, name); t != "" {
- typeof[n] = t
- return
- }
- }
- tt := typeof[t+"."+name]
- if isType(tt) {
- typeof[n] = getType(tt)
- return
- }
- }
- // Package selector.
- if x, ok := n.X.(*ast.Ident); ok && x.Obj == nil {
- str := x.Name + "." + name
- if cfg.Type[str] != nil {
- typeof[n] = mkType(str)
- return
- }
- if t := cfg.typeof(x.Name + "." + name); t != "" {
- typeof[n] = t
- return
- }
- }
-
- case *ast.CallExpr:
- // make(T) has type T.
- if isTopName(n.Fun, "make") && len(n.Args) >= 1 {
- typeof[n] = gofmt(n.Args[0])
- return
- }
- // new(T) has type *T
- if isTopName(n.Fun, "new") && len(n.Args) == 1 {
- typeof[n] = "*" + gofmt(n.Args[0])
- return
- }
- // Otherwise, use type of function to determine arguments.
- t := typeof[n.Fun]
- if t == "" {
- t = cfg.External[gofmt(n.Fun)]
- }
- in, out := splitFunc(t)
- if in == nil && out == nil {
- return
- }
- typeof[n] = join(out)
- for i, arg := range n.Args {
- if i >= len(in) {
- break
- }
- if typeof[arg] == "" {
- typeof[arg] = in[i]
- }
- }
-
- case *ast.TypeAssertExpr:
- // x.(type) has type of x.
- if n.Type == nil {
- typeof[n] = typeof[n.X]
- return
- }
- // x.(T) has type T.
- if t := typeof[n.Type]; isType(t) {
- typeof[n] = getType(t)
- } else {
- typeof[n] = gofmt(n.Type)
- }
-
- case *ast.SliceExpr:
- // x[i:j] has type of x.
- typeof[n] = typeof[n.X]
-
- case *ast.IndexExpr:
- // x[i] has key type of x's type.
- t := expand(typeof[n.X])
- if strings.HasPrefix(t, "[") || strings.HasPrefix(t, "map[") {
- // Lazy: assume there are no nested [] in the array
- // length or map key type.
- if _, elem, ok := strings.Cut(t, "]"); ok {
- typeof[n] = elem
- }
- }
-
- case *ast.StarExpr:
- // *x for x of type *T has type T when x is an expr.
- // We don't use the result when *x is a type, but
- // compute it anyway.
- t := expand(typeof[n.X])
- if isType(t) {
- typeof[n] = "type *" + getType(t)
- } else if strings.HasPrefix(t, "*") {
- typeof[n] = t[len("*"):]
- }
-
- case *ast.UnaryExpr:
- // &x for x of type T has type *T.
- t := typeof[n.X]
- if t != "" && n.Op == token.AND {
- typeof[n] = "*" + t
- }
-
- case *ast.CompositeLit:
- // T{...} has type T.
- typeof[n] = gofmt(n.Type)
-
- // Propagate types down to values used in the composite literal.
- t := expand(typeof[n])
- if strings.HasPrefix(t, "[") { // array or slice
- // Lazy: assume there are no nested [] in the array length.
- if _, et, ok := strings.Cut(t, "]"); ok {
- for _, e := range n.Elts {
- if kv, ok := e.(*ast.KeyValueExpr); ok {
- e = kv.Value
- }
- if typeof[e] == "" {
- typeof[e] = et
- }
- }
- }
- }
- if strings.HasPrefix(t, "map[") { // map
- // Lazy: assume there are no nested [] in the map key type.
- if kt, vt, ok := strings.Cut(t[len("map["):], "]"); ok {
- for _, e := range n.Elts {
- if kv, ok := e.(*ast.KeyValueExpr); ok {
- if typeof[kv.Key] == "" {
- typeof[kv.Key] = kt
- }
- if typeof[kv.Value] == "" {
- typeof[kv.Value] = vt
- }
- }
- }
- }
- }
- if typ := cfg.Type[t]; typ != nil && len(typ.Field) > 0 { // struct
- for _, e := range n.Elts {
- if kv, ok := e.(*ast.KeyValueExpr); ok {
- if ft := typ.Field[fmt.Sprintf("%s", kv.Key)]; ft != "" {
- if typeof[kv.Value] == "" {
- typeof[kv.Value] = ft
- }
- }
- }
- }
- }
-
- case *ast.ParenExpr:
- // (x) has type of x.
- typeof[n] = typeof[n.X]
-
- case *ast.RangeStmt:
- t := expand(typeof[n.X])
- if t == "" {
- return
- }
- var key, value string
- if t == "string" {
- key, value = "int", "rune"
- } else if strings.HasPrefix(t, "[") {
- key = "int"
- _, value, _ = strings.Cut(t, "]")
- } else if strings.HasPrefix(t, "map[") {
- if k, v, ok := strings.Cut(t[len("map["):], "]"); ok {
- key, value = k, v
- }
- }
- changed := false
- if n.Key != nil && key != "" {
- changed = true
- set(n.Key, key, n.Tok == token.DEFINE)
- }
- if n.Value != nil && value != "" {
- changed = true
- set(n.Value, value, n.Tok == token.DEFINE)
- }
- // Ugly failure of vision: already type-checked body.
- // Do it again now that we have that type info.
- if changed {
- typecheck1(cfg, n.Body, typeof, assign)
- }
-
- case *ast.TypeSwitchStmt:
- // Type of variable changes for each case in type switch,
- // but go/parser generates just one variable.
- // Repeat type check for each case with more precise
- // type information.
- as, ok := n.Assign.(*ast.AssignStmt)
- if !ok {
- return
- }
- varx, ok := as.Lhs[0].(*ast.Ident)
- if !ok {
- return
- }
- t := typeof[varx]
- for _, cas := range n.Body.List {
- cas := cas.(*ast.CaseClause)
- if len(cas.List) == 1 {
- // Variable has specific type only when there is
- // exactly one type in the case list.
- if tt := typeof[cas.List[0]]; isType(tt) {
- tt = getType(tt)
- typeof[varx] = tt
- typeof[varx.Obj] = tt
- typecheck1(cfg, cas.Body, typeof, assign)
- }
- }
- }
- // Restore t.
- typeof[varx] = t
- typeof[varx.Obj] = t
-
- case *ast.ReturnStmt:
- if len(curfn) == 0 {
- // Probably can't happen.
- return
- }
- f := curfn[len(curfn)-1]
- res := n.Results
- if f.Results != nil {
- t := split(typeof[f.Results])
- for i := 0; i < len(res) && i < len(t); i++ {
- set(res[i], t[i], false)
- }
- }
-
- case *ast.BinaryExpr:
- // Propagate types across binary ops that require two args of the same type.
- switch n.Op {
- case token.EQL, token.NEQ: // TODO: more cases. This is enough for the cftype fix.
- if typeof[n.X] != "" && typeof[n.Y] == "" {
- typeof[n.Y] = typeof[n.X]
- }
- if typeof[n.X] == "" && typeof[n.Y] != "" {
- typeof[n.X] = typeof[n.Y]
- }
- }
- }
- }
- walkBeforeAfter(f, before, after)
-}
-
-// Convert between function type strings and lists of types.
-// Using strings makes this a little harder, but it makes
-// a lot of the rest of the code easier. This will all go away
-// when we can use go/typechecker directly.
-
-// splitFunc splits "func(x,y,z) (a,b,c)" into ["x", "y", "z"] and ["a", "b", "c"].
-func splitFunc(s string) (in, out []string) {
- if !strings.HasPrefix(s, "func(") {
- return nil, nil
- }
-
- i := len("func(") // index of beginning of 'in' arguments
- nparen := 0
- for j := i; j < len(s); j++ {
- switch s[j] {
- case '(':
- nparen++
- case ')':
- nparen--
- if nparen < 0 {
- // found end of parameter list
- out := strings.TrimSpace(s[j+1:])
- if len(out) >= 2 && out[0] == '(' && out[len(out)-1] == ')' {
- out = out[1 : len(out)-1]
- }
- return split(s[i:j]), split(out)
- }
- }
- }
- return nil, nil
-}
-
-// joinFunc is the inverse of splitFunc.
-func joinFunc(in, out []string) string {
- outs := ""
- if len(out) == 1 {
- outs = " " + out[0]
- } else if len(out) > 1 {
- outs = " (" + join(out) + ")"
- }
- return "func(" + join(in) + ")" + outs
-}
-
-// split splits "int, float" into ["int", "float"] and splits "" into [].
-func split(s string) []string {
- out := []string{}
- i := 0 // current type being scanned is s[i:j].
- nparen := 0
- for j := 0; j < len(s); j++ {
- switch s[j] {
- case ' ':
- if i == j {
- i++
- }
- case '(':
- nparen++
- case ')':
- nparen--
- if nparen < 0 {
- // probably can't happen
- return nil
- }
- case ',':
- if nparen == 0 {
- if i < j {
- out = append(out, s[i:j])
- }
- i = j + 1
- }
- }
- }
- if nparen != 0 {
- // probably can't happen
- return nil
- }
- if i < len(s) {
- out = append(out, s[i:])
- }
- return out
-}
-
-// join is the inverse of split.
-func join(x []string) string {
- return strings.Join(x, ", ")
-}
diff --git a/src/cmd/go.mod b/src/cmd/go.mod
index c72a250aea..017883a787 100644
--- a/src/cmd/go.mod
+++ b/src/cmd/go.mod
@@ -11,7 +11,7 @@ require (
golang.org/x/sys v0.36.0
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053
golang.org/x/term v0.34.0
- golang.org/x/tools v0.37.1-0.20250915202913-9fccddc465ef
+ golang.org/x/tools v0.37.1-0.20250924232827-4df13e317ce4
)
require (
diff --git a/src/cmd/go.sum b/src/cmd/go.sum
index a4801f1843..0906ffcc60 100644
--- a/src/cmd/go.sum
+++ b/src/cmd/go.sum
@@ -22,7 +22,7 @@ golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
-golang.org/x/tools v0.37.1-0.20250915202913-9fccddc465ef h1:ISPkUgvOYIt0oS7oVnwAPktCKBvgWkDlWWGMgX0veZM=
-golang.org/x/tools v0.37.1-0.20250915202913-9fccddc465ef/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
+golang.org/x/tools v0.37.1-0.20250924232827-4df13e317ce4 h1:IcXDtHggZZo+GzNzvVRPyNFLnOc2/Z1gg3ZVIWF2uCU=
+golang.org/x/tools v0.37.1-0.20250924232827-4df13e317ce4/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8=
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ=
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index 3e691abe41..e4ee9bd1e8 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -9,7 +9,6 @@ import (
"debug/elf"
"debug/macho"
"debug/pe"
- "encoding/binary"
"flag"
"fmt"
"go/format"
@@ -2131,38 +2130,6 @@ func testBuildmodePIE(t *testing.T, useCgo, setBuildmodeToPIE bool) {
if (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) == 0 {
t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
}
- if useCgo {
- // Test that only one symbol is exported (#40795).
- // PIE binaries don´t require .edata section but unfortunately
- // binutils doesn´t generate a .reloc section unless there is
- // at least one symbol exported.
- // See https://sourceware.org/bugzilla/show_bug.cgi?id=19011
- section := f.Section(".edata")
- if section == nil {
- t.Skip(".edata section is not present")
- }
- // TODO: deduplicate this struct from cmd/link/internal/ld/pe.go
- type IMAGE_EXPORT_DIRECTORY struct {
- _ [2]uint32
- _ [2]uint16
- _ [2]uint32
- NumberOfFunctions uint32
- NumberOfNames uint32
- _ [3]uint32
- }
- var e IMAGE_EXPORT_DIRECTORY
- if err := binary.Read(section.Open(), binary.LittleEndian, &e); err != nil {
- t.Fatalf("binary.Read failed: %v", err)
- }
-
- // Only _cgo_dummy_export should be exported
- if e.NumberOfFunctions != 1 {
- t.Fatalf("got %d exported functions; want 1", e.NumberOfFunctions)
- }
- if e.NumberOfNames != 1 {
- t.Fatalf("got %d exported names; want 1", e.NumberOfNames)
- }
- }
default:
// testBuildmodePIE opens object files, so it needs to understand the object
// file format.
diff --git a/src/cmd/internal/obj/riscv/anames.go b/src/cmd/internal/obj/riscv/anames.go
index a8807fc7a8..6c48e2f7de 100644
--- a/src/cmd/internal/obj/riscv/anames.go
+++ b/src/cmd/internal/obj/riscv/anames.go
@@ -195,20 +195,16 @@ var Anames = []string{
"FLTQ",
"FCLASSQ",
"CLWSP",
- "CFLWSP",
"CLDSP",
"CFLDSP",
"CSWSP",
"CSDSP",
- "CFSWSP",
"CFSDSP",
"CLW",
"CLD",
- "CFLW",
"CFLD",
"CSW",
"CSD",
- "CFSW",
"CFSD",
"CJ",
"CJR",
diff --git a/src/cmd/internal/obj/riscv/cpu.go b/src/cmd/internal/obj/riscv/cpu.go
index 305ef061e3..60174a0b3a 100644
--- a/src/cmd/internal/obj/riscv/cpu.go
+++ b/src/cmd/internal/obj/riscv/cpu.go
@@ -588,22 +588,18 @@ const (
// 26.3.1: Compressed Stack-Pointer-Based Loads and Stores
ACLWSP
- ACFLWSP
ACLDSP
ACFLDSP
ACSWSP
ACSDSP
- ACFSWSP
ACFSDSP
// 26.3.2: Compressed Register-Based Loads and Stores
ACLW
ACLD
- ACFLW
ACFLD
ACSW
ACSD
- ACFSW
ACFSD
// 26.4: Compressed Control Transfer Instructions
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go
index 9d595f301c..91642ffbcb 100644
--- a/src/cmd/internal/obj/riscv/obj.go
+++ b/src/cmd/internal/obj/riscv/obj.go
@@ -2176,6 +2176,12 @@ var instructions = [ALAST & obj.AMask]instructionData{
AVSOXEI32V & obj.AMask: {enc: sVIVEncoding},
AVSOXEI64V & obj.AMask: {enc: sVIVEncoding},
+ // 31.7.7: Unit-stride Fault-Only-First Loads
+ AVLE8FFV & obj.AMask: {enc: iVEncoding},
+ AVLE16FFV & obj.AMask: {enc: iVEncoding},
+ AVLE32FFV & obj.AMask: {enc: iVEncoding},
+ AVLE64FFV & obj.AMask: {enc: iVEncoding},
+
// 31.7.8: Vector Load/Store Segment Instructions
AVLSEG2E8V & obj.AMask: {enc: iVEncoding},
AVLSEG3E8V & obj.AMask: {enc: iVEncoding},
@@ -3839,7 +3845,7 @@ func instructionsForProg(p *obj.Prog) []*instruction {
ins.rs1 = uint32(p.From.Offset)
}
- case AVLE8V, AVLE16V, AVLE32V, AVLE64V, AVSE8V, AVSE16V, AVSE32V, AVSE64V, AVLMV, AVSMV,
+ case AVLE8V, AVLE16V, AVLE32V, AVLE64V, AVSE8V, AVSE16V, AVSE32V, AVSE64V, AVLE8FFV, AVLE16FFV, AVLE32FFV, AVLE64FFV, AVLMV, AVSMV,
AVLSEG2E8V, AVLSEG3E8V, AVLSEG4E8V, AVLSEG5E8V, AVLSEG6E8V, AVLSEG7E8V, AVLSEG8E8V,
AVLSEG2E16V, AVLSEG3E16V, AVLSEG4E16V, AVLSEG5E16V, AVLSEG6E16V, AVLSEG7E16V, AVLSEG8E16V,
AVLSEG2E32V, AVLSEG3E32V, AVLSEG4E32V, AVLSEG5E32V, AVLSEG6E32V, AVLSEG7E32V, AVLSEG8E32V,
diff --git a/src/cmd/link/dwarf_test.go b/src/cmd/link/dwarf_test.go
index d269aa70c6..4ca578498d 100644
--- a/src/cmd/link/dwarf_test.go
+++ b/src/cmd/link/dwarf_test.go
@@ -358,3 +358,65 @@ func TestDWARFLocationList(t *testing.T) {
}
}
}
+
+func TestFlagW(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ tmpdir := t.TempDir()
+ src := filepath.Join(tmpdir, "a.go")
+ err := os.WriteFile(src, []byte(helloSrc), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ type testCase struct {
+ flag string
+ wantDWARF bool
+ }
+ tests := []testCase{
+ {"-w", false}, // -w flag disables DWARF
+ {"-s", false}, // -s implies -w
+ {"-s -w=0", true}, // -w=0 negates the implied -w
+ }
+ if testenv.HasCGO() {
+ tests = append(tests,
+ testCase{"-w -linkmode=external", false},
+ testCase{"-s -linkmode=external", false},
+ // Some external linkers don't have a way to preserve DWARF
+ // without emitting the symbol table. Skip this case for now.
+ // I suppose we can post- process, e.g. with objcopy.
+ //testCase{"-s -w=0 -linkmode=external", true},
+ )
+ }
+
+ for _, test := range tests {
+ name := strings.ReplaceAll(test.flag, " ", "_")
+ t.Run(name, func(t *testing.T) {
+ ldflags := "-ldflags=" + test.flag
+ exe := filepath.Join(t.TempDir(), "a.exe")
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", ldflags, "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("build failed: %v\n%s", err, out)
+ }
+
+ f, err := objfile.Open(exe)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if test.wantDWARF {
+ if err != nil {
+ t.Errorf("want binary with DWARF, got error %v", err)
+ }
+ } else {
+ if d != nil {
+ t.Errorf("want binary with no DWARF, got DWARF")
+ }
+ }
+ })
+ }
+}
diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go
index b8127a2538..5424de800c 100644
--- a/src/cmd/link/internal/amd64/asm.go
+++ b/src/cmd/link/internal/amd64/asm.go
@@ -208,7 +208,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
}
// The second relocation has the target symbol we want
su.SetRelocType(rIdx+1, objabi.R_PCREL)
- su.SetRelocAdd(rIdx+1, r.Add()+int64(r.Off())-off)
+ su.SetRelocAdd(rIdx+1, r.Add()+int64(r.Off())+int64(r.Siz())-off)
// Remove the other relocation
su.SetRelocSiz(rIdx, 0)
return true
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 1a1bc18675..623acc1ad4 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1451,6 +1451,8 @@ func (ctxt *Link) hostlink() {
} else {
argv = append(argv, "-s")
}
+ } else if *FlagW {
+ argv = append(argv, "-Wl,-S") // suppress debugging symbols
}
// On darwin, whether to combine DWARF into executable.
diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
index c290410b0e..5219a98dd4 100644
--- a/src/cmd/link/internal/ld/pe.go
+++ b/src/cmd/link/internal/ld/pe.go
@@ -487,9 +487,6 @@ func (f *peFile) addDWARFSection(name string, size int) *peSection {
// addDWARF adds DWARF information to the COFF file f.
func (f *peFile) addDWARF() {
- if *FlagS { // disable symbol table
- return
- }
if *FlagW { // disable dwarf
return
}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
index a4e455d9b3..d35b85f03a 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
@@ -157,7 +157,10 @@ func checkCopyLocksCallExpr(pass *analysis.Pass, ce *ast.CallExpr) {
}
if fun, ok := pass.TypesInfo.Uses[id].(*types.Builtin); ok {
switch fun.Name() {
- case "new", "len", "cap", "Sizeof", "Offsetof", "Alignof":
+ case "len", "cap", "Sizeof", "Offsetof", "Alignof":
+ // The argument of this operation is used only
+ // for its type (e.g. len(array)), or the operation
+ // does not copy a lock (e.g. len(slice)).
return
}
}
diff --git a/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go
index bc7f9984e9..cea89d34da 100644
--- a/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go
+++ b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go
@@ -193,18 +193,23 @@ func CheckReadable(pass *analysis.Pass, filename string) error {
return fmt.Errorf("Pass.ReadFile: %s is not among OtherFiles, IgnoredFiles, or names of Files", filename)
}
-// AddImport checks whether this file already imports pkgpath and
-// that import is in scope at pos. If so, it returns the name under
-// which it was imported and a zero edit. Otherwise, it adds a new
-// import of pkgpath, using a name derived from the preferred name,
-// and returns the chosen name, a prefix to be concatenated with member
-// to form a qualified name, and the edit for the new import.
+// AddImport checks whether this file already imports pkgpath and that
+// the import is in scope at pos. If so, it returns the name under
+// which it was imported and no edits. Otherwise, it adds a new import
+// of pkgpath, using a name derived from the preferred name, and
+// returns the chosen name, a prefix to be concatenated with member to
+// form a qualified name, and the edit for the new import.
//
-// In the special case that pkgpath is dot-imported then member, the
-// identifier for which the import is being added, is consulted. If
-// member is not shadowed at pos, AddImport returns (".", "", nil).
-// (AddImport accepts the caller's implicit claim that the imported
-// package declares member.)
+// The member argument indicates the name of the desired symbol within
+// the imported package. This is needed in the case when the existing
+// import is a dot import, because then it is possible that the
+// desired symbol is shadowed by other declarations in the current
+// package. If member is not shadowed at pos, AddImport returns (".",
+// "", nil). (AddImport accepts the caller's implicit claim that the
+// imported package declares member.)
+//
+// Use a preferredName of "_" to request a blank import;
+// member is ignored in this case.
//
// It does not mutate its arguments.
func AddImport(info *types.Info, file *ast.File, preferredName, pkgpath, member string, pos token.Pos) (name, prefix string, newImport []analysis.TextEdit) {
@@ -220,6 +225,10 @@ func AddImport(info *types.Info, file *ast.File, preferredName, pkgpath, member
pkgname := info.PkgNameOf(spec)
if pkgname != nil && pkgname.Imported().Path() == pkgpath {
name = pkgname.Name()
+ if preferredName == "_" {
+ // Request for blank import; any existing import will do.
+ return name, "", nil
+ }
if name == "." {
// The scope of ident must be the file scope.
if s, _ := scope.LookupParent(member, pos); s == info.Scopes[file] {
@@ -232,8 +241,12 @@ func AddImport(info *types.Info, file *ast.File, preferredName, pkgpath, member
}
// We must add a new import.
+
// Ensure we have a fresh name.
- newName := FreshName(scope, pos, preferredName)
+ newName := preferredName
+ if preferredName != "_" {
+ newName = FreshName(scope, pos, preferredName)
+ }
// Create a new import declaration either before the first existing
// declaration (which must exist), including its comments; or
@@ -246,6 +259,7 @@ func AddImport(info *types.Info, file *ast.File, preferredName, pkgpath, member
if newName != preferredName || newName != pathpkg.Base(pkgpath) {
newText = fmt.Sprintf("%s %q", newName, pkgpath)
}
+
decl0 := file.Decls[0]
var before ast.Node = decl0
switch decl0 := decl0.(type) {
diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt
index f166b77ea2..133271355f 100644
--- a/src/cmd/vendor/modules.txt
+++ b/src/cmd/vendor/modules.txt
@@ -73,7 +73,7 @@ golang.org/x/text/internal/tag
golang.org/x/text/language
golang.org/x/text/transform
golang.org/x/text/unicode/norm
-# golang.org/x/tools v0.37.1-0.20250915202913-9fccddc465ef
+# golang.org/x/tools v0.37.1-0.20250924232827-4df13e317ce4
## explicit; go 1.24.0
golang.org/x/tools/cmd/bisect
golang.org/x/tools/cover