iter 22b.3.3: fix — revert dead pub(crate), reuse build_module_types, drop defensive unwrap

Quality-review follow-up to 89311ee:

- Revert `substitute_rigids` to private. Task 3 does not call it; the
  visibility bump was forward-looking for Task 4 (synthesise_mono_fn).
  Task 4 will re-bump it in the same diff that uses it.
- Promote `build_module_types` to pub(crate); drop the inline
  reimplementation in `mono::build_workspace_env`. Removes drift risk
  between the two paths.
- Replace defensive `unwrap_or_default()` on `build_module_globals`
  with `expect(...)` naming the pre-condition. Silent default violated
  CLAUDE.md "no defensive validation in internal code paths".
- Add a one-line drift-risk reminder at the top of `build_workspace_env`
  flagging that it and `check_in_workspace` both populate `Env`.

Build clean; all 5 typeclass_22b3 tests green; no workspace regressions.
This commit is contained in:
2026-05-09 20:36:52 +02:00
parent 89311ee20b
commit 9f86c072dc
2 changed files with 8 additions and 21 deletions
+2 -2
View File
@@ -123,7 +123,7 @@ fn instantiate(forall_vars: &[String], body: &Type, counter: &mut u32) -> (Vec<T
/// Substitutes named rigid vars throughout a type (used by
/// `instantiate`). Unlike `Subst::apply`, this targets vars by name —
/// it has no notion of metavar ids.
pub(crate) fn substitute_rigids(t: &Type, mapping: &BTreeMap<String, Type>) -> Type {
fn substitute_rigids(t: &Type, mapping: &BTreeMap<String, Type>) -> Type {
match t {
Type::Var { name } => mapping.get(name).cloned().unwrap_or_else(|| t.clone()),
Type::Con { name, args } => Type::Con {
@@ -874,7 +874,7 @@ pub fn check_and_lift(m: &Module) -> Result<(CheckedModule, Module)> {
/// cross-module `Pattern::Ctor` fallback. Duplicate type / ctor errors
/// inside a single module surface during the per-module body-check
/// phase, not here.
fn build_module_types(
pub(crate) fn build_module_types(
ws: &Workspace,
) -> BTreeMap<String, IndexMap<String, TypeDef>> {
let mut out: BTreeMap<String, IndexMap<String, TypeDef>> = BTreeMap::new();
+6 -19
View File
@@ -171,6 +171,9 @@ pub(crate) fn mono_target_key(t: &MonoTarget) -> (String, String, String) {
/// declared param types. Module-resident fns are still needed
/// for non-class-method calls inside instance bodies.
pub fn build_workspace_env(ws: &Workspace) -> crate::Env {
// Drift risk: this fn and `crate::check_in_workspace` both
// populate `crate::Env`. If `synth` starts reading a new `Env`
// field, update both paths.
let mut env = crate::Env::default();
crate::builtins::install(&mut env);
env.workspace_registry = ws.registry.clone();
@@ -180,30 +183,14 @@ pub fn build_workspace_env(ws: &Workspace) -> crate::Env {
// env shape is interchangeable. `build_module_globals` may
// surface duplicate-def errors for malformed workspaces; the
// mono pass's pre-condition is that typecheck has already
// succeeded, so any error here means a caller violated that
// contract — fall back to an empty per-module table to keep
// this builder infallible.
// succeeded, so a failure here is a caller-contract violation.
let module_globals_index = crate::build_module_globals(ws)
.unwrap_or_default();
.expect("build_module_globals: pre-condition (typecheck succeeded) violated");
env.module_globals = module_globals_index
.iter()
.map(|(mname, g)| (mname.clone(), g.fns.clone()))
.collect();
env.module_types = ws
.modules
.iter()
.map(|(mname, m)| {
let tys: IndexMap<String, ailang_core::ast::TypeDef> = m
.defs
.iter()
.filter_map(|d| match d {
Def::Type(td) => Some((td.name.clone(), td.clone())),
_ => None,
})
.collect();
(mname.clone(), tys)
})
.collect();
env.module_types = crate::build_module_types(ws);
// Workspace-wide class-method table: merge per module's
// ModuleGlobals.class_methods.