Skip to content

A const of an owning let has a slot of its own (#454) - #467

Merged
ASDAlexander77 merged 2 commits into
mainfrom
fix-rc-const-of-let
Oct 3, 2026
Merged

ASDAlexander77 merged 2 commits into
mainfrom
fix-rc-const-of-let

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Fixes #454.

Under rc, let a = new C(); const b = a; a = new C(); freed the first object while b still pointed at it.

The cause: a const initialised with a plain value is folded, so the name is that value and has no storage. Here the value is a read of a's slot, so b held no reference of its own, and reassigning a released the only one.

processConstRef now gives such a const identity storage, as it already does for arrays and Shared<T> handles. The rule covers a const of anything that owns a block, when it is read (through any casts) out of a local that owns what it holds, meaning OWNED_LOCAL_ATTR_NAME: a declared let or a parameter, not a literal's temporary slot. The const then takes its own reference, as a let does, in every model. A const of a const, a call or a field is unchanged.

Under own, b is now a borrow of a rather than a later read of its slot. So own_err_const_alias_field is rejected for what it really is, 'a' is moved here while 'b' borrows it, and its two expectations are updated.

Tests

  • New 00const_of_let.ts, registered as compile and JIT and in the rc/none corpus. It covers:

    • a class, a string and an array const of a let;
    • a const of a parameter;
    • a loop.

    Each is read after allocations of the same size have reused anything freed. It failed under rc before the change.

  • The own corpus is unchanged: 453 / 593 plain, 451 / 593 with --opt, with the same files.

  • Full Release ctest: 3636/3637. The one failure is test-compile-gc-defaultlib-collector, the known local LNK2005 against the stale installed default library, which fails on main the same way.

🤖 Generated with Claude Code

ASDAlexander77 and others added 2 commits October 3, 2026 19:00
Under rc, `let a = new C(); const b = a; a = new C();` freed the first object while `b` still
pointed at it. A `const` whose initializer is a plain value is folded: the name is that value,
with no storage. Here the value is a read of `a`'s slot, so `b` held no reference of its own, and
reassigning `a` released the only one.

processConstRef now gives such a const identity storage, as it already does for arrays and
Shared<T> handles: a const of anything that owns a block, read (through any casts) out of a local
that owns what it holds (OWNED_LOCAL_ATTR_NAME - a declared `let`, a parameter; not a literal's
temporary slot). It then takes its own reference, as a `let` does, in every model. A const of a
const, a call or a field is unchanged.

Under own, `b` is now a borrow of `a` rather than a later read of its slot, so
own_err_const_alias_field is rejected for what it is - `'a' is moved here while 'b' borrows it` -
and its two expectations are updated. The own corpus is unchanged (453 / 593, 451 with --opt).

Test: 00const_of_let (compile and JIT, and in the rc/none corpus): a class, a string and an array
const of a let, a const of a parameter, and a loop, each read after allocations of the same size
have reused anything freed. It failed under rc before the change.

Fixes #454.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	tslang/test/tester/CMakeLists.txt
@ASDAlexander77
ASDAlexander77 merged commit 43b6e63 into main Oct 3, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-rc-const-of-let branch October 3, 2026 18:43
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.

-mm=rc: const b = a (a let of a class) takes no reference; reassigning a frees what b holds

1 participant