Skip to content

Commit 56a915b

Browse files
committed
internal/core/adt: handle vertices properly for comprehensions spanning disjunctions
Previously, if a comprehension spanned across a disjunction, the arc type might not be properly resolved, possibly leading to dropped fields in the worst case. This change fixes that by keeping track of the overlay context. Fixes #3894 Signed-off-by: Marcel van Lohuizen <[email protected]> Change-Id: I54a63a412dfef7288511b0995535d6a35176777a Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1214701 Reviewed-by: Daniel Martí <[email protected]> TryBot-Result: CUEcueckoo <[email protected]> Unity-Result: CUE porcuepine <[email protected]>
1 parent b17be30 commit 56a915b

File tree

5 files changed

+67
-67
lines changed

5 files changed

+67
-67
lines changed

cue/testdata/comprehensions/pushdown.txtar

Lines changed: 20 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,7 +1742,12 @@ Result:
17421742
#sub2?: (bool){ bool }
17431743
}
17441744
}
1745-
root: ((null|struct)){ |((null){ null }, (#struct){
1745+
root: ((null|struct)){ |((null){
1746+
null
1747+
#sub1?: (#struct){
1748+
#sub2: (bool){ true }
1749+
}
1750+
}, (#struct){
17461751
#sub1?: (#struct){
17471752
#sub2: (bool){ true }
17481753
}
@@ -1787,7 +1792,7 @@ Result:
17871792
arcTypeAcrossTwo: (struct){
17881793
a: (struct){
17891794
b: (struct){
1790-
c?: (struct){
1795+
c: (struct){
17911796
d: (bool){ true }
17921797
}
17931798
}
@@ -1796,6 +1801,9 @@ Result:
17961801
issue3894: (struct){
17971802
reduced: (struct){
17981803
p1: (struct){ |((struct){
1804+
b: (struct){
1805+
value: (string){ "true" }
1806+
}
17991807
a: (string){ string }
18001808
}, (struct){
18011809
b: (struct){
@@ -1821,13 +1829,11 @@ Result:
18211829
value: (string){ string }
18221830
}
18231831
}) }
1824-
out: (#struct){ |((#struct){
1825-
a?: (string){ string }
1826-
}, (#struct){
1827-
b: (#struct){
1828-
value: (string){ "true" }
1829-
}
1830-
}) }
1832+
out: (#struct){
1833+
b: (#struct){
1834+
value: (string){ "true" }
1835+
}
1836+
}
18311837
}
18321838
}
18331839
unifyDynamicReflectSuccess: (struct){
@@ -2162,21 +2168,7 @@ diff old new
21622168
// ./issue3729.cue:11:9
21632169
#sub1?: (#struct){
21642170
#sub2: (bool){ true }
2165-
@@ -850,12 +789,7 @@
2166-
#sub2?: (bool){ bool }
2167-
}
2168-
}
2169-
- root: ((null|struct)){ |((null){
2170-
- null
2171-
- #sub1?: (#struct){
2172-
- #sub2: (bool){ true }
2173-
- }
2174-
- }, (#struct){
2175-
+ root: ((null|struct)){ |((null){ null }, (#struct){
2176-
#sub1?: (#struct){
2177-
#sub2: (bool){ true }
2178-
}
2179-
@@ -864,14 +798,7 @@
2171+
@@ -864,14 +803,7 @@
21802172
}
21812173
full: (struct){
21822174
#Application: (#struct){
@@ -2192,44 +2184,6 @@ diff old new
21922184
}
21932185
#ApplicationSpec: (#struct){
21942186
syncPolicy?: ((null|struct)){ |((null){ null }, (#struct){
2195-
@@ -907,7 +834,7 @@
2196-
arcTypeAcrossTwo: (struct){
2197-
a: (struct){
2198-
b: (struct){
2199-
- c: (struct){
2200-
+ c?: (struct){
2201-
d: (bool){ true }
2202-
}
2203-
}
2204-
@@ -916,9 +843,6 @@
2205-
issue3894: (struct){
2206-
reduced: (struct){
2207-
p1: (struct){ |((struct){
2208-
- b: (struct){
2209-
- value: (string){ "true" }
2210-
- }
2211-
a: (string){ string }
2212-
}, (struct){
2213-
b: (struct){
2214-
@@ -944,11 +868,13 @@
2215-
value: (string){ string }
2216-
}
2217-
}) }
2218-
- out: (#struct){
2219-
- b: (#struct){
2220-
- value: (string){ "true" }
2221-
- }
2222-
- }
2223-
+ out: (#struct){ |((#struct){
2224-
+ a?: (string){ string }
2225-
+ }, (#struct){
2226-
+ b: (#struct){
2227-
+ value: (string){ "true" }
2228-
+ }
2229-
+ }) }
2230-
}
2231-
}
2232-
unifyDynamicReflectSuccess: (struct){
22332187
-- out/evalalpha/stats --
22342188
Leaks: 690
22352189
Freed: 0
@@ -2238,10 +2192,10 @@ Allocs: 690
22382192
Retain: 0
22392193

22402194
Unifications: 602
2241-
Conjuncts: 1027
2195+
Conjuncts: 1029
22422196
Disjuncts: 44
22432197

2244-
CloseIDElems: 218
2198+
CloseIDElems: 222
22452199
NumCloseIDs: 181
22462200
-- diff/-out/evalalpha/stats<==>+out/eval/stats --
22472201
diff old new
@@ -2264,10 +2218,10 @@ diff old new
22642218
+Retain: 0
22652219
+
22662220
+Unifications: 602
2267-
+Conjuncts: 1027
2221+
+Conjuncts: 1029
22682222
+Disjuncts: 44
22692223
+
2270-
+CloseIDElems: 218
2224+
+CloseIDElems: 222
22712225
+NumCloseIDs: 181
22722226
-- out/eval/stats --
22732227
Leaks: 73

internal/core/adt/comprehension.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ func (n *nodeContext) processComprehension(d *envYield, state vertexStatus) *Bot
459459

460460
v := n.node
461461
for c := d.leaf; c.parent != nil; c = c.parent {
462+
v = n.ctx.deref(v)
462463
v.updateArcType(c.arcType)
463464
if v.ArcType == ArcNotPresent {
464465
parent := v.Parent

internal/core/adt/context.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ type OpContext struct {
178178
// TODO: strictly separate validators and functions.
179179
IsValidator bool
180180

181+
overlays []overlayFrame
182+
181183
// ==== Debugging ====
182184
logID int // sequence number for log messages
183185

internal/core/adt/disjunct2.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,10 @@ func (n *nodeContext) doDisjunct(c Conjunct, m defaultMode, mode runMode, orig *
493493
n.scheduler.blocking = n.scheduler.blocking[:0]
494494

495495
d := oc.cloneRoot(n)
496+
497+
n.ctx.pushOverlay(n.node, oc.vertexMap)
498+
defer n.ctx.popOverlay()
499+
496500
d.runMode = mode
497501
c.Env = oc.derefDisjunctsEnv(c.Env)
498502

internal/core/adt/overlay.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,45 @@ type overlayContext struct {
6868

6969
type vertexMap map[*Vertex]*Vertex
7070

71+
// overlayFrom is used to store overlay information in the OpContext. This
72+
// is used for dynamic resolution of vertices, which prevents data structures
73+
// from having to be copied in the overlay.
74+
//
75+
// TODO(perf): right now this is only used for resolving vertices in
76+
// comprehensions. We could also use this for resolving environments, though.
77+
// Furthermore, we could used the "cleared" vertexMaps on this stack to avoid
78+
// allocating memory.
79+
//
80+
// NOTE: using a stack globally in OpContext is not very principled, as we
81+
// may be evaluating nested evaluations of different disjunctions. However,
82+
// in practice this just results in more work: as the vertices should not
83+
// overlap, there will be no cycles.
84+
type overlayFrame struct {
85+
vertexMap vertexMap
86+
root *Vertex
87+
}
88+
89+
func (c *OpContext) pushOverlay(v *Vertex, m vertexMap) {
90+
c.overlays = append(c.overlays, overlayFrame{m, v})
91+
}
92+
93+
func (c *OpContext) popOverlay() {
94+
c.overlays = c.overlays[:len(c.overlays)-1]
95+
}
96+
97+
func (c *OpContext) deref(v *Vertex) *Vertex {
98+
for i := len(c.overlays) - 1; i >= 0; i-- {
99+
f := c.overlays[i]
100+
if f.root == v {
101+
continue
102+
}
103+
if x, ok := f.vertexMap[v]; ok {
104+
return x
105+
}
106+
}
107+
return v
108+
}
109+
71110
// deref reports a replacement of v or v itself if such a replacement does not
72111
// exists. It computes the transitive closure of the replacement graph.
73112
// TODO(perf): it is probably sufficient to only replace one level. But we need
@@ -346,7 +385,7 @@ func (ctx *overlayContext) cloneTask(t *task, dst, src *scheduler) *task {
346385

347386
node: dst.node,
348387

349-
// TODO: need to copy closeContexts?
388+
// TODO(evalv3): rewrite comp and leaf to reflect disjunctions.
350389
comp: t.comp,
351390
leaf: t.leaf,
352391
}

0 commit comments

Comments
 (0)