// the core types, if any, of non-local (unbound) type parameters.
enableCoreTypeUnification = true
- // If enableInterfaceInference is set, type inference uses
- // shared methods for improved type inference involving
- // interfaces.
- enableInterfaceInference = true
-
// If traceInference is set, unification will print a trace of its operation.
// Interpretation of trace:
// x ≡ y attempt to unify types x and y
// that inferring the type for a given type parameter P will
// automatically infer the same type for all other parameters
// unified (joined) with P.
- handles map[*TypeParam]*Type
- depth int // recursion depth during unification
+ handles map[*TypeParam]*Type
+ depth int // recursion depth during unification
+ enableInterfaceInference bool // use shared methods for better inference
}
// newUnifier returns a new unifier initialized with the given type parameter
// and corresponding type argument lists. The type argument list may be shorter
// than the type parameter list, and it may contain nil types. Matching type
// parameters and arguments must have the same index.
-func newUnifier(tparams []*TypeParam, targs []Type) *unifier {
+func newUnifier(tparams []*TypeParam, targs []Type, enableInterfaceInference bool) *unifier {
assert(len(tparams) >= len(targs))
handles := make(map[*TypeParam]*Type, len(tparams))
// Allocate all handles up-front: in a correct program, all type parameters
}
handles[x] = &t
}
- return &unifier{handles, 0}
+ return &unifier{handles, 0, enableInterfaceInference}
}
// unifyMode controls the behavior of the unifier.
u.depth--
}()
+ x = Unalias(x)
+ y = Unalias(y)
+
// nothing to do if x == y
if x == y {
return true
// Ensure that if we have at least one
// - defined type, make sure one is in y
// - type parameter recorded with u, make sure one is in x
- if _, ok := x.(*Named); ok || u.asTypeParam(y) != nil {
+ if asNamed(x) != nil || u.asTypeParam(y) != nil {
if traceInference {
u.tracef("%s ≡ %s\t// swap", y, x)
}
// we will fail at function instantiation or argument assignment time.
//
// If we have at least one defined type, there is one in y.
- if ny, _ := y.(*Named); mode&exact == 0 && ny != nil && isTypeLit(x) && !(enableInterfaceInference && IsInterface(x)) {
+ if ny := asNamed(y); mode&exact == 0 && ny != nil && isTypeLit(x) && !(u.enableInterfaceInference && IsInterface(x)) {
if traceInference {
u.tracef("%s ≡ under %s", x, ny)
}
// We have a match, possibly through underlying types.
xi := asInterface(x)
yi := asInterface(y)
- _, xn := x.(*Named)
- _, yn := y.(*Named)
+ xn := asNamed(x) != nil
+ yn := asNamed(y) != nil
// If we have two interfaces, what to do depends on
// whether they are named and their method sets.
if xi != nil && yi != nil {
// 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).
- // Either both types are interfaces, or neither type is.
- // If both are interfaces, they have the same methods.
+ // If we have inexact unification and one of x or y is a defined type, select the
+ // defined type. This ensures that in a series of types, all matching against the
+ // same type parameter, we infer a defined type if there is one, independent of
+ // order. Type inference or assignment may fail, which is ok.
+ // Selecting a defined type, if any, ensures that we don't lose the type name;
+ // and since we have inexact unification, a value of equally named or matching
+ // undefined type remains assignable (go.dev/issue/43056).
+ //
+ // Similarly, if we have inexact unification and there are no defined types but
+ // channel types, select a directed channel, if any. This ensures that in a series
+ // of unnamed types, all matching against the same type parameter, we infer the
+ // directed channel if there is one, independent of order.
+ // Selecting a directional channel, if any, ensures that a value of another
+ // inexactly unifying channel type remains assignable (go.dev/issue/62157).
//
- // Note: Changing the recorded type for a type parameter to
- // a defined type is only ok when unification is inexact.
- // But in exact unification, if we have a match, x and y must
- // be identical, so changing the recorded type for x is a no-op.
- if yn {
- u.set(px, y)
+ // If we have multiple defined channel types, they are either identical or we
+ // have assignment conflicts, so we can ignore directionality in this case.
+ //
+ // If we have defined and literal channel types, a defined type wins to avoid
+ // order dependencies.
+ if mode&exact == 0 {
+ switch {
+ case xn:
+ // x is a defined type: nothing to do.
+ case yn:
+ // x is not a defined type and y is a defined type: select y.
+ u.set(px, y)
+ default:
+ // Neither x nor y are defined types.
+ if yc, _ := under(y).(*Chan); yc != nil && yc.dir != SendRecv {
+ // y is a directed channel type: select y.
+ u.set(px, y)
+ }
+ }
}
return true
}
// x != y if we get here
assert(x != y)
- // Type elements (array, slice, etc. elements) use emode for unification.
- // Element types must match exactly if the types are used in an assignment.
- emode := mode
- if mode&assign != 0 {
- emode |= exact
- }
-
- // If EnableInterfaceInference is set and we don't require exact unification,
+ // If u.EnableInterfaceInference is set and we don't require exact unification,
// if both types are interfaces, one interface must have a subset of the
// methods of the other and corresponding method signatures must unify.
// If only one type is an interface, all its methods must be present in the
// other type and corresponding method signatures must unify.
- if enableInterfaceInference && mode&exact == 0 {
+ if u.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).
xi := asInterface(x)
}
// All xmethods must exist in ymethods and corresponding signatures must unify.
for _, xm := range xmethods {
- if ym := ymap[xm.Id()]; ym == nil || !u.nify(xm.typ, ym.typ, emode, p) {
+ if ym := ymap[xm.Id()]; ym == nil || !u.nify(xm.typ, ym.typ, exact, p) {
return false
}
}
xmethods := xi.typeSet().methods
for _, xm := range xmethods {
obj, _, _ := LookupFieldOrMethod(y, false, xm.pkg, xm.name)
- if ym, _ := obj.(*Func); ym == nil || !u.nify(xm.typ, ym.typ, emode, p) {
+ if ym, _ := obj.(*Func); ym == nil || !u.nify(xm.typ, ym.typ, exact, p) {
return false
}
}
x, y = y, x
}
+ // Type elements (array, slice, etc. elements) use emode for unification.
+ // Element types must match exactly if the types are used in an assignment.
+ emode := mode
+ if mode&assign != 0 {
+ emode |= exact
+ }
+
switch x := x.(type) {
case *Basic:
// Basic types are singletons except for the rune and byte
}
case *Interface:
- assert(!enableInterfaceInference || mode&exact != 0) // handled before this switch
+ assert(!u.enableInterfaceInference || mode&exact != 0) // handled before this switch
// Two interface types unify if they have the same set of methods with
// the same names, and corresponding function types unify.
}
for i, f := range a {
g := b[i]
- if f.Id() != g.Id() || !u.nify(f.typ, g.typ, emode, q) {
+ if f.Id() != g.Id() || !u.nify(f.typ, g.typ, exact, q) {
return false
}
}
case *Named:
// Two named types unify if their type names originate in the same type declaration.
// If they are instantiated, their type argument lists must unify.
- if y, ok := y.(*Named); ok {
+ if y := asNamed(y); y != nil {
// Check type arguments before origins so they unify
// even if the origins don't match; for better error
// messages (see go.dev/issue/53692).
return false
}
}
- return indenticalOrigin(x, y)
+ return identicalOrigin(x, y)
}
case *TypeParam: