Repository navigation
A closure over a const read out of a place captures the value it read - #654
Merged
Merged
Conversation
`const v = n.v; n.v = d; const c = () => v.x;` gave `d.x` under gc and none. A const read out of a field, an element or a global is folded into the read and has no storage of its own (under rc it does, see processConstRef). A by-value capture was taken through the reference GetReferenceFromValue found for it - for a folded read, the place it was read from - and CaptureOpLowering dereferenced that place when the closure was made, after the overwrite. A capture now goes through a reference only when that reference is the variable's own storage (a variable, a parameter, a captured cell); anything else captures the value itself. Fuzzer (#646) G1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ce-read # Conflicts: # tslang/test/tester/CMakeLists.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes fuzzer bug G1 (#646): under gc and none, a closure over a const read out of a field saw a later write to that field.
Why
A const read out of a field, an element or a global is folded into the read, so it has no storage of its own under gc and none. Under rc it does (
processConstRef), which is why rc was right. A by-value capture usedGetReferenceFromValue, which for a folded read is the place it was read from, andCaptureOpLoweringdereferenced that place when the closure was made, after the overwrite.Fix
A capture goes through a reference only when that reference is the variable's own storage: a
ts.Variable, a parameter, or a captured cell. Anything else captures the value itself. Read-write captures (by reference) are unchanged.Tests
tests/00closure_const_read.ts: a closure over a const read out of a field, an element and a global, each overwritten before the closure is made. It fails under gc and none on main. It runs compiled, under JIT, and in the rc/none corpus.The interface-field variant (
const v = ix.v) is left out of the test until #651 merges. Without #651 that const has no count under rc (R7), and capturing the value it actually read turns R7's dangling const into a crash. Before this change the capture happened to re-read the live new value.Verification
ctest -C Release: Windows 4982/4982, WSL Linux 4964/4964.mm_fuzz.py --jobs 10 run --count 500 --seed 1: only the 3 known own failures (R1, O4).🤖 Generated with Claude Code