[dev.typeparams] go/types: unify methods in missingMethod

Unify methods in Checker.missingMethod. This code was accidentally
dropped from the merge, while dropping support for method type
parameters, but is needed for checking implementations of generic
interfaces.

Put the logic back, including checks that are only needed for method
type parameters. It makes the code no simpler to assume that method type
parameters are disallowed, and we have checks elsewhere that produce
errors for methods with type parameters.

Change-Id: I91f0c9d3e04537fdb9f7ae23a4ce4cec9f1da10e
Reviewed-on: https://go-review.googlesource.com/c/go/+/284252
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Rob Findley 2021-01-15 11:19:13 -05:00 committed by Robert Findley
parent 958927c824
commit 2e64511ac9

View file

@ -325,21 +325,30 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
return m, f return m, f
} }
if !check.identical(f.Type(), m.Type()) { ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature)
if len(ftyp.tparams) != len(mtyp.tparams) {
return m, f return m, f
} }
// TODO(rFindley) delete this note once the spec has stabilized to // If the methods have type parameters we don't care whether they
// exclude method type parameters. // are the same or not, as long as they match up. Use unification
// NOTE: if enabling method type parameters, we need to unify f.Type() // to see if they can be made to match.
// and m.Type() here to verify that their type parameters align (assuming // TODO(gri) is this always correct? what about type bounds?
// this behaves correctly with respect to type bounds). // (Alternative is to rename/subst type parameters and compare.)
u := newUnifier(check, true)
u.x.init(ftyp.tparams)
if !u.unify(ftyp, mtyp) {
return m, f
}
} }
return return
} }
// A concrete type implements T if it implements all methods of T. // A concrete type implements T if it implements all methods of T.
Vd, _ := deref(V)
Vn := asNamed(Vd)
for _, m := range T.allMethods { for _, m := range T.allMethods {
// TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)? // TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)?
obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name) obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name)
@ -368,16 +377,43 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
check.objDecl(f, nil) check.objDecl(f, nil)
} }
if !check.identical(f.Type(), m.Type()) { // both methods must have the same number of type parameters
ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature)
if len(ftyp.tparams) != len(mtyp.tparams) {
return m, f return m, f
} }
// TODO(rFindley) delete this note once the spec has stabilized to exclude // If V is a (instantiated) generic type, its methods are still
// method type parameters. // parameterized using the original (declaration) receiver type
// NOTE: if enabling method type parameters, one needs to subst any // parameters (subst simply copies the existing method list, it
// receiver type parameters for V here, and unify f.Type() with m.Type() to // does not instantiate the methods).
// verify that their type parameters align (assuming this behaves correctly // In order to compare the signatures, substitute the receiver
// with respect to type bounds). // type parameters of ftyp with V's instantiation type arguments.
// This lazily instantiates the signature of method f.
if Vn != nil && len(Vn.tparams) > 0 {
// Be careful: The number of type arguments may not match
// the number of receiver parameters. If so, an error was
// reported earlier but the length discrepancy is still
// here. Exit early in this case to prevent an assertion
// failure in makeSubstMap.
// TODO(gri) Can we avoid this check by fixing the lengths?
if len(ftyp.rparams) != len(Vn.targs) {
return
}
ftyp = check.subst(token.NoPos, ftyp, makeSubstMap(ftyp.rparams, Vn.targs)).(*Signature)
}
// If the methods have type parameters we don't care whether they
// are the same or not, as long as they match up. Use unification
// to see if they can be made to match.
// TODO(gri) is this always correct? what about type bounds?
// (Alternative is to rename/subst type parameters and compare.)
u := newUnifier(check, true)
u.x.init(ftyp.tparams)
if !u.unify(ftyp, mtyp) {
return m, f
}
} }
return return