Skip to main content

crisp_typeck/
infer.rs

1use crate::display::format_ty;
2use crate::env::{TypeEnv, collect_free_vars, generalize, instantiate, scheme, substitute_var};
3use crate::types::{InferContext, InferredSig, Scheme, Ty, is_arith_bound};
4use crate::unify::{UnifyError, unify};
5use crate::warning::TypeWarning;
6use crisp_ast::Span;
7use crisp_ast::count_holes;
8use crisp_ast::expr::{BinaryOp, Block, Expr, ExprKind, FieldInit, Stmt, UnaryOp};
9use crisp_ast::ident::Ident;
10use crisp_ast::is_hole_ident;
11use crisp_ast::item::{ExternBlock, FunctionDef, ImplBlock, Item, SourceFile, TypeBody};
12use crisp_ast::lift_holes;
13use crisp_ast::pat::{Pat, PatKind};
14use crisp_ast::ty::{Type, TypeKind};
15use crisp_resolve::module::load_module_graph;
16use crisp_resolve::{ResolvedRustImport, Resolver};
17use std::collections::{BTreeMap, BTreeSet, HashMap};
18use std::path::Path;
19use thiserror::Error;
20
21#[derive(Debug, Error)]
22pub enum TypeError {
23    #[error("unification error: {0}")]
24    Unify(#[from] UnifyError),
25    #[error("[E0040] unknown type `{name}`")]
26    UnknownType { name: String, span: Span },
27    #[error("[E0041] unknown name `{name}`")]
28    UnknownName { name: String, span: Span },
29    #[error("[E0042] resolve error: {0}")]
30    Resolve(#[from] crisp_resolve::ResolveError),
31    #[error(
32        "[E0043] ambiguous field `{field}` on unresolved type; annotate the parameter (candidates: {candidates})"
33    )]
34    AmbiguousField {
35        field: String,
36        candidates: String,
37        span: Span,
38    },
39    #[error("[E0084] cannot instantiate `{func}` with `{ty}`: `{ty}` does not implement `{bound}`")]
40    UnsatisfiedBound {
41        func: String,
42        ty: String,
43        bound: String,
44        span: Span,
45    },
46    #[error(
47        "[E0085] implicit closure has {found} hole(s) `_` but a function of {expected} parameter(s) is expected; write `|x, y| …`"
48    )]
49    HoleArity {
50        expected: usize,
51        found: usize,
52        span: Span,
53    },
54    #[error(
55        "[E0086] hole `_` is only valid where a function value is expected; write `|x| …` or use `_` as a call argument"
56    )]
57    HoleMisplaced { span: Span },
58    #[error("[E0087] cannot cast `{from}` as `{to}`; only `int` and `float`")]
59    InvalidCast {
60        from: String,
61        to: String,
62        span: Span,
63    },
64    #[error(
65        "[E0088] cannot infer element type of `vec`; pin it (`xs: vec<float> := new()`, or `-> vec<int> = new()`)"
66    )]
67    UninferredVec { span: Span },
68    #[error(
69        "[E0089] `{item}` is not declared in `extern rust {crate_name}`; add a scalar signature (`float`/`int`/`str`/`bool`)"
70    )]
71    UndeclaredRustImport {
72        crate_name: String,
73        item: String,
74        span: Span,
75    },
76    #[error(
77        "[E0090] `extern rust` types must be `float`, `int`, `str`, or `bool` (`{found}` is not allowed)"
78    )]
79    InvalidExternRustTy { found: String, span: Span },
80    #[error("unification error: {message}")]
81    UnifyAt { message: String, span: Span },
82}
83
84/// Inserted `int` → `float` (or explicit `as`) recorded for CIR / reveal (#112).
85#[derive(Debug, Clone)]
86pub struct NumericCoercion {
87    pub span: Span,
88    /// True when the source is an int literal (including `-2`). Emit a float literal, no lint.
89    pub literal: bool,
90    /// Explicit `as float` / `as int` — no W0087.
91    pub explicit: bool,
92    pub to_float: bool,
93}
94
95#[derive(Debug, Clone)]
96pub struct TypedCrate {
97    pub signatures: BTreeMap<String, InferredSig>,
98    /// `TypeName` → `method` → signature key (`module::Type::method`).
99    pub inherent_methods: BTreeMap<String, BTreeMap<String, String>>,
100    /// Resolved `use <crate> { … }` / `use rust.<crate> { … }` imports (spec §14.2).
101    pub rust_imports: Vec<ResolvedRustImport>,
102    /// `module::Trait for Type` → inferred trait args when the impl omitted `<>` (#77).
103    pub impl_trait_args: BTreeMap<String, Vec<Ty>>,
104    /// Checking-position numeric coercions (#112).
105    pub coercions: Vec<NumericCoercion>,
106    pub warnings: Vec<crate::warning::TypeWarning>,
107    /// Instantiated types of selected exprs (calls, array lits) for CIR emit (#119).
108    pub expr_tys: HashMap<Span, Ty>,
109    /// `extern rust crate { item(...) }` scalar signatures (#116).
110    pub rust_externs: Vec<RustExternSig>,
111}
112
113/// Declared `extern rust` function used to type imported crate items (#116).
114#[derive(Debug, Clone)]
115pub struct RustExternSig {
116    pub crate_name: String,
117    pub item: String,
118    pub params: Vec<Ty>,
119    pub ret: Ty,
120    pub fallible: bool,
121    pub span: Span,
122}
123
124impl TypedCrate {
125    pub fn rust_call_fallible(&self, crate_name: &str, item: &str) -> bool {
126        rust_import_returns_result(crate_name, item)
127            || self
128                .rust_externs
129                .iter()
130                .any(|s| s.crate_name == crate_name && s.item == item && s.fallible)
131    }
132}
133
134#[derive(Debug, Clone)]
135struct TraitMethodStub {
136    params: Vec<(String, Option<Ty>)>,
137    ret: Option<Ty>,
138}
139
140#[derive(Debug, Clone)]
141struct CallInst {
142    args: Vec<Ty>,
143    span: Span,
144}
145
146pub struct TypeChecker {
147    ctx: InferContext,
148    env: TypeEnv,
149    structs: BTreeMap<String, BTreeMap<String, Ty>>,
150    /// Named `shape` definitions (structural; also present in `structs` for field access).
151    shapes: BTreeSet<String>,
152    /// Type/shape name → declared type-parameter names (`Pair` → `["A", "B"]`).
153    type_params: BTreeMap<String, Vec<String>>,
154    /// Trait name → declared type-parameter names.
155    trait_generics: BTreeMap<String, Vec<String>>,
156    /// Rigid generic bindings in the current definition (`T` → `Named("T")`).
157    generic_params: BTreeMap<String, Ty>,
158    /// Enum name → variant name → payload field types.
159    enums: BTreeMap<String, BTreeMap<String, Vec<Ty>>>,
160    /// Trait name → method stubs (`self` filled at impl site).
161    traits: BTreeMap<String, BTreeMap<String, TraitMethodStub>>,
162    signatures: BTreeMap<String, InferredSig>,
163    /// `TypeName` → `method` → signature key.
164    inherent_methods: BTreeMap<String, BTreeMap<String, String>>,
165    /// Stack of expected `break` value types for nested `loop` expressions (§6.3).
166    loop_break_tys: Vec<Ty>,
167    /// Fresh vars for omitted impl trait args (`module::Trait for Type`).
168    impl_trait_fresh: BTreeMap<String, Vec<Ty>>,
169    /// Finalized inferred impl trait args.
170    impl_trait_args: BTreeMap<String, Vec<Ty>>,
171    /// Call-site argument types for crate-internal specialization (#76).
172    fn_instantiations: BTreeMap<String, Vec<CallInst>>,
173    /// Named generics used in operators / unique trait methods (`T` → `Add` / `Show`).
174    arith_named: BTreeMap<String, BTreeSet<String>>,
175    /// Unannotated type vars used as bound subjects (mapped to names after generalization).
176    arith_vars: BTreeMap<u32, BTreeSet<String>>,
177    /// `TypeName` → traits implemented in this crate (`Point` → `Show`).
178    trait_impls: BTreeMap<String, BTreeSet<String>>,
179    coercions: Vec<NumericCoercion>,
180    warnings: Vec<crate::warning::TypeWarning>,
181    expr_tys: HashMap<Span, Ty>,
182    fn_vec_tys: Vec<(Span, Ty)>,
183    extern_rust: BTreeMap<(String, String), RustExternSig>,
184    undeclared_rust: BTreeMap<String, (String, String)>,
185    /// Module currently being checked (`core.a`). Bare calls look up `{current}::{name}` (#146).
186    current_module: String,
187    /// Crisp `use path { name }` aliases for the module being checked (#146).
188    imported: TypeEnv,
189}
190
191impl TypeChecker {
192    pub fn check_crate(crate_root: &Path) -> Result<TypedCrate, TypeError> {
193        let resolved = Resolver::resolve_crate(crate_root)?;
194        let graph = load_module_graph(crate_root)?;
195        let mut checker = Self::new();
196        checker.register_prelude();
197        for node in graph.modules.values() {
198            checker.collect_extern_rust(&node.ast)?;
199        }
200        checker.register_rust_imports(&resolved.rust_imports);
201        for node in graph.modules.values() {
202            checker.collect_types(&node.module_path, &node.ast);
203        }
204        // Stub every function into the env before checking bodies so modules that
205        // sort after `main` (and same-module forward refs) are visible (#13).
206        for node in graph.modules.values() {
207            checker.collect_fn_stubs(&node.module_path, &node.ast)?;
208        }
209        for node in graph.modules.values() {
210            checker.check_module(&node.module_path, &node.ast)?;
211        }
212        // Apply substitutions from later modules, then name leftover holes.
213        // Otherwise `math.double` (before `math.scale`) publishes `twice<T>(x: float) -> T`.
214        checker.seal_open_signatures();
215        checker.specialize_internal_functions()?;
216        let expr_tys = checker
217            .expr_tys
218            .iter()
219            .map(|(s, t)| (*s, checker.ctx.apply(t)))
220            .collect();
221        Ok(TypedCrate {
222            signatures: checker.signatures,
223            inherent_methods: checker.inherent_methods,
224            rust_imports: resolved.rust_imports,
225            impl_trait_args: checker.impl_trait_args,
226            coercions: checker.coercions,
227            warnings: checker.warnings,
228            expr_tys,
229            rust_externs: checker.extern_rust.into_values().collect(),
230        })
231    }
232
233    fn new() -> Self {
234        Self {
235            ctx: InferContext::new(),
236            env: TypeEnv::new(),
237            structs: BTreeMap::new(),
238            shapes: BTreeSet::new(),
239            type_params: BTreeMap::new(),
240            trait_generics: BTreeMap::new(),
241            generic_params: BTreeMap::new(),
242            enums: BTreeMap::new(),
243            traits: BTreeMap::new(),
244            signatures: BTreeMap::new(),
245            inherent_methods: BTreeMap::new(),
246            loop_break_tys: Vec::new(),
247            impl_trait_fresh: BTreeMap::new(),
248            impl_trait_args: BTreeMap::new(),
249            fn_instantiations: BTreeMap::new(),
250            arith_named: BTreeMap::new(),
251            arith_vars: BTreeMap::new(),
252            trait_impls: BTreeMap::new(),
253            coercions: Vec::new(),
254            warnings: Vec::new(),
255            expr_tys: HashMap::new(),
256            fn_vec_tys: Vec::new(),
257            extern_rust: BTreeMap::new(),
258            undeclared_rust: BTreeMap::new(),
259            current_module: String::new(),
260            imported: TypeEnv::new(),
261        }
262    }
263
264    /// Bind imported Rust crate items into the type env (known stubs or `extern rust`).
265    fn register_rust_imports(&mut self, imports: &[ResolvedRustImport]) {
266        for imp in imports {
267            let (params, ret, _fallible, span) =
268                if let Some((params, ret)) = rust_import_fn_type(&imp.crate_name, &imp.item) {
269                    (
270                        params,
271                        ret,
272                        rust_import_returns_result(&imp.crate_name, &imp.item),
273                        Span::new(0, 0),
274                    )
275                } else if let Some(sig) = self
276                    .extern_rust
277                    .get(&(imp.crate_name.clone(), imp.item.clone()))
278                {
279                    (sig.params.clone(), sig.ret.clone(), sig.fallible, sig.span)
280                } else {
281                    self.undeclared_rust.insert(
282                        imp.local_name.clone(),
283                        (imp.crate_name.clone(), imp.item.clone()),
284                    );
285                    continue;
286                };
287            let fn_ty = Ty::Fn {
288                params: params.clone(),
289                ret: Box::new(ret.clone()),
290            };
291            self.env.insert(imp.local_name.clone(), scheme(fn_ty));
292            let module = format!("rust.{}", imp.crate_name);
293            let key = format!("{module}::{}", imp.local_name);
294            self.signatures.insert(
295                key,
296                InferredSig {
297                    module,
298                    name: imp.local_name.clone(),
299                    impl_ty: None,
300                    params: params
301                        .into_iter()
302                        .enumerate()
303                        .map(|(i, t)| (format!("arg{i}"), t))
304                        .collect(),
305                    ret,
306                    span,
307                    generics: Vec::new(),
308                    is_pub: false,
309                    inferred_from_use: false,
310                    instantiations: Vec::new(),
311                    mono_args: None,
312                    op_bounds: BTreeMap::new(),
313                },
314            );
315        }
316    }
317
318    fn collect_extern_rust(&mut self, file: &SourceFile) -> Result<(), TypeError> {
319        for item in &file.items {
320            let Item::Extern(ext) = item else {
321                continue;
322            };
323            let Some(crate_id) = &ext.rust_crate else {
324                continue;
325            };
326            for f in &ext.functions {
327                let mut param_tys = Vec::new();
328                for p in &f.params {
329                    let ty = if let Some(ast_ty) = &p.ty {
330                        self.ast_type(ast_ty)?
331                    } else {
332                        return Err(TypeError::InvalidExternRustTy {
333                            found: "unannotated".into(),
334                            span: p.span,
335                        });
336                    };
337                    if !rust_extern_scalar_ok(&ty) {
338                        return Err(TypeError::InvalidExternRustTy {
339                            found: format_ty(&ty),
340                            span: p.span,
341                        });
342                    }
343                    param_tys.push(ty);
344                }
345                let ret = if let Some(ast_ty) = &f.ret_type {
346                    self.ast_type(ast_ty)?
347                } else {
348                    Ty::Unit
349                };
350                if !rust_extern_scalar_ok(&ret) {
351                    return Err(TypeError::InvalidExternRustTy {
352                        found: format_ty(&ret),
353                        span: f.span,
354                    });
355                }
356                self.extern_rust.insert(
357                    (crate_id.name.clone(), f.name.name.clone()),
358                    RustExternSig {
359                        crate_name: crate_id.name.clone(),
360                        item: f.name.name.clone(),
361                        params: param_tys,
362                        ret,
363                        fallible: f.fallible,
364                        span: f.span,
365                    },
366                );
367            }
368        }
369        Ok(())
370    }
371
372    fn register_prelude(&mut self) {
373        for (name, ty) in [
374            ("int", Ty::Int),
375            ("uint", Ty::UInt),
376            ("float", Ty::Float),
377            ("bool", Ty::Bool),
378            ("char", Ty::Char),
379            ("str", Ty::Str),
380            ("Never", Ty::Never),
381            (
382                "vec",
383                Ty::Named {
384                    name: "vec".into(),
385                    args: vec![],
386                },
387            ),
388            (
389                "map",
390                Ty::Named {
391                    name: "map".into(),
392                    args: vec![],
393                },
394            ),
395            (
396                "set",
397                Ty::Named {
398                    name: "set".into(),
399                    args: vec![],
400                },
401            ),
402            (
403                "log",
404                Ty::Fn {
405                    params: vec![Ty::StrSlice],
406                    ret: Box::new(Ty::Unit),
407                },
408            ),
409            (
410                "some",
411                Ty::Fn {
412                    params: vec![self.ctx.fresh()],
413                    ret: Box::new(Ty::Option(Box::new(self.ctx.fresh()))),
414                },
415            ),
416            (
417                "none",
418                Ty::Fn {
419                    params: vec![],
420                    ret: Box::new(Ty::Option(Box::new(self.ctx.fresh()))),
421                },
422            ),
423        ] {
424            self.env.insert(name, scheme(ty));
425        }
426        let p = self.ctx.fresh();
427        let assert_ty = Ty::Fn {
428            params: vec![p.clone(), p],
429            ret: Box::new(Ty::Unit),
430        };
431        let assert_scheme = generalize(&self.env, &mut self.ctx, &assert_ty);
432        self.env.insert("assert_eq", assert_scheme);
433
434        let pp = self.ctx.fresh();
435        let print_ty = Ty::Fn {
436            params: vec![pp.clone()],
437            ret: Box::new(Ty::Unit),
438        };
439        let print_scheme = generalize(&self.env, &mut self.ctx, &print_ty);
440        self.env.insert("print", print_scheme);
441
442        {
443            let t = self.ctx.fresh();
444            let vt = vec_of(t.clone());
445            let new_ty = Ty::Fn {
446                params: vec![],
447                ret: Box::new(vt),
448            };
449            self.env
450                .insert("new", generalize(&self.env, &mut self.ctx, &new_ty));
451            let t = self.ctx.fresh();
452            let vt = vec_of(t.clone());
453            let push_ty = Ty::Fn {
454                params: vec![vt, t],
455                ret: Box::new(Ty::Unit),
456            };
457            self.env
458                .insert("push", generalize(&self.env, &mut self.ctx, &push_ty));
459            let t = self.ctx.fresh();
460            let vt = vec_of(t);
461            let len_ty = Ty::Fn {
462                params: vec![vt],
463                ret: Box::new(Ty::Int),
464            };
465            self.env
466                .insert("len", generalize(&self.env, &mut self.ctx, &len_ty));
467        }
468
469        for (name, ty) in stdlib_fn_types() {
470            self.env.insert(name, scheme(ty));
471        }
472        self.register_prelude_traits();
473    }
474
475    fn register_prelude_traits(&mut self) {
476        self.traits.entry("Show".into()).or_insert_with(|| {
477            BTreeMap::from([(
478                "show".into(),
479                TraitMethodStub {
480                    params: vec![("self".into(), None)],
481                    ret: Some(Ty::Str),
482                },
483            )])
484        });
485        self.traits.entry("Eq".into()).or_insert_with(|| {
486            BTreeMap::from([(
487                "equal".into(),
488                TraitMethodStub {
489                    params: vec![("self".into(), None), ("other".into(), None)],
490                    ret: Some(Ty::Bool),
491                },
492            )])
493        });
494        self.traits.entry("Ord".into()).or_insert_with(|| {
495            BTreeMap::from([(
496                "compare".into(),
497                TraitMethodStub {
498                    params: vec![("self".into(), None), ("other".into(), None)],
499                    ret: Some(Ty::Int),
500                },
501            )])
502        });
503    }
504
505    fn collect_types(&mut self, module: &str, file: &SourceFile) {
506        for item in &file.items {
507            if let Item::TypeDef(td) = item {
508                let gens: Vec<String> = td.generics.iter().map(|g| g.name.clone()).collect();
509                if !gens.is_empty() {
510                    self.type_params.insert(td.name.name.clone(), gens.clone());
511                }
512                let saved = self.bind_rigid_generics(&gens);
513                if let TypeBody::Struct(fields) = &td.body {
514                    let mut field_map = BTreeMap::new();
515                    for f in fields {
516                        if let Ok(ty) = self.ast_type(&f.ty) {
517                            field_map.insert(f.name.name.clone(), self.ctx.apply(&ty));
518                        }
519                    }
520                    self.structs.insert(td.name.name.clone(), field_map);
521                    self.env.insert(
522                        td.name.name.clone(),
523                        scheme(Ty::Named {
524                            name: td.name.name.clone(),
525                            args: vec![],
526                        }),
527                    );
528                } else if let TypeBody::Enum(variants) = &td.body {
529                    let mut variant_map = BTreeMap::new();
530                    for v in variants {
531                        let mut fields = Vec::new();
532                        for t in &v.fields {
533                            if let Ok(ty) = self.ast_type(t) {
534                                fields.push(self.ctx.apply(&ty));
535                            }
536                        }
537                        variant_map.insert(v.name.name.clone(), fields);
538                    }
539                    self.enums.insert(td.name.name.clone(), variant_map);
540                    self.env.insert(
541                        td.name.name.clone(),
542                        scheme(Ty::Named {
543                            name: td.name.name.clone(),
544                            args: vec![],
545                        }),
546                    );
547                } else if let TypeBody::Alias(ty) = &td.body
548                    && let Ok(t) = self.ast_type(ty)
549                {
550                    self.env.insert(td.name.name.clone(), scheme(t));
551                }
552                self.generic_params = saved;
553            } else if let Item::ShapeDef(shape) = item {
554                // Data shapes participate in field access like structs (#61).
555                let gens: Vec<String> = shape.generics.iter().map(|g| g.name.clone()).collect();
556                if !gens.is_empty() {
557                    self.type_params
558                        .insert(shape.name.name.clone(), gens.clone());
559                }
560                let saved = self.bind_rigid_generics(&gens);
561                let mut field_map = BTreeMap::new();
562                for f in &shape.fields {
563                    if let crisp_ast::item::ShapeField::Data { name, ty, .. } = f
564                        && let Ok(field_ty) = self.ast_type(ty)
565                    {
566                        field_map.insert(name.name.clone(), self.ctx.apply(&field_ty));
567                    }
568                }
569                self.shapes.insert(shape.name.name.clone());
570                self.structs.insert(shape.name.name.clone(), field_map);
571                self.env.insert(
572                    shape.name.name.clone(),
573                    scheme(Ty::Named {
574                        name: shape.name.name.clone(),
575                        args: vec![],
576                    }),
577                );
578                self.generic_params = saved;
579            } else if let Item::TraitDef(td) = item {
580                let gens: Vec<String> = td.generics.iter().map(|g| g.name.clone()).collect();
581                if !gens.is_empty() {
582                    self.trait_generics
583                        .insert(td.name.name.clone(), gens.clone());
584                }
585                let saved = self.bind_rigid_generics(&gens);
586                let mut methods = BTreeMap::new();
587                for m in &td.items {
588                    let mut params = Vec::new();
589                    let mut ok = true;
590                    for p in &m.params {
591                        let ty = if p.name.name == "self" && p.ty.is_none() {
592                            None
593                        } else if let Some(ast_ty) = &p.ty {
594                            match self.ast_type(ast_ty) {
595                                Ok(t) => Some(t),
596                                Err(_) => {
597                                    ok = false;
598                                    break;
599                                }
600                            }
601                        } else {
602                            Some(self.ctx.fresh())
603                        };
604                        params.push((p.name.name.clone(), ty));
605                    }
606                    if !ok {
607                        continue;
608                    }
609                    let ret = match &m.ret_type {
610                        Some(t) => match self.ast_type(t) {
611                            Ok(t) => Some(t),
612                            Err(_) => continue,
613                        },
614                        None => None,
615                    };
616                    methods.insert(m.name.name.clone(), TraitMethodStub { params, ret });
617                }
618                self.traits.insert(td.name.name.clone(), methods);
619                self.generic_params = saved;
620            }
621        }
622        let _ = module;
623    }
624
625    fn collect_fn_stubs(&mut self, module: &str, file: &SourceFile) -> Result<(), TypeError> {
626        for item in &file.items {
627            match item {
628                Item::Extern(ext) if ext.rust_crate.is_none() => {
629                    self.check_extern(module, ext)?;
630                }
631                Item::Function(f) => {
632                    let gens: Vec<String> = f.generics.iter().map(|g| g.name.clone()).collect();
633                    let saved = self.bind_rigid_generics(&gens);
634                    let mut params = Vec::new();
635                    for p in &f.params {
636                        let ty = if let Some(ast_ty) = &p.ty {
637                            self.ast_type(ast_ty)?
638                        } else {
639                            self.ctx.fresh()
640                        };
641                        params.push(ty);
642                    }
643                    let ret = if let Some(t) = &f.ret_type {
644                        self.ast_type(t)?
645                    } else {
646                        self.ctx.fresh()
647                    };
648                    let fn_ty = Ty::Fn {
649                        params,
650                        ret: Box::new(ret),
651                    };
652                    self.env.insert(
653                        format!("{}::{}", module, f.name.name),
654                        generalize_named_params(&fn_ty, &gens, &mut self.ctx),
655                    );
656                    self.generic_params = saved;
657                }
658                Item::Impl(ib) => {
659                    self.collect_impl_stubs(module, ib)?;
660                }
661                _ => {}
662            }
663        }
664        Ok(())
665    }
666
667    fn collect_impl_stubs(&mut self, module: &str, ib: &ImplBlock) -> Result<(), TypeError> {
668        let ty_name = match &ib.ty.kind {
669            TypeKind::Named(id) => id.name.clone(),
670            _ => {
671                return Err(TypeError::UnknownType {
672                    name: "impl".into(),
673                    span: ib.span,
674                });
675            }
676        };
677        let self_ty = Ty::Named {
678            name: ty_name.clone(),
679            args: vec![],
680        };
681        if let Some(tn) = &ib.trait_name {
682            self.trait_impls
683                .entry(ty_name.clone())
684                .or_default()
685                .insert(tn.name.clone());
686        }
687        let trait_subst = self.impl_trait_subst(module, ib, &ty_name)?;
688        for f in &ib.items {
689            let mut params = Vec::new();
690            for p in &f.params {
691                let ty = if p.name.name == "self" && p.ty.is_none() {
692                    self_ty.clone()
693                } else if let Some(ast_ty) = &p.ty {
694                    self.ast_type(ast_ty)?
695                } else {
696                    self.ctx.fresh()
697                };
698                params.push(ty);
699            }
700            let mut ret = if let Some(t) = &f.ret_type {
701                self.ast_type(t)?
702            } else {
703                self.ctx.fresh()
704            };
705            if !trait_subst.is_empty() {
706                params = params
707                    .into_iter()
708                    .map(|t| subst_named_params(&t, &trait_subst))
709                    .collect();
710                ret = subst_named_params(&ret, &trait_subst);
711            }
712            if let Some(tn) = &ib.trait_name
713                && let Some(methods) = self.traits.get(&tn.name)
714                && let Some(stub) = methods.get(&f.name.name)
715                && let Some(trait_ret) = &stub.ret
716            {
717                ret = subst_named_params(trait_ret, &trait_subst);
718            }
719            let key = format!("{module}::{ty_name}::{}", f.name.name);
720            self.inherent_methods
721                .entry(ty_name.clone())
722                .or_default()
723                .insert(f.name.name.clone(), key.clone());
724            // Placeholder signature so later passes can see the key early.
725            self.signatures.insert(
726                key,
727                InferredSig {
728                    module: module.to_string(),
729                    name: f.name.name.clone(),
730                    impl_ty: Some(ty_name.clone()),
731                    params: f
732                        .params
733                        .iter()
734                        .zip(params.iter())
735                        .map(|(p, t)| (p.name.name.clone(), t.clone()))
736                        .collect(),
737                    ret,
738                    span: f.span,
739                    generics: Vec::new(),
740                    is_pub: f.is_pub,
741                    inferred_from_use: false,
742                    instantiations: Vec::new(),
743                    mono_args: None,
744                    op_bounds: BTreeMap::new(),
745                },
746            );
747        }
748        // Trait impl: register remaining trait methods (including defaults) on the type (#59).
749        if let Some(tn) = &ib.trait_name
750            && let Some(trait_methods) = self.traits.get(&tn.name).cloned()
751        {
752            for (mname, stub) in trait_methods {
753                let methods = self.inherent_methods.entry(ty_name.clone()).or_default();
754                if methods.contains_key(&mname) {
755                    continue;
756                }
757                let key = format!("{module}::{ty_name}::{mname}");
758                methods.insert(mname.clone(), key.clone());
759                let sig_params: Vec<(String, Ty)> = stub
760                    .params
761                    .into_iter()
762                    .map(|(pname, pty)| {
763                        let ty = if pname == "self" {
764                            self_ty.clone()
765                        } else {
766                            let t = pty.unwrap_or_else(|| self.ctx.fresh());
767                            subst_named_params(&t, &trait_subst)
768                        };
769                        (pname, ty)
770                    })
771                    .collect();
772                let sig_ret =
773                    subst_named_params(&stub.ret.unwrap_or_else(|| self.ctx.fresh()), &trait_subst);
774                self.signatures.insert(
775                    key,
776                    InferredSig {
777                        module: module.to_string(),
778                        name: mname,
779                        impl_ty: Some(ty_name.clone()),
780                        params: sig_params,
781                        ret: sig_ret,
782                        span: ib.span,
783                        generics: Vec::new(),
784                        is_pub: false,
785                        inferred_from_use: false,
786                        instantiations: Vec::new(),
787                        mono_args: None,
788                        op_bounds: BTreeMap::new(),
789                    },
790                );
791            }
792        }
793        Ok(())
794    }
795
796    fn check_module(&mut self, module: &str, file: &SourceFile) -> Result<(), TypeError> {
797        let saved_mod = std::mem::replace(&mut self.current_module, module.to_string());
798        let imports = self.collect_crisp_imports(file);
799        let saved_imp = std::mem::replace(&mut self.imported, imports);
800        let result = (|| {
801            for item in &file.items {
802                match item {
803                    Item::Function(f) => self.check_function(module, f)?,
804                    Item::Impl(ib) => self.check_impl(module, ib)?,
805                    Item::Test(t) => self.check_test_block(module, &t.name, &t.body)?,
806                    Item::TestCompileFail(_) | Item::Extern(_) => {}
807                    _ => {}
808                }
809            }
810            Ok(())
811        })();
812        self.current_module = saved_mod;
813        self.imported = saved_imp;
814        result
815    }
816
817    fn collect_crisp_imports(&self, file: &SourceFile) -> TypeEnv {
818        let mut env = TypeEnv::new();
819        for item in &file.items {
820            let Item::Use(u) = item else {
821                continue;
822            };
823            if u.path.first().is_some_and(|p| p.name == "rust") {
824                continue;
825            }
826            let path = u
827                .path
828                .iter()
829                .map(|p| p.name.as_str())
830                .collect::<Vec<_>>()
831                .join(".");
832            if let Some(imports) = &u.imports {
833                for imp in imports {
834                    let local = imp.alias.as_ref().unwrap_or(&imp.name).name.clone();
835                    let q = format!("{}::{}", path, imp.name.name);
836                    if let Some(s) = self.env.get(&q) {
837                        env.insert(local, s.clone());
838                    }
839                }
840            } else {
841                let prefix = format!("{path}::");
842                for (k, s) in self.env.entries() {
843                    if let Some(bare) = k.strip_prefix(&prefix)
844                        && !bare.contains("::")
845                    {
846                        env.insert(bare.to_string(), s.clone());
847                    }
848                }
849            }
850        }
851        env
852    }
853
854    /// Env used to decide which holes may be named now. Skip this item (its own
855    /// stub still holds the body vars) and skip already-checked items so a later
856    /// `id(x) = x` can still generalize after an earlier `wrap(x) = id(x)`.
857    fn env_for_generalize(&self, self_name: &str) -> TypeEnv {
858        let mut env = self.env.clone();
859        env.remove(self_name);
860        env.remove(&format!("{}::{self_name}", self.current_module));
861        for sig in self.signatures.values() {
862            if sig.impl_ty.is_some() || sig.name.starts_with("test::") {
863                continue;
864            }
865            if sig.name != self_name {
866                env.remove(&sig.name);
867                env.remove(&format!("{}::{}", sig.module, sig.name));
868            }
869        }
870        env
871    }
872
873    /// After every body has been checked, substitute and name leftover holes.
874    fn seal_open_signatures(&mut self) {
875        let keys: Vec<String> = self.signatures.keys().cloned().collect();
876        for key in keys {
877            let Some(sig) = self.signatures.get(&key).cloned() else {
878                continue;
879            };
880            let param_types: Vec<(String, Ty)> = sig
881                .params
882                .iter()
883                .map(|(n, t)| (n.clone(), self.ctx.apply(t)))
884                .collect();
885            let ret = self.ctx.apply(&sig.ret);
886            if !sig.generics.is_empty() {
887                if let Some(s) = self.signatures.get_mut(&key) {
888                    s.params = param_types;
889                    s.ret = ret;
890                }
891                continue;
892            }
893            let fn_ty = Ty::Fn {
894                params: param_types.iter().map(|(_, t)| t.clone()).collect(),
895                ret: Box::new(ret.clone()),
896            };
897            let (named, inferred) = name_free_vars(&fn_ty);
898            if inferred.is_empty() {
899                if let Some(s) = self.signatures.get_mut(&key) {
900                    s.params = param_types;
901                    s.ret = ret;
902                }
903                continue;
904            }
905            let mut named_vars = Vec::new();
906            collect_free_vars(&fn_ty, &mut named_vars);
907            named_vars.sort_unstable();
908            named_vars.dedup();
909            self.name_expr_ty_vars(&named_vars, &inferred);
910            let mut params = param_types;
911            let mut named_ret = ret;
912            if let Ty::Fn { params: ps, ret: r } = &named {
913                for (i, t) in ps.iter().enumerate() {
914                    if let Some(slot) = params.get_mut(i) {
915                        slot.1 = t.clone();
916                    }
917                }
918                named_ret = r.as_ref().clone();
919            }
920            if let Some(s) = self.signatures.get_mut(&key) {
921                s.params = params;
922                s.ret = named_ret;
923                s.generics = inferred;
924                s.inferred_from_use = true;
925            }
926        }
927    }
928
929    fn check_impl(&mut self, module: &str, ib: &ImplBlock) -> Result<(), TypeError> {
930        let ty_name = match &ib.ty.kind {
931            TypeKind::Named(id) => id.name.clone(),
932            _ => {
933                return Err(TypeError::UnknownType {
934                    name: "impl".into(),
935                    span: ib.span,
936                });
937            }
938        };
939        for f in &ib.items {
940            self.check_impl_method(module, &ty_name, f)?;
941        }
942        self.finalize_impl_trait_args(module, ib, &ty_name)?;
943        Ok(())
944    }
945
946    fn check_impl_method(
947        &mut self,
948        module: &str,
949        ty_name: &str,
950        f: &FunctionDef,
951    ) -> Result<(), TypeError> {
952        let key = format!("{module}::{ty_name}::{}", f.name.name);
953        let stub = self.signatures.get(&key).cloned();
954        let (stub_params, stub_ret) = match stub {
955            Some(s) => (
956                s.params.iter().map(|(_, t)| t.clone()).collect::<Vec<_>>(),
957                Some(s.ret),
958            ),
959            None => (Vec::new(), None),
960        };
961        let self_ty = Ty::Named {
962            name: ty_name.to_string(),
963            args: vec![],
964        };
965
966        let mut local = self.env.clone();
967        let mut param_vars = Vec::new();
968        for (i, p) in f.params.iter().enumerate() {
969            let mut ty = stub_params
970                .get(i)
971                .cloned()
972                .unwrap_or_else(|| self.ctx.fresh());
973            if p.name.name == "self" && p.ty.is_none() {
974                unify(&mut self.ctx, &ty, &self_ty)?;
975                ty = self.ctx.apply(&self_ty);
976            } else if let Some(ast_ty) = &p.ty {
977                let ann = self.ast_type(ast_ty)?;
978                unify(&mut self.ctx, &ty, &ann)?;
979                ty = self.ctx.apply(&ann);
980            }
981            param_vars.push((p.name.name.clone(), ty.clone()));
982            local.insert(p.name.name.clone(), scheme(ty));
983        }
984        let ret_ann = f.ret_type.as_ref().map(|t| self.ast_type(t)).transpose()?;
985        let body_ty = self.infer_expr(&mut local, &f.body)?;
986        let param_types: Vec<(String, Ty)> = param_vars
987            .iter()
988            .map(|(n, t)| (n.clone(), self.ctx.apply(t)))
989            .collect();
990        let ret = if let Some(ann) = ret_ann {
991            self.unify_checking(&f.body, &body_ty, &ann)?;
992            if let Some(stub_r) = &stub_ret {
993                unify(&mut self.ctx, &body_ty, stub_r)?;
994            }
995            self.ctx.apply(&ann)
996        } else if let Some(stub_r) = &stub_ret {
997            unify(&mut self.ctx, &body_ty, stub_r)?;
998            self.ctx.apply(&body_ty)
999        } else {
1000            self.ctx.apply(&body_ty)
1001        };
1002        self.signatures.insert(
1003            key.clone(),
1004            InferredSig {
1005                module: module.to_string(),
1006                name: f.name.name.clone(),
1007                impl_ty: Some(ty_name.to_string()),
1008                params: param_types,
1009                ret,
1010                span: f.span,
1011                generics: Vec::new(),
1012                is_pub: f.is_pub,
1013                inferred_from_use: false,
1014                instantiations: Vec::new(),
1015                mono_args: None,
1016                op_bounds: BTreeMap::new(),
1017            },
1018        );
1019        self.inherent_methods
1020            .entry(ty_name.to_string())
1021            .or_default()
1022            .insert(f.name.name.clone(), key);
1023        Ok(())
1024    }
1025
1026    fn check_extern(&mut self, module: &str, ext: &ExternBlock) -> Result<(), TypeError> {
1027        for f in &ext.functions {
1028            let mut param_tys = Vec::new();
1029            for p in &f.params {
1030                let ty = if let Some(ast_ty) = &p.ty {
1031                    self.ast_type(ast_ty)?
1032                } else {
1033                    Ty::Int
1034                };
1035                param_tys.push(ty);
1036            }
1037            let ret = if let Some(ast_ty) = &f.ret_type {
1038                self.ast_type(ast_ty)?
1039            } else {
1040                Ty::Unit
1041            };
1042            let fn_ty = Ty::Fn {
1043                params: param_tys.clone(),
1044                ret: Box::new(ret.clone()),
1045            };
1046            self.env.insert(f.name.name.clone(), scheme(fn_ty));
1047            let key = format!("{}::{}", module, f.name.name);
1048            self.signatures.insert(
1049                key,
1050                InferredSig {
1051                    module: module.to_string(),
1052                    name: f.name.name.clone(),
1053                    impl_ty: None,
1054                    params: f
1055                        .params
1056                        .iter()
1057                        .enumerate()
1058                        .map(|(i, p)| (p.name.name.clone(), param_tys[i].clone()))
1059                        .collect(),
1060                    ret,
1061                    span: f.span,
1062                    generics: Vec::new(),
1063                    is_pub: false,
1064                    inferred_from_use: false,
1065                    instantiations: Vec::new(),
1066                    mono_args: None,
1067                    op_bounds: BTreeMap::new(),
1068                },
1069            );
1070        }
1071        let _ = ext;
1072        Ok(())
1073    }
1074
1075    fn check_test_block(
1076        &mut self,
1077        module: &str,
1078        name: &str,
1079        body: &Block,
1080    ) -> Result<(), TypeError> {
1081        self.fn_vec_tys.clear();
1082        let mut local = self.env.clone();
1083        let body_ty = self.infer_block(&mut local, body)?;
1084        unify(&mut self.ctx, &body_ty, &Ty::Unit)?;
1085        let key = format!("{module}::test::{name}");
1086        self.signatures.insert(
1087            key,
1088            InferredSig {
1089                module: module.to_string(),
1090                name: format!("test::{name}"),
1091                impl_ty: None,
1092                params: vec![],
1093                ret: Ty::Unit,
1094                span: body.span,
1095                generics: Vec::new(),
1096                is_pub: false,
1097                inferred_from_use: false,
1098                instantiations: Vec::new(),
1099                mono_args: None,
1100                op_bounds: BTreeMap::new(),
1101            },
1102        );
1103        self.reject_uninferred_vec(&[])?;
1104        Ok(())
1105    }
1106
1107    fn check_function(&mut self, module: &str, f: &FunctionDef) -> Result<(), TypeError> {
1108        self.arith_named.clear();
1109        self.arith_vars.clear();
1110        self.fn_vec_tys.clear();
1111        let gens: Vec<String> = f.generics.iter().map(|g| g.name.clone()).collect();
1112        let saved = self.bind_rigid_generics(&gens);
1113        // Reuse stub param/ret vars so call-site unifications from earlier modules stick.
1114        // Explicit generics are instantiated per call; the body is checked with rigid names.
1115        let stub_key = format!("{module}::{}", f.name.name);
1116        let stub = if gens.is_empty() {
1117            self.env
1118                .get(&stub_key)
1119                .map(|s| instantiate(&mut self.ctx, s))
1120                .map(|t| self.ctx.apply(&t))
1121        } else {
1122            None
1123        };
1124        let (stub_params, stub_ret) = match stub {
1125            Some(Ty::Fn { params, ret }) => (params, Some(*ret)),
1126            _ => (Vec::new(), None),
1127        };
1128
1129        let mut local = self.env.clone();
1130        let mut param_vars = Vec::new();
1131        for (i, p) in f.params.iter().enumerate() {
1132            let mut ty = stub_params
1133                .get(i)
1134                .cloned()
1135                .unwrap_or_else(|| self.ctx.fresh());
1136            if let Some(ast_ty) = &p.ty {
1137                let ann = self.ast_type(ast_ty)?;
1138                unify(&mut self.ctx, &ty, &ann)?;
1139                ty = self.ctx.apply(&ann);
1140            }
1141            param_vars.push((p.name.name.clone(), ty.clone()));
1142            local.insert(p.name.name.clone(), scheme(ty));
1143        }
1144        let ret_ann = f.ret_type.as_ref().map(|t| self.ast_type(t)).transpose()?;
1145        let body_ty = self.infer_expr(&mut local, &f.body)?;
1146        let ret = if let Some(ann) = ret_ann {
1147            self.unify_checking(&f.body, &body_ty, &ann)?;
1148            if let Some(stub_r) = &stub_ret {
1149                unify(&mut self.ctx, &body_ty, stub_r)?;
1150            }
1151            self.ctx.apply(&ann)
1152        } else if let Some(stub_r) = &stub_ret {
1153            unify(&mut self.ctx, &body_ty, stub_r)?;
1154            self.ctx.apply(&body_ty)
1155        } else {
1156            self.ctx.apply(&body_ty)
1157        };
1158        let param_types: Vec<(String, Ty)> = param_vars
1159            .iter()
1160            .map(|(n, t)| (n.clone(), self.ctx.apply(t)))
1161            .collect();
1162        let ret = self.ctx.apply(&ret);
1163        let fn_params: Vec<Ty> = param_types.iter().map(|(_, t)| t.clone()).collect();
1164        let mut fn_ty = Ty::Fn {
1165            params: fn_params,
1166            ret: Box::new(ret.clone()),
1167        };
1168        let mut gens = gens;
1169        let mut param_types = param_types;
1170        let mut ret = ret;
1171        let mut inferred_from_use = false;
1172        // Unannotated items with leftover free vars become a scheme (#76).
1173        // Forward-ref calls that already pinned the stub stay monomorphic.
1174        // Explicit `<>` / free type names (`x: T`) are pins and are not specialized.
1175        // Do not name holes that still belong to unchecked stubs (callee later in
1176        // filename order): `twice(x) = scale(x, 2.0)` must wait for `scale`.
1177        let pre_ty = fn_ty.clone();
1178        if gens.is_empty() {
1179            let env_wo = self.env_for_generalize(&f.name.name);
1180            let gen_scheme = generalize(&env_wo, &mut self.ctx, &fn_ty);
1181            if !gen_scheme.vars.is_empty() {
1182                let (named, inferred) = name_vars(&fn_ty, &gen_scheme.vars);
1183                if !inferred.is_empty() {
1184                    fn_ty = named;
1185                    gens = inferred;
1186                    inferred_from_use = true;
1187                    self.name_expr_ty_vars(&gen_scheme.vars, &gens);
1188                    if let Ty::Fn { params, ret: r } = &fn_ty {
1189                        for (i, t) in params.iter().enumerate() {
1190                            if let Some(slot) = param_types.get_mut(i) {
1191                                slot.1 = t.clone();
1192                            }
1193                        }
1194                        ret = r.as_ref().clone();
1195                    }
1196                }
1197            }
1198        }
1199        let op_bounds = self.take_op_bounds(&pre_ty, &gens);
1200        let key = format!("{module}::{}", f.name.name);
1201        self.signatures.insert(
1202            key,
1203            InferredSig {
1204                module: module.to_string(),
1205                name: f.name.name.clone(),
1206                impl_ty: None,
1207                params: param_types,
1208                ret: ret.clone(),
1209                span: f.span,
1210                generics: gens.clone(),
1211                is_pub: f.is_pub,
1212                inferred_from_use,
1213                instantiations: Vec::new(),
1214                mono_args: None,
1215                op_bounds,
1216            },
1217        );
1218        self.env.insert(
1219            format!("{module}::{}", f.name.name),
1220            if gens.is_empty() {
1221                scheme(fn_ty)
1222            } else {
1223                generalize_named_params(&fn_ty, &gens, &mut self.ctx)
1224            },
1225        );
1226        self.generic_params = saved;
1227        self.reject_uninferred_vec(&gens)?;
1228        Ok(())
1229    }
1230
1231    /// Record call-site instantiations. Internal single-use schemes set `mono_args`
1232    /// for emit; the typeck scheme stays generic so ownership still sees `T` (#76).
1233    /// Concrete instantiations must satisfy inferred bounds (#84).
1234    fn specialize_internal_functions(&mut self) -> Result<(), TypeError> {
1235        let insts = std::mem::take(&mut self.fn_instantiations);
1236        self.check_instantiation_bounds(&insts)?;
1237        let keys: Vec<String> = self.signatures.keys().cloned().collect();
1238        for key in keys {
1239            let Some(sig) = self.signatures.get(&key) else {
1240                continue;
1241            };
1242            if let Some(uses) = insts.get(&key).or_else(|| insts.get(&sig.name)) {
1243                let mut labels: Vec<String> = uses
1244                    .iter()
1245                    .map(|u| u.args.iter().map(format_ty).collect::<Vec<_>>().join(", "))
1246                    .collect();
1247                labels.sort();
1248                labels.dedup();
1249                if let Some(sig) = self.signatures.get_mut(&key) {
1250                    sig.instantiations = labels;
1251                }
1252            }
1253            let Some(sig) = self.signatures.get(&key) else {
1254                continue;
1255            };
1256            if sig.is_pub
1257                || !sig.inferred_from_use
1258                || sig.generics.is_empty()
1259                || sig.impl_ty.is_some()
1260            {
1261                continue;
1262            }
1263            let Some(uses) = insts.get(&key).or_else(|| insts.get(&sig.name)) else {
1264                continue;
1265            };
1266            if uses.is_empty() {
1267                continue;
1268            }
1269            let first = &uses[0].args;
1270            if !first.iter().all(ty_is_ground) {
1271                continue;
1272            }
1273            if !uses.iter().all(|u| &u.args == first) {
1274                continue;
1275            }
1276            if let Some(sig) = self.signatures.get_mut(&key) {
1277                sig.mono_args = Some(first.clone());
1278            }
1279        }
1280        Ok(())
1281    }
1282
1283    fn infer_expr(&mut self, env: &mut TypeEnv, expr: &Expr) -> Result<Ty, TypeError> {
1284        match &expr.kind {
1285            ExprKind::Int(_) => Ok(Ty::Int),
1286            ExprKind::Float(_) => Ok(Ty::Float),
1287            ExprKind::Bool(_) => Ok(Ty::Bool),
1288            ExprKind::Char(_) => Ok(Ty::Char),
1289            ExprKind::Str(_) => Ok(Ty::Str),
1290            ExprKind::Unit => Ok(Ty::Unit),
1291            ExprKind::Ident(id) if is_hole_ident(&id.name) => {
1292                Err(TypeError::HoleMisplaced { span: id.span })
1293            }
1294            ExprKind::Ident(id) => self.lookup(env, &id.name, id.span),
1295            ExprKind::Block(b) => self.infer_block(env, b),
1296            ExprKind::If {
1297                cond,
1298                then_branch,
1299                else_branch,
1300            } => {
1301                let cty = self.infer_expr(env, cond)?;
1302                unify(&mut self.ctx, &cty, &Ty::Bool)?;
1303                let t = self.infer_expr(env, then_branch)?;
1304                if let Some(e) = else_branch {
1305                    let e_ty = self.infer_expr(env, e)?;
1306                    unify(&mut self.ctx, &t, &e_ty)?;
1307                }
1308                Ok(self.ctx.apply(&t))
1309            }
1310            ExprKind::Match { scrutinee, arms } => {
1311                let scrut = self.infer_expr(env, scrutinee)?;
1312                let mut local = env.clone();
1313                let mut result = None;
1314                for arm in arms {
1315                    self.infer_pat(&mut local, &arm.pat, &scrut)?;
1316                    if let Some(g) = &arm.guard {
1317                        let gty = self.infer_expr(&mut local, g)?;
1318                        unify(&mut self.ctx, &gty, &Ty::Bool)?;
1319                    }
1320                    let body = self.infer_expr(&mut local, &arm.body)?;
1321                    result = Some(match result {
1322                        None => body,
1323                        Some(prev) => {
1324                            unify(&mut self.ctx, &prev, &body)?;
1325                            prev
1326                        }
1327                    });
1328                }
1329                Ok(result.unwrap_or(Ty::Unit))
1330            }
1331            ExprKind::Lambda { params, body } => {
1332                let mut local = env.clone();
1333                let mut ptys = Vec::new();
1334                for p in params {
1335                    let ty = if let Some(ast_ty) = &p.ty {
1336                        self.ast_type(ast_ty)?
1337                    } else {
1338                        self.ctx.fresh()
1339                    };
1340                    ptys.push(self.ctx.apply(&ty));
1341                    local.insert(p.name.name.clone(), scheme(ty));
1342                }
1343                let ret = self.infer_expr(&mut local, body)?;
1344                Ok(Ty::Fn {
1345                    params: ptys,
1346                    ret: Box::new(self.ctx.apply(&ret)),
1347                })
1348            }
1349            ExprKind::Call { func, args } => {
1350                if let ExprKind::Ident(id) = &func.kind
1351                    && let Some((crate_name, item)) = self.undeclared_rust.get(&id.name)
1352                {
1353                    return Err(TypeError::UndeclaredRustImport {
1354                        crate_name: crate_name.clone(),
1355                        item: item.clone(),
1356                        span: expr.span,
1357                    });
1358                }
1359                // Inherent methods parse as Call(Field(...), args) — not MethodCall.
1360                if let ExprKind::Field { base, field } = &func.kind
1361                    && let Some(ret) = self.try_infer_method_call(env, base, field, args)?
1362                {
1363                    return Ok(ret);
1364                }
1365                let ft = self.infer_expr(env, func)?;
1366                let ft = self.ctx.apply(&ft);
1367                let (params, ret) = match ft {
1368                    Ty::Fn { params, ret } => (params, ret),
1369                    Ty::Var(v) => {
1370                        let ps: Vec<_> = (0..args.len()).map(|_| self.ctx.fresh()).collect();
1371                        let ret = self.ctx.fresh();
1372                        unify(
1373                            &mut self.ctx,
1374                            &Ty::Var(v),
1375                            &Ty::Fn {
1376                                params: ps.clone(),
1377                                ret: Box::new(ret.clone()),
1378                            },
1379                        )?;
1380                        (ps, Box::new(ret))
1381                    }
1382                    other => {
1383                        return Err(TypeError::UnifyAt {
1384                            message: format!("type mismatch: expected function, found {other:?}"),
1385                            span: expr.span,
1386                        });
1387                    }
1388                };
1389                if args.len() != params.len() {
1390                    return Err(TypeError::Unify(UnifyError::Mismatch {
1391                        expected: format!("{} arguments", params.len()),
1392                        found: format!("{} arguments", args.len()),
1393                    }));
1394                }
1395                for (arg, pty) in args.iter().zip(params.iter()) {
1396                    let aty = self.infer_call_arg(env, arg, pty)?;
1397                    self.unify_or_shape_checking(arg, &aty, pty)?;
1398                }
1399                if let ExprKind::Ident(id) = &func.kind {
1400                    let applied: Vec<Ty> = params.iter().map(|p| self.ctx.apply(p)).collect();
1401                    if applied.iter().all(ty_is_ground) {
1402                        self.propagate_callee_bounds(&id.name, &applied);
1403                        let inst_key = format!("{}::{}", self.current_module, id.name);
1404                        self.fn_instantiations
1405                            .entry(inst_key)
1406                            .or_default()
1407                            .push(CallInst {
1408                                args: applied,
1409                                span: expr.span,
1410                            });
1411                    }
1412                }
1413                let ret = self.ctx.apply(&ret);
1414                self.record_expr_ty(expr.span, ret.clone());
1415                Ok(ret)
1416            }
1417            ExprKind::Field { base, field } => {
1418                // Enum variants: Color.Red (unit) / Color.Custom (ctor fn type)
1419                if let ExprKind::Ident(id) = &base.kind
1420                    && let Some(variants) = self.enums.get(&id.name)
1421                {
1422                    return match variants.get(&field.name) {
1423                        Some(payload) if payload.is_empty() => Ok(Ty::Named {
1424                            name: id.name.clone(),
1425                            args: vec![],
1426                        }),
1427                        Some(payload) => Ok(Ty::Fn {
1428                            params: payload.clone(),
1429                            ret: Box::new(Ty::Named {
1430                                name: id.name.clone(),
1431                                args: vec![],
1432                            }),
1433                        }),
1434                        None => Err(TypeError::UnknownName {
1435                            name: format!("{}.{}", id.name, field.name),
1436                            span: field.span,
1437                        }),
1438                    };
1439                }
1440                // Associated inherent method as a value: `Vec2.new` → fn type (no self).
1441                if let ExprKind::Ident(id) = &base.kind
1442                    && self.structs.contains_key(&id.name)
1443                    && let Some(sig) = self.method_sig(&id.name, &field.name)
1444                {
1445                    let has_self = sig
1446                        .params
1447                        .first()
1448                        .map(|(n, _)| n == "self")
1449                        .unwrap_or(false);
1450                    if !has_self {
1451                        let params: Vec<Ty> = sig.params.iter().map(|(_, t)| t.clone()).collect();
1452                        return Ok(Ty::Fn {
1453                            params,
1454                            ret: Box::new(sig.ret.clone()),
1455                        });
1456                    }
1457                }
1458                let base_ty = self.infer_expr(env, base)?;
1459                self.field_type(&base_ty, &field.name, field.span)
1460            }
1461            ExprKind::Unary { op, expr } => match op {
1462                UnaryOp::Not => {
1463                    let t = self.infer_expr(env, expr)?;
1464                    unify(&mut self.ctx, &t, &Ty::Bool)?;
1465                    Ok(Ty::Bool)
1466                }
1467                UnaryOp::Neg => {
1468                    let t = self.infer_expr(env, expr)?;
1469                    let t = self.ctx.apply(&t);
1470                    if matches!(t, Ty::Float) {
1471                        Ok(Ty::Float)
1472                    } else if matches!(t, Ty::Int | Ty::UInt) {
1473                        Ok(Ty::Int)
1474                    } else {
1475                        unify(&mut self.ctx, &t, &Ty::Int)?;
1476                        Ok(Ty::Int)
1477                    }
1478                }
1479            },
1480            ExprKind::Cast { expr: inner, ty } => self.infer_cast(env, expr.span, inner, ty),
1481            ExprKind::Binary { op, left, right } => self.infer_binary(env, *op, left, right),
1482            ExprKind::StructLit { name, fields } => self.check_struct_lit(env, name, fields),
1483            ExprKind::Bind { pat, value, .. } => {
1484                let ty = self.infer_value(env, value)?;
1485                let mut local = env.clone();
1486                self.infer_pat(&mut local, pat, &ty)?;
1487                Ok(Ty::Unit)
1488            }
1489            ExprKind::Pipe { left, right } => {
1490                let lt = self.infer_expr(env, left)?;
1491                let mut local = env.clone();
1492                let v = self.ctx.fresh();
1493                local.insert("_pipe".to_string(), scheme(v.clone()));
1494                unify(&mut self.ctx, &lt, &v)?;
1495                self.infer_expr(&mut local, right)
1496            }
1497            ExprKind::Return(Some(e)) => {
1498                self.infer_expr(env, e)?;
1499                Ok(Ty::Never)
1500            }
1501            ExprKind::Return(None) => Ok(Ty::Never),
1502            ExprKind::Try(inner) => {
1503                let t = self.infer_expr(env, inner)?;
1504                match self.ctx.apply(&t) {
1505                    Ty::Option(inner) => Ok(*inner),
1506                    other => {
1507                        let fresh = self.ctx.fresh();
1508                        unify(&mut self.ctx, &other, &Ty::Option(Box::new(fresh.clone())))?;
1509                        Ok(self.ctx.apply(&fresh))
1510                    }
1511                }
1512            }
1513            ExprKind::Catch { body, arms } => {
1514                let _ = self.infer_expr(env, body)?;
1515                let mut result = None;
1516                for arm in arms {
1517                    let body_ty = self.infer_expr(env, &arm.body)?;
1518                    result = Some(match result {
1519                        None => body_ty,
1520                        Some(prev) => {
1521                            unify(&mut self.ctx, &prev, &body_ty)?;
1522                            prev
1523                        }
1524                    });
1525                }
1526                Ok(result.unwrap_or(Ty::Unit))
1527            }
1528            ExprKind::Async(inner) => {
1529                let inner_ty = self.infer_expr(env, inner)?;
1530                Ok(Ty::Named {
1531                    name: "Future".into(),
1532                    args: vec![self.ctx.apply(&inner_ty)],
1533                })
1534            }
1535            ExprKind::Await(inner) => {
1536                let t = self.infer_expr(env, inner)?;
1537                match self.ctx.apply(&t) {
1538                    Ty::Named { name, args } if name == "Future" && args.len() == 1 => {
1539                        Ok(args[0].clone())
1540                    }
1541                    other => {
1542                        let fresh = self.ctx.fresh();
1543                        unify(
1544                            &mut self.ctx,
1545                            &other,
1546                            &Ty::Named {
1547                                name: "Future".into(),
1548                                args: vec![fresh.clone()],
1549                            },
1550                        )?;
1551                        Ok(self.ctx.apply(&fresh))
1552                    }
1553                }
1554            }
1555            ExprKind::Unsafe(inner) => self.infer_expr(env, inner),
1556            ExprKind::Spawn(inner) => {
1557                self.infer_expr(env, inner)?;
1558                Ok(Ty::Named {
1559                    name: "JoinHandle".into(),
1560                    args: vec![],
1561                })
1562            }
1563            ExprKind::While { cond, body } => {
1564                let cty = self.infer_expr(env, cond)?;
1565                unify(&mut self.ctx, &cty, &Ty::Bool)?;
1566                self.infer_expr(env, body)?;
1567                Ok(Ty::Unit)
1568            }
1569            ExprKind::For { pat, iter, body } => {
1570                let iter_ty = self.infer_expr(env, iter)?;
1571                let elem = self.ctx.fresh();
1572                let vec_ty = vec_of(elem.clone());
1573                unify(&mut self.ctx, &iter_ty, &vec_ty)?;
1574                let item_ty = match self.ctx.apply(&iter_ty) {
1575                    Ty::Named { name, args } if name == "vec" && args.len() == 1 => args[0].clone(),
1576                    _ => self.ctx.apply(&elem),
1577                };
1578                let mut local = env.clone();
1579                self.infer_pat(&mut local, pat, &item_ty)?;
1580                self.infer_expr(&mut local, body)?;
1581                Ok(Ty::Unit)
1582            }
1583            ExprKind::Loop(body) => {
1584                let break_ty = self.ctx.fresh();
1585                self.loop_break_tys.push(break_ty.clone());
1586                let _ = self.infer_expr(env, body)?;
1587                self.loop_break_tys.pop();
1588                Ok(self.ctx.apply(&break_ty))
1589            }
1590            ExprKind::Break(value) => {
1591                let vt = if let Some(v) = value {
1592                    self.infer_expr(env, v)?
1593                } else {
1594                    Ty::Unit
1595                };
1596                if let Some(expected) = self.loop_break_tys.last().cloned() {
1597                    unify(&mut self.ctx, &vt, &expected)?;
1598                }
1599                Ok(Ty::Never)
1600            }
1601            ExprKind::Continue => Ok(Ty::Never),
1602            ExprKind::Assign { target, value } => {
1603                let expected = self.lookup(env, &target.name, target.span)?;
1604                let got = self.infer_value(env, value)?;
1605                self.unify_checking(value, &got, &expected)
1606                    .map_err(|e| TypeError::UnifyAt {
1607                        message: e.to_string(),
1608                        span: expr.span,
1609                    })?;
1610                Ok(Ty::Unit)
1611            }
1612            ExprKind::Index { base, index } => {
1613                let elem = self.infer_index(env, base, index, expr.span)?;
1614                self.record_expr_ty(expr.span, elem.clone());
1615                Ok(elem)
1616            }
1617            ExprKind::IndexAssign { base, index, value } => {
1618                let elem = self.infer_index(env, base, index, expr.span)?;
1619                let got = self.infer_value(env, value)?;
1620                self.unify_checking(value, &got, &elem)
1621                    .map_err(|e| TypeError::UnifyAt {
1622                        message: e.to_string(),
1623                        span: expr.span,
1624                    })?;
1625                Ok(Ty::Unit)
1626            }
1627            ExprKind::Array(elems) => {
1628                let elem = self.ctx.fresh();
1629                for e in elems {
1630                    let et = self.infer_expr(env, e)?;
1631                    self.unify_checking(e, &et, &elem)?;
1632                }
1633                let ty = vec_of(self.ctx.apply(&elem));
1634                self.record_expr_ty(expr.span, ty.clone());
1635                Ok(ty)
1636            }
1637            _ => Ok(self.ctx.fresh()),
1638        }
1639    }
1640
1641    fn infer_binary(
1642        &mut self,
1643        env: &mut TypeEnv,
1644        op: BinaryOp,
1645        left: &Expr,
1646        right: &Expr,
1647    ) -> Result<Ty, TypeError> {
1648        let lt = self.infer_expr(env, left)?;
1649        let rt = self.infer_expr(env, right)?;
1650        match op {
1651            BinaryOp::Concat => {
1652                unify(&mut self.ctx, &lt, &Ty::Str)?;
1653                unify(&mut self.ctx, &rt, &Ty::StrSlice)?;
1654                Ok(Ty::Str)
1655            }
1656            BinaryOp::Mod => {
1657                unify(&mut self.ctx, &lt, &rt)?;
1658                unify(&mut self.ctx, &lt, &Ty::Int)?;
1659                Ok(Ty::Int)
1660            }
1661            BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div => {
1662                let l = self.ctx.apply(&lt);
1663                let r = self.ctx.apply(&rt);
1664                if matches!(l, Ty::Float) && matches!(r, Ty::Int | Ty::UInt) {
1665                    self.record_int_to_float(right, false);
1666                    return Ok(Ty::Float);
1667                }
1668                if matches!(r, Ty::Float) && matches!(l, Ty::Int | Ty::UInt) {
1669                    self.record_int_to_float(left, false);
1670                    return Ok(Ty::Float);
1671                }
1672                unify(&mut self.ctx, &lt, &rt)?;
1673                let t = self.ctx.apply(&lt);
1674                let r = self.ctx.apply(&rt);
1675                if matches!(t, Ty::Float) || matches!(r, Ty::Float) {
1676                    unify(&mut self.ctx, &t, &Ty::Float)?;
1677                    Ok(Ty::Float)
1678                } else if matches!(t, Ty::Int | Ty::UInt) || matches!(r, Ty::Int | Ty::UInt) {
1679                    unify(&mut self.ctx, &t, &Ty::Int)?;
1680                    Ok(Ty::Int)
1681                } else if let Some(op) = arith_trait_name(op) {
1682                    self.record_arith(&t, op);
1683                    Ok(t)
1684                } else {
1685                    unify(&mut self.ctx, &t, &Ty::Int)?;
1686                    Ok(Ty::Int)
1687                }
1688            }
1689            BinaryOp::Pow => {
1690                self.unify_checking(left, &lt, &Ty::Float)?;
1691                self.unify_checking(right, &rt, &Ty::Float)?;
1692                Ok(Ty::Float)
1693            }
1694            BinaryOp::Eq
1695            | BinaryOp::Ne
1696            | BinaryOp::Lt
1697            | BinaryOp::Le
1698            | BinaryOp::Gt
1699            | BinaryOp::Ge => {
1700                let l = self.ctx.apply(&lt);
1701                let r = self.ctx.apply(&rt);
1702                if matches!(l, Ty::Float) && matches!(r, Ty::Int | Ty::UInt) {
1703                    self.record_int_to_float(right, false);
1704                    return Ok(Ty::Bool);
1705                }
1706                if matches!(r, Ty::Float) && matches!(l, Ty::Int | Ty::UInt) {
1707                    self.record_int_to_float(left, false);
1708                    return Ok(Ty::Bool);
1709                }
1710                unify(&mut self.ctx, &lt, &rt)?;
1711                Ok(Ty::Bool)
1712            }
1713            BinaryOp::And | BinaryOp::Or => {
1714                unify(&mut self.ctx, &lt, &Ty::Bool)?;
1715                unify(&mut self.ctx, &rt, &Ty::Bool)?;
1716                Ok(Ty::Bool)
1717            }
1718            _ => Ok(self.ctx.fresh()),
1719        }
1720    }
1721
1722    fn infer_value(&mut self, env: &mut TypeEnv, expr: &Expr) -> Result<Ty, TypeError> {
1723        if count_holes(expr) > 0 {
1724            self.infer_hole_lambda(env, expr, None)
1725        } else {
1726            self.infer_expr(env, expr)
1727        }
1728    }
1729
1730    fn infer_call_arg(
1731        &mut self,
1732        env: &mut TypeEnv,
1733        arg: &Expr,
1734        expected: &Ty,
1735    ) -> Result<Ty, TypeError> {
1736        let expected = self.ctx.apply(expected);
1737        if count_holes(arg) > 0 {
1738            return self.infer_hole_lambda(env, arg, Some(&expected));
1739        }
1740        self.infer_expr(env, arg)
1741    }
1742
1743    fn infer_hole_lambda(
1744        &mut self,
1745        env: &mut TypeEnv,
1746        expr: &Expr,
1747        expected: Option<&Ty>,
1748    ) -> Result<Ty, TypeError> {
1749        let found = count_holes(expr);
1750        if found == 0 {
1751            return self.infer_expr(env, expr);
1752        }
1753        if let Some(exp) = expected {
1754            match self.ctx.apply(exp) {
1755                Ty::Fn { params, .. } if params.len() != found => {
1756                    return Err(TypeError::HoleArity {
1757                        expected: params.len(),
1758                        found,
1759                        span: expr.span,
1760                    });
1761                }
1762                Ty::Fn { .. } => {}
1763                _ => return Err(TypeError::HoleMisplaced { span: expr.span }),
1764            }
1765        }
1766        let lifted = lift_holes(expr).expect("count_holes > 0");
1767        self.infer_expr(env, &lifted)
1768    }
1769
1770    fn infer_block(&mut self, env: &mut TypeEnv, block: &Block) -> Result<Ty, TypeError> {
1771        let mut local = env.clone();
1772        for stmt in &block.stmts {
1773            match stmt {
1774                Stmt::Bind { pat, value, .. } => {
1775                    let ty = self.infer_value(&mut local, value)?;
1776                    self.infer_pat(&mut local, pat, &ty)?;
1777                }
1778                Stmt::Assign { target, value } => {
1779                    let expected = self.lookup(&local, &target.name, target.span)?;
1780                    let got = self.infer_value(&mut local, value)?;
1781                    self.unify_checking(value, &got, &expected)?;
1782                }
1783                Stmt::Expr(e) => {
1784                    self.infer_expr(&mut local, e)?;
1785                }
1786            }
1787        }
1788        if let Some(tail) = &block.tail {
1789            self.infer_expr(&mut local, tail)
1790        } else {
1791            Ok(Ty::Unit)
1792        }
1793    }
1794
1795    fn infer_pat(&mut self, env: &mut TypeEnv, pat: &Pat, ty: &Ty) -> Result<(), TypeError> {
1796        match &pat.kind {
1797            PatKind::Wildcard => Ok(()),
1798            PatKind::Ident(id) => {
1799                // Value restriction (#78): locals and `mut` bindings stay monomorphic.
1800                env.insert(id.name.clone(), scheme(self.ctx.apply(ty)));
1801                Ok(())
1802            }
1803            PatKind::Tuple(pats) => {
1804                if let Ty::Tuple(ts) = self.ctx.apply(ty) {
1805                    for (p, t) in pats.iter().zip(ts) {
1806                        self.infer_pat(env, p, &t)?;
1807                    }
1808                    Ok(())
1809                } else {
1810                    let vars: Vec<_> = (0..pats.len()).map(|_| self.ctx.fresh()).collect();
1811                    unify(&mut self.ctx, ty, &Ty::Tuple(vars.clone()))?;
1812                    for (p, t) in pats.iter().zip(vars) {
1813                        self.infer_pat(env, p, &t)?;
1814                    }
1815                    Ok(())
1816                }
1817            }
1818            PatKind::Enum {
1819                name,
1820                variant,
1821                args,
1822            } => {
1823                let enum_ty = Ty::Named {
1824                    name: name.name.clone(),
1825                    args: vec![],
1826                };
1827                unify(&mut self.ctx, ty, &enum_ty)?;
1828                let Some(variants) = self.enums.get(&name.name) else {
1829                    return Err(TypeError::UnknownType {
1830                        name: name.name.clone(),
1831                        span: name.span,
1832                    });
1833                };
1834                let Some(payload) = variants.get(&variant.name) else {
1835                    return Err(TypeError::UnknownName {
1836                        name: format!("{}.{}", name.name, variant.name),
1837                        span: variant.span,
1838                    });
1839                };
1840                if args.len() != payload.len() {
1841                    return Err(TypeError::Unify(UnifyError::Mismatch {
1842                        expected: format!("{} payload fields", payload.len()),
1843                        found: format!("{} pattern args", args.len()),
1844                    }));
1845                }
1846                let payload = payload.clone();
1847                for (arg, field_ty) in args.iter().zip(payload) {
1848                    self.infer_pat(env, arg, &field_ty)?;
1849                }
1850                Ok(())
1851            }
1852            _ => Ok(()),
1853        }
1854    }
1855
1856    fn check_struct_lit(
1857        &mut self,
1858        env: &mut TypeEnv,
1859        name: &Ident,
1860        fields: &[FieldInit],
1861    ) -> Result<Ty, TypeError> {
1862        let schema =
1863            self.structs
1864                .get(&name.name)
1865                .cloned()
1866                .ok_or_else(|| TypeError::UnknownType {
1867                    name: name.name.clone(),
1868                    span: name.span,
1869                })?;
1870        let gens = self
1871            .type_params
1872            .get(&name.name)
1873            .cloned()
1874            .unwrap_or_default();
1875        let subst: BTreeMap<String, Ty> =
1876            gens.iter().map(|g| (g.clone(), self.ctx.fresh())).collect();
1877        let schema: BTreeMap<String, Ty> = schema
1878            .iter()
1879            .map(|(k, v)| (k.clone(), subst_named_params(v, &subst)))
1880            .collect();
1881        let field_types: Vec<_> = fields
1882            .iter()
1883            .map(|field| {
1884                schema
1885                    .get(&field.name.name)
1886                    .cloned()
1887                    .ok_or_else(|| TypeError::UnknownType {
1888                        name: field.name.name.clone(),
1889                        span: field.name.span,
1890                    })
1891            })
1892            .collect::<Result<_, _>>()?;
1893        for (field, expected) in fields.iter().zip(field_types) {
1894            let got = self.infer_expr(env, &field.value)?;
1895            self.unify_checking(&field.value, &got, &expected)?;
1896        }
1897        let args: Vec<Ty> = gens
1898            .iter()
1899            .map(|g| self.ctx.apply(subst.get(g).expect("generic subst")))
1900            .collect();
1901        Ok(Ty::Named {
1902            name: name.name.clone(),
1903            args,
1904        })
1905    }
1906
1907    fn method_sig(&self, ty_name: &str, method: &str) -> Option<&InferredSig> {
1908        let key = self.inherent_methods.get(ty_name)?.get(method)?;
1909        self.signatures.get(key)
1910    }
1911
1912    /// Resolve `Type.assoc(args)` / `recv.method(args)` for inherent impls (§5.4).
1913    /// Returns `Ok(None)` when this is not an inherent-method call (caller falls through).
1914    fn try_infer_method_call(
1915        &mut self,
1916        env: &mut TypeEnv,
1917        base: &Expr,
1918        field: &Ident,
1919        args: &[Expr],
1920    ) -> Result<Option<Ty>, TypeError> {
1921        // Associated: `Vec2.new(x, y)` — base is a known struct type name, not an enum.
1922        if let ExprKind::Ident(id) = &base.kind
1923            && self.structs.contains_key(&id.name)
1924            && !self.enums.contains_key(&id.name)
1925            && let Some(sig) = self.method_sig(&id.name, &field.name).cloned()
1926        {
1927            let has_self = sig
1928                .params
1929                .first()
1930                .map(|(n, _)| n == "self")
1931                .unwrap_or(false);
1932            if has_self {
1933                return Err(TypeError::Unify(UnifyError::Mismatch {
1934                    expected: format!("instance method `{}.{}(self, …)`", id.name, field.name),
1935                    found: "associated call on type name".into(),
1936                }));
1937            }
1938            if args.len() != sig.params.len() {
1939                return Err(TypeError::Unify(UnifyError::Mismatch {
1940                    expected: format!("{} arguments", sig.params.len()),
1941                    found: format!("{} arguments", args.len()),
1942                }));
1943            }
1944            for (arg, (_, pty)) in args.iter().zip(sig.params.iter()) {
1945                let aty = self.infer_expr(env, arg)?;
1946                self.unify_checking(arg, &aty, pty)?;
1947            }
1948            return Ok(Some(self.ctx.apply(&sig.ret)));
1949        }
1950
1951        // Instance: `v.magnitude()` / `v.scale(2.0)` / generic `x.show()` → `T: Show` (#84).
1952        let base_ty = self.infer_expr(env, base)?;
1953        let base_ty = self.ctx.apply(&base_ty);
1954        if self.is_bound_subject(&base_ty)
1955            && let Some(ret) = self.try_infer_bound_method(env, &base_ty, field, args)?
1956        {
1957            return Ok(Some(ret));
1958        }
1959
1960        let candidate_tys: Vec<String> = self
1961            .inherent_methods
1962            .iter()
1963            .filter_map(|(ty, methods)| {
1964                if methods.contains_key(&field.name) {
1965                    Some(ty.clone())
1966                } else {
1967                    None
1968                }
1969            })
1970            .collect();
1971        if candidate_tys.is_empty() {
1972            return Ok(None);
1973        }
1974
1975        let ty_name = match &base_ty {
1976            Ty::Named { name, .. } => name.clone(),
1977            Ty::Var(v) if candidate_tys.len() == 1 => {
1978                let name = candidate_tys[0].clone();
1979                unify(
1980                    &mut self.ctx,
1981                    &Ty::Var(*v),
1982                    &Ty::Named {
1983                        name: name.clone(),
1984                        args: vec![],
1985                    },
1986                )?;
1987                name
1988            }
1989            _ => return Ok(None),
1990        };
1991
1992        let Some(sig) = self.method_sig(&ty_name, &field.name).cloned() else {
1993            return Ok(None);
1994        };
1995        let has_self = sig
1996            .params
1997            .first()
1998            .map(|(n, _)| n == "self")
1999            .unwrap_or(false);
2000        if !has_self {
2001            // Associated method called on a value — not supported.
2002            return Ok(None);
2003        }
2004        let self_ty = Ty::Named {
2005            name: ty_name,
2006            args: vec![],
2007        };
2008        unify(&mut self.ctx, &base_ty, &self_ty)?;
2009        let param_tys: Vec<&Ty> = sig.params.iter().skip(1).map(|(_, t)| t).collect();
2010        if args.len() != param_tys.len() {
2011            return Err(TypeError::Unify(UnifyError::Mismatch {
2012                expected: format!("{} arguments", param_tys.len()),
2013                found: format!("{} arguments", args.len()),
2014            }));
2015        }
2016        for (arg, pty) in args.iter().zip(param_tys) {
2017            let aty = self.infer_expr(env, arg)?;
2018            self.unify_checking(arg, &aty, pty)?;
2019        }
2020        Ok(Some(self.ctx.apply(&sig.ret)))
2021    }
2022
2023    fn field_type(&mut self, base: &Ty, field: &str, span: Span) -> Result<Ty, TypeError> {
2024        let base = self.ctx.apply(base);
2025        if let Ty::Named { name, args } = &base
2026            && let Some(fields) = self.instantiate_schema(name, args)
2027        {
2028            return fields.get(field).cloned().ok_or(TypeError::UnknownType {
2029                name: field.to_string(),
2030                span,
2031            });
2032        }
2033        // Unannotated params stay as type vars. If exactly one known struct has
2034        // this field, constrain the var to that struct (issue #12).
2035        // Exclude shapes — they are constraints, not concrete constructors.
2036        if let Ty::Var(v) = base {
2037            let mut candidates: Vec<(&String, &Ty)> = self
2038                .structs
2039                .iter()
2040                .filter(|(name, _)| !self.shapes.contains(*name))
2041                .filter_map(|(name, fields)| fields.get(field).map(|ty| (name, ty)))
2042                .collect();
2043            candidates.sort_by(|a, b| a.0.cmp(b.0));
2044            match candidates.as_slice() {
2045                [(name, field_ty)] => {
2046                    unify(
2047                        &mut self.ctx,
2048                        &Ty::Var(v),
2049                        &Ty::Named {
2050                            name: (*name).clone(),
2051                            args: vec![],
2052                        },
2053                    )?;
2054                    return Ok((*field_ty).clone());
2055                }
2056                [] => {}
2057                many => {
2058                    let names = many
2059                        .iter()
2060                        .map(|(n, _)| n.as_str())
2061                        .collect::<Vec<_>>()
2062                        .join(", ");
2063                    return Err(TypeError::AmbiguousField {
2064                        field: field.to_string(),
2065                        candidates: names,
2066                        span,
2067                    });
2068                }
2069            }
2070        }
2071        Err(TypeError::UnknownType {
2072            name: field.to_string(),
2073            span,
2074        })
2075    }
2076
2077    fn is_int_literal(expr: &Expr) -> bool {
2078        match &expr.kind {
2079            ExprKind::Int(_) => true,
2080            ExprKind::Unary {
2081                op: UnaryOp::Neg,
2082                expr,
2083            } => Self::is_int_literal(expr),
2084            _ => false,
2085        }
2086    }
2087
2088    fn record_int_to_float(&mut self, expr: &Expr, explicit: bool) {
2089        let literal = Self::is_int_literal(expr);
2090        self.coercions.push(NumericCoercion {
2091            span: expr.span,
2092            literal,
2093            explicit,
2094            to_float: true,
2095        });
2096        if !literal && !explicit {
2097            self.warnings
2098                .push(TypeWarning::IntToFloat { span: expr.span });
2099        }
2100    }
2101
2102    fn unify_checking(&mut self, expr: &Expr, got: &Ty, expected: &Ty) -> Result<(), TypeError> {
2103        let got = self.ctx.apply(got);
2104        let expected = self.ctx.apply(expected);
2105        if matches!(expected, Ty::Float) && matches!(got, Ty::Int | Ty::UInt) {
2106            self.record_int_to_float(expr, false);
2107            return Ok(());
2108        }
2109        unify(&mut self.ctx, &got, &expected)?;
2110        Ok(())
2111    }
2112
2113    fn unify_or_shape_checking(
2114        &mut self,
2115        expr: &Expr,
2116        actual: &Ty,
2117        expected: &Ty,
2118    ) -> Result<(), TypeError> {
2119        let got = self.ctx.apply(actual);
2120        let expected_ty = self.ctx.apply(expected);
2121        if matches!(expected_ty, Ty::Float) && matches!(got, Ty::Int | Ty::UInt) {
2122            self.record_int_to_float(expr, false);
2123            return Ok(());
2124        }
2125        self.unify_or_shape(actual, expected)
2126    }
2127
2128    fn infer_cast(
2129        &mut self,
2130        env: &mut TypeEnv,
2131        span: Span,
2132        inner: &Expr,
2133        ty: &Type,
2134    ) -> Result<Ty, TypeError> {
2135        let from = self.infer_expr(env, inner)?;
2136        let to = self.ast_type(ty)?;
2137        let from = self.ctx.apply(&from);
2138        let to = self.ctx.apply(&to);
2139        match (&from, &to) {
2140            (Ty::Int | Ty::UInt | Ty::Float, Ty::Float) => {
2141                if matches!(from, Ty::Int | Ty::UInt) {
2142                    self.record_int_to_float(inner, true);
2143                }
2144                Ok(Ty::Float)
2145            }
2146            (Ty::Int | Ty::UInt | Ty::Float, Ty::Int) => {
2147                if matches!(from, Ty::Float) {
2148                    self.coercions.push(NumericCoercion {
2149                        span: inner.span,
2150                        literal: false,
2151                        explicit: true,
2152                        to_float: false,
2153                    });
2154                }
2155                Ok(Ty::Int)
2156            }
2157            _ => Err(TypeError::InvalidCast {
2158                from: format_ty(&from),
2159                to: format_ty(&to),
2160                span,
2161            }),
2162        }
2163    }
2164
2165    /// Unify normally, or accept structural match when the expected type is a shape (§3.5).
2166    fn unify_or_shape(&mut self, actual: &Ty, expected: &Ty) -> Result<(), TypeError> {
2167        let expected = self.ctx.apply(expected);
2168        if let Ty::Named { name, args } = &expected
2169            && self.shapes.contains(name)
2170        {
2171            return self.check_shape_arg(actual, name, args);
2172        }
2173        unify(&mut self.ctx, actual, &expected)?;
2174        Ok(())
2175    }
2176
2177    fn check_shape_arg(
2178        &mut self,
2179        actual: &Ty,
2180        shape_name: &str,
2181        shape_args: &[Ty],
2182    ) -> Result<(), TypeError> {
2183        let actual = self.ctx.apply(actual);
2184        let Some(shape_fields) = self.instantiate_schema(shape_name, shape_args) else {
2185            return Err(TypeError::UnknownType {
2186                name: shape_name.to_string(),
2187                span: Span::new(0, 0),
2188            });
2189        };
2190        match &actual {
2191            Ty::Named { name, args } if name == shape_name => {
2192                if args.len() == shape_args.len() {
2193                    for (a, b) in args.iter().zip(shape_args) {
2194                        unify(&mut self.ctx, a, b)?;
2195                    }
2196                }
2197                Ok(())
2198            }
2199            Ty::Named { name, args } => {
2200                let Some(fields) = self.instantiate_schema(name, args) else {
2201                    return Err(TypeError::Unify(UnifyError::Mismatch {
2202                        expected: format!("type satisfying shape `{shape_name}`"),
2203                        found: name.clone(),
2204                    }));
2205                };
2206                for (fname, fty) in &shape_fields {
2207                    let Some(aty) = fields.get(fname) else {
2208                        return Err(TypeError::Unify(UnifyError::Mismatch {
2209                            expected: format!("shape `{shape_name}` (field `{fname}: {fty:?}`)"),
2210                            found: name.clone(),
2211                        }));
2212                    };
2213                    unify(&mut self.ctx, aty, fty).map_err(|err| {
2214                        TypeError::Unify(UnifyError::Mismatch {
2215                            expected: format!("shape `{shape_name}` (field `{fname}: {fty:?}`)"),
2216                            found: format!("{name} ({err})"),
2217                        })
2218                    })?;
2219                }
2220                Ok(())
2221            }
2222            other => Err(TypeError::Unify(UnifyError::Mismatch {
2223                expected: format!("type satisfying shape `{shape_name}`"),
2224                found: format!("{other:?}"),
2225            })),
2226        }
2227    }
2228
2229    fn lookup(&mut self, env: &TypeEnv, name: &str, span: Span) -> Result<Ty, TypeError> {
2230        let scheme = env
2231            .get(name)
2232            .or_else(|| self.imported.get(name))
2233            .or_else(|| {
2234                let q = format!("{}::{name}", self.current_module);
2235                env.get(&q).or_else(|| self.env.get(&q))
2236            })
2237            .ok_or_else(|| TypeError::UnknownName {
2238                name: name.to_string(),
2239                span,
2240            })?;
2241        Ok(instantiate(&mut self.ctx, scheme))
2242    }
2243
2244    fn infer_index(
2245        &mut self,
2246        env: &mut TypeEnv,
2247        base: &Expr,
2248        index: &Expr,
2249        span: Span,
2250    ) -> Result<Ty, TypeError> {
2251        let bt = self.infer_expr(env, base)?;
2252        let it = self.infer_expr(env, index)?;
2253        unify(&mut self.ctx, &it, &Ty::Int).map_err(|e| TypeError::UnifyAt {
2254            message: e.to_string(),
2255            span: index.span,
2256        })?;
2257        let elem = self.ctx.fresh();
2258        self.unify_checking(base, &bt, &vec_of(elem.clone()))
2259            .map_err(|e| TypeError::UnifyAt {
2260                message: e.to_string(),
2261                span,
2262            })?;
2263        Ok(match self.ctx.apply(&bt) {
2264            Ty::Named { name, args } if name == "vec" && args.len() == 1 => args[0].clone(),
2265            _ => self.ctx.apply(&elem),
2266        })
2267    }
2268
2269    fn record_expr_ty(&mut self, span: Span, ty: Ty) {
2270        if matches!(&ty, Ty::Named { name, .. } if name == "vec") {
2271            self.fn_vec_tys.push((span, ty.clone()));
2272        }
2273        self.expr_tys.insert(span, ty);
2274    }
2275
2276    /// After leftover holes are named `T`/`U`/…, rewrite recorded call types so
2277    /// `new()` emits `Vec::<T>` (or the mono arg) instead of `CirTy::Var` → String.
2278    fn name_expr_ty_vars(&mut self, vars: &[u32], names: &[String]) {
2279        for ty in self.expr_tys.values_mut() {
2280            *ty = self.ctx.apply(ty);
2281            for (v, name) in vars.iter().zip(names) {
2282                *ty = substitute_var(
2283                    ty,
2284                    *v,
2285                    &Ty::Named {
2286                        name: name.clone(),
2287                        args: vec![],
2288                    },
2289                );
2290            }
2291        }
2292    }
2293
2294    fn reject_uninferred_vec(&mut self, gens: &[String]) -> Result<(), TypeError> {
2295        let pending = std::mem::take(&mut self.fn_vec_tys);
2296        if !gens.is_empty() {
2297            return Ok(());
2298        }
2299        for (span, ty) in pending {
2300            let t = self.ctx.apply(&ty);
2301            if vec_elem_uninferred(&t) {
2302                return Err(TypeError::UninferredVec { span });
2303            }
2304        }
2305        Ok(())
2306    }
2307
2308    fn record_arith(&mut self, ty: &Ty, op: &str) {
2309        self.record_bound(ty, op);
2310    }
2311
2312    fn record_bound(&mut self, ty: &Ty, bound: &str) {
2313        match self.ctx.apply(ty) {
2314            Ty::Named { name, args } if args.is_empty() => {
2315                self.arith_named
2316                    .entry(name)
2317                    .or_default()
2318                    .insert(bound.into());
2319            }
2320            Ty::Var(v) => {
2321                self.arith_vars.entry(v).or_default().insert(bound.into());
2322            }
2323            _ => {}
2324        }
2325    }
2326
2327    fn take_op_bounds(&mut self, pre_ty: &Ty, gens: &[String]) -> BTreeMap<String, Vec<String>> {
2328        let mut named = std::mem::take(&mut self.arith_named);
2329        let vars = std::mem::take(&mut self.arith_vars);
2330        let mut applied_vars: BTreeMap<u32, BTreeSet<String>> = BTreeMap::new();
2331        // Body recording may key a var that later unified with the stub ret (#84).
2332        for (v, ops) in vars {
2333            match self.ctx.apply(&Ty::Var(v)) {
2334                Ty::Named { name, args } if args.is_empty() => {
2335                    named.entry(name).or_default().extend(ops);
2336                }
2337                Ty::Var(w) => {
2338                    applied_vars.entry(w).or_default().extend(ops);
2339                }
2340                _ => {}
2341            }
2342        }
2343        let mut free = Vec::new();
2344        collect_free_vars(pre_ty, &mut free);
2345        free.sort_unstable();
2346        free.dedup();
2347        for (i, v) in free.iter().enumerate() {
2348            if let Some(ops) = applied_vars.get(v) {
2349                named
2350                    .entry(generic_name(i))
2351                    .or_default()
2352                    .extend(ops.iter().cloned());
2353            }
2354        }
2355        let mut out = BTreeMap::new();
2356        for g in gens {
2357            if let Some(ops) = named.remove(g) {
2358                let mut list: Vec<String> = ops.into_iter().collect();
2359                list.sort();
2360                out.insert(g.clone(), list);
2361            }
2362        }
2363        out
2364    }
2365
2366    fn is_bound_subject(&self, ty: &Ty) -> bool {
2367        match ty {
2368            Ty::Var(_) => true,
2369            Ty::Named { name, args } if args.is_empty() => self.generic_params.contains_key(name),
2370            _ => false,
2371        }
2372    }
2373
2374    fn try_infer_bound_method(
2375        &mut self,
2376        env: &mut TypeEnv,
2377        base_ty: &Ty,
2378        field: &Ident,
2379        args: &[Expr],
2380    ) -> Result<Option<Ty>, TypeError> {
2381        let mut candidates: Vec<String> = self
2382            .traits
2383            .iter()
2384            .filter(|(name, methods)| {
2385                methods.contains_key(&field.name)
2386                    && self
2387                        .trait_generics
2388                        .get(*name)
2389                        .map(|g| g.is_empty())
2390                        .unwrap_or(true)
2391            })
2392            .map(|(name, _)| name.clone())
2393            .collect();
2394        candidates.sort();
2395        match candidates.as_slice() {
2396            [] => Ok(None),
2397            [trait_name] => {
2398                let stub = self
2399                    .traits
2400                    .get(trait_name)
2401                    .and_then(|m| m.get(&field.name))
2402                    .cloned()
2403                    .ok_or_else(|| TypeError::UnknownName {
2404                        name: field.name.clone(),
2405                        span: field.span,
2406                    })?;
2407                self.record_bound(base_ty, trait_name);
2408                let param_tys: Vec<Ty> = stub
2409                    .params
2410                    .iter()
2411                    .skip(1)
2412                    .map(|(_, t)| t.clone().unwrap_or_else(|| base_ty.clone()))
2413                    .collect();
2414                if args.len() != param_tys.len() {
2415                    return Err(TypeError::Unify(UnifyError::Mismatch {
2416                        expected: format!("{} arguments", param_tys.len()),
2417                        found: format!("{} arguments", args.len()),
2418                    }));
2419                }
2420                for (arg, pty) in args.iter().zip(param_tys.iter()) {
2421                    let aty = self.infer_expr(env, arg)?;
2422                    unify(&mut self.ctx, &aty, pty)?;
2423                }
2424                Ok(Some(stub.ret.unwrap_or_else(|| base_ty.clone())))
2425            }
2426            many => Err(TypeError::Unify(UnifyError::Mismatch {
2427                expected: format!("unique trait providing `{}`", field.name),
2428                found: many.join(", "),
2429            })),
2430        }
2431    }
2432
2433    fn free_fn_sig(&self, name: &str) -> Option<&InferredSig> {
2434        let q = format!("{}::{name}", self.current_module);
2435        if let Some(s) = self.signatures.get(&q).filter(|s| s.impl_ty.is_none()) {
2436            return Some(s);
2437        }
2438        self.signatures
2439            .values()
2440            .find(|s| s.name == name && s.impl_ty.is_none())
2441    }
2442
2443    fn propagate_callee_bounds(&mut self, fname: &str, applied: &[Ty]) {
2444        let Some(sig) = self.free_fn_sig(fname) else {
2445            return;
2446        };
2447        if sig.op_bounds.is_empty() {
2448            return;
2449        }
2450        let bounds = sig.op_bounds.clone();
2451        let gens = sig.generics.clone();
2452        let params = sig.params.clone();
2453        let mut subst = BTreeMap::new();
2454        for ((_, scheme_ty), inst_ty) in params.iter().zip(applied.iter()) {
2455            collect_generic_subst(scheme_ty, inst_ty, &gens, &mut subst);
2456        }
2457        for (g, bs) in &bounds {
2458            if let Some(ty) = subst.get(g) {
2459                for b in bs {
2460                    self.record_bound(ty, b);
2461                }
2462            }
2463        }
2464    }
2465
2466    fn check_instantiation_bounds(
2467        &self,
2468        insts: &BTreeMap<String, Vec<CallInst>>,
2469    ) -> Result<(), TypeError> {
2470        for (fname, uses) in insts {
2471            let Some(sig) = self
2472                .signatures
2473                .get(fname)
2474                .or_else(|| self.free_fn_sig(fname))
2475            else {
2476                continue;
2477            };
2478            if sig.op_bounds.is_empty() || sig.generics.is_empty() {
2479                continue;
2480            }
2481            for use_site in uses {
2482                let mut subst = BTreeMap::new();
2483                for ((_, scheme_ty), inst_ty) in sig.params.iter().zip(use_site.args.iter()) {
2484                    collect_generic_subst(scheme_ty, inst_ty, &sig.generics, &mut subst);
2485                }
2486                for g in &sig.generics {
2487                    let Some(bounds) = sig.op_bounds.get(g) else {
2488                        continue;
2489                    };
2490                    let Some(ty) = subst.get(g) else {
2491                        continue;
2492                    };
2493                    if !self.ty_is_checkable(ty) {
2494                        continue;
2495                    }
2496                    for bound in bounds {
2497                        if !self.ty_implements(ty, bound) {
2498                            return Err(TypeError::UnsatisfiedBound {
2499                                func: fname.clone(),
2500                                ty: format_ty(ty),
2501                                bound: bound.clone(),
2502                                span: use_site.span,
2503                            });
2504                        }
2505                    }
2506                }
2507            }
2508        }
2509        Ok(())
2510    }
2511
2512    fn ty_is_checkable(&self, ty: &Ty) -> bool {
2513        match ty {
2514            Ty::Int
2515            | Ty::UInt
2516            | Ty::Float
2517            | Ty::Bool
2518            | Ty::Char
2519            | Ty::Str
2520            | Ty::StrSlice
2521            | Ty::Unit
2522            | Ty::Never => true,
2523            Ty::Named { name, args } => {
2524                (self.structs.contains_key(name) || self.enums.contains_key(name))
2525                    && !self.shapes.contains(name)
2526                    && args.iter().all(|a| self.ty_is_checkable(a))
2527            }
2528            Ty::Option(inner) | Ty::Slice(inner) | Ty::Ref { inner, .. } => {
2529                self.ty_is_checkable(inner)
2530            }
2531            Ty::Tuple(ts) => ts.iter().all(|t| self.ty_is_checkable(t)),
2532            _ => false,
2533        }
2534    }
2535
2536    fn ty_implements(&self, ty: &Ty, bound: &str) -> bool {
2537        if is_arith_bound(bound) {
2538            return matches!(ty, Ty::Int | Ty::UInt | Ty::Float);
2539        }
2540        match ty {
2541            Ty::Named { name, args } if args.is_empty() => self
2542                .trait_impls
2543                .get(name)
2544                .is_some_and(|s| s.contains(bound)),
2545            _ => false,
2546        }
2547    }
2548
2549    fn bind_rigid_generics(&mut self, gens: &[String]) -> BTreeMap<String, Ty> {
2550        let saved = self.generic_params.clone();
2551        for g in gens {
2552            self.generic_params.insert(
2553                g.clone(),
2554                Ty::Named {
2555                    name: g.clone(),
2556                    args: vec![],
2557                },
2558            );
2559        }
2560        saved
2561    }
2562
2563    fn instantiate_schema(&self, name: &str, args: &[Ty]) -> Option<BTreeMap<String, Ty>> {
2564        let fields = self.structs.get(name)?.clone();
2565        let Some(gens) = self.type_params.get(name) else {
2566            return Some(fields);
2567        };
2568        if args.len() != gens.len() {
2569            return Some(fields);
2570        }
2571        let subst: BTreeMap<String, Ty> = gens.iter().cloned().zip(args.iter().cloned()).collect();
2572        Some(
2573            fields
2574                .iter()
2575                .map(|(k, v)| (k.clone(), subst_named_params(v, &subst)))
2576                .collect(),
2577        )
2578    }
2579
2580    fn impl_trait_key(module: &str, trait_name: &str, ty_name: &str) -> String {
2581        format!("{module}::{trait_name} for {ty_name}")
2582    }
2583
2584    fn impl_trait_subst(
2585        &mut self,
2586        module: &str,
2587        ib: &ImplBlock,
2588        ty_name: &str,
2589    ) -> Result<BTreeMap<String, Ty>, TypeError> {
2590        let Some(tn) = &ib.trait_name else {
2591            return Ok(BTreeMap::new());
2592        };
2593        let gens = self
2594            .trait_generics
2595            .get(&tn.name)
2596            .cloned()
2597            .unwrap_or_default();
2598        if gens.is_empty() {
2599            return Ok(BTreeMap::new());
2600        }
2601        if ib.trait_args.is_empty() {
2602            let mut subst = BTreeMap::new();
2603            let mut fresh = Vec::new();
2604            for g in &gens {
2605                let v = self.ctx.fresh();
2606                subst.insert(g.clone(), v.clone());
2607                fresh.push(v);
2608            }
2609            self.impl_trait_fresh
2610                .insert(Self::impl_trait_key(module, &tn.name, ty_name), fresh);
2611            return Ok(subst);
2612        }
2613        self.trait_arg_subst(&tn.name, &ib.trait_args)
2614    }
2615
2616    fn finalize_impl_trait_args(
2617        &mut self,
2618        module: &str,
2619        ib: &ImplBlock,
2620        ty_name: &str,
2621    ) -> Result<(), TypeError> {
2622        let Some(tn) = &ib.trait_name else {
2623            return Ok(());
2624        };
2625        let key = Self::impl_trait_key(module, &tn.name, ty_name);
2626        let Some(fresh) = self.impl_trait_fresh.remove(&key) else {
2627            return Ok(());
2628        };
2629        let mut args = Vec::new();
2630        for t in fresh {
2631            let applied = self.ctx.apply(&t);
2632            if matches!(applied, Ty::Var(_)) {
2633                return Err(TypeError::UnknownType {
2634                    name: format!(
2635                        "cannot infer `{}` type arguments for `{ty_name}`; write `impl {}<...> for {ty_name}`",
2636                        tn.name, tn.name
2637                    ),
2638                    span: ib.span,
2639                });
2640            }
2641            args.push(applied);
2642        }
2643        self.impl_trait_args.insert(key, args);
2644        Ok(())
2645    }
2646
2647    fn trait_arg_subst(
2648        &mut self,
2649        trait_name: &str,
2650        args: &[Type],
2651    ) -> Result<BTreeMap<String, Ty>, TypeError> {
2652        let Some(gens) = self.trait_generics.get(trait_name).cloned() else {
2653            return Ok(BTreeMap::new());
2654        };
2655        if gens.is_empty() || args.is_empty() {
2656            return Ok(BTreeMap::new());
2657        }
2658        let mut subst = BTreeMap::new();
2659        for (g, ast_ty) in gens.iter().zip(args.iter()) {
2660            subst.insert(g.clone(), self.ast_type(ast_ty)?);
2661        }
2662        Ok(subst)
2663    }
2664
2665    fn ast_type(&mut self, ty: &Type) -> Result<Ty, TypeError> {
2666        match &ty.kind {
2667            TypeKind::Named(id) => {
2668                if let Some(bound) = self.generic_params.get(&id.name) {
2669                    return Ok(bound.clone());
2670                }
2671                match id.name.as_str() {
2672                    "Never" => Ok(Ty::Never),
2673                    "()" => Ok(Ty::Unit),
2674                    "int" => Ok(Ty::Int),
2675                    "uint" => Ok(Ty::UInt),
2676                    "float" => Ok(Ty::Float),
2677                    "bool" => Ok(Ty::Bool),
2678                    "char" => Ok(Ty::Char),
2679                    "str" => Ok(Ty::Str),
2680                    other => Ok(Ty::Named {
2681                        name: other.to_string(),
2682                        args: vec![],
2683                    }),
2684                }
2685            }
2686            TypeKind::Never => Ok(Ty::Never),
2687            TypeKind::Unit => Ok(Ty::Unit),
2688            TypeKind::Option(inner) => Ok(Ty::Option(Box::new(self.ast_type(inner)?))),
2689            TypeKind::Ref { mutable, inner } => Ok(Ty::Ref {
2690                mutable: *mutable,
2691                inner: Box::new(self.ast_type(inner)?),
2692            }),
2693            TypeKind::Tuple(ts) => Ok(Ty::Tuple(
2694                ts.iter()
2695                    .map(|t| self.ast_type(t))
2696                    .collect::<Result<_, _>>()?,
2697            )),
2698            TypeKind::Array { elem, len } => Ok(Ty::Array {
2699                elem: Box::new(self.ast_type(elem)?),
2700                len: *len,
2701            }),
2702            TypeKind::Slice(inner) => Ok(Ty::Slice(Box::new(self.ast_type(inner)?))),
2703            TypeKind::Fn { params, ret } => Ok(Ty::Fn {
2704                params: params
2705                    .iter()
2706                    .map(|p| self.ast_type(p))
2707                    .collect::<Result<_, _>>()?,
2708                ret: Box::new(self.ast_type(ret)?),
2709            }),
2710            TypeKind::Generic { base, args } => {
2711                let base_ty = self.ast_type(base)?;
2712                if let Ty::Named { name, .. } = base_ty {
2713                    Ok(Ty::Named {
2714                        name,
2715                        args: args
2716                            .iter()
2717                            .map(|a| self.ast_type(a))
2718                            .collect::<Result<_, _>>()?,
2719                    })
2720                } else {
2721                    Ok(base_ty)
2722                }
2723            }
2724            TypeKind::Constrained { inner, .. } => self.ast_type(inner),
2725        }
2726    }
2727}
2728
2729fn arith_trait_name(op: BinaryOp) -> Option<&'static str> {
2730    match op {
2731        BinaryOp::Add => Some("Add"),
2732        BinaryOp::Sub => Some("Sub"),
2733        BinaryOp::Mul => Some("Mul"),
2734        BinaryOp::Div => Some("Div"),
2735        _ => None,
2736    }
2737}
2738
2739fn subst_named_params(ty: &Ty, subst: &BTreeMap<String, Ty>) -> Ty {
2740    match ty {
2741        Ty::Named { name, args } if args.is_empty() => {
2742            subst.get(name).cloned().unwrap_or_else(|| ty.clone())
2743        }
2744        Ty::Named { name, args } => Ty::Named {
2745            name: name.clone(),
2746            args: args.iter().map(|a| subst_named_params(a, subst)).collect(),
2747        },
2748        Ty::Fn { params, ret } => Ty::Fn {
2749            params: params
2750                .iter()
2751                .map(|p| subst_named_params(p, subst))
2752                .collect(),
2753            ret: Box::new(subst_named_params(ret, subst)),
2754        },
2755        Ty::Option(inner) => Ty::Option(Box::new(subst_named_params(inner, subst))),
2756        Ty::Slice(inner) => Ty::Slice(Box::new(subst_named_params(inner, subst))),
2757        Ty::Array { elem, len } => Ty::Array {
2758            elem: Box::new(subst_named_params(elem, subst)),
2759            len: *len,
2760        },
2761        Ty::Ref { mutable, inner } => Ty::Ref {
2762            mutable: *mutable,
2763            inner: Box::new(subst_named_params(inner, subst)),
2764        },
2765        Ty::Tuple(ts) => Ty::Tuple(ts.iter().map(|t| subst_named_params(t, subst)).collect()),
2766        other => other.clone(),
2767    }
2768}
2769
2770fn ty_is_ground(ty: &Ty) -> bool {
2771    let mut vars = Vec::new();
2772    collect_free_vars(ty, &mut vars);
2773    vars.is_empty()
2774}
2775
2776fn collect_generic_subst(scheme: &Ty, inst: &Ty, gens: &[String], out: &mut BTreeMap<String, Ty>) {
2777    match (scheme, inst) {
2778        (Ty::Named { name, args }, inst) if args.is_empty() && gens.iter().any(|g| g == name) => {
2779            out.entry(name.clone()).or_insert_with(|| inst.clone());
2780        }
2781        (Ty::Named { name: n1, args: a1 }, Ty::Named { name: n2, args: a2 })
2782            if n1 == n2 && a1.len() == a2.len() =>
2783        {
2784            for (s, i) in a1.iter().zip(a2.iter()) {
2785                collect_generic_subst(s, i, gens, out);
2786            }
2787        }
2788        (
2789            Ty::Fn {
2790                params: p1,
2791                ret: r1,
2792            },
2793            Ty::Fn {
2794                params: p2,
2795                ret: r2,
2796            },
2797        ) if p1.len() == p2.len() => {
2798            for (s, i) in p1.iter().zip(p2.iter()) {
2799                collect_generic_subst(s, i, gens, out);
2800            }
2801            collect_generic_subst(r1, r2, gens, out);
2802        }
2803        (Ty::Option(a), Ty::Option(b)) | (Ty::Slice(a), Ty::Slice(b)) => {
2804            collect_generic_subst(a, b, gens, out);
2805        }
2806        (Ty::Array { elem: a, .. }, Ty::Array { elem: b, .. }) => {
2807            collect_generic_subst(a, b, gens, out);
2808        }
2809        (Ty::Ref { inner: a, .. }, Ty::Ref { inner: b, .. }) => {
2810            collect_generic_subst(a, b, gens, out);
2811        }
2812        (Ty::Tuple(a), Ty::Tuple(b)) if a.len() == b.len() => {
2813            for (s, i) in a.iter().zip(b.iter()) {
2814                collect_generic_subst(s, i, gens, out);
2815            }
2816        }
2817        _ => {}
2818    }
2819}
2820
2821fn generic_name(i: usize) -> String {
2822    match i {
2823        0 => "T".into(),
2824        1 => "U".into(),
2825        2 => "V".into(),
2826        3 => "W".into(),
2827        n => format!("T{n}"),
2828    }
2829}
2830
2831fn name_free_vars(ty: &Ty) -> (Ty, Vec<String>) {
2832    let mut vars = Vec::new();
2833    collect_free_vars(ty, &mut vars);
2834    vars.sort_unstable();
2835    vars.dedup();
2836    name_vars(ty, &vars)
2837}
2838
2839fn name_vars(ty: &Ty, vars: &[u32]) -> (Ty, Vec<String>) {
2840    let mut vars = vars.to_vec();
2841    vars.sort_unstable();
2842    vars.dedup();
2843    if vars.is_empty() {
2844        return (ty.clone(), Vec::new());
2845    }
2846    let names: Vec<String> = vars
2847        .iter()
2848        .enumerate()
2849        .map(|(i, _)| generic_name(i))
2850        .collect();
2851    let mut named = ty.clone();
2852    for (v, name) in vars.iter().zip(&names) {
2853        named = substitute_var(
2854            &named,
2855            *v,
2856            &Ty::Named {
2857                name: name.clone(),
2858                args: vec![],
2859            },
2860        );
2861    }
2862    (named, names)
2863}
2864
2865fn generalize_named_params(ty: &Ty, gens: &[String], ctx: &mut InferContext) -> Scheme {
2866    if gens.is_empty() {
2867        return scheme(ty.clone());
2868    }
2869    let mut subst = BTreeMap::new();
2870    let mut vars = Vec::new();
2871    for g in gens {
2872        let fresh = ctx.fresh();
2873        if let Ty::Var(v) = &fresh {
2874            vars.push(*v);
2875        }
2876        subst.insert(g.clone(), fresh);
2877    }
2878    Scheme {
2879        vars,
2880        ty: subst_named_params(ty, &subst),
2881    }
2882}
2883
2884fn vec_of(elem: Ty) -> Ty {
2885    Ty::Named {
2886        name: "vec".into(),
2887        args: vec![elem],
2888    }
2889}
2890
2891fn vec_elem_uninferred(ty: &Ty) -> bool {
2892    matches!(
2893        ty,
2894        Ty::Named { name, args }
2895            if name == "vec"
2896                && (args.is_empty() || matches!(args.first(), Some(Ty::Var(_))))
2897    )
2898}
2899
2900fn float_unop() -> Ty {
2901    Ty::Fn {
2902        params: vec![Ty::Float],
2903        ret: Box::new(Ty::Float),
2904    }
2905}
2906
2907fn stdlib_fn_types() -> Vec<(&'static str, Ty)> {
2908    vec![
2909        (
2910            "read_to_string",
2911            Ty::Fn {
2912                params: vec![Ty::StrSlice],
2913                ret: Box::new(Ty::Str),
2914            },
2915        ),
2916        (
2917            "sleep_ms",
2918            Ty::Fn {
2919                params: vec![Ty::Int],
2920                ret: Box::new(Ty::Unit),
2921            },
2922        ),
2923        (
2924            "parse_ip",
2925            Ty::Fn {
2926                params: vec![Ty::Str],
2927                ret: Box::new(Ty::Str),
2928            },
2929        ),
2930        ("exp", float_unop()),
2931        ("sin", float_unop()),
2932        ("cos", float_unop()),
2933        ("tanh", float_unop()),
2934        ("sqrt", float_unop()),
2935    ]
2936}
2937
2938/// Known Rust crate item stubs for typeck (Result Ok payload types).
2939fn rust_import_fn_type(crate_name: &str, item: &str) -> Option<(Vec<Ty>, Ty)> {
2940    let json_value = Ty::Named {
2941        name: "serde_json::Value".into(),
2942        args: vec![],
2943    };
2944    match (crate_name, item) {
2945        ("serde_json", "from_str") => Some((vec![Ty::Str], json_value)),
2946        ("serde_json", "to_string" | "to_string_pretty" | "to_vec") => {
2947            Some((vec![json_value], Ty::Str))
2948        }
2949        ("serde_json", "from_value") => Some((vec![json_value.clone()], json_value)),
2950        // Type-like imports (e.g. `Value as JsonValue`) — not callable; placeholder unit fn.
2951        ("serde_json", "Value") => Some((vec![], json_value)),
2952        ("ureq", "get") => Some((vec![Ty::Str], Ty::Str)),
2953        _ => None,
2954    }
2955}
2956
2957fn rust_extern_scalar_ok(ty: &Ty) -> bool {
2958    matches!(
2959        ty,
2960        Ty::Float | Ty::Int | Ty::UInt | Ty::Bool | Ty::Str | Ty::StrSlice | Ty::Unit
2961    )
2962}
2963
2964/// Whether a known `rust = true` import returns Rust `Result` and should lower via Crisp `?`
2965/// / ambient errors (spec §14.2) instead of panic `.expect`.
2966pub fn rust_import_returns_result(crate_name: &str, item: &str) -> bool {
2967    matches!(
2968        (crate_name, item),
2969        (
2970            "serde_json",
2971            "from_str" | "to_string" | "to_string_pretty" | "to_vec" | "from_value"
2972        ) | ("ureq", "get")
2973    )
2974}