diff options
Diffstat (limited to 'src/cmd/compile/internal/types2/unify.go')
-rw-r--r-- | src/cmd/compile/internal/types2/unify.go | 60 |
1 files changed, 49 insertions, 11 deletions
diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go index 3e2b299e49..0f1423ff98 100644 --- a/src/cmd/compile/internal/types2/unify.go +++ b/src/cmd/compile/internal/types2/unify.go @@ -270,6 +270,15 @@ func (u *unifier) inferred(tparams []*TypeParam) []Type { return list } +// asInterface returns the underlying type of x as an interface if +// it is a non-type parameter interface. Otherwise it returns nil. +func asInterface(x Type) (i *Interface) { + if _, ok := x.(*TypeParam); !ok { + i, _ = under(x).(*Interface) + } + return i +} + // nify implements the core unification algorithm which is an // adapted version of Checker.identical. For changes to that // code the corresponding changes should be made here. @@ -364,11 +373,46 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) { if x := u.at(px); x != nil { // x has an inferred type which must match y if u.nify(x, y, mode, p) { - // If we have a match, possibly through underlying types, - // and y is a defined type, make sure we record that type + // We have a match, possibly through underlying types. + xi := asInterface(x) + yi := asInterface(y) + _, xn := x.(*Named) + _, yn := y.(*Named) + // If we have two interfaces, what to do depends on + // whether they are named and their method sets. + if xi != nil && yi != nil { + // Both types are interfaces. + // If both types are defined types, they must be identical + // because unification doesn't know which type has the "right" name. + if xn && yn { + return Identical(x, y) + } + // In all other cases, the method sets must match. + // The types unified so we know that corresponding methods + // match and we can simply compare the number of methods. + // TODO(gri) We may be able to relax this rule and select + // the more general interface. But if one of them is a defined + // type, it's not clear how to choose and whether we introduce + // an order dependency or not. Requiring the same method set + // is conservative. + if len(xi.typeSet().methods) != len(yi.typeSet().methods) { + return false + } + } else if xi != nil || yi != nil { + // One but not both of them are interfaces. + // In this case, either x or y could be viable matches for the corresponding + // type parameter, which means choosing either introduces an order dependence. + // Therefore, we must fail unification (go.dev/issue/60933). + return false + } + // If y is a defined type, make sure we record that type // for type parameter x, which may have until now only // recorded an underlying type (go.dev/issue/43056). - if _, ok := y.(*Named); ok { + // Either both types are interfaces, or neither type is. + // If both are interfaces, they have the same methods. + // TODO(gri) We probably can do this only for inexact + // unification. Need to find a failure case. + if yn { u.set(px, y) } return true @@ -398,14 +442,8 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) { if enableInterfaceInference && mode&exact == 0 { // One or both interfaces may be defined types. // Look under the name, but not under type parameters (go.dev/issue/60564). - var xi *Interface - if _, ok := x.(*TypeParam); !ok { - xi, _ = under(x).(*Interface) - } - var yi *Interface - if _, ok := y.(*TypeParam); !ok { - yi, _ = under(y).(*Interface) - } + xi := asInterface(x) + yi := asInterface(y) // If we have two interfaces, check the type terms for equivalence, // and unify common methods if possible. if xi != nil && yi != nil { |