internal/core/eval: minor performance improvements for disjunctions
This also remove some duplicates.
This makes Issue #758 a bit more pallatable, although
the core issue remains.
Change-Id: I0d7c60c96cc7ebf65380c677626215f586d7a7e9
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8801
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/testdata/disjunctions/embed.txtar b/cue/testdata/disjunctions/embed.txtar
index 5d8e063..3a98652 100644
--- a/cue/testdata/disjunctions/embed.txtar
+++ b/cue/testdata/disjunctions/embed.txtar
@@ -159,10 +159,6 @@
e: (string){ string }
}, (#struct){
d: (string){ string }
- }, (#struct){
- d: (string){ string }
- }, (#struct){
- d: (string){ string }
e: (string){ string }
}, (#struct){
a: (string){ string }
@@ -186,12 +182,6 @@
}, (#struct){
a: (string){ string }
d: (string){ string }
- }, (#struct){
- a: (string){ string }
- d: (string){ string }
- }, (#struct){
- a: (string){ string }
- d: (string){ string }
e: (string){ string }
}, (#struct){
b: (string){ string }
@@ -215,12 +205,6 @@
}, (#struct){
b: (string){ string }
d: (string){ string }
- }, (#struct){
- b: (string){ string }
- d: (string){ string }
- }, (#struct){
- b: (string){ string }
- d: (string){ string }
e: (string){ string }
}) }
a: (struct){ |(*(#struct){
diff --git a/cue/types.go b/cue/types.go
index e78c41f..8e09deb 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -1831,7 +1831,7 @@
if v.v == nil || other.v == nil {
return false
}
- return adt.Equal(v.ctx().opCtx, v.v, other.v, false)
+ return adt.Equal(v.ctx().opCtx, v.v, other.v, adt.CheckStructural)
}
// Format prints a debug version of a value.
diff --git a/internal/core/adt/disjunct.go b/internal/core/adt/disjunct.go
index 636f07d..f03a338 100644
--- a/internal/core/adt/disjunct.go
+++ b/internal/core/adt/disjunct.go
@@ -122,7 +122,7 @@
state VertexStatus,
parent *nodeContext,
parentMode defaultMode, // default mode of this disjunct
- recursive bool) {
+ recursive, last bool) {
n.ctx.stats.DisjunctCount++
@@ -210,6 +210,7 @@
n.disjuncts = n.buffer[:0]
n.buffer = a[:0]
+ last := i+1 == len(n.disjunctions)
skipNonMonotonicChecks := i+1 < len(n.disjunctions)
if skipNonMonotonicChecks {
n.ctx.inDisjunct++
@@ -228,7 +229,7 @@
newMode := mode(d.hasDefaults, v.Default)
- cn.expandDisjuncts(state, n, newMode, true)
+ cn.expandDisjuncts(state, n, newMode, true, last)
}
case d.value != nil:
@@ -241,7 +242,7 @@
newMode := mode(d.hasDefaults, i < d.value.NumDefaults)
- cn.expandDisjuncts(state, n, newMode, true)
+ cn.expandDisjuncts(state, n, newMode, true, last)
}
}
}
@@ -365,7 +366,11 @@
outer:
for _, d := range n.disjuncts {
for k, v := range p.disjuncts {
- if Equal(n.ctx, &v.result, &d.result, true) {
+ flags := CheckStructural
+ if last {
+ flags |= IgnoreOptional
+ }
+ if Equal(n.ctx, &v.result, &d.result, flags) {
m := maybeDefault
for _, u := range d.usedDefault {
m = combineDefault(m, u.nestedMode)
diff --git a/internal/core/adt/equality.go b/internal/core/adt/equality.go
index de57df9..d1b5a1d 100644
--- a/internal/core/adt/equality.go
+++ b/internal/core/adt/equality.go
@@ -14,17 +14,30 @@
package adt
-func Equal(ctx *OpContext, v, w Value, optional bool) bool {
+type Flag uint16
+
+const (
+ // IgnoreOptional allows optional information to be ignored. This only
+ // applies when CheckStructural is given.
+ IgnoreOptional Flag = 1 << iota
+
+ // CheckStructural indicates that closedness information should be
+ // considered for equality. Equal may return false even when values are
+ // equal.
+ CheckStructural Flag = 1 << iota
+)
+
+func Equal(ctx *OpContext, v, w Value, flags Flag) bool {
if x, ok := v.(*Vertex); ok {
- return equalVertex(ctx, x, w, optional)
+ return equalVertex(ctx, x, w, flags)
}
if y, ok := w.(*Vertex); ok {
- return equalVertex(ctx, y, v, optional)
+ return equalVertex(ctx, y, v, flags)
}
- return equalTerminal(ctx, v, w, optional)
+ return equalTerminal(ctx, v, w, flags)
}
-func equalVertex(ctx *OpContext, x *Vertex, v Value, opt bool) bool {
+func equalVertex(ctx *OpContext, x *Vertex, v Value, flags Flag) bool {
y, ok := v.(*Vertex)
if !ok {
return false
@@ -47,7 +60,7 @@
if x.IsClosed(ctx) != y.IsClosed(ctx) {
return false
}
- if opt && !equalClosed(ctx, x, y) {
+ if flags != 0 && !equalClosed(ctx, x, y, flags) {
return false
}
@@ -55,7 +68,7 @@
for _, a := range x.Arcs {
for _, b := range y.Arcs {
if a.Label == b.Label {
- if !Equal(ctx, a, b, opt) {
+ if !Equal(ctx, a, b, flags) {
return false
}
continue loop1
@@ -81,7 +94,7 @@
return true // both are struct or list.
}
- return equalTerminal(ctx, v, w, opt)
+ return equalTerminal(ctx, v, w, flags)
}
// equalClosed tests if x and y have the same set of close information.
@@ -93,16 +106,21 @@
//
// For all these refinements it would be necessary to have well-working
// structure sharing so as to not repeatedly recompute optional arcs.
-func equalClosed(ctx *OpContext, x, y *Vertex) bool {
- return verifyStructs(x, y) && verifyStructs(y, x)
+func equalClosed(ctx *OpContext, x, y *Vertex, flags Flag) bool {
+ return verifyStructs(x, y, flags) && verifyStructs(y, x, flags)
}
-func verifyStructs(x, y *Vertex) bool {
+func verifyStructs(x, y *Vertex, flags Flag) bool {
outer:
for _, s := range x.Structs {
- if s.closeInfo == nil || s.closeInfo.span|DefinitionSpan == 0 {
+ if (flags&IgnoreOptional != 0) && !s.StructLit.HasOptional() {
continue
}
+ if s.closeInfo == nil || s.closeInfo.span&DefinitionSpan == 0 {
+ if !s.StructLit.HasOptional() {
+ continue
+ }
+ }
for _, t := range y.Structs {
if s.StructLit == t.StructLit {
continue outer
@@ -113,7 +131,7 @@
return true
}
-func equalTerminal(ctx *OpContext, v, w Value, opt bool) bool {
+func equalTerminal(ctx *OpContext, v, w Value, flags Flag) bool {
if v == w {
return true
}
@@ -130,7 +148,7 @@
case *BoundValue:
if y, ok := w.(*BoundValue); ok {
- return x.Op == y.Op && Equal(ctx, x.Value, y.Value, opt)
+ return x.Op == y.Op && Equal(ctx, x.Value, y.Value, flags)
}
case *BasicType:
@@ -145,7 +163,7 @@
}
// always ordered the same
for i, xe := range x.Values {
- if !Equal(ctx, xe, y.Values[i], opt) {
+ if !Equal(ctx, xe, y.Values[i], flags) {
return false
}
}
@@ -159,7 +177,7 @@
return false
}
for i, xe := range x.Values {
- if !Equal(ctx, xe, y.Values[i], opt) {
+ if !Equal(ctx, xe, y.Values[i], flags) {
return false
}
}
diff --git a/internal/core/adt/eval.go b/internal/core/adt/eval.go
index 7386e66..330c54b 100644
--- a/internal/core/adt/eval.go
+++ b/internal/core/adt/eval.go
@@ -306,7 +306,7 @@
if len(n.disjunctions) > 0 && disState != Finalized {
disState = Finalized
}
- n.expandDisjuncts(disState, n, maybeDefault, false)
+ n.expandDisjuncts(disState, n, maybeDefault, false, true)
n.finalizeDisjuncts()
diff --git a/internal/core/adt/simplify.go b/internal/core/adt/simplify.go
index dea3066..4bf0876 100644
--- a/internal/core/adt/simplify.go
+++ b/internal/core/adt/simplify.go
@@ -214,7 +214,7 @@
return nil
}
for i, a := range x.Args {
- if !Equal(ctx, a, y.Args[i], false) {
+ if !Equal(ctx, a, y.Args[i], CheckStructural) {
return nil
}
}