paavohtl Claude Opus 4.6 (1M context) commited on
Commit
9686af7
·
1 Parent(s): ca43e7d

docs: update CLAUDE.md for _raw methods, noalias rule, typed messages

Browse files

- bind_! macro now generates _raw associated functions alongside &mut self methods
- Noalias rule: always use _raw methods on WA objects, never create references
- broadcast_message_raw documented
- CTask::ddgame_raw pattern documented

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. CLAUDE.md +3 -3
CLAUDE.md CHANGED
@@ -337,11 +337,11 @@ vtable_replace!(DSSoundVtable, va::DS_SOUND_VTABLE, {
337
  - Naked asm uses `naked_asm!` (Rust 1.79+ syntax), not `asm!`
338
  - **Generic CTask<V> for typed vtables**: `CTask<V: Vtable = *const c_void>` and `CGameTask<V: Vtable = *const c_void>` take a vtable pointer type parameter. Subclasses specify their typed vtable: `CTaskTeam { base: CTask<*const CTaskTeamVTable> }`. The `Vtable` marker trait is auto-implemented by the `#[vtable]` macro. `FieldRegistry` derive supports generics by substituting type params with their defaults for `offset_of`/`size_of`.
339
  - **`Task` trait**: Provides `task()`, `ddgame()`, `as_task_ptr()`, `as_task_ptr_mut()`, and `broadcast_message()` on all CTask subclasses. Eliminates `.base.base` chains. Implemented for all task types in `task/mod.rs`.
340
- - **`broadcast_message()`**: Pure Rust port of CTask::HandleMessage (0x562F30). Iterates the sparse children array and calls each child's vtable[2]. Uses `read_volatile` for `children_watermark` and `children_data` — **required** because LLVM caches these reads across virtual dispatch calls even through `*mut`.
341
  - **Typed vtable structs via `#[vtable(...)]`**: Use the attribute macro to define vtable structs with `fn(this: *mut T, ...)` shorthand and `#[slot(N)]` for sparse layouts. The macro handles `unsafe extern "thiscall"`, gap-filling, registry metadata, generates `bind_!` calling wrappers, and implements the `Vtable` marker trait. See the `#[vtable(...)]` section above.
342
  - **CTask base vtable**: 7 slots (not 8). ProcessFrame is slot 6. Slots 7+ are class-specific extensions. CGameTask adds no virtuals.
343
- - **`bind_XxxVtable!`**: Generated by `#[vtable(...)]`, creates `&mut self` method wrappers on the class struct. Supports dotted field paths for inherited vtables: `bind_CTaskTeamVTable!(CTaskTeam, base.vtable)`.
344
- - **Noalias caution**: `bind_!` methods use `&mut self`, which gives LLVM noalias guarantees. In weapon dispatch and other code that mixes Rust and WA function calls through bridges, prefer raw-pointer associated functions (e.g., `CTaskWorm::set_state_raw(ptr, state)`) to avoid potential miscompilation from LLVM caching reads across bridge calls.
345
  - **`vcall!` macro**: Still available for raw-pointer vtable dispatch without bind wrappers. Expands to `((*(*obj).vtable).method)(obj, args...)`.
346
 
347
  ## FFI Style
 
337
  - Naked asm uses `naked_asm!` (Rust 1.79+ syntax), not `asm!`
338
  - **Generic CTask<V> for typed vtables**: `CTask<V: Vtable = *const c_void>` and `CGameTask<V: Vtable = *const c_void>` take a vtable pointer type parameter. Subclasses specify their typed vtable: `CTaskTeam { base: CTask<*const CTaskTeamVTable> }`. The `Vtable` marker trait is auto-implemented by the `#[vtable]` macro. `FieldRegistry` derive supports generics by substituting type params with their defaults for `offset_of`/`size_of`.
339
  - **`Task` trait**: Provides `task()`, `ddgame()`, `as_task_ptr()`, `as_task_ptr_mut()`, and `broadcast_message()` on all CTask subclasses. Eliminates `.base.base` chains. Implemented for all task types in `task/mod.rs`.
340
+ - **`broadcast_message()` / `broadcast_message_raw()`**: Pure Rust port of CTask::HandleMessage (0x562F30). Iterates the sparse children array and calls each child's vtable[2]. Uses `read_volatile` for `children_watermark` and `children_data` — **required** because LLVM caches these reads across virtual dispatch calls even through `*mut`. Prefer `CTask::broadcast_message_raw(ptr, ...)` over `(*ptr).broadcast_message(...)` — see noalias rule above.
341
  - **Typed vtable structs via `#[vtable(...)]`**: Use the attribute macro to define vtable structs with `fn(this: *mut T, ...)` shorthand and `#[slot(N)]` for sparse layouts. The macro handles `unsafe extern "thiscall"`, gap-filling, registry metadata, generates `bind_!` calling wrappers, and implements the `Vtable` marker trait. See the `#[vtable(...)]` section above.
342
  - **CTask base vtable**: 7 slots (not 8). ProcessFrame is slot 6. Slots 7+ are class-specific extensions. CGameTask adds no virtuals.
343
+ - **`bind_XxxVtable!`**: Generated by `#[vtable(...)]`, creates both `&mut self` method wrappers AND `_raw` associated functions (raw pointers) on the class struct. Supports dotted field paths for inherited vtables: `bind_CTaskTeamVTable!(CTaskTeam, base.vtable)`.
344
+ - **Noalias rule — ALWAYS use `_raw` methods on WA objects**: `bind_!` methods with `&mut self` give LLVM noalias guarantees that WA bridge calls violate, causing silent miscompilation. **Always use `Type::method_raw(ptr, args)` instead of `(*ptr).method(args)`**. Similarly use `CTask::ddgame_raw(ptr)` instead of `(*ptr).ddgame()`, and `CTask::broadcast_message_raw(ptr, ...)` instead of `(*ptr).broadcast_message(...)`. For `as_task_ptr`, just cast: `worm as *mut CTask`.
345
  - **`vcall!` macro**: Still available for raw-pointer vtable dispatch without bind wrappers. Expands to `((*(*obj).vtable).method)(obj, args...)`.
346
 
347
  ## FFI Style