aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Findley <rfindley@google.com>2021-09-15 17:18:37 -0400
committerRobert Findley <rfindley@google.com>2021-09-17 22:19:21 +0000
commit3fa35b5f9741d7d1c9a9e047057c7210da04fbba (patch)
tree7ffdc54df20d009a2f38bd40c6d4b8f4274b8e54
parent3fa7dbeff53b56edd98f295bd0c34423c080ac57 (diff)
downloadgo-3fa35b5f9741d7d1c9a9e047057c7210da04fbba.tar.gz
go-3fa35b5f9741d7d1c9a9e047057c7210da04fbba.zip
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>
-rw-r--r--src/go/types/instantiate_test.go43
-rw-r--r--src/go/types/named.go6
2 files changed, 49 insertions, 0 deletions
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 = &copy
+ }
sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp)
m.typ = sig