Skip to main content

sigil_parser/
typeck.rs

1//! Type checker for Sigil.
2//!
3//! Implements bidirectional type inference with evidentiality tracking.
4//! The type system enforces that evidence levels propagate correctly
5//! through computations.
6
7use crate::ast::*;
8use crate::span::Span;
9use std::cell::RefCell;
10use std::collections::HashMap;
11use std::fmt;
12use std::rc::Rc;
13
14/// Internal type representation.
15#[derive(Debug, Clone, PartialEq)]
16pub enum Type {
17    /// Primitive types
18    Unit,
19    Bool,
20    Int(IntSize),
21    Float(FloatSize),
22    Char,
23    Str,
24
25    /// Compound types
26    Array {
27        element: Box<Type>,
28        size: Option<usize>,
29    },
30    Slice(Box<Type>),
31    Tuple(Vec<Type>),
32
33    /// Named type (struct, enum, type alias)
34    Named {
35        name: String,
36        generics: Vec<Type>,
37    },
38
39    /// Function type
40    Function {
41        params: Vec<Type>,
42        return_type: Box<Type>,
43        is_async: bool,
44    },
45
46    /// Reference types
47    Ref {
48        lifetime: Option<String>,
49        mutable: bool,
50        inner: Box<Type>,
51    },
52    Ptr {
53        mutable: bool,
54        inner: Box<Type>,
55    },
56
57    /// Evidential wrapper - the core of Sigil's type system
58    Evidential {
59        inner: Box<Type>,
60        evidence: EvidenceLevel,
61    },
62
63    /// Cyclic type (modular arithmetic)
64    Cycle {
65        modulus: usize,
66    },
67
68    /// SIMD vector type
69    Simd {
70        element: Box<Type>,
71        lanes: u8,
72    },
73
74    /// Atomic type
75    Atomic(Box<Type>),
76
77    /// Type variable for inference
78    Var(TypeVar),
79
80    /// Error type (for error recovery)
81    Error,
82
83    /// Never type (diverging)
84    Never,
85
86    /// Lifetime bound ('static, 'a)
87    Lifetime(String),
88
89    /// Trait object: dyn Trait
90    TraitObject(Vec<Type>),
91
92    /// Higher-ranked trait bound: for<'a> Trait<'a>
93    Hrtb {
94        lifetimes: Vec<String>,
95        bound: Box<Type>,
96    },
97    /// Inline struct type: struct { field: Type, ... }
98    InlineStruct {
99        fields: Vec<(String, Type)>,
100    },
101    /// Impl trait: impl Trait bounds
102    ImplTrait(Vec<Type>),
103    /// Inline enum type: enum { Variant1, Variant2, ... }
104    InlineEnum(Vec<String>),
105    /// Associated type binding: Output = Type
106    AssocTypeBinding { name: String, ty: Box<Type> },
107
108    /// Linear type wrapper - value must be used exactly once (no-cloning theorem)
109    Linear(Box<Type>),
110
111    /// Affine type wrapper - value can be used at most once (can be dropped)
112    Affine(Box<Type>),
113
114    /// Relevant type wrapper - value must be used at least once (can be cloned)
115    Relevant(Box<Type>),
116}
117
118/// Integer sizes
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub enum IntSize {
121    I8,
122    I16,
123    I32,
124    I64,
125    I128,
126    U8,
127    U16,
128    U32,
129    U64,
130    U128,
131    ISize,
132    USize,
133}
134
135/// Float sizes
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137pub enum FloatSize {
138    F32,
139    F64,
140}
141
142/// Evidence levels in the type system.
143///
144/// Evidence forms a lattice:
145///   Known (!) < Uncertain (?) < Reported (~) < Paradox (‽)
146///
147/// Operations combine evidence levels using join (⊔):
148///   a + b : join(evidence(a), evidence(b))
149#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
150pub enum EvidenceLevel {
151    /// Direct knowledge - computed locally, verified
152    Known, // !
153    /// Uncertain - inferred, possible, not verified
154    Uncertain, // ?
155    /// Reported - from external source, hearsay
156    Reported, // ~
157    /// Paradox - contradictory information
158    Paradox, // ‽
159}
160
161impl EvidenceLevel {
162    /// Join two evidence levels (least upper bound in lattice)
163    pub fn join(self, other: Self) -> Self {
164        std::cmp::max(self, other)
165    }
166
167    /// Meet two evidence levels (greatest lower bound)
168    pub fn meet(self, other: Self) -> Self {
169        std::cmp::min(self, other)
170    }
171
172    /// Convert from AST representation
173    pub fn from_ast(e: Evidentiality) -> Self {
174        match e {
175            Evidentiality::Known => EvidenceLevel::Known,
176            Evidentiality::Uncertain | Evidentiality::Predicted => EvidenceLevel::Uncertain,
177            Evidentiality::Reported => EvidenceLevel::Reported,
178            Evidentiality::Paradox => EvidenceLevel::Paradox,
179        }
180    }
181
182    /// Symbol representation
183    pub fn symbol(&self) -> &'static str {
184        match self {
185            EvidenceLevel::Known => "!",
186            EvidenceLevel::Uncertain => "?",
187            EvidenceLevel::Reported => "~",
188            EvidenceLevel::Paradox => "‽",
189        }
190    }
191
192    /// Human-readable name
193    pub fn name(&self) -> &'static str {
194        match self {
195            EvidenceLevel::Known => "known",
196            EvidenceLevel::Uncertain => "uncertain",
197            EvidenceLevel::Reported => "reported",
198            EvidenceLevel::Paradox => "paradox",
199        }
200    }
201
202    /// Check if this evidence level can satisfy a required level.
203    ///
204    /// Evidence is covariant: you can pass more certain data where less certain is expected.
205    /// Known (!) can satisfy any requirement.
206    /// Reported (~) can only satisfy Reported or Paradox requirements.
207    ///
208    /// Returns true if `self` (actual) can be used where `required` is expected.
209    pub fn satisfies(self, required: Self) -> bool {
210        // More certain evidence (lower in lattice) can satisfy less certain requirements
211        // Known <= Uncertain <= Reported <= Paradox
212        self <= required
213    }
214}
215
216/// Type variable for inference
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
218pub struct TypeVar(pub u32);
219
220/// Type error categories for better diagnostics
221#[derive(Debug, Clone)]
222pub enum TypeErrorKind {
223    /// Type mismatch: expected one type, got another
224    Mismatch {
225        expected: String,
226        actual: String,
227        context: Option<String>,
228    },
229    /// Undefined type, trait, or associated item
230    Undefined {
231        name: String,
232        kind: &'static str, // "type", "trait", "method", "field"
233    },
234    /// Missing trait implementation
235    MissingImpl {
236        type_name: String,
237        trait_name: String,
238    },
239    /// Invalid operation on type
240    InvalidOperation {
241        operation: String,
242        type_name: String,
243    },
244    /// Generic error with message
245    Generic {
246        message: String,
247    },
248}
249
250/// Type error with rich context
251#[derive(Debug, Clone)]
252pub struct TypeError {
253    pub kind: TypeErrorKind,
254    pub message: String,
255    pub span: Option<Span>,
256    pub notes: Vec<String>,
257    pub help: Option<String>,
258}
259
260impl TypeError {
261    pub fn new(message: impl Into<String>) -> Self {
262        let msg = message.into();
263        Self {
264            kind: TypeErrorKind::Generic { message: msg.clone() },
265            message: msg,
266            span: None,
267            notes: Vec::new(),
268            help: None,
269        }
270    }
271
272    /// Create a type mismatch error
273    pub fn mismatch(expected: impl Into<String>, actual: impl Into<String>) -> Self {
274        let exp = expected.into();
275        let act = actual.into();
276        let msg = format!("expected `{}`, found `{}`", exp, act);
277        Self {
278            kind: TypeErrorKind::Mismatch {
279                expected: exp,
280                actual: act,
281                context: None,
282            },
283            message: msg,
284            span: None,
285            notes: Vec::new(),
286            help: None,
287        }
288    }
289
290    /// Create an undefined type/trait error
291    pub fn undefined(name: impl Into<String>, kind: &'static str) -> Self {
292        let n = name.into();
293        let msg = format!("cannot find {} `{}` in this scope", kind, n);
294        Self {
295            kind: TypeErrorKind::Undefined { name: n, kind },
296            message: msg,
297            span: None,
298            notes: Vec::new(),
299            help: None,
300        }
301    }
302
303    /// Create a missing trait implementation error
304    pub fn missing_impl(type_name: impl Into<String>, trait_name: impl Into<String>) -> Self {
305        let t = type_name.into();
306        let tr = trait_name.into();
307        let msg = format!("the trait `{}` is not implemented for `{}`", tr, t);
308        Self {
309            kind: TypeErrorKind::MissingImpl {
310                type_name: t,
311                trait_name: tr,
312            },
313            message: msg,
314            span: None,
315            notes: Vec::new(),
316            help: None,
317        }
318    }
319
320    /// Create an invalid operation error
321    pub fn invalid_op(operation: impl Into<String>, type_name: impl Into<String>) -> Self {
322        let op = operation.into();
323        let ty = type_name.into();
324        let msg = format!("cannot {} type `{}`", op, ty);
325        Self {
326            kind: TypeErrorKind::InvalidOperation {
327                operation: op,
328                type_name: ty,
329            },
330            message: msg,
331            span: None,
332            notes: Vec::new(),
333            help: None,
334        }
335    }
336
337    pub fn with_span(mut self, span: Span) -> Self {
338        self.span = Some(span);
339        self
340    }
341
342    pub fn with_note(mut self, note: impl Into<String>) -> Self {
343        self.notes.push(note.into());
344        self
345    }
346
347    pub fn with_help(mut self, help: impl Into<String>) -> Self {
348        self.help = Some(help.into());
349        self
350    }
351
352    /// Get error code for documentation
353    pub fn code(&self) -> &'static str {
354        match &self.kind {
355            TypeErrorKind::Mismatch { .. } => "T001",
356            TypeErrorKind::Undefined { .. } => "T002",
357            TypeErrorKind::MissingImpl { .. } => "T003",
358            TypeErrorKind::InvalidOperation { .. } => "T004",
359            TypeErrorKind::Generic { .. } => "T000",
360        }
361    }
362}
363
364impl fmt::Display for TypeError {
365    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366        write!(f, "error[{}]: {}", self.code(), self.message)?;
367        if let Some(span) = self.span {
368            write!(f, "\n  --> at {}", span)?;
369        }
370        for note in &self.notes {
371            write!(f, "\n  note: {}", note)?;
372        }
373        if let Some(help) = &self.help {
374            write!(f, "\n  help: {}", help)?;
375        }
376        Ok(())
377    }
378}
379
380/// Type environment for scoped lookups
381#[derive(Debug, Clone)]
382pub struct TypeEnv {
383    /// Variable bindings: name -> (type, evidence)
384    bindings: HashMap<String, (Type, EvidenceLevel)>,
385    /// Parent scope
386    parent: Option<Rc<RefCell<TypeEnv>>>,
387    /// Set of linear variables that have been consumed (used)
388    consumed_linear: std::collections::HashSet<String>,
389}
390
391impl TypeEnv {
392    pub fn new() -> Self {
393        Self {
394            bindings: HashMap::new(),
395            parent: None,
396            consumed_linear: std::collections::HashSet::new(),
397        }
398    }
399
400    pub fn with_parent(parent: Rc<RefCell<TypeEnv>>) -> Self {
401        Self {
402            bindings: HashMap::new(),
403            parent: Some(parent),
404            consumed_linear: std::collections::HashSet::new(),
405        }
406    }
407
408    /// Define a new binding
409    pub fn define(&mut self, name: String, ty: Type, evidence: EvidenceLevel) {
410        self.bindings.insert(name, (ty, evidence));
411    }
412
413    /// Look up a binding (without consuming)
414    pub fn lookup(&self, name: &str) -> Option<(Type, EvidenceLevel)> {
415        if let Some(binding) = self.bindings.get(name) {
416            Some(binding.clone())
417        } else if let Some(ref parent) = self.parent {
418            parent.borrow().lookup(name)
419        } else {
420            None
421        }
422    }
423
424    /// Check if a variable has a linear type
425    pub fn is_linear(&self, name: &str) -> bool {
426        if let Some((ty, _)) = self.lookup(name) {
427            matches!(ty, Type::Linear(_))
428        } else {
429            false
430        }
431    }
432
433    /// Check if a linear variable has already been consumed
434    pub fn is_consumed(&self, name: &str) -> bool {
435        if self.consumed_linear.contains(name) {
436            return true;
437        }
438        if let Some(ref parent) = self.parent {
439            return parent.borrow().is_consumed(name);
440        }
441        false
442    }
443
444    /// Mark a linear variable as consumed
445    pub fn consume(&mut self, name: &str) {
446        self.consumed_linear.insert(name.to_string());
447    }
448
449    /// Get all unconsumed linear variables in this scope (for checking at scope exit)
450    pub fn get_unconsumed_linear_vars(&self) -> Vec<String> {
451        self.bindings
452            .iter()
453            .filter(|(name, (ty, _))| {
454                matches!(ty, Type::Linear(_)) && !self.consumed_linear.contains(*name)
455            })
456            .map(|(name, _)| name.clone())
457            .collect()
458    }
459}
460
461impl Default for TypeEnv {
462    fn default() -> Self {
463        Self::new()
464    }
465}
466
467/// Type definitions (structs, enums, type aliases)
468#[derive(Debug, Clone)]
469pub enum TypeDef {
470    Struct {
471        generics: Vec<String>,
472        fields: Vec<(String, Type)>,
473    },
474    Enum {
475        generics: Vec<String>,
476        variants: Vec<(String, Option<Vec<Type>>)>,
477    },
478    Alias {
479        generics: Vec<String>,
480        target: Type,
481    },
482}
483
484/// The type checker
485pub struct TypeChecker {
486    /// Type environment stack
487    env: Rc<RefCell<TypeEnv>>,
488    /// Type definitions
489    types: HashMap<String, TypeDef>,
490    /// Function signatures
491    functions: HashMap<String, Type>,
492    /// Stdlib function names (can be shadowed by user code)
493    stdlib_functions: std::collections::HashSet<String>,
494    /// Associated functions/methods per type: type_name -> (method_name -> method_type)
495    impl_methods: HashMap<String, HashMap<String, Type>>,
496    /// Current Self type when inside an impl block (includes generic type variables)
497    current_self_type: Option<Type>,
498    /// Current generic type parameters (name -> type variable)
499    current_generics: HashMap<String, Type>,
500    /// Expected return type for the current function (for checking return statements)
501    expected_return_type: Option<Type>,
502    /// Type variable counter
503    next_var: u32,
504    /// Inferred type variable substitutions
505    substitutions: HashMap<TypeVar, Type>,
506    /// Collected errors
507    errors: Vec<TypeError>,
508    /// Span of the current top-level item being checked (for error fallback)
509    current_item_span: Span,
510}
511
512impl TypeChecker {
513    pub fn new() -> Self {
514        let mut checker = Self {
515            env: Rc::new(RefCell::new(TypeEnv::new())),
516            types: HashMap::new(),
517            functions: HashMap::new(),
518            stdlib_functions: std::collections::HashSet::new(),
519            impl_methods: HashMap::new(),
520            current_self_type: None,
521            current_generics: HashMap::new(),
522            expected_return_type: None,
523            next_var: 0,
524            substitutions: HashMap::new(),
525            errors: Vec::new(),
526            current_item_span: Span::default(),
527        };
528
529        // Register built-in types and functions
530        checker.register_builtins();
531        checker
532    }
533
534    /// Add a stdlib function (these can be shadowed by user code)
535    fn add_stdlib_fn(&mut self, name: &str, fn_type: Type) {
536        self.functions.insert(name.to_string(), fn_type);
537        self.stdlib_functions.insert(name.to_string());
538    }
539
540    fn register_builtins(&mut self) {
541        // Helper to create a function type
542        let func = |params: Vec<Type>, ret: Type| Type::Function {
543            params,
544            return_type: Box::new(ret),
545            is_async: false,
546        };
547
548        // Type variable for generic functions
549        let any = Type::Var(TypeVar(9999)); // Use high number to avoid conflicts
550
551        // ===================
552        // Core I/O
553        // ===================
554        // print accepts any type (polymorphic)
555        self.functions
556            .insert("print".to_string(), func(vec![any.clone()], Type::Unit));
557        self.functions
558            .insert("println".to_string(), func(vec![any.clone()], Type::Unit));
559        self.functions
560            .insert("input".to_string(), func(vec![], Type::Str));
561        self.functions
562            .insert("input_line".to_string(), func(vec![], Type::Str));
563
564        // ===================
565        // Type inspection
566        // ===================
567        self.functions
568            .insert("type_of".to_string(), func(vec![any.clone()], Type::Str));
569        self.functions.insert(
570            "len".to_string(),
571            func(vec![any.clone()], Type::Int(IntSize::USize)),
572        );
573
574        // ===================
575        // String functions
576        // ===================
577        self.functions
578            .insert("str".to_string(), func(vec![any.clone()], Type::Str));
579        self.functions
580            .insert("upper".to_string(), func(vec![Type::Str], Type::Str));
581        self.functions
582            .insert("lower".to_string(), func(vec![Type::Str], Type::Str));
583        self.functions
584            .insert("trim".to_string(), func(vec![Type::Str], Type::Str));
585        self.functions.insert(
586            "split".to_string(),
587            func(
588                vec![Type::Str, Type::Str],
589                Type::Array {
590                    element: Box::new(Type::Str),
591                    size: None,
592                },
593            ),
594        );
595        self.functions.insert(
596            "join".to_string(),
597            func(
598                vec![
599                    Type::Array {
600                        element: Box::new(Type::Str),
601                        size: None,
602                    },
603                    Type::Str,
604                ],
605                Type::Str,
606            ),
607        );
608        self.functions.insert(
609            "contains".to_string(),
610            func(vec![Type::Str, Type::Str], Type::Bool),
611        );
612        self.functions.insert(
613            "starts_with".to_string(),
614            func(vec![Type::Str, Type::Str], Type::Bool),
615        );
616        self.functions.insert(
617            "ends_with".to_string(),
618            func(vec![Type::Str, Type::Str], Type::Bool),
619        );
620        self.functions.insert(
621            "replace".to_string(),
622            func(vec![Type::Str, Type::Str, Type::Str], Type::Str),
623        );
624        self.functions.insert(
625            "char_at".to_string(),
626            func(vec![Type::Str, Type::Int(IntSize::I64)], Type::Str),
627        );
628        self.functions.insert(
629            "substring".to_string(),
630            func(
631                vec![Type::Str, Type::Int(IntSize::I64), Type::Int(IntSize::I64)],
632                Type::Str,
633            ),
634        );
635
636        // ===================
637        // Math functions
638        // ===================
639        let f64_ty = Type::Float(FloatSize::F64);
640        let i64_ty = Type::Int(IntSize::I64);
641
642        self.functions.insert(
643            "abs".to_string(),
644            func(vec![f64_ty.clone()], f64_ty.clone()),
645        );
646        self.functions.insert(
647            "sqrt".to_string(),
648            func(vec![f64_ty.clone()], f64_ty.clone()),
649        );
650        self.functions.insert(
651            "sin".to_string(),
652            func(vec![f64_ty.clone()], f64_ty.clone()),
653        );
654        self.functions.insert(
655            "cos".to_string(),
656            func(vec![f64_ty.clone()], f64_ty.clone()),
657        );
658        self.functions.insert(
659            "tan".to_string(),
660            func(vec![f64_ty.clone()], f64_ty.clone()),
661        );
662        self.functions.insert(
663            "floor".to_string(),
664            func(vec![f64_ty.clone()], f64_ty.clone()),
665        );
666        self.functions.insert(
667            "ceil".to_string(),
668            func(vec![f64_ty.clone()], f64_ty.clone()),
669        );
670        self.functions.insert(
671            "round".to_string(),
672            func(vec![f64_ty.clone()], f64_ty.clone()),
673        );
674        self.functions.insert(
675            "pow".to_string(),
676            func(vec![f64_ty.clone(), f64_ty.clone()], f64_ty.clone()),
677        );
678        self.functions.insert(
679            "log".to_string(),
680            func(vec![f64_ty.clone(), f64_ty.clone()], f64_ty.clone()),  // log(value, base)
681        );
682        self.functions.insert(
683            "log10".to_string(),
684            func(vec![f64_ty.clone()], f64_ty.clone()),
685        );
686        self.functions.insert(
687            "log2".to_string(),
688            func(vec![f64_ty.clone()], f64_ty.clone()),
689        );
690        self.functions.insert(
691            "ln".to_string(),
692            func(vec![f64_ty.clone()], f64_ty.clone()),
693        );
694        self.functions.insert(
695            "exp".to_string(),
696            func(vec![f64_ty.clone()], f64_ty.clone()),
697        );
698        self.functions.insert(
699            "min".to_string(),
700            func(vec![any.clone(), any.clone()], any.clone()),
701        );
702        self.functions.insert(
703            "max".to_string(),
704            func(vec![any.clone(), any.clone()], any.clone()),
705        );
706
707        // ===================
708        // Array/Collection functions
709        // ===================
710        self.functions
711            .insert("sum".to_string(), func(vec![any.clone()], f64_ty.clone()));
712        self.functions
713            .insert("avg".to_string(), func(vec![any.clone()], f64_ty.clone()));
714        self.functions.insert(
715            "push".to_string(),
716            func(vec![any.clone(), any.clone()], Type::Unit),
717        );
718        self.functions
719            .insert("pop".to_string(), func(vec![any.clone()], any.clone()));
720        self.functions
721            .insert("first".to_string(), func(vec![any.clone()], any.clone()));
722        self.functions
723            .insert("last".to_string(), func(vec![any.clone()], any.clone()));
724        self.functions
725            .insert("reverse".to_string(), func(vec![any.clone()], any.clone()));
726        self.functions
727            .insert("sort".to_string(), func(vec![any.clone()], any.clone()));
728        self.functions.insert(
729            "range".to_string(),
730            func(
731                vec![i64_ty.clone(), i64_ty.clone()],
732                Type::Array {
733                    element: Box::new(i64_ty.clone()),
734                    size: None,
735                },
736            ),
737        );
738
739        // ===================
740        // Assertions (for testing)
741        // ===================
742        self.functions
743            .insert("assert".to_string(), func(vec![Type::Bool], Type::Unit));
744        self.functions.insert(
745            "assert_eq".to_string(),
746            func(vec![any.clone(), any.clone()], Type::Unit),
747        );
748        self.functions.insert(
749            "assert_ne".to_string(),
750            func(vec![any.clone(), any.clone()], Type::Unit),
751        );
752        self.functions.insert(
753            "assert_lt".to_string(),
754            func(vec![any.clone(), any.clone()], Type::Unit),
755        );
756        self.functions.insert(
757            "assert_le".to_string(),
758            func(vec![any.clone(), any.clone()], Type::Unit),
759        );
760        self.functions.insert(
761            "assert_gt".to_string(),
762            func(vec![any.clone(), any.clone()], Type::Unit),
763        );
764        self.functions.insert(
765            "assert_ge".to_string(),
766            func(vec![any.clone(), any.clone()], Type::Unit),
767        );
768        self.functions.insert(
769            "assert_true".to_string(),
770            func(vec![Type::Bool], Type::Unit),
771        );
772        self.functions.insert(
773            "assert_false".to_string(),
774            func(vec![Type::Bool], Type::Unit),
775        );
776        self.functions.insert(
777            "assert_null".to_string(),
778            func(vec![any.clone()], Type::Unit),
779        );
780        self.functions.insert(
781            "assert_not_null".to_string(),
782            func(vec![any.clone()], Type::Unit),
783        );
784        self.functions.insert(
785            "assert_contains".to_string(),
786            func(vec![any.clone(), any.clone()], Type::Unit),
787        );
788        self.functions.insert(
789            "assert_len".to_string(),
790            func(vec![any.clone(), i64_ty.clone()], Type::Unit),
791        );
792
793        // ===================
794        // Random
795        // ===================
796        self.functions
797            .insert("random".to_string(), func(vec![], f64_ty.clone()));
798        self.functions.insert(
799            "random_int".to_string(),
800            func(vec![i64_ty.clone(), i64_ty.clone()], i64_ty.clone()),
801        );
802        self.functions
803            .insert("shuffle".to_string(), func(vec![any.clone()], any.clone()));
804
805        // ===================
806        // Time
807        // ===================
808        self.functions
809            .insert("now".to_string(), func(vec![], f64_ty.clone()));
810        self.functions
811            .insert("sleep".to_string(), func(vec![f64_ty.clone()], Type::Unit));
812
813        // ===================
814        // Conversion
815        // ===================
816        self.functions
817            .insert("int".to_string(), func(vec![any.clone()], i64_ty.clone()));
818        self.functions
819            .insert("float".to_string(), func(vec![any.clone()], f64_ty.clone()));
820        self.functions
821            .insert("bool".to_string(), func(vec![any.clone()], Type::Bool));
822
823        // ===================
824        // Error handling
825        // ===================
826        self.functions
827            .insert("panic".to_string(), func(vec![Type::Str], Type::Never));
828        self.functions
829            .insert("todo".to_string(), func(vec![], Type::Never));
830        self.functions
831            .insert("unreachable".to_string(), func(vec![], Type::Never));
832
833        // ===================
834        // Evidentiality functions
835        // ===================
836        // Create known evidence (!)
837        self.functions.insert(
838            "known".to_string(),
839            func(
840                vec![any.clone()],
841                Type::Evidential {
842                    inner: Box::new(any.clone()),
843                    evidence: EvidenceLevel::Known,
844                },
845            ),
846        );
847        // Create uncertain evidence (?)
848        self.functions.insert(
849            "uncertain".to_string(),
850            func(
851                vec![any.clone()],
852                Type::Evidential {
853                    inner: Box::new(any.clone()),
854                    evidence: EvidenceLevel::Uncertain,
855                },
856            ),
857        );
858        // Create reported evidence (~)
859        self.functions.insert(
860            "reported".to_string(),
861            func(
862                vec![any.clone()],
863                Type::Evidential {
864                    inner: Box::new(any.clone()),
865                    evidence: EvidenceLevel::Reported,
866                },
867            ),
868        );
869        // Get evidence level as string
870        self.functions.insert(
871            "evidence_of".to_string(),
872            func(vec![any.clone()], Type::Str),
873        );
874        // Validate reported -> uncertain
875        self.functions.insert(
876            "validate".to_string(),
877            func(
878                vec![any.clone()],
879                Type::Evidential {
880                    inner: Box::new(any.clone()),
881                    evidence: EvidenceLevel::Uncertain,
882                },
883            ),
884        );
885        // Verify uncertain -> known
886        self.functions.insert(
887            "verify".to_string(),
888            func(
889                vec![any.clone()],
890                Type::Evidential {
891                    inner: Box::new(any.clone()),
892                    evidence: EvidenceLevel::Known,
893                },
894            ),
895        );
896
897        // ===================
898        // Poly-cultural math (cycles, music theory)
899        // ===================
900        // MIDI note to frequency (A4 = 440Hz)
901        self.functions.insert(
902            "freq".to_string(),
903            func(vec![i64_ty.clone()], f64_ty.clone()),
904        );
905        // Octave calculation (12-tone equal temperament)
906        self.functions.insert(
907            "octave".to_string(),
908            func(vec![i64_ty.clone()], i64_ty.clone()),
909        );
910        // Note within octave (0-11)
911        self.functions.insert(
912            "pitch_class".to_string(),
913            func(vec![i64_ty.clone()], i64_ty.clone()),
914        );
915        // Modular arithmetic (cycles)
916        self.functions.insert(
917            "mod_cycle".to_string(),
918            func(vec![i64_ty.clone(), i64_ty.clone()], i64_ty.clone()),
919        );
920
921        // Mark all registered functions as stdlib (can be shadowed by user code)
922        self.stdlib_functions = self.functions.keys().cloned().collect();
923    }
924
925    /// Fresh type variable
926    fn fresh_var(&mut self) -> Type {
927        let var = TypeVar(self.next_var);
928        self.next_var += 1;
929        Type::Var(var)
930    }
931
932    /// Check if a type contains unresolved type variables
933    fn type_contains_var(&self, ty: &Type) -> bool {
934        match ty {
935            Type::Var(v) => !self.substitutions.contains_key(v),
936            Type::Array { element, .. } => self.type_contains_var(element.as_ref()),
937            Type::Slice(inner) => self.type_contains_var(inner.as_ref()),
938            Type::Tuple(elems) => elems.iter().any(|e| self.type_contains_var(e)),
939            Type::Ref { inner, .. } | Type::Ptr { inner, .. } => self.type_contains_var(inner.as_ref()),
940            Type::Function { params, return_type, .. } => {
941                params.iter().any(|p| self.type_contains_var(p)) || self.type_contains_var(return_type.as_ref())
942            }
943            Type::Named { generics, .. } => generics.iter().any(|g| self.type_contains_var(g)),
944            Type::ImplTrait(bounds) => bounds.iter().any(|b| self.type_contains_var(b)),
945            Type::Evidential { inner, .. } => self.type_contains_var(inner.as_ref()),
946            Type::Atomic(inner) => self.type_contains_var(inner.as_ref()),
947            Type::Simd { element, .. } => self.type_contains_var(element.as_ref()),
948            _ => false,
949        }
950    }
951
952    /// Occurs check: does type variable v occur in type t?
953    /// Used to prevent creating cyclic/infinite types
954    fn occurs_in(&self, v: &TypeVar, t: &Type) -> bool {
955        match t {
956            Type::Var(w) => {
957                if v == w {
958                    return true;
959                }
960                if let Some(resolved) = self.substitutions.get(w) {
961                    self.occurs_in(v, resolved)
962                } else {
963                    false
964                }
965            }
966            Type::Array { element, .. } => self.occurs_in(v, element),
967            Type::Slice(inner) => self.occurs_in(v, inner),
968            Type::Tuple(elems) => elems.iter().any(|e| self.occurs_in(v, e)),
969            Type::Ref { inner, .. } | Type::Ptr { inner, .. } => self.occurs_in(v, inner),
970            Type::Function { params, return_type, .. } => {
971                params.iter().any(|p| self.occurs_in(v, p)) || self.occurs_in(v, return_type)
972            }
973            Type::Named { generics, .. } => generics.iter().any(|g| self.occurs_in(v, g)),
974            Type::ImplTrait(bounds) => bounds.iter().any(|b| self.occurs_in(v, b)),
975            Type::Evidential { inner, .. } => self.occurs_in(v, inner),
976            Type::Atomic(inner) => self.occurs_in(v, inner),
977            Type::Simd { element, .. } => self.occurs_in(v, element),
978            _ => false,
979        }
980    }
981
982    /// Freshen a type by replacing all type variables with fresh ones
983    /// This is used for polymorphic built-in functions
984    fn freshen(&mut self, ty: &Type) -> Type {
985        let mut mapping = std::collections::HashMap::new();
986        self.freshen_inner(ty, &mut mapping)
987    }
988
989    fn freshen_inner(
990        &mut self,
991        ty: &Type,
992        mapping: &mut std::collections::HashMap<u32, Type>,
993    ) -> Type {
994        match ty {
995            Type::Var(TypeVar(id)) => {
996                if let Some(fresh) = mapping.get(id) {
997                    fresh.clone()
998                } else {
999                    let fresh = self.fresh_var();
1000                    mapping.insert(*id, fresh.clone());
1001                    fresh
1002                }
1003            }
1004            Type::Array { element, size } => Type::Array {
1005                element: Box::new(self.freshen_inner(element, mapping)),
1006                size: *size,
1007            },
1008            Type::Slice(inner) => Type::Slice(Box::new(self.freshen_inner(inner, mapping))),
1009            Type::Ref { lifetime, mutable, inner } => Type::Ref {
1010                lifetime: lifetime.clone(),
1011                mutable: *mutable,
1012                inner: Box::new(self.freshen_inner(inner, mapping)),
1013            },
1014            Type::Tuple(elems) => Type::Tuple(
1015                elems
1016                    .iter()
1017                    .map(|e| self.freshen_inner(e, mapping))
1018                    .collect(),
1019            ),
1020            Type::Function {
1021                params,
1022                return_type,
1023                is_async,
1024            } => Type::Function {
1025                params: params
1026                    .iter()
1027                    .map(|p| self.freshen_inner(p, mapping))
1028                    .collect(),
1029                return_type: Box::new(self.freshen_inner(return_type, mapping)),
1030                is_async: *is_async,
1031            },
1032            Type::Evidential { inner, evidence } => Type::Evidential {
1033                inner: Box::new(self.freshen_inner(inner, mapping)),
1034                evidence: *evidence,
1035            },
1036            Type::Named { name, generics } => Type::Named {
1037                name: name.clone(),
1038                generics: generics
1039                    .iter()
1040                    .map(|g| self.freshen_inner(g, mapping))
1041                    .collect(),
1042            },
1043            // Primitive types don't contain type variables
1044            _ => ty.clone(),
1045        }
1046    }
1047
1048    /// Push a new scope
1049    fn push_scope(&mut self) {
1050        let new_env = TypeEnv::with_parent(self.env.clone());
1051        self.env = Rc::new(RefCell::new(new_env));
1052    }
1053
1054    /// Pop current scope
1055    fn pop_scope(&mut self) {
1056        let parent = self.env.borrow().parent.clone();
1057        if let Some(p) = parent {
1058            self.env = p;
1059        }
1060    }
1061
1062    /// Record an error, auto-attaching current item span if no span is set
1063    fn error(&mut self, mut err: TypeError) {
1064        if err.span.is_none() && !self.current_item_span.is_empty() {
1065            err.span = Some(self.current_item_span);
1066        }
1067        self.errors.push(err);
1068    }
1069
1070    /// Check if actual evidence can satisfy expected evidence requirement.
1071    /// Returns Ok(()) if compatible, Err with helpful message if not.
1072    fn check_evidence(
1073        &mut self,
1074        expected: EvidenceLevel,
1075        actual: EvidenceLevel,
1076        context: &str,
1077    ) -> bool {
1078        if actual.satisfies(expected) {
1079            true
1080        } else {
1081            let mut err = TypeError::new(format!(
1082                "evidence mismatch {}: expected {} ({}), found {} ({})",
1083                context,
1084                expected.name(),
1085                expected.symbol(),
1086                actual.name(),
1087                actual.symbol(),
1088            ));
1089
1090            // Add helpful notes based on the specific mismatch
1091            match (expected, actual) {
1092                (EvidenceLevel::Known, EvidenceLevel::Reported) => {
1093                    err = err.with_note(
1094                        "reported data (~) cannot be used where known data (!) is required",
1095                    );
1096                    err = err.with_note(
1097                        "help: use |validate!{...} to verify and promote evidence level",
1098                    );
1099                }
1100                (EvidenceLevel::Known, EvidenceLevel::Uncertain) => {
1101                    err = err.with_note(
1102                        "uncertain data (?) cannot be used where known data (!) is required",
1103                    );
1104                    err = err.with_note(
1105                        "help: use pattern matching or unwrap to handle the uncertainty",
1106                    );
1107                }
1108                (EvidenceLevel::Uncertain, EvidenceLevel::Reported) => {
1109                    err = err.with_note(
1110                        "reported data (~) cannot be used where uncertain data (?) is required",
1111                    );
1112                    err = err.with_note("help: use |validate?{...} to verify external data");
1113                }
1114                _ => {
1115                    err = err.with_note(format!(
1116                        "evidence lattice: known (!) < uncertain (?) < reported (~) < paradox (‽)"
1117                    ));
1118                }
1119            }
1120
1121            self.error(err);
1122            false
1123        }
1124    }
1125
1126    /// Extract evidence level from a type, defaulting to Known
1127    fn get_evidence(&self, ty: &Type) -> EvidenceLevel {
1128        match ty {
1129            Type::Evidential { evidence, .. } => *evidence,
1130            _ => EvidenceLevel::Known,
1131        }
1132    }
1133
1134    /// Check a source file
1135    pub fn check_file(&mut self, file: &SourceFile) -> Result<(), Vec<TypeError>> {
1136        // First pass: collect type definitions
1137        for item in &file.items {
1138            self.collect_type_def(&item.node);
1139        }
1140
1141        // Second pass: collect function signatures
1142        for item in &file.items {
1143            self.collect_fn_sig(&item.node);
1144        }
1145
1146        // Third pass: check function bodies
1147        for item in &file.items {
1148            self.current_item_span = item.span;
1149            self.check_item(&item.node);
1150        }
1151
1152        if self.errors.is_empty() {
1153            Ok(())
1154        } else {
1155            Err(std::mem::take(&mut self.errors))
1156        }
1157    }
1158
1159    /// Collect type definitions (first pass)
1160    fn collect_type_def(&mut self, item: &Item) {
1161        match item {
1162            Item::Struct(s) => {
1163                let generics = s
1164                    .generics
1165                    .as_ref()
1166                    .map(|g| {
1167                        g.params
1168                            .iter()
1169                            .filter_map(|p| {
1170                                if let GenericParam::Type { name, .. } = p {
1171                                    Some(name.name.clone())
1172                                } else {
1173                                    None
1174                                }
1175                            })
1176                            .collect()
1177                    })
1178                    .unwrap_or_default();
1179
1180                let fields = match &s.fields {
1181                    StructFields::Named(fs) => fs
1182                        .iter()
1183                        .map(|f| (f.name.name.clone(), self.convert_type(&f.ty)))
1184                        .collect(),
1185                    StructFields::Tuple(ts) => ts
1186                        .iter()
1187                        .enumerate()
1188                        .map(|(i, t)| (i.to_string(), self.convert_type(t)))
1189                        .collect(),
1190                    StructFields::Unit => vec![],
1191                };
1192
1193                // Check for duplicate struct definition
1194                if self.types.contains_key(&s.name.name) {
1195                    self.error(TypeError::new(format!(
1196                        "duplicate type definition: '{}'",
1197                        s.name.name
1198                    )));
1199                }
1200                self.types
1201                    .insert(s.name.name.clone(), TypeDef::Struct { generics, fields });
1202            }
1203            Item::Enum(e) => {
1204                let generics = e
1205                    .generics
1206                    .as_ref()
1207                    .map(|g| {
1208                        g.params
1209                            .iter()
1210                            .filter_map(|p| {
1211                                if let GenericParam::Type { name, .. } = p {
1212                                    Some(name.name.clone())
1213                                } else {
1214                                    None
1215                                }
1216                            })
1217                            .collect()
1218                    })
1219                    .unwrap_or_default();
1220
1221                let variants = e
1222                    .variants
1223                    .iter()
1224                    .map(|v| {
1225                        let fields = match &v.fields {
1226                            StructFields::Tuple(ts) => {
1227                                Some(ts.iter().map(|t| self.convert_type(t)).collect())
1228                            }
1229                            StructFields::Named(fs) => {
1230                                Some(fs.iter().map(|f| self.convert_type(&f.ty)).collect())
1231                            }
1232                            StructFields::Unit => None,
1233                        };
1234                        (v.name.name.clone(), fields)
1235                    })
1236                    .collect();
1237
1238                self.types
1239                    .insert(e.name.name.clone(), TypeDef::Enum { generics, variants });
1240            }
1241            Item::TypeAlias(t) => {
1242                let generics = t
1243                    .generics
1244                    .as_ref()
1245                    .map(|g| {
1246                        g.params
1247                            .iter()
1248                            .filter_map(|p| {
1249                                if let GenericParam::Type { name, .. } = p {
1250                                    Some(name.name.clone())
1251                                } else {
1252                                    None
1253                                }
1254                            })
1255                            .collect()
1256                    })
1257                    .unwrap_or_default();
1258
1259                let target = self.convert_type(&t.ty);
1260                self.types
1261                    .insert(t.name.name.clone(), TypeDef::Alias { generics, target });
1262            }
1263            _ => {}
1264        }
1265    }
1266
1267    /// Collect function signatures (second pass)
1268    fn collect_fn_sig(&mut self, item: &Item) {
1269        match item {
1270            Item::Function(f) => {
1271                // Set up generic type parameters as type variables
1272                if let Some(ref generics) = f.generics {
1273                    for param in &generics.params {
1274                        if let crate::ast::GenericParam::Type { name, .. } = param {
1275                            let type_var = self.fresh_var();
1276                            self.current_generics.insert(name.name.clone(), type_var);
1277                        }
1278                    }
1279                }
1280
1281                let params: Vec<Type> = f.params.iter().map(|p| self.convert_type(&p.ty)).collect();
1282
1283                let return_type = f
1284                    .return_type
1285                    .as_ref()
1286                    .map(|t| self.convert_type(t))
1287                    .unwrap_or(Type::Unit);
1288
1289                let fn_type = Type::Function {
1290                    params,
1291                    return_type: Box::new(return_type),
1292                    is_async: f.is_async,
1293                };
1294
1295                // Check for duplicate function definition (allow shadowing stdlib functions)
1296                if self.functions.contains_key(&f.name.name)
1297                    && !self.stdlib_functions.contains(&f.name.name)
1298                {
1299                    self.error(TypeError::new(format!(
1300                        "duplicate function definition: '{}'",
1301                        f.name.name
1302                    )));
1303                }
1304                self.functions.insert(f.name.name.clone(), fn_type);
1305
1306                // Clear generics after processing
1307                self.current_generics.clear();
1308            }
1309            Item::Impl(impl_block) => {
1310                // Get the type name being implemented
1311                let type_name = self.type_path_to_name(&impl_block.self_ty);
1312
1313                // Set up generic type parameters as type variables
1314                // Must do this FIRST so we can include generics in current_self_type
1315                let mut generic_types = Vec::new();
1316                if let Some(ref generics) = impl_block.generics {
1317                    for param in &generics.params {
1318                        if let crate::ast::GenericParam::Type { name, .. } = param {
1319                            let type_var = self.fresh_var();
1320                            self.current_generics.insert(name.name.clone(), type_var.clone());
1321                            generic_types.push(type_var);
1322                        }
1323                    }
1324                }
1325
1326                // Set current_self_type with generics so Self resolves correctly
1327                self.current_self_type = Some(Type::Named {
1328                    name: type_name.clone(),
1329                    generics: generic_types,
1330                });
1331
1332                // Collect associated functions/methods
1333                for impl_item in &impl_block.items {
1334                    if let crate::ast::ImplItem::Function(f) = impl_item {
1335                        let params: Vec<Type> =
1336                            f.params.iter().map(|p| self.convert_type(&p.ty)).collect();
1337
1338                        let return_type = f
1339                            .return_type
1340                            .as_ref()
1341                            .map(|t| self.convert_type(t))
1342                            .unwrap_or(Type::Unit);
1343
1344                        let fn_type = Type::Function {
1345                            params,
1346                            return_type: Box::new(return_type),
1347                            is_async: f.is_async,
1348                        };
1349
1350                        // Register in impl_methods
1351                        self.impl_methods
1352                            .entry(type_name.clone())
1353                            .or_insert_with(HashMap::new)
1354                            .insert(f.name.name.clone(), fn_type);
1355                    }
1356                }
1357
1358                // Clear current_self_type and generics when done
1359                self.current_self_type = None;
1360                self.current_generics.clear();
1361            }
1362            _ => {}
1363        }
1364    }
1365
1366    /// Convert a TypePath to a simple type name string
1367    fn type_path_to_name(&self, ty: &crate::ast::TypeExpr) -> String {
1368        match ty {
1369            crate::ast::TypeExpr::Path(path) => {
1370                path.segments
1371                    .iter()
1372                    .map(|s| s.ident.name.clone())
1373                    .collect::<Vec<_>>()
1374                    .join("::")
1375            }
1376            _ => "Unknown".to_string(),
1377        }
1378    }
1379
1380    /// Check an item (third pass)
1381    fn check_item(&mut self, item: &Item) {
1382        match item {
1383            Item::Function(f) => self.check_function(f),
1384            Item::Const(c) => {
1385                let declared = self.convert_type(&c.ty);
1386                let inferred = self.infer_expr(&c.value);
1387                if !self.unify(&declared, &inferred) {
1388                    self.error(
1389                        TypeError::new(format!(
1390                            "type mismatch in const '{}': expected {:?}, found {:?}",
1391                            c.name.name, declared, inferred
1392                        ))
1393                        .with_span(c.name.span),
1394                    );
1395                }
1396            }
1397            Item::Static(s) => {
1398                let declared = self.convert_type(&s.ty);
1399                let inferred = self.infer_expr(&s.value);
1400                if !self.unify(&declared, &inferred) {
1401                    self.error(
1402                        TypeError::new(format!(
1403                            "type mismatch in static '{}': expected {:?}, found {:?}",
1404                            s.name.name, declared, inferred
1405                        ))
1406                        .with_span(s.name.span),
1407                    );
1408                }
1409            }
1410            Item::Impl(impl_block) => {
1411                // Get the type name being implemented
1412                let type_name = self.type_path_to_name(&impl_block.self_ty);
1413
1414                // Set up generic type parameters as type variables
1415                // Must do this FIRST so we can include generics in current_self_type
1416                let mut generic_types = Vec::new();
1417                if let Some(ref generics) = impl_block.generics {
1418                    for param in &generics.params {
1419                        if let crate::ast::GenericParam::Type { name, .. } = param {
1420                            let type_var = self.fresh_var();
1421                            self.current_generics.insert(name.name.clone(), type_var.clone());
1422                            generic_types.push(type_var);
1423                        }
1424                    }
1425                }
1426
1427                // Set current_self_type with generics so Self resolves correctly
1428                self.current_self_type = Some(Type::Named {
1429                    name: type_name,
1430                    generics: generic_types,
1431                });
1432
1433                // Check each function in the impl block
1434                for impl_item in &impl_block.items {
1435                    if let crate::ast::ImplItem::Function(f) = impl_item {
1436                        self.check_function(f);
1437                    }
1438                }
1439
1440                // Clear current_self_type and generics when done
1441                self.current_self_type = None;
1442                self.current_generics.clear();
1443            }
1444            _ => {}
1445        }
1446    }
1447
1448    /// Check a function body
1449    fn check_function(&mut self, func: &Function) {
1450        self.push_scope();
1451
1452        // Set up generic type parameters as type variables
1453        if let Some(ref generics) = func.generics {
1454            for param in &generics.params {
1455                if let crate::ast::GenericParam::Type { name, .. } = param {
1456                    let type_var = self.fresh_var();
1457                    self.current_generics.insert(name.name.clone(), type_var);
1458                }
1459            }
1460        }
1461
1462        // Bind parameters with evidence inference
1463        for param in &func.params {
1464            let ty = self.convert_type(&param.ty);
1465            // Infer parameter evidence from type annotation if present,
1466            // otherwise from pattern annotation, otherwise default to Known
1467            let type_evidence = self.get_evidence(&ty);
1468            let evidence = param
1469                .pattern
1470                .evidentiality()
1471                .map(EvidenceLevel::from_ast)
1472                .unwrap_or(type_evidence);
1473
1474            if let Some(name) = param.pattern.binding_name() {
1475                self.env.borrow_mut().define(name, ty, evidence);
1476            }
1477        }
1478
1479        // Set expected return type for checking explicit return statements
1480        let expected_return = func
1481            .return_type
1482            .as_ref()
1483            .map(|t| self.convert_type(t))
1484            .unwrap_or(Type::Unit);
1485        let old_return_type = self.expected_return_type.clone();
1486        self.expected_return_type = Some(expected_return.clone());
1487
1488        // Check body
1489        if let Some(ref body) = func.body {
1490            let body_type = self.check_block(body);
1491
1492            // Restore old return type
1493            self.expected_return_type = old_return_type;
1494
1495            // Check return type (for implicit returns)
1496            let expected_return = func
1497                .return_type
1498                .as_ref()
1499                .map(|t| self.convert_type(t))
1500                .unwrap_or(Type::Unit);
1501
1502            // Check structural type compatibility
1503            // For bootstrapping: skip return type checking to be lenient with
1504            // cross-file references and unresolved type variables
1505            let _ = self.unify(&expected_return, &body_type);
1506
1507            // Evidence inference for return types:
1508            // - If return type has explicit evidence annotation → check compatibility
1509            // - If function name has evidence annotation (e.g., validate!) → use that
1510            // - If no explicit annotation → infer evidence from body
1511            // - For public functions, warn if evidence should be annotated at module boundary
1512            let type_has_evidence = self.type_has_explicit_evidence(func.return_type.as_ref());
1513            // Function name evidentiality (e.g., validate_model! has ! evidentiality)
1514            let name_evidence = func.name.evidentiality.as_ref()
1515                .map(|e| EvidenceLevel::from_ast(*e));
1516            let has_explicit_evidence = type_has_evidence || name_evidence.is_some();
1517            let actual_evidence = self.get_evidence(&body_type);
1518
1519            if has_explicit_evidence {
1520                // Explicit annotation: check compatibility
1521                // EXCEPT: if evidentiality is on the function NAME (e.g., validate!),
1522                // the function is declaring it transforms evidence - trust that declaration
1523                if name_evidence.is_none() {
1524                    let expected_evidence = self.get_evidence(&expected_return);
1525                    self.check_evidence(
1526                        expected_evidence,
1527                        actual_evidence,
1528                        &format!("in return type of '{}'", func.name.name),
1529                    );
1530                }
1531                // If name has evidentiality, skip the check - function transforms evidence
1532            } else {
1533                // No explicit annotation: infer from body
1534                // For public functions at module boundaries, suggest annotation
1535                if func.visibility == Visibility::Public && actual_evidence > EvidenceLevel::Known {
1536                    self.error(
1537                        TypeError::new(format!(
1538                            "public function '{}' returns {} ({}) data but has no explicit evidence annotation",
1539                            func.name.name,
1540                            actual_evidence.name(),
1541                            actual_evidence.symbol(),
1542                        ))
1543                        .with_span(func.name.span)
1544                        .with_note("help: add explicit evidence annotation to the return type")
1545                        .with_note(format!(
1546                            "example: fn {}(...) -> {}{} {{ ... }}",
1547                            func.name.name,
1548                            expected_return,
1549                            actual_evidence.symbol()
1550                        )),
1551                    );
1552                }
1553                // Inference succeeds - the body's evidence becomes the function's evidence
1554            }
1555        }
1556
1557        // Clear generics after processing
1558        self.current_generics.clear();
1559        self.pop_scope();
1560    }
1561
1562    /// Check if a type expression has an explicit evidence annotation
1563    fn type_has_explicit_evidence(&self, ty: Option<&TypeExpr>) -> bool {
1564        match ty {
1565            Some(TypeExpr::Evidential { .. }) => true,
1566            Some(TypeExpr::Reference { inner, .. })
1567            | Some(TypeExpr::Pointer { inner, .. })
1568            | Some(TypeExpr::Slice(inner))
1569            | Some(TypeExpr::Array { element: inner, .. }) => {
1570                self.type_has_explicit_evidence(Some(inner.as_ref()))
1571            }
1572            Some(TypeExpr::Tuple(elements)) => elements
1573                .iter()
1574                .any(|e| self.type_has_explicit_evidence(Some(e))),
1575            _ => false,
1576        }
1577    }
1578
1579    /// Check a block and return its type
1580    fn check_block(&mut self, block: &Block) -> Type {
1581        self.push_scope();
1582
1583        let mut diverges = false;
1584        for stmt in &block.stmts {
1585            let stmt_ty = self.check_stmt(stmt);
1586            if matches!(stmt_ty, Type::Never) {
1587                diverges = true;
1588            }
1589        }
1590
1591        let result = if let Some(ref expr) = block.expr {
1592            self.infer_expr(expr)
1593        } else if diverges {
1594            Type::Never
1595        } else {
1596            Type::Unit
1597        };
1598
1599        self.pop_scope();
1600        result
1601    }
1602
1603    /// Check a statement and return its type (Never if it diverges)
1604    fn check_stmt(&mut self, stmt: &Stmt) -> Type {
1605        match stmt {
1606            Stmt::Let { pattern, ty, init } => {
1607                let declared_ty = ty.as_ref().map(|t| self.convert_type(t));
1608                let init_ty = init.as_ref().map(|e| self.infer_expr(e));
1609
1610                let final_ty = match (&declared_ty, &init_ty) {
1611                    (Some(d), Some(i)) => {
1612                        if !self.unify(d, i) {
1613                            // Report type mismatch error with helpful hints
1614                            let binding_name = pattern.binding_name().unwrap_or_else(|| "<pattern>".to_string());
1615
1616                            // Check for common Rust-ism: using [T] slice syntax with array literal
1617                            let hint = match (d, i) {
1618                                (Type::Slice(_), Type::Array { .. }) => {
1619                                    ". Hint: `[T]` is slice syntax in Sigil. \
1620                                    For arrays, use `[T; N]` or omit the type annotation entirely"
1621                                }
1622                                _ => "",
1623                            };
1624
1625                            let mut err = TypeError::new(format!(
1626                                "type mismatch in let binding '{}': expected {:?}, found {:?}{}",
1627                                binding_name, d, i, hint
1628                            ));
1629                            if let Some(span) = pattern.binding_span() {
1630                                err = err.with_span(span);
1631                            }
1632                            self.error(err);
1633                        }
1634                        d.clone()
1635                    }
1636                    (Some(d), None) => d.clone(),
1637                    (None, Some(i)) => i.clone(),
1638                    (None, None) => self.fresh_var(),
1639                };
1640
1641                // Evidence inference: explicit annotation takes precedence,
1642                // otherwise infer from initializer expression.
1643                // This reduces annotation burden while maintaining safety:
1644                // - `let x = network_call()` → x is automatically ~
1645                // - `let x! = validated_data` → explicit ! annotation honored
1646                let evidence = pattern
1647                    .evidentiality()
1648                    .map(EvidenceLevel::from_ast)
1649                    .unwrap_or_else(|| {
1650                        // Infer evidence from initializer type
1651                        init_ty
1652                            .as_ref()
1653                            .map(|ty| self.get_evidence(ty))
1654                            .unwrap_or(EvidenceLevel::Known)
1655                    });
1656
1657                // For simple ident patterns, use define() directly (preserves evidence wrapping).
1658                // For complex patterns (tuples, structs), use bind_pattern for destructuring.
1659                if let Some(name) = pattern.binding_name() {
1660                    self.env.borrow_mut().define(name, final_ty, evidence);
1661                } else {
1662                    self.bind_pattern(pattern, &final_ty, evidence);
1663                }
1664                Type::Unit
1665            }
1666            Stmt::LetElse { pattern, ty, init, else_branch } => {
1667                // Type check let-else similar to let
1668                let declared_ty = ty.as_ref().map(|t| self.convert_type(t));
1669                let init_ty = self.infer_expr(init);
1670                // Infer evidence before moving init_ty
1671                let evidence = pattern
1672                    .evidentiality()
1673                    .map(EvidenceLevel::from_ast)
1674                    .unwrap_or_else(|| self.get_evidence(&init_ty));
1675                let final_ty = declared_ty.unwrap_or(init_ty);
1676                // Check else branch
1677                self.infer_expr(else_branch);
1678                // For simple ident patterns, use define() directly (preserves evidence wrapping).
1679                // For complex patterns (tuples, structs), use bind_pattern for destructuring.
1680                if let Some(name) = pattern.binding_name() {
1681                    self.env.borrow_mut().define(name, final_ty, evidence);
1682                } else {
1683                    self.bind_pattern(pattern, &final_ty, evidence);
1684                }
1685                Type::Unit
1686            }
1687            Stmt::Expr(e) | Stmt::Semi(e) => self.infer_expr(e),
1688            Stmt::Item(item) => {
1689                self.check_item(item);
1690                Type::Unit
1691            }
1692        }
1693    }
1694
1695    /// Infer the type of an expression
1696    pub fn infer_expr(&mut self, expr: &Expr) -> Type {
1697        match expr {
1698            Expr::Literal(lit) => self.infer_literal(lit),
1699
1700            Expr::Path(path) => {
1701                if path.segments.len() == 1 {
1702                    let name = &path.segments[0].ident.name;
1703
1704                    // First, lookup the type (immutable borrow)
1705                    let lookup_result = self.env.borrow().lookup(name);
1706
1707                    if let Some((ty, _)) = lookup_result {
1708                        // Check for linear type double-use
1709                        if matches!(ty, Type::Linear(_)) {
1710                            let already_consumed = self.env.borrow().is_consumed(name);
1711                            if already_consumed {
1712                                // Linear variable used twice - no-cloning violation!
1713                                self.error(TypeError::new(format!(
1714                                    "linear value '{}' used twice: linear types cannot be cloned (no-cloning theorem)",
1715                                    name
1716                                )));
1717                            } else {
1718                                // Mark as consumed for future use checks
1719                                self.env.borrow_mut().consume(name);
1720                            }
1721                        }
1722                        return ty;
1723                    }
1724                    if let Some(ty) = self.functions.get(name).cloned() {
1725                        // Freshen polymorphic types to get fresh type variables
1726                        return self.freshen(&ty);
1727                    }
1728                } else if path.segments.len() == 2 {
1729                    // Handle Type::method() - associated function lookup
1730                    let type_name = &path.segments[0].ident.name;
1731                    let method_name = &path.segments[1].ident.name;
1732
1733                    // Check impl_methods for associated functions
1734                    if let Some(methods) = self.impl_methods.get(type_name) {
1735                        if let Some(ty) = methods.get(method_name) {
1736                            let ty_cloned = ty.clone();
1737                            return self.freshen(&ty_cloned);
1738                        }
1739                    }
1740
1741                    // Check for enum variant constructors: Enum::Variant
1742                    if let Some(TypeDef::Enum { variants, .. }) = self.types.get(type_name) {
1743                        for (variant_name, _variant_fields) in variants {
1744                            if variant_name == method_name {
1745                                // Return the enum type for unit/tuple variants
1746                                return Type::Named {
1747                                    name: type_name.clone(),
1748                                    generics: vec![],
1749                                };
1750                            }
1751                        }
1752                    }
1753                }
1754                // For bootstrapping: treat undefined paths as unknown types
1755                // This allows cross-file references to not cause errors
1756                // A real type checker would require imports or multi-file analysis
1757                self.fresh_var()
1758            }
1759
1760            Expr::Binary { left, op, right } => {
1761                let lt = self.infer_expr(left);
1762                let rt = self.infer_expr(right);
1763                self.infer_binary_op(op, &lt, &rt)
1764            }
1765
1766            Expr::Unary { op, expr } => {
1767                let inner = self.infer_expr(expr);
1768                self.infer_unary_op(op, &inner)
1769            }
1770
1771            Expr::Call { func, args } => {
1772                let fn_type = self.infer_expr(func);
1773                let arg_types: Vec<Type> = args.iter().map(|a| self.infer_expr(a)).collect();
1774
1775                if let Type::Function {
1776                    params,
1777                    return_type,
1778                    ..
1779                } = fn_type
1780                {
1781                    // Extract function name for variadic builtin check
1782                    let func_name = match func.as_ref() {
1783                        Expr::Path(path) if path.segments.len() == 1 => {
1784                            Some(path.segments[0].ident.name.as_str())
1785                        }
1786                        _ => None,
1787                    };
1788
1789                    // Known variadic builtins: interpreter accepts variable args
1790                    // (registered with arity: None in stdlib.rs). Allow extra
1791                    // arguments beyond the minimum registered parameter count.
1792                    let is_variadic_builtin = func_name.map_or(false, |name| {
1793                        matches!(
1794                            name,
1795                            "assert"
1796                                | "println"
1797                                | "print"
1798                                | "eprintln"
1799                                | "eprint"
1800                                | "panic"
1801                                | "todo"
1802                                | "unreachable"
1803                                | "format"
1804                        )
1805                    });
1806
1807                    // Check argument count: variadic builtins require at least
1808                    // params.len() args; all others require exact match.
1809                    if is_variadic_builtin {
1810                        if arg_types.len() < params.len() {
1811                            self.error(TypeError::new(format!(
1812                                "expected at least {} arguments, found {}",
1813                                params.len(),
1814                                arg_types.len()
1815                            )));
1816                        }
1817                    } else if params.len() != arg_types.len() {
1818                        self.error(TypeError::new(format!(
1819                            "expected {} arguments, found {}",
1820                            params.len(),
1821                            arg_types.len()
1822                        )));
1823                    }
1824
1825                    // Check argument types and evidence levels
1826                    for (i, (param, arg)) in params.iter().zip(arg_types.iter()).enumerate() {
1827                        // Check argument type matches parameter type
1828                        if !self.unify(param, arg) {
1829                            // Allow implicit numeric coercion: int → float
1830                            let is_numeric_coercion = Self::is_numeric_coercion(param, arg);
1831                            // Allow reference coercions: &mut T → &T, &Box<T> → &T, &Vec<T> → &[T], &&T → &T
1832                            let is_reference_coercion = Self::is_reference_coercion(param, arg);
1833                            // Allow auto-ref/deref: T → &T, &T → T
1834                            let is_ref_value_coercion = Self::is_ref_value_coercion(param, arg);
1835                            // Only report error for concrete type mismatches, not type variables
1836                            if !matches!(param, Type::Var(_)) && !matches!(arg, Type::Var(_))
1837                                && !is_numeric_coercion && !is_reference_coercion
1838                                && !is_ref_value_coercion {
1839                                self.error(TypeError::new(format!(
1840                                    "type mismatch in argument {}: expected {}, found {}",
1841                                    i + 1, param, arg
1842                                )));
1843                            }
1844                        }
1845
1846                        // Check evidence compatibility only when the parameter has an
1847                        // explicit evidence annotation (Type::Evidential). Unannotated
1848                        // parameters like `x: usize` accept any evidence level.
1849                        // Type variables (polymorphic) also skip evidence checking.
1850                        if matches!(param, Type::Evidential { .. }) {
1851                            let expected_evidence = self.get_evidence(param);
1852                            let actual_evidence = self.get_evidence(arg);
1853                            self.check_evidence(
1854                                expected_evidence,
1855                                actual_evidence,
1856                                &format!("in argument {}", i + 1),
1857                            );
1858                        }
1859                    }
1860
1861                    *return_type
1862                } else if let Type::Var(_) = &fn_type {
1863                    // For bootstrapping: if function is a type variable (undefined path),
1864                    // create a function type and unify, then return fresh result
1865                    let result_ty = self.fresh_var();
1866                    let inferred_fn = Type::Function {
1867                        params: arg_types,
1868                        return_type: Box::new(result_ty.clone()),
1869                        is_async: false,
1870                    };
1871                    self.unify(&fn_type, &inferred_fn);
1872                    result_ty
1873                } else {
1874                    // For bootstrapping: return fresh type variable instead of error
1875                    self.fresh_var()
1876                }
1877            }
1878
1879            Expr::Array(elements) => {
1880                if elements.is_empty() {
1881                    Type::Array {
1882                        element: Box::new(self.fresh_var()),
1883                        size: Some(0),
1884                    }
1885                } else {
1886                    let elem_ty = self.infer_expr(&elements[0]);
1887                    for elem in &elements[1..] {
1888                        let t = self.infer_expr(elem);
1889                        if !self.unify(&elem_ty, &t) {
1890                            self.error(TypeError::new("array elements must have same type"));
1891                        }
1892                    }
1893                    Type::Array {
1894                        element: Box::new(elem_ty),
1895                        size: Some(elements.len()),
1896                    }
1897                }
1898            }
1899
1900            Expr::Tuple(elements) => {
1901                Type::Tuple(elements.iter().map(|e| self.infer_expr(e)).collect())
1902            }
1903
1904            Expr::Block(block) => self.check_block(block),
1905
1906            Expr::If {
1907                condition,
1908                then_branch,
1909                else_branch,
1910            } => {
1911                let cond_ty = self.infer_expr(condition);
1912                // Strip evidence wrapper before checking: bool? is still bool
1913                let (bare_cond_ty, _) = self.strip_evidence(&cond_ty);
1914                if !self.unify(&Type::Bool, &bare_cond_ty) {
1915                    self.error(TypeError::new("if condition must be bool"));
1916                }
1917
1918                let then_ty = self.check_block(then_branch);
1919
1920                if let Some(else_expr) = else_branch {
1921                    // Else branch can be another expr (e.g., if-else chain) or a block
1922                    let else_ty = match else_expr.as_ref() {
1923                        Expr::Block(block) => self.check_block(block),
1924                        other => self.infer_expr(other),
1925                    };
1926                    // For bootstrapping: just try to unify, skip error if unification fails
1927                    // (type inference is incomplete so false positives are common)
1928                    let _ = self.unify(&then_ty, &else_ty);
1929
1930                    // Evidence inference for control flow:
1931                    // Join evidence from both branches (pessimistic - takes least certain)
1932                    // This ensures that if either branch produces uncertain data,
1933                    // the result is marked as uncertain.
1934                    let then_ev = self.get_evidence(&then_ty);
1935                    let else_ev = self.get_evidence(&else_ty);
1936                    let joined_ev = then_ev.join(else_ev);
1937
1938                    let (inner_ty, _) = self.strip_evidence(&then_ty);
1939                    if joined_ev > EvidenceLevel::Known {
1940                        Type::Evidential {
1941                            inner: Box::new(inner_ty),
1942                            evidence: joined_ev,
1943                        }
1944                    } else {
1945                        inner_ty
1946                    }
1947                } else {
1948                    Type::Unit
1949                }
1950            }
1951
1952            Expr::While {
1953                condition,
1954                body,
1955                ..
1956            } => {
1957                let cond_ty = self.infer_expr(condition);
1958                let (bare_cond_ty, _) = self.strip_evidence(&cond_ty);
1959                if !self.unify(&Type::Bool, &bare_cond_ty) {
1960                    self.error(TypeError::new("while condition must be bool"));
1961                }
1962                self.check_block(body);
1963                Type::Unit
1964            }
1965
1966            Expr::Loop { body, .. } => {
1967                self.check_block(body);
1968                Type::Unit
1969            }
1970
1971            Expr::For {
1972                pattern: _,
1973                iter,
1974                body,
1975                ..
1976            } => {
1977                // Infer the iterable type (for basic type checking)
1978                let _ = self.infer_expr(iter);
1979                self.check_block(body);
1980                Type::Unit
1981            }
1982
1983            Expr::Pipe { expr, operations } => {
1984                let mut current = self.infer_expr(expr);
1985
1986                for op in operations {
1987                    current = self.infer_pipe_op(op, &current);
1988                }
1989
1990                current
1991            }
1992
1993            Expr::Index { expr, index } => {
1994                let coll_ty = self.infer_expr(expr);
1995                let idx_ty = self.infer_expr(index);
1996
1997                match coll_ty {
1998                    Type::Array { element, .. } | Type::Slice(element) => {
1999                        if !matches!(idx_ty, Type::Int(_)) {
2000                            self.error(TypeError::new("index must be integer"));
2001                        }
2002                        *element
2003                    }
2004                    _ => {
2005                        // For bootstrapping: return fresh type variable
2006                        self.fresh_var()
2007                    }
2008                }
2009            }
2010
2011            Expr::Return(val) => {
2012                let actual_type = if let Some(e) = val {
2013                    self.infer_expr(e)
2014                } else {
2015                    Type::Unit
2016                };
2017
2018                // Check against expected return type if we're inside a function
2019                if let Some(expected) = self.expected_return_type.clone() {
2020                    if !self.unify(&expected, &actual_type) {
2021                        self.error(TypeError::new(format!(
2022                            "type mismatch in return: expected {}, found {}",
2023                            expected, actual_type
2024                        )));
2025                    }
2026                }
2027
2028                Type::Never
2029            }
2030
2031            // Mark expression with evidence
2032            Expr::Evidential {
2033                expr,
2034                evidentiality,
2035            } => {
2036                let inner = self.infer_expr(expr);
2037                let ev = EvidenceLevel::from_ast(*evidentiality);
2038
2039                // When ? (Uncertain) is applied to Result<T, E> or Option<T>,
2040                // this is the try operator: unwrap to T
2041                if ev == EvidenceLevel::Uncertain {
2042                    let resolved = if let Type::Var(v) = &inner {
2043                        self.substitutions.get(v).cloned().unwrap_or(inner.clone())
2044                    } else {
2045                        inner.clone()
2046                    };
2047                    // Strip evidentiality wrapper (e.g., !Result<T,E> → Result<T,E>)
2048                    let (stripped, _) = self.strip_evidence(&resolved);
2049                    match &stripped {
2050                        Type::Named { name, generics } if name == "Result" && !generics.is_empty() => {
2051                            return generics[0].clone();
2052                        }
2053                        Type::Named { name, generics } if name == "Option" && !generics.is_empty() => {
2054                            return generics[0].clone();
2055                        }
2056                        _ => {}
2057                    }
2058                }
2059
2060                Type::Evidential {
2061                    inner: Box::new(inner),
2062                    evidence: ev,
2063                }
2064            }
2065
2066            // Match expression with evidence-aware dispatch
2067            Expr::Match { expr, arms } => {
2068                let scrutinee = self.infer_expr(expr);
2069                let scrutinee_ev = self.get_evidence(&scrutinee);
2070
2071                if arms.is_empty() {
2072                    return Type::Never; // Empty match is diverging
2073                }
2074
2075                // Check all arms and collect their types
2076                let mut arm_types: Vec<Type> = Vec::new();
2077                let mut max_evidence = EvidenceLevel::Known;
2078
2079                // Snapshot substitutions before match: each arm should start
2080                // from the same type variable state. This prevents one arm's
2081                // bindings (e.g., Device=Cuda) from conflicting with another
2082                // arm's bindings (e.g., Device=Cpu) in device dispatch patterns.
2083                let saved_substitutions = self.substitutions.clone();
2084
2085                for arm in arms {
2086                    // Restore substitutions to pre-match state for each arm
2087                    self.substitutions = saved_substitutions.clone();
2088                    self.push_scope();
2089
2090                    // Bind pattern variables with scrutinee's evidence level
2091                    // This propagates evidence through pattern matching
2092                    self.bind_pattern(&arm.pattern, &scrutinee, scrutinee_ev);
2093
2094                    // Check guard if present
2095                    if let Some(ref guard) = arm.guard {
2096                        let guard_ty = self.infer_expr(guard);
2097                        if !self.unify(&Type::Bool, &guard_ty) {
2098                            self.error(TypeError::new("match guard must be bool"));
2099                        }
2100                    }
2101
2102                    // Infer arm body type
2103                    let body_ty = self.infer_expr(&arm.body);
2104                    let body_ev = self.get_evidence(&body_ty);
2105
2106                    // Join evidence from all arms (pessimistic)
2107                    max_evidence = max_evidence.join(body_ev);
2108                    arm_types.push(body_ty);
2109
2110                    self.pop_scope();
2111                }
2112
2113                // Restore to pre-match state, then let arm type unification
2114                // establish the final bindings from the match result type
2115                self.substitutions = saved_substitutions;
2116
2117                // Unify all arm types
2118                // For bootstrapping: skip error, just try to unify
2119                let first_ty = &arm_types[0];
2120                for (_i, ty) in arm_types.iter().enumerate().skip(1) {
2121                    let _ = self.unify(first_ty, ty);
2122                }
2123
2124                // Result has joined evidence from all arms
2125                let (inner_ty, _) = self.strip_evidence(first_ty);
2126                if max_evidence > EvidenceLevel::Known {
2127                    Type::Evidential {
2128                        inner: Box::new(inner_ty),
2129                        evidence: max_evidence,
2130                    }
2131                } else {
2132                    inner_ty
2133                }
2134            }
2135
2136            Expr::MethodCall {
2137                receiver,
2138                method,
2139                args,
2140                ..
2141            } => {
2142                let recv_ty = self.infer_expr(receiver);
2143                let (recv_inner, recv_ev) = self.strip_evidence(&recv_ty);
2144                // Strip references to get the underlying type for method lookup
2145                let recv_derefed = match &recv_inner {
2146                    Type::Ref { inner, .. } => {
2147                        // Also strip evidence from inner ref
2148                        let (inner_stripped, _) = self.strip_evidence(inner);
2149                        // Handle &&T -> T
2150                        match &inner_stripped {
2151                            Type::Ref { inner: inner2, .. } => {
2152                                let (i2, _) = self.strip_evidence(inner2);
2153                                i2
2154                            }
2155                            other => other.clone(),
2156                        }
2157                    }
2158                    other => other.clone(),
2159                };
2160                let _arg_types: Vec<Type> = args.iter().map(|a| self.infer_expr(a)).collect();
2161
2162                // FIRST: Check user-defined methods in impl_methods
2163                // This takes priority over hardcoded patterns
2164                // Try both the original recv_inner and the deref'd version
2165                let user_method_result = {
2166                    // Try original type first
2167                    let mut result = None;
2168                    if let Type::Named { name: ref type_name, .. } = recv_inner {
2169                        if let Some(fn_type) = self.impl_methods.get(type_name)
2170                            .and_then(|methods| methods.get(&method.name))
2171                            .cloned()
2172                        {
2173                            if let Type::Function { return_type, .. } = self.freshen(&fn_type) {
2174                                result = Some(*return_type);
2175                            }
2176                        }
2177                    }
2178                    // If not found, try deref'd type
2179                    if result.is_none() {
2180                        if let Type::Named { name: ref type_name, .. } = recv_derefed {
2181                            if let Some(fn_type) = self.impl_methods.get(type_name)
2182                                .and_then(|methods| methods.get(&method.name))
2183                                .cloned()
2184                            {
2185                                if let Type::Function { return_type, .. } = self.freshen(&fn_type) {
2186                                    result = Some(*return_type);
2187                                }
2188                            }
2189                        }
2190                    }
2191                    result
2192                };
2193
2194                // If user-defined method found, use it; otherwise fall back to hardcoded patterns
2195                let result_ty = if let Some(user_ty) = user_method_result {
2196                    user_ty
2197                } else {
2198                    // Resolve known methods based on receiver type and method name
2199                    match method.name.as_str() {
2200                    // Collection methods returning usize
2201                    "len" | "count" | "size" => Type::Int(IntSize::USize),
2202
2203                    // Boolean predicates
2204                    "is_empty" | "contains" | "starts_with" | "ends_with" | "is_some"
2205                    | "is_none" | "is_ok" | "is_err" | "is_ascii" | "is_alphabetic"
2206                    | "is_numeric" | "is_alphanumeric" | "is_whitespace" | "is_uppercase"
2207                    | "is_lowercase" | "exists" | "is_file" | "is_dir" | "is_match"
2208                    | "matches" | "eq" | "ne" | "lt" | "le" | "gt" | "ge" => Type::Bool,
2209
2210                    // String methods returning String
2211                    "to_string" | "to_lowercase" | "to_uppercase" | "trim" | "trim_start"
2212                    | "trim_end" | "to_owned" | "replace" | "replacen" | "repeat"
2213                    | "to_string_lossy" => Type::Named {
2214                        name: "String".to_string(),
2215                        generics: vec![],
2216                    },
2217
2218                    // String methods returning &str
2219                    "as_str" | "trim_matches" | "trim_start_matches" | "trim_end_matches"
2220                    | "strip_prefix" | "strip_suffix" => Type::Ref {
2221                        lifetime: None,
2222                        mutable: false,
2223                        inner: Box::new(Type::Str),
2224                    },
2225
2226                    // Clone returns same type as receiver
2227                    "clone" | "cloned" | "copied" => recv_inner.clone(),
2228
2229                    // Option/Result unwrapping - return inner type or fresh var
2230                    "unwrap" | "unwrap_or" | "unwrap_or_default" | "unwrap_or_else"
2231                    | "expect" | "ok" | "err" => {
2232                        if let Type::Named { name, generics } = &recv_inner {
2233                            if (name == "Option" || name == "Result") && !generics.is_empty() {
2234                                generics[0].clone()
2235                            } else {
2236                                self.fresh_var()
2237                            }
2238                        } else {
2239                            self.fresh_var()
2240                        }
2241                    }
2242
2243                    // collect() returns fresh var to unify with type annotation
2244                    "collect" => self.fresh_var(),
2245
2246                    // Iterator/collection transformation methods - preserve receiver type
2247                    "iter" | "into_iter" | "iter_mut" | "rev" | "skip" | "take"
2248                    | "filter" | "map" | "filter_map" | "flat_map" | "enumerate"
2249                    | "zip" | "chain" | "flatten" | "reverse" | "sorted"
2250                    | "dedup" | "unique" | "peekable" | "fuse" | "cycle" | "step_by"
2251                    | "take_while" | "skip_while" | "scan" | "inspect" => recv_inner.clone(),
2252
2253                    // String splitting/iteration - returns iterator (fresh var for proper chaining)
2254                    "split" | "rsplit" | "splitn" | "rsplitn" | "split_whitespace"
2255                    | "split_ascii_whitespace" | "lines" | "chars" | "bytes"
2256                    | "char_indices" | "split_terminator" | "rsplit_terminator"
2257                    | "split_inclusive" | "matches_iter" => self.fresh_var(),
2258
2259                    // HashMap/BTreeMap methods - return iterator-like fresh var
2260                    "keys" | "values" | "values_mut" | "into_keys"
2261                    | "into_values" | "entry" | "drain" => self.fresh_var(),
2262
2263                    // Methods returning Option<T>
2264                    "first" | "last" | "get" | "get_mut" | "pop" | "pop_front"
2265                    | "pop_back" | "find" | "find_map" | "position" | "rposition"
2266                    | "next" | "next_back" | "peek" | "nth" | "last_mut"
2267                    | "binary_search" | "parent" | "file_name" | "file_stem"
2268                    | "extension" => Type::Named {
2269                        name: "Option".to_string(),
2270                        generics: vec![self.fresh_var()],
2271                    },
2272
2273                    // Methods returning Result
2274                    "parse" | "try_into" | "try_from" => Type::Named {
2275                        name: "Result".to_string(),
2276                        generics: vec![self.fresh_var(), self.fresh_var()],
2277                    },
2278
2279                    // Methods that remove and return an element
2280                    "remove" | "swap_remove" => {
2281                        // Vec::remove(index) returns T, not ()
2282                        let effective_recv = if let Type::Named { .. } = &recv_inner {
2283                            &recv_inner
2284                        } else {
2285                            &recv_derefed
2286                        };
2287                        if let Type::Named { generics, .. } = effective_recv {
2288                            if !generics.is_empty() {
2289                                generics[0].clone()
2290                            } else {
2291                                self.fresh_var()
2292                            }
2293                        } else {
2294                            self.fresh_var()
2295                        }
2296                    }
2297
2298                    // Push/insert/mutating methods return unit
2299                    "push" | "push_str" | "push_front" | "push_back" | "insert"
2300                    | "clear" | "sort" | "sort_by" | "sort_by_key"
2301                    | "sort_unstable" | "truncate" | "resize" | "extend" | "append"
2302                    | "retain" | "swap" => Type::Unit,
2303
2304                    // Numeric methods
2305                    "abs" | "floor" | "ceil" | "round" | "trunc" | "fract" | "sqrt"
2306                    | "cbrt" | "sin" | "cos" | "tan" | "asin" | "acos" | "atan"
2307                    | "sinh" | "cosh" | "tanh" | "exp" | "exp2" | "ln" | "log"
2308                    | "log2" | "log10" | "pow" | "powi" | "powf" | "min" | "max"
2309                    | "clamp" | "signum" | "copysign" | "saturating_add"
2310                    | "saturating_sub" | "saturating_mul" | "wrapping_add"
2311                    | "wrapping_sub" | "wrapping_mul" | "checked_add" | "checked_sub"
2312                    | "checked_mul" | "checked_div" => recv_inner.clone(),
2313
2314                    // Char methods
2315                    "to_digit" | "to_lowercase_char" | "to_uppercase_char" => Type::Named {
2316                        name: "Option".to_string(),
2317                        generics: vec![Type::Int(IntSize::U32)],
2318                    },
2319
2320                    // Duration/Time methods
2321                    "duration_since" | "elapsed" | "as_secs" | "as_millis" | "as_micros"
2322                    | "as_nanos" | "from_secs" | "from_millis" => recv_inner.clone(),
2323
2324                    // Path methods returning PathBuf (only for Path/PathBuf receivers)
2325                    "to_path_buf" | "with_extension" | "with_file_name" => {
2326                        Type::Named {
2327                            name: "PathBuf".to_string(),
2328                            generics: vec![],
2329                        }
2330                    }
2331
2332                    // join: Path::join returns PathBuf, but Vec::join/Iterator::join returns String
2333                    "join" => {
2334                        // Check if receiver is Path or PathBuf
2335                        let is_path_type = if let Type::Named { name, .. } = &recv_inner {
2336                            name == "Path" || name == "PathBuf"
2337                        } else {
2338                            false
2339                        };
2340                        if is_path_type {
2341                            Type::Named {
2342                                name: "PathBuf".to_string(),
2343                                generics: vec![],
2344                            }
2345                        } else {
2346                            // Vec::join and Iterator::join return String
2347                            Type::Named {
2348                                name: "String".to_string(),
2349                                generics: vec![],
2350                            }
2351                        }
2352                    }
2353
2354                    // Path methods returning &str via OsStr
2355                    "to_str" => Type::Named {
2356                        name: "Option".to_string(),
2357                        generics: vec![Type::Ref {
2358                            lifetime: None,
2359                            mutable: false,
2360                            inner: Box::new(Type::Str),
2361                        }],
2362                    },
2363
2364                    // Formatting
2365                    "fmt" | "write_str" | "write_fmt" => Type::Named {
2366                        name: "Result".to_string(),
2367                        generics: vec![Type::Unit, self.fresh_var()],
2368                    },
2369
2370                    // IO methods
2371                    "read" | "write" | "flush" | "read_to_string" | "read_to_end"
2372                    | "read_line" | "write_all" => Type::Named {
2373                        name: "Result".to_string(),
2374                        generics: vec![self.fresh_var(), self.fresh_var()],
2375                    },
2376
2377                    // fs metadata
2378                    "metadata" | "modified" | "created" | "accessed" | "len_file"
2379                    | "is_readonly" | "permissions" => Type::Named {
2380                        name: "Result".to_string(),
2381                        generics: vec![self.fresh_var(), self.fresh_var()],
2382                    },
2383
2384                    // Map error
2385                    "map_err" | "and_then" | "or_else" => recv_inner.clone(),
2386
2387                    // and/or for Option/Result
2388                    "and" | "or" => recv_inner.clone(),
2389
2390                    // Default: return fresh type variable
2391                    // (user-defined methods were already checked above)
2392                    _ => self.fresh_var(),
2393                    }
2394                };
2395
2396                // Propagate evidence from receiver
2397                if recv_ev > EvidenceLevel::Known {
2398                    Type::Evidential {
2399                        inner: Box::new(result_ty),
2400                        evidence: recv_ev,
2401                    }
2402                } else {
2403                    result_ty
2404                }
2405            }
2406
2407            Expr::Field { expr, field } => {
2408                let recv_ty = self.infer_expr(expr);
2409                let (recv_inner, recv_ev) = self.strip_evidence(&recv_ty);
2410
2411                // Try to resolve field type from struct definition
2412                let field_ty = if let Type::Named { name, .. } = &recv_inner {
2413                    // Look up struct definition in type definitions
2414                    if let Some(struct_def) = self.types.get(name) {
2415                        if let TypeDef::Struct { fields, .. } = struct_def {
2416                            fields
2417                                .iter()
2418                                .find(|(n, _)| n == &field.name)
2419                                .map(|(_, ty)| ty.clone())
2420                                .unwrap_or_else(|| self.fresh_var())
2421                        } else {
2422                            self.fresh_var()
2423                        }
2424                    } else {
2425                        self.fresh_var()
2426                    }
2427                } else {
2428                    self.fresh_var()
2429                };
2430
2431                // Propagate evidence from receiver
2432                if recv_ev > EvidenceLevel::Known {
2433                    Type::Evidential {
2434                        inner: Box::new(field_ty),
2435                        evidence: recv_ev,
2436                    }
2437                } else {
2438                    field_ty
2439                }
2440            }
2441
2442            Expr::Index { expr, index, .. } => {
2443                let arr_ty = self.infer_expr(expr);
2444                let idx_ty = self.infer_expr(index);
2445                let (arr_inner, arr_ev) = self.strip_evidence(&arr_ty);
2446
2447                // Index should be usize
2448                let _ = self.unify(&idx_ty, &Type::Int(IntSize::USize));
2449
2450                // Get element type from array/slice
2451                let elem_ty = match arr_inner {
2452                    Type::Array { element, .. } => *element,
2453                    Type::Slice(element) => *element,
2454                    Type::Named { name, generics } if name == "Vec" && !generics.is_empty() => {
2455                        generics[0].clone()
2456                    }
2457                    _ => self.fresh_var(),
2458                };
2459
2460                // Propagate evidence
2461                if arr_ev > EvidenceLevel::Known {
2462                    Type::Evidential {
2463                        inner: Box::new(elem_ty),
2464                        evidence: arr_ev,
2465                    }
2466                } else {
2467                    elem_ty
2468                }
2469            }
2470
2471            Expr::Try(inner) => {
2472                // expr? unwraps Result<T, E> or Option<T> to T
2473                let inner_ty = self.infer_expr(inner);
2474                // Resolve type variables before matching
2475                let resolved = if let Type::Var(v) = &inner_ty {
2476                    self.substitutions.get(v).cloned().unwrap_or(inner_ty.clone())
2477                } else {
2478                    inner_ty.clone()
2479                };
2480                // Strip evidentiality wrapper (e.g., !Result<T,E> → Result<T,E>)
2481                let (stripped, _ev) = self.strip_evidence(&resolved);
2482                match &stripped {
2483                    Type::Named { name, generics } if name == "Result" && !generics.is_empty() => {
2484                        // Result<T, E>? → T with uncertain evidence
2485                        generics[0].clone()
2486                    }
2487                    Type::Named { name, generics } if name == "Option" && !generics.is_empty() => {
2488                        // Option<T>? → T with uncertain evidence
2489                        generics[0].clone()
2490                    }
2491                    _ => {
2492                        // For unresolved types, ? produces a fresh type variable
2493                        // (type inference will resolve it later)
2494                        self.fresh_var()
2495                    }
2496                }
2497            }
2498
2499            _ => {
2500                // Handle other expression types
2501                self.fresh_var()
2502            }
2503        }
2504    }
2505
2506    /// Infer type from literal
2507    fn infer_literal(&self, lit: &Literal) -> Type {
2508        match lit {
2509            Literal::Int { .. } => Type::Int(IntSize::I64),
2510            Literal::Float { suffix, .. } => {
2511                match suffix.as_ref().map(|s| s.as_str()) {
2512                    Some("f32") => Type::Float(FloatSize::F32),
2513                    Some("f64") => Type::Float(FloatSize::F64),
2514                    // Default to f64 for unsuffixed or other suffixes
2515                    None | Some(_) => Type::Float(FloatSize::F64),
2516                }
2517            }
2518            Literal::Bool(_) => Type::Bool,
2519            Literal::Char(_) => Type::Char,
2520            Literal::ByteChar(_) => Type::Int(IntSize::U8),
2521            // String literals have type &str
2522            Literal::String(_) => Type::Ref {
2523                lifetime: None,
2524                mutable: false,
2525                inner: Box::new(Type::Str),
2526            },
2527            Literal::MultiLineString(_) => Type::Ref {
2528                lifetime: None,
2529                mutable: false,
2530                inner: Box::new(Type::Str),
2531            },
2532            Literal::RawString(_) => Type::Ref {
2533                lifetime: None,
2534                mutable: false,
2535                inner: Box::new(Type::Str),
2536            },
2537            Literal::ByteString(bytes) => Type::Ref {
2538                lifetime: None,
2539                mutable: false,
2540                inner: Box::new(Type::Array {
2541                    element: Box::new(Type::Int(IntSize::U8)),
2542                    size: Some(bytes.len()),
2543                }),
2544            },
2545            Literal::InterpolatedString { .. } => Type::Str,
2546            Literal::SigilStringSql(_) => Type::Str,
2547            Literal::SigilStringRoute(_) => Type::Str,
2548            Literal::Null => Type::Unit, // null has unit type
2549            Literal::Empty => Type::Unit,
2550            Literal::Infinity => Type::Float(FloatSize::F64),
2551            Literal::Circle => Type::Float(FloatSize::F64),
2552        }
2553    }
2554
2555    /// Infer type of binary operation
2556    fn infer_binary_op(&mut self, op: &BinOp, left: &Type, right: &Type) -> Type {
2557        // Extract evidence levels for propagation
2558        let (left_inner, left_ev) = self.strip_evidence(left);
2559        let (right_inner, right_ev) = self.strip_evidence(right);
2560
2561        // Helper to detect type variable or function types (incomplete inference)
2562        let is_var_or_fn = |ty: &Type| {
2563            matches!(ty, Type::Var(_) | Type::Function { .. })
2564        };
2565
2566        let result_ty = match op {
2567            // Arithmetic: numeric -> numeric with coercion
2568            BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div | BinOp::Rem | BinOp::Pow => {
2569                // Numeric coercion: if either operand is float, result is float
2570                let is_left_float = matches!(&left_inner, Type::Float(_));
2571                let is_right_float = matches!(&right_inner, Type::Float(_));
2572
2573                if is_left_float || is_right_float {
2574                    // Result is the wider float type
2575                    if is_left_float { left_inner } else { right_inner }
2576                } else {
2577                    // Both are ints (or unknown) - try to unify
2578                    let _ = self.unify(&left_inner, &right_inner);
2579                    left_inner
2580                }
2581            }
2582
2583            // Matrix multiplication: tensor @ tensor -> tensor
2584            // Hadamard/element-wise: tensor ⊙ tensor -> tensor
2585            // Tensor product: tensor ⊗ tensor -> tensor
2586            BinOp::MatMul | BinOp::Hadamard | BinOp::TensorProd | BinOp::Convolve => {
2587                // Return a fresh type variable for now (proper tensor type checking would go here)
2588                self.fresh_var()
2589            }
2590
2591            // Comparison: any -> bool
2592            BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => {
2593                // Allow T == null/Unit comparisons (null-check idiom)
2594                let has_unit = |a: &Type, b: &Type| {
2595                    matches!(a, Type::Unit) || matches!(b, Type::Unit)
2596                };
2597                // Allow integer size coercion in comparisons (i32 == i64, etc.)
2598                let both_int = |a: &Type, b: &Type| {
2599                    matches!(a, Type::Int(_)) && matches!(b, Type::Int(_))
2600                };
2601                // Allow comparisons involving user-defined types where method return
2602                // types may not be fully inferred (e.g., IrEvidence.symbol() == "!")
2603                let has_named = |a: &Type, b: &Type| {
2604                    matches!(a, Type::Named { .. }) || matches!(b, Type::Named { .. })
2605                };
2606                // For bootstrapping: skip error when either side is a type variable or function
2607                // (indicates incomplete type inference from unhandled expressions)
2608                if !self.unify(&left_inner, &right_inner)
2609                    && !is_var_or_fn(&left_inner)
2610                    && !is_var_or_fn(&right_inner)
2611                    && !has_unit(&left_inner, &right_inner)
2612                    && !both_int(&left_inner, &right_inner)
2613                    && !has_named(&left_inner, &right_inner)
2614                {
2615                    self.error(TypeError::new(format!(
2616                        "comparison operands must have same type: left={:?}, right={:?}",
2617                        left_inner, right_inner
2618                    )));
2619                }
2620                Type::Bool
2621            }
2622
2623            // Logical: bool -> bool
2624            BinOp::And | BinOp::Or => {
2625                if !self.unify(&Type::Bool, &left_inner) {
2626                    self.error(TypeError::new("logical operand must be bool"));
2627                }
2628                if !self.unify(&Type::Bool, &right_inner) {
2629                    self.error(TypeError::new("logical operand must be bool"));
2630                }
2631                Type::Bool
2632            }
2633
2634            // Bitwise: int -> int
2635            BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor | BinOp::Shl | BinOp::Shr => left_inner,
2636
2637            // String concatenation
2638            BinOp::Concat => {
2639                if !self.unify(&Type::Str, &left_inner) {
2640                    self.error(TypeError::new("concat operand must be string"));
2641                }
2642                Type::Str
2643            }
2644        };
2645
2646        // Combine evidence levels
2647        let combined_ev = left_ev.join(right_ev);
2648
2649        // Wrap result in evidence if either operand had evidence
2650        if combined_ev > EvidenceLevel::Known {
2651            Type::Evidential {
2652                inner: Box::new(result_ty),
2653                evidence: combined_ev,
2654            }
2655        } else {
2656            result_ty
2657        }
2658    }
2659
2660    /// Infer type of unary operation
2661    fn infer_unary_op(&mut self, op: &UnaryOp, inner: &Type) -> Type {
2662        let (inner_ty, evidence) = self.strip_evidence(inner);
2663
2664        let result = match op {
2665            UnaryOp::Neg => inner_ty,
2666            UnaryOp::Not => {
2667                // ! operator: logical NOT for bool, bitwise NOT for integers
2668                if matches!(inner_ty, Type::Int(_)) {
2669                    // Bitwise NOT on integer types - returns same type
2670                    inner_ty
2671                } else {
2672                    if !self.unify(&Type::Bool, &inner_ty) {
2673                        self.error(TypeError::new(format!(
2674                            "type mismatch: '!' requires bool or integer, found {}",
2675                            inner_ty
2676                        )));
2677                    }
2678                    Type::Bool
2679                }
2680            }
2681            UnaryOp::Ref => Type::Ref {
2682                lifetime: None,
2683                mutable: false,
2684                inner: Box::new(inner_ty),
2685            },
2686            UnaryOp::RefMut => Type::Ref {
2687                lifetime: None,
2688                mutable: true,
2689                inner: Box::new(inner_ty),
2690            },
2691            UnaryOp::Deref => {
2692                if let Type::Ref { inner, .. } | Type::Ptr { inner, .. } = inner_ty {
2693                    *inner
2694                } else {
2695                    // For bootstrapping: return fresh type variable
2696                    self.fresh_var()
2697                }
2698            }
2699        };
2700
2701        // Preserve evidence
2702        if evidence > EvidenceLevel::Known {
2703            Type::Evidential {
2704                inner: Box::new(result),
2705                evidence,
2706            }
2707        } else {
2708            result
2709        }
2710    }
2711
2712    /// Infer type of pipe operation
2713    fn infer_pipe_op(&mut self, op: &PipeOp, input: &Type) -> Type {
2714        let (inner_ev_stripped, evidence) = self.strip_evidence(input);
2715
2716        // Also strip reference wrapper for pipe operations
2717        // This allows `&[T]` to be treated as `[T]` in pipes
2718        let inner = match inner_ev_stripped {
2719            Type::Ref { inner: ref_inner, .. } => (*ref_inner).clone(),
2720            other => other,
2721        };
2722
2723        let result = match op {
2724            // Transform: [T] -> [U] where body: T -> U
2725            PipeOp::Transform(_body) => {
2726                if let Type::Array { element, size } = inner {
2727                    Type::Array { element, size }
2728                } else if let Type::Slice(element) = inner {
2729                    Type::Slice(element)
2730                } else {
2731                    // For bootstrapping: return fresh type variable
2732                    self.fresh_var()
2733                }
2734            }
2735
2736            // Filter: [T] -> [T]
2737            PipeOp::Filter(_pred) => inner,
2738
2739            // Sort: [T] -> [T]
2740            PipeOp::Sort(_) | PipeOp::SortBy(_) => inner,
2741
2742            // Reduce: [T] -> T (also Vec<T> -> T)
2743            PipeOp::Reduce(_) | PipeOp::ReduceWithInit(_, _) => {
2744                if let Type::Array { element, .. } | Type::Slice(element) = inner {
2745                    *element
2746                } else if let Type::Named { name, generics } = &inner {
2747                    // Support Vec<T>, LinkedList<T>, etc.
2748                    if (name == "Vec" || name == "LinkedList" || name == "VecDeque")
2749                        && !generics.is_empty() {
2750                        generics[0].clone()
2751                    } else {
2752                        self.fresh_var()
2753                    }
2754                } else if let Type::Var(_) = inner {
2755                    // For bootstrapping: return fresh type variable when input is unknown
2756                    self.fresh_var()
2757                } else {
2758                    self.error(TypeError::new("reduce requires array or slice"));
2759                    Type::Error
2760                }
2761            }
2762            PipeOp::ReduceSum | PipeOp::ReduceProd | PipeOp::ReduceMin | PipeOp::ReduceMax => {
2763                // Numeric reductions return the element type
2764                let element = if let Type::Array { element, .. } | Type::Slice(element) = &inner {
2765                    Some(element.clone())
2766                } else if let Type::Named { name, generics } = &inner {
2767                    // Support Vec<T>, etc.
2768                    if (name == "Vec" || name == "LinkedList" || name == "VecDeque")
2769                        && !generics.is_empty() {
2770                        Some(Box::new(generics[0].clone()))
2771                    } else {
2772                        None
2773                    }
2774                } else {
2775                    None
2776                };
2777                if let Some(element) = element {
2778                    match element.as_ref() {
2779                        Type::Int(_) | Type::Float(_) => *element,
2780                        Type::Var(_) => *element, // For bootstrapping: allow type variables
2781                        _ => {
2782                            self.error(TypeError::new("numeric reduction requires numeric array"));
2783                            Type::Error
2784                        }
2785                    }
2786                } else if let Type::Var(_) = inner {
2787                    // For bootstrapping: return fresh type variable when input is unknown
2788                    self.fresh_var()
2789                } else {
2790                    self.error(TypeError::new("reduction requires array or slice"));
2791                    Type::Error
2792                }
2793            }
2794            PipeOp::ReduceConcat => {
2795                // Concat returns string or array depending on element type
2796                if let Type::Array { element, .. } | Type::Slice(element) = inner {
2797                    match element.as_ref() {
2798                        Type::Str => Type::Str,
2799                        Type::Array { .. } => *element,
2800                        Type::Var(_) => self.fresh_var(), // For bootstrapping
2801                        _ => {
2802                            self.error(TypeError::new(
2803                                "concat reduction requires array of strings or arrays",
2804                            ));
2805                            Type::Error
2806                        }
2807                    }
2808                } else if let Type::Var(_) = inner {
2809                    // For bootstrapping: return fresh type variable
2810                    self.fresh_var()
2811                } else {
2812                    self.error(TypeError::new("concat reduction requires array or slice"));
2813                    Type::Error
2814                }
2815            }
2816            PipeOp::ReduceAll | PipeOp::ReduceAny => {
2817                // Boolean reductions return bool
2818                if let Type::Array { element, .. } | Type::Slice(element) = inner {
2819                    match element.as_ref() {
2820                        Type::Bool => Type::Bool,
2821                        Type::Var(_) => Type::Bool, // For bootstrapping: assume bool
2822                        _ => {
2823                            self.error(TypeError::new(
2824                                "boolean reduction requires array of booleans",
2825                            ));
2826                            Type::Error
2827                        }
2828                    }
2829                } else if let Type::Var(_) = inner {
2830                    // For bootstrapping: return bool
2831                    Type::Bool
2832                } else {
2833                    self.error(TypeError::new("boolean reduction requires array or slice"));
2834                    Type::Error
2835                }
2836            }
2837
2838            // Match morpheme: |match{ Pattern => expr, ... }
2839            PipeOp::Match(arms) => {
2840                // All arms should return the same type
2841                if arms.is_empty() {
2842                    self.error(TypeError::new("match expression has no arms"));
2843                    Type::Error
2844                } else {
2845                    // Infer type from first arm, other arms should match
2846                    let result_type = self.infer_expr(&arms[0].body);
2847                    for arm in arms.iter().skip(1) {
2848                        let arm_type = self.infer_expr(&arm.body);
2849                        self.unify(&result_type, &arm_type);
2850                    }
2851                    result_type
2852                }
2853            }
2854
2855            // Try/Error transformation: |? or |?{mapper}
2856            PipeOp::TryMap(_) => {
2857                // Unwraps Result<T, E> to T or Option<T> to T
2858                // For now, return a fresh type variable
2859                // (proper implementation would extract inner type from Result/Option)
2860                self.fresh_var()
2861            }
2862
2863            // Call expression (like |self.layer)
2864            PipeOp::Call(callee) => {
2865                // Infer the type of the callee and extract return type
2866                let callee_ty = self.infer_expr(callee);
2867                if let Type::Function { return_type, .. } = callee_ty {
2868                    *return_type
2869                } else {
2870                    // Could be a callable struct or closure, return fresh var
2871                    self.fresh_var()
2872                }
2873            }
2874
2875            // Method call
2876            PipeOp::Method { name, type_args: _, args: _ } => {
2877                // Look up method
2878                if let Some(fn_ty) = self.functions.get(&name.name).cloned() {
2879                    // Freshen to get fresh type variables for polymorphic functions
2880                    let fresh_ty = self.freshen(&fn_ty);
2881                    if let Type::Function { return_type, .. } = fresh_ty {
2882                        *return_type
2883                    } else {
2884                        Type::Error
2885                    }
2886                } else {
2887                    // Could be a method on the type
2888                    self.fresh_var()
2889                }
2890            }
2891
2892            // Named operation (morpheme)
2893            PipeOp::Named { prefix, body: _ } => {
2894                // Named operations like |sum, |product
2895                if let Some(first) = prefix.first() {
2896                    match first.name.as_str() {
2897                        "sum" | "product" => {
2898                            if let Type::Array { element, .. } | Type::Slice(element) = inner {
2899                                *element
2900                            } else {
2901                                self.error(TypeError::new("sum/product requires array"));
2902                                Type::Error
2903                            }
2904                        }
2905                        _ => self.fresh_var(),
2906                    }
2907                } else {
2908                    self.fresh_var()
2909                }
2910            }
2911
2912            // Await: unwrap future
2913            PipeOp::Await => {
2914                // Future<T> -> T
2915                inner
2916            }
2917
2918            // Access morphemes: [T] -> T (return element type)
2919            PipeOp::First
2920            | PipeOp::Last
2921            | PipeOp::Middle
2922            | PipeOp::Choice
2923            | PipeOp::Nth(_)
2924            | PipeOp::Next => {
2925                if let Type::Array { element, .. } | Type::Slice(element) = inner {
2926                    *element
2927                } else if let Type::Named { name, generics } = &inner {
2928                    // Support Vec<T>, VecDeque<T>, etc.
2929                    if (name == "Vec" || name == "VecDeque" || name == "LinkedList")
2930                        && !generics.is_empty() {
2931                        generics[0].clone()
2932                    } else {
2933                        self.fresh_var()
2934                    }
2935                } else if let Type::Tuple(elements) = inner {
2936                    // For tuple, return Any since elements might be different types
2937                    if let Some(first) = elements.first() {
2938                        first.clone()
2939                    } else {
2940                        Type::Unit
2941                    }
2942                } else if let Type::Var(_) = inner {
2943                    // For bootstrapping: return fresh type variable
2944                    self.fresh_var()
2945                } else {
2946                    // For bootstrapping: allow access on unknown types, return fresh var
2947                    self.fresh_var()
2948                }
2949            }
2950
2951            // Parallel morpheme: ∥ - wraps another operation
2952            // Type is determined by the inner operation
2953            PipeOp::Parallel(inner_op) => self.infer_pipe_op(inner_op, input),
2954
2955            // GPU morpheme: ⊛ - wraps another operation for GPU execution
2956            // Type is determined by the inner operation
2957            PipeOp::Gpu(inner_op) => self.infer_pipe_op(inner_op, input),
2958
2959            // ==========================================
2960            // Protocol Operations - Sigil-native networking
2961            // All protocol results have Reported evidentiality
2962            // ==========================================
2963
2964            // Send: connection -> response (with Reported evidence)
2965            PipeOp::Send(_) => {
2966                // Returns response object with Reported evidentiality
2967                Type::Evidential {
2968                    inner: Box::new(self.fresh_var()),
2969                    evidence: EvidenceLevel::Reported,
2970                }
2971            }
2972
2973            // Recv: connection -> data (with Reported evidence)
2974            PipeOp::Recv => {
2975                // Returns received data with Reported evidentiality
2976                Type::Evidential {
2977                    inner: Box::new(self.fresh_var()),
2978                    evidence: EvidenceLevel::Reported,
2979                }
2980            }
2981
2982            // Stream: connection -> Stream<T> (elements have Reported evidence)
2983            PipeOp::Stream(_) => {
2984                // Returns a stream type
2985                self.fresh_var()
2986            }
2987
2988            // Connect: url/config -> connection
2989            PipeOp::Connect(_) => {
2990                // Returns connection object
2991                self.fresh_var()
2992            }
2993
2994            // Close: connection -> ()
2995            PipeOp::Close => Type::Unit,
2996
2997            // Header: request -> request (adds header)
2998            PipeOp::Header { .. } => inner,
2999
3000            // Body: request -> request (sets body)
3001            PipeOp::Body(_) => inner,
3002
3003            // Timeout: request -> request (sets timeout)
3004            PipeOp::Timeout(_) => inner,
3005
3006            // Retry: request -> request (sets retry policy)
3007            PipeOp::Retry { .. } => inner,
3008
3009            // ==========================================
3010            // Evidence Promotion Operations
3011            // ==========================================
3012
3013            // Validate: T~ -> T! (promotes with validation)
3014            PipeOp::Validate {
3015                predicate: _,
3016                target_evidence,
3017            } => {
3018                // Check that the predicate returns bool
3019                // (We'd need to infer the closure type properly, skipping for now)
3020
3021                let target_ev = EvidenceLevel::from_ast(*target_evidence);
3022
3023                // Validation can only promote evidence (make more certain)
3024                if evidence < target_ev {
3025                    self.error(
3026                        TypeError::new(format!(
3027                            "cannot demote evidence from {} ({}) to {} ({}) using validate",
3028                            evidence.name(),
3029                            evidence.symbol(),
3030                            target_ev.name(),
3031                            target_ev.symbol()
3032                        ))
3033                        .with_note("validate! can only promote evidence to a more certain level"),
3034                    );
3035                }
3036
3037                // Return inner type with promoted evidence
3038                return Type::Evidential {
3039                    inner: Box::new(inner.clone()),
3040                    evidence: target_ev,
3041                };
3042            }
3043
3044            // Assume: T~ -> T! (explicit trust with audit trail)
3045            PipeOp::Assume {
3046                reason: _,
3047                target_evidence,
3048            } => {
3049                let target_ev = EvidenceLevel::from_ast(*target_evidence);
3050
3051                // Assumption always succeeds but should be logged/audited
3052                // In a real implementation, this would record for security review
3053
3054                if evidence < target_ev {
3055                    self.error(
3056                        TypeError::new(format!(
3057                            "assume! cannot demote evidence from {} ({}) to {} ({})",
3058                            evidence.name(),
3059                            evidence.symbol(),
3060                            target_ev.name(),
3061                            target_ev.symbol()
3062                        ))
3063                        .with_note("assume! is for promoting evidence, not demoting"),
3064                    );
3065                }
3066
3067                // Return inner type with assumed evidence
3068                return Type::Evidential {
3069                    inner: Box::new(inner.clone()),
3070                    evidence: target_ev,
3071                };
3072            }
3073
3074            // AssertEvidence: compile-time evidence check
3075            PipeOp::AssertEvidence(expected_ast) => {
3076                let expected = EvidenceLevel::from_ast(*expected_ast);
3077
3078                if !evidence.satisfies(expected) {
3079                    self.error(
3080                        TypeError::new(format!(
3081                            "evidence assertion failed: expected {} ({}) or more certain, found {} ({})",
3082                            expected.name(), expected.symbol(),
3083                            evidence.name(), evidence.symbol()
3084                        ))
3085                        .with_note("use |validate!{...} or |assume! to promote evidence before assertion")
3086                    );
3087                }
3088
3089                // Return the same type (this is just an assertion)
3090                return input.clone();
3091            }
3092
3093            // ==========================================
3094            // Scope Functions (Kotlin-inspired)
3095            // ==========================================
3096
3097            // Also: execute side effect, return original value unchanged
3098            // T -> T (side effect executed but value preserved)
3099            PipeOp::Also(_) => {
3100                // The closure is executed for side effects only
3101                // Return type is same as input, evidence preserved
3102                return input.clone();
3103            }
3104
3105            // Apply: mutate value in place, return modified value
3106            // T -> T (value may be mutated)
3107            PipeOp::Apply(_) => {
3108                // The closure can mutate the value
3109                // Return type is same as input, evidence preserved
3110                return input.clone();
3111            }
3112
3113            // TakeIf: return Some(value) if predicate true, None otherwise
3114            // T -> Option<T>
3115            PipeOp::TakeIf(_) => {
3116                // Returns Option wrapping the input type
3117                // Evidence is preserved in the inner type
3118                return Type::Named {
3119                    name: "Option".to_string(),
3120                    generics: vec![input.clone()],
3121                };
3122            }
3123
3124            // TakeUnless: return Some(value) if predicate false, None otherwise
3125            // T -> Option<T>
3126            PipeOp::TakeUnless(_) => {
3127                // Returns Option wrapping the input type
3128                // Evidence is preserved in the inner type
3129                return Type::Named {
3130                    name: "Option".to_string(),
3131                    generics: vec![input.clone()],
3132                };
3133            }
3134
3135            // Let: transform value (alias for Transform/tau)
3136            // T -> U
3137            PipeOp::Let(func) => {
3138                // Same as Transform - applies function and returns result
3139                let _ = self.infer_expr(func);
3140                self.fresh_var() // Result type depends on function
3141            }
3142
3143            // Mathematical & APL-Inspired Operations
3144            PipeOp::All(_) | PipeOp::Any(_) => Type::Bool,
3145            PipeOp::Compose(f) => {
3146                let _ = self.infer_expr(f);
3147                self.fresh_var()
3148            }
3149            PipeOp::Zip(other) => {
3150                let _ = self.infer_expr(other);
3151                self.fresh_var() // Array of tuples
3152            }
3153            PipeOp::Scan(f) => {
3154                let _ = self.infer_expr(f);
3155                self.fresh_var() // Array of accumulated values
3156            }
3157            PipeOp::Diff => self.fresh_var(), // Array of differences
3158            PipeOp::Gradient(var) => {
3159                let _ = self.infer_expr(var);
3160                self.fresh_var() // Gradient value
3161            }
3162            PipeOp::SortAsc | PipeOp::SortDesc | PipeOp::Reverse => {
3163                inner.clone() // Same type, reordered
3164            }
3165            PipeOp::Cycle(n) | PipeOp::Windows(n) | PipeOp::Chunks(n) => {
3166                let _ = self.infer_expr(n);
3167                self.fresh_var() // Array type
3168            }
3169            PipeOp::Flatten | PipeOp::Unique => self.fresh_var(),
3170            PipeOp::Enumerate => self.fresh_var(), // Array of (index, value) tuples
3171
3172            // Holographic operations (Spec 11-HOLOGRAPHIC.md)
3173            PipeOp::Universal => {
3174                // |∀ - Universal reconstruction: [T] -> T (sum/aggregate)
3175                if let Type::Array { element, .. } | Type::Slice(element) = inner {
3176                    *element
3177                } else if let Type::Named { name, generics } = &inner {
3178                    if (name == "Vec" || name == "LinkedList" || name == "VecDeque")
3179                        && !generics.is_empty()
3180                    {
3181                        generics[0].clone()
3182                    } else {
3183                        self.fresh_var()
3184                    }
3185                } else {
3186                    self.fresh_var()
3187                }
3188            }
3189            PipeOp::Possibility { .. } => self.fresh_var(), // |◊method - approximate query result
3190            PipeOp::Necessity { .. } => self.fresh_var(),   // |□method - verified result
3191            PipeOp::PossibilityExtract => self.fresh_var(), // |◊ - extract from Option/Array
3192            PipeOp::NecessityVerify => inner,               // |□ - verify non-empty, pass through
3193        };
3194
3195        // Preserve evidence through pipe
3196        if evidence > EvidenceLevel::Known {
3197            Type::Evidential {
3198                inner: Box::new(result),
3199                evidence,
3200            }
3201        } else {
3202            result
3203        }
3204    }
3205
3206    /// Strip evidence wrapper, returning (inner_type, evidence_level)
3207    fn strip_evidence(&self, ty: &Type) -> (Type, EvidenceLevel) {
3208        match ty {
3209            Type::Evidential { inner, evidence } => (*inner.clone(), *evidence),
3210            _ => (ty.clone(), EvidenceLevel::Known),
3211        }
3212    }
3213
3214    /// Bind pattern variables with the given type and evidence level.
3215    /// This propagates evidence through pattern matching.
3216    fn bind_pattern(&mut self, pattern: &Pattern, ty: &Type, evidence: EvidenceLevel) {
3217        let (inner_ty, ty_ev) = self.strip_evidence(ty);
3218        // Use the more restrictive evidence level
3219        let final_ev = evidence.join(ty_ev);
3220
3221        match pattern {
3222            Pattern::Ident {
3223                name,
3224                evidentiality,
3225                ..
3226            } => {
3227                // Explicit evidence annotation overrides inference
3228                let ev = evidentiality
3229                    .map(EvidenceLevel::from_ast)
3230                    .unwrap_or(final_ev);
3231                self.env
3232                    .borrow_mut()
3233                    .define(name.name.clone(), inner_ty, ev);
3234            }
3235            Pattern::Tuple(patterns) => {
3236                if let Type::Tuple(types) = &inner_ty {
3237                    for (pat, ty) in patterns.iter().zip(types.iter()) {
3238                        self.bind_pattern(pat, ty, final_ev);
3239                    }
3240                }
3241            }
3242            Pattern::Struct { fields, .. } => {
3243                // For struct patterns, we'd need field type info
3244                // For now, bind with fresh vars
3245                for field in fields {
3246                    let fresh = self.fresh_var();
3247                    if let Some(ref pat) = field.pattern {
3248                        self.bind_pattern(pat, &fresh, final_ev);
3249                    } else {
3250                        self.env
3251                            .borrow_mut()
3252                            .define(field.name.name.clone(), fresh, final_ev);
3253                    }
3254                }
3255            }
3256            Pattern::TupleStruct { fields, .. } => {
3257                for pat in fields {
3258                    let fresh = self.fresh_var();
3259                    self.bind_pattern(pat, &fresh, final_ev);
3260                }
3261            }
3262            Pattern::Slice(patterns) => {
3263                let elem_ty = if let Type::Array { element, .. } | Type::Slice(element) = &inner_ty
3264                {
3265                    *element.clone()
3266                } else {
3267                    self.fresh_var()
3268                };
3269                for pat in patterns {
3270                    self.bind_pattern(pat, &elem_ty, final_ev);
3271                }
3272            }
3273            Pattern::Or(patterns) => {
3274                // For or-patterns, bind the same variables from any branch
3275                // (they should all have the same bindings)
3276                if let Some(first) = patterns.first() {
3277                    self.bind_pattern(first, ty, evidence);
3278                }
3279            }
3280            Pattern::Wildcard | Pattern::Rest | Pattern::Literal(_) | Pattern::Range { .. } | Pattern::Path(_) => {
3281                // These don't introduce bindings
3282            }
3283            Pattern::Ref { pattern, .. } => {
3284                // For reference patterns, bind the inner pattern
3285                // The inner type would be the deref'd type, but for now use a fresh var
3286                let inner_ty = self.fresh_var();
3287                self.bind_pattern(pattern, &inner_ty, final_ev);
3288            }
3289            Pattern::RefBinding {
3290                name,
3291                evidentiality,
3292                ..
3293            } => {
3294                // Ref binding - similar to Ident but binds by reference
3295                let ev = evidentiality
3296                    .map(EvidenceLevel::from_ast)
3297                    .unwrap_or(final_ev);
3298                self.env
3299                    .borrow_mut()
3300                    .define(name.name.clone(), inner_ty, ev);
3301            }
3302        }
3303    }
3304
3305    /// Resolve type aliases to their underlying types
3306    fn resolve_alias(&self, ty: &Type) -> Type {
3307        if let Type::Named { name, generics } = ty {
3308            if generics.is_empty() {
3309                if let Some(TypeDef::Alias { target, .. }) = self.types.get(name) {
3310                    return target.clone();
3311                }
3312            }
3313        }
3314        ty.clone()
3315    }
3316
3317    /// Attempt to unify two types
3318    fn unify(&mut self, a: &Type, b: &Type) -> bool {
3319        // Resolve type aliases first
3320        let a = self.resolve_alias(a);
3321        let b = self.resolve_alias(b);
3322
3323        match (&a, &b) {
3324            // Type variables - check these FIRST before other patterns
3325            (Type::Var(v), t) => {
3326                if let Some(resolved) = self.substitutions.get(v) {
3327                    let resolved = resolved.clone();
3328                    self.unify(&resolved, t)
3329                } else if !self.occurs_in(v, t) {
3330                    self.substitutions.insert(*v, t.clone());
3331                    true
3332                } else {
3333                    // Occurs check failed - cyclic type, just return true for bootstrapping
3334                    true
3335                }
3336            }
3337            (t, Type::Var(v)) => {
3338                if let Some(resolved) = self.substitutions.get(v) {
3339                    let resolved = resolved.clone();
3340                    self.unify(t, &resolved)
3341                } else if !self.occurs_in(v, t) {
3342                    self.substitutions.insert(*v, t.clone());
3343                    true
3344                } else {
3345                    // Occurs check failed - cyclic type, just return true for bootstrapping
3346                    true
3347                }
3348            }
3349
3350            // Same types
3351            (Type::Unit, Type::Unit) |
3352            (Type::Bool, Type::Bool) |
3353            (Type::Char, Type::Char) |
3354            (Type::Str, Type::Str) |
3355            (Type::Never, Type::Never) |
3356            (Type::Error, _) |
3357            (_, Type::Error) |
3358            // Never (bottom type) unifies with anything
3359            (Type::Never, _) |
3360            (_, Type::Never) => true,
3361
3362            // Linear type wrapper: Linear(T) unifies with T (linear is a usage qualifier)
3363            (Type::Linear(inner), other) => self.unify(inner, other),
3364            (other, Type::Linear(inner)) => self.unify(other, inner),
3365
3366            // For bootstrapping: allow integer literals to coerce to any integer type
3367            // This is lenient - a proper type system would have more precise rules
3368            (Type::Int(_), Type::Int(_)) => true,
3369            // For bootstrapping: allow float literals to coerce to any float type
3370            // This handles cases like `const X: f32 = 0.3;` where 0.3 infers as f64
3371            (Type::Float(_), Type::Float(_)) => true,
3372
3373            // For bootstrapping: allow &str to coerce to Str and vice versa
3374            (Type::Ref { mutable: false, inner: a, .. }, Type::Str) if matches!(a.as_ref(), Type::Str) => true,
3375            (Type::Str, Type::Ref { mutable: false, inner: b, .. }) if matches!(b.as_ref(), Type::Str) => true,
3376
3377            // For bootstrapping: allow String to coerce to &str (via Deref)
3378            // This allows passing String where &str is expected
3379            (Type::Named { name: n, .. }, Type::Ref { mutable: false, inner, .. })
3380                if n == "String" && matches!(inner.as_ref(), Type::Str) => true,
3381            (Type::Ref { mutable: false, inner, .. }, Type::Named { name: n, .. })
3382                if n == "String" && matches!(inner.as_ref(), Type::Str) => true,
3383
3384            // String to str coercion (deref-like): String ↔ str
3385            // Analogous to Rust's String → &str deref coercion.
3386            // String owns string data, str is a view — they're interchangeable
3387            // in type checking since the interpreter uses the same representation.
3388            (Type::Str, Type::Named { name, .. }) if name == "String" => true,
3389            (Type::Named { name, .. }, Type::Str) if name == "String" => true,
3390
3391            // Arrays
3392            (Type::Array { element: a, size: sa }, Type::Array { element: b, size: sb }) => {
3393                (sa == sb || sa.is_none() || sb.is_none()) && self.unify(a, b)
3394            }
3395
3396            // Slices
3397            (Type::Slice(a), Type::Slice(b)) => self.unify(a, b),
3398
3399            // Array to Slice coercion: [T; N] → [T]
3400            // A fixed-size array is always a valid slice of the same element type.
3401            (Type::Slice(a), Type::Array { element: b, .. }) => self.unify(a, b),
3402            (Type::Array { element: a, .. }, Type::Slice(b)) => self.unify(a, b),
3403
3404            // Tuples
3405            (Type::Tuple(a), Type::Tuple(b)) if a.len() == b.len() => {
3406                a.iter().zip(b.iter()).all(|(x, y)| self.unify(x, y))
3407            }
3408
3409            // References
3410            (Type::Ref { mutable: ma, inner: a, .. }, Type::Ref { mutable: mb, inner: b, .. }) => {
3411                // Allow &[T; N] to coerce to &[T] (array to slice)
3412                match (a.as_ref(), b.as_ref()) {
3413                    (Type::Array { element: ea, .. }, Type::Slice(es)) => {
3414                        (ma == mb || !ma) && self.unify(ea, es)
3415                    }
3416                    (Type::Slice(es), Type::Array { element: ea, .. }) => {
3417                        (ma == mb || !mb) && self.unify(es, ea)
3418                    }
3419                    _ => {
3420                        let mut_ok = ma == mb || (!ma && *mb) || (!mb && *ma);
3421                        if mut_ok && self.unify(a, b) {
3422                            return true;
3423                        }
3424                        // Auto-deref: &&T → &T (strip one layer of reference)
3425                        if let Type::Ref { inner: inner_b, .. } = b.as_ref() {
3426                            if self.unify(a, inner_b) {
3427                                return true;
3428                            }
3429                        }
3430                        if let Type::Ref { inner: inner_a, .. } = a.as_ref() {
3431                            if self.unify(inner_a, b) {
3432                                return true;
3433                            }
3434                        }
3435                        // Smart pointer deref: &Arc<T> → &T, &Rc<T> → &T, etc.
3436                        if let Type::Named { name, generics, .. } = b.as_ref() {
3437                            if matches!(name.as_str(), "Arc" | "Rc" | "Box" | "Cell" | "RefCell" | "Mutex")
3438                                && !generics.is_empty()
3439                            {
3440                                if self.unify(a, &generics[0]) {
3441                                    return true;
3442                                }
3443                            }
3444                        }
3445                        if let Type::Named { name, generics, .. } = a.as_ref() {
3446                            if matches!(name.as_str(), "Arc" | "Rc" | "Box" | "Cell" | "RefCell" | "Mutex")
3447                                && !generics.is_empty()
3448                            {
3449                                if self.unify(&generics[0], b) {
3450                                    return true;
3451                                }
3452                            }
3453                        }
3454                        false
3455                    }
3456                }
3457            }
3458
3459            // Functions
3460            (Type::Function { params: pa, return_type: ra, is_async: aa },
3461             Type::Function { params: pb, return_type: rb, is_async: ab }) => {
3462                aa == ab && pa.len() == pb.len() &&
3463                pa.iter().zip(pb.iter()).all(|(x, y)| self.unify(x, y)) &&
3464                self.unify(ra, rb)
3465            }
3466
3467            // Named types
3468            (Type::Named { name: na, generics: ga }, Type::Named { name: nb, generics: gb }) => {
3469                if na == nb {
3470                    // Same name, same arity: unify generics pairwise
3471                    if ga.len() == gb.len() {
3472                        return ga.iter().zip(gb.iter()).all(|(x, y)| self.unify(x, y));
3473                    }
3474                    // Bare type (0 generics) is compatible with the generic version
3475                    // e.g., `Tensor` (user wrote without generics) matches `Tensor<S, D, Dev>`
3476                    if ga.is_empty() || gb.is_empty() {
3477                        return true;
3478                    }
3479                    return false;
3480                }
3481                // Different names: check if either is a type parameter
3482                // Type parameters (single uppercase letter like T, N, M) unify with any type
3483                if (ga.is_empty() && Self::is_type_parameter(na))
3484                    || (gb.is_empty() && Self::is_type_parameter(nb)) {
3485                    return true;
3486                }
3487                false
3488            }
3489
3490            // Null (Unit) is assignable to any uncertain type (like None for Option<T>)
3491            (Type::Unit, Type::Evidential { evidence, .. })
3492                if *evidence == EvidenceLevel::Uncertain => true,
3493            (Type::Evidential { evidence, .. }, Type::Unit)
3494                if *evidence == EvidenceLevel::Uncertain => true,
3495
3496            // Evidential types: inner must unify, evidence can differ
3497            (Type::Evidential { inner: a, .. }, Type::Evidential { inner: b, .. }) => {
3498                self.unify(a, b)
3499            }
3500            (Type::Evidential { inner: a, .. }, b) => {
3501                self.unify(a, b)
3502            }
3503            (a, Type::Evidential { inner: b, .. }) => {
3504                self.unify(a, b)
3505            }
3506
3507            // Cycles
3508            (Type::Cycle { modulus: a }, Type::Cycle { modulus: b }) => a == b,
3509
3510            // ImplTrait: impl Trait bounds
3511            // Two impl Trait types unify if their bounds match
3512            (Type::ImplTrait(bounds_a), Type::ImplTrait(bounds_b)) => {
3513                bounds_a.len() == bounds_b.len() &&
3514                bounds_a.iter().zip(bounds_b.iter()).all(|(a, b)| self.unify(a, b))
3515            }
3516            // impl Trait acts as an existential type — it accepts any concrete type
3517            // that satisfies the bound. For type checking purposes, unify permissively.
3518            (Type::ImplTrait(_), _) | (_, Type::ImplTrait(_)) => true,
3519
3520            // For bootstrapping: treat type parameters (single uppercase letter names like T, U, E)
3521            // as compatible with any type. This allows generic functions to type check without
3522            // full generic instantiation support.
3523            (Type::Named { name, generics }, _) | (_, Type::Named { name, generics })
3524                if generics.is_empty() && Self::is_type_parameter(name) => {
3525                true
3526            }
3527
3528            // Auto ref/deref coercion: &T ↔ T
3529            // When one side is a reference and the other is not, try unifying the inner type
3530            (Type::Ref { inner: a, .. }, b) => self.unify(a, b),
3531            (a, Type::Ref { inner: b, .. }) => self.unify(a, b),
3532
3533            _ => false,
3534        }
3535    }
3536
3537    /// Check if a name looks like a type parameter (single uppercase letter or common generic names)
3538    fn is_type_parameter(name: &str) -> bool {
3539        // Single uppercase letter (T, U, E, K, V, etc.)
3540        if name.len() == 1 && name.chars().next().map(|c| c.is_ascii_uppercase()).unwrap_or(false) {
3541            return true;
3542        }
3543        // Common generic parameter names
3544        matches!(name, "Item" | "Output" | "Error" | "Key" | "Value" | "Idx" | "Self")
3545    }
3546
3547    /// Check if this is an allowed implicit numeric coercion (int → float)
3548    fn is_numeric_coercion(expected: &Type, actual: &Type) -> bool {
3549        // Allow integers to coerce to floats
3550        match (expected, actual) {
3551            (Type::Float(_), Type::Int(_)) => true,
3552            // Also allow through evidential wrappers
3553            (Type::Evidential { inner: exp, .. }, Type::Int(_)) => {
3554                matches!(exp.as_ref(), Type::Float(_))
3555            }
3556            (Type::Float(_), Type::Evidential { inner: act, .. }) => {
3557                matches!(act.as_ref(), Type::Int(_))
3558            }
3559            _ => false,
3560        }
3561    }
3562
3563    /// Check for reference coercions (reborrow, deref coercion, unsized coercion)
3564    fn is_reference_coercion(expected: &Type, actual: &Type) -> bool {
3565        // Extract inner types from references
3566        let (exp_inner, exp_mutable) = match expected {
3567            Type::Ref { inner, mutable, .. } => (inner.as_ref(), *mutable),
3568            _ => return false,
3569        };
3570        let (act_inner, act_mutable) = match actual {
3571            Type::Ref { inner, mutable, .. } => (inner.as_ref(), *mutable),
3572            _ => return false,
3573        };
3574
3575        // 1. Reborrow: &mut T → &T (mutable ref can become immutable ref)
3576        if !exp_mutable && act_mutable {
3577            // Compare inner types (ignoring mutability)
3578            if Self::types_structurally_equal(exp_inner, act_inner) {
3579                return true;
3580            }
3581        }
3582
3583        // 2. Deref coercion: &Box<T> → &T, &Arc<T> → &T, &Rc<T> → &T, etc.
3584        if let Type::Named { name, generics, .. } = act_inner {
3585            if matches!(name.as_str(), "Box" | "Arc" | "Rc" | "Cell" | "RefCell" | "Mutex")
3586                && !generics.is_empty()
3587            {
3588                if Self::types_structurally_equal(exp_inner, &generics[0]) {
3589                    return true;
3590                }
3591            }
3592        }
3593
3594        // 3. Unsized coercion: &Vec<T> → &[T]
3595        if let Type::Named { name, generics, .. } = act_inner {
3596            if name == "Vec" && !generics.is_empty() {
3597                if let Type::Slice(element) = exp_inner {
3598                    if Self::types_structurally_equal(element.as_ref(), &generics[0]) {
3599                        return true;
3600                    }
3601                }
3602            }
3603        }
3604
3605        // 4. Auto-deref: &&T → &T (strip one layer of reference from actual)
3606        if let Type::Ref { inner: act_inner_inner, .. } = act_inner {
3607            if Self::types_structurally_equal(exp_inner, act_inner_inner.as_ref()) {
3608                return true;
3609            }
3610        }
3611
3612        false
3613    }
3614
3615    /// Check if an implicit ref/deref coercion between non-reference types is valid.
3616    /// Handles: T → &T (auto-ref) and &T → T (auto-deref)
3617    fn is_ref_value_coercion(expected: &Type, actual: &Type) -> bool {
3618        // Auto-deref: &T → T (strip reference from actual to match expected value type)
3619        if let Type::Ref { inner, .. } = actual {
3620            if Self::types_structurally_equal(expected, inner.as_ref()) {
3621                return true;
3622            }
3623        }
3624        // Auto-ref: T → &T (expected is a reference, actual is a value)
3625        if let Type::Ref { inner, .. } = expected {
3626            if Self::types_structurally_equal(inner.as_ref(), actual) {
3627                return true;
3628            }
3629        }
3630        false
3631    }
3632
3633    /// Helper to compare types structurally (ignoring small differences)
3634    fn types_structurally_equal(a: &Type, b: &Type) -> bool {
3635        match (a, b) {
3636            (Type::Int(a_bits), Type::Int(b_bits)) => a_bits == b_bits,
3637            (Type::Float(a_bits), Type::Float(b_bits)) => a_bits == b_bits,
3638            (Type::Bool, Type::Bool) => true,
3639            (Type::Str, Type::Str) => true,
3640            (Type::Named { name: a_name, generics: a_gen, .. },
3641             Type::Named { name: b_name, generics: b_gen, .. }) => {
3642                a_name == b_name && a_gen.len() == b_gen.len() &&
3643                a_gen.iter().zip(b_gen.iter()).all(|(a, b)| Self::types_structurally_equal(a, b))
3644            }
3645            (Type::Slice(a_el), Type::Slice(b_el)) => {
3646                Self::types_structurally_equal(a_el, b_el)
3647            }
3648            (Type::Ref { inner: a_in, .. }, Type::Ref { inner: b_in, .. }) => {
3649                Self::types_structurally_equal(a_in, b_in)
3650            }
3651            (Type::Evidential { inner: a_in, .. }, Type::Evidential { inner: b_in, .. }) => {
3652                Self::types_structurally_equal(a_in, b_in)
3653            }
3654            // Allow evidential to match non-evidential for inner comparison
3655            (Type::Evidential { inner, .. }, other) | (other, Type::Evidential { inner, .. }) => {
3656                Self::types_structurally_equal(inner, other)
3657            }
3658            _ => false,
3659        }
3660    }
3661
3662    /// Convert AST type to internal type
3663    fn convert_type(&self, ty: &TypeExpr) -> Type {
3664        match ty {
3665            TypeExpr::Path(path) => {
3666                if path.segments.len() == 1 {
3667                    let name = &path.segments[0].ident.name;
3668                    match name.as_str() {
3669                        "bool" => return Type::Bool,
3670                        "char" => return Type::Char,
3671                        "str" | "String" => return Type::Str,
3672                        "i8" => return Type::Int(IntSize::I8),
3673                        "i16" => return Type::Int(IntSize::I16),
3674                        "i32" => return Type::Int(IntSize::I32),
3675                        "i64" => return Type::Int(IntSize::I64),
3676                        "i128" => return Type::Int(IntSize::I128),
3677                        "isize" => return Type::Int(IntSize::ISize),
3678                        "u8" => return Type::Int(IntSize::U8),
3679                        "u16" => return Type::Int(IntSize::U16),
3680                        "u32" => return Type::Int(IntSize::U32),
3681                        "u64" => return Type::Int(IntSize::U64),
3682                        "u128" => return Type::Int(IntSize::U128),
3683                        "usize" => return Type::Int(IntSize::USize),
3684                        "f32" => return Type::Float(FloatSize::F32),
3685                        "f64" => return Type::Float(FloatSize::F64),
3686                        // Handle Self type - resolve to current impl type (with generics)
3687                        "Self" => {
3688                            if let Some(ref self_ty) = self.current_self_type {
3689                                return self_ty.clone();
3690                            }
3691                        }
3692                        _ => {
3693                            // Check if this is a generic type parameter
3694                            if let Some(ty) = self.current_generics.get(name) {
3695                                return ty.clone();
3696                            }
3697                        }
3698                    }
3699                }
3700
3701                let name = path
3702                    .segments
3703                    .iter()
3704                    .map(|s| s.ident.name.clone())
3705                    .collect::<Vec<_>>()
3706                    .join("::");
3707
3708                let generics = path
3709                    .segments
3710                    .last()
3711                    .and_then(|s| s.generics.as_ref())
3712                    .map(|gs| gs.iter().map(|t| self.convert_type(t)).collect())
3713                    .unwrap_or_default();
3714
3715                Type::Named { name, generics }
3716            }
3717
3718            TypeExpr::Reference { lifetime, mutable, inner } => Type::Ref {
3719                lifetime: lifetime.clone(),
3720                mutable: *mutable,
3721                inner: Box::new(self.convert_type(inner)),
3722            },
3723
3724            TypeExpr::Pointer { mutable, inner } => Type::Ptr {
3725                mutable: *mutable,
3726                inner: Box::new(self.convert_type(inner)),
3727            },
3728
3729            TypeExpr::Array { element, size: _ } => {
3730                Type::Array {
3731                    element: Box::new(self.convert_type(element)),
3732                    size: None, // Could evaluate const expr
3733                }
3734            }
3735
3736            TypeExpr::Slice(inner) => Type::Slice(Box::new(self.convert_type(inner))),
3737
3738            TypeExpr::Tuple(elements) => {
3739                Type::Tuple(elements.iter().map(|t| self.convert_type(t)).collect())
3740            }
3741
3742            TypeExpr::Function {
3743                params,
3744                return_type,
3745            } => Type::Function {
3746                params: params.iter().map(|t| self.convert_type(t)).collect(),
3747                return_type: Box::new(
3748                    return_type
3749                        .as_ref()
3750                        .map(|t| self.convert_type(t))
3751                        .unwrap_or(Type::Unit),
3752                ),
3753                is_async: false,
3754            },
3755
3756            TypeExpr::Evidential {
3757                inner,
3758                evidentiality,
3759                error_type,
3760            } => {
3761                // If error_type is specified, this is sugar for Result<T, E>
3762                // For now, lower as evidential type; full expansion to Result comes later
3763                let _ = error_type; // TODO: expand T?[E] to Result<T, E> with evidence
3764                Type::Evidential {
3765                    inner: Box::new(self.convert_type(inner)),
3766                    evidence: EvidenceLevel::from_ast(*evidentiality),
3767                }
3768            }
3769
3770            TypeExpr::Cycle { modulus: _ } => {
3771                Type::Cycle { modulus: 12 } // Default, should evaluate
3772            }
3773
3774            TypeExpr::Simd { element, lanes } => {
3775                let elem_ty = self.convert_type(element);
3776                Type::Simd {
3777                    element: Box::new(elem_ty),
3778                    lanes: *lanes,
3779                }
3780            }
3781
3782            TypeExpr::Atomic(inner) => {
3783                let inner_ty = self.convert_type(inner);
3784                Type::Atomic(Box::new(inner_ty))
3785            }
3786
3787            TypeExpr::Never => Type::Never,
3788            TypeExpr::Infer => Type::Var(TypeVar(0)), // Fresh var
3789            TypeExpr::Lifetime(name) => Type::Lifetime(name.clone()),
3790            TypeExpr::TraitObject(bounds) => {
3791                let converted: Vec<Type> = bounds.iter().map(|b| self.convert_type(b)).collect();
3792                Type::TraitObject(converted)
3793            }
3794            TypeExpr::Hrtb { lifetimes, bound } => Type::Hrtb {
3795                lifetimes: lifetimes.clone(),
3796                bound: Box::new(self.convert_type(bound)),
3797            },
3798            TypeExpr::InlineStruct { fields } => Type::InlineStruct {
3799                fields: fields
3800                    .iter()
3801                    .map(|f| (f.name.name.clone(), self.convert_type(&f.ty)))
3802                    .collect(),
3803            },
3804            TypeExpr::ImplTrait(bounds) => {
3805                Type::ImplTrait(bounds.iter().map(|b| self.convert_type(b)).collect())
3806            }
3807            TypeExpr::InlineEnum { variants } => {
3808                Type::InlineEnum(variants.iter().map(|v| v.name.name.clone()).collect())
3809            }
3810            TypeExpr::AssocTypeBinding { name, ty } => Type::AssocTypeBinding {
3811                name: name.name.clone(),
3812                ty: Box::new(self.convert_type(ty)),
3813            },
3814            TypeExpr::ConstExpr(_) => {
3815                // Const expressions in type position (const generics)
3816                // For now, treat as an inferred/opaque type
3817                Type::Var(TypeVar(0))
3818            }
3819            TypeExpr::QualifiedPath { self_type, trait_path, item_path } => {
3820                // Qualified path: <Type as Trait>::AssociatedType
3821                // For now, represent as a named type with a synthesized name
3822                let trait_part = trait_path.as_ref()
3823                    .map(|tp| tp.segments.iter().map(|s| s.ident.name.clone()).collect::<Vec<_>>().join("::"))
3824                    .unwrap_or_default();
3825                let item_part = item_path.segments.iter().map(|s| s.ident.name.clone()).collect::<Vec<_>>().join("::");
3826                let name = if trait_part.is_empty() {
3827                    format!("<_>::{}", item_part)
3828                } else {
3829                    format!("<_ as {}>::{}", trait_part, item_part)
3830                };
3831                Type::Named {
3832                    name,
3833                    generics: vec![self.convert_type(self_type)],
3834                }
3835            }
3836            // Linear/affine/relevant type modifiers - wrap the inner type
3837            // Linear types enforce the no-cloning theorem at compile time
3838            TypeExpr::Linear(inner) => Type::Linear(Box::new(self.convert_type(inner))),
3839            TypeExpr::Affine(inner) => Type::Affine(Box::new(self.convert_type(inner))),
3840            TypeExpr::Relevant(inner) => Type::Relevant(Box::new(self.convert_type(inner))),
3841        }
3842    }
3843
3844    /// Get errors
3845    pub fn errors(&self) -> &[TypeError] {
3846        &self.errors
3847    }
3848}
3849
3850impl Default for TypeChecker {
3851    fn default() -> Self {
3852        Self::new()
3853    }
3854}
3855
3856// Helper trait for Pattern
3857trait PatternExt {
3858    fn evidentiality(&self) -> Option<Evidentiality>;
3859    fn binding_name(&self) -> Option<String>;
3860    fn binding_span(&self) -> Option<Span>;
3861}
3862
3863impl PatternExt for Pattern {
3864    fn evidentiality(&self) -> Option<Evidentiality> {
3865        match self {
3866            Pattern::Ident { evidentiality, .. } => *evidentiality,
3867            _ => None,
3868        }
3869    }
3870
3871    fn binding_name(&self) -> Option<String> {
3872        match self {
3873            Pattern::Ident { name, .. } => Some(name.name.clone()),
3874            _ => None,
3875        }
3876    }
3877
3878    fn binding_span(&self) -> Option<Span> {
3879        match self {
3880            Pattern::Ident { name, .. } => Some(name.span),
3881            _ => None,
3882        }
3883    }
3884}
3885
3886impl fmt::Display for Type {
3887    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3888        match self {
3889            Type::Unit => write!(f, "()"),
3890            Type::Bool => write!(f, "bool"),
3891            Type::Int(size) => write!(f, "{:?}", size),
3892            Type::Float(size) => write!(f, "{:?}", size),
3893            Type::Char => write!(f, "char"),
3894            Type::Str => write!(f, "str"),
3895            Type::Array { element, size } => {
3896                if let Some(n) = size {
3897                    write!(f, "[{}; {}]", element, n)
3898                } else {
3899                    write!(f, "[{}]", element)
3900                }
3901            }
3902            Type::Slice(inner) => write!(f, "[{}]", inner),
3903            Type::Tuple(elems) => {
3904                write!(f, "(")?;
3905                for (i, e) in elems.iter().enumerate() {
3906                    if i > 0 {
3907                        write!(f, ", ")?;
3908                    }
3909                    write!(f, "{}", e)?;
3910                }
3911                write!(f, ")")
3912            }
3913            Type::Named { name, generics } => {
3914                write!(f, "{}", name)?;
3915                if !generics.is_empty() {
3916                    write!(f, "<")?;
3917                    for (i, g) in generics.iter().enumerate() {
3918                        if i > 0 {
3919                            write!(f, ", ")?;
3920                        }
3921                        write!(f, "{}", g)?;
3922                    }
3923                    write!(f, ">")?;
3924                }
3925                Ok(())
3926            }
3927            Type::Function {
3928                params,
3929                return_type,
3930                is_async,
3931            } => {
3932                if *is_async {
3933                    write!(f, "async ")?;
3934                }
3935                write!(f, "fn(")?;
3936                for (i, p) in params.iter().enumerate() {
3937                    if i > 0 {
3938                        write!(f, ", ")?;
3939                    }
3940                    write!(f, "{}", p)?;
3941                }
3942                write!(f, ") -> {}", return_type)
3943            }
3944            Type::Ref { lifetime, mutable, inner } => {
3945                let lt = lifetime.as_ref().map(|l| format!("'{} ", l)).unwrap_or_default();
3946                write!(f, "&{}{}{}", lt, if *mutable { "mut " } else { "" }, inner)
3947            }
3948            Type::Ptr { mutable, inner } => {
3949                write!(f, "*{}{}", if *mutable { "mut " } else { "const " }, inner)
3950            }
3951            Type::Evidential { inner, evidence } => {
3952                write!(f, "{}{}", inner, evidence.symbol())
3953            }
3954            Type::Cycle { modulus } => write!(f, "Cycle<{}>", modulus),
3955            Type::Var(v) => write!(f, "?{}", v.0),
3956            Type::Error => write!(f, "<error>"),
3957            Type::Never => write!(f, "!"),
3958            Type::Simd { element, lanes } => write!(f, "simd<{}, {}>", element, lanes),
3959            Type::Atomic(inner) => write!(f, "atomic<{}>", inner),
3960            Type::Lifetime(name) => write!(f, "'{}", name),
3961            Type::TraitObject(bounds) => {
3962                write!(f, "dyn ")?;
3963                for (i, bound) in bounds.iter().enumerate() {
3964                    if i > 0 {
3965                        write!(f, " + ")?;
3966                    }
3967                    write!(f, "{}", bound)?;
3968                }
3969                Ok(())
3970            }
3971            Type::Hrtb { lifetimes, bound } => {
3972                write!(f, "for<")?;
3973                for (i, lt) in lifetimes.iter().enumerate() {
3974                    if i > 0 {
3975                        write!(f, ", ")?;
3976                    }
3977                    write!(f, "'{}", lt)?;
3978                }
3979                write!(f, "> {}", bound)
3980            }
3981            Type::InlineStruct { fields } => {
3982                write!(f, "struct {{ ")?;
3983                for (i, (name, ty)) in fields.iter().enumerate() {
3984                    if i > 0 {
3985                        write!(f, ", ")?;
3986                    }
3987                    write!(f, "{}: {}", name, ty)?;
3988                }
3989                write!(f, " }}")
3990            }
3991            Type::ImplTrait(bounds) => {
3992                write!(f, "impl ")?;
3993                for (i, bound) in bounds.iter().enumerate() {
3994                    if i > 0 {
3995                        write!(f, " + ")?;
3996                    }
3997                    write!(f, "{}", bound)?;
3998                }
3999                Ok(())
4000            }
4001            Type::InlineEnum(variants) => {
4002                write!(f, "enum {{ ")?;
4003                for (i, name) in variants.iter().enumerate() {
4004                    if i > 0 {
4005                        write!(f, ", ")?;
4006                    }
4007                    write!(f, "{}", name)?;
4008                }
4009                write!(f, " }}")
4010            }
4011            Type::AssocTypeBinding { name, ty } => {
4012                write!(f, "{} = {}", name, ty)
4013            }
4014            // Linear type modifiers for quantum computing
4015            Type::Linear(inner) => write!(f, "linear {}", inner),
4016            Type::Affine(inner) => write!(f, "affine {}", inner),
4017            Type::Relevant(inner) => write!(f, "relevant {}", inner),
4018        }
4019    }
4020}
4021
4022#[cfg(test)]
4023mod tests {
4024    use super::*;
4025    use crate::Parser;
4026
4027    fn check(source: &str) -> Result<(), Vec<TypeError>> {
4028        let mut parser = Parser::new(source);
4029        let file = parser.parse_file().expect("parse failed");
4030        let mut checker = TypeChecker::new();
4031        checker.check_file(&file)
4032    }
4033
4034    #[test]
4035    fn test_basic_types() {
4036        assert!(check("rite main() { ≔ x: i64 = 42; }").is_ok());
4037        assert!(check("rite main() { ≔ x: bool = true; }").is_ok());
4038        assert!(check("rite main() { ≔ x: f64 = 3.14; }").is_ok());
4039    }
4040
4041    #[test]
4042    fn test_type_mismatch() {
4043        assert!(check("rite main() { ≔ x: bool = 42; }").is_err());
4044    }
4045
4046    #[test]
4047    fn test_evidence_propagation() {
4048        // Evidence should propagate through operations
4049        assert!(check(
4050            r#"
4051            rite main() {
4052                ≔ known: i64! = 42;
4053                ≔ uncertain: i64? = 10;
4054                ≔ result = known + uncertain;
4055            }
4056        "#
4057        )
4058        .is_ok());
4059    }
4060
4061    #[test]
4062    fn test_function_return() {
4063        let result = check(
4064            r#"
4065            rite add(a: i64, b: i64) -> i64 {
4066                ⤺ a + b;
4067            }
4068            rite main() {
4069                ≔ x = add(1, 2);
4070            }
4071        "#,
4072        );
4073        if let Err(errors) = &result {
4074            for e in errors {
4075                eprintln!("Error: {}", e);
4076            }
4077        }
4078        assert!(result.is_ok());
4079    }
4080
4081    #[test]
4082    fn test_array_types() {
4083        assert!(check(
4084            r#"
4085            rite main() {
4086                ≔ arr = [1, 2, 3];
4087                ≔ x = arr[0];
4088            }
4089        "#
4090        )
4091        .is_ok());
4092    }
4093
4094    // ==========================================
4095    // Evidence Inference Tests
4096    // ==========================================
4097
4098    #[test]
4099    fn test_evidence_inference_from_initializer() {
4100        // Evidence should be inferred from initializer when not explicitly annotated
4101        assert!(check(
4102            r#"
4103            rite main() {
4104                ≔ reported_val: i64~ = 42;
4105                // x should inherit ~ evidence from reported_val
4106                ≔ x = reported_val + 1;
4107            }
4108        "#
4109        )
4110        .is_ok());
4111    }
4112
4113    #[test]
4114    fn test_evidence_inference_explicit_override() {
4115        // Explicit annotation should override inference
4116        assert!(check(
4117            r#"
4118            rite main() {
4119                ≔ reported_val: i64~ = 42;
4120                // Explicit ! annotation - this would fail ⎇ we checked evidence properly
4121                // but the type system allows it as an override
4122                ≔ x! = 42;
4123            }
4124        "#
4125        )
4126        .is_ok());
4127    }
4128
4129    #[test]
4130    fn test_if_else_evidence_join() {
4131        // Evidence from both branches should be joined
4132        assert!(check(
4133            r#"
4134            rite main() {
4135                ≔ known_val: i64! = 1;
4136                ≔ reported_val: i64~ = 2;
4137                ≔ cond: bool = true;
4138                // Result should have ~ evidence (join of ! and ~)
4139                ≔ result = ⎇ cond { known_val } ⎉ { reported_val };
4140            }
4141        "#
4142        )
4143        .is_ok());
4144    }
4145
4146    #[test]
4147    fn test_binary_op_evidence_propagation() {
4148        // Binary operations should join evidence levels
4149        assert!(check(
4150            r#"
4151            rite main() {
4152                ≔ known: i64! = 1;
4153                ≔ reported: i64~ = 2;
4154                // Result should have ~ evidence (max of ! and ~)
4155                ≔ result = known + reported;
4156            }
4157        "#
4158        )
4159        .is_ok());
4160    }
4161
4162    #[test]
4163    fn test_match_evidence_join() {
4164        // Match arms should join evidence from all branches
4165        // Note: This test is structural - the type checker should handle it
4166        assert!(check(
4167            r#"
4168            rite main() {
4169                ≔ x: i64 = 1;
4170            }
4171        "#
4172        )
4173        .is_ok());
4174    }
4175}