You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
let x = [];
x.push(x);
print(x); // stack overflow, process aborts
Any Value traversal that recurses structurally has no cycle handling, so a value that (transitively) contains itself crashes the interpreter with a stack overflow or a nested RefCell borrow panic. print is the obvious entry point, but the same applies to:
Display — print, string interpolation, and any error message that formats a value. Found via feat(stdlib): make json_encode strict and add json_encode_lossy 🧊 #199: json_encode_lossy on a map with a cyclic non-string key crashes inside key.to_string(), and even strict json_encode's rejection message (cannot convert a map with non-string key {key}) crashes while formatting the key.
Ord/PartialOrd — comparing two cyclic lists recurses forever (also reachable through heap operations, sort, etc.).
Eq/Hash — equality checks and using a cyclic value as a map key.
The big-stack thread from #176 doesn't help here: cycles are infinite, not merely deep.
Scope
#199 added cycle detection to the JSON encoder's own traversal (visited-pointer set on the recursion path), but it can only protect paths it owns — as soon as it calls Display for key stringification it inherits the crash. The fix belongs in the trait impls in ndc_vm, not in per-caller workarounds.
Possible direction
Track visited Rc pointers during Display and print a placeholder for back-references, the way Python prints [1, [...]]. Ord/Eq/Hash need their own decision (a placeholder isn't meaningful there — erroring or defining cycles as equal-by-identity are options). Alternatively, decide whether cyclic values should be constructible at all.
Repro
Any
Valuetraversal that recurses structurally has no cycle handling, so a value that (transitively) contains itself crashes the interpreter with a stack overflow or a nestedRefCellborrow panic.printis the obvious entry point, but the same applies to:Display—print, string interpolation, and any error message that formats a value. Found via feat(stdlib): make json_encode strict and add json_encode_lossy 🧊 #199:json_encode_lossyon a map with a cyclic non-string key crashes insidekey.to_string(), and even strictjson_encode's rejection message (cannot convert a map with non-string key {key}) crashes while formatting the key.Ord/PartialOrd— comparing two cyclic lists recurses forever (also reachable through heap operations,sort, etc.).Eq/Hash— equality checks and using a cyclic value as a map key.The big-stack thread from #176 doesn't help here: cycles are infinite, not merely deep.
Scope
#199 added cycle detection to the JSON encoder's own traversal (visited-pointer set on the recursion path), but it can only protect paths it owns — as soon as it calls
Displayfor key stringification it inherits the crash. The fix belongs in the trait impls inndc_vm, not in per-caller workarounds.Possible direction
Track visited
Rcpointers duringDisplayand print a placeholder for back-references, the way Python prints[1, [...]].Ord/Eq/Hashneed their own decision (a placeholder isn't meaningful there — erroring or defining cycles as equal-by-identity are options). Alternatively, decide whether cyclic values should be constructible at all.🤖