Skip to content

Commit db3a7c9

Browse files
committedDec 24, 2024·
internal/core/adt: fix self reference issue
Evaluating comprehension sources was done using "yield" mode. This caused sets to be evaluated out of band, causing structs to be closed before fields can be added. We now, instead, rely on the recursive call-by-need behavior in combination with the freezing mechanism. In the future we plan to move to a scheduler that relies fully on either call by need or scheduling, but for now this will have to do. comprehension.txtar: fixes a P0 bug freeze.txtar: fixes the target issue. It would also fix the comprehension.t1.ok tests if we use "attemptOnly" instead of "finalize". But this breaks issue 3616. It is either way unclear what the correct behavior should be and V2 also errors here, so it is not the worst. This change would also fix a possible issue with propagating up an incomplete error. Also here the behavior is the same as V2, so no problem for now. adt.txtar: the changes are benign and due to reordering. Fixes #3178 Signed-off-by: Marcel van Lohuizen <[email protected]> Change-Id: I94e49767d59a5d0c13f9ebb2ce8882f8c7a68051 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1206318 Reviewed-by: Daniel Martí <[email protected]> Unity-Result: CUE porcuepine <[email protected]> TryBot-Result: CUEcueckoo <[email protected]>
1 parent 0b6a4e8 commit db3a7c9

File tree

4 files changed

+286
-158
lines changed

4 files changed

+286
-158
lines changed
 

‎cue/testdata/cycle/comprehension.txtar

+41-120
Original file line numberDiff line numberDiff line change
@@ -310,23 +310,19 @@ issue2367: {
310310
}
311311

312312
-- out/evalalpha/stats --
313-
Leaks: 778
313+
Leaks: 782
314314
Freed: 63
315315
Reused: 63
316-
Allocs: 778
316+
Allocs: 782
317317
Retain: 0
318318

319-
Unifications: 498
320-
Conjuncts: 2977
319+
Unifications: 502
320+
Conjuncts: 3031
321321
Disjuncts: 196
322322
-- out/evalalpha --
323323
Errors:
324324
selfReferential.insertionError.A: adding field foo3 not allowed as field set was already referenced:
325325
./in.cue:117:14
326-
siblingInsertion.t2.p1.deployment: adding field logging not allowed as field set was already referenced:
327-
./in.cue:277:21
328-
siblingInsertion.t2.p2.deployment: adding field logging not allowed as field set was already referenced:
329-
./in.cue:292:21
330326

