cue: avoid early evaluation in several cases

Change-Id: Id7aa7d83b44732530805ed00bd44ece8412c6dc1
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/1786
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cue/ast.go b/cue/ast.go
index 4c5ece5..c4a2366 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -112,9 +112,11 @@
 	ctx := v.ctx()
 	label := v.label(n.Name, true)
 	if r := v.resolveRoot; r != nil {
-		if a := r.lookup(v.ctx(), label); a.val() != nil {
-			return &selectorExpr{newExpr(n),
-				&nodeRef{baseValue: newExpr(n), node: r}, label}
+		for _, a := range r.arcs {
+			if a.feature == label {
+				return &selectorExpr{newExpr(n),
+					&nodeRef{baseValue: newExpr(n), node: r}, label}
+			}
 		}
 		if v.inSelector > 0 {
 			if p := getBuiltinShorthandPkg(ctx, n.Name); p != nil {
@@ -131,6 +133,8 @@
 	if err != nil {
 		return ctx.mkErr(newNode(imp), "illformed import spec")
 	}
+	// TODO: allow builtin *and* imported package. The result is a unified
+	// struct.
 	if p := getBuiltinPkg(ctx, path); p != nil {
 		return p
 	}
diff --git a/cue/context.go b/cue/context.go
index 7bfaae5..c115c7f 100644
--- a/cue/context.go
+++ b/cue/context.go
@@ -34,6 +34,8 @@
 	inSum       int
 	cycleErr    bool
 
+	noManifest bool
+
 	// for debug strings
 	nodeRefs map[scope]string
 
diff --git a/cue/evaluator.go b/cue/evaluator.go
index ad8c835..37b306c 100644
--- a/cue/evaluator.go
+++ b/cue/evaluator.go
@@ -16,6 +16,9 @@
 
 func (c *context) manifest(v value) evaluated {
 	evaluated := v.evalPartial(c)
+	if c.noManifest {
+		return evaluated
+	}
 	for {
 		x, ok := evaluated.(*disjunction)
 		if !ok {
diff --git a/cue/types.go b/cue/types.go
index df76119..35a9576 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -920,9 +920,10 @@
 	if w.path == nil {
 		return v
 	}
-	a := v.eval(ctx)
-	b := w.eval(ctx)
-	val := ctx.manifest(mkBin(ctx, token.NoPos, opUnify, a, b))
+	a := v.path.cache.evalPartial(ctx)
+	b := w.path.cache.evalPartial(ctx)
+	src := binSrc(token.NoPos, opUnify, a, b)
+	val := binOp(ctx, src, opUnify, a, b)
 	if err := validate(ctx, val); err != nil {
 		val = err
 	}
diff --git a/cue/value.go b/cue/value.go
index 976cd40..d1d8cf6 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -395,10 +395,17 @@
 func mkIntRange(a, b string) evaluated {
 	from := &bound{op: opGeq, value: parseInt(intKind, a)}
 	to := &bound{op: opLeq, value: parseInt(intKind, b)}
-	return &unification{
+	e := &unification{
 		binSrc(token.NoPos, opUnify, from, to),
 		[]evaluated{from, to},
 	}
+	// TODO: make this an integer
+	// int := &basicType{k: intKind}
+	// e = &unification{
+	// 	binSrc(token.NoPos, opUnify, int, e),
+	// 	[]evaluated{int, e},
+	// }
+	return e
 }
 
 var predefinedRanges = map[string]evaluated{
@@ -1029,6 +1036,12 @@
 		return (!lt.marked || gt.marked) && subsumes(ctx, gt.val, lt.val, 0)
 	}
 	k := 0
+
+	// manifesting values should be disabled for recursive evaluation as
+	// these values may still be bound to another value later on, for instance
+	// when the result of this value is unified with another value.
+	noManifest := ctx.noManifest
+	ctx.noManifest = true
 outer:
 	for i, v := range x.values {
 		// TODO: this is pre-evaluation is quite aggressive. Verify whether
@@ -1059,6 +1072,7 @@
 		x.values[k] = v
 		k++
 	}
+	ctx.noManifest = noManifest
 
 	switch k {
 	case 0: