cue: fix comparing against bottom

May only be against a verbatim error.

Change-Id: I82faa0031cc2b39b7a2e2114c07289de74c5ad88
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3766
Reviewed-by: roger peppe <rogpeppe@gmail.com>
diff --git a/cue/ast.go b/cue/ast.go
index 263cee6..35ce5c6 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -496,7 +496,7 @@
 
 	case *ast.BottomLit:
 		// TODO: record inline comment.
-		ret = &bottom{baseValue: newExpr(n), format: "from source"}
+		ret = &bottom{baseValue: newExpr(n), code: codeUser, format: "from source"}
 
 	case *ast.BadDecl:
 		// nothing to do
diff --git a/cue/errors.go b/cue/errors.go
index a3ce783..98af40e 100644
--- a/cue/errors.go
+++ b/cue/errors.go
@@ -100,6 +100,7 @@
 	codeNotExist
 	codeTypeError
 	codeIncomplete
+	codeUser
 	codeCycle
 )
 
@@ -110,6 +111,13 @@
 	return false
 }
 
+func isLiteralBottom(v value) bool {
+	if err, ok := v.(*bottom); ok {
+		return err.code == codeUser
+	}
+	return false
+}
+
 var errNotExists = &bottom{code: codeNotExist, format: "undefined value"}
 
 func exists(v value) bool {
diff --git a/cue/eval.go b/cue/eval.go
index 59e1ffd..04a9c77 100644
--- a/cue/eval.go
+++ b/cue/eval.go
@@ -428,7 +428,7 @@
 		// principled perhaps. One should especially take care that two values
 		// evaluating to bottom don't evaluate to true. For now we check for
 		// bottom here and require that one of the values be a bottom literal.
-		if l, r := isBottom(x.left), isBottom(x.right); l || r {
+		if isLiteralBottom(x.left) || isLiteralBottom(x.right) {
 			leftBottom := isBottom(left)
 			rightBottom := isBottom(right)
 			switch x.op {
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 3e1aab0..9582f78 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -2444,6 +2444,19 @@
 		}}
 		`,
 		out: `<0>{a: <1>{<>: <2>(Name: string)-><3>{info :: <4>C{X: "foo"}}, d: <5>C{info :: <6>C{X: "foo"}, Y: "foo"}}, base :: <7>C{info :: <8>{...}}}`,
+	}, {
+		desc: "comparison against bottom",
+		in: `
+		a: _|_ == _|_
+		b: err == 1&2 // not a literal error, so not allowed
+		c: err == _|_ // allowed
+		d: err != _|_ // allowed
+		e: err != 1&3
+		// z: err == err // TODO: should infer to be true?
+
+		err: 1 & 2
+		`,
+		out: `<0>{a: true, b: _|_((1 & 2):conflicting values 1 and 2), err: _|_((1 & 2):conflicting values 1 and 2), c: true, d: false, e: _|_((1 & 2):conflicting values 1 and 2)}`,
 	}}
 	rewriteHelper(t, testCases, evalFull)
 }
diff --git a/cue/rewrite_test.go b/cue/rewrite_test.go
index 2673e41..bbf3288 100644
--- a/cue/rewrite_test.go
+++ b/cue/rewrite_test.go
@@ -75,7 +75,7 @@
 			t = v
 		}
 		emit := testResolve(ctx, x.emit, m)
-		obj := &structLit{x.baseValue, emit, t, x.closeStatus, nil, arcs, nil}
+		obj := &structLit{x.baseValue, emit, t, x.closeStatus, x.comprehensions, arcs, nil}
 		return obj
 	case *list:
 		elm := rewriteRec(ctx, x.elem, x.elem, m).(*structLit)