cue: implementation of marked defaults

Change-Id: I7b0ed2b1e372a71410cbd05253ca819f08509a2a
diff --git a/cmd/cue/cmd/testdata/trim/trim.cue b/cmd/cue/cmd/testdata/trim/trim.cue
index db0ca2e..362e3c5 100644
--- a/cmd/cue/cmd/testdata/trim/trim.cue
+++ b/cmd/cue/cmd/testdata/trim/trim.cue
@@ -14,7 +14,7 @@
 
 	struct: {a: 3.0}
 
-	sList: [{a: 8, b: string}, {a: 9, b: "foo" | string}]
+	sList: [{a: 8, b: string}, {a: 9, b: *"foo" | string}]
 	rList: [{a: "a"}]
 	rcList: [{a: "a", c: b}]
 
diff --git a/cmd/cue/cmd/testdata/trim/trim.out b/cmd/cue/cmd/testdata/trim/trim.out
index 70677ac..862b8a4 100644
--- a/cmd/cue/cmd/testdata/trim/trim.out
+++ b/cmd/cue/cmd/testdata/trim/trim.out
@@ -14,7 +14,7 @@
 
 	struct: {a: 3.0}
 
-	sList: [{a: 8, b: string}, {a: 9, b: "foo" | string}]
+	sList: [{a: 8, b: string}, {a: 9, b: *"foo" | string}]
 	rList: [{a: "a"}]
 	rcList: [{a: "a", c: b}]
 
diff --git a/cue/ast.go b/cue/ast.go
index 43a3616..013b119 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -457,6 +457,9 @@
 		value = call
 
 	case *ast.UnaryExpr:
