fix: resolve cross-module borrow modes in the uniqueness pass (refs #43)
A2a leg of the owned-RawBuf drop-leak. Prerequisite: regression-free
and independently correct, but does NOT alone close the leak — the
three shadow_rebind RED tests (f7f4c3b) stay RED pending the A2b
shadow-collapse leg, which needs a UniquenessTable contract change and
its own spec.
The leg. The uniqueness pass infer_module runs strictly per module
(called per-Emitter from codegen lib.rs:993 with a single Module). The
module holding `main` registers only its own + builtin signatures in
`globals`; the monomorphised intrinsic signatures (RawBuf_get__Int with
param_modes=[Borrow,Implicit], etc.) live exclusively in the raw_buf
module's separate pass. So a qualified cross-module callee
`raw_buf.RawBuf_get__Int` missed `main`'s globals entirely, returned
vec![], and the walk defaulted every arg to Position::Consume —
mis-counting the borrowed `buf` arg as a consume and inflating the
binder's consume_count, which gates off the scope-close drop. This is a
latent over-conservative default affecting ANY cross-module borrow
callee, not just RawBuf.
The fix (additive, two files). infer_module is kept as a thin wrapper
over a new infer_module_with_cross(m, &cross_module_types) that threads
the workspace-flat module->def->Type table (the same module_def_ail_types
codegen already builds and the A1 emit_call rule already reads). On a
same-module globals miss, callee_arg_modes now falls back to
cross_module_callee_ty: split the callee on the last `.`, resolve
<mod>.<def> positionally against the cross-module table, and read its
Type::Fn.param_modes. Same-module + builtin lookup stays the first
resort; unqualified names and absent entries fall through to the
existing all-consume default unchanged. Modes are matched positionally,
so RawBuf.set's Own first param still counts as a consume (the
new/set-chain binders keep consume_count==1 and are not dropped —
correct, set is in-place, same slab).
Verified: full workspace green except the three intended-RED
shadow_rebind tests; ailang-check and ailang-codegen (incl.
intercepts_bijection_with_intrinsic_markers) green; A1's
raw_buf_owned_drop_balances_rc_stats still green. raw_buf_int.ail still
leaks (live=1) — that is the A2b leg, addressed next.
This commit is contained in:
@@ -38,7 +38,7 @@ use ailang_core::ast::*;
|
||||
use ailang_core::Workspace;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use ailang_check::uniqueness::{infer_module, UniquenessTable};
|
||||
use ailang_check::uniqueness::{infer_module_with_cross, UniquenessTable};
|
||||
|
||||
mod drop;
|
||||
mod escape;
|
||||
@@ -989,8 +989,14 @@ impl<'a> Emitter<'a> {
|
||||
// build the per-module uniqueness side-table once
|
||||
// per emitter. The inference is pure (no I/O, no global state),
|
||||
// so doing it here is cheap and keeps codegen's input self-
|
||||
// contained.
|
||||
let uniqueness = infer_module(module);
|
||||
// contained. Pass the workspace-flat cross-module signature
|
||||
// table so a qualified cross-module borrow callee (e.g. the
|
||||
// monomorphised intrinsic `raw_buf.RawBuf_get__Int` reached
|
||||
// from a `main` in another module) resolves to its registered
|
||||
// `param_modes` instead of defaulting every arg to Consume —
|
||||
// which otherwise inflates the borrow binder's consume_count
|
||||
// and gates off its scope-close drop.
|
||||
let uniqueness = infer_module_with_cross(module, module_def_ail_types);
|
||||
|
||||
Self {
|
||||
module,
|
||||
|
||||
Reference in New Issue
Block a user