Files
scriptc/tests/corpus/2480-recursive-record-tree.ts
Chris Tate b49052a389 Recursive record types compile statically as named recursive shapes
- mapType back-references mint placeholder shapes/unions finalized at frame exit; identity is per checker type, and context-sensitive (generic/mixin) knots stay fenced
- every type walk survives cyclic shape graphs: coinductive JSON/dyn-safety predicates, guarded formatIrType/jsvalLiftable, cycle headers via the existing collector fixpoint on both backends
- JSON.stringify throws V8's byte-exact circular TypeError (hop lines, ellipsis rule); cyclic values crossing into checked-dynamic slots trap instead of hanging
- console.log/util.inspect render <ref *N>/[Circular *N] exactly (SEMANTICS 129 resolved; depth:null lowers) and deepStrictEqual terminates through Node's pair memo
- corpus 2480-2488 green on C, LLVM, and the sanitized RC-audit lane, which proves child->parent cycles collect
2026-07-23 13:24:13 -05:00

55 lines
1.7 KiB
TypeScript

// Recursive record types: a self-referential interface maps to a named
// recursive shape (the mu-type knot) instead of fencing — trees build,
// traverse, mutate, and reduce exactly like Node.
interface TreeNode { label: string; children: TreeNode[] }
const leafA: TreeNode = { label: "a", children: [] };
const leafB: TreeNode = { label: "b", children: [] };
const mid: TreeNode = { label: "mid", children: [leafA, leafB] };
const root: TreeNode = { label: "root", children: [mid, { label: "c", children: [] }] };
function count(n: TreeNode): number {
let total = 1;
for (const c of n.children) total += count(c);
return total;
}
function labels(n: TreeNode): string {
let out = n.label;
for (const c of n.children) out += "," + labels(c);
return out;
}
function depth(n: TreeNode): number {
let deepest = 0;
for (const c of n.children) {
const d = depth(c);
if (d > deepest) deepest = d;
}
return deepest + 1;
}
console.log(count(root));
console.log(labels(root));
console.log(depth(root));
// Mutation through the recursive field: grafting a subtree.
mid.children.push({ label: "d", children: [{ label: "e", children: [] }] });
console.log(count(root), labels(root), depth(root));
// Recursive values flow through arrays, params, and returns like any
// other record.
function collect(n: TreeNode, into: TreeNode[]): TreeNode[] {
into.push(n);
for (const c of n.children) collect(c, into);
return into;
}
const all = collect(root, []);
console.log(all.length);
let joined = "";
for (const n of all) joined += n.label;
console.log(joined);
// Reference equality is pointer identity, exactly Node.
console.log(root.children[0] === mid, leafA === leafB);