cue: improve support for exporting partial results

- add aliases if references become shadowed
- don’t return false for Err if a value is merely incomplete
- export struct templates

Also:
- remove Default from API

Change-Id: If3d30c25efc4bae77b3c10c018f64586668f298f
Reviewed-on: https://cue-review.googlesource.com/c/1530
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cmd/cue/cmd/testdata/hello/export.out b/cmd/cue/cmd/testdata/hello/export.out
new file mode 100644
index 0000000..ccd2471
--- /dev/null
+++ b/cmd/cue/cmd/testdata/hello/export.out
@@ -0,0 +1,4 @@
+{
+    "who": "World",
+    "message": "Hello World!"
+}
diff --git a/cmd/cue/cmd/testdata/partial/eval.out b/cmd/cue/cmd/testdata/partial/eval.out
new file mode 100644
index 0000000..0f97899
--- /dev/null
+++ b/cmd/cue/cmd/testdata/partial/eval.out
@@ -0,0 +1,20 @@
+// $CWD/testdata/partial
+{
+    def: 1
+    sum: 1 | 2
+    A = a
+    b: {
+        idx: A[str]
+        a b: 4
+        str: string
+    }
+    a: {
+        b:  3
+        c:  4
+    }
+    c: {
+        idx: 3
+        a b: 4
+        str: "b"
+    }
+}
diff --git a/cmd/cue/cmd/testdata/partial/partial.cue b/cmd/cue/cmd/testdata/partial/partial.cue
new file mode 100644
index 0000000..ce33304
--- /dev/null
+++ b/cmd/cue/cmd/testdata/partial/partial.cue
@@ -0,0 +1,15 @@
+package partial
+
+def: *1 | int
+sum: 1 | 2
+
+b: {
+	idx: a[str] // should resolve to top-level `a`
+	str: string
+}
+b a b: 4
+a: {
+	b: 3
+	c: 4
+}
+c: b & {str: "b"}
diff --git a/cue/export.go b/cue/export.go
index 4b18225..f42d36c 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"math/rand"
 	"strconv"
 	"strings"
 	"unicode"
