doc/ref/spec: clarification on cycles

See https://cue-review.googlesource.com/c/cue/+/6522 for
details on the specification and, specifically, the test cases.

Change-Id: I8f977c8dc09c1dd931fd4d3f2f66e349cb1278ed
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6741
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/doc/ref/spec.md b/doc/ref/spec.md
index 502e680..74ee09a 100644
--- a/doc/ref/spec.md
+++ b/doc/ref/spec.md
@@ -2705,7 +2705,8 @@
 d: b
 ```
 
-Implementations should report these as an error except in the following cases:
+Implementations should treat these as `_`.
+Two particular cases are discussed below.
 
 
 #### Expressions that unify an atom with an expression
@@ -2713,8 +2714,8 @@
 An expression of the form `a & e`, where `a` is an atom
 and `e` is an expression, always evaluates to `a` or bottom.
 As it does not matter how we fail, we can assume the result to be `a`
-and validate after the field in which the expression occurs has been evaluated
-that `a == e`.
+and postpone validating `a == e` until after all referenecs
+in `e` have been resolved.
 
 ```
 // Config            Evaluates to (requiring concrete values)
@@ -2733,7 +2734,7 @@
 #### Field values
 
 A field value of the form `r & v`,
-where `r` evaluates to a reference cycle and `v` is a value,
+where `r` evaluates to a reference cycle and `v` is a concrete value,
 evaluates to `v`.
 Unification is idempotent and unifying a value with itself ad infinitum,
 which is what the cycle represents, results in this value.
@@ -2786,16 +2787,48 @@
 
 ### Structural cycles
 
-CUE disallows infinite structures.
-Implementations must report an error when encountering such declarations.
+A structural cycle is when a node references one of its ancestor nodes.
+It is possible to construct a structural cycle by unifying two acyclic values:
+```
+// acyclic
+y: {
+    f: h: g
+    g: _
+}
+// acyclic
+x: {
+    f: _
+    g: f
+}
+// introduces structural cycle
+z: x & y  
+```
+Implementations should be able to detect such structural cycles dynamically.
 
-<!-- for instance using an occurs check -->
+A structural cycle can result in infinite structure or evaluation loops.
+```
+// infinite structure
+a: b: a
+
+// infinite evaluation
+f: {
+    n:   int
+    out: n + (f & {n: 1}).out
+}
+```
+CUE must allow or disallow structural cycles under certain circumstances.
+
+If a node `a` references an ancestor node, we call it and any of its
+field values `a.f` _cyclic_.
+So if `a` is cyclic, all of its descendants are also regarded as cyclic.
+A given node `x`, whose value is composed of the conjuncts `c1 & ... & cn`,
+is valid if any of its conjuncts is not cyclic.
 
 ```
 // Disallowed: a list of infinite length with all elements being 1.
-list: {
+#List: {
     head: 1
-    tail: list
+    tail: #List
 }
 
 // Disallowed: another infinite structure (a:{b:{d:{b:{d:{...}}}}}, ...).
@@ -2805,37 +2838,22 @@
 c: {
     d: a
 }
-```
 
-It is allowed for a value to define an infinite set of possibilities
-without evaluating to an infinite structure itself.
-
-```
-// List defines a list of arbitrary length (default null).
-List: *null | {
+// #List defines a list of arbitrary length. Because the recursive reference
+// is part of a disjunction, this does not result in a structural cycle.
+#List: {
     head: _
-    tail: List
+    tail: null | #List
 }
+
+// Usage of #List. The value of tail in the most deeply nested element will
+// be `null`: as the value of the disjunct referring to list is the only
+// conjunct, all conjuncts are cyclic and the value is invalid and so
+// eliminated from the disjunction.
+MyList: #List & { head: 1, tail: { head: 2 }}
 ```
 
 <!--
-Consider banning any construct that makes CUE not having a linear
-running time expressed in the number of nodes in the output.
-
-This would require restricting constructs like:
-
-(fib&{n:2}).out
-
-fib: {
-        n: int
-
-        out: (fib&{n:n-2}).out + (fib&{n:n-1}).out if n >= 2
-        out: fib({n:n-2}).out + fib({n:n-1}).out if n >= 2
-        out: n if n < 2
-}
-
--->
-<!--
 ### Unused fields
 
 TODO: rules for detection of unused fields