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>
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
|
| 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
|