/// @module std::core::add
/// `Add` and `AddAssign` — operator traits for `+` and `+=`.
///
/// Shape's strict-typing arithmetic dispatch for built-in scalar types
/// (`int`, `number`, `decimal`, `bigint`, `string`) emits typed opcodes
/// (`AddInt`, `AddNumber`, `StringConcatTyped`, ...) at compile time and
/// never routes through this trait. User-defined types (`type X { ... }`)
/// that want to participate in `+` / `+=` opt in by providing an
/// `impl Add for X { method add(other: X) -> X { ... } }` block; the
/// compiler then desugars `a + b` to `Add::add(a, b)` via the operator
/// trait dispatch path at `compiler/expressions/binary_ops.rs:69-83`.
///
/// `AddAssign` is the in-place counterpart. `a += b` is grammar-desugared
/// at parse time (`shape-ast/parser/expressions/binary_ops.rs:295-307`) to
/// `a = a + b`, so authoring an `impl Add for X` already covers `+=` for
/// user-defined types without a separate `AddAssign::add_assign` body.
/// `AddAssign` is declared here for symmetry with future in-place
/// optimizations (e.g. `Buffer.add_assign(other)` that mutates in-place
/// instead of allocating a fresh value) and so generic bounds
/// `fn f<T: AddAssign>(...)` can be authored today.
///
/// ## Architectural note (Phase 4 close, 2026-05-16)
///
/// `AddAssign::add_assign` is declared with an explicit `Self` return type
/// (the in-place op canonically returns `self` for chaining) **rather than
/// a return-typeless `add_assign(other: Self)` signature**, because the
/// latter triggers a pre-existing compiler bug that manifests when a
/// prelude-imported trait carries a method without an explicit return
/// type. Empirically bisected at phase-4 close 2026-05-16:
///
/// | Trait shape | Smoke 2 / `+=` |
/// |---------------------------------------------------|----------------|
/// | `add_assign(other: Self)` (no return type) | BROKEN |
/// | `add_assign()` (no args, no return type) | BROKEN |
/// | `add_assign(other: Self): int` | OK |
/// | `add_assign(other: Self): Self` | OK |
/// | (only `trait Add` in prelude, no second trait) | OK |
/// | (add.shape present but NOT imported by prelude) | OK |
///
/// Failure mode: when the return-typeless trait is prelude-imported,
/// `Vec.map<U>` monomorphization for `[1,2,3,4,5].map(|x| x*2)` (Smoke 2)
/// skips the `Vec.map::i64_…` PHF specialization and falls through to
/// the generic `__main__` path that hits the V3-S5 ckpt-2 `map` SURFACE;
/// independently, `+=` fixture exhibits non-deterministic
/// `free(): double free detected in tcache 2` on cleanup (correct output
/// produced first, then refcount-corrupted teardown).
///
/// Tracked as a follow-up sub-cluster
/// `phase-4-followup-return-typeless-trait-method-monomorphization`:
/// root cause is in the trait-registration → type-inference →
/// monomorphization pipeline, where a trait method without an explicit
/// return type leaves a `Type::Variable(TypeVar::fresh())` or
/// `Type::Unit` placeholder in the trait def that the inferencer later
/// picks up when resolving downstream call-site return-type positions,
/// affecting both `Vec.map<U>` specialization-key mangling AND the
/// refcount discipline on TypedObject heap returns. The genuine fix
/// flips trait-def return-type propagation in
/// `crates/shape-runtime/src/type_system/` (territory beyond Phase 4
/// scope per supervisor "bounded to regression fix + framing-discipline
/// recovery" disposition).
///
/// ## Known implementations
///
/// | Type | Dispatch source |
/// |-------------|--------------------------------------------------|
/// | int | typed `AddInt` opcode (no trait dispatch) |
/// | number | typed `AddNumber` opcode (no trait dispatch) |
/// | decimal | typed decimal-add opcode (no trait dispatch) |
/// | bigint | typed bigint-add opcode (no trait dispatch) |
/// | string | typed `StringConcatTyped` opcode (no dispatch) |
/// | Array<T> | typed `ArrayConcat` opcode (no trait dispatch) |
/// | DateTime | builtin `DATETIME_METHODS::add` PHF entry |
/// | TimeSpan | builtin `TIMESPAN_METHODS::add` PHF entry |
/// | user type X | `impl Add for X { method add(other: X) -> X }` |
/// Operator trait for `+`. Implementing `Add` for a user-defined type
/// enables the binary `+` operator on values of that type. The compiler
/// calls `Add::add(lhs, rhs)` via UFCS dispatch through the
/// `function_name_index` for `X::add`.
trait Add {
/// Return `self + other`. By convention the result is a fresh value;
/// in-place mutation belongs to `AddAssign::add_assign`.
add(other: Self): Self
}
/// Operator trait for `+=`. The grammar desugars `a += b` to `a = a + b`,
/// so providing `impl Add for X` already covers `+=` for user-defined
/// types — `AddAssign` is declared here for generic bounds and for the
/// future in-place specialization opt-in.
///
/// The return type is `Self` (the in-place op returns `self` for
/// chaining) to sidestep a pre-existing compiler bug — see the module-
/// level architectural note. Implementations may mutate `self` in place
/// and `return self` from the method body; the desugar at parse time
/// means the return value is unused under current semantics.
trait AddAssign {
/// Mutate `self` in place by adding `other`. Return `self` for chaining.
add_assign(other: Self): Self
}