Skip to content

-mm=own: tuple field reads, caught values, defaulted parameters, literals into arrays, --di locals - #704

Merged
ASDAlexander77 merged 7 commits into
mainfrom
own-defaultlib-batch
Oct 27, 2026
Merged

ASDAlexander77 merged 7 commits into
mainfrom
own-defaultlib-batch

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Five places where -mm=own refused a borrow or a move that was safe. Each one blocked a DefaultLib test. Spec sections 44–47, and the user doc's limits section is updated.

Change Spec DefaultLib tests unblocked
A field read out of a borrowed tuple value borrows what the tuple borrows 44 array_find2, array_find3
A caught value belongs to the exception until the catch ends 44 catch_library_error
A parameter with a default is kept like any other parameter 45 new Array<number>() (array_every)
An object literal that owns what it holds can move into an array 46 regexp_parse_examples
Under --di, the debugger's record of a local keeps nothing 47 debug-pass array_concat, owning_copies

Details

Tuple field reads (§44). console.log of the T | undefined that find returns reads each field with ts.ExtractProperty. walkBorrowed used to take that read for a keep. Now a number field keeps nothing, and a field that holds a block is walked on as part of the same borrow, so storing it is still refused.

Caught values (§44). ts.SaveCatchVar fills a catch variable and nothing else assigns it. A throw takes what it throws, and own refuses to throw an element, a field or a reassigned global. So nothing but the exception holds the caught value: a call can free what is read out of it only when the call is given something derived from it. Before, <RangeError>e followed by a virtual instanceof call was refused.

Defaulted parameters (§45). MLIRGen builds a parameter with a default as a merge of Value(p) and the default. isParameterSlot and parameterSlotsOf now accept that local as the parameter's slot, so the §33 rules for kept parameters apply. Callers move the argument in, and a path that does not keep it releases what the slot holds: the argument given or the default. isLiteral treats a ts.Undef of an optional type, the argument left out, as holding no block.

Object literals in arrays (§46). recordRetainOf now also lets a literal's read go into a ts.CreateArray element or a ts.ArrayPush item. Each fresh field value's release is erased, and the array's release frees them.

--di locals (§47). collectBorrowerUses now skips ts.DebugVariable.

Tests

  • New positive tests, each run under own, rc, gc and none, with a churn step so a value freed too early reads garbage. Own emits no counting.
    • own_optional_record_read
    • own_param_default_kept
    • own_literal_into_array
  • own_for_of_nested_di: built with --di and run under the JIT.
  • own_caught_value_call: compile-only.
  • Negative test: own_err_optional_record_field_kept.
  • Every new own test fails on the compiler without these changes.
  • Full ctest: 5952/5952. The own subset was run again after merging main: 1548/1548.
  • The DefaultLib own suite is 159/172 with 10 skipped, up from 150/170. rc, gc and none pass every test.

Still failing under own

  • string_matchAll, string_match_lastIndex: keeping a value a generator yields ([...s.matchAll(re)], or a push inside for...of) needs a new way to move a value out of next()'s result.
  • array_reduceRight: the accumulator starts as an element borrow and later becomes owned. Separately, a.concat(b) moves b into the rest array.

The dependent DefaultLib PR follows.

🤖 Generated with Claude Code

ASDAlexander77 and others added 7 commits October 27, 2026 18:04
…nto a place

A function that returns `this` has a result that borrows `this`, and spec 22.4 kept every string
read out of it from being copied - so `this[t] = this[s]` (copyWithin) and a swap's `let v = this[i]`
(reverse) were "borrows an element". The fact is about what is returned: `copyForRetain` now copies
such a read stored into an element or a field, and starts a `let` from a copy when the local cannot
be returned (`mayBeReturned`). A read returned, directly or through a local, stays the borrow.
Spec section 41.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A swap through a local, `let v = a[i]; a[i] = a[j]; a[j] = v`, gives an object element a second
owner, so `sort` and `reverse` over class instances did not compile under own. An expression
statement `[p, q] = [q, p]` whose two sides name the same two elements or fields of one type is
now a `ts.Swap` of the two places: every value keeps one place, so it is emitted with no retain,
release or drop in any model, and its lowering loads both before it stores either.

The inference treats a swap as a drop of a borrow read out of either place (it is the other
place's now, perhaps under another root), and the signature pass as an overwrite of both places
for the callers. Spec section 42; docs limits bullet updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…backs

`reverse` over class instances works with the exchange (spec 42); `sort` does not yet, because it
gives its optional comparison on to a helper, and a callback given on through a parameter has no
callback facts. The limits bullet and spec section 42 say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A callback parameter (a function value, or an optional one) that is only called, tested for being
given, unwrapped or passed to one call is in a family whose members are the functions passed for
it - including those passed for the caller's own parameter it is given from. A family given on is
closed only while the families it is given from and to are. A call of an optional callback once
tested resolves through its family too. DefaultLib `sort` over class instances with a comparison
compiles under own (spec 43).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	docs/own-memory-model.md
#	tslang/docs/superpowers/specs/2026-09-24-own-memory-model-design.md
#	tslang/test/tester/CMakeLists.txt
…rals into arrays, --di locals

Five borrows or moves that own refused although they were safe: nothing
could free what was read, or nothing else held what was moved.

- A field read out of a borrowed tuple value borrows what the tuple
  borrows. console.log of the T | undefined that find returns reads each
  field with ts.ExtractProperty. A number field keeps nothing (spec 44).
- A caught value belongs to the exception until the catch ends. A throw
  takes what it throws, since own refuses to throw a borrow. So a call frees
  what is read out of a catch variable only when it is given something
  derived from it (spec 44).
- A parameter with a default (constructor(private d: T[] = [])) is kept as
  any parameter is. Its slot merges the argument with the default made
  here, and it counts as the parameter's slot. The argument left out is a
  ts.Undef of the optional type, which holds no block (spec 45).
- An object literal that owns what it holds moves into an array literal's
  element or a pushed item, as it already moved into a local, the result or
  a box (spec 46).
- Under --di, a borrowing local's ts.DebugVariable keeps nothing (spec 47).

DefaultLib now compiles these under own: array_find2/3,
catch_library_error, new Array<number>(), regexp_parse_examples, and concat
and owning_copies in the debug pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	tslang/docs/superpowers/specs/2026-09-24-own-memory-model-design.md
#	tslang/test/tester/CMakeLists.txt
@ASDAlexander77
ASDAlexander77 merged commit 9001e0c into main Oct 27, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the own-defaultlib-batch branch October 27, 2026 21:38
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.

1 participant