Coercion classification now lives in src/ir/conversions.zig behind a *Lowering facade (CoercionResolver), mirroring CallResolver / GenericResolver / ProtocolResolver. Two pure classifiers: - classify(src, dst) -> CoercionPlan (15 kinds: no_op / unbox_any / box_any / closure_to_fn_reject / tuple_elementwise / optional_unwrap / void_to_optional / optional_wrap / erase_protocol / int_to_float / float_to_int / ptr_int_bitcast / widen / narrow / none) — the built-in coercion ladder. - classifyXX(src, dst) -> XXPlan (unbox_any / no_op / erase_protocol / protocol_to_pointer / coerce) — the xx-operator head. coerceToType and lowerXX now `switch (classify…)` then emit; branch order mirrors the originals exactly and every arm reproduces the prior lowering — the f32/f64 Any match dispatch, buildProtocolErasure (lowerXX) vs buildProtocolValue (coerceToType), tuple/optional recursion, and the user-Into fallback + pointer materialization + recursion-guard/diagnostics (which stay in lowerXX / tryUserConversion). IR emission stays entirely in Lowering; the classifiers are pure. lowerXX keeps the operand's lowered Ref type as src_ty. `.none` means no built-in applies (pass through; the Into fallback runs) — no silent default. New pub: isFloat / isIntEx / typeBitsEx / resolveConcreteTypeName (the classifier reads them); coercionResolver() accessor. lower.zig net -54 lines. conversions.test.zig drives CoercionResolver directly: the full classify ladder (no-op, Any box/unbox, widen/narrow, int<->float, ptr<->int, optional wrap/unwrap, void->optional, tuple, closure-reject, .none for two unrelated structs), erase_protocol for a concrete source, and classifyXX (all 5 kinds incl. protocol-to-pointer vs coerce and pointer-materialization -> coerce). zig build, zig build test, tests/run_examples.sh (357/0) all green — no .ir churn.
5.7 KiB
5.7 KiB