test: red for env-construction drift-shape pin

This commit is contained in:
2026-05-10 02:03:09 +02:00
parent 08abfdf791
commit d7706dc3ed
@@ -0,0 +1,196 @@
//! Drift-shape pin for env construction (env-construction unify
//! milestone). Asserts that `build_check_env(&ws)` agrees with an
//! in-test reproduction of the pre-refactor inline seeding on every
//! workspace-flat field. Future-you adds a new workspace-flat env
//! field without updating `build_check_env` -> this test fails before
//! the bug ships.
//!
//! Fixture: a two-module workspace covering one `Def::Class`, one
//! `Def::Instance`, one user ADT, and one cross-module import (the
//! same shape that `test_mono_imports_*` use).
use ailang_check::{build_check_env, build_module_globals, build_module_types, Env};
use ailang_core::ast::Def;
use ailang_core::load_workspace;
use std::collections::BTreeMap;
use std::path::Path;
fn examples_dir() -> std::path::PathBuf {
let manifest = env!("CARGO_MANIFEST_DIR");
Path::new(manifest).parent().unwrap().parent().unwrap().join("examples")
}
/// In-test reproduction of the pre-refactor `check_in_workspace`
/// workspace-flat seeding (lib.rs as of commit a9c685d). Kept as a
/// frozen reference: any future divergence between this body and
/// `build_check_env` makes the assertion below fail.
fn build_env_inline_pre_refactor(ws: &ailang_core::workspace::Workspace) -> Env {
let mut env = Env::default();
ailang_check::builtins::install(&mut env);
// env.types + env.ctor_index — workspace-flat from every Def::Type.
for m in ws.modules.values() {
for d in &m.defs {
if let Def::Type(td) = d {
for c in &td.ctors {
env.ctor_index.insert(
c.name.clone(),
ailang_check::CtorRef { type_name: td.name.clone() },
);
}
env.types.insert(td.name.clone(), td.clone());
}
}
}
// env.module_globals — per-module .fns projection.
let mg = build_module_globals(ws).expect("build_module_globals");
env.module_globals = mg
.iter()
.map(|(k, v)| (k.clone(), v.fns.clone()))
.collect();
// env.module_types — workspace-wide.
env.module_types = build_module_types(ws);
// env.module_imports — per-module alias map.
for m in ws.modules.values() {
let mut import_map: BTreeMap<String, String> = BTreeMap::new();
for imp in &m.imports {
let key = imp.alias.clone().unwrap_or_else(|| imp.module.clone());
import_map.insert(key, imp.module.clone());
}
env.module_imports.insert(m.name.clone(), import_map);
}
// env.class_methods — workspace-merged from each module's
// ModuleGlobals.class_methods.
for g in mg.values() {
for (n, e) in &g.class_methods {
env.class_methods.insert(n.clone(), e.clone());
}
}
// env.class_superclasses — walk every module's Def::Class.
for m in ws.modules.values() {
for d in &m.defs {
if let Def::Class(cd) = d {
if let Some(sc) = &cd.superclass {
env.class_superclasses.insert(cd.name.clone(), sc.class.clone());
}
}
}
}
// env.workspace_registry — clone of ws.registry.
env.workspace_registry = ws.registry.clone();
env
}
#[test]
fn build_check_env_matches_inline_seeding() {
let entry = examples_dir().join("test_mono_imports_main.ail.json");
let ws = load_workspace(&entry).expect("load test_mono_imports_main");
let env_helper = build_check_env(&ws);
let env_inline = build_env_inline_pre_refactor(&ws);
// env.types — IndexMap<String, TypeDef>: compare key-set + name.
assert_eq!(
env_helper.types.keys().collect::<Vec<_>>(),
env_inline.types.keys().collect::<Vec<_>>(),
"env.types key-set differs"
);
for k in env_helper.types.keys() {
assert_eq!(
env_helper.types[k].name,
env_inline.types[k].name,
"env.types[{k}] type-def name differs"
);
}
// env.ctor_index — IndexMap<String, CtorRef>: compare key-set +
// type_name.
assert_eq!(
env_helper.ctor_index.keys().collect::<Vec<_>>(),
env_inline.ctor_index.keys().collect::<Vec<_>>(),
"env.ctor_index key-set differs"
);
for k in env_helper.ctor_index.keys() {
assert_eq!(
env_helper.ctor_index[k].type_name,
env_inline.ctor_index[k].type_name,
"env.ctor_index[{k}].type_name differs"
);
}
// env.module_globals — BTreeMap<String, IndexMap<String, Type>>.
assert_eq!(
env_helper.module_globals.keys().collect::<Vec<_>>(),
env_inline.module_globals.keys().collect::<Vec<_>>(),
"env.module_globals key-set differs"
);
for k in env_helper.module_globals.keys() {
assert_eq!(
env_helper.module_globals[k].keys().collect::<Vec<_>>(),
env_inline.module_globals[k].keys().collect::<Vec<_>>(),
"env.module_globals[{k}] inner key-set differs"
);
}
// env.module_types — same shape as module_globals.
assert_eq!(
env_helper.module_types.keys().collect::<Vec<_>>(),
env_inline.module_types.keys().collect::<Vec<_>>(),
"env.module_types key-set differs"
);
for k in env_helper.module_types.keys() {
assert_eq!(
env_helper.module_types[k].keys().collect::<Vec<_>>(),
env_inline.module_types[k].keys().collect::<Vec<_>>(),
"env.module_types[{k}] inner key-set differs"
);
}
// env.module_imports — BTreeMap<String, BTreeMap<String, String>>:
// direct equality.
assert_eq!(
env_helper.module_imports, env_inline.module_imports,
"env.module_imports differs"
);
// env.class_methods — BTreeMap<String, ClassMethodEntry>:
// compare key-set (entry shape has no PartialEq).
assert_eq!(
env_helper.class_methods.keys().collect::<Vec<_>>(),
env_inline.class_methods.keys().collect::<Vec<_>>(),
"env.class_methods key-set differs"
);
// env.class_superclasses — direct equality.
assert_eq!(
env_helper.class_superclasses, env_inline.class_superclasses,
"env.class_superclasses differs"
);
// env.effect_ops — IndexMap<String, EffectOpSig>: compare key-set.
assert_eq!(
env_helper.effect_ops.keys().collect::<Vec<_>>(),
env_inline.effect_ops.keys().collect::<Vec<_>>(),
"env.effect_ops key-set differs"
);
// env.workspace_registry — entries key-set must match.
assert_eq!(
env_helper.workspace_registry.entries.keys().collect::<Vec<_>>(),
env_inline.workspace_registry.entries.keys().collect::<Vec<_>>(),
"env.workspace_registry.entries key-set differs"
);
// Per-call overlay fields must NOT be seeded by the helper.
assert_eq!(env_helper.current_module, "", "current_module is overlay");
assert!(env_helper.globals.is_empty(), "globals is overlay");
assert!(env_helper.imports.is_empty(), "imports is overlay");
assert!(env_helper.rigid_vars.is_empty(), "rigid_vars is overlay");
}