cue/export: partially evaluate incomplete values

Change-Id: I5e66586bb73dafad3985206e53a6f03c4d04b9a4
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2174
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cue/export.go b/cue/export.go
index 7624fa9..94bcfbc 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -165,12 +165,17 @@
 
 func (p *exporter) expr(v value) ast.Expr {
 	if doEval(p.mode) {
-		x := p.ctx.manifest(v)
+		e := v.evalPartial(p.ctx)
+		x := p.ctx.manifest(e)
 		if isIncomplete(x) {
-			p = &exporter{p.ctx, options{raw: true}, p.stack, p.top, p.imports}
-			return p.expr(v)
+			if isBottom(e) {
+				p = &exporter{p.ctx, options{raw: true}, p.stack, p.top, p.imports}
+				return p.expr(v)
+			}
+			v = e
+		} else {
+			v = x
 		}
-		v = x
 	}
 
 	old := p.stack
@@ -369,11 +374,14 @@
 			}
 			if !doEval(p.mode) {
 				f.Value = p.expr(a.v)
-			} else if v := p.ctx.manifest(x.at(p.ctx, i)); isIncomplete(v) && !p.mode.concrete {
-				p := &exporter{p.ctx, options{raw: true}, p.stack, p.top, p.imports}
-				f.Value = p.expr(a.v)
 			} else {
-				f.Value = p.expr(v)
+				e := x.at(p.ctx, i)
+				if v := p.ctx.manifest(e); isIncomplete(v) && !p.mode.concrete && isBottom(e) {
+					p := &exporter{p.ctx, options{raw: true}, p.stack, p.top, p.imports}
+					f.Value = p.expr(a.v)
+				} else {
+					f.Value = p.expr(e)
+				}
 			}
 			if a.attrs != nil && !p.mode.omitAttrs {
 				for _, at := range a.attrs.attr {
diff --git a/cue/export_test.go b/cue/export_test.go
index e6a1f7c..45659a2 100644
--- a/cue/export_test.go
+++ b/cue/export_test.go
@@ -153,6 +153,18 @@
 				a: >=0 & <=10 & !=1
 			}`),
 	}, {
+		raw:  true,
+		eval: true,
+		in: `{
+				a: (*1 | 2) & (1 | *2)
+				b: [(*1 | 2) & (1 | *2)]
+			}`,
+		out: unindent(`
+			{
+				a: 1 | 2
+				b: [1 | 2]
+			}`),
+	}, {
 		raw: true,
 		in:  `{ a: [1, 2], b: { "\(k)": v for k, v in a if v > 1 } }`,
 		out: unindent(`