Skip to content

Commit

Permalink
internal/core/eval: fix cycle bug for comprehensions
Browse files Browse the repository at this point in the history
don't evaluate comprehensions in active cycle

Fixes #509

Change-Id: I50b1cbda2da548fc4b1a2c5bb91e83f542a0f9ab
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7482
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
  • Loading branch information
mpvl committed Oct 22, 2020
1 parent 1888d65 commit aee9955
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 42 deletions.
179 changes: 138 additions & 41 deletions cue/testdata/cycle/structural.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,43 @@ b10: {
})
}

// Issue #509 -- with comprehension
b11: {
#list: {
tail: #list | *null
if tail != null {
}
}
}

// Issue #509 -- with comprehension
b12: {
#list: {
V=value: int
T=tail: #list|*null
if T != null {
sum: V + T.sum
}
if T == null {
sum: V
}
}

list1: #list
list1: {
value: 1,
tail: {
value: 2
tail: {
value: 3
tail: {
value: 4
}
}
}
}
}

c1: {
a: {
b: {}
Expand Down Expand Up @@ -263,43 +300,43 @@ e1.b.c: structural cycle
e2.a.c: structural cycle
e2.b.c: structural cycle
e3.a: conflicting values [a] and {c:a} (mismatched types list and struct):
./in.cue:141:8
./in.cue:142:8
./in.cue:178:8
./in.cue:179:8
e3.a.0: conflicting values [a] and {c:a} (mismatched types list and struct):
./in.cue:141:8
./in.cue:142:8
./in.cue:178:8
./in.cue:179:8
e3.a.0: structural cycle
e3.a.c: conflicting values [a] and {c:a} (mismatched types list and struct):
./in.cue:141:8
./in.cue:142:8
./in.cue:178:8
./in.cue:179:8
e3.a.c: structural cycle
e3.b: conflicting values [b] and {c:b} (mismatched types list and struct):
./in.cue:144:8
./in.cue:145:8
./in.cue:181:8
./in.cue:182:8
e3.b.0: conflicting values [b] and {c:b} (mismatched types list and struct):
./in.cue:144:8
./in.cue:145:8
./in.cue:181:8
./in.cue:182:8
e3.b.0: structural cycle
e3.b.c: conflicting values [b] and {c:b} (mismatched types list and struct):
./in.cue:144:8
./in.cue:145:8
./in.cue:181:8
./in.cue:182:8
e3.b.c: structural cycle
e4.a.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
./in.cue:149:13
./in.cue:150:9
./in.cue:186:13
./in.cue:187:9
e4.b.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
./in.cue:152:9
./in.cue:153:13
./in.cue:189:9
./in.cue:190:13
z1.z.f.h.h: structural cycle
z1.z.g.h: structural cycle
b4.x.y.0: structural cycle:
./in.cue:41:8
d2.a.b.c.d.t: structural cycle:
./in.cue:106:8
./in.cue:143:8
d2.r: structural cycle:
./in.cue:106:8
./in.cue:143:8
0: structural cycle:
./in.cue:116:19
./in.cue:153:19

Result:
(_|_){
Expand Down Expand Up @@ -502,6 +539,35 @@ Result:
d: (string){ string }
}
}
b11: (struct){
#list: (#struct){
tail: (null){ null }
}
}
b12: (struct){
#list: (#struct){
value: (int){ int }
tail: (null){ null }
sum: (int){ int }
}
list1: (#struct){
value: (int){ 1 }
tail: (#struct){
value: (int){ 2 }
tail: (#struct){
value: (int){ 3 }
tail: (#struct){
value: (int){ 4 }
tail: (null){ null }
sum: (int){ 4 }
}
sum: (int){ 7 }
}
sum: (int){ 9 }
}
sum: (int){ 10 }
}
}
c1: (_|_){
// [structural cycle]
a: (_|_){
Expand Down Expand Up @@ -565,11 +631,11 @@ Result:
// [structural cycle]
x: (_|_){
// [structural cycle] d2.a.b.c.d.t: structural cycle:
// ./in.cue:106:8
// ./in.cue:143:8
}
r: (_|_){
// [structural cycle] d2.r: structural cycle:
// ./in.cue:106:8
// ./in.cue:143:8
c: (_|_){// {
// d: {
// h: int
Expand Down Expand Up @@ -612,13 +678,13 @@ Result:
// [structural cycle]
c: (_|_){
// [structural cycle] 0: structural cycle:
// ./in.cue:116:19
// ./in.cue:153:19
}
}
}
indirect: (_|_){
// [structural cycle] 0: structural cycle:
// ./in.cue:116:19
// ./in.cue:153:19
}
i: (int){ 1 }
}
Expand All @@ -630,13 +696,13 @@ Result:
// [structural cycle]
c: (_|_){
// [structural cycle] 0: structural cycle:
// ./in.cue:116:19
// ./in.cue:153:19
}
}
}
indirect: (_|_){
// [structural cycle] 0: structural cycle:
// ./in.cue:116:19
// ./in.cue:153:19
}
i: (int){ 0 }
}
Expand Down Expand Up @@ -683,12 +749,12 @@ Result:
// [eval]
a: (_|_){
// [eval] e3.a: conflicting values [a] and {c:a} (mismatched types list and struct):
// ./in.cue:141:8
// ./in.cue:142:8
// ./in.cue:178:8
// ./in.cue:179:8
c: (_|_){
// [eval] e3.a.c: conflicting values [a] and {c:a} (mismatched types list and struct):
// ./in.cue:141:8
// ./in.cue:142:8
// ./in.cue:178:8
// ./in.cue:179:8
// e3.a.c: structural cycle
c: (_|_){// 〈1;a〉
}
Expand All @@ -697,8 +763,8 @@ Result:
}
0: (_|_){
// [eval] e3.a.0: conflicting values [a] and {c:a} (mismatched types list and struct):
// ./in.cue:141:8
// ./in.cue:142:8
// ./in.cue:178:8
// ./in.cue:179:8
// e3.a.0: structural cycle
c: (_|_){// 〈1;a〉
}
Expand All @@ -708,12 +774,12 @@ Result:
}
b: (_|_){
// [eval] e3.b: conflicting values [b] and {c:b} (mismatched types list and struct):
// ./in.cue:144:8
// ./in.cue:145:8
// ./in.cue:181:8
// ./in.cue:182:8
c: (_|_){
// [eval] e3.b.c: conflicting values [b] and {c:b} (mismatched types list and struct):
// ./in.cue:144:8
// ./in.cue:145:8
// ./in.cue:181:8
// ./in.cue:182:8
// e3.b.c: structural cycle
c: (_|_){// 〈1;b〉
}
Expand All @@ -722,8 +788,8 @@ Result:
}
0: (_|_){
// [eval] e3.b.0: conflicting values [b] and {c:b} (mismatched types list and struct):
// ./in.cue:144:8
// ./in.cue:145:8
// ./in.cue:181:8
// ./in.cue:182:8
// e3.b.0: structural cycle
c: (_|_){// 〈1;b〉
}
Expand All @@ -738,8 +804,8 @@ Result:
// [eval]
0: (_|_){
// [eval] e4.a.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
// ./in.cue:149:13
// ./in.cue:150:9
// ./in.cue:186:13
// ./in.cue:187:9
0: (struct){
c: (int){ 1 }
}
Expand All @@ -749,8 +815,8 @@ Result:
// [eval]
0: (_|_){
// [eval] e4.b.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
// ./in.cue:152:9
// ./in.cue:153:13
// ./in.cue:189:9
// ./in.cue:190:13
0: (struct){
c: (int){ 1 }
}
Expand Down Expand Up @@ -1041,6 +1107,37 @@ Result:
d: (string|〈1;a〉)
})
}
b11: {
#list: {
tail: (〈1;#list〉|*null)
if (〈0;tail〉 != null) {}
}
}
b12: {
#list: {
value: int
tail: (〈1;#list〉|*null)
if (〈0;tail〉 != null) {
sum: (〈1;value〉 + 〈1;tail〉.sum)
}
if (〈0;tail〉 == null) {
sum: 〈1;value〉
}
}
list1: 〈0;#list〉
list1: {
value: 1
tail: {
value: 2
tail: {
value: 3
tail: {
value: 4
}
}
}
}
}
c1: {
a: {
b: {}
Expand Down
3 changes: 2 additions & 1 deletion internal/core/eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -1701,7 +1701,8 @@ func (n *nodeContext) insertField(f adt.Feature, x adt.Conjunct) *adt.Vertex {
// TODO(errors): detect when a field is added to a struct that is already used
// in a for clause.
func (n *nodeContext) expandOne() (done bool) {
if n.done() {
// Don't expand incomplete expressions if we detected a cycle.
if n.done() || (n.hasCycle && !n.hasNonCycle) {
return false
}

Expand Down

0 comments on commit aee9955

Please sign in to comment.