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
 		}