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),