Structural identity is not implicit conversion, and writability decides the layout question

This commit is contained in:
Joseph Ferano 2026-09-12 13:56:24 +07:00
parent 95b4f1173b
commit c49286ca01

View File

@ -128,15 +128,28 @@ front-end question, not a data-model change.
**Two costs, and the second is the real work:** **Two costs, and the second is the real work:**
- **It cuts against the project's standing instinct.** This language has repeatedly chosen explicit over convenient: no - **Not an "explicit over convenient" question — that framing was wrong and is recorded so it is not repeated.** The
implicit numeric conversions, a bare integer cannot become an enum, `:spcae` must fail at the call site. Structural standing rules about implicit numeric conversion and about a bare integer not becoming an enum are about *lossy*
typing says things are compatible when they happen to line up, which is the opposite reflex. Worth deciding things happening silently. A structural match is not lossy; it is a different notion of type *identity*. The two were
deliberately rather than drifting into — and worth asking whether structural compatibility should be *written* at the lumped together in conversation and the author rejected the pairing, correctly. Strong typing and nominal typing are
site that relies on it, the way `(GamepadAxis n)` made an enum conversion explicit without making it implicit. separate choices and this only touches the second.
- **Field order is layout, and that is the hard part.** `{x f32, y f32}` and `{y f32, x f32}` have the same fields and - **Field order is layout, and that is the hard part.** `{x f32, y f32}` and `{y f32, x f32}` have the same fields and
different memory. So either field order becomes significant — surprising, since nothing else about a structural type different memory. So either field order becomes significant — surprising, since nothing else about a structural type
should be — or matching a differently-ordered type requires a copy, which breaks the zero-cost property the whole data should be — or matching a differently-ordered type requires a copy, which breaks the zero-cost property the whole data
model rests on. This is the question to settle first; the type checking is comparatively easy. model rests on. Accepted in conversation as a limitation to respect rather than fight.
**Writability is what decides whether the copy escape exists.** Fields would be writable on the same terms as any struct:
by value you get a copy and writes are local; through a `(Ptr T)` they reach the original. But that settles the layout
question rather than sitting beside it. *Read-only* structural access has an out — the compiler could gather the
requested fields into a temporary, and then field order would not matter at all. *Writable* structural access cannot:
it has to alias the real storage, so the layout must genuinely line up. Decide writability first; the layout answer
follows from it.
**`defclass` may solve this, for the cases that can afford it.** A class has an implementation-defined representation, so
the compiler owns the layout and access goes through metadata rather than a fixed offset — field order stops being a
user-visible fact. The limit is that a class carries identity and metadata, and a `Vector2` should not pay for either.
So plain structs still need their own answer and classes only cover the cases where the overhead was already accepted.
See item 7.
Also unresolved: the shim generates a C typedef per *named* struct, and an anonymous struct has no name to generate one Also unresolved: the shim generates a C typedef per *named* struct, and an anonymous struct has no name to generate one
from. Either anonymous structs cannot cross the FFI, or the generator learns to name them. from. Either anonymous structs cannot cross the FFI, or the generator learns to name them.