internal/core/eval: allow lists resulting from dependent expressions

Fixes #494

Change-Id: I4d9e92193156e39e8677ba091250a73881d141b0
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6962
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/cue/testdata/cycle/issue494.txtar b/cue/testdata/cycle/issue494.txtar
new file mode 100644
index 0000000..7b1d905
--- /dev/null
+++ b/cue/testdata/cycle/issue494.txtar
@@ -0,0 +1,190 @@
+-- in.cue --
+_Q : [{pos: 0},{pos: 1}]
+
+a: [rn=string]:  _Q[0:len(a[rn])]
+a: ben: [{}]
+
+b: [rn=string]: _Q[0:1]
+b: ben: [{}]
+
+c: [rn=string]: [...{l: len(a[rn])}]
+c: ben: [{}]
+
+#d: [rn=string]:  [...{pos:uint}] & _Q[0:len(#d[rn])]
+#d: ben: [{}]
+
+d: #d
+
+e: [rn=string]:  _Q[0:len(a[rn])+1]
+e: ben: [{}, ...]
+
+f: [rn=string]:  _Q[0:len(a[rn])+1]
+f: ben: [{}]
+
+g: [rn=string]:  _Q[0:len(a[rn])]
+g: ben: [{}, {}]
+
+-- out/eval --
+Errors:
+f.ben: incompatible list lengths (1 and 2)
+g.ben: incompatible list lengths (1 and 2)
+
+Result:
+(_|_){
+  // [eval]
+  _Q: (#list){
+    0: (struct){
+      pos: (int){ 0 }
+    }
+    1: (struct){
+      pos: (int){ 1 }
+    }
+  }
+  a: (struct){
+    ben: (#list){
+      0: (struct){
+        pos: (int){ 0 }
+      }
+    }
+  }
+  b: (struct){
+    ben: (#list){
+      0: (struct){
+        pos: (int){ 0 }
+      }
+    }
+  }
+  c: (struct){
+    ben: (#list){
+      0: (struct){
+        l: (int){ 1 }
+      }
+    }
+  }
+  #d: (#struct){
+    ben: (#list){
+      0: (#struct){
+        pos: (int){ 0 }
+      }
+    }
+  }
+  d: (#struct){
+    ben: (#list){
+      0: (#struct){
+        pos: (int){ 0 }
+      }
+    }
+  }
+  e: (struct){
+    ben: (#list){
+      0: (struct){
+        pos: (int){ 0 }
+      }
+      1: (struct){
+        pos: (int){ 1 }
+      }
+    }
+  }
+  f: (_|_){
+    // [eval]
+    ben: (_|_){
+      // [eval] f.ben: incompatible list lengths (1 and 2)
+      0: (struct){
+        pos: (int){ 0 }
+      }
+      1: (struct){
+        pos: (int){ 1 }
+      }
+    }
+  }
+  g: (_|_){
+    // [eval]
+    ben: (_|_){
+      // [eval] g.ben: incompatible list lengths (1 and 2)
+      0: (struct){
+        pos: (int){ 0 }
+      }
+      1: (struct){
+      }
+    }
+  }
+}
+-- out/compile --
+--- in.cue
+{
+  _Q: [
+    {
+      pos: 0
+    },
+    {
+      pos: 1
+    },
+  ]
+  a: {
+    [string]: 〈1;_Q〉[0:len(〈1;a〉[〈0;-〉])]
+  }
+  a: {
+    ben: [
+      {},
+    ]
+  }
+  b: {
+    [string]: 〈1;_Q〉[0:1]
+  }
+  b: {
+    ben: [
+      {},
+    ]
+  }
+  c: {
+    [string]: [
+      ...{
+        l: len(〈2;a〉[〈1;-〉])
+      },
+    ]
+  }
+  c: {
+    ben: [
+      {},
+    ]
+  }
+  #d: {
+    [string]: ([
+      ...{
+        pos: &(int, >=0)
+      },
+    ] & 〈1;_Q〉[0:len(〈1;#d〉[〈0;-〉])])
+  }
+  #d: {
+    ben: [
+      {},
+    ]
+  }
+  d: 〈0;#d〉
+  e: {
+    [string]: 〈1;_Q〉[0:(len(〈1;a〉[〈0;-〉]) + 1)]
+  }
+  e: {
+    ben: [
+      {},
+      ...,
+    ]
+  }
+  f: {
+    [string]: 〈1;_Q〉[0:(len(〈1;a〉[〈0;-〉]) + 1)]
+  }
+  f: {
+    ben: [
+      {},
+    ]
+  }
+  g: {
+    [string]: 〈1;_Q〉[0:len(〈1;a〉[〈0;-〉])]
+  }
+  g: {
+    ben: [
+      {},
+      {},
+    ]
+  }
+}
diff --git a/internal/core/eval/eval.go b/internal/core/eval/eval.go
index 3298f7d..647d4b8 100644
--- a/internal/core/eval/eval.go
+++ b/internal/core/eval/eval.go
@@ -420,13 +420,18 @@
 func (n *nodeContext) postDisjunct() {
 	ctx := n.ctx
 
-	// Use maybeSetCache for cycle breaking
-	for n.maybeSetCache(); n.expandOne(); n.maybeSetCache() {
+	for {
+		// Use maybeSetCache for cycle breaking
+		for n.maybeSetCache(); n.expandOne(); n.maybeSetCache() {
+		}
+
+		if aList := n.addLists(ctx); aList != nil {
+			n.updateNodeType(adt.ListKind, aList)
+		} else {
+			break
+		}
 	}
 
-	if aList := n.addLists(ctx); aList != nil {
-		n.updateNodeType(adt.ListKind, aList)
-	}
 	if n.aStruct != nil {
 		n.updateNodeType(adt.StructKind, n.aStruct)
 	}
@@ -1600,6 +1605,11 @@
 	max := 0
 	var maxNode adt.Expr
 
+	if m, ok := n.node.Value.(*adt.ListMarker); ok {
+		isOpen = m.IsOpen
+		max = len(n.node.Arcs)
+	}
+
 	for _, l := range n.vLists {
 		oneOfTheLists = l
 
@@ -1747,10 +1757,21 @@
 
 	n.openList = isOpen
 
-	n.node.SetValue(c, adt.Partial, &adt.ListMarker{
-		Src:    ast.NewBinExpr(token.AND, sources...),
-		IsOpen: isOpen,
-	})
+	if m, ok := n.node.Value.(*adt.ListMarker); !ok {
+		n.node.SetValue(c, adt.Partial, &adt.ListMarker{
+			Src:    ast.NewBinExpr(token.AND, sources...),
+			IsOpen: isOpen,
+		})
+	} else {
+		if expr, _ := m.Src.(ast.Expr); expr != nil {
+			sources = append(sources, expr)
+		}
+		m.Src = ast.NewBinExpr(token.AND, sources...)
+		m.IsOpen = m.IsOpen && isOpen
+	}
+
+	n.lists = n.lists[:0]
+	n.vLists = n.vLists[:0]
 
 	return oneOfTheLists
 }