Skip to content

Fix unbounded recursion on cyclic tables at the eval boundary - #2

Merged
smn merged 2 commits into
mainfrom
cyclic-table-fix
Jul 27, 2026
Merged

smn merged 2 commits into
mainfrom
cyclic-table-fix

Conversation

@smn

@smn smn commented Jul 27, 2026

Copy link
Copy Markdown

Mirror of tv-labs#407.

Bounds cyclic table walks at the eval boundary: Value.decode leaves the table's {:tref, id} reference in place, and Display renders a :circular peek. Also tracks the ancestor set as a plain map to satisfy OTP 28's opacity checker.

Upstream PR: tv-labs#407

fedme added 2 commits July 27, 2026 11:00
The standard Lua OOP idiom (T.__index = T) creates tables that contain
themselves. Both eval-boundary walks recursed into them forever,
growing memory without bound: Value.decode in decode: true mode and
Display.peek_table in decode: false mode.

The walk now terminates at the point of recurrence: decode leaves the
table's {:tref, id} reference there, mirroring how functions already
pass through as opaque references, and Display renders a :circular
peek. Shared non-cyclic references decode in full.
OTP 28's opacity checker false-positives on the MapSet the walkers
capture in their entry closures (call_without_opaque on every MapSet
call), failing CI. Specs on the private clauses don't appease it. A
plain map with the ids as keys is the same structure MapSet wraps, so
behavior is identical and there is no opaque type left to police.
@smn
smn merged commit b74611e into main Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants