emitObjcDefinedPropertySetter now dispatches on objcPropertyKind to
emit the right runtime ops per Apple's ARC contract:
- assign → bare store (primitives, explicitly opted-out object slots).
- strong → load old; objc_retain(new); store new; objc_release(old).
Apple's runtime treats release(NULL) as a safe no-op, so
no explicit null-check on the old value.
- weak → objc_storeWeak(field_addr, val) — handles first-store
(init) and re-store (destroy + init) atomically. Registers
the slot with libobjc's side-table; the runtime auto-nils
it when the target deallocates.
- copy → [val copy] (sends `copy` selector — returns retained per
the NSCopying contract); load old; store the copied
instance; release old.
Side-effect on the weak path: even with the bare-load getter still in
place (loaded directly from the slot), weak reads work because Apple's
runtime side-table-nils the slot at target dealloc. The getter
improvement via objc_loadWeakRetained is the next commit and is
needed for race-safe reads (between load and use, the target could
deinit on another thread); for the single-threaded test scenarios
the bare load is sufficient.
ffi-objc-arc-02-strong-property advances from "child dealloc'd at
midpoint" to "unbalanced; alloc=2 dealloc=1" — strong setter now
retains, but the M4.B-dealloc cleanup hasn't landed so the child
held by the property isn't released when the parent deallocates.
Final commit (M4.B dealloc) closes the loop.
ffi-objc-arc-03-weak-property turns fully green: storeWeak +
auto-nil side-table do the work.
189/189 example tests pass; chess on iOS-sim green.
Three pieces, no behavior change yet:
1. `ObjcPropertyKind` enum (strong/weak/copy/assign) + `objcPropertyKind`
helper in lower.zig. Reads `field.property_modifiers`, applies the
default rule (`*<ObjC-class>` → strong; primitives → assign), and
emits loud diagnostics for the silent-error budget:
- unknown modifier name (typo) → "expected one of: strong, weak, copy, ..."
- conflicting modifiers (e.g. `strong,weak`) → "mutually exclusive"
- `weak` on non-object slot → "requires a pointer-to-Obj-C-class type"
- `copy` on non-object slot → same
- `strong` (default or explicit) on `*void` → "ambiguous: specify
#property(strong|weak|copy|assign) explicitly"
Called from `emitObjcDefinedClassPropertyImps` for validation; the
returned kind isn't wired into setter/getter/dealloc yet — that's
the next three commits.
2. `ensureArcRuntimeDecls` lazily declares libobjc's ARC helpers:
objc_retain, objc_release, objc_storeWeak, objc_loadWeakRetained,
objc_initWeak, objc_destroyWeak. Uses the existing
`ensureCRuntimeDecl` pattern; idempotent.
3. Fix existing NSObject method names in std/objc.sx — `isEqual_`,
`isKindOfClass_`, `respondsToSelector_` had trailing underscores
that the selector mangling turned into double-colon selectors
(`isEqual::`). Removed the trailing underscore so the selectors
come out as `isEqual:`, `isKindOfClass:`, `respondsToSelector:`
as Apple's runtime expects.
4. Two xfail regression tests:
- ffi-objc-arc-02-strong-property: assigns child to parent's strong
property, releases the original child reference. Midpoint check:
child's dealloc should NOT have fired (strong setter retained).
Pre-M4.B-setter: child dealloc fires immediately → "FAIL: child
dealloc'd at midpoint" snapshot. Exit code 1.
- ffi-objc-arc-03-weak-property: assigns target to holder's weak
property, releases target. Reads holder.target → should be null
(auto-niled). Pre-M4.B-getter/setter: reads stale pointer →
"FAIL: weak property didn't auto-nil" snapshot.
These will turn green as M4.B setter (commit 2), getter (commit 3),
and dealloc-cleanup (commit 4) land. Each subsequent commit updates
the snapshot to reflect the now-passing output.
189/189 example tests pass; chess on iOS-sim green.