internal/core/eval: fix hang in cyclic in comprehension evaluation
Fixes #486
Change-Id: I8a7e42c3eaff476c3bae23b40f7cb0ae5658975c
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7502
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/testdata/cycle/comprehension.txtar b/cue/testdata/cycle/comprehension.txtar
new file mode 100644
index 0000000..0dd958e
--- /dev/null
+++ b/cue/testdata/cycle/comprehension.txtar
@@ -0,0 +1,155 @@
+-- in.cue --
+// Allow lookup in partially evaluated struct as long as the end result is
+// concrete.
+A: {
+ a: {
+ parent: ""
+ children: [ for k, v in A if v.parent == k { k } ]
+ }
+ b: {
+ parent: "a"
+ children: [ for k, v in A if v.parent == k { k } ]
+ }
+}
+
+// TODO(errors): this should result in an incomplete error.
+// A simplified control flow should help here.
+B: {
+ a: {
+ parent: ""
+ children: [ for k, v in B for _, w in v.children { k } ]
+ }
+}
+
+// Issue #486
+Issue486: {
+ A: {
+ a: {
+ parent: ""
+ children: [...string]
+ }
+ b: {
+ parent: "a"
+ children: [...string]
+ }
+ c: {
+ parent: "b"
+ children: [...string]
+ }
+ }
+
+ A: [Name=string]: {
+ children: [
+ for k, v in A
+ if v.parent == Name {
+ k
+ }
+ ]
+ }
+}
+-- out/eval --
+(struct){
+ A: (struct){
+ a: (struct){
+ parent: (string){ "" }
+ children: (#list){
+ }
+ }
+ b: (struct){
+ parent: (string){ "a" }
+ children: (#list){
+ }
+ }
+ }
+ B: (struct){
+ a: (struct){
+ parent: (string){ "" }
+ children: (#list){
+ }
+ }
+ }
+ Issue486: (struct){
+ A: (struct){
+ a: (struct){
+ parent: (string){ "" }
+ children: (#list){
+ 0: (string){ "b" }
+ }
+ }
+ b: (struct){
+ parent: (string){ "a" }
+ children: (#list){
+ 0: (string){ "c" }
+ }
+ }
+ c: (struct){
+ parent: (string){ "b" }
+ children: (#list){
+ }
+ }
+ }
+ }
+}
+-- out/compile --
+--- in.cue
+{
+ A: {
+ a: {
+ parent: ""
+ children: [
+ for k, v in 〈2;A〉 if (〈0;v〉.parent == 〈0;k〉) {
+ 〈1;k〉
+ },
+ ]
+ }
+ b: {
+ parent: "a"
+ children: [
+ for k, v in 〈2;A〉 if (〈0;v〉.parent == 〈0;k〉) {
+ 〈1;k〉
+ },
+ ]
+ }
+ }
+ B: {
+ a: {
+ parent: ""
+ children: [
+ for k, v in 〈2;B〉 for _, w in 〈0;v〉.children {
+ 〈2;k〉
+ },
+ ]
+ }
+ }
+ Issue486: {
+ A: {
+ a: {
+ parent: ""
+ children: [
+ ...string,
+ ]
+ }
+ b: {
+ parent: "a"
+ children: [
+ ...string,
+ ]
+ }
+ c: {
+ parent: "b"
+ children: [
+ ...string,
+ ]
+ }
+ }
+ A: {
+ [string]: {
+ children: [
+ for k, v in 〈2;A〉 if (〈0;v〉.parent == 〈2;-〉) {
+ 〈1;k〉
+ },
+ ]
+ }
+ }
+ }
+}
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
index 131c3b3..e3b7267 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -582,7 +582,7 @@
}
func (x *SelectorExpr) resolve(c *OpContext) *Vertex {
- n := c.node(x.X, Partial)
+ n := c.node(x.X, EvaluatingArcs)
return c.lookup(n, x.Src.Sel.Pos(), x.Sel)
}
@@ -605,7 +605,7 @@
func (x *IndexExpr) resolve(ctx *OpContext) *Vertex {
// TODO: support byte index.
- n := ctx.node(x.X, Partial)
+ n := ctx.node(x.X, EvaluatingArcs)
i := ctx.value(x.Index)
f := ctx.Label(i)
return ctx.lookup(n, x.Src.Index.Pos(), f)
@@ -1171,8 +1171,10 @@
}
func (x *ForClause) yield(c *OpContext, f YieldFunc) {
- n := c.node(x.Src, Finalized)
+ n := c.node(x.Src, EvaluatingArcs)
for _, a := range n.Arcs {
+ c.Unify(c, a, Partial)
+
if !a.Label.IsRegular() {
continue
}