/// @module std::core::mul
/// `Mul` and `MulAssign` — operator traits for `*` and `*=`.
///
/// Shape's strict-typing arithmetic dispatch for built-in scalar types
/// (`int`, `number`, `decimal`, `bigint`) emits typed opcodes
/// (`MulInt`, `MulNumber`, ...) 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 Mul for X { method mul(other: X) -> X { ... } }` block; the
/// compiler then desugars `a * b` to `Mul::mul(a, b)` via the operator
/// trait dispatch path at `compiler/expressions/binary_ops.rs:1459-1473`.
///
/// `MulAssign` is the in-place counterpart. `a *= b` is grammar-desugared
/// at parse time (`shape-ast/parser/expressions/binary_ops.rs:236`) to
/// `a = a * b`, so authoring an `impl Mul for X` already covers `*=` for
/// user-defined types without a separate `MulAssign::mul_assign` body.
/// `MulAssign` is declared here for symmetry with future in-place
/// optimizations (e.g. `Matrix.mul_assign(other)` that mutates in-place
/// instead of allocating a fresh value) and so generic bounds
/// `fn f<T: MulAssign>(...)` can be authored today.
///
/// ## Architectural note (W6 imprecision 83 fix, v0.3)
///
/// `MulAssign::mul_assign` is declared with an explicit `Self` return type
/// by convention (the in-place op canonically returns `self` for
/// chaining), but trait methods may omit the return type entirely — the
/// grammar treats a trait method without `: ReturnType` as returning
/// `void`. The Phase 4 close-out workaround that forced `: Self` here to
/// sidestep a parse-failure-cascade bug is no longer required.
///
/// ## Known implementations
///
/// | Type | Dispatch source |
/// |-------------|--------------------------------------------------|
/// | int | typed `MulInt` opcode (no trait dispatch) |
/// | number | typed `MulNumber` opcode (no trait dispatch) |
/// | decimal | typed decimal-mul opcode (no trait dispatch) |
/// | bigint | typed bigint-mul opcode (no trait dispatch) |
/// | Vec<number> | typed `VecMulScalar` / SIMD path (no dispatch) |
/// | Matrix | typed matrix-mul kernel (no trait dispatch) |
/// | TimeSpan | builtin `TIMESPAN_METHODS::mul` PHF entry |
/// | user type X | `impl Mul for X { method mul(other: X) -> X }` |
/// Operator trait for `*`. Implementing `Mul` for a user-defined type
/// enables the binary `*` operator on values of that type. The compiler
/// calls `Mul::mul(lhs, rhs)` via UFCS dispatch through the
/// `function_name_index` for `X::mul`.
trait Mul {
/// Return `self * other`. By convention the result is a fresh value;
/// in-place mutation belongs to `MulAssign::mul_assign`.
method mul(other: Self) -> Self;
}
/// Operator trait for `*=`. The grammar desugars `a *= b` to `a = a * b`,
/// so providing `impl Mul for X` already covers `*=` for user-defined
/// types — `MulAssign` 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 MulAssign {
/// Mutate `self` in place by multiplying by `other`. Return `self` for chaining.
method mul_assign(other: Self) -> Self;
}