@@ -28,18 +29,39 @@
 type exportMode int
 
 const (
-	exportRaw exportMode = iota
-	exportEval
+	exportEval exportMode = 1 << iota
+	exportRaw  exportMode = 0
 )
 
 func export(ctx *context, v value, m exportMode) ast.Expr {
-	e := exporter{ctx, m}
+	e := exporter{ctx, m, nil}
 	return e.expr(v)
 }
 
 type exporter struct {
-	ctx  *context
-	mode exportMode
+	ctx   *context
+	mode  exportMode
+	stack []remap
+}
+
+type remap struct {
+	key  scope // structLit or params
+	from label
+	to   *ast.Ident
+	syn  *ast.StructLit
+}
+
+func (p *exporter) unique(s string) string {
+	s = strings.ToUpper(s)
+	lab := s
+	for {
+		if _, ok := p.ctx.labelMap[lab]; !ok {
+			p.ctx.label(lab, true)
+			break
+		}
+		lab = s + fmt.Sprintf("%0.6x", rand.Intn(1<<24))
+	}
+	return lab
 }
 
 func (p *exporter) label(f label) ast.Label {
@@ -90,44 +112,92 @@
 
 func (p *exporter) expr(v value) ast.Expr {
 	if p.mode == exportEval {
-		v = v.evalPartial(p.ctx)
+		x := p.ctx.manifest(v)
+		if isIncomplete(x) {
+			p = &exporter{p.ctx, exportRaw, p.stack}
+			return p.expr(v)
+		}
+		v = x
 	}
 
+	old := p.stack
+	defer func() { p.stack = old }()
+
 	// TODO: also add position information.
 	switch x := v.(type) {
 	case *builtin:
 		return &ast.Ident{Name: x.Name}
+
 	case *nodeRef:
 		return nil
+
 	case *selectorExpr:
 		n := p.expr(x.x)
-		if n == nil {
-			return p.identifier(x.feature)
+		if n != nil {
+			return &ast.SelectorExpr{X: n, Sel: p.identifier(x.feature)}
 		}
-		return &ast.SelectorExpr{X: n, Sel: p.identifier(x.feature)}
+		ident := p.identifier(x.feature)
+		node, ok := x.x.(*nodeRef)
+		if !ok {
+			// TODO: should not happen: report error
+			return ident
+		}
+		conflict := false
+		for i := len(p.stack) - 1; i >= 0; i-- {
+			e := &p.stack[i]
+			if e.from != x.feature {
+				continue
+			}
+			if e.key != node.node {
+				conflict = true
+				continue
+			}
+			if conflict {
+				ident = e.to
+				if e.to == nil {
+					name := p.unique(p.ctx.labelStr(x.feature))
+					e.syn.Elts = append(e.syn.Elts, &ast.Alias{
+						Ident: p.ident(name),
+						Expr:  p.identifier(x.feature),
+					})
+					ident = p.ident(name)
+					e.to = ident
+				}
+			}
+			return ident
+		}
+		// TODO: should not happen: report error
+		return ident
+
 	case *indexExpr:
 		return &ast.IndexExpr{X: p.expr(x.x), Index: p.expr(x.index)}
+
 	case *sliceExpr:
 		return &ast.SliceExpr{
 			X:    p.expr(x.x),
 			Low:  p.expr(x.lo),
 			High: p.expr(x.hi),
 		}
+
 	case *callExpr:
 		call := &ast.CallExpr{Fun: p.expr(x.x)}
 		for _, a := range x.args {
 			call.Args = append(call.Args, p.expr(a))
 		}
 		return call
+
 	case *unaryExpr:
 		return &ast.UnaryExpr{Op: opMap[x.op], X: p.expr(x.x)}
+
 	case *binaryExpr:
 		return &ast.BinaryExpr{
 			X:  p.expr(x.left),
 			Op: opMap[x.op], Y: p.expr(x.right),
 		}
+
 	case *bound:
 		return &ast.UnaryExpr{Op: opMap[x.op], X: p.expr(x.value)}
+
 	case *unification:
 		if len(x.values) == 1 {
 			return p.expr(x.values[0])
@@ -158,17 +228,37 @@
 	case *structLit:
 		obj := &ast.StructLit{}
 		if p.mode == exportEval {
+			for _, a := range x.arcs {
+				p.stack = append(p.stack, remap{
+					key:  x,
+					from: a.feature,
+					to:   nil,
+					syn:  obj,
+				})
+			}
 			x = x.expandFields(p.ctx)
 		}
 		if x.emit != nil {
 			obj.Elts = append(obj.Elts, &ast.EmitDecl{Expr: p.expr(x.emit)})
 		}
+		if p.mode != exportEval && x.template != nil {
+			l, ok := x.template.evalPartial(p.ctx).(*lambdaExpr)
+			if ok {
+				obj.Elts = append(obj.Elts, &ast.Field{
+					Label: &ast.TemplateLabel{
+						Ident: p.identifier(l.params.arcs[0].feature),
+					},
+					Value: p.expr(l.value),
+				})
+			} // TODO: else record error
+		}
 		for _, a := range x.arcs {
 			obj.Elts = append(obj.Elts, &ast.Field{
 				Label: p.label(a.feature),
 				Value: p.expr(a.v),
 			})
 		}
+
 		for _, c := range x.comprehensions {
 			var clauses []ast.Clause
 			next := c.clauses
diff --git a/cue/export_test.go b/cue/export_test.go
index 7578f92..d47e61c 100644
--- a/cue/export_test.go
+++ b/cue/export_test.go
@@ -27,6 +27,7 @@
 func TestExport(t *testing.T) {
 	testCases := []struct {
 		raw     bool
+		mode    exportMode
 		in, out string
 	}{{
 		in:  `"hello"`,
@@ -85,14 +86,14 @@
 			f: [1, 2, ...]
 		}`,
 		out: unindent(`
-			{
-				a: 5*[int] & [1, 2, ...int]
-				b: (>=2 & <=5)*[int] & [1, 2, ...int]
-				c: (<=5 & >=3)*[int] & [1, 2, ...int]
-				d: [1, 2, ...int]
-				e: [1, 2, ...int]
-				f: [1, 2, ...]
-			}`),
+		{
+			a: 5*[int] & [1, 2, ...int]
+			b: (>=2 & <=5)*[int] & [1, 2, ...int]
+			c: (<=5 & >=3)*[int] & [1, 2, ...int]
+			d: [1, 2, ...int]
+			e: [1, 2, ...int]
+			f: [1, 2, ...]
+		}`),
 	}, {
 		in: `{
 			a: >=0*[int]
@@ -170,6 +171,49 @@
 					a: ""
 					b: len(a)
 				}`),
+	}, {
+		raw:  true,
+		mode: exportEval,
+		in: `{
+			b: {
+				idx: a[str]
+				str: string
+			}
+			b a b: 4
+			a b: 3
+		}`,
+		// reference to a must be redirected to outer a through alias
+		out: unindent(`
+		{
+			A = a
+			b: {
+				idx: A[str]
+				a b: 4
+				str: string
+			}
+			a b: 3
+		}`),
+	}, {
+		raw:  true,
+		mode: exportEval,
+		in: `{
+			b: [{
+				<X>: int
+				f: 4 if a > 4
+			}][a]
+			a: int
+			c: *1 | 2
+		}`,
+		// reference to a must be redirected to outer a through alias
+		out: unindent(`
+		{
+			b: [{
+				<X>: int
+				"f": 4 if a > 4
+			}][a]
+			a: int
+			c: 1
+		}`),
 	}}
 	for _, tc := range testCases {
 		t.Run("", func(t *testing.T) {
@@ -186,7 +230,7 @@
 			v := newValueRoot(ctx, n)
 
 			buf := &bytes.Buffer{}
-			err := format.Node(buf, export(ctx, v.eval(ctx), exportRaw))
+			err := format.Node(buf, export(ctx, v.eval(ctx), tc.mode))
 			if err != nil {
 				log.Fatal(err)
 			}
diff --git a/cue/types.go b/cue/types.go
index d12f805..51f9ba7 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -478,11 +478,6 @@
 	return ctx.manifest(v.path.v)
 }
 
-// Default returs v if v.Exists or a value converted from x otherwise.
-func (v Value) Default(x interface{}) Value {
-	return v
-}
-
 // Label reports he label used to obtain this value from the enclosing struct.
 //
 // TODO: get rid of this somehow. Maybe by passing it to walk
@@ -709,12 +704,14 @@
 		return b
 	}
 	got := x.kind()
-	if got&want&concreteKind == bottomKind && want != bottomKind {
-		return ctx.mkErr(x, "not of right kind (%v vs %v)", got, want)
-	}
-	if !got.isGround() {
-		return ctx.mkErr(x, codeIncomplete,
-			"non-concrete value %v", got)
+	if want != bottomKind {
+		if got&want&concreteKind == bottomKind {
+			return ctx.mkErr(x, "not of right kind (%v vs %v)", got, want)
+		}
+		if !got.isGround() {
+			return ctx.mkErr(x, codeIncomplete,
+				"non-concrete value %v", got)
+		}
 	}
 	return nil
 }
@@ -985,13 +982,39 @@
 	return v, true
 }
 
+type options struct {
+	concrete bool
+}
+
+// An Option defines modes of evaluation.
+type Option func(p *options)
+
+// Used in Validate, Subsume?, Fields()
+
+// TODO: could also be used for subsumption.
+
+// RequireConcrete verifies that all values in a tree are concrete.
+func RequireConcrete() Option {
+	return func(p *options) { p.concrete = true }
+}
+
+// VisitHidden(visit bool)
+//
+
 // Validate reports any errors, recursively. The returned error may be an
 // errors.List reporting multiple errors, where the total number of errors
 // reported may be less than the actual number.
-func (v Value) Validate() error {
+func (v Value) Validate(opts ...Option) error {
+	var o options
+	for _, fn := range opts {
+		fn(&o)
+	}
 	list := errors.List{}
 	v.Walk(func(v Value) bool {
 		if err := v.Err(); err != nil {
+			if !o.concrete && isIncomplete(v.eval(v.ctx())) {
+				return false
+			}
 			list.Add(err)
 			if len(list) > 50 {
 				return false // mostly to avoid some hypothetical cycle issue
diff --git a/cue/types_test.go b/cue/types_test.go
index 4f43796..9ef0215 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -429,7 +429,7 @@
 		value: `"Hello world!"`,
 	}, {
 		value: `string`,
-		err:   "non-concrete value (string)*",
+		err:   "",
 	}}
 	for _, tc := range testCases {
 		t.Run(tc.value, func(t *testing.T) {