mirror of https://github.com/golang/go.git
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:
parent
3fa7dbeff5
commit
3fa35b5f97
|
|
@ -6,6 +6,7 @@ package types_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "go/types"
|
. "go/types"
|
||||||
|
"strings"
|
||||||
"testing"
|
"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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -309,6 +309,12 @@ func (check *Checker) completeMethod(env *Environment, m *Func) {
|
||||||
|
|
||||||
smap := makeSubstMap(origSig.RecvTypeParams().list(), rtyp.targs.list())
|
smap := makeSubstMap(origSig.RecvTypeParams().list(), rtyp.targs.list())
|
||||||
sig := check.subst(orig.pos, origSig, smap, env).(*Signature)
|
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)
|
sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp)
|
||||||
|
|
||||||
m.typ = sig
|
m.typ = sig
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue