go/types, types2: simplify symmetric code
Because unification is symmetric, in cases where we have symmetric
code for x and y depending on some property we can swap x and y as
needed and simplify the code.
Also, change u.depth increment/decrement position for slightly
nicer tracing ooutput.
Change-Id: I2e84570d463d1c32f6556108f3cb54062b57c718
Reviewed-on: https://go-review.googlesource.com/c/go/+/464896
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Robert Griesemer <gri@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go
index abf159d..48be5ae 100644
--- a/src/cmd/compile/internal/types2/unify.go
+++ b/src/cmd/compile/internal/types2/unify.go
@@ -209,12 +209,19 @@
// 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) {
+ u.depth++
if traceInference {
u.tracef("%s ≡ %s", x, y)
}
+ defer func() {
+ if traceInference && !result {
+ u.tracef("%s ≢ %s", x, y)
+ }
+ u.depth--
+ }()
// Stop gap for cases where unification fails.
- if u.depth >= unificationDepthLimit {
+ if u.depth > unificationDepthLimit {
if traceInference {
u.tracef("depth %d >= %d", u.depth, unificationDepthLimit)
}
@@ -223,36 +230,36 @@
}
return false
}
- u.depth++
- defer func() {
- u.depth--
- if traceInference && !result {
- u.tracef("%s ≢ %s", x, y)
+
+ // Unification is symmetric, so we can swap the operands.
+ // Ensure that if we have at least one
+ // - defined type, make sure 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 traceInference {
+ u.tracef("%s ≡ %s (swap)", y, x)
}
- }()
+ x, y = y, x
+ }
// If exact unification is known to fail because we attempt to
- // match a type name against an unnamed type literal, consider
- // the underlying type of the named type.
+ // match a defined type against an unnamed type literal, consider
+ // the underlying type of the defined type.
+ // If we have at least one defined type, there is one in y.
// (We use !hasName to exclude any type with a name, including
// basic types and type parameters; the rest are unamed types.)
- if nx, _ := x.(*Named); nx != nil && !hasName(y) {
- if traceInference {
- u.tracef("under %s ≡ %s", nx, y)
- }
- x = nx.under()
- // Per the spec, a defined type cannot have an underlying type
- // that is a type parameter.
- assert(!isTypeParam(x))
- } else if ny, _ := y.(*Named); ny != nil && !hasName(x) {
+ if ny, _ := y.(*Named); ny != nil && !hasName(x) {
if traceInference {
u.tracef("%s ≡ under %s", x, ny)
}
y = ny.under()
+ // Per the spec, a defined type cannot have an underlying type
+ // that is a type parameter.
assert(!isTypeParam(y))
}
// Cases where at least one of x or y is a type parameter recorded with u.
+ // If we have ar least one type parameter, there is one in x.
switch px, py := u.asTypeParam(x), u.asTypeParam(y); {
case px != nil && py != nil:
// both x and y are type parameters
@@ -270,21 +277,20 @@
// otherwise, infer type from y
u.set(px, y)
return true
-
- case py != nil:
- // y is a type parameter, x is not
- if ty := u.at(py); ty != nil {
- return u.nifyEq(x, ty, p)
- }
- // otherwise, infer type from x
- u.set(py, x)
- return true
}
// If we get here and x or y is a type parameter, they are type parameters
// from outside our declaration list. Try to unify their core types, if any
// (see go.dev/issue/50755 for a test case).
if enableCoreTypeUnification {
+ // swap x and y as needed
+ // (the earlier swap checks for _recorded_ type parameters only)
+ if isTypeParam(y) {
+ if traceInference {
+ u.tracef("%s ≡ %s (swap)", y, x)
+ }
+ x, y = y, x
+ }
if isTypeParam(x) && !hasName(y) {
// When considering the type parameter for unification
// we look at the adjusted core term (adjusted core type
@@ -303,14 +309,6 @@
}
return u.nify(cx, y, p)
}
- } else if isTypeParam(y) && !hasName(x) {
- // see comment above
- if cy := coreType(y); cy != nil {
- if traceInference {
- u.tracef("%s ≡ core %s", x, y)
- }
- return u.nify(x, cy, p)
- }
}
}
diff --git a/src/go/types/unify.go b/src/go/types/unify.go
index 886e841..e104938 100644
--- a/src/go/types/unify.go
+++ b/src/go/types/unify.go
@@ -211,12 +211,19 @@
// 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) {
+ u.depth++
if traceInference {
u.tracef("%s ≡ %s", x, y)
}
+ defer func() {
+ if traceInference && !result {
+ u.tracef("%s ≢ %s", x, y)
+ }
+ u.depth--
+ }()
// Stop gap for cases where unification fails.
- if u.depth >= unificationDepthLimit {
+ if u.depth > unificationDepthLimit {
if traceInference {
u.tracef("depth %d >= %d", u.depth, unificationDepthLimit)
}
@@ -225,36 +232,36 @@
}
return false
}
- u.depth++
- defer func() {
- u.depth--
- if traceInference && !result {
- u.tracef("%s ≢ %s", x, y)
+
+ // Unification is symmetric, so we can swap the operands.
+ // Ensure that if we have at least one
+ // - defined type, make sure 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 traceInference {
+ u.tracef("%s ≡ %s (swap)", y, x)
}
- }()
+ x, y = y, x
+ }
// If exact unification is known to fail because we attempt to
- // match a type name against an unnamed type literal, consider
- // the underlying type of the named type.
+ // match a defined type against an unnamed type literal, consider
+ // the underlying type of the defined type.
+ // If we have at least one defined type, there is one in y.
// (We use !hasName to exclude any type with a name, including
// basic types and type parameters; the rest are unamed types.)
- if nx, _ := x.(*Named); nx != nil && !hasName(y) {
- if traceInference {
- u.tracef("under %s ≡ %s", nx, y)
- }
- x = nx.under()
- // Per the spec, a defined type cannot have an underlying type
- // that is a type parameter.
- assert(!isTypeParam(x))
- } else if ny, _ := y.(*Named); ny != nil && !hasName(x) {
+ if ny, _ := y.(*Named); ny != nil && !hasName(x) {
if traceInference {
u.tracef("%s ≡ under %s", x, ny)
}
y = ny.under()
+ // Per the spec, a defined type cannot have an underlying type
+ // that is a type parameter.
assert(!isTypeParam(y))
}
// Cases where at least one of x or y is a type parameter recorded with u.
+ // If we have ar least one type parameter, there is one in x.
switch px, py := u.asTypeParam(x), u.asTypeParam(y); {
case px != nil && py != nil:
// both x and y are type parameters
@@ -272,21 +279,20 @@
// otherwise, infer type from y
u.set(px, y)
return true
-
- case py != nil:
- // y is a type parameter, x is not
- if ty := u.at(py); ty != nil {
- return u.nifyEq(x, ty, p)
- }
- // otherwise, infer type from x
- u.set(py, x)
- return true
}
// If we get here and x or y is a type parameter, they are type parameters
// from outside our declaration list. Try to unify their core types, if any
// (see go.dev/issue/50755 for a test case).
if enableCoreTypeUnification {
+ // swap x and y as needed
+ // (the earlier swap checks for _recorded_ type parameters only)
+ if isTypeParam(y) {
+ if traceInference {
+ u.tracef("%s ≡ %s (swap)", y, x)
+ }
+ x, y = y, x
+ }
if isTypeParam(x) && !hasName(y) {
// When considering the type parameter for unification
// we look at the adjusted core term (adjusted core type
@@ -305,14 +311,6 @@
}
return u.nify(cx, y, p)
}
- } else if isTypeParam(y) && !hasName(x) {
- // see comment above
- if cy := coreType(y); cy != nil {
- if traceInference {
- u.tracef("%s ≡ core %s", x, y)
- }
- return u.nify(x, cy, p)
- }
}
}