Skip to content

Commit

Permalink
internal/core/adt: "fix" disjunction resolution
Browse files Browse the repository at this point in the history
The recent performance improvement eliminated disjuncts
too aggressively, causing some disjuncts to disappear.
Early elimination prevents exponential blowup, but of course
it should not make it incorrect.

Note that for most common uses, like protobuf and K8s
structs with discriminator fields, special optimizations exist
that make this linear. But this has not been implemented yet.

Now this fix exposed another bug. This bug was deliberate to
work around a limitation of default values when representing
oneOf fields. We planned to introduce a "required field" annotation
which would also allow to represent these kind of semantics
much more elegantly.

For now, though, we are stuck in a position that more than one
oneOf field cannot be represented properly with defaults.
We fix it for now in an even more AWFUL way, and instead work
towards a proper solution based on required fields.

Issue #726

Change-Id: Id4bcd0445612e12fba48a744654de4e1852c552e
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8641
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
  • Loading branch information
mpvl committed Feb 6, 2021
1 parent f0d1fa4 commit 63104c8
Show file tree
Hide file tree
Showing 6 changed files with 365 additions and 20 deletions.
276 changes: 276 additions & 0 deletions cue/testdata/disjunctions/elimination.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,46 @@ nestedNonMonotonic: incomplete: b: n2: p1: {
x: { a: b: struct.MinFields(3) } | null
}

preserveClosedness: small: p1: {
#A: #B & {a: string}
#B: {
*{} | {a: string}
*{} | {b: int}
}
}

preserveClosedness: small: p2: {
#A: #B & {a: string}
#B: {
{a: string} | *{}
*{} | {b: int}
}
}

preserveClosedness: medium: p1: {
#A: #B & {a: string}
#B: {
*{} | {a: string} | {b: string}
*{} | {c: int} | {d: string}
}
}

preserveClosedness: medium: p2: {
#A: #B & {a: string}
#B: {
{a: string} | *{} | {b: string}
*{} | {c: int} | {d: string}
}
}

preserveClosedness: medium: p3: {
#A: #B & {a: string}
#B: {
{a: string} | {b: string} | *{}
*{} | {c: int} | {d: string}
}
}

-- out/eval --
(struct){
disambiguateClosed: (struct){
Expand Down Expand Up @@ -321,6 +361,145 @@ nestedNonMonotonic: incomplete: b: n2: p1: {
}
}
}
preserveClosedness: (struct){
small: (struct){
p1: (struct){
#A: (struct){ |(*(#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
b: (int){ int }
}) }
#B: (struct){ |(*(#struct){
}, (#struct){
b: (int){ int }
}, (#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
b: (int){ int }
}) }
}
p2: (struct){
#A: (struct){ |(*(#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
b: (int){ int }
}) }
#B: (struct){ |(*(#struct){
}, (#struct){
a: (string){ string }
b: (int){ int }
}, (#struct){
a: (string){ string }
}, (#struct){
b: (int){ int }
}) }
}
}
medium: (struct){
p1: (struct){
#A: (struct){ |(*(#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
c: (int){ int }
}, (#struct){
a: (string){ string }
d: (string){ string }
}) }
#B: (struct){ |(*(#struct){
}, (#struct){
c: (int){ int }
}, (#struct){
d: (string){ string }
}, (#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
c: (int){ int }
}, (#struct){
a: (string){ string }
d: (string){ string }
}, (#struct){
b: (string){ string }
}, (#struct){
b: (string){ string }
c: (int){ int }
}, (#struct){
b: (string){ string }
d: (string){ string }
}) }
}
p2: (struct){
#A: (struct){ |(*(#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
c: (int){ int }
}, (#struct){
a: (string){ string }
d: (string){ string }
}) }
#B: (struct){ |(*(#struct){
}, (#struct){
a: (string){ string }
c: (int){ int }
}, (#struct){
a: (string){ string }
d: (string){ string }
}, (#struct){
a: (string){ string }
}, (#struct){
c: (int){ int }
}, (#struct){
d: (string){ string }
}, (#struct){
b: (string){ string }
}, (#struct){
b: (string){ string }
c: (int){ int }
}, (#struct){
b: (string){ string }
d: (string){ string }
}) }
}
p3: (struct){
#A: (struct){ |(*(#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
c: (int){ int }
}, (#struct){
a: (string){ string }
d: (string){ string }
}) }
#B: (struct){ |(*(#struct){
}, (#struct){
a: (string){ string }
c: (int){ int }
}, (#struct){
a: (string){ string }
d: (string){ string }
}, (#struct){
b: (string){ string }
}, (#struct){
b: (string){ string }
c: (int){ int }
}, (#struct){
b: (string){ string }
d: (string){ string }
}, (#struct){
a: (string){ string }
}, (#struct){
c: (int){ int }
}, (#struct){
d: (string){ string }
}) }
}
}
}
}
-- out/compile --
--- in.cue
Expand Down Expand Up @@ -785,4 +964,101 @@ nestedNonMonotonic: incomplete: b: n2: p1: {
}
}
}
preserveClosedness: {
small: {
p1: {
#A: (〈0;#B〉 & {
a: string
})
#B: {
(*{}|{
a: string
})
(*{}|{
b: int
})
}
}
}
}
preserveClosedness: {
small: {
p2: {
#A: (〈0;#B〉 & {
a: string
})
#B: {
({
a: string
}|*{})
(*{}|{
b: int
})
}
}
}
}
preserveClosedness: {
medium: {
p1: {
#A: (〈0;#B〉 & {
a: string
})
#B: {
(*{}|{
a: string
}|{
b: string
})
(*{}|{
c: int
}|{
d: string
})
}
}
}
}
preserveClosedness: {
medium: {
p2: {
#A: (〈0;#B〉 & {
a: string
})
#B: {
({
a: string
}|*{}|{
b: string
})
(*{}|{
c: int
}|{
d: string
})
}
}
}
}
preserveClosedness: {
medium: {
p3: {
#A: (〈0;#B〉 & {
a: string
})
#B: {
({
a: string
}|{
b: string
}|*{})
(*{}|{
c: int
}|{
d: string
})
}
}
}
}
}
2 changes: 1 addition & 1 deletion cue/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1831,7 +1831,7 @@ func (v Value) Equals(other Value) bool {
if v.v == nil || other.v == nil {
return false
}
return adt.Equal(v.ctx().opCtx, v.v, other.v)
return adt.Equal(v.ctx().opCtx, v.v, other.v, false)
}

// Format prints a debug version of a value.
Expand Down

0 comments on commit 63104c8

Please sign in to comment.