-- in.cue --
import "list"

a1: {
	f: [f]
	@test(err, code=structural_cycle, path=f.0, contains="structural cycle", pos=[])
}

a2: {
	f: f
	@test(eq, {f: _})
}

a3: {
	f: {g: f}
	@test(err, code=structural_cycle, path=f.g, contains="structural cycle", pos=[])
}

a4: {
	a: [a | int]
	@test(eq, {a: [int]})
}

a5: {
	a: b: a | int
	@test(eq, {a: {b: int}})
}

a6: {
	a: a | int
	@test(eq, {a: _ | int})
}

a7: {
	a: c.x
	b: {
		x: c
		y: "foo"
	}
	c: {
		x: b.y
		y: 3
	}
	@test(eq, {
		a: "foo"
		b: {x: {x: "foo", y: 3}, y: "foo"}
		c: {x: "foo", y: 3}
	})
}

b1: {
	b: a & [1]
	a: [a | int]
	@test(eq, {b: [1], a: [int]})
}

b2: {
	a: [a | int]
	b: a & [1]
	@test(eq, {a: [int], b: [1]})
}

b3: {
	x: a: [a | int]
	b: x & {a: [1]}
	@test(eq, {x: {a: [int]}, b: {a: [1]}})
}

b4: {
	b: x.y & [1]
	x: y: [y]
	@test(err, code=eval, path=b.0, contains="conflicting values %s and %s (mismatched types %s and %s)", args=[1, [y], int, list], pos=[1:5, 1:12, 2:8, 2:9])
}

// Ensure that the cycle error is not blindly copied to a referring field,
// as the cycle may not persist.
b4: cond1: {
	b: x.y & { _z: false, [1] }
	x: y: {
		_z: *true | bool
		if _z {
			[y]
		}
	}
	@test(err, code=structural_cycle, path=x.y.0, contains="structural cycle", pos=[])
}

// This reports a structural cycle at b4.cond2.b, because the value it refers
// has a structural cycle. Technically, this is incorrect, as the cycle
// really only occurs in b4.cond2.x.y. But since structural cycles are
// permanent, the entire configuration is invalidated, so the spurious
// additional cycle is merely cosmetic. By most logical standards, one can prove
// any fact from a contradiction. So assuming that, this is not incorrect.
// TODO: fix this cosmetic blemish.
b4: cond2: {
	x: y: {
		_z: *true | bool
		if _z {
			[y]
		}
	}
	b: x.y & { _z: false, [1] }
	@test(err, code=structural_cycle, path=x.y.0, contains="structural cycle", pos=[])
}

b5: {
	b: x.y & {a: [1]}
	x: y: a: [a | int]
	@test(eq, {b: {a: [1]}, x: {y: {a: [int]}}})
}

b6: {
	b: x & {a: [1]}
	x: a: [a]
	@test(err, code=eval, path=b.a.0, contains="conflicting values %s and %s (mismatched types %s and %s)",
		args=[1, [1], int, list], pos=[1:5, 1:13, 1:14, 2:9])
}

b7: {
	b: a & [[1]]
	a: [a]
	@test(err, code=eval, path=b.0.0, contains="conflicting values %s and %s (mismatched types %s and %s)",
		args=[1, [a], int, list], pos=[1:5, 1:11, 2:5, 2:6])
}

// Issue #555
b8: {
	x: a
	a: f: b
	b: a | string
	@test(eq, {
		x: {f: string}
		a: {f: string}
		b: string
	})
}

// Issue #555
b9: {
	#a: string | #b | #ref
	#b: {
		c: [#a, #a, #a]
	}
	#ref: ref: string
	x: #b | #ref
	@test(eq, {
		#a: string | {ref: string}
		#b: {
			c: [string | {ref: string},
			string | {ref: string},
			string | {ref: string}]
		}
		#ref: {ref: string}
		x: {c: [string | {ref: string},
	string | {ref: string},
	string | {ref: string}]} | {ref: string}
	})
}

// Issue #534
// TODO: the resolution here is arguably incorrect: c should be
// 		d: string | { b: string }
// However, this is not the end of the world, as the disjunction is preserved
// under the hood and still unifies with extended structure.
b10: {
	a: close({
		b: string | a | c
	})
	c: close({
		d: string | a
	})

	d: d: b: string
	@test(eq, {
		a: {b: string | {d: string}}
		c: {
			d: string | {b: string | {b: string | {b: string}}}
		}
		d: {d: {b: string}}
	})
}

