Skip to content

-mm=own: a library exports which of its functions destroy nothing - #409

Merged
ASDAlexander77 merged 1 commit into
mainfrom
own-shared-no-drops
Sep 29, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
own-shared-no-drops

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

The last phase-4 leftover (spec §3.2, §15.7): a library built under -mm=own tells its importers which of its exported functions destroy nothing.

What crosses, and why only this

A library now exports __tsown_<module> beside its marker __tsmm_own_<module>. It lists, one name per line, the exported functions that have __own_no_drops.

The other two facts can't cross. __own_params and __own_result_borrows change the callee's body: its retains are gone.

  • An importer that doesn't know them releases a moved argument or frees a borrowed result, which is a double free.
  • A module that links the library statically is exactly such an importer. It re-parses the library's source and never sees a binary.
  • Under own there is no counting, so no adapter can turn a borrow back into an owned result.

A missing __own_no_drops only makes an importer report more errors, so it is safe to export in every link mode.

How

Export. OwnershipSignaturePass writes the list as a copy of the marker global, under another name and value. It is therefore exported the way the marker is on Windows and Linux, and MLIRGen emits the same IR under own as under rc (test-own-mlirgen-matches-rc).

Import. mlirGenImportSharedLib reads the list beside the marker, from the loaded library or from the file. It records the names as the module attribute ts.own_imported_no_drops in every model, for the same reason; only own reads it.

Resolution. The signature pass treats a call to a listed function as known and drop-free, like the .instanceOf slot, in two forms:

  • a declaration linked through the import library;
  • Load(AddressOf @f), where f is filled by SearchForAddressOfSymbol("<name>") when the library is loaded at run time. Such a global is not trusted if its address is used other than to load from it.

A virtual call to a method of an imported class stays unknown, because the importer may override it.

The JIT cache already hashes every imported library as a dependency, so rebuilding a library invalidates its importers' cached objects.

Tests

  • own/import_own_no_drops.ts + own/export_own_no_drops.ts, as a -shared pair under JIT and AOT. The importer holds a borrow of a parameter's field across calls to total and M.first.
  • own-shared-no-drops.cmake (Windows) checks two things:
    • import_own_err_imported_drops.ts is still rejected, because shrink removes an element and isn't listed;
    • the same positive program is rejected against the library built under rc, which lists nothing. That is the fact's teeth.
  • Windows Release: 3232/3232.
  • Linux (WSL): 3218/3218. The -shared pair ran there too, and it can only compile if the facts crossed.
  • -mm=own corpus: unchanged at 303 compiling files.

Found, not fixed

An importer under own can't yet build an object of an imported class: new H() through the library reports 'this value' is used here after its value was moved. This predates the change and is recorded in §15.7.

Independent of #407 and #408.

🤖 Generated with Claude Code

A library built under own now writes, beside its memory-model marker
__tsmm_own_<module>, an exported string __tsown_<module>: the exported
functions that have __own_no_drops, one name per line. The signature
pass writes it as a copy of the marker global, so it is exported the way
the marker is on every platform and MLIRGen emits the same under own as
under rc.

The importer reads it beside the marker (from the loaded library or from
the file) and keeps the names on its module, ts.own_imported_no_drops,
in every model. The signature pass resolves a call to one - through a
declaration linked with the import library, or through the global a
library loaded at run time fills from SearchForAddressOfSymbol - as
known and drop-free, like the .instanceOf slot. A global whose address
is used other than to load from it is not trusted.

Only this fact crosses. A missing one only makes an importer report more,
so a module linking the library statically, which re-parses its source
and sees no facts, stays sound. The kept-parameter and borrowed-result
facts change the callee's body; an importer that does not know them
frees twice, and under own there is no counting to adapt the call with.

Tests: a -shared pair under JIT and AOT (a borrow of a parameter's field
held across the imported calls), and on Windows a script that checks an
unlisted dropping function is still rejected and that the same program
is rejected against the library built under rc, which lists nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 06e88a2 into main Sep 29, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the own-shared-no-drops branch September 29, 2026 21:56
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