Skip to content

Commit

Permalink
internal/core/adt: prevent early decrements in comprehensions
Browse files Browse the repository at this point in the history
Pending arcs in comprehensions follow a slightly
different code path and are evaluated a bit earlier
to determine whether the fields will exist.
We need to ensure that if a "complete all" path is
agitated, we do not break incoming dependencies
if we are not finalizing. Doing so might cause the
closeContexts to be closed, causing an "already
closed" panic when a matching pattern constraint
is later evaluated.

We enforce this by requiring `breakIncomingDeps`
to take the current mode and then only commence
when mode is finalize.

Note that there are two call sites. We added two
test cases, the reported one and a variant, each of
which triggered a panic for the respective code paths.

Fixes #3691

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: Ib4e9dda97a002f2ac5f96f4b31985e6c7ddb4f07
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1207545
Reviewed-by: Daniel Martí <[email protected]>
Unity-Result: CUE porcuepine <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
  • Loading branch information
mpvl committed Jan 21, 2025
1 parent 6b292d4 commit bdc2929
Show file tree
Hide file tree
Showing 3 changed files with 349 additions and 11 deletions.
346 changes: 338 additions & 8 deletions cue/testdata/eval/comprehensions.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,268 @@ matchOrder: {
out: [string]: Val: 1
}
}

-- issue3691.cue --
// Ensure that parent nodes are properly processed, even when a pending arc
// is evaluated early.
issue3691: original: {
X: [string]: string
a: [string]: X
a: {
if true {
b: c: 1
}
}
}
// Structural cycles follow a different code path.
issue3691: structuralCycle: {
X: [string]: string
a: [string]: X
a: {
if true {
b: c: a
}
}
}
-- out/eval/stats --
Leaks: 4
Freed: 64
Reused: 57
Allocs: 11
Freed: 77
Reused: 69
Allocs: 12
Retain: 19

Unifications: 68
Conjuncts: 96
Disjuncts: 81
Unifications: 81
Conjuncts: 121
Disjuncts: 94
-- out/evalalpha --
Errors:
issue3691.original.a.b: conflicting values 1 and string (mismatched types int and string):
./issue3691.cue:4:15
./issue3691.cue:8:10
issue3691.structuralCycle.a.b.c: structural cycle

Result:
(_|_){
// [eval]
a: (struct){
x: (int){ 10 }
y: (int){ 100 }
z: (int){ 50 }
}
b: (struct){
x: (int){ 10 }
k: (int){ 20 }
l: (int){ 40 }
z: (int){ 50 }
}
c: (struct){
y: (int){ 110 }
z: (int){ 60 }
}
A: (struct){
X: (struct){
run: (string){ "dfoo" }
files: (string){ "dfoo" }
}
}
matchOrder: (struct){
a1: (struct){
out: (struct){
b: (struct){
Val: (int){ 1 }
}
}
in: (struct){
a: (struct){
b: (struct){
}
}
}
}
a2: (struct){
out: (struct){
b: (struct){
Val: (int){ 1 }
}
}
in: (struct){
a: (struct){
b: (struct){
}
}
}
}
a3: (struct){
in: (struct){
a: (struct){
b: (struct){
}
}
}
out: (struct){
b: (struct){
Val: (int){ 1 }
}
}
}
a4: (struct){
out: (struct){
b: (struct){
Val: (int){ 1 }
}
}
in: (struct){
a: (struct){
b: (struct){
}
}
}
}
a5: (struct){
out: (struct){
b: (struct){
Val: (int){ 1 }
}
}
in: (struct){
a: (struct){
b: (struct){
}
}
}
}
a6: (struct){
in: (struct){
a: (struct){
b: (struct){
}
}
}
out: (struct){
b: (struct){
Val: (int){ 1 }
}
}
}
}
issue3691: (_|_){
// [eval]
original: (_|_){
// [eval]
X: (struct){
}
a: (_|_){
// [eval]
b: (_|_){
// [eval]
c: (_|_){
// [eval] issue3691.original.a.b: conflicting values 1 and string (mismatched types int and string):
// ./issue3691.cue:4:15
// ./issue3691.cue:8:10
}
}
}
}
structuralCycle: (_|_){
// [structural cycle]
X: (struct){
}
a: (_|_){
// [structural cycle]
b: (_|_){
// [structural cycle]
c: (_|_){
// [structural cycle] issue3691.structuralCycle.a.b.c: structural cycle
}
}
}
}
}
}
-- diff/-out/evalalpha<==>+out/eval --
diff old new
--- old
+++ new
@@ -1,14 +1,8 @@
Errors:
-issue3691.original.a.b.c: conflicting values string and 1 (mismatched types string and int):
+issue3691.original.a.b: conflicting values 1 and string (mismatched types int and string):
./issue3691.cue:4:15
- ./issue3691.cue:7:3
./issue3691.cue:8:10
-issue3691.structuralCycle.a.b.c: conflicting values string and {[string]:X} (mismatched types string and struct):
- ./issue3691.cue:14:15
- ./issue3691.cue:15:5
- ./issue3691.cue:17:3
- ./issue3691.cue:18:10
-issue3691.structuralCycle.a.b.c.b.c: structural cycle
+issue3691.structuralCycle.a.b.c: structural cycle

Result:
(_|_){
@@ -125,9 +119,8 @@
b: (_|_){
// [eval]
c: (_|_){
- // [eval] issue3691.original.a.b.c: conflicting values string and 1 (mismatched types string and int):
+ // [eval] issue3691.original.a.b: conflicting values 1 and string (mismatched types int and string):
// ./issue3691.cue:4:15
- // ./issue3691.cue:7:3
// ./issue3691.cue:8:10
}
}
@@ -134,25 +127,15 @@
}
}
structuralCycle: (_|_){
- // [eval]
- X: (struct){
- }
- a: (_|_){
- // [eval]
- b: (_|_){
- // [eval]
- c: (_|_){
- // [eval] issue3691.structuralCycle.a.b.c: conflicting values string and {[string]:X} (mismatched types string and struct):
- // ./issue3691.cue:14:15
- // ./issue3691.cue:15:5
- // ./issue3691.cue:17:3
- // ./issue3691.cue:18:10
- b: (_|_){
- // [structural cycle]
- c: (_|_){
- // [structural cycle] issue3691.structuralCycle.a.b.c.b.c: structural cycle
- }
- }
+ // [structural cycle]
+ X: (struct){
+ }
+ a: (_|_){
+ // [structural cycle]
+ b: (_|_){
+ // [structural cycle]
+ c: (_|_){
+ // [structural cycle] issue3691.structuralCycle.a.b.c: structural cycle
}
}
}
-- diff/todo/p2 --
Missing error positions.
-- out/eval --
(struct){
Errors:
issue3691.original.a.b.c: conflicting values string and 1 (mismatched types string and int):
./issue3691.cue:4:15
./issue3691.cue:7:3
./issue3691.cue:8:10
issue3691.structuralCycle.a.b.c: conflicting values string and {[string]:X} (mismatched types string and struct):
./issue3691.cue:14:15
./issue3691.cue:15:5
./issue3691.cue:17:3
./issue3691.cue:18:10
issue3691.structuralCycle.a.b.c.b.c: structural cycle

Result:
(_|_){
// [eval]
a: (struct){
x: (int){ 10 }
y: (int){ 100 }
Expand Down Expand Up @@ -210,6 +459,50 @@ Disjuncts: 81
}
}
}
issue3691: (_|_){
// [eval]
original: (_|_){
// [eval]
X: (struct){
}
a: (_|_){
// [eval]
b: (_|_){
// [eval]
c: (_|_){
// [eval] issue3691.original.a.b.c: conflicting values string and 1 (mismatched types string and int):
// ./issue3691.cue:4:15
// ./issue3691.cue:7:3
// ./issue3691.cue:8:10
}
}
}
}
structuralCycle: (_|_){
// [eval]
X: (struct){
}
a: (_|_){
// [eval]
b: (_|_){
// [eval]
c: (_|_){
// [eval] issue3691.structuralCycle.a.b.c: conflicting values string and {[string]:X} (mismatched types string and struct):
// ./issue3691.cue:14:15
// ./issue3691.cue:15:5
// ./issue3691.cue:17:3
// ./issue3691.cue:18:10
b: (_|_){
// [structural cycle]
c: (_|_){
// [structural cycle] issue3691.structuralCycle.a.b.c.b.c: structural cycle
}
}
}
}
}
}
}
}
-- out/compile --
--- in.cue
Expand Down Expand Up @@ -345,3 +638,40 @@ Disjuncts: 81
}
}
}
--- issue3691.cue
{
issue3691: {
original: {
X: {
[string]: string
}
a: {
[string]: 〈1;X〉
}
a: {
if true {
b: {
c: 1
}
}
}
}
}
issue3691: {
structuralCycle: {
X: {
[string]: string
}
a: {
[string]: 〈1;X〉
}
a: {
if true {
b: {
c: 〈3;a〉
}
}
}
}
}
}
10 changes: 9 additions & 1 deletion internal/core/adt/dep.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,15 @@ func (n *nodeContext) breakIncomingArcs(mode runMode) {

// breakIncomingDeps breaks all incoming dependencies, which includes arcs and
// pending notifications and attempts all remaining work.
func (n *nodeContext) breakIncomingDeps() {
//
// We should only break incoming dependencies if we are finalizing nodes, as
// breaking them earlier can cause a "already closed" panic. To make sure of
// this, we force the caller to pass mode.
func (n *nodeContext) breakIncomingDeps(mode runMode) {
if mode != finalize {
return
}

// TODO: remove this block in favor of finalizing notification nodes,
// or what have you. We have patched this to skip evaluating when using
// disjunctions, but this is overall a brittle approach.
Expand Down
Loading

0 comments on commit bdc2929

Please sign in to comment.