Skip to content

Commit

Permalink
go/types, types2: add unifyMode to unifier, pass it through
Browse files Browse the repository at this point in the history
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 golang#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 golang#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>
  • Loading branch information
griesemer authored and Hiro committed Jun 4, 2023
1 parent bf058e1 commit bc625d4
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 56 deletions.
8 changes: 4 additions & 4 deletions src/cmd/compile/internal/types2/infer.go
Expand Up @@ -155,7 +155,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
// 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
}
Expand Down Expand Up @@ -230,7 +230,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
// 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
}
Expand All @@ -248,7 +248,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
// 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
}
Expand Down Expand Up @@ -340,7 +340,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
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
}
Expand Down
54 changes: 30 additions & 24 deletions src/cmd/compile/internal/types2/unify.go
Expand Up @@ -106,10 +106,13 @@ func newUnifier(tparams []*TypeParam, targs []Type) *unifier {
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{}) {
Expand Down Expand Up @@ -241,10 +244,10 @@ func (u *unifier) inferred(tparams []*TypeParam) []Type {
// 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 {
Expand Down Expand Up @@ -324,13 +327,13 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
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
Expand Down Expand Up @@ -361,6 +364,9 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
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.
Expand Down Expand Up @@ -427,7 +433,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
}
// 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
}
}
Expand All @@ -448,7 +454,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
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
}
}
Expand All @@ -474,13 +480,13 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
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:
Expand All @@ -495,7 +501,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
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
}
}
Expand All @@ -506,7 +512,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
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:
Expand All @@ -517,7 +523,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
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
}
}
Expand All @@ -534,8 +540,8 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
// 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:
Expand Down Expand Up @@ -592,7 +598,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
}
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
}
}
Expand All @@ -603,13 +609,13 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
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:
Expand All @@ -625,11 +631,11 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
// 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.
}
Expand All @@ -643,7 +649,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
return false
}
for i, xarg := range xargs {
if !u.nify(xarg, yargs[i], p) {
if !u.nify(xarg, yargs[i], mode, p) {
return false
}
}
Expand Down Expand Up @@ -678,7 +684,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
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
Expand All @@ -687,7 +693,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
// 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
Expand Down
8 changes: 4 additions & 4 deletions src/go/types/infer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit bc625d4

Please sign in to comment.