Brummel 449df13c9c fix(check): preserve fn-type modes through Subst::apply
`Subst::apply` rebuilt every `Type::Fn` with `param_modes: vec![]`
and `ret_mode: Implicit`, discarding the modes the surrounding type
carried. That was harmless while nothing downstream of inference read
fn-type modes — but it is a latent under-specification: substitution
is meant to be type-preserving, and a fn type's modes are part of its
identity (an `(own T) -> (own U)` contract is not the same contract as
its borrow-returning sibling). Modes are positional over `params`, and
substitution remaps each param type element-wise without changing the
arity, so the mode lists stay aligned — preserving them is a clone, not
a re-derivation.

The typed-MIR boundary (milestone 0060) makes this load-bearing: a node
type synthesised by `lower_to_mir::synth_pure` ends with `subst.apply`,
and the codegen drop path reads `ret_mode == Own` off a call's callee
node to decide RC trackability. With the modes stripped here, an
Own-returning call read back `Implicit`, nothing was trackable, and a
heap-allocated binder leaked at scope close. Preserving the modes
restores that signal at the source rather than re-deriving it in codegen
(which is exactly the re-derivation the milestone exists to delete).

Safe by construction: `unify`'s Fn arm destructures modes with `..` and
compares only params/ret/effects, so carrying modes through `apply`
cannot change unification; and `Type`'s custom `PartialEq` already
treats `Implicit` and `Own` as interchangeable (only `Borrow` is
distinct), so equality outcomes are unaffected except where preserving
a `Borrow` is the more correct answer anyway. Whole `ailang-check`
suite stays green.

RED pin: crates/ailang-check/tests/subst_preserves_modes.rs.
2026-05-31 18:01:24 +02:00
S
Description
No description provided
26 MiB
Languages
Rust 91.6%
Python 4.6%
C 1.8%
LLVM 1.6%
Shell 0.4%