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 <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Robert Findley 2021-09-15 17:18:37 -04:00
parent 3fa7dbeff5
commit 3fa35b5f97
2 changed files with 49 additions and 0 deletions

View File

@ -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
}

View File

@ -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 = &copy
}
sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp)
m.typ = sig