internal/core/adt: fix nil panic

A vertex should always be associated with state in case
it is under evaluation. However, when it was "virtually"
tagged as evaluating, this would sometimes be missed.

Once we have redone the cycle detection algorithm,
this "virtual" state will probably go.

Fixes #672

Change-Id: I9b41be5cd6e87f7387fcdb7bfef64abc1d75f814
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8303
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/internal/core/adt/composite.go b/internal/core/adt/composite.go
index 49995be..4ffc567 100644
--- a/internal/core/adt/composite.go
+++ b/internal/core/adt/composite.go
@@ -265,6 +265,25 @@
 	Finalized
 )
 
+func (s VertexStatus) String() string {
+	switch s {
+	case Unprocessed:
+		return "unprocessed"
+	case Evaluating:
+		return "evaluating"
+	case Partial:
+		return "partial"
+	case AllArcs:
+		return "allarcs"
+	case EvaluatingArcs:
+		return "evaluatingArcs"
+	case Finalized:
+		return "finalized"
+	default:
+		return "unknown"
+	}
+}
+
 func (v *Vertex) Status() VertexStatus {
 	if v.EvalCount > 0 {
 		return EvaluatingArcs
diff --git a/internal/core/adt/eval.go b/internal/core/adt/eval.go
index 806797f..b00b0b0 100644
--- a/internal/core/adt/eval.go
+++ b/internal/core/adt/eval.go
@@ -164,7 +164,7 @@
 				return &w
 			}
 		}
-		panic("nil value")
+		Assertf(false, "no BaseValue: state: %v; requested: %v", v.status, state)
 	}
 
 	if v.status < Finalized && v.state != nil {
@@ -180,15 +180,17 @@
 func (e *Unifier) Unify(c *OpContext, v *Vertex, state VertexStatus) {
 	// defer c.PopVertex(c.PushVertex(v))
 
+	// Ensure a node will always have a nodeContext after calling Unify if it is
+	// not yet Finalized.
+	n := v.getNodeContext(c)
+	defer v.freeNode(n)
+
 	if state <= v.Status() {
 		if v.Status() != Partial && state != Partial {
 			return
 		}
 	}
 
-	n := v.getNodeContext(c)
-	defer v.freeNode(n)
-
 	switch v.Status() {
 	case Evaluating:
 		return