// Issue #509 -- with comprehension
b11: {
	#list: {
		tail: #list | *null
		if tail != null {
		}
	}
	@test(eq, {#list: {tail: null}})
}

// Issue #509 -- with comprehension
b12: {
	#list: {
		V=value: int
		T=tail:  #list | *null
		if T != null {
			sum: V + T.sum
		}
		if T == null {
			sum: V
		}
	}

	list1: #list
	list1: {
		value: 1
		tail: {
			value: 2
			tail: {
				value: 3
				tail: {
					value: 4
				}
			}
		}
	}
	@test(eq, {
		#list: {value: int, tail: null, sum: int}
		list1: {
			value: 1
			tail: {
				value: 2
				tail: {
					value: 3
					tail: {
						value: 4
						tail: *null | {value: int, tail: null, sum: int}
						sum: 4
					}
					sum: 7
				}
				sum: 9
			}
			sum: 10
		}
	})
}

// More trigger happy on stack overflows.
b12b: {
	#list: {
		tail: #list

		if tail != null {
			sum: tail.sum
		}
	} @test(err, code=structural_cycle, path=tail, contains="structural cycle", pos=[])

	list1: #list  @test(err, code=structural_cycle, path=b12b.#list.tail, contains="structural cycle", pos=[])
	list1: {
		tail: {
			tail: {
			}
		}
	}
}

// Issue #587
b13: {
	root: a: [for x in root {x}]
	@test(err, code=structural_cycle, path=root.a.0.0, contains="structural cycle", pos=[])
}

// Issue #587
// TODO: this should probably be okay if we allow shallow evaluation of
// comprehension sources. See #1881.
b14: {
	root: {
		a: [...int]

		for x in a {
			"\(x)": {}
		}

		b: [for x in root {x}]
	}
	@test(err, code=structural_cycle, path=root.b.1.1, contains="structural cycle", pos=[])
}

// This is okay
// Issue #587
b15: {
	root: a: {for x in root {x}}
	@test(eq, {root: {a: {}}})
}

// Issue #502 -- unused bulk constraints are not cyclic
p1: {
	#T: {
		a: [string]: link: #T
	}

	a: #T & {
		a: one: link: a: two: {}
	}
	@test(eq, {
		#T: {a: {}}
		a: {
			a: {
				one: {link: {a: {two: {link: {a: {}}}}}}
			}
		}
	})
}

// Issue #502 -- but they are if it is invoked within the struct.
p2: {
	#T: {
		a: [string]: link: #T
		a: b: {}
	}

	a: #T & {
		a: one: link: a: two: {}
	}
	@test(eq, {one: {link: {a: {two: {}}}}}, at=a.a)
	@test(err, code=structural_cycle, path=#T.a.b.link, contains="structural cycle", pos=[])
}

// Issue #502 -- or added later.
p3: {
	#S: #T: {
		a: [string]: link: #T
	}

	#U: {
		#S
		#T: a: b: {}
	} @test(err, code=structural_cycle, path=#T.a.b.link, contains="structural cycle", pos=[])

	a: #U.#T & {
		a: one: link: a: two: {}
	} @test(err, code=structural_cycle, path=p3.#U.#T.a.b.link, contains="structural cycle", pos=[])
}

// Issue #502 -- unused bulk constraints are not cyclic
p4: {
	#T: {
		a: [...{link: #T}]
	}

	a: #T & {
		a: [{link: a: [{}]}]
	}
	@test(eq, {
		#T: {a: []}
		a: {a: [{link: {a: [{link: {a: []}}]}}]}
	})
}

