refactor: collapse duplicated helpers and drop dead code across check/core/codegen
Pure simplification pass, no behaviour change — hashes, canonical forms, diagnostic codes, and emitted IR are byte-identical (hash-pin and IR-pin tests unchanged and green). ailang-check: - strip_forall and collect_pattern_binders were triplicated verbatim across linearity.rs / reuse_shape.rs / uniqueness.rs; hoisted each to a single pub(crate) fn in lib.rs. - mono.rs::pattern_binders was a fourth copy of collect_pattern_binders (iterative style, same result for every Pattern shape); deleted, now calls the shared fn. - maybe_instantiate and expect_eq were single-call wrappers; inlined at their sole call sites and removed. ailang-core: - collect_used_in_pattern was a verbatim duplicate of pattern_binds; deleted, call site repointed. pattern_binds made private (no external callers; the doc-comment's lift_letrecs claim was stale). - is_false serde helper replaced by std::ops::Not::not at the four skip_serializing_if sites (all plain-bool fields); helper removed. - test-only any_nested_ctor / any_let_rec folded into one generic any_term(t, &pred) walker. - removed dead test helpers tmp_dir / examples_dir, orphaned when their tests relocated to tests/workspace_pin.rs. ailang-codegen: - removed the write-only Emitter::types field, its populating loop, and the now-unused CtorInfo struct it fed. - adt_drop_symbol / adt_partial_drop_symbol differed only in a prefix literal; folded into one adt_symbol(prefix, ...). Symbol strings unchanged. Net -180 LOC.
This commit is contained in:
@@ -43,6 +43,30 @@ mod reuse_shape;
|
|||||||
mod suppress_filter;
|
mod suppress_filter;
|
||||||
pub mod uniqueness;
|
pub mod uniqueness;
|
||||||
|
|
||||||
|
/// Returns the inner type of a top-level `Forall<Fn ...>` (or `t`
|
||||||
|
/// unchanged if not a `Forall`). The uniqueness/linearity/reuse-shape
|
||||||
|
/// analyses only inspect `param_modes`, which sit on the inner
|
||||||
|
/// `Type::Fn`.
|
||||||
|
pub(crate) fn strip_forall(t: &Type) -> &Type {
|
||||||
|
match t {
|
||||||
|
Type::Forall { body, .. } => body,
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recursively collect every binder name introduced by a pattern.
|
||||||
|
/// Wildcards and literals contribute nothing; `Var` adds itself; `Ctor`
|
||||||
|
/// recurses into fields.
|
||||||
|
pub(crate) fn collect_pattern_binders(p: &Pattern) -> Vec<String> {
|
||||||
|
match p {
|
||||||
|
Pattern::Wild | Pattern::Lit { .. } => vec![],
|
||||||
|
Pattern::Var { name } => vec![name.clone()],
|
||||||
|
Pattern::Ctor { fields, .. } => {
|
||||||
|
fields.iter().flat_map(collect_pattern_binders).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Metavariable substitution. Maps fresh metavar ids (from `$m<id>` in
|
/// Metavariable substitution. Maps fresh metavar ids (from `$m<id>` in
|
||||||
/// `Type::Var.name`) to the type they have been unified against.
|
/// `Type::Var.name`) to the type they have been unified against.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
@@ -3818,7 +3842,15 @@ pub(crate) fn synth(
|
|||||||
});
|
});
|
||||||
return Ok(inst);
|
return Ok(inst);
|
||||||
}
|
}
|
||||||
Ok(maybe_instantiate(raw, counter))
|
// Inlined from the former `maybe_instantiate` wrapper: if
|
||||||
|
// `raw` is a `Forall`, instantiate it with fresh metavars;
|
||||||
|
// otherwise pass through unchanged.
|
||||||
|
if let Type::Forall { vars, constraints: _, body } = &raw {
|
||||||
|
let (_, inst) = instantiate(vars, body, counter);
|
||||||
|
Ok(inst)
|
||||||
|
} else {
|
||||||
|
Ok(raw)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Term::App { callee, args, .. } => {
|
Term::App { callee, args, .. } => {
|
||||||
let cty = synth(callee, env, locals, loop_stack, effects, in_def, subst, counter, residuals, free_fn_calls, warnings, let_binder_types)?;
|
let cty = synth(callee, env, locals, loop_stack, effects, in_def, subst, counter, residuals, free_fn_calls, warnings, let_binder_types)?;
|
||||||
@@ -4652,17 +4684,6 @@ pub(crate) fn synth(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// If `t` is a `Forall`, instantiate it with fresh metavars; otherwise
|
|
||||||
/// pass through unchanged. Used at every var-resolution site.
|
|
||||||
fn maybe_instantiate(t: Type, counter: &mut u32) -> Type {
|
|
||||||
if let Type::Forall { vars, constraints: _, body } = &t {
|
|
||||||
let (_, inst) = instantiate(vars, body, counter);
|
|
||||||
inst
|
|
||||||
} else {
|
|
||||||
t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// rewrites bare `Type::Con` references that resolve against
|
/// rewrites bare `Type::Con` references that resolve against
|
||||||
/// `local_types` into qualified `module.Type` form. Used when pulling a
|
/// `local_types` into qualified `module.Type` form. Used when pulling a
|
||||||
/// fn type across module boundaries: a `Maybe a` declared inside
|
/// fn type across module boundaries: a `Maybe a` declared inside
|
||||||
@@ -5084,7 +5105,14 @@ pub(crate) fn type_check_pattern(
|
|||||||
return Err(CheckError::FloatPatternNotAllowed);
|
return Err(CheckError::FloatPatternNotAllowed);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
expect_eq(expected, <)?;
|
// Inlined from the former `expect_eq` helper: the literal
|
||||||
|
// pattern's type must equal the expected scrutinee type.
|
||||||
|
if *expected != lt {
|
||||||
|
return Err(CheckError::TypeMismatch {
|
||||||
|
expected: ailang_core::pretty::type_to_string(expected),
|
||||||
|
got: ailang_core::pretty::type_to_string(<),
|
||||||
|
});
|
||||||
|
}
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
Pattern::Ctor { ctor, fields } => {
|
Pattern::Ctor { ctor, fields } => {
|
||||||
@@ -5206,17 +5234,6 @@ fn callee_name(t: &Term) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_eq(expected: &Type, got: &Type) -> Result<()> {
|
|
||||||
if expected == got {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(CheckError::TypeMismatch {
|
|
||||||
expected: ailang_core::pretty::type_to_string(expected),
|
|
||||||
got: ailang_core::pretty::type_to_string(got),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Typechecker environment for a single module-check pass.
|
/// Typechecker environment for a single module-check pass.
|
||||||
///
|
///
|
||||||
/// Cloned cheaply when we need a scoped extension (e.g. installing
|
/// Cloned cheaply when we need a scoped extension (e.g. installing
|
||||||
|
|||||||
@@ -143,6 +143,7 @@
|
|||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::uniqueness::{infer_module as infer_uniqueness, UniquenessTable};
|
use crate::uniqueness::{infer_module as infer_uniqueness, UniquenessTable};
|
||||||
use ailang_core::ast::{Arm, Ctor, Def, FnDef, Module, NewArg, ParamMode, Pattern, Term, Type};
|
use ailang_core::ast::{Arm, Ctor, Def, FnDef, Module, NewArg, ParamMode, Pattern, Term, Type};
|
||||||
|
use crate::{collect_pattern_binders, strip_forall};
|
||||||
use ailang_surface::{term_to_form_a, type_to_form_a};
|
use ailang_surface::{term_to_form_a, type_to_form_a};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@@ -339,16 +340,6 @@ pub(crate) fn check_module_with_visible(
|
|||||||
diags
|
diags
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the inner type of a top-level `Forall<Fn ...>` (or `t`
|
|
||||||
/// unchanged if not a `Forall`). The linearity check only inspects
|
|
||||||
/// `param_modes`, which sit on the inner `Type::Fn`.
|
|
||||||
fn strip_forall(t: &Type) -> &Type {
|
|
||||||
match t {
|
|
||||||
Type::Forall { body, .. } => body,
|
|
||||||
other => other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pull a function-typed value's declared parameter modes out of its
|
/// Pull a function-typed value's declared parameter modes out of its
|
||||||
/// type, unwrapping a leading `forall`. Returns `None` for any
|
/// type, unwrapping a leading `forall`. Returns `None` for any
|
||||||
/// non-function type and for a fn-type with no explicit modes (legacy
|
/// non-function type and for a fn-type with no explicit modes (legacy
|
||||||
@@ -965,20 +956,6 @@ impl<'a> Checker<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively collect every binder name introduced by a pattern.
|
|
||||||
/// Wildcards and literals contribute nothing; `Var` adds itself; `Ctor`
|
|
||||||
/// recurses into fields.
|
|
||||||
fn collect_pattern_binders(p: &Pattern) -> Vec<String> {
|
|
||||||
match p {
|
|
||||||
Pattern::Wild | Pattern::Lit { .. } => vec![],
|
|
||||||
Pattern::Var { name } => vec![name.clone()],
|
|
||||||
Pattern::Ctor { fields, .. } => fields
|
|
||||||
.iter()
|
|
||||||
.flat_map(collect_pattern_binders)
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collect the names of pattern binders that have an unboxed value
|
/// Collect the names of pattern binders that have an unboxed value
|
||||||
/// type, by walking the pattern alongside the ctor field types.
|
/// type, by walking the pattern alongside the ctor field types.
|
||||||
/// Mirrors `pattern_has_consumed_heap_binder_at`'s descent: a
|
/// Mirrors `pattern_has_consumed_heap_binder_at`'s descent: a
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
//! `module_hash` is unchanged end-to-end for any workspace with no
|
//! `module_hash` is unchanged end-to-end for any workspace with no
|
||||||
//! monomorphisation targets.
|
//! monomorphisation targets.
|
||||||
|
|
||||||
use ailang_core::ast::{Arm, ClassDef, ConstDef, Def, FnDef as AstFnDef, InstanceDef, NewArg, Pattern, Term, Type};
|
use ailang_core::ast::{Arm, ClassDef, ConstDef, Def, FnDef as AstFnDef, InstanceDef, NewArg, Term, Type};
|
||||||
use ailang_core::workspace::Workspace;
|
use ailang_core::workspace::Workspace;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
@@ -1415,7 +1415,7 @@ fn rewrite_mono_calls(
|
|||||||
Term::Match { scrutinee, arms } => {
|
Term::Match { scrutinee, arms } => {
|
||||||
rewrite_mono_calls(scrutinee, method_to_candidate_classes, poly_free_fns, poly_free_fn_ccounts, caller_module, ordered_targets, cursor, locals);
|
rewrite_mono_calls(scrutinee, method_to_candidate_classes, poly_free_fns, poly_free_fn_ccounts, caller_module, ordered_targets, cursor, locals);
|
||||||
for Arm { pat, body } in arms {
|
for Arm { pat, body } in arms {
|
||||||
let binders = pattern_binders(pat);
|
let binders = crate::collect_pattern_binders(pat);
|
||||||
let mut inserted: Vec<String> = Vec::new();
|
let mut inserted: Vec<String> = Vec::new();
|
||||||
for b in &binders {
|
for b in &binders {
|
||||||
if locals.insert(b.clone()) {
|
if locals.insert(b.clone()) {
|
||||||
@@ -1531,29 +1531,6 @@ fn rewrite_mono_calls(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// collect the variable binders introduced by a
|
|
||||||
/// match pattern. Used by the walker's `Term::Match` arm to extend
|
|
||||||
/// `locals` for each arm body. Mirrors `Pattern`'s shape:
|
|
||||||
/// - [`Pattern::Wild`] / [`Pattern::Lit`] bind nothing,
|
|
||||||
/// - [`Pattern::Var`] binds its `name`,
|
|
||||||
/// - [`Pattern::Ctor`] recurses through its `fields`.
|
|
||||||
fn pattern_binders(pat: &Pattern) -> Vec<String> {
|
|
||||||
let mut out: Vec<String> = Vec::new();
|
|
||||||
fn rec(pat: &Pattern, out: &mut Vec<String>) {
|
|
||||||
match pat {
|
|
||||||
Pattern::Wild | Pattern::Lit { .. } => {}
|
|
||||||
Pattern::Var { name } => out.push(name.clone()),
|
|
||||||
Pattern::Ctor { fields, .. } => {
|
|
||||||
for f in fields {
|
|
||||||
rec(f, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rec(pat, &mut out);
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
/// resolve a residual's class for mono target construction.
|
/// resolve a residual's class for mono target construction.
|
||||||
/// Returns `Some(class)` for a discharge-ready residual (single-class
|
/// Returns `Some(class)` for a discharge-ready residual (single-class
|
||||||
/// or refined-multi-candidate); `None` if the multi-candidate residual
|
/// or refined-multi-candidate); `None` if the multi-candidate residual
|
||||||
@@ -1862,7 +1839,7 @@ fn interleave_slots(
|
|||||||
Term::Match { scrutinee, arms } => {
|
Term::Match { scrutinee, arms } => {
|
||||||
interleave_slots(scrutinee, method_to_candidate_classes, poly_free_fns, poly_free_fn_ccounts, class_slots, free_fn_slots, class_cur, free_cur, locals, out);
|
interleave_slots(scrutinee, method_to_candidate_classes, poly_free_fns, poly_free_fn_ccounts, class_slots, free_fn_slots, class_cur, free_cur, locals, out);
|
||||||
for Arm { pat, body } in arms {
|
for Arm { pat, body } in arms {
|
||||||
let binders = pattern_binders(pat);
|
let binders = crate::collect_pattern_binders(pat);
|
||||||
let mut inserted: Vec<String> = Vec::new();
|
let mut inserted: Vec<String> = Vec::new();
|
||||||
for b in &binders {
|
for b in &binders {
|
||||||
if locals.insert(b.clone()) {
|
if locals.insert(b.clone()) {
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
|
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
use ailang_core::ast::{Arm, Def, FnDef, Module, NewArg, ParamMode, Pattern, Term, Type, TypeDef};
|
use ailang_core::ast::{Arm, Def, FnDef, Module, NewArg, ParamMode, Pattern, Term, Type, TypeDef};
|
||||||
|
use crate::{collect_pattern_binders, strip_forall};
|
||||||
use ailang_surface::term_to_form_a;
|
use ailang_surface::term_to_form_a;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@@ -114,15 +115,6 @@ fn check_fn(f: &FnDef, types: &HashMap<String, TypeDef>, diags: &mut Vec<Diagnos
|
|||||||
checker.walk(&f.body);
|
checker.walk(&f.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the inner type of a top-level `Forall<Fn ...>` (or `t`
|
|
||||||
/// unchanged if not a `Forall`).
|
|
||||||
fn strip_forall(t: &Type) -> &Type {
|
|
||||||
match t {
|
|
||||||
Type::Forall { body, .. } => body,
|
|
||||||
other => other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// One entry in the path-ctor stack: the bare ctor name `<var>` carries
|
/// One entry in the path-ctor stack: the bare ctor name `<var>` carries
|
||||||
/// on the current control-flow path.
|
/// on the current control-flow path.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -487,17 +479,6 @@ fn llvm_kind(t: &Type) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_pattern_binders(p: &Pattern) -> Vec<String> {
|
|
||||||
match p {
|
|
||||||
Pattern::Wild | Pattern::Lit { .. } => vec![],
|
|
||||||
Pattern::Var { name } => vec![name.clone()],
|
|
||||||
Pattern::Ctor { fields, .. } => fields
|
|
||||||
.iter()
|
|
||||||
.flat_map(collect_pattern_binders)
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build a `reuse-as-shape-mismatch` diagnostic. The suggested
|
/// Build a `reuse-as-shape-mismatch` diagnostic. The suggested
|
||||||
/// rewrite drops the wrapper and keeps the body alone — the LLM can
|
/// rewrite drops the wrapper and keeps the body alone — the LLM can
|
||||||
/// then either accept the normal allocator path or reshape the body.
|
/// then either accept the normal allocator path or reshape the body.
|
||||||
|
|||||||
@@ -58,7 +58,8 @@
|
|||||||
//! dec (uniform `drop_<m>_<T>` call vs inlined partial drop) is a
|
//! dec (uniform `drop_<m>_<T>` call vs inlined partial drop) is a
|
||||||
//! codegen-side decision driven by `moved_slots`.
|
//! codegen-side decision driven by `moved_slots`.
|
||||||
|
|
||||||
use ailang_core::ast::{Arm, Def, FnDef, Module, NewArg, ParamMode, Pattern, Term, Type};
|
use ailang_core::ast::{Arm, Def, FnDef, Module, NewArg, ParamMode, Term, Type};
|
||||||
|
use crate::{collect_pattern_binders, strip_forall};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// Per-binder classification produced by the inference. See module
|
/// Per-binder classification produced by the inference. See module
|
||||||
@@ -175,13 +176,6 @@ pub fn infer_module_with_cross(
|
|||||||
table
|
table
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strip_forall(t: &Type) -> &Type {
|
|
||||||
match t {
|
|
||||||
Type::Forall { body, .. } => body,
|
|
||||||
other => other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Per-fn driver: install the parameters as binders, walk the body,
|
/// Per-fn driver: install the parameters as binders, walk the body,
|
||||||
/// snapshot the per-binder consume counts into the side table.
|
/// snapshot the per-binder consume counts into the side table.
|
||||||
fn infer_fn(
|
fn infer_fn(
|
||||||
@@ -490,18 +484,10 @@ fn merge_states(into: &mut BTreeMap<String, BinderState>, other: &BTreeMap<Strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_pattern_binders(p: &Pattern) -> Vec<String> {
|
|
||||||
match p {
|
|
||||||
Pattern::Wild | Pattern::Lit { .. } => vec![],
|
|
||||||
Pattern::Var { name } => vec![name.clone()],
|
|
||||||
Pattern::Ctor { fields, .. } => fields.iter().flat_map(collect_pattern_binders).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ailang_core::ast::{FnDef, Literal};
|
use ailang_core::ast::{FnDef, Literal, Pattern};
|
||||||
|
|
||||||
fn fn_simple(name: &str, params: Vec<&str>, body: Term) -> Def {
|
fn fn_simple(name: &str, params: Vec<&str>, body: Term) -> Def {
|
||||||
Def::Fn(FnDef {
|
Def::Fn(FnDef {
|
||||||
|
|||||||
@@ -501,16 +501,7 @@ impl<'a> Emitter<'a> {
|
|||||||
/// with the emission loop, so a call symbol matches its definition
|
/// with the emission loop, so a call symbol matches its definition
|
||||||
/// byte-for-byte (a mismatch is an IR link error).
|
/// byte-for-byte (a mismatch is an IR link error).
|
||||||
pub(crate) fn adt_drop_symbol(&self, name: &str, args: &[Type]) -> String {
|
pub(crate) fn adt_drop_symbol(&self, name: &str, args: &[Type]) -> String {
|
||||||
let key = super::dropmono::resolve_adt_key(
|
self.adt_symbol("drop_", name, args)
|
||||||
name,
|
|
||||||
self.module_name,
|
|
||||||
&self.import_map,
|
|
||||||
);
|
|
||||||
let base = format!("drop_{owner}_{bare}", owner = key.0, bare = key.1);
|
|
||||||
match self.drop_monos.suffix_for(&key, args) {
|
|
||||||
Some(suffix) => format!("{base}{suffix}"),
|
|
||||||
None => base,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// resolve the `partial_drop_<owner>_<T>` symbol for an ADT
|
/// resolve the `partial_drop_<owner>_<T>` symbol for an ADT
|
||||||
@@ -518,13 +509,23 @@ impl<'a> Emitter<'a> {
|
|||||||
/// to [`Self::adt_drop_symbol`]. The caller has already excluded
|
/// to [`Self::adt_drop_symbol`]. The caller has already excluded
|
||||||
/// `Str` / non-`Con` shapes.
|
/// `Str` / non-`Con` shapes.
|
||||||
pub(crate) fn adt_partial_drop_symbol(&self, name: &str, args: &[Type]) -> String {
|
pub(crate) fn adt_partial_drop_symbol(&self, name: &str, args: &[Type]) -> String {
|
||||||
|
self.adt_symbol("partial_drop_", name, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// shared core of [`Self::adt_drop_symbol`] and
|
||||||
|
/// [`Self::adt_partial_drop_symbol`]: build the `<prefix><owner>_<bare>`
|
||||||
|
/// base from the resolved ADT key and append the leg-C per-monomorph
|
||||||
|
/// suffix when one applies. The two public symbols differ only in the
|
||||||
|
/// `prefix` literal (`drop_` vs `partial_drop_`); the emitted string is
|
||||||
|
/// byte-identical to the pre-dedup form, which matters because these are
|
||||||
|
/// IR symbol names (a mismatch is a link error).
|
||||||
|
fn adt_symbol(&self, prefix: &str, name: &str, args: &[Type]) -> String {
|
||||||
let key = super::dropmono::resolve_adt_key(
|
let key = super::dropmono::resolve_adt_key(
|
||||||
name,
|
name,
|
||||||
self.module_name,
|
self.module_name,
|
||||||
&self.import_map,
|
&self.import_map,
|
||||||
);
|
);
|
||||||
let base =
|
let base = format!("{prefix}{owner}_{bare}", owner = key.0, bare = key.1);
|
||||||
format!("partial_drop_{owner}_{bare}", owner = key.0, bare = key.1);
|
|
||||||
match self.drop_monos.suffix_for(&key, args) {
|
match self.drop_monos.suffix_for(&key, args) {
|
||||||
Some(suffix) => format!("{base}{suffix}"),
|
Some(suffix) => format!("{base}{suffix}"),
|
||||||
None => base,
|
None => base,
|
||||||
|
|||||||
@@ -787,12 +787,6 @@ struct Emitter<'a> {
|
|||||||
/// `drop_`/`partial_drop_` symbol. Shared by reference with every
|
/// `drop_`/`partial_drop_` symbol. Shared by reference with every
|
||||||
/// `Emitter`.
|
/// `Emitter`.
|
||||||
drop_monos: &'a dropmono::DropAdtMeta,
|
drop_monos: &'a dropmono::DropAdtMeta,
|
||||||
/// ADT table: type_name -> list of ctors in definition order.
|
|
||||||
/// Tag of a ctor = index in this list.
|
|
||||||
/// Kept around for future tools (pretty-printer for ADT values,
|
|
||||||
/// decision-tree optimization).
|
|
||||||
#[allow(dead_code)]
|
|
||||||
types: BTreeMap<String, Vec<CtorInfo>>,
|
|
||||||
/// cross-module ctor index, keyed by module name. Used by
|
/// cross-module ctor index, keyed by module name. Used by
|
||||||
/// `lookup_ctor_by_type` (for `Term::Ctor.type_name`) and
|
/// `lookup_ctor_by_type` (for `Term::Ctor.type_name`) and
|
||||||
/// `lookup_ctor_in_pattern` (for `Pattern::Ctor.ctor`). Built once
|
/// `lookup_ctor_in_pattern` (for `Pattern::Ctor.ctor`). Built once
|
||||||
@@ -938,13 +932,6 @@ struct Emitter<'a> {
|
|||||||
entry_block_end_marker: Option<usize>,
|
entry_block_end_marker: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
struct CtorInfo {
|
|
||||||
name: String,
|
|
||||||
fields: Vec<String>, // llvm types
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct CtorRef {
|
struct CtorRef {
|
||||||
type_name: String,
|
type_name: String,
|
||||||
@@ -993,34 +980,6 @@ impl<'a> Emitter<'a> {
|
|||||||
drop_monos: &'a dropmono::DropAdtMeta,
|
drop_monos: &'a dropmono::DropAdtMeta,
|
||||||
alloc: AllocStrategy,
|
alloc: AllocStrategy,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut types: BTreeMap<String, Vec<CtorInfo>> = BTreeMap::new();
|
|
||||||
for def in &module.defs {
|
|
||||||
if let Def::Type(td) = def {
|
|
||||||
let mut infos = Vec::new();
|
|
||||||
for c in td.ctors.iter() {
|
|
||||||
// precomputed LLVM field types are only
|
|
||||||
// meaningful for monomorphic ADTs. For parameterised
|
|
||||||
// ADTs the field types reference free `Type::Var`s
|
|
||||||
// and must be derived per use site after
|
|
||||||
// substituting; we still populate the slot with
|
|
||||||
// `i64`/`ptr` placeholders so the index shape stays
|
|
||||||
// uniform, but neither `lower_ctor` nor
|
|
||||||
// `lower_match` reads from it when `type_vars` is
|
|
||||||
// non-empty.
|
|
||||||
let fields: Vec<String> = c
|
|
||||||
.fields
|
|
||||||
.iter()
|
|
||||||
.map(|t| llvm_type(t).unwrap_or_else(|_| "ptr".into()))
|
|
||||||
.collect();
|
|
||||||
infos.push(CtorInfo {
|
|
||||||
name: c.name.clone(),
|
|
||||||
fields,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
types.insert(td.name.clone(), infos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mir.3a: read the per-binder consume_count off MIR (filled by
|
// mir.3a: read the per-binder consume_count off MIR (filled by
|
||||||
// lower_to_mir from the single uniqueness pass) instead of
|
// lower_to_mir from the single uniqueness pass) instead of
|
||||||
// re-running the uniqueness pass here.
|
// re-running the uniqueness pass here.
|
||||||
@@ -1049,7 +1008,6 @@ impl<'a> Emitter<'a> {
|
|||||||
module_user_fns,
|
module_user_fns,
|
||||||
import_map,
|
import_map,
|
||||||
drop_monos,
|
drop_monos,
|
||||||
types,
|
|
||||||
module_ctor_index,
|
module_ctor_index,
|
||||||
module_consts,
|
module_consts,
|
||||||
current_block: String::new(),
|
current_block: String::new(),
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub struct Module {
|
|||||||
/// from canonical JSON when `false`, so every pre-existing
|
/// from canonical JSON when `false`, so every pre-existing
|
||||||
/// fixture's hash is bit-stable. See prep.3 of the
|
/// fixture's hash is bit-stable. See prep.3 of the
|
||||||
/// kernel-extension-mechanics milestone.
|
/// kernel-extension-mechanics milestone.
|
||||||
#[serde(default, skip_serializing_if = "is_false")]
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||||
pub kernel: bool,
|
pub kernel: bool,
|
||||||
/// Imports of other modules. Resolved by the workspace loader as
|
/// Imports of other modules. Resolved by the workspace loader as
|
||||||
/// `<root_dir>/<module>.ail.json`.
|
/// `<root_dir>/<module>.ail.json`.
|
||||||
@@ -174,7 +174,7 @@ pub struct TypeDef {
|
|||||||
#[serde(
|
#[serde(
|
||||||
default,
|
default,
|
||||||
rename = "drop-iterative",
|
rename = "drop-iterative",
|
||||||
skip_serializing_if = "is_false"
|
skip_serializing_if = "std::ops::Not::not"
|
||||||
)]
|
)]
|
||||||
pub drop_iterative: bool,
|
pub drop_iterative: bool,
|
||||||
/// Closed-set restriction on type-parameter instantiation. Maps
|
/// Closed-set restriction on type-parameter instantiation. Maps
|
||||||
@@ -452,7 +452,7 @@ pub enum Term {
|
|||||||
callee: Box<Term>,
|
callee: Box<Term>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
args: Vec<Term>,
|
args: Vec<Term>,
|
||||||
#[serde(default, skip_serializing_if = "is_false")]
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||||
tail: bool,
|
tail: bool,
|
||||||
},
|
},
|
||||||
/// Let-binding: `value` is evaluated and bound to `name` in `body`.
|
/// Let-binding: `value` is evaluated and bound to `name` in `body`.
|
||||||
@@ -494,7 +494,7 @@ pub enum Term {
|
|||||||
Do {
|
Do {
|
||||||
op: String,
|
op: String,
|
||||||
args: Vec<Term>,
|
args: Vec<Term>,
|
||||||
#[serde(default, skip_serializing_if = "is_false")]
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||||
tail: bool,
|
tail: bool,
|
||||||
},
|
},
|
||||||
/// Constructor application. `type_name` binds the ADT, `ctor` the
|
/// Constructor application. `type_name` binds the ADT, `ctor` the
|
||||||
@@ -903,17 +903,6 @@ impl PartialEq for Type {
|
|||||||
}
|
}
|
||||||
impl Eq for Type {}
|
impl Eq for Type {}
|
||||||
|
|
||||||
/// Serde helper for `#[serde(skip_serializing_if = "is_false")]`.
|
|
||||||
///
|
|
||||||
/// Used by [`Term::App::tail`] and [`Term::Do::tail`] so the `tail`
|
|
||||||
/// flag is omitted from the canonical JSON whenever it is false,
|
|
||||||
/// preserving bit-identical hashes for every fixture that does not
|
|
||||||
/// carry the flag.
|
|
||||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
|
||||||
fn is_false(b: &bool) -> bool {
|
|
||||||
!*b
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ fn collect_used_in_term(t: &Term, used: &mut BTreeSet<String>) {
|
|||||||
Term::Match { scrutinee, arms } => {
|
Term::Match { scrutinee, arms } => {
|
||||||
collect_used_in_term(scrutinee, used);
|
collect_used_in_term(scrutinee, used);
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
collect_used_in_pattern(&arm.pat, used);
|
pattern_binds(&arm.pat, used);
|
||||||
collect_used_in_term(&arm.body, used);
|
collect_used_in_term(&arm.body, used);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -439,22 +439,6 @@ fn collect_used_in_term(t: &Term, used: &mut BTreeSet<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Walks a pattern and inserts every [`Pattern::Var.name`] into `used`.
|
|
||||||
fn collect_used_in_pattern(p: &Pattern, used: &mut BTreeSet<String>) {
|
|
||||||
match p {
|
|
||||||
Pattern::Wild => {}
|
|
||||||
Pattern::Var { name } => {
|
|
||||||
used.insert(name.clone());
|
|
||||||
}
|
|
||||||
Pattern::Lit { .. } => {}
|
|
||||||
Pattern::Ctor { fields, .. } => {
|
|
||||||
for sub in fields {
|
|
||||||
collect_used_in_pattern(sub, used);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// State carried across a module-wide desugar pass.
|
/// State carried across a module-wide desugar pass.
|
||||||
///
|
///
|
||||||
/// `counter` is incremented for every fresh-name request; `used`
|
/// `counter` is incremented for every fresh-name request; `used`
|
||||||
@@ -1540,11 +1524,7 @@ fn rename_pattern_binders(pat: &Pattern, renamed: &BTreeMap<String, String>) ->
|
|||||||
/// collect every name a pattern binds into `out`. Mirrors
|
/// collect every name a pattern binds into `out`. Mirrors
|
||||||
/// `Pattern::pattern_bound_names` in `ailang-codegen`; duplicated here
|
/// `Pattern::pattern_bound_names` in `ailang-codegen`; duplicated here
|
||||||
/// because `ailang-core` cannot depend on the codegen crate.
|
/// because `ailang-core` cannot depend on the codegen crate.
|
||||||
///
|
fn pattern_binds(p: &Pattern, out: &mut BTreeSet<String>) {
|
||||||
/// made `pub` so callers (e.g. `ailang-check::lift_letrecs`)
|
|
||||||
/// can reproduce the same shadowing semantics during their own
|
|
||||||
/// scope-aware walks.
|
|
||||||
pub fn pattern_binds(p: &Pattern, out: &mut BTreeSet<String>) {
|
|
||||||
match p {
|
match p {
|
||||||
Pattern::Wild | Pattern::Lit { .. } => {}
|
Pattern::Wild | Pattern::Lit { .. } => {}
|
||||||
Pattern::Var { name } => {
|
Pattern::Var { name } => {
|
||||||
@@ -1949,85 +1929,58 @@ pub fn find_non_callee_use(t: &Term, name: &str) -> Option<Term> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
/// Generic test-only tree walk: returns true if `pred` holds for
|
||||||
|
/// `t` or any of its subterms. The structural recursion visits the
|
||||||
|
/// same subterm set every node-specific walker needs (callee/args,
|
||||||
|
/// let value+body, if branches, match scrutinee + arm bodies, etc.).
|
||||||
|
fn any_term(t: &Term, pred: &impl Fn(&Term) -> bool) -> bool {
|
||||||
|
if pred(t) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
match t {
|
||||||
|
Term::Lit { .. } | Term::Var { .. } | Term::Intrinsic => false,
|
||||||
|
Term::App { callee, args, .. } => {
|
||||||
|
any_term(callee, pred) || args.iter().any(|a| any_term(a, pred))
|
||||||
|
}
|
||||||
|
Term::Let { value, body, .. } => any_term(value, pred) || any_term(body, pred),
|
||||||
|
Term::If { cond, then, else_ } => {
|
||||||
|
any_term(cond, pred) || any_term(then, pred) || any_term(else_, pred)
|
||||||
|
}
|
||||||
|
Term::Do { args, .. } => args.iter().any(|a| any_term(a, pred)),
|
||||||
|
Term::Ctor { args, .. } => args.iter().any(|a| any_term(a, pred)),
|
||||||
|
Term::Match { scrutinee, arms } => {
|
||||||
|
any_term(scrutinee, pred) || arms.iter().any(|a| any_term(&a.body, pred))
|
||||||
|
}
|
||||||
|
Term::Lam { body, .. } => any_term(body, pred),
|
||||||
|
Term::Seq { lhs, rhs } => any_term(lhs, pred) || any_term(rhs, pred),
|
||||||
|
Term::LetRec { body, in_term, .. } => {
|
||||||
|
any_term(body, pred) || any_term(in_term, pred)
|
||||||
|
}
|
||||||
|
Term::Clone { value } => any_term(value, pred),
|
||||||
|
Term::ReuseAs { source, body } => any_term(source, pred) || any_term(body, pred),
|
||||||
|
Term::Loop { binders, body } => {
|
||||||
|
binders.iter().any(|b| any_term(&b.init, pred)) || any_term(body, pred)
|
||||||
|
}
|
||||||
|
Term::Recur { args } => args.iter().any(|a| any_term(a, pred)),
|
||||||
|
Term::New { args, .. } => args.iter().any(|arg| match arg {
|
||||||
|
NewArg::Value(v) => any_term(v, pred),
|
||||||
|
NewArg::Type(_) => false,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper: walk a term, return true iff any [`Pattern::Ctor`] in
|
/// Helper: walk a term, return true iff any [`Pattern::Ctor`] in
|
||||||
/// any reachable [`Term::Match`] has a non-flat field.
|
/// any reachable [`Term::Match`] has a non-flat field.
|
||||||
fn any_nested_ctor(t: &Term) -> bool {
|
fn any_nested_ctor(t: &Term) -> bool {
|
||||||
match t {
|
any_term(t, &|x| {
|
||||||
Term::Lit { .. } | Term::Var { .. } => false,
|
matches!(x, Term::Match { arms, .. } if arms.iter().any(|a| !is_flat(&a.pat)))
|
||||||
Term::App { callee, args, .. } => {
|
})
|
||||||
any_nested_ctor(callee) || args.iter().any(any_nested_ctor)
|
|
||||||
}
|
|
||||||
Term::Let { value, body, .. } => any_nested_ctor(value) || any_nested_ctor(body),
|
|
||||||
Term::If { cond, then, else_ } => {
|
|
||||||
any_nested_ctor(cond) || any_nested_ctor(then) || any_nested_ctor(else_)
|
|
||||||
}
|
|
||||||
Term::Do { args, .. } => args.iter().any(any_nested_ctor),
|
|
||||||
Term::Ctor { args, .. } => args.iter().any(any_nested_ctor),
|
|
||||||
Term::Match { scrutinee, arms } => {
|
|
||||||
if any_nested_ctor(scrutinee) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for a in arms {
|
|
||||||
if !is_flat(&a.pat) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if any_nested_ctor(&a.body) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
Term::Lam { body, .. } => any_nested_ctor(body),
|
|
||||||
Term::Seq { lhs, rhs } => any_nested_ctor(lhs) || any_nested_ctor(rhs),
|
|
||||||
Term::LetRec { body, in_term, .. } => {
|
|
||||||
any_nested_ctor(body) || any_nested_ctor(in_term)
|
|
||||||
}
|
|
||||||
Term::Clone { value } => any_nested_ctor(value),
|
|
||||||
Term::ReuseAs { source, body } => any_nested_ctor(source) || any_nested_ctor(body),
|
|
||||||
Term::Loop { binders, body } => {
|
|
||||||
binders.iter().any(|b| any_nested_ctor(&b.init)) || any_nested_ctor(body)
|
|
||||||
}
|
|
||||||
Term::Recur { args } => args.iter().any(any_nested_ctor),
|
|
||||||
Term::New { args, .. } => args.iter().any(|arg| match arg {
|
|
||||||
NewArg::Value(v) => any_nested_ctor(v),
|
|
||||||
NewArg::Type(_) => false,
|
|
||||||
}),
|
|
||||||
Term::Intrinsic => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper: walk a term, return true iff any [`Term::LetRec`] is
|
/// Helper: walk a term, return true iff any [`Term::LetRec`] is
|
||||||
/// reachable. After 16b.1 desugaring this should be `false`.
|
/// reachable. After 16b.1 desugaring this should be `false`.
|
||||||
fn any_let_rec(t: &Term) -> bool {
|
fn any_let_rec(t: &Term) -> bool {
|
||||||
match t {
|
any_term(t, &|x| matches!(x, Term::LetRec { .. }))
|
||||||
Term::Lit { .. } | Term::Var { .. } => false,
|
|
||||||
Term::App { callee, args, .. } => {
|
|
||||||
any_let_rec(callee) || args.iter().any(any_let_rec)
|
|
||||||
}
|
|
||||||
Term::Let { value, body, .. } => any_let_rec(value) || any_let_rec(body),
|
|
||||||
Term::If { cond, then, else_ } => {
|
|
||||||
any_let_rec(cond) || any_let_rec(then) || any_let_rec(else_)
|
|
||||||
}
|
|
||||||
Term::Do { args, .. } => args.iter().any(any_let_rec),
|
|
||||||
Term::Ctor { args, .. } => args.iter().any(any_let_rec),
|
|
||||||
Term::Match { scrutinee, arms } => {
|
|
||||||
any_let_rec(scrutinee) || arms.iter().any(|a| any_let_rec(&a.body))
|
|
||||||
}
|
|
||||||
Term::Lam { body, .. } => any_let_rec(body),
|
|
||||||
Term::Seq { lhs, rhs } => any_let_rec(lhs) || any_let_rec(rhs),
|
|
||||||
Term::LetRec { .. } => true,
|
|
||||||
Term::Clone { value } => any_let_rec(value),
|
|
||||||
Term::ReuseAs { source, body } => any_let_rec(source) || any_let_rec(body),
|
|
||||||
Term::Loop { binders, body } => {
|
|
||||||
binders.iter().any(|b| any_let_rec(&b.init)) || any_let_rec(body)
|
|
||||||
}
|
|
||||||
Term::Recur { args } => args.iter().any(any_let_rec),
|
|
||||||
Term::New { args, .. } => args.iter().any(|arg| match arg {
|
|
||||||
NewArg::Value(v) => any_let_rec(v),
|
|
||||||
NewArg::Type(_) => false,
|
|
||||||
}),
|
|
||||||
Term::Intrinsic => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// a `match` containing `(Cons a (Cons b _))` desugars to
|
/// a `match` containing `(Cons a (Cons b _))` desugars to
|
||||||
|
|||||||
@@ -1659,16 +1659,6 @@ mod tests {
|
|||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tmp_dir(tag: &str) -> PathBuf {
|
|
||||||
let d = std::env::temp_dir().join(format!(
|
|
||||||
"ailang_workspace_test_{tag}_{}",
|
|
||||||
std::process::id()
|
|
||||||
));
|
|
||||||
let _ = fs::remove_dir_all(&d);
|
|
||||||
fs::create_dir_all(&d).unwrap();
|
|
||||||
d
|
|
||||||
}
|
|
||||||
|
|
||||||
// `loads_example_workspace_happy_path` + `loads_workspace_auto_injects_prelude`
|
// `loads_example_workspace_happy_path` + `loads_workspace_auto_injects_prelude`
|
||||||
// relocated to `crates/ailang-core/tests/workspace_pin.rs` in iter
|
// relocated to `crates/ailang-core/tests/workspace_pin.rs` in iter
|
||||||
// form-a.1 Task 5 (use `ailang_surface::load_workspace` on `.ail`
|
// form-a.1 Task 5 (use `ailang_surface::load_workspace` on `.ail`
|
||||||
@@ -1692,16 +1682,6 @@ mod tests {
|
|||||||
// `iter22b1_workspace_with_no_classes_has_empty_registry` relocated
|
// `iter22b1_workspace_with_no_classes_has_empty_registry` relocated
|
||||||
// to `crates/ailang-core/tests/workspace_pin.rs` in iter form-a.1 Task 5.
|
// to `crates/ailang-core/tests/workspace_pin.rs` in iter form-a.1 Task 5.
|
||||||
|
|
||||||
fn examples_dir() -> PathBuf {
|
|
||||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
|
||||||
Path::new(manifest_dir)
|
|
||||||
.parent()
|
|
||||||
.unwrap()
|
|
||||||
.parent()
|
|
||||||
.unwrap()
|
|
||||||
.join("examples")
|
|
||||||
}
|
|
||||||
|
|
||||||
// a coherent instance (in the class's module)
|
// a coherent instance (in the class's module)
|
||||||
// loads cleanly and produces one registry entry from the
|
// loads cleanly and produces one registry entry from the
|
||||||
// fixture itself. With the auto-loaded prelude (iter 23.2)
|
// fixture itself. With the auto-loaded prelude (iter 23.2)
|
||||||
|
|||||||
Reference in New Issue
Block a user