cue: bring default values up to spec

This is bolted on to the old implementation. This results
in a more compact representation that could be considered
a premature optimization, but so be it.

Fixes #28.

Change-Id: I57939e69d39d486cf0d8f21d7cdf525d376df670
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/1981
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cue/ast.go b/cue/ast.go
index d2bed29..1db9ad8 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -536,6 +536,7 @@
 			mark = true
 			n = x.X
 		}
+		d.hasDefaults = true
 	}
 	d.values = append(d.values, dValue{v.walk(n), mark})
 }
diff --git a/cue/binop.go b/cue/binop.go
index 0b1831b..e9bbd43 100644
--- a/cue/binop.go
+++ b/cue/binop.go
@@ -37,15 +37,15 @@
 }
 
 func binOp(ctx *context, src source, op op, left, right evaluated) (result evaluated) {
-	if isBottom(left) {
-		if op == opUnify && ctx.exprDepth == 0 && cycleError(left) != nil {
+	if b, ok := left.(*bottom); ok {
+		if op == opUnify && b.exprDepth == 0 && cycleError(b) != nil {
 			ctx.cycleErr = true
 			return right
 		}
 		return left
 	}
-	if isBottom(right) {
-		if op == opUnify && ctx.exprDepth == 0 && cycleError(right) != nil {
+	if b, ok := right.(*bottom); ok {
+		if op == opUnify && b.exprDepth == 0 && cycleError(b) != nil {
 			ctx.cycleErr = true
 			return left
 		}
@@ -70,9 +70,9 @@
 		if !leftKind.isGround() || !rightKind.isGround() {
 			return ctx.mkErr(src, codeIncomplete, "incomplete error")
 		}
-		ctx.exprDepth++
+		ctx.incEvalDepth()
 		v := left.binOp(ctx, src, op, right) // may return incomplete
-		ctx.exprDepth--
+		ctx.decEvalDepth()
 		return v
 	}
 
@@ -119,29 +119,41 @@
 }
 
 // distribute distributes a value over the element of a disjunction in a
-// unification operation. If allowCycle is true, references that resolve
-// to a cycle are dropped.
-func distribute(ctx *context, src source, x *disjunction, y evaluated) evaluated {
-	return dist(ctx, src, x, mVal{y, false}).val
+// unification operation.
+// TODO: this is an exponential algorithm. There is no reason to have to
+// resolve this early. Revise this to only do early pruning but not a full
+// evaluation.
+func distribute(ctx *context, src source, x, y evaluated) evaluated {
+	dn := &disjunction{baseValue: src.base()}
+	dist(ctx, dn, false, mVal{x, true}, mVal{y, true})
+	return dn.normalize(ctx, src).val
 }
 
-func dist(ctx *context, src source, dx *disjunction, y mVal) mVal {
-	dn := &disjunction{src.base(), make([]dValue, 0, len(dx.values))}
-	for _, dv := range dx.values {
-		x := mVal{dv.val.evalPartial(ctx), dv.marked}
-		src := binSrc(src.Pos(), opUnify, x.val, y.val)
-
-		var v mVal
-		if dy, ok := y.val.(*disjunction); ok {
-			v = dist(ctx, src, dy, x)
-		} else if ddv, ok := dv.val.(*disjunction); ok {
-			v = dist(ctx, src, ddv, y)
-		} else {
-			v = mVal{binOp(ctx, src, opUnify, x.val, y.val), x.mark || y.mark}
+func dist(ctx *context, d *disjunction, mark bool, x, y mVal) {
+	if dx, ok := x.val.(*disjunction); ok {
+		if dx.hasDefaults {
+			mark = true
+			d.hasDefaults = true
 		}
-		dn.add(ctx, v.val, v.mark)
+		for _, dxv := range dx.values {
+			m := dxv.marked || !dx.hasDefaults
+			dist(ctx, d, mark, mVal{dxv.val.evalPartial(ctx), m}, y)
+		}
+		return
 	}
-	return dn.normalize(ctx, src)
+	if dy, ok := y.val.(*disjunction); ok {
+		if dy.hasDefaults {
+			mark = true
+			d.hasDefaults = true
+		}
+		for _, dxy := range dy.values {
+			m := dxy.marked || !dy.hasDefaults
+			dist(ctx, d, mark, x, mVal{dxy.val.evalPartial(ctx), m})
+		}
+		return
+	}
+	src := binSrc(0, opUnify, x.val, y.val)
+	d.add(ctx, binOp(ctx, src, opUnify, x.val, y.val), mark && x.mark && y.mark)
 }
 
 func (x *disjunction) binOp(ctx *context, src source, op op, other evaluated) evaluated {
diff --git a/cue/builtin.go b/cue/builtin.go
index 63d0547..a2dce51 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -162,7 +162,7 @@
 		for iter.Next() {
 			d = append(d, dValue{iter.Value().path.v, false})
 		}
-		c.ret = &disjunction{baseValue{c.src}, d}
+		c.ret = &disjunction{baseValue{c.src}, d, false}
 		if len(d) == 0 {
 			c.ret = errors.New("empty or")
 		}
diff --git a/cue/context.go b/cue/context.go
index c115c7f..ff81001 100644
--- a/cue/context.go
+++ b/cue/context.go
@@ -29,10 +29,10 @@
 
 	// constraints are to be evaluated at the end values to be evaluated later.
 	constraints []*binaryExpr
-	exprDepth   int // nesting of expression that are not unification
-	evalDepth   int
-	inSum       int
-	cycleErr    bool
+	evalStack   []bottom
+
+	inSum    int
+	cycleErr bool
 
 	noManifest bool
 
@@ -44,6 +44,18 @@
 	level int
 }
 
+func (c *context) incEvalDepth() {
+	if len(c.evalStack) > 0 {
+		c.evalStack[len(c.evalStack)-1].exprDepth++
+	}
+}
+
+func (c *context) decEvalDepth() {
+	if len(c.evalStack) > 0 {
+		c.evalStack[len(c.evalStack)-1].exprDepth--
+	}
+}
+
 var baseContext apd.Context
 
 func init() {
diff --git a/cue/errors.go b/cue/errors.go
index f15927a..fe8ed52 100644
--- a/cue/errors.go
+++ b/cue/errors.go
@@ -66,6 +66,7 @@
 
 	index          *index
 	code           errCode
+	exprDepth      int
 	value          value
 	offendingValue value
 	replacement    evaluated // for cycle resolution
@@ -143,20 +144,6 @@
 	return nil
 }
 
-// sentinel values used for signalling a type of error.
-var (
-	// errNonGround =  // values are not compatible
-	cycleSentinel = &bottom{
-		code: codeCycle,
-		msg:  "cycle detected",
-	}
-
-	// unifyNotSupported may be returned when a node implementation cannot
-	// handle the other type. In this case it may still be the case that the
-	// converse can be implemented.
-	// unifyNotSupported node = &bottom{}
-)
-
 func (idx *index) mkErrUnify(src source, a, b evaluated) evaluated {
 	if err := firstBottom(a, b); err != nil {
 		return err
diff --git a/cue/eval.go b/cue/eval.go
index 681b21d..68056ab 100644
--- a/cue/eval.go
+++ b/cue/eval.go
@@ -279,8 +279,11 @@
 		defer func() { ctx.debugPrint("result:", result) }()
 	}
 
-	ctx.inSum++
-	dn := &disjunction{x.baseValue, make([]dValue, 0, len(x.values))}
+	// decSum := false
+	if len(ctx.evalStack) > 1 {
+		ctx.inSum++
+	}
+	dn := &disjunction{x.baseValue, make([]dValue, 0, len(x.values)), x.hasDefaults}
 	changed := false
 	for _, v := range x.values {
 		n := v.val.evalPartial(ctx)
@@ -299,7 +302,9 @@
 	if !changed {
 		dn = x
 	}
-	ctx.inSum--
+	if len(ctx.evalStack) > 1 {
+		ctx.inSum--
+	}
 	return dn.normalize(ctx, x).val
 }
 
@@ -351,10 +356,10 @@
 	var left, right evaluated
 
 	if x.op != opUnify {
-		ctx.exprDepth++
+		ctx.incEvalDepth()
 		left = ctx.manifest(x.left)
 		right = ctx.manifest(x.right)
-		ctx.exprDepth--
+		ctx.decEvalDepth()
 
 		// TODO: allow comparing to a literal bottom only. Find something more
 		// principled perhaps. One should especially take care that two values
diff --git a/cue/go.go b/cue/go.go
index c452228..b2610b6 100644
--- a/cue/go.go
+++ b/cue/go.go
@@ -191,10 +191,7 @@
 	case nil:
 		// Interpret a nil pointer as an undefined value that is only
 		// null by default, but may still be set: *null | _.
-		return &disjunction{values: []dValue{
-			{val: &nullLit{src.base()}, marked: true},
-			{val: &top{src.base()}}},
-		}
+		return makeNullable(&top{src.base()}).(evaluated)
 
 	case *big.Int:
 		n := newNum(src, intKind)
@@ -291,10 +288,7 @@
 			if value.IsNil() {
 				// Interpret a nil pointer as an undefined value that is only
 				// null by default, but may still be set: *null | _.
-				return &disjunction{values: []dValue{
-					{val: &nullLit{src.base()}, marked: true},
-					{val: &top{src.base()}}},
-				}
+				return makeNullable(&top{src.base()}).(evaluated)
 			}
 			return convert(ctx, src, value.Elem().Interface())
 
@@ -387,7 +381,7 @@
 	// TODO: this can be much more efficient.
 	mutex.Lock()
 	defer mutex.Unlock()
-	return goTypeToValue(ctx, t)
+	return goTypeToValue(ctx, true, t)
 }
 
 var (
@@ -400,7 +394,7 @@
 //
 // TODO: if this value will always be unified with a concrete type in Go, then
 // many of the fields may be omitted.
-func goTypeToValue(ctx *context, t reflect.Type) (e value) {
+func goTypeToValue(ctx *context, allowNullDefault bool, t reflect.Type) (e value) {
 	if e, ok := typeCache.Load(t); ok {
 		return e.(value)
 	}
@@ -419,7 +413,10 @@
 		for elem.Kind() == reflect.Ptr {
 			elem = elem.Elem()
 		}
-		e = wrapOrNull(goTypeToValue(ctx, elem))
+		e = goTypeToValue(ctx, false, elem)
+		if allowNullDefault {
+			e = wrapOrNull(e)
+		}
 
 	case reflect.Interface:
 		switch t.Name() {
@@ -477,7 +474,8 @@
 			if f.PkgPath != "" {
 				continue
 			}
-			elem := goTypeToValue(ctx, f.Type)
+			_, ok := f.Tag.Lookup("cue")
+			elem := goTypeToValue(ctx, !ok, f.Type)
 
 			// leave errors like we do during normal evaluation or do we
 			// want to return the error?
@@ -507,7 +505,6 @@
 					// with the constraints from the tags. The type constraints
 					// will be implied when unified with a concrete value.
 					obj.arcs[i].v = mkBin(ctx, 0, opUnify, a.v, v)
-					// obj.arcs[i].v = v
 				}
 			}
 		}
@@ -518,7 +515,7 @@
 		if t.Elem().Kind() == reflect.Uint8 {
 			e = &basicType{k: bytesKind}
 		} else {
-			elem := goTypeToValue(ctx, t.Elem())
+			elem := goTypeToValue(ctx, allowNullDefault, t.Elem())
 
 			var ln value = &top{}
 			if t.Kind() == reflect.Array {
@@ -540,7 +537,7 @@
 		obj := newStruct(baseValue{})
 		sig := &params{}
 		sig.add(ctx.label("_", true), &basicType{k: stringKind})
-		v := goTypeToValue(ctx, t.Elem())
+		v := goTypeToValue(ctx, allowNullDefault, t.Elem())
 		obj.template = &lambdaExpr{params: sig, value: v}
 
 		e = wrapOrNull(obj)
@@ -558,9 +555,17 @@
 	if e.kind().isAnyOf(nullKind) {
 		return e
 	}
-	e = &disjunction{values: []dValue{
-		{val: &nullLit{}, marked: true},
-		{val: e}},
+	return makeNullable(e)
+}
+
+const nullIsDefault = true
+
+func makeNullable(e value) value {
+	return &disjunction{
+		baseValue: baseValue{e},
+		values: []dValue{
+			{val: &nullLit{}, marked: nullIsDefault},
+			{val: e}},
+		hasDefaults: nullIsDefault,
 	}
-	return e
 }
diff --git a/cue/go_test.go b/cue/go_test.go
index 8bb6907..50f9248 100644
--- a/cue/go_test.go
+++ b/cue/go_test.go
@@ -198,7 +198,7 @@
 	for _, tc := range testCases {
 		ctx := inst.newContext()
 		t.Run("", func(t *testing.T) {
-			v := goTypeToValue(ctx, reflect.TypeOf(tc.goTyp))
+			v := goTypeToValue(ctx, true, reflect.TypeOf(tc.goTyp))
 			got := debugStr(ctx, v)
 			if got != tc.want {
 				t.Errorf("\n got %q;\nwant %q", got, tc.want)
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 6cb4050..262af6b 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -423,7 +423,7 @@
 			// All errors are treated the same as per the unification model.
 			i1: [1, 2][3] | "c"
 			`,
-		out: `<0>{o1: (1 | 2 | 3), o2: 1, o3: 2, o4: (1 | *2 | *3), o5: (1 | *2 | 3), o6: (1 | 2 | 3), o7: (2 | 3), o8: (2 | 3), o9: (2 | 3), o10: (3 | *2), m1: (*2 | 3), m2: (*2 | 3), m3: (*2 | 3), m4: (*2 | 3), m5: (*2 | 3), i1: "c"}`,
+		out: `<0>{o1: (1 | 2 | 3), o2: 1, o3: 2, o4: (1 | 2 | 3), o5: (1 | *2 | 3), o6: (1 | 2 | 3), o7: (2 | 3), o8: (2 | 3), o9: (2 | 3), o10: (3 | *2), m1: (*2 | 3), m2: (*2 | 3), m3: (*2 | 3), m4: (*2 | 3), m5: (*2 | 3), i1: "c"}`,
 	}, {
 		desc: "types",
 		in: `
@@ -474,7 +474,9 @@
 
 			c: [c[1], c[0]]
 		`,
-		out: `<0>{a: _|_(cycle detected), b: _|_(cycle detected), c: [_|_(cycle detected),_|_(cycle detected)]}`,
+		out: `<0>{a: _|_((<1>.b - 100):cycle detected), ` +
+			`b: _|_((<1>.a + 100):cycle detected), ` +
+			`c: [_|_(<1>.c[1]:cycle detected),_|_(<1>.c[0]:cycle detected)]}`,
 	}, {
 		desc: "resolved self-reference cycles",
 		in: `
@@ -543,7 +545,7 @@
 			b: *"b" | "a"
 			c: a & b
 			`,
-		out: `<0>{a: "a", b: "b", c: _|_((*"a" | *"b"):more than one default remaining ("a" and "b"))}`,
+		out: `<0>{a: "a", b: "b", c: _|_(("a" | "b"):more than one element remaining ("a" and "b"))}`,
 	}, {
 		desc: "associativity of defaults",
 		in: `
@@ -1039,9 +1041,9 @@
 		b: a & { l: [_, "bar"] }
 		`,
 		out: `<0>{` +
-			`a: <1>{l: ["foo",_|_(cycle detected)], ` +
-			`v: _|_(cycle detected)}, ` +
-			`b: <2>{l: ["foo","bar"], v: "bar"}}`,
+			`a: <1>{l: ["foo",_|_(<2>.v:cycle detected)], ` +
+			`v: _|_(<2>.l[1]:cycle detected)}, ` +
+			`b: <3>{l: ["foo","bar"], v: "bar"}}`,
 	}, {
 		desc: "correct error messages",
 		// Tests that it is okay to partially evaluate structs.
@@ -1361,16 +1363,22 @@
 		// once the user specifies one.
 		desc: "resolved self-reference cycles with disjunction",
 		in: `
-			// The disjunction in xa could be resolved, but as disjunctions
-			// are not resolved for expression, it remains unresolved.
+			// The second disjunct in xa1 is not resolvable and can be
+			// eliminated:
+			//   xa4 & 9
+			//   (xa2 + 2) & 9
+			//   ((xa3 + 2) + 2) & 9
+			//   (((6 & xa1-2) + 2) + 2) & 9
+			//   ((6 + 2) + 2) & 9 // 6 == xa1-2
+			//   10 & 9 => _|_
+			// The remaining values resolve.
 			xa1: (xa2 & 8) | (xa4 & 9)
 			xa2: xa3 + 2
 			xa3: 6 & xa1-2
 			xa4: xa2 + 2
 
-			// As xb3 is a disjunction, xb2 cannot be resolved and evaluating
-			// the cycle completely is broken. However, it is not an error
-			// as the user might still resolve the disjunction.
+			// The second disjunct in xb4 can be eliminated as both disjuncts
+			// of xb3 result in an incompatible sum when substituted.
 			xb1: (xb2 & 8) | (xb4 & 9)
 			xb2: xb3 + 2
 			xb3: (6 & (xb1-2)) | (xb4 & 9)
@@ -1404,14 +1412,11 @@
 			xe5: xe2 + 2
 			xe1: 9
 
-			// TODO: this is resolvable, but requires some detection logic
-			// in disjunctions.
-			// xf1: xf2 & 8 | xf4 & 9
-			// xf2: xf3 + 2
-			// xf3: 6 & xf1-2 | xf4 & 9
-			// xf4: xf2 + 2
-			// xf1: 8
-			// xf3: 6
+			// Only one solution.
+			xf1: xf2 & 8 | xf4 & 9
+			xf2: xf3 + 2
+			xf3: 6 & xf1-2 | xf4 & 9
+			xf4: xf2 + 2
 
 			z1: z2 + 1 | z3 + 5
 			z2: z3 + 2
@@ -1419,17 +1424,17 @@
 			z3: 8
 		`,
 		out: `<0>{` +
-			`xa1: (8 | 9), ` +
-			`xa2: (<1>.xa3 + 2), ` +
-			`xa4: (<1>.xa2 + 2), ` +
-			`xa3: (6 & (<1>.xa1 - 2)), ` +
+			`xa1: 8, ` +
+			`xa2: 8, ` +
+			`xa4: 10, ` +
+			`xa3: 6, ` +
 
-			`xb1: _|_(((<1>.xb2 & 8) | (<1>.xb4 & 9)):empty disjunction: empty disjunction: cycle detected), ` +
-			`xb2: _|_(((6 & (<1>.xb1 - 2)) | (<1>.xb4 & 9)):empty disjunction: cycle detected), ` +
-			`xb4: _|_(((6 & (<1>.xb1 - 2)) | (<1>.xb4 & 9)):empty disjunction: cycle detected), ` +
-			`xb3: _|_(((6 & (<1>.xb1 - 2)) | (<1>.xb4 & 9)):empty disjunction: cycle detected), ` +
+			`xb1: 8, ` +
+			`xb2: 8, ` +
+			`xb4: 10, ` +
+			`xb3: 6, ` +
 
-			`xc1: (8 | 9), ` +
+			`xc1: ((<1>.xc2 & 8) | (<1>.xc4 & 9) | (<1>.xc5 & 9)), ` +
 			`xc2: (<1>.xc3 + 2), ` +
 			`xc4: (<1>.xc2 + 1), ` +
 			`xc5: (<1>.xc2 + 2), ` +
@@ -1441,15 +1446,20 @@
 			`xd5: 10, ` +
 			`xd3: 6, ` +
 
-			`xe1: 9, ` +
+			`xe1: _|_((6 & 7):cannot unify numbers 6 and 7), ` +
 			`xe2: _|_((6 & 7):cannot unify numbers 6 and 7), ` +
 			`xe4: _|_((6 & 7):cannot unify numbers 6 and 7), ` +
 			`xe5: _|_((6 & 7):cannot unify numbers 6 and 7), ` +
 			`xe3: _|_((6 & 7):cannot unify numbers 6 and 7), ` +
 
-			`z1: _|_(((<1>.z2 + 1) | (<1>.z3 + 5)):empty disjunction: cycle detected), ` +
-			`z2: _|_(((<1>.z2 + 1) | (<1>.z3 + 5)):empty disjunction: cycle detected), ` +
-			`z3: _|_(((<1>.z2 + 1) | (<1>.z3 + 5)):empty disjunction: cycle detected)}`,
+			`xf1: 8, ` +
+			`xf2: 8, ` +
+			`xf4: 10, ` +
+			`xf3: 6, ` +
+
+			`z1: ((<1>.z2 + 1) | (<1>.z3 + 5)), ` +
+			`z2: (<1>.z3 + 2), ` +
+			`z3: ((<1>.z1 - 3) & 8)}`,
 	}, {
 		// Defaults should not alter the result of the above disjunctions.
 		// The results may differ, but errors and resolution should be roughly
@@ -1458,7 +1468,7 @@
 		in: `
 			// The disjunction in xa could be resolved, but as disjunctions
 			// are not resolved for expression, it remains unresolved.
-			xa1: *(xa2 & 8) | (xa4 & 9)
+			xa1: (xa2 & 8) | *(xa4 & 9)
 			xa2: xa3 + 2
 			xa3: 6 & xa1-2
 			xa4: xa2 + 2
@@ -1476,7 +1486,7 @@
 			// However, to fully determine that, all options of the remaining
 			// disjunction will have to be evaluated algebraically, which is
 			// not done.
-			xc1: *(xc2 & 8) | xc4 & 9 | xc5 & 9
+			xc1: *(xc2 & 8) | (xc4 & 9) | (xc5 & 9)
 			xc2: xc3 + 2
 			xc3: 6 & xc1-2
 			xc4: xc2 + 1
@@ -1488,7 +1498,6 @@
 			xd3: 6 & xd1-2
 			xd4: xd2 + 1
 			xd5: xd2 + 2
-			xd1: 8
 
 			// The above is resolved by setting xd1 explicitly to the wrong
 			// value, resulting in an error.
@@ -1505,37 +1514,37 @@
 			z3: 8
 		`,
 		out: `<0>{` +
-			`xa1: (*8 | 9), ` + // TODO: should resolve. Just testing logic, so okay for now.
+			`xa1: 8, ` +
 			`xa2: 8, ` +
 			`xa4: 10, ` +
 			`xa3: 6, ` +
 
-			`xb1: _|_((*(<1>.xb2 & 8) | (<1>.xb4 & 9)):empty disjunction: empty disjunction: cycle detected), ` +
-			`xb2: _|_((*(6 & (<1>.xb1 - 2)) | (<1>.xb4 & 9)):empty disjunction: cycle detected), ` +
-			`xb4: _|_((*(6 & (<1>.xb1 - 2)) | (<1>.xb4 & 9)):empty disjunction: cycle detected), ` +
-			`xb3: _|_((*(6 & (<1>.xb1 - 2)) | (<1>.xb4 & 9)):empty disjunction: cycle detected), ` +
+			`xb1: 8, ` +
+			`xb2: 8, ` +
+			`xb4: 10, ` +
+			`xb3: 6, ` +
 
-			`xc1: (*8 | 9), ` + // TODO: resolve
+			`xc1: (*8 | 9), ` + // not resolved because we use evalPartial
 			`xc2: 8, ` +
 			`xc4: 9, ` +
 			`xc5: 10, ` +
 			`xc3: 6, ` +
 
-			`xd1: 8, ` +
+			`xd1: (*8 | 9), ` + // TODO: eliminate 9?
 			`xd2: 8, ` +
 			`xd4: 9, ` +
 			`xd5: 10, ` +
 			`xd3: 6, ` +
 
-			`xe1: 9, ` +
+			`xe1: _|_((6 & 7):cannot unify numbers 6 and 7), ` +
 			`xe2: _|_((6 & 7):cannot unify numbers 6 and 7), ` +
 			`xe4: _|_((6 & 7):cannot unify numbers 6 and 7), ` +
 			`xe5: _|_((6 & 7):cannot unify numbers 6 and 7), ` +
 			`xe3: _|_((6 & 7):cannot unify numbers 6 and 7), ` +
 
-			`z1: _|_((*(<1>.z2 + 1) | (<1>.z3 + 5)):empty disjunction: cycle detected), ` +
-			`z2: _|_((*(<1>.z2 + 1) | (<1>.z3 + 5)):empty disjunction: cycle detected), ` +
-			`z3: _|_((*(<1>.z2 + 1) | (<1>.z3 + 5)):empty disjunction: cycle detected)}`,
+			`z1: (*11 | 13), ` + // 13 is eliminated with evalFull
+			`z2: 10, ` +
+			`z3: 8}`,
 	}}
 	rewriteHelper(t, testCases, evalPartial)
 }
@@ -1716,7 +1725,7 @@
 		`,
 		out: `<0>{a: string, b: int, c: float}`,
 	}, {
-		desc: "default disambiguation",
+		desc: "default disambiguation and elimination",
 		in: `
 		a: *1 | int
 		b: *3 | int
@@ -1725,7 +1734,7 @@
 
 		e: *1 | *1
 		`,
-		out: `<0>{a: 1, b: 3, c: _|_((*1 | *3 | int):more than one default remaining (1 and 3)), d: _|_((*3 | *1 | int):more than one default remaining (3 and 1)), e: 1}`,
+		out: `<0>{a: 1, b: 3, c: int, d: int, e: 1}`,
 	}, {
 		desc: "list comprehension",
 		in: `
@@ -1884,6 +1893,29 @@
 			y: x & {a:3}
 		`,
 		out: `<3>{x: _|_((<0>{a: 1} | <1>{a: 2}):more than one element remaining (<0>{a: 1} and <1>{a: 2})), y: _|_((<4>.x & <5>{a: 3}):empty disjunction: <2>{a: (1 & 3)})}`,
+	}, {
+		desc: "cannot resolve references that would be ambiguous",
+		in: `
+		a1: *0 | 1
+		a1: a3 - a2
+		a2: *0 | 1
+		a2: a3 - a1
+		a3: 1
+
+		b1: (*0 | 1) & b2
+		b2: (0 | *1) & b1
+
+		c1: (*{a:1} | {b:1}) & c2
+		c2: (*{a:2} | {b:2}) & c1
+		`,
+		out: `<4>{` +
+			`a1: _|_(((*0 | 1) & (<5>.a3 - <5>.a2)):cycle detected), ` +
+			`a3: 1, ` +
+			`a2: _|_(((*0 | 1) & (<5>.a3 - <5>.a1)):cycle detected), ` +
+			`b1: _|_((0 | 1):more than one element remaining (0 and 1)), ` +
+			`b2: _|_((0 | 1):more than one element remaining (0 and 1)), ` +
+			`c1: _|_((<0>{a: 1, b: 2} | <1>{a: 2, b: 1}):more than one element remaining (<0>{a: 1, b: 2} and <1>{a: 2, b: 1})), ` +
+			`c2: _|_((<2>{a: 2, b: 1} | <3>{a: 1, b: 2}):more than one element remaining (<2>{a: 2, b: 1} and <3>{a: 1, b: 2}))}`,
 	}}
 	rewriteHelper(t, testCases, evalFull)
 }
diff --git a/cue/rewrite.go b/cue/rewrite.go
index eda68c4..44dc982 100644
--- a/cue/rewrite.go
+++ b/cue/rewrite.go
@@ -195,7 +195,7 @@
 	if !changed {
 		return x
 	}
-	return &disjunction{x.baseValue, values}
+	return &disjunction{x.baseValue, values, x.hasDefaults}
 }
 
 func (x *listComprehension) rewrite(ctx *context, fn rewriteFunc) value {
diff --git a/cue/validate.go b/cue/validate.go
index 0379d71..15c2e23 100644
--- a/cue/validate.go
+++ b/cue/validate.go
@@ -17,7 +17,7 @@
 // validate returns whether there is any error, recursively.
 func validate(ctx *context, v value) *bottom {
 	eval := v.evalPartial(ctx)
-	if err, ok := eval.(*bottom); ok && err.code != codeIncomplete {
+	if err, ok := eval.(*bottom); ok && err.code != codeIncomplete && err.code != codeCycle {
 		return eval.(*bottom)
 	}
 	switch x := eval.(type) {
diff --git a/cue/value.go b/cue/value.go
index 36268f4..67944a2 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -677,18 +677,26 @@
 	// Lookup is done by selector or index references. Either this is done on
 	// literal nodes or nodes obtained from references. In the later case,
 	// noderef will have ensured that the ancestors were evaluated.
-	if x.arcs[i].cache == nil {
+	if v := x.arcs[i].cache; v == nil {
 
 		// cycle detection
-		x.arcs[i].cache = cycleSentinel
 
-		ctx.evalDepth++
+		popped := ctx.evalStack
+		ctx.evalStack = append(ctx.evalStack, bottom{
+			baseValue: x.base(),
+			index:     ctx.index,
+			code:      codeCycle,
+			value:     x.arcs[i].v,
+			msg:       "cycle detected",
+		})
+		x.arcs[i].cache = &(ctx.evalStack[len(ctx.evalStack)-1])
+
 		v := x.arcs[i].v.evalPartial(ctx)
-		ctx.evalDepth--
+		ctx.evalStack = popped
 
 		v = x.applyTemplate(ctx, i, v)
 
-		if (ctx.evalDepth > 0 && ctx.cycleErr) || cycleError(v) != nil {
+		if (len(ctx.evalStack) > 0 && ctx.cycleErr) || cycleError(v) != nil {
 			// Don't cache while we're in a evaluation cycle as it will cache
 			// partial results. Each field involved in the cycle will have to
 			// reevaluated the values from scratch. As the result will be
@@ -702,11 +710,14 @@
 		ctx.cycleErr = false
 
 		x.arcs[i].cache = v
-		if ctx.evalDepth == 0 {
+		if len(ctx.evalStack) == 0 {
 			if err := ctx.processDelayedConstraints(); err != nil {
 				x.arcs[i].cache = err
 			}
 		}
+	} else if b := cycleError(v); b != nil {
+		copy := *b
+		return &copy
 	}
 	return x.arcs[i].cache
 }
@@ -1070,6 +1081,8 @@
 
 	values []dValue
 
+	hasDefaults bool // also true if it had elminated defaults.
+
 	// bind is the node that a successful disjunction will bind to. This
 	// allows other arcs to point to this node before the disjunction is
 	// completed. For early failure, this node can be set to the glb of all
diff --git a/cuego/cuego.go b/cuego/cuego.go
index aaa8cc3..7e799c9 100644
--- a/cuego/cuego.go
+++ b/cuego/cuego.go
@@ -168,7 +168,7 @@
 	context := build.NewContext()
 	fset = context.FileSet()
 	inst := context.NewInstance("<cuego>", nil)
-	if err := inst.AddFile("<ceugo>", "{}"); err != nil {
+	if err := inst.AddFile("<cuego>", "{}"); err != nil {
 		panic(err)
 	}
 	instance = cue.Build([]*build.Instance{inst})[0]