From 92330b4ec7b6cc12005d5c7a7dc55b0a14515e8f Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Fri, 27 Sep 2024 14:04:41 +0200 Subject: [PATCH] internal/core/adt: new cycle algorithm V3 only partially implemented the V2 cycle algorithm. Rather than implementing the rest, we came up with a new algorithm that fits better with the depth-first nature of V3. It is overall considerably simpler than the V2 algorithm. This CL reflects a "mostly done" state. There are still improvements to be made (e.g. inline and let fields), but it is already in better shape that V3 was before. Further fixes will be done in subsequent CLs. Note that this algorith is much simpler. It no longer needs: - track individual reference points (although it still does so for debugging purposes, but it does not use it to influence outcome) - track nodeContext.cyclicReferences - have a special mechanism to detect and disable mutual references - track depth of optional markers - track depth of references - additional finding of evidence of non-cyclic nodes in some cases CHANGES IN TESTS benchmarks/issue1684: more efficient at trimming disjunctions comprehensions/errors: more efficient at trimming disjunctions cycle/comprehenion: changes are strict improvements, even though they do not fix the underlying bugs cycle/constraints: - both shorter and deeper cutoffs cycle/evaluate: - reordering, mostly cycle/inline_non_recursive: - FIXES cycle/inline: - Minor improvements - Does not yet fix underlying bug cycle/structural: - Some processing is now deeper - Some less deep - Some P0-level bugs now FIXED that were somehow not listed in diff/todo/*. eval/v0.7: - NEW BUG: notification mechanism bypassed, it seems. export/030: - performance improvements Issue #2850 Signed-off-by: Marcel van Lohuizen Change-Id: I280fcf9cb333735fcc1774f980bf34e7ed72c9a9 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1201897 Reviewed-by: Matthew Sackman TryBot-Result: CUEcueckoo Unity-Result: CUE porcuepine --- cue/testdata/benchmarks/issue1684.txtar | 58 +-- cue/testdata/benchmarks/listdisj.txtar | 22 +- cue/testdata/comprehensions/errors.txtar | 19 +- cue/testdata/cycle/comprehension.txtar | 212 +++------ cue/testdata/cycle/constraints.txtar | 105 ++++- cue/testdata/cycle/evaluate.txtar | 187 +++++--- cue/testdata/cycle/inline.txtar | 81 ++-- cue/testdata/cycle/inline_non_recursive.txtar | 227 +--------- cue/testdata/cycle/structural.txtar | 413 ++++++++--------- cue/testdata/eval/v0.7.txtar | 72 +-- cue/testdata/export/030.txtar | 32 ++ internal/core/adt/comprehension.go | 3 + internal/core/adt/conjunct.go | 12 + internal/core/adt/context.go | 10 +- internal/core/adt/cycle.go | 426 +++++++++++++++++- internal/core/adt/eval.go | 7 +- internal/core/adt/eval_test.go | 8 +- internal/core/adt/fields.go | 5 + internal/core/adt/tasks.go | 25 +- 19 files changed, 1124 insertions(+), 800 deletions(-) diff --git a/cue/testdata/benchmarks/issue1684.txtar b/cue/testdata/benchmarks/issue1684.txtar index e19be93864e..3681fa18472 100644 --- a/cue/testdata/benchmarks/issue1684.txtar +++ b/cue/testdata/benchmarks/issue1684.txtar @@ -62,15 +62,15 @@ nestedClosed: passing: { D: {id: {}} | {[string]: D} } -- out/evalalpha/stats -- -Leaks: 2533 -Freed: 612 -Reused: 612 -Allocs: 2533 +Leaks: 2135 +Freed: 658 +Reused: 658 +Allocs: 2135 Retain: 0 -Unifications: 707 -Conjuncts: 5373 -Disjuncts: 1102 +Unifications: 475 +Conjuncts: 4781 +Disjuncts: 918 -- out/evalalpha -- (struct){ #Secret: (#struct){ @@ -127,12 +127,6 @@ Disjuncts: 1102 id: (struct){ |((struct){ id: (struct){ } - }, (struct){ - id: (struct){ |((struct){ - id: (struct){ - } - }, (struct){ - }) } }, (struct){ }) } }, (struct){ @@ -149,28 +143,26 @@ diff old new -Freed: 1064333 -Reused: 1064282 -Allocs: 51 -+Leaks: 2533 -+Freed: 612 -+Reused: 612 -+Allocs: 2533 ++Leaks: 2135 ++Freed: 658 ++Reused: 658 ++Allocs: 2135 Retain: 0 -Unifications: 792123 -Conjuncts: 2480117 -Disjuncts: 1064333 -+Unifications: 707 -+Conjuncts: 5373 -+Disjuncts: 1102 ++Unifications: 475 ++Conjuncts: 4781 ++Disjuncts: 918 -- diff/-out/evalalpha<==>+out/eval -- diff old new --- old +++ new -@@ -54,35 +54,11 @@ - id: (struct){ - } +@@ -56,36 +56,6 @@ }, (struct){ -- }) } -- }, (struct){ + }) } + }, (struct){ - id: (struct){ |((struct){ - id: (struct){ - } @@ -198,14 +190,14 @@ diff old new - id: (struct){ |((struct){ - id: (struct){ - } -+ id: (struct){ |((struct){ -+ id: (struct){ -+ } -+ }, (struct){ -+ }) } - }, (struct){ - }) } - }, (struct){ +- }, (struct){ +- }) } +- }, (struct){ + }) } + } + } +-- diff/explanation -- +New algorithm is better at trimming recursive disjunctions. -- out/eval/stats -- Leaks: 0 Freed: 1064333 diff --git a/cue/testdata/benchmarks/listdisj.txtar b/cue/testdata/benchmarks/listdisj.txtar index 0af27e70cab..c25e4dc5379 100644 --- a/cue/testdata/benchmarks/listdisj.txtar +++ b/cue/testdata/benchmarks/listdisj.txtar @@ -64,17 +64,6 @@ Retain: 0 Unifications: 15 Conjuncts: 43 Disjuncts: 4 --- out/evalalpha -- -(struct){ - #T: (list){ - 0: (string){ "d" } - } - x: ~(#T) - y: ~(#T) - z: ~(#T) - v: ~(#T) - #X: ~(#T) -} -- diff/-out/evalalpha/stats<==>+out/eval/stats -- diff old new --- old @@ -96,6 +85,17 @@ diff old new +Unifications: 15 +Conjuncts: 43 +Disjuncts: 4 +-- out/evalalpha -- +(struct){ + #T: (list){ + 0: (string){ "d" } + } + x: ~(#T) + y: ~(#T) + z: ~(#T) + v: ~(#T) + #X: ~(#T) +} -- diff/-out/evalalpha<==>+out/eval -- diff old new --- old diff --git a/cue/testdata/comprehensions/errors.txtar b/cue/testdata/comprehensions/errors.txtar index c3110f57c74..714b8e1a9b7 100644 --- a/cue/testdata/comprehensions/errors.txtar +++ b/cue/testdata/comprehensions/errors.txtar @@ -68,9 +68,7 @@ Result: // [eval] circularIf: (struct){ #list: (#struct){ - tail: ((null|struct)){ |(*(null){ null }, (#struct){ - tail: (null){ null } - }) } + tail: (null){ null } } } circularFor: (_|_){ @@ -116,7 +114,18 @@ diff old new circularFor.#list: cannot range over tail != null (found bool, want list or struct): ./in.cue:12:12 intField: integer fields not supported: -@@ -35,10 +34,6 @@ +@@ -13,9 +12,7 @@ + // [eval] + circularIf: (struct){ + #list: (#struct){ +- tail: ((null|struct)){ |(*(null){ null }, (#struct){ +- tail: (null){ null } +- }) } ++ tail: (null){ null } + } + } + circularFor: (_|_){ +@@ -35,10 +32,6 @@ intField: (_|_){ // [eval] intField: integer fields not supported: // ./in.cue:27:4 @@ -127,7 +136,7 @@ diff old new } conflictRangingOverSelf: (_|_){ // [eval] -@@ -49,7 +44,6 @@ +@@ -49,7 +42,6 @@ // [eval] conflictRangingOverSelf.x.age: conflicting values int and "age" (mismatched types int and string): // ./in.cue:36:9 // ./in.cue:40:3 diff --git a/cue/testdata/cycle/comprehension.txtar b/cue/testdata/cycle/comprehension.txtar index cbb167f4029..0eb2397c9f6 100644 --- a/cue/testdata/cycle/comprehension.txtar +++ b/cue/testdata/cycle/comprehension.txtar @@ -310,15 +310,15 @@ issue2367: { } -- out/evalalpha/stats -- -Leaks: 431 -Freed: 12 -Reused: 12 -Allocs: 431 +Leaks: 546 +Freed: 15 +Reused: 15 +Allocs: 546 Retain: 0 -Unifications: 397 -Conjuncts: 2202 -Disjuncts: 36 +Unifications: 497 +Conjuncts: 2032 +Disjuncts: 46 -- out/evalalpha -- Errors: issue1881.p1.o.retry: field not allowed: @@ -327,21 +327,11 @@ selfReferential.acrossOr.t1.p1.o.retry: field not allowed: ./in.cue:125:17 selfReferential.insertionError.A: adding field foo3 not allowed as field set was already referenced: ./in.cue:117:14 -selfReferential.acrossOr.t1.p1.0.reject: structural cycle: - ./in.cue:133:14 selfReferential.acrossOr.t1.p1.2.retry: structural cycle: ./in.cue:133:14 -selfReferential.acrossOr.t1.p2.o.retry: field not allowed: - ./in.cue:139:11 - ./in.cue:143:17 -selfReferential.acrossOr.t1.p2.0.reject: structural cycle: - ./in.cue:139:14 selfReferential.acrossOr.t1.p2.2.retry: structural cycle: ./in.cue:139:14 -selfReferential.acrossOr.t1.p3.o.retry: field not allowed: - ./in.cue:153:11 - ./in.cue:163:17 -selfReferential.acrossOr.t1.p3.0.reject: structural cycle: +selfReferential.acrossOr.t1.p3.2.retry: structural cycle: ./in.cue:153:14 selfReferential.acrossOr.t2.p1.2.b: structural cycle: ./in.cue:167:8 @@ -349,18 +339,12 @@ selfReferential.acrossOr.t2.p2.2.b: structural cycle: ./in.cue:178:8 selfReferential.acrossOr.t2.p3.2.b: structural cycle: ./in.cue:193:8 -issue1881.p1.0.reject: structural cycle: +issue1881.p1.2.retry: structural cycle: ./in.cue:205:14 issue1881.p1.2.retry.output: structural cycle: ./in.cue:205:14 -issue1881.p2.o.retry: field not allowed: - ./in.cue:219:11 - ./in.cue:217:17 issue1881.p2.2.retry: structural cycle: ./in.cue:219:14 -issue1881.p3.o.retry: field not allowed: - ./in.cue:231:11 - ./in.cue:235:17 issue1881.p3.2.retry: structural cycle: ./in.cue:231:14 siblingInsertion.t2.p1.deployment: adding field logging not allowed as field set was already referenced: @@ -513,47 +497,39 @@ Result: reject: (string){ string } resource: (string){ string } retry: (_|_){ - // [structural cycle] selfReferential.acrossOr.t1.p1.0.reject: structural cycle: + // [structural cycle] selfReferential.acrossOr.t1.p1.2.retry: structural cycle: // ./in.cue:133:14 } } #Output: (_|_){ - // [structural cycle] selfReferential.acrossOr.t1.p1.0.reject: structural cycle: + // [structural cycle] selfReferential.acrossOr.t1.p1.2.retry: structural cycle: // ./in.cue:133:14 } } p2: (_|_){ - // [eval] + // [structural cycle] #Output: (_|_){ - // [structural cycle] selfReferential.acrossOr.t1.p2.0.reject: structural cycle: + // [structural cycle] selfReferential.acrossOr.t1.p2.2.retry: structural cycle: // ./in.cue:139:14 } o: (_|_){ - // [eval] selfReferential.acrossOr.t1.p2.o.retry: field not allowed: - // ./in.cue:139:11 - // ./in.cue:143:17 - // selfReferential.acrossOr.t1.p2.2.retry: structural cycle: + // [structural cycle] selfReferential.acrossOr.t1.p2.2.retry: structural cycle: // ./in.cue:139:14 - retry: (_|_){ - // [eval] selfReferential.acrossOr.t1.p2.o.retry: field not allowed: - // ./in.cue:139:11 - // ./in.cue:143:17 - } } #AllOutputs: (_|_){ // [structural cycle] reject: (string){ string } resource: (string){ string } retry: (_|_){ - // [structural cycle] selfReferential.acrossOr.t1.p2.0.reject: structural cycle: + // [structural cycle] selfReferential.acrossOr.t1.p2.2.retry: structural cycle: // ./in.cue:139:14 } } } p3: (_|_){ - // [eval] + // [structural cycle] #Output: (_|_){ - // [structural cycle] selfReferential.acrossOr.t1.p3.0.reject: structural cycle: + // [structural cycle] selfReferential.acrossOr.t1.p3.2.retry: structural cycle: // ./in.cue:153:14 } #AllOutputs: (_|_){ @@ -561,21 +537,13 @@ Result: reject: (string){ string } resource: (string){ string } retry: (_|_){ - // [structural cycle] selfReferential.acrossOr.t1.p3.0.reject: structural cycle: + // [structural cycle] selfReferential.acrossOr.t1.p3.2.retry: structural cycle: // ./in.cue:153:14 } } o: (_|_){ - // [eval] selfReferential.acrossOr.t1.p3.o.retry: field not allowed: - // ./in.cue:153:11 - // ./in.cue:163:17 - // selfReferential.acrossOr.t1.p3.0.reject: structural cycle: + // [structural cycle] selfReferential.acrossOr.t1.p3.2.retry: structural cycle: // ./in.cue:153:14 - retry: (_|_){ - // [eval] selfReferential.acrossOr.t1.p3.o.retry: field not allowed: - // ./in.cue:153:11 - // ./in.cue:163:17 - } } } } @@ -684,18 +652,18 @@ Result: retry: (_|_){ // [structural cycle] output: (_|_){ - // [structural cycle] issue1881.p1.0.reject: structural cycle: + // [structural cycle] issue1881.p1.2.retry: structural cycle: // ./in.cue:205:14 } } } #Output: (_|_){ - // [structural cycle] issue1881.p1.0.reject: structural cycle: + // [structural cycle] issue1881.p1.2.retry: structural cycle: // ./in.cue:205:14 } } p2: (_|_){ - // [eval] + // [structural cycle] #AllOutputs: (_|_){ // [structural cycle] reject: (string){ string } @@ -709,16 +677,8 @@ Result: } } o: (_|_){ - // [eval] issue1881.p2.o.retry: field not allowed: - // ./in.cue:219:11 - // ./in.cue:217:17 - // issue1881.p2.2.retry: structural cycle: + // [structural cycle] issue1881.p2.2.retry: structural cycle: // ./in.cue:219:14 - retry: (_|_){ - // [eval] issue1881.p2.o.retry: field not allowed: - // ./in.cue:219:11 - // ./in.cue:217:17 - } } #Output: (_|_){ // [structural cycle] issue1881.p2.2.retry: structural cycle: @@ -726,7 +686,7 @@ Result: } } p3: (_|_){ - // [eval] + // [structural cycle] #AllOutputs: (_|_){ // [structural cycle] reject: (string){ string } @@ -744,16 +704,8 @@ Result: // ./in.cue:231:14 } o: (_|_){ - // [eval] issue1881.p3.o.retry: field not allowed: - // ./in.cue:231:11 - // ./in.cue:235:17 - // issue1881.p3.2.retry: structural cycle: + // [structural cycle] issue1881.p3.2.retry: structural cycle: // ./in.cue:231:14 - retry: (_|_){ - // [eval] issue1881.p3.o.retry: field not allowed: - // ./in.cue:231:11 - // ./in.cue:235:17 - } } } } @@ -826,7 +778,7 @@ Result: diff old new --- old +++ new -@@ -1,5 +1,52 @@ +@@ -1,5 +1,36 @@ Errors: -selfReferential.insertionError.A: field foo3 not allowed by earlier comprehension or reference cycle +issue1881.p1.o.retry: field not allowed: @@ -835,21 +787,11 @@ diff old new + ./in.cue:125:17 +selfReferential.insertionError.A: adding field foo3 not allowed as field set was already referenced: + ./in.cue:117:14 -+selfReferential.acrossOr.t1.p1.0.reject: structural cycle: -+ ./in.cue:133:14 +selfReferential.acrossOr.t1.p1.2.retry: structural cycle: + ./in.cue:133:14 -+selfReferential.acrossOr.t1.p2.o.retry: field not allowed: -+ ./in.cue:139:11 -+ ./in.cue:143:17 -+selfReferential.acrossOr.t1.p2.0.reject: structural cycle: -+ ./in.cue:139:14 +selfReferential.acrossOr.t1.p2.2.retry: structural cycle: + ./in.cue:139:14 -+selfReferential.acrossOr.t1.p3.o.retry: field not allowed: -+ ./in.cue:153:11 -+ ./in.cue:163:17 -+selfReferential.acrossOr.t1.p3.0.reject: structural cycle: ++selfReferential.acrossOr.t1.p3.2.retry: structural cycle: + ./in.cue:153:14 +selfReferential.acrossOr.t2.p1.2.b: structural cycle: + ./in.cue:167:8 @@ -857,18 +799,12 @@ diff old new + ./in.cue:178:8 +selfReferential.acrossOr.t2.p3.2.b: structural cycle: + ./in.cue:193:8 -+issue1881.p1.0.reject: structural cycle: ++issue1881.p1.2.retry: structural cycle: + ./in.cue:205:14 +issue1881.p1.2.retry.output: structural cycle: + ./in.cue:205:14 -+issue1881.p2.o.retry: field not allowed: -+ ./in.cue:219:11 -+ ./in.cue:217:17 +issue1881.p2.2.retry: structural cycle: + ./in.cue:219:14 -+issue1881.p3.o.retry: field not allowed: -+ ./in.cue:231:11 -+ ./in.cue:235:17 +issue1881.p3.2.retry: structural cycle: + ./in.cue:231:14 +siblingInsertion.t2.p1.deployment: adding field logging not allowed as field set was already referenced: @@ -880,7 +816,7 @@ diff old new Result: (_|_){ -@@ -19,7 +66,9 @@ +@@ -19,7 +50,9 @@ B: (struct){ a: (struct){ parent: (string){ "" } @@ -891,7 +827,7 @@ diff old new } } } -@@ -52,17 +101,12 @@ +@@ -52,17 +85,12 @@ } } _e: (#struct){ @@ -915,7 +851,7 @@ diff old new } } e: (#struct){ -@@ -76,12 +120,7 @@ +@@ -76,12 +104,7 @@ } f2: (#struct){ a: (#struct){ @@ -929,7 +865,7 @@ diff old new } } } -@@ -112,15 +151,14 @@ +@@ -112,15 +135,14 @@ } } list: (struct){ @@ -948,7 +884,7 @@ diff old new } } } -@@ -127,116 +165,168 @@ +@@ -127,116 +149,152 @@ insertionError: (_|_){ // [eval] A: (_|_){ @@ -1087,47 +1023,39 @@ diff old new + reject: (string){ string } + resource: (string){ string } + retry: (_|_){ -+ // [structural cycle] selfReferential.acrossOr.t1.p1.0.reject: structural cycle: ++ // [structural cycle] selfReferential.acrossOr.t1.p1.2.retry: structural cycle: + // ./in.cue:133:14 + } + } + #Output: (_|_){ -+ // [structural cycle] selfReferential.acrossOr.t1.p1.0.reject: structural cycle: ++ // [structural cycle] selfReferential.acrossOr.t1.p1.2.retry: structural cycle: + // ./in.cue:133:14 + } + } + p2: (_|_){ -+ // [eval] ++ // [structural cycle] + #Output: (_|_){ -+ // [structural cycle] selfReferential.acrossOr.t1.p2.0.reject: structural cycle: ++ // [structural cycle] selfReferential.acrossOr.t1.p2.2.retry: structural cycle: + // ./in.cue:139:14 + } + o: (_|_){ -+ // [eval] selfReferential.acrossOr.t1.p2.o.retry: field not allowed: -+ // ./in.cue:139:11 -+ // ./in.cue:143:17 -+ // selfReferential.acrossOr.t1.p2.2.retry: structural cycle: ++ // [structural cycle] selfReferential.acrossOr.t1.p2.2.retry: structural cycle: + // ./in.cue:139:14 -+ retry: (_|_){ -+ // [eval] selfReferential.acrossOr.t1.p2.o.retry: field not allowed: -+ // ./in.cue:139:11 -+ // ./in.cue:143:17 -+ } + } + #AllOutputs: (_|_){ + // [structural cycle] + reject: (string){ string } + resource: (string){ string } + retry: (_|_){ -+ // [structural cycle] selfReferential.acrossOr.t1.p2.0.reject: structural cycle: ++ // [structural cycle] selfReferential.acrossOr.t1.p2.2.retry: structural cycle: + // ./in.cue:139:14 + } + } + } + p3: (_|_){ -+ // [eval] ++ // [structural cycle] + #Output: (_|_){ -+ // [structural cycle] selfReferential.acrossOr.t1.p3.0.reject: structural cycle: ++ // [structural cycle] selfReferential.acrossOr.t1.p3.2.retry: structural cycle: + // ./in.cue:153:14 + } + #AllOutputs: (_|_){ @@ -1135,21 +1063,13 @@ diff old new + reject: (string){ string } + resource: (string){ string } + retry: (_|_){ -+ // [structural cycle] selfReferential.acrossOr.t1.p3.0.reject: structural cycle: ++ // [structural cycle] selfReferential.acrossOr.t1.p3.2.retry: structural cycle: + // ./in.cue:153:14 + } + } + o: (_|_){ -+ // [eval] selfReferential.acrossOr.t1.p3.o.retry: field not allowed: -+ // ./in.cue:153:11 -+ // ./in.cue:163:17 -+ // selfReferential.acrossOr.t1.p3.0.reject: structural cycle: ++ // [structural cycle] selfReferential.acrossOr.t1.p3.2.retry: structural cycle: + // ./in.cue:153:14 -+ retry: (_|_){ -+ // [eval] selfReferential.acrossOr.t1.p3.o.retry: field not allowed: -+ // ./in.cue:153:11 -+ // ./in.cue:163:17 -+ } + } + } + } @@ -1226,7 +1146,7 @@ diff old new } } } -@@ -251,84 +341,102 @@ +@@ -251,84 +309,86 @@ } } } @@ -1329,18 +1249,18 @@ diff old new + retry: (_|_){ + // [structural cycle] + output: (_|_){ -+ // [structural cycle] issue1881.p1.0.reject: structural cycle: ++ // [structural cycle] issue1881.p1.2.retry: structural cycle: + // ./in.cue:205:14 + } + } + } + #Output: (_|_){ -+ // [structural cycle] issue1881.p1.0.reject: structural cycle: ++ // [structural cycle] issue1881.p1.2.retry: structural cycle: + // ./in.cue:205:14 + } + } + p2: (_|_){ -+ // [eval] ++ // [structural cycle] + #AllOutputs: (_|_){ + // [structural cycle] + reject: (string){ string } @@ -1354,16 +1274,8 @@ diff old new + } + } + o: (_|_){ -+ // [eval] issue1881.p2.o.retry: field not allowed: -+ // ./in.cue:219:11 -+ // ./in.cue:217:17 -+ // issue1881.p2.2.retry: structural cycle: ++ // [structural cycle] issue1881.p2.2.retry: structural cycle: + // ./in.cue:219:14 -+ retry: (_|_){ -+ // [eval] issue1881.p2.o.retry: field not allowed: -+ // ./in.cue:219:11 -+ // ./in.cue:217:17 -+ } + } + #Output: (_|_){ + // [structural cycle] issue1881.p2.2.retry: structural cycle: @@ -1371,7 +1283,7 @@ diff old new + } + } + p3: (_|_){ -+ // [eval] ++ // [structural cycle] + #AllOutputs: (_|_){ + // [structural cycle] + reject: (string){ string } @@ -1389,16 +1301,8 @@ diff old new + // ./in.cue:231:14 + } + o: (_|_){ -+ // [eval] issue1881.p3.o.retry: field not allowed: -+ // ./in.cue:231:11 -+ // ./in.cue:235:17 -+ // issue1881.p3.2.retry: structural cycle: ++ // [structural cycle] issue1881.p3.2.retry: structural cycle: + // ./in.cue:231:14 -+ retry: (_|_){ -+ // [eval] issue1881.p3.o.retry: field not allowed: -+ // ./in.cue:231:11 -+ // ./in.cue:235:17 -+ } + } + } + } @@ -1407,7 +1311,7 @@ diff old new t1: (struct){ p1: (struct){ D: (struct){ -@@ -361,40 +469,34 @@ +@@ -361,40 +421,34 @@ } } } @@ -1487,18 +1391,18 @@ diff old new -Reused: 1260 -Allocs: 60 -Retain: 145 -+Leaks: 431 -+Freed: 12 -+Reused: 12 -+Allocs: 431 ++Leaks: 546 ++Freed: 15 ++Reused: 15 ++Allocs: 546 +Retain: 0 -Unifications: 832 -Conjuncts: 2525 -Disjuncts: 1404 -+Unifications: 397 -+Conjuncts: 2202 -+Disjuncts: 36 ++Unifications: 497 ++Conjuncts: 2032 ++Disjuncts: 46 -- out/eval/stats -- Leaks: 50 Freed: 1270 diff --git a/cue/testdata/cycle/constraints.txtar b/cue/testdata/cycle/constraints.txtar index 10f00a1d8ff..e960228f421 100644 --- a/cue/testdata/cycle/constraints.txtar +++ b/cue/testdata/cycle/constraints.txtar @@ -445,11 +445,11 @@ Conjuncts: 321 Disjuncts: 147 -- out/evalalpha -- Errors: -mutuallyTriggeringCycle.t1.x.c.b.b.b.b: structural cycle -noCancelSelfInvoke.t1.x.c.b.b.b: structural cycle +mutuallyTriggeringCycle.t1.x.c.b.b.b.b.b.b: structural cycle +noCancelSelfInvoke.t1.x.c.b.b.b.b.b: structural cycle noCancelSelfInvoke.t1.x.c.c: structural cycle selfTriggerCycle.issue1503.#T.a.b.link: structural cycle -selfTriggerCycle.long1.a.c.b.c.b.c.b.c.b: structural cycle +selfTriggerCycle.long1.a.c.b.c.b.c.b: structural cycle selfTriggerCycle.t1.#T.b.b: structural cycle selfTriggerCycle.t2.b.c.a: structural cycle @@ -641,7 +641,13 @@ Result: b: (_|_){ // [structural cycle] b: (_|_){ - // [structural cycle] noCancelSelfInvoke.t1.x.c.b.b.b: structural cycle + // [structural cycle] + b: (_|_){ + // [structural cycle] + b: (_|_){ + // [structural cycle] noCancelSelfInvoke.t1.x.c.b.b.b.b.b: structural cycle + } + } } } } @@ -696,13 +702,7 @@ Result: c: (_|_){ // [structural cycle] b: (_|_){ - // [structural cycle] - c: (_|_){ - // [structural cycle] - b: (_|_){ - // [structural cycle] selfTriggerCycle.long1.a.c.b.c.b.c.b.c.b: structural cycle - } - } + // [structural cycle] selfTriggerCycle.long1.a.c.b.c.b.c.b: structural cycle } } } @@ -729,14 +729,18 @@ Result: } b: (_|_){ // [structural cycle] - link: ~(selfTriggerCycle.issue1503.#T) + link: (_|_){ + // [structural cycle] + } } } } } b: (_|_){ // [structural cycle] - link: ~(selfTriggerCycle.issue1503.#T) + link: (_|_){ + // [structural cycle] + } } } } @@ -771,7 +775,13 @@ Result: b: (_|_){ // [structural cycle] b: (_|_){ - // [structural cycle] mutuallyTriggeringCycle.t1.x.c.b.b.b.b: structural cycle + // [structural cycle] + b: (_|_){ + // [structural cycle] + b: (_|_){ + // [structural cycle] mutuallyTriggeringCycle.t1.x.c.b.b.b.b.b.b: structural cycle + } + } } } } @@ -785,6 +795,19 @@ Result: diff old new --- old +++ new +@@ -1,9 +1,9 @@ + Errors: +-mutuallyTriggeringCycle.t1.x.c.b.b.b.b: structural cycle +-noCancelSelfInvoke.t1.x.c.b.b.b: structural cycle ++mutuallyTriggeringCycle.t1.x.c.b.b.b.b.b.b: structural cycle ++noCancelSelfInvoke.t1.x.c.b.b.b.b.b: structural cycle + noCancelSelfInvoke.t1.x.c.c: structural cycle + selfTriggerCycle.issue1503.#T.a.b.link: structural cycle +-selfTriggerCycle.long1.a.c.b.c.b.c.b.c.b: structural cycle ++selfTriggerCycle.long1.a.c.b.c.b.c.b: structural cycle + selfTriggerCycle.t1.#T.b.b: structural cycle + selfTriggerCycle.t2.b.c.a: structural cycle + @@ -149,18 +149,10 @@ #D: (#struct){ } @@ -808,7 +831,7 @@ diff old new } #S: (#struct){ y: (#struct){ -@@ -198,18 +190,18 @@ +@@ -198,18 +190,24 @@ // [structural cycle] c: (_|_){ // [structural cycle] @@ -817,7 +840,13 @@ diff old new + b: (_|_){ + // [structural cycle] + b: (_|_){ -+ // [structural cycle] noCancelSelfInvoke.t1.x.c.b.b.b: structural cycle ++ // [structural cycle] ++ b: (_|_){ ++ // [structural cycle] ++ b: (_|_){ ++ // [structural cycle] noCancelSelfInvoke.t1.x.c.b.b.b.b.b: structural cycle ++ } ++ } + } + } + } @@ -836,7 +865,7 @@ diff old new } } } -@@ -218,12 +210,7 @@ +@@ -218,12 +216,7 @@ // [structural cycle] t1: (_|_){ // [structural cycle] @@ -850,7 +879,22 @@ diff old new #T: (_|_){ // [structural cycle] b: (_|_){ -@@ -284,21 +271,26 @@ +@@ -263,13 +256,7 @@ + c: (_|_){ + // [structural cycle] + b: (_|_){ +- // [structural cycle] +- c: (_|_){ +- // [structural cycle] +- b: (_|_){ +- // [structural cycle] selfTriggerCycle.long1.a.c.b.c.b.c.b.c.b: structural cycle +- } +- } ++ // [structural cycle] selfTriggerCycle.long1.a.c.b.c.b.c.b: structural cycle + } + } + } +@@ -284,20 +271,29 @@ // [structural cycle] a: (_|_){ // [structural cycle] @@ -868,7 +912,6 @@ diff old new - two: (struct){ - } - } -- } + a: (_|_){ + // [structural cycle] + two: (_|_){ @@ -877,17 +920,35 @@ diff old new + } + b: (_|_){ + // [structural cycle] -+ link: ~(selfTriggerCycle.issue1503.#T) ++ link: (_|_){ ++ // [structural cycle] ++ } + } + } + } + } + b: (_|_){ + // [structural cycle] -+ link: ~(selfTriggerCycle.issue1503.#T) ++ link: (_|_){ ++ // [structural cycle] + } } } - } +@@ -333,7 +329,13 @@ + b: (_|_){ + // [structural cycle] + b: (_|_){ +- // [structural cycle] mutuallyTriggeringCycle.t1.x.c.b.b.b.b: structural cycle ++ // [structural cycle] ++ b: (_|_){ ++ // [structural cycle] ++ b: (_|_){ ++ // [structural cycle] mutuallyTriggeringCycle.t1.x.c.b.b.b.b.b.b: structural cycle ++ } ++ } + } + } + } -- diff/todo/p2 -- issue1503: cycle detected too late (but not super late) selfTriggerCycle.t1.a.b.b: cycle detected slightly too late diff --git a/cue/testdata/cycle/evaluate.txtar b/cue/testdata/cycle/evaluate.txtar index 531657e8aa1..a1e3c04ebfa 100644 --- a/cue/testdata/cycle/evaluate.txtar +++ b/cue/testdata/cycle/evaluate.txtar @@ -127,10 +127,11 @@ Conjuncts: 299 Disjuncts: 192 -- out/evalalpha -- Errors: +printCycle.a.X: structural cycle letCycleOK.t2.a.X: structural cycle: - ./in.cue:24:6 + ./in.cue:23:11 letCycleFail.t1.a.X: structural cycle: - ./in.cue:34:6 + ./in.cue:33:11 listCycleOK.0: structural cycle: ./in.cue:50:9 disjunctionCycle.b: cannot use 1 (type int) as list in argument 1 to and: @@ -144,7 +145,7 @@ closeCycle.d: structural cycle: structCycle.0.d: structural cycle: ./in.cue:79:9 embedCycle: structural cycle: - ./in.cue:85:11 + ./in.cue:85:12 listAddCycle.0.0: structural cycle: ./in.cue:91:17 listMulCycle.0.a.b: structural cycle: @@ -153,8 +154,6 @@ closeFail.x.b: field not allowed: ./in.cue:105:6 ./in.cue:104:6 ./in.cue:107:5 -printCycle.a: cannot use {X:~(printCycle.a)} (type struct) as type bool: - ./in.cue:113:3 Result: (_|_){ @@ -176,7 +175,7 @@ Result: } c: (_|_){ // [structural cycle] letCycleOK.t2.a.X: structural cycle: - // ./in.cue:24:6 + // ./in.cue:23:11 } } } @@ -193,20 +192,15 @@ Result: } c: (_|_){ // [structural cycle] letCycleFail.t1.a.X: structural cycle: - // ./in.cue:34:6 + // ./in.cue:33:11 } } } t2: (struct){ a: (struct){ - let X#4 = (struct){ - let X#4 = (_|_){ - // [structural cycle] letCycleFail.t2.a.X.X: structural cycle: - // ./in.cue:43:6 - } - x: (struct){ - y: (string){ "" } - } + let X#4 = (_|_){ + // [structural cycle] letCycleFail.t2.a.X: structural cycle: + // ./in.cue:43:6 } x: (struct){ y: (string){ "" } @@ -271,7 +265,9 @@ Result: // [structural cycle] letCycleWithAnd.0: structural cycle: // ./in.cue:67:9 } - c: ~(letCycleWithAnd.a) + c: (_|_){ + // [structural cycle] + } } closeCycle: (_|_){ // [structural cycle] @@ -307,15 +303,15 @@ Result: // [structural cycle] a: (_|_){ // [structural cycle] embedCycle: structural cycle: - // ./in.cue:85:11 + // ./in.cue:85:12 } b: (_|_){ // [structural cycle] embedCycle: structural cycle: - // ./in.cue:85:11 + // ./in.cue:85:12 } c: (_|_){ // [structural cycle] embedCycle: structural cycle: - // ./in.cue:85:11 + // ./in.cue:85:12 } } listAddCycle: (_|_){ @@ -365,11 +361,12 @@ Result: } } printCycle: (_|_){ - // [eval] + // [structural cycle] a: (_|_){ - // [eval] printCycle.a: cannot use {X:~(printCycle.a)} (type struct) as type bool: - // ./in.cue:113:3 - X: ~(printCycle.a) + // [structural cycle] + X: (_|_){ + // [structural cycle] printCycle.a.X: structural cycle + } } } } @@ -377,36 +374,19 @@ Result: diff old new --- old +++ new -@@ -1,47 +1,55 @@ +@@ -1,47 +1,54 @@ Errors: -closeCycle.a: structural cycle -closeCycle.b.d: structural cycle --closeFail.x.b: field not allowed: -- ./in.cue:104:6 -- ./in.cue:105:12 -- ./in.cue:106:6 -- ./in.cue:107:5 --letCycleFail.t1.a.c: structural cycle --structCycle.a: structural cycle --structCycle.b.d: structural cycle --disjunctionCycle.a: cannot use 1 (type int) as list in argument 1 to and: -- ./in.cue:56:9 ++printCycle.a.X: structural cycle +letCycleOK.t2.a.X: structural cycle: -+ ./in.cue:24:6 ++ ./in.cue:23:11 +letCycleFail.t1.a.X: structural cycle: -+ ./in.cue:34:6 ++ ./in.cue:33:11 +listCycleOK.0: structural cycle: + ./in.cue:50:9 - disjunctionCycle.b: cannot use 1 (type int) as list in argument 1 to and: - ./in.cue:56:9 --disjunctionCycle.c: cannot use 1 (type int) as list in argument 1 to and: -- ./in.cue:56:9 --b: structural cycle: -- ./in.cue:62:6 --closeCycle.c: structural cycle: -- ./in.cue:73:15 --structCycle.c: structural cycle: -- ./in.cue:79:14 ++disjunctionCycle.b: cannot use 1 (type int) as list in argument 1 to and: ++ ./in.cue:56:9 +forCycle.0.b: structural cycle: + ./in.cue:62:9 +letCycleWithAnd.0: structural cycle: @@ -415,20 +395,37 @@ diff old new + ./in.cue:73:11 +structCycle.0.d: structural cycle: + ./in.cue:79:9 - embedCycle: structural cycle: - ./in.cue:85:11 --printCycle.a.X.X: structural cycle: -- ./in.cue:113:6 ++embedCycle: structural cycle: ++ ./in.cue:85:12 +listAddCycle.0.0: structural cycle: + ./in.cue:91:17 +listMulCycle.0.a.b: structural cycle: + ./in.cue:97:17 -+closeFail.x.b: field not allowed: + closeFail.x.b: field not allowed: + ./in.cue:105:6 -+ ./in.cue:104:6 -+ ./in.cue:107:5 -+printCycle.a: cannot use {X:~(printCycle.a)} (type struct) as type bool: -+ ./in.cue:113:3 + ./in.cue:104:6 +- ./in.cue:105:12 +- ./in.cue:106:6 + ./in.cue:107:5 +-letCycleFail.t1.a.c: structural cycle +-structCycle.a: structural cycle +-structCycle.b.d: structural cycle +-disjunctionCycle.a: cannot use 1 (type int) as list in argument 1 to and: +- ./in.cue:56:9 +-disjunctionCycle.b: cannot use 1 (type int) as list in argument 1 to and: +- ./in.cue:56:9 +-disjunctionCycle.c: cannot use 1 (type int) as list in argument 1 to and: +- ./in.cue:56:9 +-b: structural cycle: +- ./in.cue:62:6 +-closeCycle.c: structural cycle: +- ./in.cue:73:15 +-structCycle.c: structural cycle: +- ./in.cue:79:14 +-embedCycle: structural cycle: +- ./in.cue:85:11 +-printCycle.a.X.X: structural cycle: +- ./in.cue:113:6 Result: (_|_){ @@ -454,22 +451,43 @@ diff old new - c: (int){ 1 } + c: (_|_){ + // [structural cycle] letCycleOK.t2.a.X: structural cycle: -+ // ./in.cue:24:6 ++ // ./in.cue:23:11 + } } } } -@@ -56,7 +64,8 @@ +@@ -56,20 +63,16 @@ // [structural cycle] letCycleFail.t1.a.X: structural cycle } c: (_|_){ - // [structural cycle] letCycleFail.t1.a.c: structural cycle +- } +- } +- } +- t2: (struct){ +- a: (struct){ +- let X#4 = (struct){ +- let X#4 = (_|_){ +- // [structural cycle] letCycleFail.t2.a.X.X: structural cycle: +- // ./in.cue:43:6 +- } +- x: (struct){ +- y: (string){ "" } +- } + // [structural cycle] letCycleFail.t1.a.X: structural cycle: -+ // ./in.cue:34:6 ++ // ./in.cue:33:11 ++ } ++ } ++ } ++ t2: (struct){ ++ a: (struct){ ++ let X#4 = (_|_){ ++ // [structural cycle] letCycleFail.t2.a.X: structural cycle: ++ // ./in.cue:43:6 } - } - } -@@ -77,23 +86,33 @@ + x: (struct){ + y: (string){ "" } +@@ -77,23 +80,33 @@ } } } @@ -516,7 +534,7 @@ diff old new // ./in.cue:56:9 } } -@@ -102,57 +121,58 @@ +@@ -102,57 +115,60 @@ #A: (_|_){ // [structural cycle] a: (_|_){ @@ -541,7 +559,6 @@ diff old new - c: (struct){ - d: (struct){ - } -- } + // [structural cycle] forCycle.0.b: structural cycle: + // ./in.cue:62:9 + } @@ -559,7 +576,9 @@ diff old new + // [structural cycle] letCycleWithAnd.0: structural cycle: + // ./in.cue:67:9 + } -+ c: ~(letCycleWithAnd.a) ++ c: (_|_){ ++ // [structural cycle] + } } closeCycle: (_|_){ // [structural cycle] @@ -614,7 +633,30 @@ diff old new } } embedCycle: (_|_){ -@@ -173,25 +193,31 @@ +@@ -159,39 +175,45 @@ + // [structural cycle] + a: (_|_){ + // [structural cycle] embedCycle: structural cycle: +- // ./in.cue:85:11 +- } +- b: (_|_){ +- // [structural cycle] embedCycle: structural cycle: +- // ./in.cue:85:11 +- } +- c: (_|_){ +- // [structural cycle] embedCycle: structural cycle: +- // ./in.cue:85:11 ++ // ./in.cue:85:12 ++ } ++ b: (_|_){ ++ // [structural cycle] embedCycle: structural cycle: ++ // ./in.cue:85:12 ++ } ++ c: (_|_){ ++ // [structural cycle] embedCycle: structural cycle: ++ // ./in.cue:85:12 + } + } listAddCycle: (_|_){ // [structural cycle] a: (_|_){ @@ -660,7 +702,7 @@ diff old new } } closeFail: (_|_){ -@@ -201,21 +227,21 @@ +@@ -201,21 +223,22 @@ } x: (_|_){ // [eval] @@ -677,15 +719,14 @@ diff old new } } printCycle: (_|_){ -- // [structural cycle] -- a: (_|_){ + // [structural cycle] + a: (_|_){ - // [structural cycle] printCycle.a.X.X: structural cycle: - // ./in.cue:113:6 -+ // [eval] -+ a: (_|_){ -+ // [eval] printCycle.a: cannot use {X:~(printCycle.a)} (type struct) as type bool: -+ // ./in.cue:113:3 -+ X: ~(printCycle.a) ++ // [structural cycle] ++ X: (_|_){ ++ // [structural cycle] printCycle.a.X: structural cycle ++ } } } } diff --git a/cue/testdata/cycle/inline.txtar b/cue/testdata/cycle/inline.txtar index 9e6d71ba373..9bddd6fedbc 100644 --- a/cue/testdata/cycle/inline.txtar +++ b/cue/testdata/cycle/inline.txtar @@ -152,6 +152,38 @@ inline: acrossFields: ok1: { } } } +-- out/evalalpha/stats -- +Leaks: 104 +Freed: 0 +Reused: 0 +Allocs: 104 +Retain: 0 + +Unifications: 100 +Conjuncts: 498 +Disjuncts: 0 +-- diff/-out/evalalpha/stats<==>+out/eval/stats -- +diff old new +--- old ++++ new +@@ -1,9 +1,9 @@ +-Leaks: 247 +-Freed: 141 +-Reused: 136 +-Allocs: 252 +-Retain: 834 ++Leaks: 104 ++Freed: 0 ++Reused: 0 ++Allocs: 104 ++Retain: 0 + +-Unifications: 388 +-Conjuncts: 1307 +-Disjuncts: 707 ++Unifications: 100 ++Conjuncts: 498 ++Disjuncts: 0 -- out/eval/stats -- Leaks: 247 Freed: 141 @@ -271,16 +303,8 @@ structural cycle: ./x.cue:15:9 structural cycle: ./x.cue:20:9 -in: structural cycle: - ./x.cue:34:17 -in: structural cycle: - ./x.cue:35:17 in: structural cycle: ./x.cue:38:17 -in: structural cycle: - ./x.cue:49:17 -in: structural cycle: - ./x.cue:50:17 in: structural cycle: ./x.cue:54:17 @@ -311,7 +335,7 @@ Result: nn: (int){ int } out: (_|_){ // [structural cycle] structural cycle: - // ./x.cue:12:24 + // ./x.cue:15:9 } } f: (_|_){ @@ -352,11 +376,11 @@ Result: } k20: (_|_){ // [structural cycle] in: structural cycle: - // ./x.cue:34:17 + // ./x.cue:38:17 } k30: (_|_){ // [structural cycle] in: structural cycle: - // ./x.cue:35:17 + // ./x.cue:38:17 } } ok1: (_|_){ @@ -372,11 +396,11 @@ Result: } k20: (_|_){ // [structural cycle] in: structural cycle: - // ./x.cue:49:17 + // ./x.cue:54:17 } k30: (_|_){ // [structural cycle] in: structural cycle: - // ./x.cue:50:17 + // ./x.cue:54:17 } } } @@ -386,7 +410,7 @@ Result: diff old new --- old +++ new -@@ -2,11 +2,23 @@ +@@ -2,11 +2,15 @@ structural cycle: ./x.cue:6:9 structural cycle: @@ -397,21 +421,13 @@ diff old new ./x.cue:20:9 in: structural cycle: - ./x.cue:30:8 -+ ./x.cue:34:17 -+in: structural cycle: -+ ./x.cue:35:17 -+in: structural cycle: + ./x.cue:38:17 +in: structural cycle: -+ ./x.cue:49:17 -+in: structural cycle: -+ ./x.cue:50:17 -+in: structural cycle: + ./x.cue:54:17 Result: (_|_){ -@@ -28,7 +40,7 @@ +@@ -28,7 +32,7 @@ // [structural cycle] f2: (_|_){ // [structural cycle] structural cycle: @@ -420,16 +436,7 @@ diff old new } fRec: (_|_){ // [structural cycle] -@@ -35,7 +47,7 @@ - nn: (int){ int } - out: (_|_){ - // [structural cycle] structural cycle: -- // ./x.cue:15:9 -+ // ./x.cue:12:24 - } - } - f: (_|_){ -@@ -72,26 +84,36 @@ +@@ -72,26 +76,36 @@ k00: (int){ 0 } k10: (_|_){ // [structural cycle] in: structural cycle: @@ -449,11 +456,11 @@ diff old new + } + k20: (_|_){ + // [structural cycle] in: structural cycle: -+ // ./x.cue:34:17 ++ // ./x.cue:38:17 + } + k30: (_|_){ + // [structural cycle] in: structural cycle: -+ // ./x.cue:35:17 ++ // ./x.cue:38:17 + } + } + ok1: (_|_){ @@ -472,11 +479,11 @@ diff old new + } + k20: (_|_){ + // [structural cycle] in: structural cycle: -+ // ./x.cue:49:17 ++ // ./x.cue:54:17 + } + k30: (_|_){ + // [structural cycle] in: structural cycle: -+ // ./x.cue:50:17 ++ // ./x.cue:54:17 + } } } diff --git a/cue/testdata/cycle/inline_non_recursive.txtar b/cue/testdata/cycle/inline_non_recursive.txtar index 410805c0de1..116f5335fd0 100644 --- a/cue/testdata/cycle/inline_non_recursive.txtar +++ b/cue/testdata/cycle/inline_non_recursive.txtar @@ -70,91 +70,48 @@ Unifications: 194 Conjuncts: 1034 Disjuncts: 0 -- out/evalalpha -- -Errors: -structural cycle: - ./in.cue:10:8 -structural cycle: - ./in.cue:21:9 -structural cycle: - ./in.cue:32:15 -structural cycle: - ./in.cue:43:15 -structural cycle: - ./in.cue:56:8 - -Result: -(_|_){ - // [structural cycle] - ok1: (_|_){ - // [structural cycle] +(struct){ + ok1: (struct){ f: (struct){ in: (number){ number } out: (number){ number } } k00: (int){ 0 } k10: (int){ 0 } - k20: (_|_){ - // [structural cycle] structural cycle: - // ./in.cue:10:8 - } - k30: (_|_){ - // [structural cycle] structural cycle: - // ./in.cue:10:8 - } + k20: (int){ 0 } + k30: (int){ 0 } } - ok2: (_|_){ - // [structural cycle] + ok2: (struct){ f: (struct){ in: (number){ number } out: (number){ number } } k00: (int){ 0 } k10: (int){ 0 } - k20: (_|_){ - // [structural cycle] structural cycle: - // ./in.cue:21:9 - } - k30: (_|_){ - // [structural cycle] structural cycle: - // ./in.cue:21:9 - } + k20: (int){ 0 } + k30: (int){ 0 } } - ok3: (_|_){ - // [structural cycle] + ok3: (struct){ f: (struct){ in: (number){ number } out: (number){ number } } k00: (int){ 0 } k10: (int){ 0 } - k20: (_|_){ - // [structural cycle] structural cycle: - // ./in.cue:32:15 - } - k30: (_|_){ - // [structural cycle] structural cycle: - // ./in.cue:32:15 - } + k20: (int){ 0 } + k30: (int){ 0 } } - ok4: (_|_){ - // [structural cycle] + ok4: (struct){ f: (struct){ in: (number){ number } out: (number){ number } } k00: (int){ 0 } k10: (int){ 0 } - k20: (_|_){ - // [structural cycle] structural cycle: - // ./in.cue:43:15 - } - k30: (_|_){ - // [structural cycle] structural cycle: - // ./in.cue:43:15 - } + k20: (int){ 0 } + k30: (int){ 0 } } - issue1708: (_|_){ - // [structural cycle] + issue1708: (struct){ #add10: (#struct){ in: (number){ number } a10: (#struct){ @@ -173,14 +130,8 @@ Result: } k00: (int){ 0 } k10: (int){ 10 } - k20: (_|_){ - // [structural cycle] structural cycle: - // ./in.cue:56:8 - } - k30: (_|_){ - // [structural cycle] structural cycle: - // ./in.cue:56:8 - } + k20: (int){ 20 } + k30: (int){ 30 } } } -- diff/-out/evalalpha/stats<==>+out/eval/stats -- @@ -209,138 +160,7 @@ diff old new diff old new --- old +++ new -@@ -1,45 +1,88 @@ --(struct){ -- ok1: (struct){ -- f: (struct){ -- in: (number){ number } -- out: (number){ number } -- } -- k00: (int){ 0 } -- k10: (int){ 0 } -- k20: (int){ 0 } -- k30: (int){ 0 } -- } -- ok2: (struct){ -- f: (struct){ -- in: (number){ number } -- out: (number){ number } -- } -- k00: (int){ 0 } -- k10: (int){ 0 } -- k20: (int){ 0 } -- k30: (int){ 0 } -- } -- ok3: (struct){ -- f: (struct){ -- in: (number){ number } -- out: (number){ number } -- } -- k00: (int){ 0 } -- k10: (int){ 0 } -- k20: (int){ 0 } -- k30: (int){ 0 } -- } -- ok4: (struct){ -- f: (struct){ -- in: (number){ number } -- out: (number){ number } -- } -- k00: (int){ 0 } -- k10: (int){ 0 } -- k20: (int){ 0 } -- k30: (int){ 0 } -- } -- issue1708: (struct){ -+Errors: -+structural cycle: -+ ./in.cue:10:8 -+structural cycle: -+ ./in.cue:21:9 -+structural cycle: -+ ./in.cue:32:15 -+structural cycle: -+ ./in.cue:43:15 -+structural cycle: -+ ./in.cue:56:8 -+ -+Result: -+(_|_){ -+ // [structural cycle] -+ ok1: (_|_){ -+ // [structural cycle] -+ f: (struct){ -+ in: (number){ number } -+ out: (number){ number } -+ } -+ k00: (int){ 0 } -+ k10: (int){ 0 } -+ k20: (_|_){ -+ // [structural cycle] structural cycle: -+ // ./in.cue:10:8 -+ } -+ k30: (_|_){ -+ // [structural cycle] structural cycle: -+ // ./in.cue:10:8 -+ } -+ } -+ ok2: (_|_){ -+ // [structural cycle] -+ f: (struct){ -+ in: (number){ number } -+ out: (number){ number } -+ } -+ k00: (int){ 0 } -+ k10: (int){ 0 } -+ k20: (_|_){ -+ // [structural cycle] structural cycle: -+ // ./in.cue:21:9 -+ } -+ k30: (_|_){ -+ // [structural cycle] structural cycle: -+ // ./in.cue:21:9 -+ } -+ } -+ ok3: (_|_){ -+ // [structural cycle] -+ f: (struct){ -+ in: (number){ number } -+ out: (number){ number } -+ } -+ k00: (int){ 0 } -+ k10: (int){ 0 } -+ k20: (_|_){ -+ // [structural cycle] structural cycle: -+ // ./in.cue:32:15 -+ } -+ k30: (_|_){ -+ // [structural cycle] structural cycle: -+ // ./in.cue:32:15 -+ } -+ } -+ ok4: (_|_){ -+ // [structural cycle] -+ f: (struct){ -+ in: (number){ number } -+ out: (number){ number } -+ } -+ k00: (int){ 0 } -+ k10: (int){ 0 } -+ k20: (_|_){ -+ // [structural cycle] structural cycle: -+ // ./in.cue:43:15 -+ } -+ k30: (_|_){ -+ // [structural cycle] structural cycle: -+ // ./in.cue:43:15 -+ } -+ } -+ issue1708: (_|_){ -+ // [structural cycle] - #add10: (#struct){ - in: (number){ number } - a10: (#struct){ -@@ -48,19 +91,23 @@ +@@ -48,14 +48,12 @@ // [incomplete] issue1708.#add10.a10.o: non-concrete value number in operand to +: // ./in.cue:52:20 // ./in.cue:51:7 @@ -356,19 +176,6 @@ diff old new } } k00: (int){ 0 } - k10: (int){ 10 } -- k20: (int){ 20 } -- k30: (int){ 30 } -+ k20: (_|_){ -+ // [structural cycle] structural cycle: -+ // ./in.cue:56:8 -+ } -+ k30: (_|_){ -+ // [structural cycle] structural cycle: -+ // ./in.cue:56:8 -+ } - } - } -- diff/todo/p3 -- Error positions. Incorrect path due to structure sharing. diff --git a/cue/testdata/cycle/structural.txtar b/cue/testdata/cycle/structural.txtar index 0fddd8fb878..4e07d2a1185 100644 --- a/cue/testdata/cycle/structural.txtar +++ b/cue/testdata/cycle/structural.txtar @@ -546,6 +546,38 @@ n1: a: b: int n2: n1 & {a: n1} n3: n1 & {n1} n4: n1 & {x: n1 & {y: n1 & {z: int}}} +-- out/evalalpha/stats -- +Leaks: 1780 +Freed: 431 +Reused: 431 +Allocs: 1780 +Retain: 0 + +Unifications: 1126 +Conjuncts: 5997 +Disjuncts: 1041 +-- diff/-out/evalalpha/stats<==>+out/eval/stats -- +diff old new +--- old ++++ new +@@ -1,9 +1,9 @@ +-Leaks: 15 +-Freed: 821 +-Reused: 809 +-Allocs: 27 +-Retain: 63 ++Leaks: 1780 ++Freed: 431 ++Reused: 431 ++Allocs: 1780 ++Retain: 0 + +-Unifications: 650 +-Conjuncts: 1268 +-Disjuncts: 871 ++Unifications: 1126 ++Conjuncts: 5997 ++Disjuncts: 1041 -- out/eval/stats -- Leaks: 15 Freed: 821 @@ -561,7 +593,6 @@ Errors: a1.f.0: structural cycle a3.f.g: structural cycle b12b.#list.tail: structural cycle -b12b.list1.tail.tail.tail: structural cycle b13.root.a.0.0: structural cycle b14.root.b.1.1: structural cycle b4.b.0: conflicting values 1 and [y] (mismatched types int and list): @@ -649,8 +680,6 @@ withLetFail._schema_1: structural cycle z1.z.g.h: structural cycle d3.config.0: structural cycle: ./in.cue:317:13 -d3.x.0: structural cycle: - ./in.cue:317:13 fieldsSumInfinite.issue3310.3: structural cycle: ./in.cue:371:18 e4.a.0.0: cannot combine regular field "c" with [1,{c:1} & (a|{}) & [{c:1}]]: @@ -915,7 +944,11 @@ Result: } c: (#struct){ d: ((string|struct)){ |((string){ string }, (#struct){ - b: (string){ string } + b: ((string|struct)){ |((string){ string }, (#struct){ + b: ((string|struct)){ |((string){ string }, (#struct){ + b: (string){ string } + }) } + }) } }) } } d: (struct){ @@ -926,9 +959,7 @@ Result: } b11: (struct){ #list: (#struct){ - tail: ((null|struct)){ |(*(null){ null }, (#struct){ - tail: (null){ null } - }) } + tail: (null){ null } } } b12: (struct){ @@ -945,7 +976,11 @@ Result: value: (int){ 3 } tail: (#struct){ value: (int){ 4 } - tail: (null){ null } + tail: ((null|struct)){ |(*(null){ null }, (#struct){ + value: (int){ int } + tail: (null){ null } + sum: (int){ int } + }) } sum: (int){ 4 } } sum: (int){ 7 } @@ -961,7 +996,7 @@ Result: // [structural cycle] b12b.#list.tail: structural cycle } list1: (_|_){ - // [structural cycle] b12b.list1.tail.tail.tail: structural cycle + // [structural cycle] b12b.#list.tail: structural cycle } } b13: (_|_){ @@ -1041,29 +1076,15 @@ Result: } a: (_|_){ // [structural cycle] - a: (_|_){ - // [structural cycle] - one: (_|_){ - // [structural cycle] - link: (_|_){ - // [structural cycle] - a: (_|_){ - // [structural cycle] - two: (_|_){ - // [structural cycle] - link: ~(p2.#T) - } - b: (_|_){ - // [structural cycle] - link: ~(p2.#T) + a: (struct){ + one: (struct){ + link: (struct){ + a: (struct){ + two: (struct){ } } } } - b: (_|_){ - // [structural cycle] - link: ~(p2.#T) - } } } } @@ -1129,17 +1150,11 @@ Result: } a: (_|_){ // [structural cycle] - a: (_|_){ - // [structural cycle] - 0: (_|_){ - // [structural cycle] - link: (_|_){ - // [structural cycle] - a: (_|_){ - // [structural cycle] - 0: (_|_){ - // [structural cycle] - link: ~(p5.#T) + a: (#list){ + 0: (struct){ + link: (struct){ + a: (#list){ + 0: (struct){ } } } @@ -1244,20 +1259,6 @@ Result: x: (_|_){ // [structural cycle] i: (int){ 0 } - a: (_|_){ - // [structural cycle] - b: (_|_){ - // [structural cycle] - c: (_|_){ - // [structural cycle] d3.x.0: structural cycle: - // ./in.cue:317:13 - } - } - } - indirect: (_|_){ - // [structural cycle] d3.x.0: structural cycle: - // ./in.cue:317:13 - } } } shortPathFail: (_|_){ @@ -1287,9 +1288,7 @@ Result: } comprehension: (struct){ #list: (#struct){ - tail: ((null|struct)){ |(*(null){ null }, (#struct){ - tail: (null){ null } - }) } + tail: (null){ null } } } } @@ -1342,9 +1341,7 @@ Result: head: (int){ 3 } tail: (struct){ head: (int){ 2 } - tail?: (_|_){ - // [structural cycle] listOptOK.a.tail.tail: structural cycle - } + tail?: ~(listOptOK.list) } } } @@ -1554,7 +1551,10 @@ Result: head: (int){ 3 } tail: (struct){ head: (int){ 4 } - tail: (null){ null } + tail: ((null|struct)){ |((struct){ + head: (int){ int } + tail: (null){ null } + }, (null){ null }) } } } } @@ -1568,7 +1568,10 @@ Result: head: (int){ 2 } tail: (struct){ head: (int){ 3 } - tail: (int){ 1 } + tail: ((int|struct)){ |((struct){ + head: (int){ int } + tail: (int){ 1 } + }, (int){ 1 }) } } } } @@ -1582,8 +1585,12 @@ Result: head: (int){ 2 } tail: (struct){ |((struct){ head: (int){ 3 } - tail: (struct){ - } + tail: (struct){ |((struct){ + head: (int){ int } + tail: (struct){ + } + }, (struct){ + }) } }, (struct){ head: (int){ 3 } }) } @@ -1718,11 +1725,8 @@ Result: diff old new --- old +++ new -@@ -2,24 +2,28 @@ - a1.f.0: structural cycle - a3.f.g: structural cycle +@@ -4,22 +4,25 @@ b12b.#list.tail: structural cycle -+b12b.list1.tail.tail.tail: structural cycle b13.root.a.0.0: structural cycle b14.root.b.1.1: structural cycle +b4.b.0: conflicting values 1 and [y] (mismatched types int and list): @@ -1754,7 +1758,7 @@ diff old new e1.a.c: structural cycle e1.b.c: structural cycle e2.a.c: structural cycle -@@ -32,61 +36,76 @@ +@@ -32,61 +35,74 @@ ./in.cue:403:5 ./in.cue:404:5 e3.b.c: structural cycle @@ -1839,8 +1843,6 @@ diff old new - ./in.cue:316:12 +d3.config.0: structural cycle: + ./in.cue:317:13 -+d3.x.0: structural cycle: -+ ./in.cue:317:13 +fieldsSumInfinite.issue3310.3: structural cycle: + ./in.cue:371:18 +e4.a.0.0: cannot combine regular field "c" with [1,{c:1} & (a|{}) & [{c:1}]]: @@ -1858,7 +1860,7 @@ diff old new Result: (_|_){ -@@ -128,10 +147,7 @@ +@@ -128,10 +144,7 @@ a7: (struct){ a: (string){ "foo" } b: (struct){ @@ -1870,7 +1872,7 @@ diff old new y: (string){ "foo" } } c: (struct){ -@@ -168,11 +184,17 @@ +@@ -168,11 +181,17 @@ } } b4: (_|_){ @@ -1893,7 +1895,7 @@ diff old new } } x: (_|_){ -@@ -240,10 +262,9 @@ +@@ -240,10 +259,9 @@ // [eval] 0: (_|_){ // [eval] b6.b.a.0: conflicting values 1 and [1] (mismatched types int and list): @@ -1905,7 +1907,7 @@ diff old new 0: (_|_){ // [structural cycle] b6.b.a.0.0: structural cycle } -@@ -261,11 +282,20 @@ +@@ -261,11 +279,20 @@ } } b7: (_|_){ @@ -1931,7 +1933,7 @@ diff old new } } a: (_|_){ -@@ -276,9 +306,7 @@ +@@ -276,9 +303,7 @@ } } b8: (struct){ @@ -1942,7 +1944,7 @@ diff old new a: (struct){ f: (string){ string } } -@@ -304,7 +332,7 @@ +@@ -304,7 +329,7 @@ #ref: (#struct){ ref: (string){ string } } @@ -1951,27 +1953,46 @@ diff old new c: (#list){ 0: ((string|struct)){ |((string){ string }, (#struct){ ref: (string){ string } -@@ -327,7 +355,9 @@ +@@ -327,7 +352,13 @@ }) } } c: (#struct){ - d: (string){ string } + d: ((string|struct)){ |((string){ string }, (#struct){ -+ b: (string){ string } ++ b: ((string|struct)){ |((string){ string }, (#struct){ ++ b: ((string|struct)){ |((string){ string }, (#struct){ ++ b: (string){ string } ++ }) } ++ }) } + }) } } d: (struct){ d: (struct){ -@@ -372,7 +402,7 @@ - // [structural cycle] b12b.#list.tail: structural cycle - } - list1: (_|_){ -- // [structural cycle] b12b.#list.tail: structural cycle -+ // [structural cycle] b12b.list1.tail.tail.tail: structural cycle +@@ -337,9 +368,7 @@ + } + b11: (struct){ + #list: (#struct){ +- tail: ((null|struct)){ |(*(null){ null }, (#struct){ +- tail: (null){ null } +- }) } ++ tail: (null){ null } } } - b13: (_|_){ -@@ -428,10 +458,7 @@ + b12: (struct){ +@@ -356,7 +385,11 @@ + value: (int){ 3 } + tail: (#struct){ + value: (int){ 4 } +- tail: (null){ null } ++ tail: ((null|struct)){ |(*(null){ null }, (#struct){ ++ value: (int){ int } ++ tail: (null){ null } ++ sum: (int){ int } ++ }) } + sum: (int){ 4 } + } + sum: (int){ 7 } +@@ -428,10 +461,7 @@ link: (#struct){ a: (#struct){ two: (#struct){ @@ -1983,44 +2004,7 @@ diff old new } } } -@@ -455,14 +482,28 @@ - } - a: (_|_){ - // [structural cycle] -- a: (struct){ -- one: (struct){ -- link: (struct){ -- a: (struct){ -- two: (struct){ -- } -- } -- } -+ a: (_|_){ -+ // [structural cycle] -+ one: (_|_){ -+ // [structural cycle] -+ link: (_|_){ -+ // [structural cycle] -+ a: (_|_){ -+ // [structural cycle] -+ two: (_|_){ -+ // [structural cycle] -+ link: ~(p2.#T) -+ } -+ b: (_|_){ -+ // [structural cycle] -+ link: ~(p2.#T) -+ } -+ } -+ } -+ } -+ b: (_|_){ -+ // [structural cycle] -+ link: ~(p2.#T) - } - } - } -@@ -505,10 +546,7 @@ +@@ -505,10 +535,7 @@ link: (#struct){ a: (#list){ 0: (#struct){ @@ -2032,30 +2016,7 @@ diff old new } } } -@@ -532,11 +570,17 @@ - } - a: (_|_){ - // [structural cycle] -- a: (#list){ -- 0: (struct){ -- link: (struct){ -- a: (#list){ -- 0: (struct){ -+ a: (_|_){ -+ // [structural cycle] -+ 0: (_|_){ -+ // [structural cycle] -+ link: (_|_){ -+ // [structural cycle] -+ a: (_|_){ -+ // [structural cycle] -+ 0: (_|_){ -+ // [structural cycle] -+ link: ~(p5.#T) - } - } - } -@@ -578,12 +622,7 @@ +@@ -578,12 +605,7 @@ b: (struct){ } c: (_|_){ @@ -2069,7 +2030,7 @@ diff old new } } } -@@ -599,56 +638,26 @@ +@@ -599,56 +621,26 @@ // [structural cycle] h: (int){ int } t: (_|_){ @@ -2146,7 +2107,7 @@ diff old new } } } -@@ -655,39 +664,47 @@ +@@ -655,16 +647,7 @@ } d3: (_|_){ // [structural cycle] @@ -2157,56 +2118,38 @@ diff old new - } - } - indirect: (null){ null } +- i: (int){ |(*(int){ 1 }, (int){ int }) } +- } +- x: (_|_){ + config: (_|_){ -+ // [structural cycle] -+ a: (_|_){ -+ // [structural cycle] -+ b: (_|_){ -+ // [structural cycle] -+ c: (_|_){ -+ // [structural cycle] d3.config.0: structural cycle: -+ // ./in.cue:317:13 -+ } -+ } -+ } -+ indirect: (_|_){ -+ // [structural cycle] d3.config.0: structural cycle: -+ // ./in.cue:317:13 -+ } - i: (int){ |(*(int){ 1 }, (int){ int }) } - } - x: (_|_){ // [structural cycle] -- a: (_|_){ -- // [structural cycle] -- b: (_|_){ -- // [structural cycle] -- c: (_|_){ + a: (_|_){ + // [structural cycle] +@@ -671,14 +654,19 @@ + b: (_|_){ + // [structural cycle] + c: (_|_){ - // [structural cycle] d3.x.a.b.c: structural cycle -- } -- } -- } -- indirect: (_|_){ ++ // [structural cycle] d3.config.0: structural cycle: ++ // ./in.cue:317:13 + } + } + } + indirect: (_|_){ - // [structural cycle] d3.x.indirect: structural cycle: - // ./in.cue:316:12 - } - i: (int){ 0 } -+ a: (_|_){ -+ // [structural cycle] -+ b: (_|_){ -+ // [structural cycle] -+ c: (_|_){ -+ // [structural cycle] d3.x.0: structural cycle: -+ // ./in.cue:317:13 -+ } -+ } -+ } -+ indirect: (_|_){ -+ // [structural cycle] d3.x.0: structural cycle: ++ // [structural cycle] d3.config.0: structural cycle: + // ./in.cue:317:13 + } ++ i: (int){ |(*(int){ 1 }, (int){ int }) } ++ } ++ x: (_|_){ ++ // [structural cycle] + i: (int){ 0 } } } +@@ -685,9 +673,7 @@ shortPathFail: (_|_){ // [structural cycle] doubleRef: (struct){ @@ -2217,7 +2160,7 @@ diff old new #List: (#struct){ Next: (null){ null } } -@@ -696,9 +713,7 @@ +@@ -696,9 +682,7 @@ // [structural cycle] t1: (struct){ #Foo: (#struct){ @@ -2228,7 +2171,7 @@ diff old new } } t2: (_|_){ -@@ -706,10 +721,7 @@ +@@ -706,10 +690,7 @@ Foo: (_|_){ // [structural cycle] ref: (_|_){ @@ -2240,7 +2183,18 @@ diff old new } } } -@@ -740,20 +752,24 @@ +@@ -716,9 +697,7 @@ + } + comprehension: (struct){ + #list: (#struct){ +- tail: ((null|struct)){ |(*(null){ null }, (#struct){ +- tail: (null){ null } +- }) } ++ tail: (null){ null } + } + } + } +@@ -740,20 +719,24 @@ schema: (_|_){ // [structural cycle] next: (_|_){ @@ -2273,7 +2227,18 @@ diff old new } } listOptOK: (struct){ -@@ -809,11 +825,12 @@ +@@ -767,9 +750,7 @@ + head: (int){ 3 } + tail: (struct){ + head: (int){ 2 } +- tail?: (_|_){ +- // [structural cycle] listOptOK.a.tail.tail: structural cycle +- } ++ tail?: ~(listOptOK.list) + } + } + } +@@ -809,11 +790,12 @@ // [eval] e3.a: conflicting values [a] and {c:a} (mismatched types list and struct): // ./in.cue:400:5 // ./in.cue:401:5 @@ -2291,7 +2256,7 @@ diff old new } } b: (_|_){ -@@ -820,11 +837,12 @@ +@@ -820,11 +802,12 @@ // [eval] e3.b: conflicting values [b] and {c:b} (mismatched types list and struct): // ./in.cue:403:5 // ./in.cue:404:5 @@ -2309,7 +2274,7 @@ diff old new } } } -@@ -833,41 +851,51 @@ +@@ -833,41 +816,51 @@ a: (_|_){ // [eval] 0: (_|_){ @@ -2392,7 +2357,7 @@ diff old new } } } -@@ -890,19 +918,14 @@ +@@ -890,19 +883,14 @@ y: (_|_){ // [eval] 0: (_|_){ @@ -2415,7 +2380,7 @@ diff old new } 1: (int){ 1 } } -@@ -912,19 +935,14 @@ +@@ -912,19 +900,14 @@ y: (_|_){ // [eval] 0: (_|_){ @@ -2438,7 +2403,46 @@ diff old new } 1: (int){ 1 } } -@@ -1028,9 +1046,7 @@ +@@ -977,7 +960,10 @@ + head: (int){ 3 } + tail: (struct){ + head: (int){ 4 } +- tail: (null){ null } ++ tail: ((null|struct)){ |((struct){ ++ head: (int){ int } ++ tail: (null){ null } ++ }, (null){ null }) } + } + } + } +@@ -991,7 +977,10 @@ + head: (int){ 2 } + tail: (struct){ + head: (int){ 3 } +- tail: (int){ 1 } ++ tail: ((int|struct)){ |((struct){ ++ head: (int){ int } ++ tail: (int){ 1 } ++ }, (int){ 1 }) } + } + } + } +@@ -1005,8 +994,12 @@ + head: (int){ 2 } + tail: (struct){ |((struct){ + head: (int){ 3 } +- tail: (struct){ +- } ++ tail: (struct){ |((struct){ ++ head: (int){ int } ++ tail: (struct){ ++ } ++ }, (struct){ ++ }) } + }, (struct){ + head: (int){ 3 } + }) } +@@ -1028,9 +1021,7 @@ // [structural cycle] f: (_|_){ // [structural cycle] @@ -2449,7 +2453,7 @@ diff old new } g: (_|_){ // [structural cycle] -@@ -1051,10 +1067,7 @@ +@@ -1051,10 +1042,7 @@ x: (_){ _ } y: (_){ _ } } @@ -2461,7 +2465,7 @@ diff old new } } t2: (struct){ -@@ -1067,10 +1080,7 @@ +@@ -1067,10 +1055,7 @@ x: (_){ _ } y: (_){ _ } } @@ -2473,7 +2477,7 @@ diff old new } } t3: (struct){ -@@ -1085,16 +1095,8 @@ +@@ -1085,16 +1070,8 @@ y: (_){ _ } z: (_){ _ } } @@ -2492,7 +2496,7 @@ diff old new } } t4: (struct){ -@@ -1110,51 +1112,11 @@ +@@ -1110,51 +1087,11 @@ y: (_){ _ } z: (_){ _ } } @@ -2549,7 +2553,7 @@ diff old new } } } -@@ -1177,19 +1139,19 @@ +@@ -1177,19 +1114,19 @@ } } n4: (struct){ @@ -2586,6 +2590,7 @@ Reducer: r: a a: c: r -- diff/todo/p2 -- +b7.b.0.0.0: cycle too deep before detection. TODO(share): fix the following bug once we have structure sharing. d3.config: should structural cycle be reported? p3.a.a: field not allowed incorrect? diff --git a/cue/testdata/eval/v0.7.txtar b/cue/testdata/eval/v0.7.txtar index 28996e6246b..523249329b7 100644 --- a/cue/testdata/eval/v0.7.txtar +++ b/cue/testdata/eval/v0.7.txtar @@ -683,14 +683,14 @@ Result: } } -- out/evalalpha/stats -- -Leaks: 288 +Leaks: 278 Freed: 16 Reused: 16 -Allocs: 288 +Allocs: 278 Retain: 0 -Unifications: 258 -Conjuncts: 1989 +Unifications: 248 +Conjuncts: 1883 Disjuncts: 44 -- out/evalalpha -- Errors: @@ -970,8 +970,6 @@ Result: d: (struct){ b: (int){ 2 } } - a: (int){ 1 } - b: (int){ 2 } } } z: (struct){ @@ -992,8 +990,6 @@ Result: d: (struct){ b: (int){ 2 } } - a: (int){ 1 } - b: (int){ 2 } } } y: (struct){ @@ -1014,8 +1010,6 @@ Result: d: (struct){ b: (int){ 2 } } - a: (int){ 1 } - b: (int){ 2 } } } y: (struct){ @@ -1058,8 +1052,6 @@ Result: d: (struct){ b: (int){ 2 } } - a: (int){ 1 } - b: (int){ 2 } } } y: (struct){ @@ -1080,8 +1072,6 @@ Result: d: (struct){ b: (int){ 2 } } - a: (int){ 1 } - b: (int){ 2 } } } z: (struct){ @@ -1110,17 +1100,17 @@ diff old new -Reused: 273 -Allocs: 16 -Retain: 170 -+Leaks: 288 ++Leaks: 278 +Freed: 16 +Reused: 16 -+Allocs: 288 ++Allocs: 278 +Retain: 0 -Unifications: 269 -Conjuncts: 1619 -Disjuncts: 436 -+Unifications: 258 -+Conjuncts: 1989 ++Unifications: 248 ++Conjuncts: 1883 +Disjuncts: 44 -- diff/-out/evalalpha<==>+out/eval -- diff old new @@ -1262,7 +1252,7 @@ diff old new t0: (struct){ ok: (struct){ c: (struct){ -@@ -270,135 +245,116 @@ +@@ -270,135 +245,110 @@ } } } @@ -1425,8 +1415,6 @@ diff old new + d: (struct){ + b: (int){ 2 } + } -+ a: (int){ 1 } -+ b: (int){ 2 } + } + } + z: (struct){ @@ -1447,8 +1435,6 @@ diff old new + d: (struct){ + b: (int){ 2 } + } -+ a: (int){ 1 } -+ b: (int){ 2 } + } + } + y: (struct){ @@ -1469,8 +1455,6 @@ diff old new + d: (struct){ + b: (int){ 2 } + } -+ a: (int){ 1 } -+ b: (int){ 2 } + } + } + y: (struct){ @@ -1508,7 +1492,7 @@ diff old new } } p5: (struct){ -@@ -407,23 +363,20 @@ +@@ -407,23 +357,18 @@ d: (struct){ b: (int){ 2 } } @@ -1529,8 +1513,6 @@ diff old new - } - b: (int){ 2 } - a: (int){ 1 } -+ a: (int){ 1 } -+ b: (int){ 2 } + } + } + y: (struct){ @@ -1546,20 +1528,46 @@ diff old new } } check: (struct){ -@@ -445,10 +398,7 @@ - b: (int){ 2 } - } - y: (struct){ +@@ -432,23 +377,18 @@ + d: (struct){ + b: (int){ 2 } + } +- a: (int){ 1 } +- b: (int){ 2 } +- } +- } +- z: (struct){ +- d: (struct){ +- a: (int){ 1 } +- b: (int){ 2 } +- } +- a: (int){ 1 } +- b: (int){ 2 } +- } +- y: (struct){ - c: (struct){ - a: (int){ 1 } - b: (int){ 2 } - } ++ } ++ } ++ z: (struct){ ++ d: (struct){ ++ a: (int){ 1 } ++ b: (int){ 2 } ++ } ++ a: (int){ 1 } ++ b: (int){ 2 } ++ } ++ y: (struct){ + c: ~(mutual.t4.ok.check.z.d) } } } -- diff/explanation -- Bug fixes +-- diff/todo/p0 -- +mutual.t4.p1: dropped fields. See comment in cycle.go that references this bug. -- diff/todo/p2 -- Reordering / positions. -- out/compile -- diff --git a/cue/testdata/export/030.txtar b/cue/testdata/export/030.txtar index 19175dd92c7..434f91ea84c 100644 --- a/cue/testdata/export/030.txtar +++ b/cue/testdata/export/030.txtar @@ -51,6 +51,38 @@ eval: true #Bar: string } } +-- out/evalalpha/stats -- +Leaks: 15 +Freed: 3 +Reused: 3 +Allocs: 15 +Retain: 0 + +Unifications: 8 +Conjuncts: 42 +Disjuncts: 10 +-- diff/-out/evalalpha/stats<==>+out/eval/stats -- +diff old new +--- old ++++ new +@@ -1,9 +1,9 @@ +-Leaks: 0 +-Freed: 70 +-Reused: 59 +-Allocs: 11 +-Retain: 1 ++Leaks: 15 ++Freed: 3 ++Reused: 3 ++Allocs: 15 ++Retain: 0 + +-Unifications: 28 +-Conjuncts: 131 +-Disjuncts: 71 ++Unifications: 8 ++Conjuncts: 42 ++Disjuncts: 10 -- out/eval/stats -- Leaks: 0 Freed: 70 diff --git a/internal/core/adt/comprehension.go b/internal/core/adt/comprehension.go index 5cdd2ed61cd..e40e180e554 100644 --- a/internal/core/adt/comprehension.go +++ b/internal/core/adt/comprehension.go @@ -500,6 +500,9 @@ func (n *nodeContext) processComprehensionInner(d *envYield, state vertexStatus) } id := d.id + // TODO: should we treat comprehension values as optional? + // It seems so, but it causes some hangs. + // id.setOptional(nil) for _, env := range d.envs { if n.node.ArcType == ArcNotPresent { diff --git a/internal/core/adt/conjunct.go b/internal/core/adt/conjunct.go index 398b3837c0e..e56f66d13c2 100644 --- a/internal/core/adt/conjunct.go +++ b/internal/core/adt/conjunct.go @@ -191,6 +191,8 @@ func (n *nodeContext) scheduleConjunct(c Conjunct, id CloseInfo) { case *DisjunctionExpr: n.unshare() + id := id + id.setOptionalV3(n) // TODO(perf): reuse envDisjunct values so that we can also reuse the // disjunct slice. @@ -299,6 +301,11 @@ loop2: n.aStruct = s n.aStructID = ci } + ci := ci + if x.ArcType == ArcOptional { + ci.setOptionalV3(n) + } + fc := MakeConjunct(childEnv, x, ci) // fc.CloseInfo.cc = nil // TODO: should we add this? n.insertArc(x.Label, x.ArcType, fc, ci, true) @@ -328,6 +335,8 @@ loop2: n.scheduleTask(handleDynamic, childEnv, x, ci) case *BulkOptionalField: + ci := ci + ci.setOptionalV3(n) // All do not depend on each other, so can be added at once. n.scheduleTask(handlePatternConstraint, childEnv, x, ci) @@ -615,6 +624,9 @@ func (n *nodeContext) insertValueConjunct(env *Environment, v Value, id CloseInf case *Disjunction: // TODO(perf): reuse envDisjunct values so that we can also reuse the // disjunct slice. + id := id + id.setOptionalV3(n) + d := envDisjunct{ env: env, cloneID: id, diff --git a/internal/core/adt/context.go b/internal/core/adt/context.go index 5614e87bb57..84ade01191b 100644 --- a/internal/core/adt/context.go +++ b/internal/core/adt/context.go @@ -739,9 +739,13 @@ func (c *OpContext) evalState(v Expr, state combinedFlags) (result Value) { n := arc.state if c.isDevVersion() { n = arc.getState(c) - } - if n != nil { - c.ci, _ = n.markCycle(arc, nil, x, c.ci) + if n != nil { + c.ci, _ = n.detectCycleV3(arc, nil, x, c.ci) + } + } else { + if n != nil { + c.ci, _ = n.markCycle(arc, nil, x, c.ci) + } } c.ci.Inline = true diff --git a/internal/core/adt/cycle.go b/internal/core/adt/cycle.go index 1a5d46afaf7..aa864c31bd1 100644 --- a/internal/core/adt/cycle.go +++ b/internal/core/adt/cycle.go @@ -14,6 +14,285 @@ package adt +// TODO: +// - compiler support for detecting cross-pattern references. +// - handle propagation of cyclic references to root across disjunctions. + +// # Cycle detection algorithm V3 +// +// The cycle detection algorithm detects the following kind of cycles: +// +// - Structural cycles: cycles where a field, directly or indirectly, ends up +// referring to an ancestor node. For instance: +// +// a: b: a +// +// a: b: c +// c: a +// +// T: a?: T +// T: a: {} +// +// - Reference cycles: cycles where a field, directly or indirectly, end up +// referring to itself: +// a: a +// +// a: b +// b: a +// +// - Inline cycles: cycles within an expression, for instance: +// +// x: {y: x}.out +// +// Note that it is possible for the unification of two non-cyclic structs to be +// cyclic: +// +// y: { +// f: h: g +// g: _ +// } +// x: { +// f: _ +// g: f +// } +// +// Even though the above contains no cycles, the result of `x & y` is cyclic: +// +// f: h: g +// g: f +// +// Cycle detection is inherently a dynamic process. +// +// ## ALGORITHM OVERVIEW +// +// 1. Traversal with Path Tracking: +// • Perform a depth-first traversal of the CUE value graph. +// • Maintain a path (call stack) of ancestor nodes during traversal. +// For this purpose, we separately track the parent relation as well +// as marking nodes that are currently being processed. +// 2. Per-Conjunct Cycle Tracking: +// • For each conjunct in a node’s value (i.e., c1 & c2 & ... & cn), +// track cycles independently. +// • A node is considered non-cyclic if any of its conjuncts is +// non-cyclic. +// 3. Handling References: +// • When encountering a reference, check if it points to any node in the +// current path. +// • If yes, mark the conjunct as cyclic. +// • If no, add the referenced node to the path and continue traversal. +// 4. Handling Optional Constructs: +// • Conjuncts originating from optional fields, pattern constraints, and +// disjunctions are marked as optional. +// • Cycle tracking for optional conjuncts is identical to conjuncts for +// conjuncts not marked as optional up to the point a cycle is detected +// (i.e. all conjuncts are cyclic). +// • When a cycle is detected, the lists of referenced nodes are cleared +// for each conjuncts, which thereby are afforded one additional level +// of cycles. This allows for any optional paths to terminate. +// +// +// ## CALL STACK +// +// There are two key types of structural cycles: referencing an ancestor and +// repeated mixing in of cyclic types. We track these separately. +// +// The first kind is relatively easy to detect by simply checking if a resolved +// reference is a direct parent, or is a node that is currently under +// evaluation. +// +// For the second kind, we need to maintain a per-conjunct list of references. +// When a reference was previously resolved in a conjunct, we may have a cycle +// and will mark the conjunct as such. +// +// +// ## OPTIONAL PATHS +// +// Cyclic references for conjuncts that originate from an "optional" path, such +// as optional fields and pattern constraints, may not necessary be cyclic, as +// on a next iteration such conjuncts _may_ still terminate. +// +// To allow for this kind of eventuality, optional conjuncts are processed in +// two phases: +// +// - they behave as normal conjuncts up to the point a cycle is detected +// - afterwards, their reference history is cleared and they are afforded to +// proceed until the next cycle is detected. +// +// Note that this means we may allow processing to proceed deeper than strictly +// necessary in some cases. +// +// Note that we only allow this for references: for cycles with ancestor nodes +// we immediately terminate for optional fields. This simplifies the algorithm. +// But it is also correct: in such cases either the whole node is in an optional +// path, in which case reporting an error is benign (as they are allowed), or +// the node corresponds to a non-optional field, in which case a cycle can be +// expected to reproduce another non-optional cycle, which will be an error. +// +// ### Examples +// +// These are not cyclic: +// +// 1. The structure is cyclic, but he optional field needs to be "fed" to +// continue the cycle: +// +// a: b?: a // a: {} +// +// b: [string]: b // b: {} +// +// c: 1 | {d: c} // c: 1 +// +// 2. The structure is cyclic. Conjunct `x: a` keeps detecting cycles, but +// is fed with new structure up until x.b.c.b.c.b. After this, this +// (optional) conjunct is allowed to proceed until the next cycle, which +// not be reached, as the `b?` is not unified with a concrete value. +// So the result of `x` is `{b: c: b: c: b: c: {}}`. +// +// a: b?: c: a +// x: a +// x: b: c: b: c: b: {} +// +// These are cyclic: +// +// 3. Here the optional conjunct triggers a new cycle of itself, but also +// of a conjunct that turns `b` into a regular field. It is thus a self- +// feeding cycle. +// +// a: b?: a +// a: b: _ +// +// c: [string]: c +// c: b: _ +// +// 4. Here two optional conjuncts end up feeding each other, resulting in a +// cycle. +// +// a: c: a | int +// a: a | int +// +// y1: c?: c: y1 +// x1: y1 +// x1: c: y1 +// +// y2: [string]: b: y2 +// x2: y2 +// x2: b: y2 +// +// +// ## INLINE CYCLES +// +// The semantics for treating inline cycles can be derived by rewriting CUE of +// the form +// +// x: {...}.out +// +// as +// +// x: _x.out +// _x: {...} +// +// A key difference is that as such structs are not "rooted" (they have no path +// from the root of the configuration tree) and thus any error should be caught +// and evaluated before doing a lookup in such structs to be correct. For the +// purpose of this algorithm, this especially pertains to structural cycles. +// +// TODO: implement: current handling of inline still loosly based on old +// algorithm. +// +// ### Examples +// +// Expanding these out with the above rules should give the same results. +// +// Cyclic: +// +// 1. This is an example of mutual recursion, triggered by n >= 2. +// +// fibRec: { +// nn: int, +// out: (fib & {n: nn}).out +// } +// fib: { +// n: int +// if n >= 2 { out: (fibRec & {nn: n - 2}).out } +// if n < 2 { out: n } +// } +// fib2: fib & {n: 2} +// +// is equivalent to +// +// fibRec: { +// nn: int, +// out: _out.out +// _out: fib & {n: nn} +// } +// fib: { +// n: int +// if n >= 2 { +// out: _out.out +// _out: fibRec & {nn: n - 2} +// } +// if n < 2 { out: n } +// } +// fib2: fib & {n: 2} +// +// Non-cyclic: +// +// 2. This is not dissimilar to the previous example, but since additions are +// done on separate lines, each field is only visited once and no cycle is +// triggered. +// +// f: { in: number, out: in } +// k00: 0 +// k10: (f & {in: k00}).out +// k20: (f & {in: k10}).out +// k10: (f & {in: k20}).out +// +// which is equivalent to +// +// f: { in: number, out: in } +// k0: 0 +// k1: _k1.out +// k2: _k2.out +// k1: _k3.out +// _k1: f +// _k2: f +// _k3: f +// _k1: in: k0 +// _k2: in: k1 +// _k3: in: k2 +// +// and thus is non-cyclic. +// +// ## CORRECTNESS +// +// ### The algorithm will terminate +// +// First consider the algorithm without optional conjuncts. If a parent node is +// referenced, it will obviously be caught. The more interesting case is if a +// reference to a node is made which is later reintroduced. +// +// When a conjunct splits into multiple conjuncts, its entire cycle history is +// copied. This means that any cyclic conjunct will be marked as cyclic in +// perpetuity. Non-cyclic conjuncts will either remain non-cyclic or be turned +// into a cycle. A conjunct can only remain non-cyclic for a maximum of the +// number of nodes in a graph. For any structure to repeat, it must have a +// repeated reference. This means that eventually either all conjuncts will +// either terminate or become cyclic. +// +// Optional conjuncts do not materially alter this property. The only difference +// is that when a node-level cycle is detected, we continue processing of some +// conjuncts until this next cycle is reached. +// +// +// ## TODO +// +// - treatment of let fields +// - tighter termination for some mutual cycles in optional conjuncts. + +// DEPRECATED: V2 cycle detection. +// +// TODO(evalv3): remove these comments once we have fully moved to V3. +// + // Cycle detection: // // - Current algorithm does not allow for early non-cyclic conjunct detection. @@ -21,11 +300,6 @@ package adt // - Mark as cyclic if no evidence is found. // - Note that this also activates the same reference in other (parent) conjuncts. -// TODO: -// - get rid of nodeContext.{hasCycle|hasNonCycle}. -// - compiler support for detecting cross-pattern references. -// - handle propagation of cyclic references to root across disjunctions. - // CYCLE DETECTION ALGORITHM // // BACKGROUND @@ -209,9 +483,17 @@ package adt // Bob Carpenter, "The logic of typed feature structures." // Cambridge University Press, ISBN:0-521-41932-8 +// TODO: mark references as crossing optional boundaries, rather than +// approximating it during evaluation. + type CycleInfo struct { + // CycleType is used by the V3 cycle detection algorithm to track whether + // a cycle is detected and of which type. + CycleType CyclicType + // IsCyclic indicates whether this conjunct, or any of its ancestors, // had a violating cycle. + // TODO: make this a method and use CycleType == IsCyclic after V2 is removed. IsCyclic bool // Inline is used to detect expressions referencing themselves, for instance: @@ -259,6 +541,140 @@ type cyclicConjunct struct { arc *Vertex // cached Vertex } +// CycleType indicates the type of cycle detected. The CyclicType is associated +// with a conjunct and may only increase in value for child conjuncts. +type CyclicType uint8 + +const ( + NoCycle CyclicType = iota + + // like newStructure, but derived from a reference. If this is set, a cycle + // will move to maybeCyclic instead of isCyclic. + IsOptional + + // maybeCyclic is set if a cycle is detected within an optional field. + // + MaybeCyclic + + // IsCyclic marks that this conjunct has a structural cycle. + IsCyclic +) + +func (n *nodeContext) detectCycleV3(arc *Vertex, env *Environment, x Resolver, ci CloseInfo) (_ CloseInfo, skip bool) { + n.assertInitialized() + + // If we are pointing to a direct ancestor, and we are in an optional arc, + // we can immediately terminate, as a cycle error within an optional field + // is okay. If we are pointing to a direct ancestor in a non-optional arc, + // we also can terminate, as this is a structural cycle. + // TODO: use depth or check direct ancestry. + if n.hasAncestorV3(arc) { + if n.node.IsDynamic || ci.Inline { + n.reportCycleError() + return ci, true + } + + return n.markCyclicV3(arc, env, x, ci) + } + + // As long as a node-wide cycle has not yet been detected, we allow cycles + // in optional fields to proceed unchecked. + if n.hasNonCyclic && ci.CycleType == MaybeCyclic { + return ci, false + } + + for r := ci.Refs; r != nil; r = r.Next { + if equalDeref(r.Arc, arc) { + if n.node.IsDynamic || ci.Inline { + n.reportCycleError() + return ci, true + } + + if equalDeref(r.Node, n.node) { + // reference cycle + // TODO: in some cases we must continue to fully evaluate. + // Return false here to solve v0.7.txtar:mutual.t4.ok.p1 issue. + return ci, true + } + + // If there are still any non-cyclic conjuncts, and if this conjunct + // is optional, we allow this to continue one more cycle. + if ci.CycleType == IsOptional && n.hasNonCyclic { + ci.CycleType = MaybeCyclic + ci.Refs = nil + return ci, false + } + + return n.markCyclicV3(arc, env, x, ci) + } + } + + ci.Refs = &RefNode{ + Arc: deref(arc), + Ref: x, + Node: deref(n.node), + Next: ci.Refs, + Depth: n.depth, + } + + return ci, false +} + +// markCyclicV3 marks a conjunct as being cyclic. Also, it postpones processing +// the conjunct in the absence of evidence of a non-cyclic conjunct. +func (n *nodeContext) markCyclicV3(arc *Vertex, env *Environment, x Resolver, ci CloseInfo) (CloseInfo, bool) { + ci.CycleType = IsCyclic + ci.IsCyclic = true + + n.hasCycle = true + + if !n.hasNonCycle && env != nil { + // TODO: investigate if we can get rid of cyclicConjuncts in the new + // evaluator. + v := Conjunct{env, x, ci} + n.node.cc.incDependent(n.ctx, DEFER, nil) + n.cyclicConjuncts = append(n.cyclicConjuncts, cyclicConjunct{v, arc}) + return ci, true + } + return ci, false +} + +// hasAncestorV3 checks whether a node is currently being processed. The code +// still assumes that is includes any node that is currently being processed. +func (n *nodeContext) hasAncestorV3(arc *Vertex) bool { + // TODO: consider removing this. For now we still need it, but we could + // possibly remove it after we strictly separate processing lookups versus + // full evaluation. + if arc.status == evaluatingArcs { + return true + } + + // TODO: insert test conditions for Bloom filter that guarantee that all + // parent nodes have been marked as "hot", in which case we can avoid this + // traversal. + // if n.meets(allAncestorsProcessed) { + // return false + // } + + for p := n.node.Parent; p != nil; p = p.Parent { + // TODO(perf): deref arc only once. + if equalDeref(p, arc) { + return true + } + } + return false +} + +// setOptionalV3 marks a conjunct as being optional. The nodeContext is +// currently unused, but allows for checks to be added and to add logging during +// debugging. +func (c *CloseInfo) setOptionalV3(n *nodeContext) { + _ = n // See comment. + if c.CycleType == NoCycle { + c.CycleType = IsOptional + } +} + // markCycle checks whether the reference x is cyclic. There are two cases: // 1. it was previously used in this conjunct, and // 2. it directly references a parent node. diff --git a/internal/core/adt/eval.go b/internal/core/adt/eval.go index f0218a80ead..e6bf9348c09 100644 --- a/internal/core/adt/eval.go +++ b/internal/core/adt/eval.go @@ -1090,9 +1090,10 @@ type nodeContextState struct { // State info - hasTop bool - hasCycle bool // has conjunct with structural cycle - hasNonCycle bool // has conjunct without structural cycle + hasTop bool + hasCycle bool // has conjunct with structural cycle + hasNonCycle bool // has material conjuncts without structural cycle + hasNonCyclic bool // has non-cyclic conjuncts at start of field processing isShared bool // set if we are currently structure sharing. noSharing bool // set if structure sharing is not allowed diff --git a/internal/core/adt/eval_test.go b/internal/core/adt/eval_test.go index 40a009b294f..5d79620f776 100644 --- a/internal/core/adt/eval_test.go +++ b/internal/core/adt/eval_test.go @@ -75,15 +75,15 @@ var needFix = map[string]string{ // counter errors. // TODO: These counters should all go to zero. var skipDebugDepErrors = map[string]int{ - "benchmarks/issue1684": 8, - "benchmarks/listdedup": 1, + "benchmarks/issue1684": 16, "builtins/default": 1, "comprehensions/pushdown": 3, "cycle/chain": 4, "cycle/compbottom2": 4, - "cycle/disjunction": 7, + "cycle/comprehension": 1, + "cycle/disjunction": 4, "cycle/issue990": 1, - "cycle/structural": 18, + "cycle/structural": 17, "disjunctions/edge": 1, "disjunctions/elimination": 8, "disjunctions/embed": 6, diff --git a/internal/core/adt/fields.go b/internal/core/adt/fields.go index 36e76da68a0..ebc7d0731f2 100644 --- a/internal/core/adt/fields.go +++ b/internal/core/adt/fields.go @@ -756,6 +756,11 @@ func (cc *closeContext) insertConjunct(ctx *OpContext, key *closeContext, c Conj return } + switch id.CycleType { + case NoCycle, IsOptional: + n.hasNonCyclic = true + } + if key.src.isInProgress() { c.CloseInfo.cc = nil id.cc = arc diff --git a/internal/core/adt/tasks.go b/internal/core/adt/tasks.go index bb11e0dc0d9..85433f6907a 100644 --- a/internal/core/adt/tasks.go +++ b/internal/core/adt/tasks.go @@ -110,7 +110,7 @@ func processResolver(ctx *OpContext, t *task, mode runMode) { // A reference that points to itself indicates equality. In that case // we are done computing and we can return the arc as is. - ci, skip := t.node.markCycle(d, t.env, r, t.id) + ci, skip := t.node.detectCycleV3(d, t.env, r, t.id) if skip { return } @@ -119,6 +119,18 @@ func processResolver(ctx *OpContext, t *task, mode runMode) { return } + // TODO: consider moving this to within if arc.nonRooted below. + if b, ok := d.BaseValue.(*Bottom); ok && b.Code == StructuralCycleError { + // TODO: ensure better positioning information. + ctx.AddBottom(b) + return + } + + if arc.nonRooted { + if arc.status == finalized { + ci.Refs = nil + } + } c := MakeConjunct(t.env, t.x, ci) t.node.scheduleVertexConjuncts(c, arc, ci) } @@ -217,8 +229,10 @@ func processListLit(c *OpContext, t *task, mode runMode) { label, err := MakeLabel(x.Source(), index, IntLabel) n.addErr(err) index++ - c := MakeConjunct(e, x.Value, t.id) - n.insertArc(label, ArcMember, c, t.id, true) + id := t.id + // id.setOptional(t.node) + c := MakeConjunct(e, x.Value, id) + n.insertArc(label, ArcMember, c, id, true) }) hasComprehension = true if err != nil { @@ -237,7 +251,10 @@ func processListLit(c *OpContext, t *task, mode runMode) { elem = &Top{} } - c := MakeConjunct(t.env, elem, t.id) + id := t.id + id.setOptionalV3(t.node) + + c := MakeConjunct(t.env, elem, id) pat := &BoundValue{ Op: GreaterEqualOp, Value: n.ctx.NewInt64(index, x),