Phase 1: describe the Apple ABI once, and check the four copies agree #8
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "phase/1-shim-generator"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes the code half of #7. See
docs/13-phases.mdfor the phase table.What measuring changed
Phase 1 was scoped as "generate the Swift shim". Measuring the two shims before writing a generator disproved that premise, and the measurement is the most useful thing here:
Of the 34 functions in both shims, 7 have byte-identical bodies, 13 differ only in a type or property name, and 14 are genuinely different code.
NSImageView.imageScalingis notUIView.contentMode;placeholderStringis notplaceholder; AppKit's coordinate system is flipped. A generator emitting bodies would emit guesses, and the ~5 200-line savingdocs/09projected was never available.What is available, and is worth more
Every boundary function is declared four times — C header, two
@_cdeclsignatures, one Scalaextern. At 34 functions that is 136 declarations kept in step by hand, and nothing checks them: Swift compiles against its own signature, Scala Native against its ownextern, and the linker matches by name only. AnInt32against anInt64links cleanly, passes CI, and reads a garbage register at runtime — in a callback, on a phone. That failure had no detector anywhere in this repository.What this adds
tools/shim-gen— plain JVM, aggregated into root, no Scala Native and no Xcode, so it runs on the machine where the mistake gets written rather than only on the one that suffers it.Abi.scalaParse.scalaEmit.scalafatalErrorbodies — never a guessed bodyConsistencySuiteAbidescribes themGenerateSuiteAbireproduces exactly what is checked in10 tests, compared as declarations rather than as text: a byte diff would fail on a comment reflow and say nothing about whether the ABI was preserved.
Exit criterion
Abientry; the 2 Swift bodies stay hand-written)Two exceptions, encoded rather than papered over
sui_set_root_viewis exported by the UIKit shim and declared in neither the header norShim.scala— and that is correct. On iOS the Swift side owns@main, because aUIScenedelegate cannot live in Scala Native's static archive, so it hands its root view in. Scala never calls it.Abi.swiftOnlyrecords this so it is not reported as drift.sui_handleandconst uint8_t *to the samePtr[Byte]; demanding it tell them apart would be a false positive forever. Every difference that costs a wrong register still fails the check.Deliberately not done
The generated files are not adopted.
GenerateSuiteproves the swap changes no declaration, but it should be made on a machine that can compile the Swift and rerun the 23-check self-test. That half of #7 stays open.Also in this branch
docs/13-phases.mdphase 1 row and §13.3 rewritten against the measurements rather than the original assumption;docs/12and the decision log updated in the same PR, per rule 5.🤖 Generated with Claude Code
Phase 1 was scoped as "generate the Swift shim". Measuring the two shims before writing any generator disproved that, and the measurement is the most useful thing in this commit: per shim AppKit UIKit functions 34 35 signatures 80 82 generatable bodies 211 212 hand-written Of the 34 functions in both shims, 7 have byte-identical bodies, 13 differ only in a type or property name, and 14 are genuinely different code. NSImageView.imageScaling is not UIView.contentMode; placeholderString is not placeholder; AppKit's coordinate system is flipped. A generator emitting bodies would emit guesses, and the ~5 200-line saving docs/09 projected was never on the table. What is on the table is worth more. Every boundary function is declared four times - C header, two @_cdecl signatures, one Scala extern - which is 136 declarations at current size, kept in step by hand, with nothing checking them. Swift compiles against its own signature, Scala Native against its own extern, and the linker matches by name only: an Int32 against an Int64 links cleanly, passes CI, and reads a garbage register at runtime in a callback on a phone. That failure had no detector anywhere in this repository. tools/shim-gen is that detector, plus the description it checks against: - Abi.scala 34 functions in a type vocabulary with no struct in it - Parse.scala reads declarations back out of C, Swift and Scala - Emit.scala emits the C header, the Scala externs, and Swift signature scaffolding with fatalError bodies - never a guessed body - 10 tests the four artefacts agree; generating from Abi reproduces exactly what is checked in, compared as declarations rather than as text Plain JVM, aggregated into root, no Scala Native and no Xcode. It runs on the machine where the mistake gets written, not only on the one that suffers it. Two exceptions are encoded rather than papered over. sui_set_root_view is exported by the UIKit shim and declared in neither the header nor Shim.scala, and that is correct - on iOS the Swift side owns @main because a UIScene delegate cannot live in Scala Native's static archive, so it hands its root view in. And the Scala comparison runs at machine representation, not at the named type, because Scala Native binds sui_handle and const uint8_t * to the same Ptr[Byte]; demanding it tell them apart would be a false positive forever. The generated files are NOT adopted here. GenerateSuite proves the swap changes no declaration, but it should be made where the Swift can be compiled and the 23-check self-test rerun. Forgejo #7. 108 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>