shape-runtime 0.2.0

Bytecode compiler, builtins, and runtime infrastructure for Shape
Documentation
/// @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
}