]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: ensure that we always get a new signature in expandNamed
authorRobert Findley <rfindley@google.com>
Wed, 15 Sep 2021 21:18:37 +0000 (17:18 -0400)
committerRobert Findley <rfindley@google.com>
Fri, 17 Sep 2021 22:19:21 +0000 (22:19 +0000)
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>
src/go/types/instantiate_test.go
src/go/types/named.go

index 851800e76dbb255b9c7c9d97e4126afeeaffe721..0c66acb8750c4b4c325e03c248ae6fb46b7d2219 100644 (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
+}
index 00fde16445c1fc7412083bfac3238290abde2d69..4a263410fca4ee03bba69b201b56b8a24d06688d 100644 (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