Pass a mode parameter through all unifier calls but make no use of it.
When unifying type elements (components of composite types), use emode,
which currently is set to mode.
Preparatory step to fix #60460. Factoring out this mechanical change
will make the actual fix smaller and easier to review and understand.
Because this change doesn't affect the behavior of the unifier, it is
safe.
For #60460.
Change-Id: I5b67499d93025be2128c14cc00bcc3b8cc9f44b2
Reviewed-on: https://go-review.googlesource.com/c/go/+/498955
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
// Function parameters are always typed. Arguments may be untyped.
// Collect the indices of untyped arguments and handle them later.
if isTyped(arg.typ) {
- if !u.unify(par.typ, arg.typ) {
+ if !u.unify(par.typ, arg.typ, 0) {
errorf("type", par.typ, arg.typ, arg)
return nil
}
// core type.
// 2) If the core type doesn't have a tilde, we also must unify tx
// with the core type.
- if !u.unify(tx, core.typ) {
+ if !u.unify(tx, core.typ, 0) {
check.errorf(pos, CannotInferTypeArgs, "%s does not match %s", tpar, core.typ)
return nil
}
// the constraint.
var cause string
constraint := tpar.iface()
- if m, _ := check.missingMethod(tx, constraint, true, u.unify, &cause); m != nil {
+ if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, 0) }, &cause); m != nil {
check.errorf(pos, CannotInferTypeArgs, "%s does not satisfy %s %s", tx, constraint, cause)
return nil
}
arg := args[i]
typ := Default(arg.typ)
assert(isTyped(typ))
- if !u.unify(tpar, typ) {
+ if !u.unify(tpar, typ, 0) {
errorf("default type", tpar, typ, arg)
return nil
}
return &unifier{handles, 0}
}
+// unifyMode controls the behavior of the unifier.
+type unifyMode uint
+
// unify attempts to unify x and y and reports whether it succeeded.
// As a side-effect, types may be inferred for type parameters.
-func (u *unifier) unify(x, y Type) bool {
- return u.nify(x, y, nil)
+func (u *unifier) unify(x, y Type, mode unifyMode) bool {
+ return u.nify(x, y, mode, nil)
}
func (u *unifier) tracef(format string, args ...interface{}) {
// adapted version of Checker.identical. For changes to that
// code the corresponding changes should be made here.
// Must not be called directly from outside the unifier.
-func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
+func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
u.depth++
if traceInference {
- u.tracef("%s ≡ %s", x, y)
+ u.tracef("%s ≡ %s (mode %d)", x, y, mode)
}
defer func() {
if traceInference && !result {
return true
}
// both x and y have an inferred type - they must match
- return u.nify(u.at(px), u.at(py), p)
+ return u.nify(u.at(px), u.at(py), mode, p)
case px != nil:
// x is a type parameter, y is not
if x := u.at(px); x != nil {
// x has an inferred type which must match y
- if u.nify(x, y, p) {
+ 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
// for type parameter x, which may have until now only
x, y = y, x
}
+ // Type elements (array, slice, etc. elements) use emode for unification.
+ emode := mode
+
// If EnableInterfaceInference is set and both types are interfaces, one
// interface must have a subset of the methods of the other and corresponding
// method signatures must unify.
}
// 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, p) {
+ if ym := ymap[xm.Id()]; ym == nil || !u.nify(xm.typ, ym.typ, emode, 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, p) {
+ if ym, _ := obj.(*Func); ym == nil || !u.nify(xm.typ, ym.typ, emode, p) {
return false
}
}
if y, ok := y.(*Array); ok {
// If one or both array lengths are unknown (< 0) due to some error,
// assume they are the same to avoid spurious follow-on errors.
- return (x.len < 0 || y.len < 0 || x.len == y.len) && u.nify(x.elem, y.elem, p)
+ return (x.len < 0 || y.len < 0 || x.len == y.len) && u.nify(x.elem, y.elem, emode, p)
}
case *Slice:
// Two slice types unify if their element types unify.
if y, ok := y.(*Slice); ok {
- return u.nify(x.elem, y.elem, p)
+ return u.nify(x.elem, y.elem, emode, p)
}
case *Struct:
if f.embedded != g.embedded ||
x.Tag(i) != y.Tag(i) ||
!f.sameId(g.pkg, g.name) ||
- !u.nify(f.typ, g.typ, p) {
+ !u.nify(f.typ, g.typ, emode, p) {
return false
}
}
case *Pointer:
// Two pointer types unify if their base types unify.
if y, ok := y.(*Pointer); ok {
- return u.nify(x.base, y.base, p)
+ return u.nify(x.base, y.base, emode, p)
}
case *Tuple:
if x != nil {
for i, v := range x.vars {
w := y.vars[i]
- if !u.nify(v.typ, w.typ, p) {
+ if !u.nify(v.typ, w.typ, mode, p) {
return false
}
}
// TODO(gri) handle type parameters or document why we can ignore them.
if y, ok := y.(*Signature); ok {
return x.variadic == y.variadic &&
- u.nify(x.params, y.params, p) &&
- u.nify(x.results, y.results, p)
+ u.nify(x.params, y.params, emode, p) &&
+ u.nify(x.results, y.results, emode, p)
}
case *Interface:
}
for i, f := range a {
g := b[i]
- if f.Id() != g.Id() || !u.nify(f.typ, g.typ, q) {
+ if f.Id() != g.Id() || !u.nify(f.typ, g.typ, emode, q) {
return false
}
}
case *Map:
// Two map types unify if their key and value types unify.
if y, ok := y.(*Map); ok {
- return u.nify(x.key, y.key, p) && u.nify(x.elem, y.elem, p)
+ return u.nify(x.key, y.key, emode, p) && u.nify(x.elem, y.elem, emode, p)
}
case *Chan:
// Two channel types unify if their value types unify.
if y, ok := y.(*Chan); ok {
- return u.nify(x.elem, y.elem, p)
+ return u.nify(x.elem, y.elem, emode, p)
}
case *Named:
// If one or both of x and y are interfaces, use interface unification.
switch {
case xi != nil && yi != nil:
- return u.nify(xi, yi, p)
+ return u.nify(xi, yi, mode, p)
case xi != nil:
- return u.nify(xi, y, p)
+ return u.nify(xi, y, mode, p)
case yi != nil:
- return u.nify(x, yi, p)
+ return u.nify(x, yi, mode, p)
}
// In all other cases, the type arguments and origins must match.
}
return false
}
for i, xarg := range xargs {
- if !u.nify(xarg, yargs[i], p) {
+ if !u.nify(xarg, yargs[i], mode, p) {
return false
}
}
if traceInference {
u.tracef("core %s ≡ %s", x, y)
}
- return u.nify(cx, y, p)
+ return u.nify(cx, y, mode, p)
}
}
// x != y and there's nothing to do
// avoid a crash in case of nil type
default:
- panic(sprintf(nil, true, "u.nify(%s, %s)", x, y))
+ panic(sprintf(nil, true, "u.nify(%s, %s, %d)", x, y, mode))
}
return false
// Function parameters are always typed. Arguments may be untyped.
// Collect the indices of untyped arguments and handle them later.
if isTyped(arg.typ) {
- if !u.unify(par.typ, arg.typ) {
+ if !u.unify(par.typ, arg.typ, 0) {
errorf("type", par.typ, arg.typ, arg)
return nil
}
// core type.
// 2) If the core type doesn't have a tilde, we also must unify tx
// with the core type.
- if !u.unify(tx, core.typ) {
+ if !u.unify(tx, core.typ, 0) {
check.errorf(posn, CannotInferTypeArgs, "%s does not match %s", tpar, core.typ)
return nil
}
// the constraint.
var cause string
constraint := tpar.iface()
- if m, _ := check.missingMethod(tx, constraint, true, u.unify, &cause); m != nil {
+ if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, 0) }, &cause); m != nil {
check.errorf(posn, CannotInferTypeArgs, "%s does not satisfy %s %s", tx, constraint, cause)
return nil
}
arg := args[i]
typ := Default(arg.typ)
assert(isTyped(typ))
- if !u.unify(tpar, typ) {
+ if !u.unify(tpar, typ, 0) {
errorf("default type", tpar, typ, arg)
return nil
}
return &unifier{handles, 0}
}
+// unifyMode controls the behavior of the unifier.
+type unifyMode uint
+
// unify attempts to unify x and y and reports whether it succeeded.
// As a side-effect, types may be inferred for type parameters.
-func (u *unifier) unify(x, y Type) bool {
- return u.nify(x, y, nil)
+func (u *unifier) unify(x, y Type, mode unifyMode) bool {
+ return u.nify(x, y, mode, nil)
}
func (u *unifier) tracef(format string, args ...interface{}) {
// adapted version of Checker.identical. For changes to that
// code the corresponding changes should be made here.
// Must not be called directly from outside the unifier.
-func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
+func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
u.depth++
if traceInference {
- u.tracef("%s ≡ %s", x, y)
+ u.tracef("%s ≡ %s (mode %d)", x, y, mode)
}
defer func() {
if traceInference && !result {
return true
}
// both x and y have an inferred type - they must match
- return u.nify(u.at(px), u.at(py), p)
+ return u.nify(u.at(px), u.at(py), mode, p)
case px != nil:
// x is a type parameter, y is not
if x := u.at(px); x != nil {
// x has an inferred type which must match y
- if u.nify(x, y, p) {
+ 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
// for type parameter x, which may have until now only
x, y = y, x
}
+ // Type elements (array, slice, etc. elements) use emode for unification.
+ emode := mode
+
// If EnableInterfaceInference is set and both types are interfaces, one
// interface must have a subset of the methods of the other and corresponding
// method signatures must unify.
}
// 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, p) {
+ if ym := ymap[xm.Id()]; ym == nil || !u.nify(xm.typ, ym.typ, emode, 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, p) {
+ if ym, _ := obj.(*Func); ym == nil || !u.nify(xm.typ, ym.typ, emode, p) {
return false
}
}
if y, ok := y.(*Array); ok {
// If one or both array lengths are unknown (< 0) due to some error,
// assume they are the same to avoid spurious follow-on errors.
- return (x.len < 0 || y.len < 0 || x.len == y.len) && u.nify(x.elem, y.elem, p)
+ return (x.len < 0 || y.len < 0 || x.len == y.len) && u.nify(x.elem, y.elem, emode, p)
}
case *Slice:
// Two slice types unify if their element types unify.
if y, ok := y.(*Slice); ok {
- return u.nify(x.elem, y.elem, p)
+ return u.nify(x.elem, y.elem, emode, p)
}
case *Struct:
if f.embedded != g.embedded ||
x.Tag(i) != y.Tag(i) ||
!f.sameId(g.pkg, g.name) ||
- !u.nify(f.typ, g.typ, p) {
+ !u.nify(f.typ, g.typ, emode, p) {
return false
}
}
case *Pointer:
// Two pointer types unify if their base types unify.
if y, ok := y.(*Pointer); ok {
- return u.nify(x.base, y.base, p)
+ return u.nify(x.base, y.base, emode, p)
}
case *Tuple:
if x != nil {
for i, v := range x.vars {
w := y.vars[i]
- if !u.nify(v.typ, w.typ, p) {
+ if !u.nify(v.typ, w.typ, mode, p) {
return false
}
}
// TODO(gri) handle type parameters or document why we can ignore them.
if y, ok := y.(*Signature); ok {
return x.variadic == y.variadic &&
- u.nify(x.params, y.params, p) &&
- u.nify(x.results, y.results, p)
+ u.nify(x.params, y.params, emode, p) &&
+ u.nify(x.results, y.results, emode, p)
}
case *Interface:
}
for i, f := range a {
g := b[i]
- if f.Id() != g.Id() || !u.nify(f.typ, g.typ, q) {
+ if f.Id() != g.Id() || !u.nify(f.typ, g.typ, emode, q) {
return false
}
}
case *Map:
// Two map types unify if their key and value types unify.
if y, ok := y.(*Map); ok {
- return u.nify(x.key, y.key, p) && u.nify(x.elem, y.elem, p)
+ return u.nify(x.key, y.key, emode, p) && u.nify(x.elem, y.elem, emode, p)
}
case *Chan:
// Two channel types unify if their value types unify.
if y, ok := y.(*Chan); ok {
- return u.nify(x.elem, y.elem, p)
+ return u.nify(x.elem, y.elem, emode, p)
}
case *Named:
// If one or both of x and y are interfaces, use interface unification.
switch {
case xi != nil && yi != nil:
- return u.nify(xi, yi, p)
+ return u.nify(xi, yi, mode, p)
case xi != nil:
- return u.nify(xi, y, p)
+ return u.nify(xi, y, mode, p)
case yi != nil:
- return u.nify(x, yi, p)
+ return u.nify(x, yi, mode, p)
}
// In all other cases, the type arguments and origins must match.
}
return false
}
for i, xarg := range xargs {
- if !u.nify(xarg, yargs[i], p) {
+ if !u.nify(xarg, yargs[i], mode, p) {
return false
}
}
if traceInference {
u.tracef("core %s ≡ %s", x, y)
}
- return u.nify(cx, y, p)
+ return u.nify(cx, y, mode, p)
}
}
// x != y and there's nothing to do
// avoid a crash in case of nil type
default:
- panic(sprintf(nil, nil, true, "u.nify(%s, %s)", x, y))
+ panic(sprintf(nil, nil, true, "u.nify(%s, %s, %d)", x, y, mode))
}
return false