Two converging paths now allocate the state struct via the protocol's
allocator instead of raw malloc:
(1) sx-side `Cls.alloc()`: compiler intercepts in `lowerObjcStaticCall`
when the receiver is a sx-defined `#objc_class` and the method is
the niladic `alloc`. Emits the inline alloc-and-init sequence
using the caller's `current_ctx_ref` as the context — so
`push Context.{ allocator = my_arena } { let f := SxFoo.alloc(); }`
honors `my_arena` end-to-end. The msgSend dispatch is bypassed
entirely for this case.
(2) Obj-C-runtime `[Cls alloc]` (Info.plist principal class, NSCoder,
UIKit reflection): the synthesized `+alloc` IMP shim reads
`__sx_default_context.allocator` and calls into the same shared
helper. The IMP has `has_implicit_ctx = false` and runs with no
caller-side context — the default GPA is the right policy choice
for "everything Apple's runtime instantiates".
Shared helper `emitObjcDefinedAllocAndInit(fcd, cls_ref, ctx_addr)`
does the work: `class_createInstance` → `ctx.allocator.alloc(STATE_SIZE)`
via the inline-protocol fn-ptr → memset 0 → store allocator at
state[0] (the M4.0a slot, captured for -dealloc's later use) →
`object_setIvar(instance, __sx_state_ivar, state)`. Loud failures
on missing globals via the diagnostics system.
The sx-side interception must explicitly bitcast the
`class_createInstance` result from `*void` to the method's declared
return type (`*<Cls>` or `?*<Cls>`). lowerVarDecl reads the Ref's IR
type when no type annotation is present, and coerceToType is a
no-op for ptr→ptr — without the bitcast, `let f := SxFoo.alloc();`
binds `f` at `*void` and downstream `f.class` / `f.method()` fails
to find anything.
-dealloc still uses `free(state)` (M4.0c rewrites it). 184/184 tests
pass; chess on iOS-sim green.