Skip to content

Commit

Permalink
internal/core/adt: don't pre-expand disjunctions
Browse files Browse the repository at this point in the history
This also simplifies combining defeault modes, which
really is just max, given the proper ordering of
modes.

Change-Id: I468c4c343e53ae86a5a14b6a477c8a389a212855
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8109
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
  • Loading branch information
mpvl committed Jan 9, 2021
1 parent 69e0c96 commit 396202c
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 61 deletions.
2 changes: 1 addition & 1 deletion cue/testdata/basicrewrite/014_disjunctions.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ i1: "c"
o1: (int){ |((int){ 1 }, (int){ 2 }, (int){ 3 }) }
o2: (int){ 1 }
o3: (int){ 2 }
o4: (int){ |((int){ 2 }, (int){ 1 }, (int){ 3 }) }
o4: (int){ |((int){ 1 }, (int){ 2 }, (int){ 3 }) }
o5: (int){ |(*(int){ 2 }, (int){ 1 }, (int){ 3 }) }
o6: (int){ |((int){ 1 }, (int){ 2 }, (int){ 3 }) }
o7: (int){ |((int){ 2 }, (int){ 3 }) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ c2: (*{
}
a3: (int){ 1 }
b1: (int){ |((int){ 0 }, (int){ 1 }) }
b2: (int){ |((int){ 1 }, (int){ 0 }) }
b2: (int){ |((int){ 0 }, (int){ 1 }) }
c1: (struct){ |((struct){
a: (int){ 1 }
b: (int){ 2 }
Expand Down
14 changes: 13 additions & 1 deletion cue/testdata/eval/disjunctions.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ t11: {
a: #A
}

cross: {
a: *"word" | string
a: string | *"word"
}


d100: {
// Should we allow a selector to imply a struct or list? Would be convenient.
Expand Down Expand Up @@ -234,13 +239,16 @@ Result:
}, (#struct){
}) }
}
cross: (struct){
a: (string){ |(*(string){ "word" }, (string){ string }) }
}
d100: (struct){
i: ((null|struct)){ |((null){ null }, (struct){
bar: (int){ 2 }
}) }
j: (_|_){
// [incomplete] d100.j: unresolved disjunction null | {bar:2} (type (null|struct)):
// ./in.cue:102:6
// ./in.cue:107:6
}
}
}
Expand Down Expand Up @@ -393,6 +401,10 @@ Result:
a: 〈0;b〉
a: 〈0;#A〉
}
cross: {
a: (*"word"|string)
a: (string|*"word")
}
d100: {
i: (null|{
bar: 2
Expand Down
96 changes: 38 additions & 58 deletions internal/core/adt/disjunct.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
package adt

import (
"sort"

"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
)
Expand Down Expand Up @@ -86,48 +84,30 @@ import (

type envDisjunct struct {
env *Environment
values []disjunct
numDefaults int
hasDefaults bool
cloneID CloseInfo
}

type disjunct struct {
v *Vertex
expr Expr
isDefault bool
expr *DisjunctionExpr
value *Disjunction
hasDefaults bool
}

func (n *nodeContext) addDisjunction(env *Environment, x *DisjunctionExpr, cloneID CloseInfo) {
a := make([]disjunct, 0, len(x.Values))

// TODO: precompute
numDefaults := 0
for _, v := range x.Values {
isDef := v.Default // || n.hasDefaults(env, v.Val)
if isDef {
numDefaults++
}
a = append(a, disjunct{nil, v.Val, isDef})
}

sort.SliceStable(a, func(i, j int) bool {
return !a[j].isDefault && a[i].isDefault != a[j].isDefault
})

n.disjunctions = append(n.disjunctions,
envDisjunct{env, a, numDefaults, numDefaults > 0, cloneID})

envDisjunct{env, cloneID, x, nil, numDefaults > 0})
}

func (n *nodeContext) addDisjunctionValue(env *Environment, x *Disjunction, cloneID CloseInfo) {
a := make([]disjunct, 0, len(x.Values))

for i, v := range x.Values {
a = append(a, disjunct{v, nil, i < x.NumDefaults})
}

n.disjunctions = append(n.disjunctions,
envDisjunct{env, a, x.NumDefaults, x.HasDefaults, cloneID})
envDisjunct{env, cloneID, nil, x, x.HasDefaults})

}

Expand Down Expand Up @@ -216,22 +196,33 @@ func (n *nodeContext) expandDisjuncts(
}

for _, dn := range a {
for _, v := range d.values {
cn := dn.clone()
*cn.node = snapshotVertex(dn.snapshot)

if v.v != nil {
cn.addValueConjunct(d.env, v.v, d.cloneID)
} else {
c := MakeConjunct(d.env, v.expr, d.cloneID)
switch {
case d.expr != nil:
for _, v := range d.expr.Values {
cn := dn.clone()
*cn.node = snapshotVertex(dn.snapshot)

c := MakeConjunct(d.env, v.Val, d.cloneID)
cn.addExprConjunct(c)

newMode := mode(d.hasDefaults, v.Default)
cn.defaultMode = combineDefault(dn.defaultMode, newMode)

cn.expandDisjuncts(state, n, newMode, true)
}

newMode := mode(d, v)
case d.value != nil:
for i, v := range d.value.Values {
cn := dn.clone()
*cn.node = snapshotVertex(dn.snapshot)

cn.addValueConjunct(d.env, v, d.cloneID)

cn.expandDisjuncts(state, n, newMode, true)
newMode := mode(d.hasDefaults, i < d.value.NumDefaults)
cn.defaultMode = combineDefault(dn.defaultMode, newMode)

cn.defaultMode = combineDefault(dn.defaultMode, newMode)
cn.expandDisjuncts(state, n, newMode, true)
}
}
}

Expand All @@ -243,6 +234,7 @@ func (n *nodeContext) expandDisjuncts(

if len(n.disjuncts) == 0 {
n.makeError()
break
}
}

Expand Down Expand Up @@ -271,6 +263,9 @@ func (n *nodeContext) expandDisjuncts(
for _, v := range p.disjuncts {
if Equal(n.ctx, &v.result, &d.result) {
n.ctx.Unifier.freeNodeContext(n)
if d.defaultMode == isDefault {
v.defaultMode = isDefault
}
continue outer
}
}
Expand Down Expand Up @@ -307,12 +302,12 @@ func (n *nodeContext) makeError() {
n.node.SetValue(n.ctx, Finalized, b)
}

func mode(d envDisjunct, v disjunct) defaultMode {
func mode(hasDefault, marked bool) defaultMode {
var mode defaultMode
switch {
case !d.hasDefaults:
case !hasDefault:
mode = maybeDefault
case v.isDefault:
case marked:
mode = isDefault
default:
mode = notDefault
Expand Down Expand Up @@ -379,8 +374,8 @@ type defaultMode int

const (
maybeDefault defaultMode = iota
notDefault
isDefault
notDefault
)

// combineDefaults combines default modes for unifying conjuncts.
Expand All @@ -391,24 +386,9 @@ const (
// U2: (v1, d1) & (v2, d2) => (v1&v2, d1&d2)
func combineDefault(a, b defaultMode) defaultMode {
if a > b {
a, b = b, a
}
switch {
case a == maybeDefault && b == maybeDefault:
return maybeDefault
case a == maybeDefault && b == notDefault:
return notDefault
case a == maybeDefault && b == isDefault:
return isDefault
case a == notDefault && b == notDefault:
return notDefault
case a == notDefault && b == isDefault:
return notDefault
case a == isDefault && b == isDefault:
return isDefault
default:
panic("unreachable")
return a
}
return b
}

// disjunctError returns a compound error for a failed disjunction.
Expand Down

0 comments on commit 396202c

Please sign in to comment.