From 3fa35b5f9741d7d1c9a9e047057c7210da04fbba Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Wed, 15 Sep 2021 17:18:37 -0400 Subject: go/types: ensure that we always get a new signature in expandNamed CL 349412 introduced a bug when Checker.subst does not return a new signature: we were still setting the receiver to the instantiated type. I'm not sure how this could manifest in practice (other than confusing object strings). It's possible that I could generate a testdata-driven test for this, but in the interest of time I just added a test to verify object strings. Change-Id: I29bc8e1419ddc4574755c3def52d18cb71c738eb Reviewed-on: https://go-review.googlesource.com/c/go/+/350143 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/instantiate_test.go | 43 ++++++++++++++++++++++++++++++++++++++++ src/go/types/named.go | 6 ++++++ 2 files changed, 49 insertions(+) diff --git a/src/go/types/instantiate_test.go b/src/go/types/instantiate_test.go index 851800e76d..0c66acb875 100644 --- a/src/go/types/instantiate_test.go +++ b/src/go/types/instantiate_test.go @@ -6,6 +6,7 @@ package types_test import ( . "go/types" + "strings" "testing" ) @@ -109,3 +110,45 @@ var X T[int] } } } + +func TestImmutableSignatures(t *testing.T) { + const src = genericPkg + `p + +type T[P any] struct{} + +func (T[P]) m() {} + +var _ T[int] +` + pkg, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + typ := pkg.Scope().Lookup("T").Type().(*Named) + obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m") + if obj == nil { + t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj) + } + + // Verify that the original method is not mutated by instantiating T (this + // bug manifested when subst did not return a new signature). + want := "func (T[P]).m()" + if got := stripAnnotations(ObjectString(obj, RelativeTo(pkg))); got != want { + t.Errorf("instantiated %q, want %q", got, want) + } +} + +// Copied from errors.go. +func stripAnnotations(s string) string { + var b strings.Builder + for _, r := range s { + // strip #'s and subscript digits + if r < '₀' || '₀'+10 <= r { // '₀' == U+2080 + b.WriteRune(r) + } + } + if b.Len() < len(s) { + return b.String() + } + return s +} diff --git a/src/go/types/named.go b/src/go/types/named.go index 00fde16445..4a263410fc 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -309,6 +309,12 @@ func (check *Checker) completeMethod(env *Environment, m *Func) { smap := makeSubstMap(origSig.RecvTypeParams().list(), rtyp.targs.list()) sig := check.subst(orig.pos, origSig, smap, env).(*Signature) + if sig == origSig { + // No substitution occurred, but we still need to create a copy to hold the + // instantiated receiver. + copy := *origSig + sig = © + } sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp) m.typ = sig -- cgit v1.2.3-54-g00ecf