+		if n.Op == token.MUL {
+			return v.error(n, "preference mark not allowed at this position")
+		}
 		value = &unaryExpr{
 			newExpr(n),
 			tokenMap[n.Op],
@@ -466,7 +469,10 @@
 	case *ast.BinaryExpr:
 		switch n.Op {
 		case token.DISJUNCTION:
-			value = makeDisjunction(v.ctx(), n, v.walk(n.X), v.walk(n.Y))
+			d := &disjunction{baseValue: newExpr(n)}
+			v.addDisjunctionElem(d, n.X, false)
+			v.addDisjunctionElem(d, n.Y, false)
+			value = d
 		case token.RANGE:
 			value = &rangeLit{
 				newExpr(n),
@@ -493,6 +499,23 @@
 	return value
 }
 
+func (v *astVisitor) addDisjunctionElem(d *disjunction, n ast.Node, mark bool) {
+	switch x := n.(type) {
+	case *ast.BinaryExpr:
+		if x.Op == token.DISJUNCTION {
+			v.addDisjunctionElem(d, x.X, mark)
+			v.addDisjunctionElem(d, x.Y, mark)
+			return
+		}
+	case *ast.UnaryExpr:
+		if x.Op == token.MUL {
+			mark = true
+			n = x.X
+		}
+	}
+	d.values = append(d.values, dValue{v.walk(n), mark})
+}
+
 func wrapClauses(v *astVisitor, y yielder, clauses []ast.Clause) yielder {
 	for _, c := range clauses {
 		if n, ok := c.(*ast.ForClause); ok {
diff --git a/cue/ast_test.go b/cue/ast_test.go
index f9bdd8e..8ae1aff 100644
--- a/cue/ast_test.go
+++ b/cue/ast_test.go
@@ -137,12 +137,13 @@
 	}, {
 		in: `
 		a: 5 | "a" | true
+		aa: 5 | *"a" | true
 		b c: {
 			cc: { ccc: 3 }
 		}
 		d: true
 		`,
-		out: "<0>{a: (5 | \"a\" | true), b: <1>{c: <2>{cc: <3>{ccc: 3}}}, d: true}",
+		out: "<0>{a: (5 | \"a\" | true), aa: (5 | *\"a\" | true), b: <1>{c: <2>{cc: <3>{ccc: 3}}}, d: true}",
 	}, {
 		in: `
 		a a: { b: a } // referencing ancestor nodes is legal.
@@ -232,6 +233,13 @@
 			d: (2+3)..(4+5)
 			`,
 		out: `<0>{a: (1..2), b: ((1..2)..3), c: ("a".."b"), d: ((2 + 3)..(4 + 5))}`,
+	}, {
+		in: `
+			a: *1,
+			b: **1 | 2
+		`,
+		out: `<0>{a: _|_(preference mark not allowed at this position), ` +
+			`b: (*_|_(preference mark not allowed at this position) | 2)}`,
 	}}
 	for _, tc := range testCases {
 		t.Run("", func(t *testing.T) {
@@ -276,7 +284,7 @@
 			a: 8000 | 7080
 			a: 7080 | int
 		}`,
-		out: `<0>{a: _|_((8000! | 7080! | 7080):ambiguous disjunction)}`,
+		out: `<0>{a: _|_((8000 | 7080):more than one element remaining (8000 and 7080))}`,
 		rw:  evalFull,
 	}}
 	for _, tc := range testCases {
diff --git a/cue/binop.go b/cue/binop.go
index 30c99bb..dec382e 100644
--- a/cue/binop.go
+++ b/cue/binop.go
@@ -31,12 +31,6 @@
 	return baseValue{&computedSource{pos, op, a, b}}
 }
 
-type binFunc func(ctx *context, src source, op op, left, right evaluated) evaluated
-
-func binSwap(ctx *context, src source, op op, x, y evaluated) evaluated {
-	return binOp(ctx, src, op, y, x)
-}
-
 func unify(ctx *context, src source, left, right evaluated) evaluated {
 	return binOp(ctx, src, opUnify, left, right)
 }
@@ -65,17 +59,6 @@
 
 	// op == opUnify
 
-	// Evaluated and recompose disjunctions.
-
-	if dl, ok := left.(*disjunction); ok {
-		if dr, ok := right.(*disjunction); ok {
-			return dl.cross(ctx, src, op, dr)
-		}
-		return dl.expand(ctx, src, op, right, binOp)
-	} else if dr, ok := right.(*disjunction); ok {
-		return dr.expand(ctx, src, op, left, binSwap)
-	}
-
 	// TODO: unify type masks.
 	if left == right {
 		return left
@@ -87,6 +70,12 @@
 		return left
 	}
 
+	if dl, ok := left.(*disjunction); ok {
+		return distribute(ctx, src, dl, right)
+	} else if dr, ok := right.(*disjunction); ok {
+		return distribute(ctx, src, dr, left)
+	}
+
 	// TODO: value may be incomplete if there is a cycle. Instead of an error
 	// schedule an assert and return the atomic value, if applicable.
 	v := left.binOp(ctx, src, op, right)
@@ -101,48 +90,35 @@
 	return v
 }
 
-func (x *disjunction) expand(ctx *context, src source, op op, y evaluated, fn binFunc) evaluated {
-	// disjunction & single value
-	dn := disjunction{src.base(), make([]dValue, 0, len(x.values))}
-	changed := false
-	for _, v := range x.values {
-		e := fn(ctx, src, op, v.val.(evaluated), y)
-		changed = changed || e != v.val
-		dn.add(ctx, e, v.ambiguous)
-	}
-	if !changed {
-		return x
-	}
-	return dn.simplify(ctx, binSrc(src.Pos(), op, x, y)).(evaluated)
+type mVal struct {
+	val  evaluated
+	mark bool
 }
 
-func (x *disjunction) cross(ctx *context, src source, op op, y *disjunction) evaluated {
-	vr := []dValue{}
-	idx := make([]int, 0, len(y.values))
-	for _, va := range x.values {
-		for j, vb := range y.values {
-			e := binOp(ctx, src, op, va.val.(evaluated), vb.val.(evaluated))
-			if isBottom(e) {
-				continue
-			}
+// 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
+}
 
-			// TODO: filter using subsumption.
+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)
 
-			// Mark crossing values as ambiguous.
-			ambiguous := va.ambiguous || vb.ambiguous
-			for xi, x := range idx {
-				if x > j {
-					vr[xi].ambiguous = true
-					ambiguous = true
-				}
-			}
-			vr = append(vr, dValue{e, ambiguous})
-			idx = append(idx, j)
+		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}
 		}
+		dn.add(ctx, v.val, v.mark)
 	}
-
-	d := &disjunction{baseValue: src.base(), values: vr}
-	return d.simplify(ctx, binSrc(src.Pos(), op, x, y)).(evaluated)
+	return dn.normalize(ctx, src)
 }
 
 func (x *disjunction) binOp(ctx *context, src source, op op, other evaluated) evaluated {
diff --git a/cue/debug.go b/cue/debug.go
index 91156eb..c51cc1d 100644
--- a/cue/debug.go
+++ b/cue/debug.go
@@ -210,10 +210,10 @@
 			if i != 0 {
 				writef(" | ")
 			}
-			p.debugStr(v.val)
-			if v.ambiguous {
-				writef("!")
+			if v.marked {
+				writef("*")
 			}
+			p.debugStr(v.val)
 		}
 		write(")")
 	case *lambdaExpr:
diff --git a/cue/eval.go b/cue/eval.go
index 02c1117..e3ca92d 100644
--- a/cue/eval.go
+++ b/cue/eval.go
@@ -313,33 +313,51 @@
 	for _, v := range x.values {
 		n := v.val.evalPartial(ctx)
 		changed = changed || n != v.val
-		dn.add(ctx, n, v.ambiguous)
+		dn.add(ctx, n, v.marked)
 	}
 	// TODO: move to evaluator
 	if !changed {
 		return x
 	}
-	return dn.simplify(ctx, x).(evaluated)
+	return dn.normalize(ctx, x).val
 }
 
 func (x *disjunction) manifest(ctx *context) (result evaluated) {
-	switch len(x.values) {
-	case 0:
-		return x.simplify(ctx, x).(evaluated) // force error
-	case 1:
-		return x.values[0].val.(evaluated)
-	default:
-		for _, d := range x.values {
-			if validate(ctx, d.val) != nil {
-				continue
-			}
-			if d.ambiguous {
-				// better error
-				return ctx.mkErr(x, "ambiguous disjunction")
-			}
-			return d.val.(evaluated)
+	var err, marked, unmarked1, unmarked2 evaluated
+	for _, d := range x.values {
+		// Because of the lazy evaluation strategy, we may still have
+		// latent unification.
+		if err := validate(ctx, d.val); err != nil {
+			continue
 		}
-		return ctx.mkErr(x, "empty disjunction after evaluation")
+		switch {
+		case d.marked:
+			if marked != nil {
+				return ctx.mkErr(x, "more than one default remaining (%v and %v)", debugStr(ctx, marked), debugStr(ctx, d.val))
+			}
+			marked = d.val.(evaluated)
+		case unmarked1 == nil:
+			unmarked1 = d.val.(evaluated)
+		default:
+			unmarked2 = d.val.(evaluated)
+		}
+	}
+	switch {
+	case marked != nil:
+		return marked
+
+	case unmarked2 != nil:
+		return ctx.mkErr(x, "more than one element remaining (%v and %v)",
+			debugStr(ctx, unmarked1), debugStr(ctx, unmarked2))
+
+	case unmarked1 != nil:
+		return unmarked1
+
+	case err != nil:
+		return err
+
+	default:
+		return ctx.mkErr(x, "empty disjunction")
 	}
 }
 
diff --git a/cue/export.go b/cue/export.go
index 6774be7..15c0b04 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -103,20 +103,22 @@
 		if len(x.values) == 1 {
 			return p.expr(x.values[0].val)
 		}
+		expr := func(v dValue) ast.Expr {
+			e := p.expr(v.val)
+			if v.marked {
+				e = &ast.UnaryExpr{Op: token.MUL, X: e}
+			}
+			return e
+		}
 		bin := &ast.BinaryExpr{
-			X:  p.expr(x.values[0].val),
+			X:  expr(x.values[0]),
 			Op: token.DISJUNCTION,
-			Y:  p.expr(x.values[1].val),
+			Y:  expr(x.values[1]),
 		}
 		for _, v := range x.values[2:] {
-			bin = &ast.BinaryExpr{X: bin, Op: token.DISJUNCTION, Y: p.expr(v.val)}
+			bin = &ast.BinaryExpr{X: bin, Op: token.DISJUNCTION, Y: expr(v)}
 		}
 		return bin
-	// case *lambdaExpr:
-
-	// 	p.debugStr(x.params.arcs)
-	// 	write(")->")
-	// 	p.debugStr(x.value)
 
 	case *structLit:
 		obj := &ast.StructLit{}
diff --git a/cue/export_test.go b/cue/export_test.go
index 1b8701a..f5dd274 100644
--- a/cue/export_test.go
+++ b/cue/export_test.go
@@ -113,10 +113,10 @@
 			}`),
 	}, {
 		raw: true,
-		in:  `{ a: "foo" | "bar" | string, b: a[2:3] }`,
+		in:  `{ a: *"foo" | *"bar" | *string | int, b: a[2:3] }`,
 		out: unindent(`
 			{
-				a: "foo" | "bar" | string
+				a: *"foo" | *"bar" | *string | int
 				b: a[2:3]
 			}`),
 	}, {
diff --git a/cue/parser/parser.go b/cue/parser/parser.go
index f6ed64b..13870ec 100644
--- a/cue/parser/parser.go
+++ b/cue/parser/parser.go
@@ -1109,7 +1109,7 @@
 	}
 
 	switch p.tok {
-	case token.ADD, token.SUB, token.NOT:
+	case token.ADD, token.SUB, token.NOT, token.MUL:
 		pos, op := p.pos, p.tok
 		c := p.openComments()
 		p.next()
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index b8108fe..aab3081 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -269,19 +269,39 @@
 		in: `
 			o1: 1 | 2 | 3
 			o2: (1 | 2 | 3) & 1
-			o3: 2 & (1 | 2 | 3)
-			o4: (1 | 2 | 3) & (1 | 2 | 3)
-			o5: (1 | 2 | 3) & (3 | 2 | 1)
+			o3: 2 & (1 | *2 | 3)
+			o4: (1 | *2 | 3) & (1 | 2 | *3)
+			o5: (1 | *2 | 3) & (3 | *2 | 1)
 			o6: (1 | 2 | 3) & (3 | 1 | 2)
 			o7: (1 | 2 | 3) & (2 | 3)
 			o8: (1 | 2 | 3) & (3 | 2)
 			o9: (2 | 3) & (1 | 2 | 3)
-			o10: (3 | 2) & (1 | 2 | 3)
+			o10: (3 | 2) & (1 | *2 | 3)
+
+			m1: (*1 | (*2 | 3)) & 2..3
+			m2: (*1 | (*2 | 3)) & (2 | 3)
+			m3: (*1 | *(*2 | 3)) & (2 | 3)
+			m4: (2 | 3) & (*2 | 3)
+			m5: (*2 | 3) & (2 | 3)
+
+			// (*2 | 3) & (2 | 3)
+			// (2 | 3) & (*2 | 3)
+			// 2&(*2 | 3) | 3&(*2 | 3)
+			// (*1 | (*2 | 3)) & (2 | 3)
+			// *1& (2 | 3) | (*2 | 3)&(2 | 3)
+			// *2&(2 | 3) | 3&(2 | 3)
+
+			// (2 | 3)&(*1 | (*2 | 3))
+			// 2&(*1 | (*2 | 3)) | 3&(*1 | (*2 | 3))
+			// *1&2 | (*2 | 3)&2 | *1&3 | (*2 | 3)&3
+			// (*2 | 3)&2 | (*2 | 3)&3
+			// *2 | 3
+
 
 			// 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!), 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: `
@@ -365,8 +385,8 @@
 	testCases := []testCase{{
 		desc: "pick first",
 		in: `
-		a: 5 | "a" | true
-		b c: {
+		a: *5 | "a" | true
+		b c: *{
 			a: 2
 		} | {
 			a : 3
@@ -376,19 +396,19 @@
 	}, {
 		desc: "simple disambiguation conflict",
 		in: `
-			a: "a" | "b"
-			b: "b" | "a"
+			a: *"a" | "b"
+			b: *"b" | "a"
 			c: a & b
 			`,
-		out: `<0>{a: "a", b: "b", c: _|_(("a"! | "b"!):ambiguous disjunction)}`,
+		out: `<0>{a: "a", b: "b", c: _|_((*"a" | *"b"):more than one default remaining ("a" and "b"))}`,
 	}, {
 		desc: "disambiguation non-conflict",
 		in: `
-			a: "a" | ("b" | "c")
-			b: ("a" | "b") | "c"
+			a: *"a" | ("b" | "c")
+			b: (*"a" | "b") | "c"
 			c: a & b
 			`,
-		out: `<0>{a: "a", b: "a", c: "a"}`,
+		out: `<0>{a: "a", b: _|_(((*"a" | "b") | "c"):more than one element remaining ((*"a" | "b") and "c")), c: "a"}`,
 	}}
 	rewriteHelper(t, testCases, evalFull)
 }
@@ -451,15 +471,15 @@
 		in: `
 			a: [2][0]
 			b: {foo:"bar"}["foo"]
-			c: (l|{"3":3})["3"]
-			d: ([]|[1])[0]
+			c: (*l|{"3":3})["3"]
+			d: (*[]|[1])[0]
 			l: []
 			e1: [2][""]
 			e2: 2[2]
 			e3: [][true]
 			e4: [1,2,3][3]
 			e5: [1,2,3][-1]
-			e6: ([]|{})[1]
+			e6: (*[]|{})[1]
 		`,
 		out: `<0>{a: 2, b: "bar", c: _|_("3":invalid list index "3" (type string)), l: [], d: _|_([]:index 0 out of bounds), e1: _|_("":invalid list index "" (type string)), e2: _|_(2:invalid operation: 2[2] (type number does not support indexing)), e3: _|_(true:invalid list index true (type bool)), e4: _|_([1,2,3]:index 3 out of bounds), e5: _|_(-1:invalid list index -1 (index must be non-negative)), e6: _|_([]:index 1 out of bounds)}`,
 	}, {
@@ -810,19 +830,19 @@
 		in: `
 				a: 8000.9
 				a: 7080 | int`,
-		out: `<0>{a: _|_(empty disjunction after evaluation)}`,
+		out: `<0>{a: _|_((8000.9 & (7080 | int)):empty disjunction: cannot unify numbers 7080 and 8000.9)}`,
 	}, {
 		desc: "resolve all disjunctions",
 		in: `
 			service <Name>: {
-				name: Name | string
-				port: 7080 | int
+				name: string | *Name
+				port: int | *7080
 			}
 			service foo: _
 			service bar: { port: 8000 }
 			service baz: { name: "foobar" }
 			`,
-		out: `<0>{service: <1>{<>: <2>(Name: string)-><3>{name: (<2>.Name | string), port: (7080 | int)}, foo: <4>{name: "foo", port: 7080}, bar: <5>{name: "bar", port: 8000}, baz: <6>{name: "foobar", port: 7080}}}`,
+		out: `<0>{service: <1>{<>: <2>(Name: string)-><3>{name: (string | *<2>.Name), port: (int | *7080)}, foo: <4>{name: "foo", port: 7080}, bar: <5>{name: "bar", port: 8000}, baz: <6>{name: "foobar", port: 7080}}}`,
 	}, {
 		desc: "field templates",
 		in: `
@@ -831,7 +851,7 @@
 				k: 1
 			}
 			b: {
-				<x>: { x: 0, y: 1 | int }
+				<x>: { x: 0, y: *1 | int }
 				v: {}
 				w: { y: 0 }
 			}
@@ -842,7 +862,7 @@
 				bar: _
 			}
 			`,
-		out: `<0>{a: <1>{<>: <2>(name: string)->int, k: 1}, b: <3>{<>: <4>(x: string)->(<5>{x: 0, y: (1 | int)} & <6>{}), v: <7>{x: 0, y: 1}, w: <8>{x: 0, y: 0}}, c: <9>{<>: <10>(Name: string)-><11>{name: <10>.Name, y: 1}, foo: <12>{name: "foo", y: 1}, bar: <13>{name: "bar", y: 1}}}`,
+		out: `<0>{a: <1>{<>: <2>(name: string)->int, k: 1}, b: <3>{<>: <4>(x: string)->(<5>{x: 0, y: (*1 | int)} & <6>{}), v: <7>{x: 0, y: 1}, w: <8>{x: 0, y: 0}}, c: <9>{<>: <10>(Name: string)-><11>{name: <10>.Name, y: 1}, foo: <12>{name: "foo", y: 1}, bar: <13>{name: "bar", y: 1}}}`,
 	}, {
 		desc: "field comprehension",
 		in: `
@@ -952,7 +972,7 @@
 	}, {
 		desc: "disjunctions of lists",
 		in: `
-			l: [ int, int ] | [ string, string ]
+			l: *[ int, int ] | [ string, string ]
 
 			l1: [ "a", "b" ]
 			l2: l & [ "c", "d" ]
@@ -980,8 +1000,8 @@
 
 			service <Name>: {
 				type: "service"
-				name: Name | string
-				port: 7080 | int
+				name: *Name | string
+				port: *7080 | int
 			}
 			service foo: {}
 			service bar: { port: 8000 }
@@ -993,7 +1013,7 @@
 			`<3>{type: "service", name: "foobar", port: 7080}], ` +
 
 			`service: <4>{` +
-			`<>: <5>(Name: string)-><6>{type: "service", name: (<5>.Name | string), port: (7080 | int)}, ` +
+			`<>: <5>(Name: string)-><6>{type: "service", name: (*<5>.Name | string), port: (*7080 | int)}, ` +
 			`foo: <7>{type: "service", name: "foo", port: 7080}, ` +
 			`bar: <8>{type: "service", name: "bar", port: 8000}, ` +
 			`baz: <9>{type: "service", name: "foobar", port: 7080}}}`,
@@ -1026,7 +1046,7 @@
 		IP: 4*[ 0..255 ]
 
 		Private:
-			[ 192, 168, 0..255, 0..255 ] |
+			*[ 192, 168, 0..255, 0..255 ] |
 			[ 10, 0..255, 0..255, 0..255] |
 			[ 172, 16..32, 0..255, 0..255 ]
 
diff --git a/cue/rewrite.go b/cue/rewrite.go
index b3b328f..6e28522 100644
--- a/cue/rewrite.go
+++ b/cue/rewrite.go
@@ -182,7 +182,7 @@
 	changed := false
 	for i, d := range x.values {
 		v := rewrite(ctx, d.val, fn)
-		values[i] = dValue{v, d.ambiguous}
+		values[i] = dValue{v, d.marked}
 		changed = changed || v != d.val
 	}
 	if !changed {
diff --git a/cue/value.go b/cue/value.go
index 95abb6c..4397b74 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -1022,22 +1022,8 @@
 }
 
 type dValue struct {
-	val       value
-	ambiguous bool
-}
-
-// makeDisjunction constructs a disjunction linked list.
-func makeDisjunction(ctx *context, n ast.Expr, a, b value) *disjunction {
-	d, ok := a.(*disjunction)
-	if !ok {
-		d = &disjunction{newExpr(n), []dValue{{val: a}}}
-	}
-	if o, ok := b.(*disjunction); ok {
-		d.values = append(d.values, o.values...)
-	} else {
-		d.values = append(d.values, dValue{val: b})
-	}
-	return d
+	val    value
+	marked bool
 }
 
 func (x *disjunction) kind() kind {
@@ -1054,21 +1040,58 @@
 func (x *disjunction) Pos() token.Pos { return x.values[0].val.Pos() }
 
 // add add a value to the disjunction. It is assumed not to be a disjunction.
-func (x *disjunction) add(ctx *context, v value, ambiguous bool) {
-	if !isBottom(v) {
-		x.values = append(x.values, dValue{v, ambiguous})
-	}
+func (x *disjunction) add(ctx *context, v value, marked bool) {
+	x.values = append(x.values, dValue{v, marked})
 }
 
-// simplify unwraps the disjunction if necessary.
-func (x *disjunction) simplify(ctx *context, src source) value {
-	switch len(x.values) {
-	case 0:
-		return ctx.mkErr(src, "empty disjunction after evaluation")
-	case 1:
-		return x.values[0].val
+// normalize removes redundant element from unification.
+// x must already have been evaluated.
+func (x *disjunction) normalize(ctx *context, src source) mVal {
+	less := func(ctx *context, lt, gt dValue) bool {
+		if isBottom(lt.val) {
+			return true
+		}
+		return (!lt.marked || gt.marked) && subsumes(ctx, gt.val, lt.val, 0)
 	}
-	return x
+	k := 0
+outer:
+	for i, v := range x.values {
+		if isBottom(v.val) {
+			continue
+		}
+		for j, w := range x.values {
+			if i == j {
+				continue
+			}
+			if less(ctx, v, w) && (!less(ctx, w, v) || j < i) {
+				// strictly subsumed, or equal and and the equal element was
+				// processed earlier.
+				continue outer
+			}
+		}
+		// If there was a three-way equality, an element w, where w == v could
+		// already have been added.
+		for j := 0; j < k; j++ {
+			if less(ctx, v, x.values[j]) {
+				continue outer
+			}
+		}
+		x.values[k] = v
+		k++
+	}
+
+	switch k {
+	case 0:
+		// Empty disjunction. All elements must be errors.
+		// Take the first error as an example.
+		str := fmt.Sprintf("empty disjunction: %v", x.values[0].val)
+		return mVal{ctx.mkErr(src, str), false}
+	case 1:
+		v := x.values[0]
+		return mVal{v.val.(evaluated), v.marked}
+	}
+	x.values = x.values[:k]
+	return mVal{x, false}
 }
 
 type listComprehension struct {
diff --git a/doc/tutorial/basics/coalesce.md b/doc/tutorial/basics/coalesce.md
index 082708a..dde8c77 100644
--- a/doc/tutorial/basics/coalesce.md
+++ b/doc/tutorial/basics/coalesce.md
@@ -29,11 +29,11 @@
 ```
 list: [ "Cat", "Mouse", "Dog" ]
 
-a: list[0] | "None"
-b: list[5] | "None"
+a: *list[0] | "None"
+b: *list[5] | "None"
 
 n: [null]
-v: n[0] & string | "default"
+v: *n[0] & string | "default"
 ```
 
 <!-- result -->
diff --git a/doc/tutorial/basics/defaults.md b/doc/tutorial/basics/defaults.md
index a46825d..c65cb67 100644
--- a/doc/tutorial/basics/defaults.md
+++ b/doc/tutorial/basics/defaults.md
@@ -19,11 +19,11 @@
 <!-- CUE editor -->
 ```
 // any positive number, 1 is the default
-replicas: 1 | uint
+replicas: uint | *1
 
 // the default value is ambiguous
-protocol: "tcp" | "udp"
-protocol: "udp" | "tcp"
+protocol: *"tcp" | "udp"
+protocol: *"udp" | "tcp"
 ```
 
 <!-- result -->
diff --git a/doc/tutorial/basics/templates.md b/doc/tutorial/basics/templates.md
index 3748cdb..bd40c2a 100644
--- a/doc/tutorial/basics/templates.md
+++ b/doc/tutorial/basics/templates.md
@@ -18,7 +18,7 @@
 // The name of each element is bound to Name and visible in the struct.
 job <Name>: {
     name:     Name
-    replicas: 1 | uint
+    replicas: uint | *1
     command:  string
 }
 
diff --git a/doc/tutorial/kubernetes/manual/services/cloud.cue b/doc/tutorial/kubernetes/manual/services/cloud.cue
index 27fdb1a..4ba6142 100644
--- a/doc/tutorial/kubernetes/manual/services/cloud.cue
+++ b/doc/tutorial/kubernetes/manual/services/cloud.cue
@@ -12,11 +12,10 @@
 }
 
 deployment <Name>: _base & {
-	name:     Name | string
-// jba: why do you need to write "Name | string"? Doesn't the grammar require that the value
-// of <Name> is a string?
+	// Allow any string, but take Name by default.
+	name:     string | *Name
 	kind:     "deployment" | "stateful" | "daemon"
-	replicas: 1 | int
+	replicas: int | *1
 
 	image: string
 
@@ -36,10 +35,10 @@
 	envSpec: {"\(k)" value: v for k, v in env}
 
 	volume <Name>: {
-		name:      Name | string
+		name:      string | *Name
 		mountPath: string
-		subPath:   null | string
-		readOnly:  false | true
+		subPath:   string | *null
+		readOnly:  *false | true
 		kubernetes: {}
 	}
 }
@@ -48,11 +47,11 @@
 	name: Name | string
 
 	port <Name>: {
-		name: Name | string
+		name: string | *Name
 
 		port:       int
-		targetPort: port | int
-		protocol:   "TCP" | "UDP"
+		targetPort: int | *port
+		protocol:   *"TCP" | "UDP"
 	}
 
 	kubernetes: {}
@@ -66,10 +65,11 @@
 
 	// Copy over all ports exposed from containers.
 	port "\(Name)": {
-		port:       Port | int
-// jba: Port must be defined, so why do you need "| int"?
-		targetPort: Port | int
-// jba: I don't think you need targetPort, because it's defined above in terms of port.
+		// Set default external port to Port.
+		port:       int | *Port
+		targetPort: int | *Port
+		// TODO(verify): jba: I don't think you need targetPort, because it's defined above in terms of port.
+		// Should probably be Port fixed.
 	} for Name, Port in spec.expose.port
 
 	// Copy over the labels
diff --git a/doc/tutorial/kubernetes/manual/services/frontend/kube.cue b/doc/tutorial/kubernetes/manual/services/frontend/kube.cue
index b01c302..c8d47e8 100644
--- a/doc/tutorial/kubernetes/manual/services/frontend/kube.cue
+++ b/doc/tutorial/kubernetes/manual/services/frontend/kube.cue
@@ -3,7 +3,7 @@
 _base label component: "frontend"
 
 deployment <Name>: {
-	expose port http: 7080 | int
+	expose port http: *7080 | int
 	kubernetes spec template metadata annotations: {
 		"prometheus.io.scrape": "true"
 		"prometheus.io.port":   "\(expose.port.http)"
diff --git a/doc/tutorial/kubernetes/manual/services/kitchen/kube.cue b/doc/tutorial/kubernetes/manual/services/kitchen/kube.cue
index 00a5466..40344c0 100644
--- a/doc/tutorial/kubernetes/manual/services/kitchen/kube.cue
+++ b/doc/tutorial/kubernetes/manual/services/kitchen/kube.cue
@@ -30,16 +30,16 @@
 	// Volumes
 	volume "\(name)-disk": {
 		name:      string
-		mountPath: "/logs" | string
+		mountPath: *"/logs" | string
 		spec gcePersistentDisk: {
-			pdName: name | string
+			pdName: *name | string
 			fsType: "ext4"
 		}
 	}
 
 	volume "secret-\(name)": {
-		mountPath: "/etc/certs" | string
+		mountPath: *"/etc/certs" | string
 		readOnly:  true
-		spec secret secretName: "\(name)-secrets" | string
+		spec secret secretName: *"\(name)-secrets" | string
 	}
 }
diff --git a/doc/tutorial/kubernetes/quick/services/frontend/kube.cue b/doc/tutorial/kubernetes/quick/services/frontend/kube.cue
index 58f084c..ce4c25e 100644
--- a/doc/tutorial/kubernetes/quick/services/frontend/kube.cue
+++ b/doc/tutorial/kubernetes/quick/services/frontend/kube.cue
@@ -8,6 +8,6 @@
 		"prometheus.io.port":   "\(spec.containers[0].ports[0].containerPort)"
 	}
 	spec containers: [{
-		ports: [{containerPort: 7080 | int}] // 7080 is the default
+		ports: [{containerPort: *7080 | int}] // 7080 is the default
 	}]
 }
diff --git a/doc/tutorial/kubernetes/quick/services/kitchen/kube.cue b/doc/tutorial/kubernetes/quick/services/kitchen/kube.cue
index e213621..c4639d8 100644
--- a/doc/tutorial/kubernetes/quick/services/kitchen/kube.cue
+++ b/doc/tutorial/kubernetes/quick/services/kitchen/kube.cue
@@ -23,21 +23,21 @@
 	_hasDisks: true | bool
 
 	volumes: [{
-		name: "\(Name)-disk" | string
-		gcePersistentDisk pdName: "\(Name)-disk" | string
+		name: *"\(Name)-disk" | string
+		gcePersistentDisk pdName: *"\(Name)-disk" | string
 		gcePersistentDisk fsType: "ext4"
 	}, {
-		name: "secret-\(Name)" | string
-		secret secretName: "\(Name)-secrets" | string
+		name: *"secret-\(Name)" | string
+		secret secretName: *"\(Name)-secrets" | string
 	}, ...] if _hasDisks
 
 	containers: [{
 		volumeMounts: [{
-			name:      "\(Name)-disk" | string
-			mountPath: "/logs" | string
+			name:      *"\(Name)-disk" | string
+			mountPath: *"/logs" | string
 		}, {
-			mountPath: "/etc/certs" | string
-			name:      "secret-\(Name)" | string
+			mountPath: *"/etc/certs" | string
+			name:      *"secret-\(Name)" | string
 			readOnly:  true
 		}, ...]
 	}] if _hasDisks // field comprehension using just "if"
diff --git a/doc/tutorial/kubernetes/quick/services/kube.cue b/doc/tutorial/kubernetes/quick/services/kube.cue
index a84a50e..09688f5 100644
--- a/doc/tutorial/kubernetes/quick/services/kube.cue
+++ b/doc/tutorial/kubernetes/quick/services/kube.cue
@@ -15,8 +15,8 @@
 		// Any port has the following properties.
 		ports: [...{
 			port:     int
-			protocol: "TCP" | "UDP" // from the Kubernetes definition
-			name:     "client" | string
+			protocol: *"TCP" | "UDP" // from the Kubernetes definition
+			name:     *"client" | string
 		}]
 		selector: metadata.labels // we want those to be the same
 	}
@@ -66,7 +66,7 @@
 // for all ports defined in all containers.
 _spec spec template spec containers: [...{
 	ports: [...{
-		_export: true | false // include the port in the service
+		_export: *true | false // include the port in the service
 	}]
 }]
 
@@ -75,8 +75,8 @@
 
 	spec ports: [ {
 		Port = p.containerPort // Port is an alias
-		port:       Port | int
-		targetPort: Port | int
+		port:       *Port | int
+		targetPort: *Port | int
 	} for c in v.spec.template.spec.containers
 		for p in c.ports
 		if p._export ]