Iter 18c.2: linearity check + suggested_rewrites
Adds a per-fn linearity check that runs on every fn whose param_modes are all explicit (Borrow or Own, no Implicit), tracks each binder's consume/borrow state through the AST, and emits two diagnostics with form-A `suggested_rewrites`: - `use-after-consume` — a binder is referenced after it has already been consumed. - `consume-while-borrowed` — a binder is consumed while a borrow of it is still live (sibling arg slot, or an enclosing-fn Borrow param). Fns with any Implicit param skip the check entirely; that's the back-compat lane that keeps every existing fixture green. Pure diagnostic addition: no IR change, no codegen change, no runtime change. Each diagnostic carries a non-empty `suggested_rewrites` whose `replacement` is form-A AILang text (typically `(clone <name>)`). Adds two new public entrypoints to ailang-surface — `parse_term` and `term_to_form_a` — so the round-trip `parse_term(term_to_form_a(t)) ≡ t` is the contract every emitted replacement must satisfy. Tests assert the contract. Linearity pass runs only on modules that typechecked clean (any upstream typecheck error suppresses it for that module — running on partly-defined IR would produce noise). Tests: 3 unit tests in `linearity::tests`, 3 integration tests in `ailang-check/tests/workspace.rs`, 1 serialisation test in `diagnostic.rs`. `cargo test --workspace` green.
This commit is contained in:
@@ -37,6 +37,8 @@ use ailang_core::Workspace;
|
||||
use indexmap::IndexMap;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
mod linearity;
|
||||
|
||||
/// Metavariable substitution. Maps fresh metavar ids (from `$m<id>` in
|
||||
/// `Type::Var.name`) to the type they have been unified against.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
@@ -624,9 +626,21 @@ pub fn check_workspace(ws: &Workspace) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||
for name in order {
|
||||
let m = &ws.modules[name];
|
||||
for e in check_in_workspace(m, ws, &module_globals) {
|
||||
let typecheck_errors = check_in_workspace(m, ws, &module_globals);
|
||||
let had_typecheck_errors = !typecheck_errors.is_empty();
|
||||
for e in typecheck_errors {
|
||||
diagnostics.push(e.to_diagnostic());
|
||||
}
|
||||
// Iter 18c.2: linearity check runs only on modules that
|
||||
// typechecked clean. Running it on a body that already has a
|
||||
// type error would wade into a partly-defined IR (e.g.
|
||||
// unresolved Var lookups, mismatched ctor arities) and produce
|
||||
// noise. Cleanly-typechecked modules with at least one
|
||||
// all-explicit-mode fn are exactly the surface the check is
|
||||
// designed to inspect.
|
||||
if !had_typecheck_errors {
|
||||
diagnostics.extend(linearity::check_module(m));
|
||||
}
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user