diff --git a/crates/ailang-codegen/src/lib.rs b/crates/ailang-codegen/src/lib.rs index 5955530..8d2802c 100644 --- a/crates/ailang-codegen/src/lib.rs +++ b/crates/ailang-codegen/src/lib.rs @@ -2585,6 +2585,56 @@ impl<'a> Emitter<'a> { module = target_module, name = target_def, )); + // owned-temp drop at the call site. An argument that is + // itself an Own-returning heap allocation (an anonymous owned + // temporary, e.g. the in-place result of `RawBuf.set`) and + // that lands in a `borrow`-mode parameter slot is borrowed, + // not consumed, by the callee. After the call returns it is + // dead and no binder owns it, so neither scope-close drop + // emitter (the `Term::Let` binder gate, the Own fn-param drop) + // covers it. Emit its drop here, routed through the same + // per-type drop symbol the let gate uses. Only in the non-tail + // path: a tail call terminates the block (the call result IS + // the fn result) and there is no post-call point to emit at. + // + // Scope of the rule (intentionally narrow, per the raw-buf.5 + // steer — do NOT blanket-drop every owned arg): + // - the arg is `is_rc_heap_allocated` (Own-ret call / fresh + // escaping ctor / lambda); a plain `Term::Var` arg is an + // alias whose owner is some other binder and is dropped + // there, never here; + // - the matching callee param mode is `Borrow` — `Own` slots + // consume the arg (the callee dec's it), `Implicit` is the + // back-compat lane that carries no transfer signal; + // - the dropped SSA is never the call result `dst` (an input + // argument SSA is always distinct from the freshly-minted + // result SSA), so this can never dec a value that flows out + // as the surrounding fn's result. + if !tail { + if let Some(Type::Fn { param_modes, .. }) = self + .module_def_ail_types + .get(target_module) + .and_then(|m| m.get(target_def)) + .cloned() + { + for (i, (arg, (arg_ssa, arg_ty))) in + args.iter().zip(compiled_args.iter()).enumerate() + { + let is_borrow_slot = + matches!(param_modes.get(i), Some(ParamMode::Borrow)); + if is_borrow_slot + && arg_ty == "ptr" + && arg_ssa != &dst + && self.is_rc_heap_allocated(arg) + { + let drop_sym = self.drop_symbol_for_binder(arg, arg_ssa); + self.body.push_str(&format!( + " call void @{drop_sym}(ptr {arg_ssa})\n" + )); + } + } + } + } if tail { self.body .push_str(&format!(" ret {ret} {dst}\n", ret = sig.ret));