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