331327
Result:
332328
(_|_){
@@ -651,8 +647,7 @@ Result:
651647
}
652648
}
653649
}
654-
siblingInsertion: (_|_){
655-
// [eval]
650+
siblingInsertion: (struct){
656651
t1: (struct){
657652
p1: (struct){
658653
D: (struct){
@@ -685,28 +680,35 @@ Result:
685680
}
686681
}
687682
}
688-
t2: (_|_){
689-
// [eval]
690-
p1: (_|_){
691-
// [eval] siblingInsertion.t2.p1.deployment: adding field logging not allowed as field set was already referenced:
692-
// ./in.cue:277:21
683+
t2: (struct){
684+
p1: (struct){
693685
D: (struct){
694686
logging: (_){ _ }
695687
}
696-
deployment: (_|_){
697-
// [eval] siblingInsertion.t2.p1.deployment: adding field logging not allowed as field set was already referenced:
698-
// ./in.cue:277:21
688+
deployment: (struct){
689+
logging: (struct){
690+
env2: (struct){
691+
ENV: (string){ "True" }
692+
}
693+
env: (struct){
694+
ENV: (string){ "True" }
695+
}
696+
}
699697
}
700698
}
701-
p2: (_|_){
702-
// [eval] siblingInsertion.t2.p2.deployment: adding field logging not allowed as field set was already referenced:
703-
// ./in.cue:292:21
699+
p2: (struct){
704700
D: (struct){
705701
logging: (_){ _ }
706702
}
707-
deployment: (_|_){
708-
// [eval] siblingInsertion.t2.p2.deployment: adding field logging not allowed as field set was already referenced:
709-
// ./in.cue:292:21
703+
deployment: (struct){
704+
logging: (struct){
705+
env2: (struct){
706+
ENV: (string){ "True" }
707+
}
708+
env: (struct){
709+
ENV: (string){ "True" }
710+
}
711+
}
710712
}
711713
}
712714
}
@@ -719,19 +721,15 @@ Result:
719721
diff old new
720722
--- old
721723
+++ new
722-
@@ -1,5 +1,10 @@
724+
@@ -1,5 +1,6 @@
723725
Errors:
724726
-selfReferential.insertionError.A: field foo3 not allowed by earlier comprehension or reference cycle
725727
+selfReferential.insertionError.A: adding field foo3 not allowed as field set was already referenced:
726728
+ ./in.cue:117:14
727-
+siblingInsertion.t2.p1.deployment: adding field logging not allowed as field set was already referenced:
728-
+ ./in.cue:277:21
729-
+siblingInsertion.t2.p2.deployment: adding field logging not allowed as field set was already referenced:
730-
+ ./in.cue:292:21
731729

732730
Result:
733731
(_|_){
734-
@@ -19,7 +24,9 @@
732+
@@ -19,7 +20,9 @@
735733
B: (struct){
736734
a: (struct){
737735
parent: (string){ "" }
@@ -742,7 +740,7 @@ diff old new
742740
}
743741
}
744742
}
745-
@@ -52,17 +59,12 @@
743+
@@ -52,17 +55,12 @@
746744
}
747745
}
748746
_e: (#struct){
@@ -766,7 +764,7 @@ diff old new
766764
}
767765
}
768766
e: (#struct){
769-
@@ -76,12 +78,7 @@
767+
@@ -76,12 +74,7 @@
770768
}
771769
f2: (#struct){
772770
a: (#struct){
@@ -780,7 +778,7 @@ diff old new
780778
}
781779
}
782780
}
783-
@@ -127,9 +124,9 @@
781+
@@ -127,9 +120,9 @@
784782
insertionError: (_|_){
785783
// [eval]
786784
A: (_|_){
@@ -792,7 +790,7 @@ diff old new
792790
}
793791
}
794792
acrossOr: (struct){
795-
@@ -254,27 +251,31 @@
793+
@@ -254,27 +247,31 @@
796794
issue1881: (struct){
797795
p1: (struct){
798796
o: (#struct){
@@ -845,7 +843,7 @@ diff old new
845843
}) }
846844
}
847845
p2: (struct){
848-
@@ -290,7 +291,7 @@
846+
@@ -290,7 +287,7 @@
849847
}
850848
}
851849
o: (#struct){
@@ -854,90 +852,15 @@ diff old new
854852
output: (#struct){
855853
reject: (string){ "ok" }
856854
}
857-
@@ -320,15 +321,16 @@
855+
@@ -320,7 +317,7 @@
858856
resource: (string){ string }
859857
}) }
860858
o: (#struct){
861859
- retry: (#struct){
862-
- output: (#struct){
863-
- reject: (string){ "ok" }
864-
- }
865-
- }
866-
- }
867-
- }
868-
- }
869-
- siblingInsertion: (struct){
870860
+ retry: (struct){
871-
+ output: (#struct){
872-
+ reject: (string){ "ok" }
873-
+ }
874-
+ }
875-
+ }
876-
+ }
877-
+ }
878-
+ siblingInsertion: (_|_){
879-
+ // [eval]
880-
t1: (struct){
881-
p1: (struct){
882-
D: (struct){
883-
@@ -361,35 +363,28 @@
884-
}
885-
}
886-
}
887-
- t2: (struct){
888-
- p1: (struct){
889-
- D: (struct){
890-
- logging: (_){ _ }
891-
- }
892-
- deployment: (struct){
893-
- logging: (struct){
894-
- env2: (struct){
895-
- ENV: (string){ "True" }
896-
- }
897-
- env: (struct){
898-
- ENV: (string){ "True" }
899-
- }
900-
- }
901-
- }
902-
- }
903-
- p2: (struct){
904-
- D: (struct){
905-
- logging: (_){ _ }
906-
- }
907-
- deployment: (struct){
908-
- logging: (struct){
909-
- env2: (struct){
910-
- ENV: (string){ "True" }
911-
- }
912-
- env: (struct){
913-
- ENV: (string){ "True" }
914-
- }
915-
- }
916-
+ t2: (_|_){
917-
+ // [eval]
918-
+ p1: (_|_){
919-
+ // [eval] siblingInsertion.t2.p1.deployment: adding field logging not allowed as field set was already referenced:
920-
+ // ./in.cue:277:21
921-
+ D: (struct){
922-
+ logging: (_){ _ }
923-
+ }
924-
+ deployment: (_|_){
925-
+ // [eval] siblingInsertion.t2.p1.deployment: adding field logging not allowed as field set was already referenced:
926-
+ // ./in.cue:277:21
927-
+ }
928-
+ }
929-
+ p2: (_|_){
930-
+ // [eval] siblingInsertion.t2.p2.deployment: adding field logging not allowed as field set was already referenced:
931-
+ // ./in.cue:292:21
932-
+ D: (struct){
933-
+ logging: (_){ _ }
934-
+ }
935-
+ deployment: (_|_){
936-
+ // [eval] siblingInsertion.t2.p2.deployment: adding field logging not allowed as field set was already referenced:
937-
+ // ./in.cue:292:21
938-
}
939-
}
940-
}
861+
output: (#struct){
862+
reject: (string){ "ok" }
863+
}
941864
-- diff/-out/evalalpha/stats<==>+out/eval/stats --
942865
diff old new
943866
--- old
@@ -948,17 +871,17 @@ diff old new
948871
-Reused: 1260
949872
-Allocs: 60
950873
-Retain: 145
951-
+Leaks: 778
874+
+Leaks: 782
952875
+Freed: 63
953876
+Reused: 63
954-
+Allocs: 778
877+
+Allocs: 782
955878
+Retain: 0
956879

957880
-Unifications: 832
958881
-Conjuncts: 2525
959882
-Disjuncts: 1404
960-
+Unifications: 498
961-
+Conjuncts: 2977
883+
+Unifications: 502
884+
+Conjuncts: 3031
962885
+Disjuncts: 196
963886
-- out/eval/stats --
964887
Leaks: 50
@@ -972,8 +895,6 @@ Conjuncts: 2525
972895
Disjuncts: 1404
973896
-- diff/explanation --
974897
B.a.children: now correctly marked as incomplete
975-
-- diff/todo/p0 --
976-
siblingInsertion.t2: errors
977898
-- diff/todo/p2 --
978899
issue1881.#AllOutputs.retry: additional entry.
979900
Might be error in practice and okay.

‎cue/testdata/cycle/freeze.txtar

+8-36
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,6 @@ comprehension.t3.err.a: adding field xq not allowed as field set was already ref
158158
./comprehension.cue:82:13
159159
comprehension.moreSpecific.err.a.x: adding field z not allowed as field set was already referenced:
160160
./comprehension.cue:114:9
161-
issue3178.output: adding field Value not allowed as field set was already referenced:
162-
./issue3178.cue:9:23
163161

164162
Result:
165163
(_|_){
@@ -295,23 +293,20 @@ Result:
295293
}
296294
}
297295
}
298-
issue3178: (_|_){
299-
// [eval] issue3178.output: adding field Value not allowed as field set was already referenced:
300-
// ./issue3178.cue:9:23
296+
issue3178: (struct){
301297
input: (#list){
302298
0: (string){ "Value" }
303299
}
304-
output: (_|_){
305-
// [eval] issue3178.output: adding field Value not allowed as field set was already referenced:
306-
// ./issue3178.cue:9:23
300+
output: (struct){
301+
Value: (bool){ true }
307302
}
308303
}
309304
}
310305
-- diff/-out/evalalpha<==>+out/eval --
311306
diff old new
312307
--- old
313308
+++ new
314-
@@ -1,11 +1,12 @@
309+
@@ -1,11 +1,10 @@
315310
Errors:
316311
-comprehension.moreSpecific.err.a: field z not allowed by earlier comprehension or reference cycle
317312
-comprehension.t1.ok.p0.x: field z not allowed by earlier comprehension or reference cycle
@@ -326,12 +321,10 @@ diff old new
326321
+ ./comprehension.cue:82:13
327322
+comprehension.moreSpecific.err.a.x: adding field z not allowed as field set was already referenced:
328323
+ ./comprehension.cue:114:9
329-
+issue3178.output: adding field Value not allowed as field set was already referenced:
330-
+ ./issue3178.cue:9:23
331324

332325
Result:
333326
(_|_){
334-
@@ -12,73 +13,65 @@
327+
@@ -12,73 +11,65 @@
335328
// [eval]
336329
comprehension: (_|_){
337330
// [eval]
@@ -455,7 +448,7 @@ diff old new
455448
}
456449
}
457450
}
458-
@@ -89,9 +82,9 @@
451+
@@ -89,9 +80,9 @@
459452
err: (_|_){
460453
// [eval]
461454
a: (_|_){
@@ -468,7 +461,7 @@ diff old new
468461
}
469462
}
470463
}
471-
@@ -99,16 +92,14 @@
464+
@@ -99,16 +90,14 @@
472465
// [eval]
473466
err: (_|_){
474467
// [eval]
@@ -491,7 +484,7 @@ diff old new
491484
}
492485
}
493486
}
494-
@@ -132,10 +123,10 @@
487+
@@ -132,10 +121,10 @@
495488
err: (_|_){
496489
// [eval]
497490
a: (_|_){
@@ -504,27 +497,6 @@ diff old new
504497
}
505498
}
506499
}
507-
@@ -151,12 +142,15 @@
508-
}
509-
}
510-
}
511-
- issue3178: (struct){
512-
+ issue3178: (_|_){
513-
+ // [eval] issue3178.output: adding field Value not allowed as field set was already referenced:
514-
+ // ./issue3178.cue:9:23
515-
input: (#list){
516-
0: (string){ "Value" }
517-
}
518-
- output: (struct){
519-
- Value: (bool){ true }
520-
+ output: (_|_){
521-
+ // [eval] issue3178.output: adding field Value not allowed as field set was already referenced:
522-
+ // ./issue3178.cue:9:23
523-
}
524-
}
525-
}
526-
-- diff/todo/p3 --
527-
Error path location could improve.
528500
-- diff/explanation --
529501
v0.7 fixes bugs in v0.6:
530502
- t1 should be incomplete error, as it is fixable.

‎internal/core/adt/expr.go

+19-2
Original file line numberDiff line numberDiff line change
@@ -2026,7 +2026,13 @@ func (c *OpContext) forSource(x Expr) *Vertex {
20262026

20272027
node, ok := v.(*Vertex)
20282028
if ok && c.isDevVersion() {
2029-
node.unify(c, state.conditions(), yield)
2029+
// We do not request to "yield" here, but rather rely on the
2030+
// call-by-need behavior in combination with the freezing mechanism.
2031+
// TODO: this seems a bit fragile. At some point we need to make this
2032+
// more robust by moving to a pure call-by-need mechanism, for instance.
2033+
// TODO: using attemptOnly here will remove the cyclic reference error
2034+
// of comprehension.t1.ok (which also errors in V2),
2035+
node.unify(c, state.conditions(), finalize)
20302036
}
20312037

20322038
v, ok = c.getDefault(v)
@@ -2064,7 +2070,7 @@ func (c *OpContext) forSource(x Expr) *Vertex {
20642070
}
20652071

20662072
default:
2067-
if kind := v.Kind(); kind&StructKind != 0 {
2073+
if kind := v.Kind(); kind&(StructKind|ListKind) != 0 {
20682074
c.addErrf(IncompleteError, pos(x),
20692075
"cannot range over %s (incomplete type %s)", x, kind)
20702076
return emptyNode
@@ -2076,6 +2082,17 @@ func (c *OpContext) forSource(x Expr) *Vertex {
20762082
return emptyNode
20772083
}
20782084
}
2085+
if c.isDevVersion() {
2086+
kind := v.Kind()
2087+
// At this point it is possible that the Vertex represents an incomplete
2088+
// struct or list, which is the case if it may be struct or list, but
2089+
// is also at least some other type, such as is the case with top.
2090+
if kind&(StructKind|ListKind) != 0 && kind != StructKind && kind != ListKind {
2091+
c.addErrf(IncompleteError, pos(x),
2092+
"cannot range over %s (incomplete type %s)", x, kind)
2093+
return emptyNode
2094+
}
2095+
}
20792096

20802097
return node
20812098
}

‎internal/core/export/testdata/main/adt.txtar

+218
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,107 @@ errorListDef: {
253253
}
254254
-- diff/doc/explanation --
255255
Reordering.
256+
-- out/doc-v3 --
257+
[]
258+
[p1]
259+
[d1]
260+
[d1 foobar]
261+
[bar]
262+
[d2]
263+
[d2 foobar]
264+
[d2 foobar name]
265+
[d2 foobar foo]
266+
[a]
267+
- Issue #1910
268+
269+
[comp]
270+
[comp bar]
271+
[bytes]
272+
[c1]
273+
[s1]
274+
[l1]
275+
[l1 0]
276+
[l2]
277+
[l3]
278+
[l4]
279+
[l4 0]
280+
[l4 1]
281+
[l5]
282+
[l5 #foo]
283+
[l5 0]
284+
[l5 1]
285+
[#foo]
286+
[l6]
287+
[l6 #foo]
288+
[l6 0]
289+
[l6 1]
290+
[n1]
291+
[n10]
292+
[t]
293+
- t is true
294+
295+
[e1]
296+
[e2]
297+
[e3]
298+
[e4]
299+
[e5]
300+
[e6]
301+
[e7]
302+
[e8]
303+
[m1]
304+
[m1 foo]
305+
- foo is an optional field
306+
307+
[m1 bar]
308+
- bar is a field
309+
310+
[m1 baz]
311+
- baz is a required field.
312+
313+
[x]
314+
[y1]
315+
[y1 src]
316+
[y1 src 0]
317+
[y1 src 1]
318+
[y1 src 2]
319+
[y1 x]
320+
[y1 x 0]
321+
[y1 x 1]
322+
[y1 x 2]
323+
[y1 foo0]
324+
[y1 foo1]
325+
[y1 foo2]
326+
[y1 bar1]
327+
[y1 bar2]
328+
[preserveKeyFieldInComprehension]
329+
[errorStructDef]
330+
[errorStructDef a]
331+
[errorStructDef b]
332+
[errorStructDef #Def]
333+
[errorList]
334+
[errorList 0]
335+
[errorList 1]
336+
[errorListDef]
337+
[errorListDef #Def]
338+
[errorListDef 0]
339+
[errorListDef 1]
340+
-- diff/-out/doc-v3<==>+out/doc --
341+
diff old new
342+
--- old
343+
+++ new
344+
@@ -65,10 +65,10 @@
345+
[y1 x 1]
346+
[y1 x 2]
347+
[y1 foo0]
348+
-[y1 bar1]
349+
-[y1 bar2]
350+
[y1 foo1]
351+
[y1 foo2]
352+
+[y1 bar1]
353+
+[y1 bar2]
354+
[preserveKeyFieldInComprehension]
355+
[errorStructDef]
356+
[errorStructDef a]
256357
-- out/doc --
257358
[]
258359
[p1]
@@ -339,6 +440,123 @@ Reordering.
339440
[errorListDef 1]
340441
-- diff/value/todo/p3 --
341442
Error message change.
443+
-- out/value-v3 --
444+
== Simplified
445+
_|_ // e3: index out of range [2] with length 2
446+
== Raw
447+
_|_ // e3: index out of range [2] with length 2
448+
== Final
449+
_|_ // e3: index out of range [2] with length 2
450+
== All
451+
{
452+
@foo(bar)
453+
p1: {}
454+
d1: {
455+
foobar: int
456+
}
457+
bar: "bar"
458+
d2: {
459+
foobar: {
460+
name: "xx"
461+
foo: "xx"
462+
}
463+
}
464+
465+
// Issue #1910
466+
a: _
467+
comp: {
468+
for k, v in [0]
469+
let w = v {
470+
"\(a)": w
471+
"bar": w
472+
}
473+
}
474+
bytes: '\xeb \x1a\xf5\xaa\xf0\xd6\x06)'
475+
c1: true
476+
s1: """
477+
multi
478+
bar
479+
line
480+
"""
481+
l1: [3, ...int]
482+
l2: [...int]
483+
l3: []
484+
l4: [1, 2]
485+
l5: {
486+
#foo: int
487+
[1, 3]
488+
}
489+
#foo: int
490+
l6: {
491+
#foo: int
492+
[1, 3]
493+
}
494+
n1: 1.0
495+
n10: 10
496+
497+
// t is true
498+
t: true
499+
e1: <1.0
500+
e2: >1.0 & <10
501+
e3: _|_ // e3: index out of range [2] with length 2
502+
e4: _|_ // e4: index 3 out of range
503+
e5: _|_ // e3: index out of range [2] with length 2
504+
e6: false
505+
e7: true
506+
e8?: true
507+
m1: {
508+
// foo is an optional field
509+
foo?: 3
510+
511+
// bar is a field
512+
bar: 4
513+
514+
// baz is a required field.
515+
baz!: 5
516+
}
517+
y1: {
518+
src: [1, 2, 3]
519+
foo0: 1
520+
foo1: 2
521+
foo2: 3
522+
bar1: 2
523+
x: [1, 2, 3]
524+
bar2: 3
525+
}
526+
preserveKeyFieldInComprehension: 1
527+
errorStructDef: {
528+
a: 1
529+
b: _|_ // errorStructDef.b: conflicting values 2 and 1
530+
#Def: 1
531+
}
532+
errorList: [1, _|_]
533+
x: int
534+
errorListDef: {
535+
#Def: 1
536+
[1, _|_]
537+
}
538+
}
539+
== Eval
540+
_|_ // e3: index out of range [2] with length 2
541+
-- diff/-out/value-v3<==>+out/value --
542+
diff old new
543+
--- old
544+
+++ new
545+
@@ -74,11 +74,11 @@
546+
y1: {
547+
src: [1, 2, 3]
548+
foo0: 1
549+
- bar1: 2
550+
- bar2: 3
551+
foo1: 2
552+
- x: [1, 2, 3]
553+
foo2: 3
554+
+ bar1: 2
555+
+ x: [1, 2, 3]
556+
+ bar2: 3
557+
}
558+
preserveKeyFieldInComprehension: 1
559+
errorStructDef: {
342560
-- out/value --
343561
== Simplified
344562
_|_ // e3: index out of range [2] with length 2

0 commit comments

Comments
 (0)
Please sign in to comment.