Skip to content

Commit

Permalink
internal/core/adt: strip "notDefault" status for disjunctions with un…
Browse files Browse the repository at this point in the history
…used defaults

This most fixes all known default issues. The semantics is still not
entirely right, and will require a slightly different order of evaluation for
disjunctions. This is best addressed when doing some of the planned
performance enhancements for disjunctions.

Fixes #763
Fixes #726

This CL changes the semantics as follows:
1) a marked disjunction becomes an unmarked disjunction if,
after the conjuncts are distributed over disjuncts, none of
its marked disjuncts remain
2) a nested disjunction a | b | (nestedA | nestedC) is
evaluated according to the same rules, and then incorporated as is.

This implementation still slightly deviates from these rules:
The probem with the current implementation is that nested
disjunctions are unified agaist existing conjuncts, and possibly
eliminated early, before applying the rules. So in
```
     ("a" | "b") & (*(*"a" | string) | string)
```
the inner `*"a" | string` is combined with `"a" | "b"` before it
is determined whether its result is a marked disjunction. This was
okay with the previous semantics and had the benefit of allowing to
eliminate some disjuncts earlier, but no longer works with the new
semantics. Also if we use structure sharing, the benefits of reuse
will likely be bigger then early elimination in this case.

Some of the complexity of the logic in this CL is introduced
to deal with this discrepency. It can be simplified again
considerably when the required change in control flow is
implemented.

Change-Id: I638bbb66ba69bd1642261dd2628dfcc27b14f893
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8706
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
  • Loading branch information
mpvl committed Feb 15, 2021
1 parent 77b475f commit 87d43b7
Show file tree
Hide file tree
Showing 5 changed files with 421 additions and 83 deletions.
202 changes: 202 additions & 0 deletions cue/testdata/disjunctions/embed.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,32 @@ forceStruct: {
{}
}

defaultsMulti: {
a: {
#def: {
*{} | {a: string} | {b: string}
*{} | {c: string} | {d: string}
}

a: #def & {a: "foo"}
}

b: {
#def: {
*{} | {a: string} | {b: string}
*{} | {c: string} | {d: string}
*{} | {d: string} | {e: string}
}

a: #def & {a: "foo", e: "bar"}
}
}

nested: {
a: 1 | 2 | *(
(3 | 4 | *( 5 | 6 | *7)) & ( 3 | 4 | ( *7 | 8 )))
}

-- out/eval --
(struct){
default: (struct){
Expand Down Expand Up @@ -83,6 +109,137 @@ forceStruct: {
}) }
a: (int){ 2 }
}
defaultsMulti: (struct){
a: (struct){
#def: (struct){ |(*(#struct){
}, (#struct){
c: (string){ string }
}, (#struct){
d: (string){ string }
}, (#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
c: (string){ string }
}, (#struct){
a: (string){ string }
d: (string){ string }
}, (#struct){
b: (string){ string }
}, (#struct){
b: (string){ string }
c: (string){ string }
}, (#struct){
b: (string){ string }
d: (string){ string }
}) }
a: (struct){ |(*(#struct){
a: (string){ "foo" }
}, (#struct){
a: (string){ "foo" }
c: (string){ string }
}, (#struct){
a: (string){ "foo" }
d: (string){ string }
}) }
}
b: (struct){
#def: (struct){ |(*(#struct){
}, (#struct){
d: (string){ string }
}, (#struct){
e: (string){ string }
}, (#struct){
c: (string){ string }
}, (#struct){
c: (string){ string }
d: (string){ string }
}, (#struct){
c: (string){ string }
e: (string){ string }
}, (#struct){
d: (string){ string }
}, (#struct){
d: (string){ string }
}, (#struct){
d: (string){ string }
e: (string){ string }
}, (#struct){
a: (string){ string }
}, (#struct){
a: (string){ string }
d: (string){ string }
}, (#struct){
a: (string){ string }
e: (string){ string }
}, (#struct){
a: (string){ string }
c: (string){ string }
}, (#struct){
a: (string){ string }
c: (string){ string }
d: (string){ string }
}, (#struct){
a: (string){ string }
c: (string){ string }
e: (string){ string }
}, (#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 }
}, (#struct){
b: (string){ string }
d: (string){ string }
}, (#struct){
b: (string){ string }
e: (string){ string }
}, (#struct){
b: (string){ string }
c: (string){ string }
}, (#struct){
b: (string){ string }
c: (string){ string }
d: (string){ string }
}, (#struct){
b: (string){ string }
c: (string){ string }
e: (string){ string }
}, (#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){
a: (string){ "foo" }
e: (string){ "bar" }
}, (#struct){
a: (string){ "foo" }
e: (string){ "bar" }
c: (string){ string }
}, (#struct){
a: (string){ "foo" }
e: (string){ "bar" }
d: (string){ string }
}) }
}
}
nested: (struct){
a: (int){ |(*(int){ 7 }, (int){ 2 }, (int){ 3 }, (int){ 4 }, (int){ 1 }) }
}
}
-- out/compile --
--- in.cue
Expand Down Expand Up @@ -128,4 +285,49 @@ forceStruct: {
〈0;#y〉
{}
}
defaultsMulti: {
a: {
#def: {
(*{}|{
a: string
}|{
b: string
})
(*{}|{
c: string
}|{
d: string
})
}
a: (〈0;#def〉 & {
a: "foo"
})
}
b: {
#def: {
(*{}|{
a: string
}|{
b: string
})
(*{}|{
c: string
}|{
d: string
})
(*{}|{
d: string
}|{
e: string
})
}
a: (〈0;#def〉 & {
a: "foo"
e: "bar"
})
}
}
nested: {
a: (1|2|*((3|4|*(5|6|*7)) & (3|4|(*7|8))))
}
}
83 changes: 83 additions & 0 deletions cue/testdata/disjunctions/specdeviation.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ P: *1 | int
P: 2
p: *P | int // now 2, but should be (2 | int), according to the spec:

// Here the inner default may not be used as it is masked by the outer default.
r: (*3 | (*1 | 2)) & (1 | 2)

// Here the inner default is used, as there are no defaults marked in the
// outer disjunction.
s: (3 | (*1 | 2)) & (1 | 2)

s1: #Size & { min: 5 }

Expand All @@ -32,12 +38,39 @@ s1: #Size & { min: 5 }
res: uint | * 0
min: >res | *(1 + res)
}

staged: {
c: ("a" | "b") & (*(*"a" | string) | string)
d: (*(*"a" | string) | string) & ("a" | "b")
}

issue763a: {
#A: {
v: "a" | "b" | "c" // change to string to fix
}

h: [string]: #A

h: [=~"^b"]: #A & {
v: *h.a.v | string
}

h: a: {
v: *"a" | string
}

h: baa: _
h: boo: _
}

-- out/eval --
(struct){
Q: (int){ |(*(int){ 1 }, (int){ int }) }
q: (int){ |(*(int){ 1 }, (int){ int }) }
P: (int){ 2 }
p: (int){ |(*(int){ 2 }, (int){ int }) }
r: (int){ |((int){ 1 }, (int){ 2 }) }
s: (int){ |(*(int){ 1 }, (int){ 2 }) }
s1: (#struct){
max: (number){ |(*(int){ 5 }, (number){ >5 }) }
res: (int){ |(*(int){ 0 }, (int){ &(>=0, int) }) }
Expand All @@ -48,6 +81,26 @@ s1: #Size & { min: 5 }
res: (int){ |(*(int){ 0 }, (int){ &(>=0, int) }) }
min: (number){ |(*(int){ 1 }, (number){ >0 }) }
}
staged: (struct){
c: (string){ |(*(string){ "a" }, (string){ "b" }) }
d: (string){ |(*(string){ "a" }, (string){ "b" }) }
}
issue763a: (struct){
#A: (#struct){
v: (string){ |((string){ "a" }, (string){ "b" }, (string){ "c" }) }
}
h: (struct){
a: (#struct){
v: (string){ |(*(string){ "a" }, (string){ "b" }, (string){ "c" }) }
}
baa: (#struct){
v: (string){ |(*(string){ "a" }, (string){ "b" }, (string){ "c" }) }
}
boo: (#struct){
v: (string){ |(*(string){ "a" }, (string){ "b" }, (string){ "c" }) }
}
}
}
}
-- out/compile --
--- in.cue
Expand All @@ -57,6 +110,8 @@ s1: #Size & { min: 5 }
P: (*1|int)
P: 2
p: (*〈0;P〉|int)
r: ((*3|(*1|2)) & (1|2))
s: ((3|(*1|2)) & (1|2))
s1: (〈0;#Size〉 & {
min: 5
})
Expand All @@ -65,4 +120,32 @@ s1: #Size & { min: 5 }
res: (&(int, >=0)|*0)
min: (>〈0;res〉|*(1 + 〈0;res〉))
}
staged: {
c: (("a"|"b") & (*(*"a"|string)|string))
d: ((*(*"a"|string)|string) & ("a"|"b"))
}
issue763a: {
#A: {
v: ("a"|"b"|"c")
}
h: {
[string]: 〈1;#A〉
}
h: {
[=~"^b"]: (〈1;#A〉 & {
v: (*〈2;h〉.a.v|string)
})
}
h: {
a: {
v: (*"a"|string)
}
}
h: {
baa: _
}
h: {
boo: _
}
}
}
4 changes: 4 additions & 0 deletions doc/ref/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,10 @@ A _marked disjunction_ is one where any of its terms are marked.
So `a | b | *c | d` is a single marked disjunction of four terms,
whereas `a | (b | *c | d)` is an unmarked disjunction of two terms,
one of which is a marked disjunction of three terms.
During unification, if all the marked disjuncts of a marked disjunction are
eliminated, then the remaining unmarked disjuncts are considered as if they
originated from an unmarked disjunction
<!-- TODO: this formulation should be worked out more. -->
As explained below, distinguishing the nesting of disjunctions like this
is only relevant when both an outer and nested disjunction are marked.

Expand Down
Loading

0 comments on commit 87d43b7

Please sign in to comment.