fix: let-alias-aware mode propagation through Term::Let
Closes the carve-out shared by 18d.4 Iter A and 18g.1's pre-tail-
call seam: a let-binder whose value is Term::Var aliasing a non-
Own fn-param now inherits the param's mode in current_param_modes
for the duration of the let body. Without this, (let a t (match
a ...)) where t is Implicit / Borrow defeated scrutinee_is_owned
(default for non-fn-param scrutinees was 'owned'), and the arm-
close drop fired on pattern-binders whose underlying memory
belongs to the caller — refcount underflow / SIGSEGV on the next
recursion.
Implementation: Term::Let lowering checks if value is Term::Var
referencing a name in current_param_modes; if so, inserts that
mode under the let-binder's name for the body and restores on
let-close (push/pop pattern, symmetric with locals).
Red:
alloc_rc_let_alias_of_implicit_param_does_not_dec_borrowed_children
on examples/rc_let_alias_implicit_param. Pre-fix: SIGSEGV / RC
underflow on the second iteration of loop. Post-fix: matches
alloc=gc ('0').
The Borrow-ret-mode case is already covered by the language-design
rule (typechecker rejects 'consume-while-borrowed' on the
borrow-aliasing shape); the Implicit-mode case is what the carve-
out actually surfaces. Both are now closed at codegen-time.
Two of the three known debts from the 18g tidy now resolved
(stat-of-N earlier in the session, let-alias here). The remaining
debt — tag-conditional partial-drop helper for the dynamic-tag
carve-outs in lower_match and emit_inlined_partial_drop — is the
substantively larger piece and stays queued.
This commit is contained in:
@@ -1822,7 +1822,42 @@ impl<'a> Emitter<'a> {
|
||||
let (val_ssa, val_ty) = self.lower_term(value)?;
|
||||
self.locals
|
||||
.push((name.clone(), val_ssa.clone(), val_ty.clone(), val_ail));
|
||||
// Iter 18g.tidy.fu: let-alias-aware mode propagation.
|
||||
// If `value` is a `Term::Var` referencing a name in
|
||||
// `current_param_modes`, the let-binder inherits that
|
||||
// mode for the duration of the body. Without this,
|
||||
// `(let a t (match a ...))` where `t` is an Implicit
|
||||
// / Borrow-mode param defeats the
|
||||
// `scrutinee_is_owned` gate in `lower_match` (the
|
||||
// gate looks up `a` in `current_param_modes`, misses,
|
||||
// and defaults to "owned" — Iter A then dec's
|
||||
// pattern-binders whose underlying memory belongs to
|
||||
// the caller).
|
||||
//
|
||||
// Restored on let-body-close (push/pop pattern).
|
||||
let inherited_mode: Option<ParamMode> = match value.as_ref() {
|
||||
Term::Var { name: src } => {
|
||||
self.current_param_modes.get(src).copied()
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let prior_mode = if let Some(m) = inherited_mode {
|
||||
let prior = self.current_param_modes.insert(name.clone(), m);
|
||||
Some(prior)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let r = self.lower_term(body);
|
||||
if let Some(prior) = prior_mode {
|
||||
match prior {
|
||||
Some(m) => {
|
||||
self.current_param_modes.insert(name.clone(), m);
|
||||
}
|
||||
None => {
|
||||
self.current_param_modes.remove(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.locals.pop();
|
||||
// Iter 18d.3: lift the binder's move set out of the
|
||||
// side table. The binder is leaving scope here; we
|
||||
|
||||
Reference in New Issue
Block a user