aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/types2/unify.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/types2/unify.go')
-rw-r--r--src/cmd/compile/internal/types2/unify.go60
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 {