Commit Graph

2 Commits

Author SHA1 Message Date
agra
fcbd7a4235 ffi M4.B dealloc: release strong/copy property ivars + destroyWeak weak
emitObjcDefinedClassDeallocImp now walks the class's #property fields
BEFORE freeing the state struct. For each:

- assign  → no-op (primitives, no ARC traffic).
- strong  → val = load field; objc_release(val).
- copy    → same as strong (the stored value is a +1 retained copy
            produced by the setter's [val copy]; we release it here).
- weak    → objc_destroyWeak(&field) — unregisters the slot from
            libobjc's side-table so the runtime stops tracking it.

Order matters: property releases happen BEFORE freeing the state
struct (which would invalidate the pointers we need to read), which
happens BEFORE [super dealloc] (which eventually frees the Obj-C
instance's own memory). The full sequence is now:

  %state    = object_getIvar(self, __sx_state_ivar)
  // M4.B (this commit):
  for each strong/copy property P:
      val = load struct_gep(state, P.idx); objc_release(val)
  for each weak property P:
      objc_destroyWeak(struct_gep(state, P.idx))
  // M4.0c (already shipped):
  allocator = load struct_gep(state, 0)
  allocator.dealloc(state)
  object_setIvar(self, ivar, null)
  // M1.2 A.6:
  [super dealloc]   // → objc_msgSendSuper2

ffi-objc-arc-02-strong-property now passes: child held by parent's
strong property gets released when parent deallocates, refcount → 0,
child deallocates, both states freed via tracker. Balanced 2/2.

189/189 example tests pass; chess on iOS-sim green. M4 complete.
2026-05-26 23:10:00 +03:00
agra
5c1d00a877 ffi M4.B helpers: objcPropertyKind + ARC runtime decls + xfail tests
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.
2026-05-26 22:58:30 +03:00