// Issue #502 -- but they are if it is invoked within the struct.
p5: {
	#T: {
		a: [...{link: #T}]
		a: [{}]
	}

	a: #T & {
		a: [{link: a: [{}]}]
	}
	@test(err, code=structural_cycle, path=#T.a.0.link, contains="structural cycle", pos=[])
	@test(eq, [{link: {a: [{}]}}], at=a.a)
}

// Issue #502 -- or added later.
p6: {
	#S: #T: {
		a: [...{link: #T}]
	}

	#U: {
		#S
		#T: a: [{}]
	} @test(err, code=structural_cycle, path=#T.a.0.link, contains="structural cycle", pos=[])

	a: #U.#T & {
		a: [{link: a: [{}]}]
	} @test(err, code=structural_cycle, path=p6.#U.#T.a.0.link, contains="structural cycle", pos=[])
}

c1: {
	a: {
		b: {}
		c: a & b
	}
	@test(err, code=structural_cycle, path=a.c.c, contains="structural cycle", pos=[])
}

// indirection
d1: {
	a: b: c: d: {h: int, t: r}
	@test(err, at=a.b.c.d.t, code=structural_cycle, path=d1.r, contains="structural cycle", pos=[])

	r: a.b @test(err, code=structural_cycle, contains="structural cycle", pos=[])

	x: a.b.c

}

d2: {
	x: a.b.c @test(shareID=X)
	@test(shareID=X, at=a.b.c)

	r: a.b @test(shareID=R)
	@test(shareID=R, at=a.b)

	a: b: c: d: {h: int, t: r}
}

d3: {
	config: {
		a: b: c: indirect
		indirect: [a.b, null][i]
		i:        int | *1
	}
	x: config & {i: 0}
	@test(err, at=x.indirect, code=structural_cycle, contains="structural cycle", pos=[])
	@test(err, at=x.a.b.c, code=structural_cycle, contains="structural cycle", pos=[])
	@test(eq, at=config, {
		a: {b: {c: null}}
		indirect: null
		i: *1 | int
	})
}

// Ensure that the cycles here fail early.
// TODO: they can still be shorter.
shortPathFail: _

shortPathFail: doubleRef: {
	// Avoid this: the first #List reference is different from the second, so
	// the second #List reference counts as a first occurrence, causing the
	// result to be longer than perhaps expected.
	a: #List
	#List: Next: #List | *null
	@test(eq, {a: {Next: null}, #List: {Next: null}})
}

shortPathFail: elipsis: {
	// Avoid this: the elipsis causes `_` to be mixed in with the #Foo
	// reference, causing the first cycle to be discounted.
	t1: {
		#Foo: ref:  null | #Foo
		#Foo: { ... }
		@test(eq, {#Foo: {ref: null}})
	}
	t2: {
		Foo: ref:  Foo
		Foo: {...}
		@test(err, code=structural_cycle, at=Foo.ref, contains="structural cycle", pos=[])
	}
}

// Issue #2374: a pattern constraint with an alias referring back to itself
// (e.g., `r=[string]: {b: r}`) should be detected as a structural cycle,
// but the evaluator instead emitted output with a renamed alias.
// https://cuelang.org/issue/2374
patternFail: issue2374: {
	r=[string]: {b: r}
	a: {r: 0}
	@test(err, code=structural_cycle, at=a.b, contains="structural cycle", pos=[])
}

shortPathFail: comprehension: {
	#list: {
		tail: #list | *null
		if tail != null {
		}
	}
	@test(eq, {#list: {tail: null}})
}

withLetFail: {
	schema: next:  _schema_1
	let _schema_1 = schema
	@test(err, code=structural_cycle, at=schema.next, contains="structural cycle", pos=[])
}

// Issue #3310: `list.Sum` called with a comprehension iterating over the
// enclosing struct causes a structural cycle because `total` is included in
// its own computation. The cycle error is correct; evalv2 incorrectly produced
// a wrong numeric result instead of erroring.
// https://cuelang.org/issue/3310
fieldsSumInfinite: issue3310: {
	cheeseburger: 3.00
	fries:        2.00
	sprite:       1.00

	total: list.Sum([for _, v in issue3310 {v}])
	@test(err, code=structural_cycle, at=total, contains="error in call to %s", args=[list.Sum], pos=[5:9])
}

listOptOK: {
	list: {
		head: int
		tail?: list
	}
	a: list & {head: 3, tail: head: 2}
	@test(eq, {
		list: {
			head: int
			tail?: _|_ @test(err, code=structural_cycle, contains="list.tail: structural cycle", pos=[])
		}
		a: {
			head: 3
			tail: {
				head: 2
				tail?: {
					head: int
					tail?: _|_ @test(err, code=structural_cycle, contains="list.tail: structural cycle", pos=[])
				}
			}
		}
	})
}

// The cyclic definitions below used to work OK on old versions of evalv2,
// and broke around the release of CUE v0.6.0. evalv3 fixes this again.
// It should succeed, given that the optional field breaks the cycle.
issue2545: {
	#A: {
		B?: #B
	}
	#B: {
		A: #A
	}
	@test(err, code=structural_cycle,
		hint="the comment says this should succeed since the optional field breaks the cycle, but it still errors in both v2 and v3")
	@test(eq:todo, {
		#A: {B?: _ @test(ignore)}
		#B: {A: {B?: _|_}}
	})
}

// combining structural with reference cycles
e1: {
	a: a
	a: c: a

	b: c: b
	b: b
	@test(err, code=structural_cycle, at=a.c, contains="structural cycle", pos=[])
	@test(err, code=structural_cycle, at=b.c, contains="structural cycle", pos=[])
}

e2: {
	a: {a}
	a: c: a

	b: c: b
	b: {b}
	@test(err, code=structural_cycle, at=a.c, contains="structural cycle", pos=[])
	@test(err, code=structural_cycle, at=b.c, contains="structural cycle", pos=[])
}

e3: {
	a: [a]  @test(err, code=eval, contains="conflicting values %s and %s (mismatched types %s and %s)", args=[[a], {c:a}, list, struct], pos=[0:5, 1:5])
	a: c: a @test(err, code=structural_cycle, contains="structural cycle", pos=[])

	b: [b] @test(err, code=eval, contains="conflicting values %s and %s (mismatched types %s and %s)", args=[[b], {c:b}, list, struct], pos=[0:5, 1:5])
	b: c: b
}

e4: {
	a: [a | {}]
	a: [[{c: 1}]]

	b: [[{c: 1}]]
	b: [b | {}]
	@test(err, code=eval,
		suberr=(code=eval, path=a.0, contains="conflicting values %s and %s (mismatched types %s and %s)", args=[[{c:1}], {}, list, struct], pos=[1:10, 2:6]),
		suberr=(code=eval, path=a.0.0, contains="conflicting values %s and %s (mismatched types %s and %s)", args=[{c:1}, [{c:1}], struct, list], pos=[1:6, 2:6, 2:7]))
}

e5: {
	a: c: a | int
	a: a | int

	b: b | int
	b: c: b | int
	@test(eq, {a: {c: int}, b: {c: int}})
}

// validating values
nestedList: {
	v1e: {
		x: [x | int, 1]
		y: x & [[[2], 1], 1]
		@test(err, code=eval,
			suberr=(code=eval, path=y.0, contains="conflicting values %s and %s (mismatched types %s and %s)", pos=[1:11, 2:6, 2:11]),
			suberr=(code=eval, path=y.0.0, contains="%d errors in empty disjunction:", args=[2], pos=[]),
			suberr=(code=eval, path=y.0.0, contains="conflicting values %s and %s (mismatched types %s and %s)", pos=[1:7, 1:11, 2:6, 2:12]),
			suberr=(code=eval, path=y.0.0, contains="incompatible list lengths (%d and %d)", args=[1, 2], pos=[1:6]))
	}

	v2e: {
		y: x & [[[2], 1], 1]
		x: [x | int, 1]
		@test(err, code=eval,
			suberr=(code=eval, path=y.0, contains="conflicting values %s and %s (mismatched types %s and %s)", pos=[1:6, 1:11, 2:11]),
			suberr=(code=eval, path=y.0.0, contains="%d errors in empty disjunction:", args=[2], pos=[]),
			suberr=(code=eval, path=y.0.0, contains="conflicting values %s and %s (mismatched types %s and %s)", pos=[1:6, 1:12, 2:7, 2:11]),
			suberr=(code=eval, path=y.0.0, contains="incompatible list lengths (%d and %d)", args=[1, 2], pos=[2:6]))
	}

	v1: {
		x: [x | int, 1]
		y: x & [[[2, 1], 1], 1]
		@test(eq, {x: [int, 1], y: [[[2, 1], 1], 1]})
	}

	v2: {
		y: x & [[[2, 1], 1], 1]
		x: [x | int, 1]
		@test(eq, {y: [[[2, 1], 1], 1], x: [int, 1]})
	}
}

v3: {
	list: {
		head: int
		tail: list | null
	}

	myList: list
	myList: {
		head: 2
		tail: {
			head: 3
			tail: {
				head: 4
			}
		}
	}
	@test(eq, {
		list: {head: int, tail: null}
		myList: {
			head: 2
			tail: {
				head: 3
				tail: {
					head: 4
					tail: {head: int, tail: null} | null
				}
			}
		}
	})
}

v4: {
	list: {
		head: int
		tail: list | 1
	}

	myList: list
	myList: {
		head: 2
		tail: head: 3
	}
	@test(eq, {
		list: {head: int, tail: 1}
		myList: {
			head: 2
			tail: {
				head: 3
				tail: {head: int, tail: 1} | 1
			}
		}
	})
}

v5: {
	list: {
		head: int
		tail: list | {}
	}

	myList: list
	myList: {
		head: 2
		tail: head: 3
	}
	@test(eq, {
		list: {head: int, tail: {}}
		myList: {
			head: 2
			tail: {head: 3, tail: {head: int, tail: {}} | {}} | {head: 3}
		}
	})
}

// Example from "The Logic of Type Feature Structures" (Bob Carpenter)/
z1: {
	y: {
		f: h: g
		g: _
	}
	x: {
		f: _
		g: f
	}
	z: x & y
	@test(err, code=structural_cycle, path=z.g, contains="structural cycle", pos=[])
}

// Cross references. Here there is no cycle, but the repeated instantiations
// of T will cause, in turn, cause repeated resolution of `x`. The algorithm
// should not identify these as cyclic references. Instead, it should be
// noted that the reference is each time referring to new content.
// Issue #754
crossRefNoCycle: t1: {
	T: {
		x: _
		y: x
	}
	C: { x: T } & T
	@test(eq, {
		T: {x: _, y: _}
		C: {x: {x: _, y: _}, y: {x: _, y: _}}
	})
}

crossRefNoCycle: t2: {
	T: {
		x: _
		y: x
	}
	C: T & { x: T }
	@test(eq, {
		T: {x: _, y: _}
		C: {x: {x: _, y: _}, y: {x: _, y: _}}
	})
}

crossRefNoCycle: t3: {
	T: {
		x: _
		y: x
		z: y
	}
	C: T & { x: T }
	@test(eq, {
		T: {x: _, y: _, z: _}
		C: {
			x: {x: _, y: _, z: _}
			y: {x: _, y: _, z: _}
			z: {x: _, y: _, z: _}
		}
	})
}

crossRefNoCycle: t4: {
	T: {
		x: _
		y: x
		z: y
	}
	C: T & { x: T & { x: T } }
	@test(eq, {
		T: {x: _, y: _, z: _}
		C: {
			x: {
				x: {x: _, y: _, z: _}
				y: {x: _, y: _, z: _}
				z: {x: _, y: _, z: _}
			}
			y: {
				x: {x: _, y: _, z: _}
				y: {x: _, y: _, z: _}
				z: {x: _, y: _, z: _}
			}
			z: {
				x: {x: _, y: _, z: _}
				y: {x: _, y: _, z: _}
				z: {x: _, y: _, z: _}
			}
		}
	})
}

crossRefNoCycle: t5: {
	T: X={
		y: X.x
	}
	C: T & { x: T  }
	@test(eq, {
		T: {
			y: _|_ @test(err, code=incomplete, contains="crossRefNoCycle.t5.T.y: undefined field: x", pos=[2:8])
		}
		C: {
			x: {
				y: _|_ @test(err, code=incomplete, contains="crossRefNoCycle.t5.T.y: undefined field: x", pos=[2:8])
			}
			y: {
				y: _|_ @test(err, code=incomplete, contains="crossRefNoCycle.t5.T.y: undefined field: x", pos=[2:8])
			}
		}
	})
}

// Ensure these are NOT treated as structural errors.

n1: {
	a: b: int
	@test(eq, {a: {b: int}})
}
n2: {
	n1
	a: n1
	@test(eq, {a: {b: int, a: {b: int}}})
}
n3: {
	n1
	@test(eq, {a: {b: int}})
}
n4: {
	n1
	x: {
		n1
		y: {
			n1
			z: int
		}
	}
	@test(eq, {
		x: {y: {z: int, a: {b: int}}, a: {b: int}}
		a: {b: int}
	})
}
-- edge.cue --
// TODO(eval): notice the difference, not a huge deal, but still.
resolveToAncestor: {
	t1: X: {
		a: b.a   @test(eq, _) @test(err:todo, code=structural_cycle)
		b: X     @test(err, code=structural_cycle, contains="structural cycle", pos=[])
	}
	t2: X: {
		b: X    @test(err, code=structural_cycle, contains="structural cycle", pos=[])
		a: b.a  @test(err, code=structural_cycle, path=resolveToAncestor.t2.X.b, contains="structural cycle", pos=[])
	}
}
-- out/errors.txt --
[structural_cycle] resolveToAncestor.t1.X.b: structural cycle
[structural_cycle] resolveToAncestor.t2.X.b: structural cycle
[structural_cycle] resolveToAncestor.t2.X.b: structural cycle
[structural_cycle] a1.f.0: structural cycle
[structural_cycle] a3.f.g: structural cycle
[eval] b4.b.0: conflicting values 1 and [y] (mismatched types int and list):
    ./in.cue:69:5
    ./in.cue:69:12
    ./in.cue:70:8
    ./in.cue:70:9
[structural_cycle] b4.x.y.0: structural cycle
[structural_cycle] b4.cond1.x.y.0: structural cycle
[structural_cycle] b4.cond2.x.y.0: structural cycle
[structural_cycle] b4.cond2.x.y.0: structural cycle
[eval] b6.b.a.0: conflicting values 1 and [1] (mismatched types int and list):
    ./in.cue:112:5
    ./in.cue:112:13
    ./in.cue:112:14
    ./in.cue:113:9
[structural_cycle] b6.x.a.0: structural cycle
[eval] b7.b.0.0: conflicting values 1 and [a] (mismatched types int and list):
    ./in.cue:119:5
    ./in.cue:119:11
    ./in.cue:120:5
    ./in.cue:120:6
[structural_cycle] b7.a.0: structural cycle
[structural_cycle] b12b.#list.tail: structural cycle
[structural_cycle] b13.root.a.0.0: structural cycle
[structural_cycle] b14.root.b.1.1: structural cycle
[structural_cycle] p2.#T.a.b.link: structural cycle
[structural_cycle] p3.#U.#T.a.b.link: structural cycle
[structural_cycle] p3.#U.#T.a.b.link: structural cycle
[structural_cycle] p5.#T.a.0.link: structural cycle
[structural_cycle] p6.#U.#T.a.0.link: structural cycle
[structural_cycle] p6.#U.#T.a.0.link: structural cycle
[structural_cycle] c1.a.c.c: structural cycle
[structural_cycle] d1.r: structural cycle
[structural_cycle] d1.r: structural cycle
[structural_cycle] d1.r: structural cycle
[structural_cycle] d2.a.b.c.d.t: structural cycle
[structural_cycle] d3.x.indirect: structural cycle
[structural_cycle] d3.x.indirect: structural cycle
[structural_cycle] shortPathFail.elipsis.t2.Foo.ref: structural cycle
[structural_cycle] patternFail.issue2374.a.b: structural cycle
[structural_cycle] withLetFail.schema.next: structural cycle
[structural_cycle] fieldsSumInfinite.issue3310.total: error in call to list.Sum: error in call to list.Sum: structural cycle:
    ./in.cue:490:9
[structural_cycle] listOptOK.list.tail: structural cycle
[structural_cycle] issue2545.#B.A: structural cycle
[structural_cycle] e1.a.c: structural cycle
[structural_cycle] e1.b.c: structural cycle
[structural_cycle] e2.a.c: structural cycle
[structural_cycle] e2.b.c: structural cycle
[eval] e3.a: conflicting values [a] and {c:a} (mismatched types list and struct):
    ./in.cue:558:5
    ./in.cue:559:5
[eval] e3.b: conflicting values [b] and {c:b} (mismatched types list and struct):
    ./in.cue:561:5
    ./in.cue:562:5
[eval] e4.a.0: 2 errors in empty disjunction:
e4.a.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
    ./in.cue:566:10
    ./in.cue:567:6
e4.a.0.0: conflicting values {c:1} and [{c:1}] (mismatched types struct and list):
    ./in.cue:566:6
    ./in.cue:567:6
    ./in.cue:567:7
[eval] e4.b.0: 2 errors in empty disjunction:
e4.b.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
    ./in.cue:569:6
    ./in.cue:570:10
e4.b.0.0: conflicting values {c:1} and [{c:1}] (mismatched types struct and list):
    ./in.cue:569:6
    ./in.cue:569:7
    ./in.cue:570:6
[eval] nestedList.v1e.y.0: 4 errors in empty disjunction:
nestedList.v1e.y.0: conflicting values [[2],1] and int (mismatched types list and int):
    ./in.cue:588:11
    ./in.cue:589:6
    ./in.cue:589:11
nestedList.v1e.y.0.0: 2 errors in empty disjunction:
nestedList.v1e.y.0.0: conflicting values [2] and int (mismatched types list and int):
    ./in.cue:588:7
    ./in.cue:588:11
    ./in.cue:589:6
    ./in.cue:589:12
nestedList.v1e.y.0.0: incompatible list lengths (1 and 2):
    ./in.cue:588:6
[eval] nestedList.v2e.y.0: 4 errors in empty disjunction:
nestedList.v2e.y.0: conflicting values [[2],1] and int (mismatched types list and int):
    ./in.cue:598:6
    ./in.cue:598:11
    ./in.cue:599:11
nestedList.v2e.y.0.0: 2 errors in empty disjunction:
nestedList.v2e.y.0.0: conflicting values [2] and int (mismatched types list and int):
    ./in.cue:598:6
    ./in.cue:598:12
    ./in.cue:599:7
    ./in.cue:599:11
nestedList.v2e.y.0.0: incompatible list lengths (1 and 2):
    ./in.cue:599:6
[structural_cycle] z1.z.g: structural cycle
[structural_cycle] z1.z.g: structural cycle
[incomplete] crossRefNoCycle.t5.T.y: undefined field: x:
    ./in.cue:785:8
-- out/todo.txt --
Structural cycles are reported in all chains compared to old implementation.

b4.b: In evalv3 the structural error is converted to an eval error (conflicting values),
   which is correct. evalv2 did not report this error.

b6.b.a.0.0: evalv2 reports an additional structural cycle error here, evalv3 does not.

b7.b.0.0: evalv3 correctly reports an eval error (conflicting values); the structural
   cycle is nested one too deep before detection in evalv2.

b10.c.d: evalv3 allows b: string in the position, which is correct.

d1: evalv3 reports the error at d1.r, evalv2 at d1.a.b.c.d.t.

d2: evalv2 additionally reports d2.r.c.d.t as structural cycle.

d3: evalv3 reports at d3.x.indirect, evalv2 at d3.x.a.b.c.

e3: evalv2 additionally reports e3.a.c and e3.b.c as structural cycles.

e4: evalv2 reports 4 errors in empty disjunction; evalv3 reports 2.

fieldsSumInfinite.issue3310: evalv3 correctly spots a structural cycle;
   evalv2 computes an incorrect result without erroring.

issue2545: the source comment says the optional field should break the cycle and
   this should succeed. Both v2 and v3 report a structural cycle; this is an open bug.

nestedList.v1e/v2e: evalv2/evalv3 differ in error message details for empty disjunction.

resolveToAncestor.t1.X: evalv2 reports both a and b as cyclic; evalv3 only reports b.

shortPathFail.comprehension: evalv2 shows a disjunction in tail; evalv3 shows null.

shortPathFail.elipsis.t1: evalv2 shows a disjunction in #Foo.ref; evalv3 shows null.

withLetFail: evalv2 attributes error to _schema_1, evalv3 to schema.next.

z1.z: evalv2 reports z1.z.f.h and z1.z.g.h; evalv3 reports z1.z.g.

b12: evalv3 shows a tail disjunction in the innermost list1 node; evalv2 shows null.

n4: field ordering differs between evalv2 and evalv3.
-- out/eval/stats --
Leaks:  96
Freed:  712
Reused: 642
Allocs: 166
Retain: 0

Unifications: 541
Conjuncts:    990
Disjuncts:    149

NumCloseIDs: 177
