internal/core/adt: pass errors up in nested comprehensions

Change-Id: I97449f51f214f36d6bd7db0c69a8001e6c7393d6
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7941
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/cue/testdata/comprehensions/iferror.txtar b/cue/testdata/comprehensions/iferror.txtar
new file mode 100644
index 0000000..dd7403b
--- /dev/null
+++ b/cue/testdata/comprehensions/iferror.txtar
@@ -0,0 +1,94 @@
+-- in.cue --
+a: { b: 2, c: int }
+
+wrongConcreteType:  {
+	if a.b {
+		2
+	}
+}
+
+wrongType:  {
+	if a.c {
+		2
+	}
+}
+
+incomplete:  {
+	if a.d {
+		2
+	}
+}
+
+incomplete:  {
+    list: [1, 2, 3]
+	for x in list if a.d {
+		x
+	}
+}
+-- out/compile --
+--- in.cue
+{
+  a: {
+    b: 2
+    c: int
+  }
+  wrongConcreteType: {
+    if 〈1;a〉.b {
+      2
+    }
+  }
+  wrongType: {
+    if 〈1;a〉.c {
+      2
+    }
+  }
+  incomplete: {
+    if 〈1;a〉.d {
+      2
+    }
+  }
+  incomplete: {
+    list: [
+      1,
+      2,
+      3,
+    ]
+    for _, x in 〈0;list〉 if 〈2;a〉.d {
+      〈1;x〉
+    }
+  }
+}
+-- out/eval --
+Errors:
+wrongConcreteType: cannot use 2 (type int) as type bool:
+    ./in.cue:4:2
+wrongType: cannot use int (type int) as type bool:
+    ./in.cue:10:2
+
+Result:
+(_|_){
+  // [eval]
+  a: (struct){
+    b: (int){ 2 }
+    c: (int){ int }
+  }
+  wrongConcreteType: (_|_){
+    // [eval] wrongConcreteType: cannot use 2 (type int) as type bool:
+    //     ./in.cue:4:2
+  }
+  wrongType: (_|_){
+    // [eval] wrongType: cannot use int (type int) as type bool:
+    //     ./in.cue:10:2
+  }
+  incomplete: (_|_){
+    // [incomplete] incomplete: undefined field d:
+    //     ./in.cue:16:7
+    // incomplete: undefined field d:
+    //     ./in.cue:23:21
+    list: (#list){
+      0: (int){ 1 }
+      1: (int){ 2 }
+      2: (int){ 3 }
+    }
+  }
+}
diff --git a/cue/testdata/cycle/comprehension.txtar b/cue/testdata/cycle/comprehension.txtar
index 0dd958e..33454eb 100644
--- a/cue/testdata/cycle/comprehension.txtar
+++ b/cue/testdata/cycle/comprehension.txtar
@@ -12,8 +12,8 @@
     }
 }
 
-// TODO(errors): this should result in an incomplete error.
-// A simplified control flow should help here.
+// This should result in an incomplete error (a reference cycle error classifies
+// as incomplete).
 B: {
     a: {
         parent: ""
@@ -64,7 +64,9 @@
   B: (struct){
     a: (struct){
       parent: (string){ "" }
-      children: (#list){
+      children: (_|_){
+        // [cycle] cycle error:
+        //     ./in.cue:19:47
       }
     }
   }
diff --git a/internal/core/adt/context.go b/internal/core/adt/context.go
index da90d20..0bb10e8 100644
--- a/internal/core/adt/context.go
+++ b/internal/core/adt/context.go
@@ -187,10 +187,9 @@
 	return c.src.Pos()
 }
 
-func (c *OpContext) spawn(node *Vertex) *OpContext {
-	sub := *c
-	node.Parent = c.e.Vertex
-	sub.e = &Environment{
+func (c *OpContext) spawn(node *Vertex) *Environment {
+	node.Parent = c.e.Vertex // TODO: Is this necessary?
+	return &Environment{
 		Up:     c.e,
 		Vertex: node,
 
@@ -199,7 +198,6 @@
 		Deref:  c.e.Deref,
 		Cycles: c.e.Cycles,
 	}
-	return &sub
 }
 
 func (c *OpContext) Env(upCount int32) *Environment {
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
index cec875a..939ef6e 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -1251,7 +1251,13 @@
 			n.Arcs = append(n.Arcs, v)
 		}
 
-		x.Dst.yield(c.spawn(n), f)
+		sub := c.spawn(n)
+		saved := c.PushState(sub, x.Dst.Source())
+		x.Dst.yield(c, f)
+		if b := c.PopState(saved); b != nil {
+			c.AddBottom(b)
+			break
+		}
 		if c.HasErr() {
 			break
 		}
@@ -1304,7 +1310,13 @@
 	n := &Vertex{Arcs: []*Vertex{
 		{Label: x.Label, Conjuncts: []Conjunct{{c.Env(0), x.Expr, 0}}},
 	}}
-	x.Dst.yield(c.spawn(n), f)
+
+	sub := c.spawn(n)
+	saved := c.PushState(sub, x.Dst.Source())
+	x.Dst.yield(c, f)
+	if b := c.PopState(saved); b != nil {
+		c.AddBottom(b)
+	}
 }
 
 // A ValueClause represents the value part of a comprehension.