From fc2e860eda39f6bcbe1fecac7b92a50598d1fab9 Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Thu, 1 Oct 2026 22:18:19 +0800 Subject: [PATCH 1/3] go/ir: avoid canonicalizing function signatures --- go/ir/instantiate.go | 5 ++++- internal/passes/buildir/buildir_test.go | 6 ++++++ .../testdata/src/issue1749/issue1749.go | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 internal/passes/buildir/testdata/src/issue1749/issue1749.go diff --git a/go/ir/instantiate.go b/go/ir/instantiate.go index bcb6d9a89..cfdf16e01 100644 --- a/go/ir/instantiate.go +++ b/go/ir/instantiate.go @@ -97,7 +97,10 @@ func createInstance(fn *Function, rtargs, targs []types.Type) *Function { panic("Instantiate of a Signature returned a non-signature") } obj = fn.object // instantiation does not exist yet - sig = prog.canon.Type(instance).(*types.Signature) + // Do not canonicalize signatures here. Receiver types are not part of + // signature identity, so a function signature may otherwise reuse a + // previously canonicalized method signature with a receiver. + sig = instance } // Choose strategy (instance or wrapper). diff --git a/internal/passes/buildir/buildir_test.go b/internal/passes/buildir/buildir_test.go index fe78b3af3..358104ff7 100644 --- a/internal/passes/buildir/buildir_test.go +++ b/internal/passes/buildir/buildir_test.go @@ -11,6 +11,7 @@ import ( "golang.org/x/tools/go/analysis/analysistest" "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/internal/xtools-internal/testenv" ) func Test(t *testing.T) { @@ -26,3 +27,8 @@ func Test(t *testing.T) { } } } + +func TestGenericMethodValueWithGenericFunction(t *testing.T) { + testenv.NeedsGoCommand1Point(t, 27) + analysistest.Run(t, analysistest.TestData(), buildir.Analyzer, "issue1749") +} diff --git a/internal/passes/buildir/testdata/src/issue1749/issue1749.go b/internal/passes/buildir/testdata/src/issue1749/issue1749.go new file mode 100644 index 000000000..03f424749 --- /dev/null +++ b/internal/passes/buildir/testdata/src/issue1749/issue1749.go @@ -0,0 +1,18 @@ +package issue1749 + +type T struct{} + +func (T) M[P any]() P { + var zero P + return zero +} + +func G[P any]() P { + var zero P + return zero +} + +func F(t T) string { + f := t.M[string] + return f() + G[string]() +} From b423ddc27146d11b182d470ccde80cdffbbff348 Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Fri, 2 Oct 2026 01:27:46 +0800 Subject: [PATCH 2/3] return cache helper base64 close errors --- lintcmd/cache/prog.go | 2 +- lintcmd/cache/prog_test.go | 54 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 lintcmd/cache/prog_test.go diff --git a/lintcmd/cache/prog.go b/lintcmd/cache/prog.go index 3c134cbb8..a080abfdf 100644 --- a/lintcmd/cache/prog.go +++ b/lintcmd/cache/prog.go @@ -226,7 +226,7 @@ func (c *ProgCache) writeToChild(req *cacheprog.Request, resc chan<- *cacheprog. return err } if err := e.Close(); err != nil { - return nil + return err } if wrote != req.BodySize { return fmt.Errorf("short write writing body to GOCACHEPROG for action %x, output %x: wrote %v; expected %v", diff --git a/lintcmd/cache/prog_test.go b/lintcmd/cache/prog_test.go new file mode 100644 index 000000000..0f150f3e9 --- /dev/null +++ b/lintcmd/cache/prog_test.go @@ -0,0 +1,54 @@ +package cache + +import ( + "bufio" + "bytes" + "encoding/json" + "errors" + "strings" + "testing" + + "honnef.co/go/tools/lintcmd/cache/cacheprog" +) + +var errTestWriter = errors.New("test writer failed") + +type failingWriter struct{} + +func (failingWriter) Write([]byte) (int, error) { return 0, errTestWriter } + +func TestWriteToChildReturnsBase64CloseError(t *testing.T) { + const groups = 1000 + bodySize := groups*3 + 1 // The base64 encoder buffers a final padded group. + body := strings.Repeat("x", bodySize) + req := &cacheprog.Request{ + ID: 1, + Command: cacheprog.CmdPut, + ActionID: []byte{1}, + OutputID: []byte{2}, + BodySize: int64(bodySize), + } + var header bytes.Buffer + if err := json.NewEncoder(&header).Encode(req); err != nil { + t.Fatal(err) + } + // Leave three bytes available after the request header and the unpadded + // base64 body, so the final padded group makes the buffer flush and fail + // from Encoder.Close. + bufferSize := header.Len() + 2 + groups*4 + 3 + pc := &ProgCache{ + inFlight: map[int64]chan<- *cacheprog.Response{}, + bw: bufio.NewWriterSize(failingWriter{}, bufferSize), + } + pc.jenc = json.NewEncoder(pc.bw) + got := pc.writeToChild(&cacheprog.Request{ + Command: cacheprog.CmdPut, + ActionID: []byte{1}, + OutputID: []byte{2}, + Body: strings.NewReader(body), + BodySize: int64(bodySize), + }, make(chan *cacheprog.Response, 1)) + if !errors.Is(got, errTestWriter) { + t.Fatalf("writeToChild error = %v, want %v", got, errTestWriter) + } +} From 51e576fa6c7af6d3fbdc82d98c1e40a9f8b17ea2 Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Fri, 2 Oct 2026 14:01:16 +0800 Subject: [PATCH 3/3] Keep cache flush fix independent of generic IR change --- go/ir/instantiate.go | 5 +---- internal/passes/buildir/buildir_test.go | 6 ------ .../testdata/src/issue1749/issue1749.go | 18 ------------------ 3 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 internal/passes/buildir/testdata/src/issue1749/issue1749.go diff --git a/go/ir/instantiate.go b/go/ir/instantiate.go index cfdf16e01..bcb6d9a89 100644 --- a/go/ir/instantiate.go +++ b/go/ir/instantiate.go @@ -97,10 +97,7 @@ func createInstance(fn *Function, rtargs, targs []types.Type) *Function { panic("Instantiate of a Signature returned a non-signature") } obj = fn.object // instantiation does not exist yet - // Do not canonicalize signatures here. Receiver types are not part of - // signature identity, so a function signature may otherwise reuse a - // previously canonicalized method signature with a receiver. - sig = instance + sig = prog.canon.Type(instance).(*types.Signature) } // Choose strategy (instance or wrapper). diff --git a/internal/passes/buildir/buildir_test.go b/internal/passes/buildir/buildir_test.go index 358104ff7..fe78b3af3 100644 --- a/internal/passes/buildir/buildir_test.go +++ b/internal/passes/buildir/buildir_test.go @@ -11,7 +11,6 @@ import ( "golang.org/x/tools/go/analysis/analysistest" "honnef.co/go/tools/internal/passes/buildir" - "honnef.co/go/tools/internal/xtools-internal/testenv" ) func Test(t *testing.T) { @@ -27,8 +26,3 @@ func Test(t *testing.T) { } } } - -func TestGenericMethodValueWithGenericFunction(t *testing.T) { - testenv.NeedsGoCommand1Point(t, 27) - analysistest.Run(t, analysistest.TestData(), buildir.Analyzer, "issue1749") -} diff --git a/internal/passes/buildir/testdata/src/issue1749/issue1749.go b/internal/passes/buildir/testdata/src/issue1749/issue1749.go deleted file mode 100644 index 03f424749..000000000 --- a/internal/passes/buildir/testdata/src/issue1749/issue1749.go +++ /dev/null @@ -1,18 +0,0 @@ -package issue1749 - -type T struct{} - -func (T) M[P any]() P { - var zero P - return zero -} - -func G[P any]() P { - var zero P - return zero -} - -func F(t T) string { - f := t.M[string] - return f() + G[string]() -}