1use std::collections::{HashMap, HashSet};
22
23use crate::analysis::unify::{InferType, TyVar, TypeScheme, TypeError, UnificationTable, infer_to_logos, unify_numeric};
24use crate::analysis::{FnSig, LogosType, TypeDef, TypeEnv, TypeRegistry};
25use crate::ast::stmt::{BinaryOpKind, Expr, Pattern, Stmt};
26use crate::intern::{Interner, Symbol};
27
28#[derive(Clone, Debug)]
38struct FunctionRecord {
39 param_names: Vec<Symbol>,
41 scheme: TypeScheme,
44}
45
46struct CheckEnv<'r> {
51 scopes: Vec<HashMap<Symbol, InferType>>,
53 all_vars: HashMap<Symbol, InferType>,
55 functions: HashMap<Symbol, FunctionRecord>,
57 current_return_type: Option<InferType>,
59 table: UnificationTable,
61 registry: &'r TypeRegistry,
62 interner: &'r Interner,
63}
64
65impl<'r> CheckEnv<'r> {
66 fn new(registry: &'r TypeRegistry, interner: &'r Interner) -> Self {
67 Self {
68 scopes: vec![HashMap::new()],
69 all_vars: HashMap::new(),
70 functions: HashMap::new(),
71 current_return_type: None,
72 table: UnificationTable::new(),
73 registry,
74 interner,
75 }
76 }
77
78 fn push_scope(&mut self) {
79 self.scopes.push(HashMap::new());
80 }
81
82 fn pop_scope(&mut self) {
83 self.scopes.pop();
84 }
85
86 fn bind_var(&mut self, sym: Symbol, ty: InferType) {
88 if let Some(scope) = self.scopes.last_mut() {
89 scope.insert(sym, ty.clone());
90 }
91 self.all_vars.insert(sym, ty);
92 }
93
94 fn lookup_var(&self, sym: Symbol) -> Option<InferType> {
100 for scope in self.scopes.iter().rev() {
101 if let Some(ty) = scope.get(&sym) {
102 return Some(self.table.resolve(ty));
103 }
104 }
105 None
106 }
107
108 fn into_type_env(self) -> TypeEnv {
110 let mut type_env = TypeEnv::new();
111
112 for (sym, ty) in self.all_vars {
114 let logos_ty = self.table.to_logos_type(&ty);
115 type_env.register(sym, logos_ty);
116 }
117
118 for (name, rec) in self.functions {
120 if let InferType::Function(param_types, ret_box) = &rec.scheme.body {
124 let ret_logos = self.table.to_logos_type(ret_box);
125 let params: Vec<(Symbol, LogosType)> = rec.param_names.iter()
126 .zip(param_types.iter())
127 .map(|(sym, ty)| (*sym, self.table.to_logos_type(ty)))
128 .collect();
129 type_env.register_fn(name, FnSig { params, return_type: ret_logos });
130 }
131 }
132
133 type_env
134 }
135}
136
137impl<'r> CheckEnv<'r> {
142 fn preregister_functions(&mut self, stmts: &[Stmt]) {
150 for stmt in stmts {
151 if let Stmt::FunctionDef { name, generics, params, return_type, .. } = stmt {
152 let type_param_map: HashMap<Symbol, TyVar> = generics
154 .iter()
155 .map(|&sym| (sym, self.table.fresh_var()))
156 .collect();
157
158 let param_types: Vec<InferType> = params
159 .iter()
160 .map(|(_, ty_expr)| {
161 InferType::from_type_expr_with_params(ty_expr, self.interner, &type_param_map)
162 })
163 .collect();
164 let param_names: Vec<Symbol> = params.iter().map(|(sym, _)| *sym).collect();
165
166 let ret_type = if let Some(rt) = return_type {
167 InferType::from_type_expr_with_params(rt, self.interner, &type_param_map)
168 } else {
169 self.table.fresh()
170 };
171
172 let generic_vars: Vec<TyVar> = generics
173 .iter()
174 .filter_map(|sym| type_param_map.get(sym).copied())
175 .collect();
176
177 let scheme = TypeScheme {
178 vars: generic_vars,
179 body: InferType::Function(param_types, Box::new(ret_type)),
180 };
181
182 self.functions.insert(*name, FunctionRecord { param_names, scheme });
183 }
184 }
185 }
186}
187
188impl<'r> CheckEnv<'r> {
193 fn check_expr(
198 &mut self,
199 expr: &Expr,
200 expected: &InferType,
201 ) -> Result<InferType, TypeError> {
202 use crate::ast::stmt::Literal;
203
204 if let Expr::Literal(Literal::Number(_)) = expr {
206 match expected {
207 InferType::Float => return Ok(InferType::Float),
208 InferType::Nat => return Ok(InferType::Nat),
209 InferType::Int => return Ok(InferType::Int),
210 InferType::Byte => return Ok(InferType::Byte),
211 _ => {}
212 }
213 }
214
215 if let Expr::Literal(Literal::Nothing) = expr {
218 if let InferType::Option(_) = expected {
219 return Ok(expected.clone());
220 }
221 }
222
223 if let Expr::List(items) = expr {
228 if let InferType::Seq(elem) = self.table.zonk(expected) {
229 for item in items {
230 self.check_expr(item, &elem)?;
231 }
232 return Ok(InferType::Seq(elem));
233 }
234 }
235
236 let inferred = self.infer_expr(expr)?;
238 self.table.unify(&inferred, expected)?;
239 Ok(self.table.zonk(expected))
240 }
241
242 fn infer_expr(&mut self, expr: &Expr) -> Result<InferType, TypeError> {
244 match expr {
245 Expr::Literal(lit) => Ok(InferType::from_literal(lit)),
246
247 Expr::Identifier(sym) => {
248 Ok(self.lookup_var(*sym).unwrap_or(InferType::Unknown))
249 }
250
251 Expr::BinaryOp { op, left, right } => {
252 self.infer_binary_op(*op, left, right)
253 }
254
255 Expr::Length { .. } => Ok(InferType::Int),
256
257 Expr::Call { function, args } => {
258 self.infer_call(*function, args)
259 }
260
261 Expr::Index { collection, .. } => {
262 let coll_ty = self.infer_expr(collection)?;
263 let walked = self.table.zonk(&coll_ty);
264 match walked {
265 InferType::Seq(inner) => Ok(*inner),
266 InferType::Map(_, v) => Ok(*v),
267 _ => Ok(InferType::Unknown),
268 }
269 }
270
271 Expr::List(items) => {
272 if items.is_empty() {
273 let elem_var = self.table.fresh();
274 Ok(InferType::Seq(Box::new(elem_var)))
275 } else {
276 let elem_type = self.infer_expr(items[0])?;
277 Ok(InferType::Seq(Box::new(elem_type)))
278 }
279 }
280
281 Expr::OptionSome { value } => {
282 let inner = self.infer_expr(value)?;
283 Ok(InferType::Option(Box::new(inner)))
284 }
285
286 Expr::OptionNone => {
287 let elem_var = self.table.fresh();
288 Ok(InferType::Option(Box::new(elem_var)))
289 }
290
291 Expr::Range { .. } => Ok(InferType::Seq(Box::new(InferType::Int))),
292
293 Expr::Contains { .. } => Ok(InferType::Bool),
294
295 Expr::Copy { expr: inner } | Expr::Give { value: inner } => {
296 self.infer_expr(inner)
297 }
298
299 Expr::WithCapacity { value, .. } => self.infer_expr(value),
300
301 Expr::FieldAccess { object, field } => {
302 let obj_ty = self.infer_expr(object)?;
303 self.infer_field_access(obj_ty, *field)
304 }
305
306 Expr::New { type_name, type_args, .. } => {
307 let name = self.interner.resolve(*type_name);
308 match name {
309 "Seq" | "List" | "Vec" => {
310 let elem = type_args
311 .first()
312 .map(|t| InferType::from_type_expr(t, self.interner))
313 .unwrap_or_else(|| self.table.fresh());
314 Ok(InferType::Seq(Box::new(elem)))
315 }
316 "Map" | "HashMap" => {
317 let key = type_args
318 .first()
319 .map(|t| InferType::from_type_expr(t, self.interner))
320 .unwrap_or(InferType::String);
321 let val = type_args
322 .get(1)
323 .map(|t| InferType::from_type_expr(t, self.interner))
324 .unwrap_or(InferType::String);
325 Ok(InferType::Map(Box::new(key), Box::new(val)))
326 }
327 "Set" | "HashSet" => {
328 let elem = type_args
329 .first()
330 .map(|t| InferType::from_type_expr(t, self.interner))
331 .unwrap_or_else(|| self.table.fresh());
332 Ok(InferType::Set(Box::new(elem)))
333 }
334 _ => Ok(InferType::UserDefined(*type_name)),
335 }
336 }
337
338 Expr::NewVariant { enum_name, .. } => {
339 Ok(InferType::UserDefined(*enum_name))
340 }
341
342 Expr::CallExpr { callee, args } => {
343 self.infer_call_expr(callee, args)
344 }
345
346 Expr::Closure { params, body: closure_body, return_type } => {
347 self.infer_closure(params, closure_body, return_type)
348 }
349
350 Expr::InterpolatedString(_) => Ok(InferType::String),
351
352 Expr::Slice { collection, .. } => self.infer_expr(collection),
353
354 Expr::Union { left, .. } | Expr::Intersection { left, .. } => {
355 self.infer_expr(left)
356 }
357
358 _ => Ok(InferType::Unknown),
360 }
361 }
362
363 fn infer_binary_op(
365 &mut self,
366 op: BinaryOpKind,
367 left: &Expr,
368 right: &Expr,
369 ) -> Result<InferType, TypeError> {
370 match op {
371 BinaryOpKind::Eq
372 | BinaryOpKind::NotEq
373 | BinaryOpKind::Lt
374 | BinaryOpKind::Gt
375 | BinaryOpKind::LtEq
376 | BinaryOpKind::GtEq
377 | BinaryOpKind::ApproxEq => Ok(InferType::Bool),
378
379 BinaryOpKind::And | BinaryOpKind::Or => {
381 let lt = self.infer_expr(left)?;
382 if lt == InferType::Int {
383 Ok(InferType::Int)
384 } else {
385 Ok(InferType::Bool)
386 }
387 }
388
389 BinaryOpKind::Concat => Ok(InferType::String),
390
391 BinaryOpKind::SeqConcat => self.infer_expr(left),
393
394 BinaryOpKind::BitXor | BinaryOpKind::BitAnd | BinaryOpKind::BitOr
395 | BinaryOpKind::Shl | BinaryOpKind::Shr => Ok(InferType::Int),
396
397 BinaryOpKind::Add => {
398 let lt = self.infer_expr(left)?;
399 let rt = self.infer_expr(right)?;
400 if lt == InferType::String || rt == InferType::String {
401 Ok(InferType::String)
402 } else if lt == InferType::Unknown || rt == InferType::Unknown {
403 Ok(InferType::Unknown)
404 } else {
405 unify_numeric(<, &rt).or(Ok(InferType::Unknown))
406 }
407 }
408
409 BinaryOpKind::ExactDivide => {
412 self.infer_expr(left)?;
413 self.infer_expr(right)?;
414 Ok(InferType::Rational)
415 }
416
417 BinaryOpKind::Subtract
418 | BinaryOpKind::Multiply
419 | BinaryOpKind::Divide
420 | BinaryOpKind::FloorDivide
421 | BinaryOpKind::Modulo
422 | BinaryOpKind::Pow => {
423 let lt = self.infer_expr(left)?;
424 let rt = self.infer_expr(right)?;
425 if lt == InferType::Unknown || rt == InferType::Unknown {
426 Ok(InferType::Unknown)
427 } else {
428 unify_numeric(<, &rt).or(Ok(InferType::Unknown))
429 }
430 }
431 }
432 }
433
434 fn infer_call(&mut self, function: Symbol, args: &[&Expr]) -> Result<InferType, TypeError> {
440 let name = self.interner.resolve(function);
441 match name {
442 "sqrt" | "parseFloat" | "pow" => Ok(InferType::Float),
443 "parseInt" | "floor" | "ceil" | "round" => Ok(InferType::Int),
444 "abs" | "min" | "max" => {
445 if let Some(first) = args.first() {
446 self.infer_expr(first)
447 } else {
448 Ok(InferType::Unknown)
449 }
450 }
451 _ => {
452 if let Some(rec) = self.functions.get(&function).cloned() {
453 let instantiated = self.table.instantiate(&rec.scheme);
456
457 if let InferType::Function(param_types, ret_box) = instantiated {
458 for (arg, param_ty) in args.iter().zip(param_types.iter()) {
460 let arg_ty = self.infer_expr(arg)?;
461 self.table.unify(&arg_ty, param_ty)?;
462 }
463 Ok(self.table.zonk(&ret_box))
464 } else {
465 Ok(InferType::Unknown)
467 }
468 } else {
469 Ok(InferType::Unknown)
470 }
471 }
472 }
473 }
474
475 fn infer_call_expr(
477 &mut self,
478 callee: &Expr,
479 args: &[&Expr],
480 ) -> Result<InferType, TypeError> {
481 let callee_ty = self.infer_expr(callee)?;
482 let ret_var = self.table.fresh();
483 let arg_types: Vec<InferType> = args
484 .iter()
485 .map(|a| self.infer_expr(a))
486 .collect::<Result<_, _>>()?;
487 let fn_ty = InferType::Function(arg_types, Box::new(ret_var.clone()));
488
489 let walked = self.table.zonk(&callee_ty);
490 match walked {
491 InferType::Unknown => Ok(ret_var),
492 InferType::Function(_, _) => {
493 self.table.unify(&walked, &fn_ty)?;
494 Ok(ret_var)
495 }
496 InferType::Var(_) => {
497 self.table.unify(&walked, &fn_ty)?;
498 Ok(ret_var)
499 }
500 other => Err(TypeError::NotAFunction { found: other }),
501 }
502 }
503
504 fn infer_closure(
506 &mut self,
507 params: &[(Symbol, &crate::ast::stmt::TypeExpr)],
508 body: &crate::ast::stmt::ClosureBody,
509 return_type: &Option<&crate::ast::stmt::TypeExpr>,
510 ) -> Result<InferType, TypeError> {
511 let param_types: Vec<InferType> = params
512 .iter()
513 .map(|(_, ty_expr)| InferType::from_type_expr(ty_expr, self.interner))
514 .collect();
515
516 let ret_type = if let Some(rt) = return_type {
517 InferType::from_type_expr(rt, self.interner)
518 } else {
519 self.table.fresh()
520 };
521
522 self.push_scope();
523 for ((sym, _), ty) in params.iter().zip(param_types.iter()) {
524 self.bind_var(*sym, ty.clone());
525 }
526
527 let prev_return = self.current_return_type.take();
528 self.current_return_type = Some(ret_type.clone());
529
530 match body {
531 crate::ast::stmt::ClosureBody::Expression(expr) => {
532 let body_ty = self.infer_expr(expr)?;
533 self.table.unify(&body_ty, &ret_type).ok();
535 }
536 crate::ast::stmt::ClosureBody::Block(stmts) => {
537 for stmt in *stmts {
538 self.infer_stmt(stmt)?;
539 }
540 }
541 }
542
543 self.current_return_type = prev_return;
544 self.pop_scope();
545
546 Ok(InferType::Function(param_types, Box::new(ret_type)))
547 }
548
549 fn infer_field_access(
551 &self,
552 obj_ty: InferType,
553 field: Symbol,
554 ) -> Result<InferType, TypeError> {
555 let resolved = self.table.zonk(&obj_ty);
556 match &resolved {
557 InferType::UserDefined(type_sym) => {
558 if let Some(TypeDef::Struct { fields, .. }) = self.registry.get(*type_sym) {
559 if let Some(field_def) = fields.iter().find(|f| f.name == field) {
560 Ok(InferType::from_field_type(
561 &field_def.ty,
562 self.interner,
563 &HashMap::new(),
564 ))
565 } else {
566 Err(TypeError::FieldNotFound {
567 type_name: *type_sym,
568 field_name: field,
569 })
570 }
571 } else {
572 Ok(InferType::Unknown)
574 }
575 }
576 _ => Ok(InferType::Unknown),
578 }
579 }
580}
581
582impl<'r> CheckEnv<'r> {
587 fn infer_stmt(&mut self, stmt: &Stmt) -> Result<(), TypeError> {
588 match stmt {
589 Stmt::Let { var, ty, value, .. } => {
590 let final_ty = if let Some(type_expr) = ty {
591 let annotated = InferType::from_type_expr(type_expr, self.interner);
592 if annotated != InferType::Unknown {
593 self.check_expr(value, &annotated)?
595 } else {
596 self.infer_expr(value)?
597 }
598 } else {
599 self.infer_expr(value)?
600 };
601 self.bind_var(*var, final_ty);
602 Ok(())
603 }
604
605 Stmt::Set { target, value } => {
606 let inferred = self.infer_expr(value)?;
607 if let Some(existing) = self.lookup_var(*target) {
609 if existing != InferType::Unknown {
610 self.table.unify(&inferred, &existing).ok();
611 }
612 }
613 let resolved = self.table.zonk(&inferred);
615 if resolved != InferType::Unknown {
616 self.bind_var(*target, resolved);
617 }
618 Ok(())
619 }
620
621 Stmt::FunctionDef {
622 name,
623 generics,
624 params,
625 body,
626 return_type,
627 is_native,
628 ..
629 } => {
630 let type_param_map: HashMap<Symbol, TyVar> = {
634 let existing_vars: Vec<TyVar> = self.functions
636 .get(name)
637 .map(|rec| rec.scheme.vars.clone())
638 .unwrap_or_default();
639 if existing_vars.len() == generics.len() {
640 generics.iter().copied().zip(existing_vars).collect()
641 } else {
642 generics.iter().map(|&sym| (sym, self.table.fresh_var())).collect()
643 }
644 };
645
646 let param_types: Vec<InferType> = params
647 .iter()
648 .map(|(_, ty_expr)| {
649 InferType::from_type_expr_with_params(ty_expr, self.interner, &type_param_map)
650 })
651 .collect();
652 let param_names: Vec<Symbol> = params.iter().map(|(sym, _)| *sym).collect();
653
654 let ret_type = if let Some(rt) = return_type {
655 InferType::from_type_expr_with_params(rt, self.interner, &type_param_map)
656 } else if let Some(rec) = self.functions.get(name) {
657 if let InferType::Function(_, ret_box) = &rec.scheme.body {
659 *ret_box.clone()
660 } else {
661 self.table.fresh()
662 }
663 } else {
664 self.table.fresh()
665 };
666
667 let generic_vars: Vec<TyVar> = generics
668 .iter()
669 .filter_map(|sym| type_param_map.get(sym).copied())
670 .collect();
671
672 if *is_native {
674 let scheme = TypeScheme {
675 vars: generic_vars,
676 body: InferType::Function(param_types, Box::new(ret_type)),
677 };
678 self.functions.insert(*name, FunctionRecord { param_names, scheme });
679 return Ok(());
680 }
681
682 let prev_return_type = self.current_return_type.take();
684 self.current_return_type = Some(ret_type.clone());
685
686 self.push_scope();
688 for (sym, ty) in param_names.iter().zip(param_types.iter()) {
689 self.bind_var(*sym, ty.clone());
690 }
691 for s in *body {
692 self.infer_stmt(s)?;
693 }
694 self.pop_scope();
695
696 self.current_return_type = prev_return_type;
697
698 let resolved_params: Vec<InferType> = param_types
702 .iter()
703 .map(|ty| self.table.resolve(ty))
704 .collect();
705 let resolved_ret = self.table.resolve(&ret_type);
706
707 fn collect_type_vars(ty: &InferType, acc: &mut Vec<TyVar>) {
715 match ty {
716 InferType::Var(tv) => {
717 if !acc.contains(tv) {
718 acc.push(*tv);
719 }
720 }
721 InferType::Seq(i) | InferType::Set(i) | InferType::Option(i) => {
722 collect_type_vars(i, acc)
723 }
724 InferType::Map(k, v) => {
725 collect_type_vars(k, acc);
726 collect_type_vars(v, acc);
727 }
728 InferType::Function(ps, r) => {
729 for p in ps {
730 collect_type_vars(p, acc);
731 }
732 collect_type_vars(r, acc);
733 }
734 _ => {}
735 }
736 }
737 let mut scheme_vars = generic_vars;
738 for p in &resolved_params {
739 collect_type_vars(p, &mut scheme_vars);
740 }
741 collect_type_vars(&resolved_ret, &mut scheme_vars);
742 let scheme = TypeScheme {
743 vars: scheme_vars,
744 body: InferType::Function(resolved_params, Box::new(resolved_ret)),
745 };
746 self.functions.insert(*name, FunctionRecord { param_names, scheme });
747 Ok(())
748 }
749
750 Stmt::Return { value } => {
751 let ty = match value {
752 Some(expr) => self.infer_expr(expr)?,
753 None => InferType::Unit,
754 };
755 if let Some(expected) = self.current_return_type.clone() {
756 if expected != InferType::Unknown {
758 self.table.unify(&ty, &expected)?;
759 }
760 }
761 Ok(())
762 }
763
764 Stmt::Repeat { pattern, iterable, body } => {
765 let iterable_ty = self.infer_expr(iterable)?;
766 let elem_ty = match self.table.zonk(&iterable_ty) {
767 InferType::Seq(inner) | InferType::Set(inner) => *inner,
768 InferType::Map(_, _) => InferType::Unknown,
774 _ => InferType::Unknown,
775 };
776 match pattern {
777 Pattern::Identifier(sym) => self.bind_var(*sym, elem_ty),
778 Pattern::Tuple(syms) => {
779 for sym in syms {
780 self.bind_var(*sym, InferType::Unknown);
781 }
782 }
783 }
784 for s in *body {
785 self.infer_stmt(s)?;
786 }
787 Ok(())
788 }
789
790 Stmt::If { then_block, else_block, .. } => {
791 for s in *then_block {
792 self.infer_stmt(s)?;
793 }
794 if let Some(else_b) = else_block {
795 for s in *else_b {
796 self.infer_stmt(s)?;
797 }
798 }
799 Ok(())
800 }
801
802 Stmt::While { body, .. } => {
803 for s in *body {
804 self.infer_stmt(s)?;
805 }
806 Ok(())
807 }
808
809 Stmt::Inspect { target, arms, .. } => {
810 let _target_ty = self.infer_expr(target)?;
811 for arm in arms {
812 self.push_scope();
813 self.infer_inspect_arm(arm)?;
814 self.pop_scope();
815 }
816 Ok(())
817 }
818
819 Stmt::Zone { body, .. } => {
820 for s in *body {
821 self.infer_stmt(s)?;
822 }
823 Ok(())
824 }
825
826 Stmt::ReadFrom { var, .. } => {
827 self.bind_var(*var, InferType::String);
828 Ok(())
829 }
830
831 Stmt::CreatePipe { var, element_type, .. } => {
832 let elem = InferType::from_type_name(self.interner.resolve(*element_type));
833 self.bind_var(*var, elem);
834 Ok(())
835 }
836
837 Stmt::ReceivePipe { var, pipe } => {
838 let elem_ty = self.infer_expr(pipe)?;
840 self.bind_var(*var, elem_ty);
841 Ok(())
842 }
843
844 Stmt::TryReceivePipe { var, pipe } => {
845 let elem_ty = self.infer_expr(pipe)?;
846 self.bind_var(*var, InferType::Option(Box::new(elem_ty)));
848 Ok(())
849 }
850
851 Stmt::Pop { into: Some(var), collection } => {
852 let coll_ty = self.infer_expr(collection)?;
853 let elem_ty = match self.table.zonk(&coll_ty) {
854 InferType::Seq(inner) | InferType::Set(inner) => *inner,
855 _ => InferType::Unknown,
856 };
857 self.bind_var(*var, elem_ty);
858 Ok(())
859 }
860
861 Stmt::AwaitMessage { into, .. } => {
862 self.bind_var(*into, InferType::Unknown);
863 Ok(())
864 }
865
866 Stmt::LaunchTaskWithHandle { handle, .. } => {
867 self.bind_var(*handle, InferType::Unknown);
868 Ok(())
869 }
870
871 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
872 for s in *tasks {
873 self.infer_stmt(s)?;
874 }
875 Ok(())
876 }
877
878 Stmt::Select { branches } => {
879 for branch in branches {
880 match branch {
881 crate::ast::stmt::SelectBranch::Receive { var, pipe, body } => {
882 let elem_ty = self.infer_expr(pipe)?;
883 self.push_scope();
884 self.bind_var(*var, elem_ty);
885 for s in *body {
886 self.infer_stmt(s)?;
887 }
888 self.pop_scope();
889 }
890 crate::ast::stmt::SelectBranch::Timeout { body, .. } => {
891 for s in *body {
892 self.infer_stmt(s)?;
893 }
894 }
895 }
896 }
897 Ok(())
898 }
899
900 _ => Ok(()),
901 }
902 }
903
904 fn infer_inspect_arm(
906 &mut self,
907 arm: &crate::ast::stmt::MatchArm,
908 ) -> Result<(), TypeError> {
909 if let Some(variant_sym) = arm.variant {
910 if let Some((_, variant_def)) = self.registry.find_variant(variant_sym) {
911 let fields: Vec<_> = variant_def
913 .fields
914 .iter()
915 .map(|f| (f.name, f.ty.clone()))
916 .collect();
917
918 for (field_sym, binding_sym) in &arm.bindings {
919 let ty = fields
920 .iter()
921 .find(|(name, _)| *name == *field_sym)
922 .map(|(_, ty)| {
923 InferType::from_field_type(ty, self.interner, &HashMap::new())
924 })
925 .unwrap_or(InferType::Unknown);
926 self.bind_var(*binding_sym, ty);
927 }
928 } else {
929 for (_, binding_sym) in &arm.bindings {
931 self.bind_var(*binding_sym, InferType::Unknown);
932 }
933 }
934 } else {
935 for (_, binding_sym) in &arm.bindings {
937 self.bind_var(*binding_sym, InferType::Unknown);
938 }
939 }
940
941 for s in arm.body {
942 self.infer_stmt(s)?;
943 }
944 Ok(())
945 }
946}
947
948#[derive(Debug)]
961pub struct IndexedTypeError {
962 pub stmt_index: Option<usize>,
963 pub error: TypeError,
964}
965
966pub fn check_program_collect(
973 stmts: &[Stmt],
974 interner: &Interner,
975 registry: &TypeRegistry,
976) -> (TypeEnv, Vec<IndexedTypeError>) {
977 let mut errors = Vec::new();
978
979 if let Err(e) =
980 crate::analysis::dimension_check::DimensionChecker::new(interner).check_program(stmts)
981 {
982 errors.push(IndexedTypeError {
983 stmt_index: None,
984 error: TypeError::DimensionMismatch { message: e.message },
985 });
986 }
987
988 let mut env = CheckEnv::new(registry, interner);
989 env.preregister_functions(stmts);
990
991 for (index, stmt) in stmts.iter().enumerate() {
992 if let Err(error) = env.infer_stmt(stmt) {
993 errors.push(IndexedTypeError {
994 stmt_index: Some(index),
995 error,
996 });
997 }
998 }
999
1000 (env.into_type_env(), errors)
1001}
1002
1003pub fn check_program(
1006 stmts: &[Stmt],
1007 interner: &Interner,
1008 registry: &TypeRegistry,
1009) -> Result<TypeEnv, TypeError> {
1010 crate::analysis::dimension_check::DimensionChecker::new(interner)
1013 .check_program(stmts)
1014 .map_err(|e| TypeError::DimensionMismatch { message: e.message })?;
1015
1016 let mut env = CheckEnv::new(registry, interner);
1017
1018 env.preregister_functions(stmts);
1020
1021 for stmt in stmts {
1023 env.infer_stmt(stmt)?;
1024 }
1025
1026 Ok(env.into_type_env())
1027}
1028
1029#[cfg(test)]
1034mod tests {
1035 use super::*;
1036 use crate::ast::stmt::{Expr, Literal, Stmt, TypeExpr};
1037 use crate::intern::Interner;
1038
1039 fn mk_interner() -> Interner {
1044 Interner::new()
1045 }
1046
1047 fn run(stmts: &[Stmt], interner: &Interner) -> TypeEnv {
1048 check_program(stmts, interner, &TypeRegistry::new()).expect("check_program failed")
1049 }
1050
1051 #[test]
1056 fn let_literal_int() {
1057 let mut interner = mk_interner();
1058 let x = interner.intern("x");
1059 let val = Expr::Literal(Literal::Number(42));
1060 let stmts = [Stmt::Let { var: x, ty: None, value: &val, mutable: false }];
1061 let env = run(&stmts, &interner);
1062 assert_eq!(env.lookup(x), &LogosType::Int);
1063 }
1064
1065 #[test]
1066 fn let_literal_float() {
1067 let mut interner = mk_interner();
1068 let x = interner.intern("x");
1069 let val = Expr::Literal(Literal::Float(3.14));
1070 let stmts = [Stmt::Let { var: x, ty: None, value: &val, mutable: false }];
1071 let env = run(&stmts, &interner);
1072 assert_eq!(env.lookup(x), &LogosType::Float);
1073 }
1074
1075 #[test]
1076 fn let_literal_string() {
1077 let mut interner = mk_interner();
1078 let s = interner.intern("s");
1079 let hello = interner.intern("hello");
1080 let val = Expr::Literal(Literal::Text(hello));
1081 let stmts = [Stmt::Let { var: s, ty: None, value: &val, mutable: false }];
1082 let env = run(&stmts, &interner);
1083 assert_eq!(env.lookup(s), &LogosType::String);
1084 }
1085
1086 #[test]
1091 fn let_with_annotation_uses_annotation() {
1092 let mut interner = mk_interner();
1093 let x = interner.intern("x");
1094 let float_sym = interner.intern("Real");
1095 let val = Expr::Literal(Literal::Number(5)); let ty_ann = TypeExpr::Primitive(float_sym);
1097 let stmts = [Stmt::Let { var: x, ty: Some(&ty_ann), value: &val, mutable: false }];
1098 let env = run(&stmts, &interner);
1099 assert_eq!(env.lookup(x), &LogosType::Float);
1101 }
1102
1103 #[test]
1104 fn let_type_mismatch_fails() {
1105 let mut interner = mk_interner();
1106 let x = interner.intern("x");
1107 let int_sym = interner.intern("Int");
1108 let val = Expr::Literal(Literal::Text(Symbol::EMPTY));
1109 let ty_ann = TypeExpr::Primitive(int_sym);
1110 let stmts = [Stmt::Let { var: x, ty: Some(&ty_ann), value: &val, mutable: false }];
1111 let result = check_program(&stmts, &interner, &TypeRegistry::new());
1112 assert!(result.is_err(), "Int and Text should not unify");
1113 }
1114
1115 #[test]
1120 fn empty_list_is_seq_unknown() {
1121 let mut interner = mk_interner();
1122 let xs = interner.intern("xs");
1123 let val = Expr::List(vec![]);
1124 let stmts = [Stmt::Let { var: xs, ty: None, value: &val, mutable: false }];
1125 let env = run(&stmts, &interner);
1126 assert!(matches!(env.lookup(xs), LogosType::Seq(_)));
1128 }
1129
1130 #[test]
1131 fn non_empty_list_infers_element_type() {
1132 let mut interner = mk_interner();
1133 let xs = interner.intern("xs");
1134 let one = Expr::Literal(Literal::Number(1));
1135 let two = Expr::Literal(Literal::Number(2));
1136 let val = Expr::List(vec![&one, &two]);
1137 let stmts = [Stmt::Let { var: xs, ty: None, value: &val, mutable: false }];
1138 let env = run(&stmts, &interner);
1139 assert_eq!(env.lookup(xs), &LogosType::Seq(Box::new(LogosType::Int)));
1140 }
1141
1142 #[test]
1147 fn option_none_is_option_unknown() {
1148 let mut interner = mk_interner();
1149 let x = interner.intern("x");
1150 let val = Expr::OptionNone;
1151 let stmts = [Stmt::Let { var: x, ty: None, value: &val, mutable: false }];
1152 let env = run(&stmts, &interner);
1153 assert!(matches!(env.lookup(x), LogosType::Option(_)));
1154 }
1155
1156 #[test]
1157 fn option_some_infers_inner_type() {
1158 let mut interner = mk_interner();
1159 let x = interner.intern("x");
1160 let inner = Expr::Literal(Literal::Number(42));
1161 let val = Expr::OptionSome { value: &inner };
1162 let stmts = [Stmt::Let { var: x, ty: None, value: &val, mutable: false }];
1163 let env = run(&stmts, &interner);
1164 assert_eq!(env.lookup(x), &LogosType::Option(Box::new(LogosType::Int)));
1165 }
1166
1167 #[test]
1172 fn function_def_registers_signature() {
1173 let mut interner = mk_interner();
1174 let f = interner.intern("double");
1175 let x_param = interner.intern("x");
1176 let int_sym = interner.intern("Int");
1177 let int_ty = TypeExpr::Primitive(int_sym);
1178 let ret_ty = TypeExpr::Primitive(int_sym);
1179 let lit = Expr::Literal(Literal::Number(0));
1180 let ret_stmt = Stmt::Return { value: Some(&lit) };
1181 let body = [ret_stmt];
1182 let stmts = [Stmt::FunctionDef {
1183 name: f,
1184 generics: vec![],
1185 params: vec![(x_param, &int_ty)],
1186 body: &body,
1187 return_type: Some(&ret_ty),
1188 is_native: false,
1189 native_path: None,
1190 is_exported: false,
1191 export_target: None,
1192 opt_flags: Default::default(),
1193 }];
1194 let env = run(&stmts, &interner);
1195 let sig = env.lookup_fn(f).expect("function should be registered");
1196 assert_eq!(sig.return_type, LogosType::Int);
1197 assert_eq!(sig.params.len(), 1);
1198 assert_eq!(sig.params[0].1, LogosType::Int);
1199 }
1200
1201 #[test]
1202 fn function_call_returns_registered_type() {
1203 let mut interner = mk_interner();
1204 let f = interner.intern("compute");
1205 let float_sym = interner.intern("Real");
1206 let float_ty = TypeExpr::Primitive(float_sym);
1207 let lit = Expr::Literal(Literal::Float(1.0));
1208 let ret_stmt = Stmt::Return { value: Some(&lit) };
1209 let body = [ret_stmt];
1210 let fn_def = Stmt::FunctionDef {
1211 name: f,
1212 generics: vec![],
1213 params: vec![],
1214 body: &body,
1215 return_type: Some(&float_ty),
1216 is_native: false,
1217 native_path: None,
1218 is_exported: false,
1219 export_target: None,
1220 opt_flags: Default::default(),
1221 };
1222 let result_var = interner.intern("result");
1223 let call = Expr::Call { function: f, args: vec![] };
1224 let let_stmt = Stmt::Let { var: result_var, ty: None, value: &call, mutable: false };
1225 let stmts = [fn_def, let_stmt];
1226 let env = run(&stmts, &interner);
1227 assert_eq!(env.lookup(result_var), &LogosType::Float);
1228 }
1229
1230 #[test]
1235 fn readfrom_is_string() {
1236 let mut interner = mk_interner();
1237 let v = interner.intern("input");
1238 let stmts = [Stmt::ReadFrom {
1239 var: v,
1240 source: crate::ast::stmt::ReadSource::Console,
1241 }];
1242 let env = run(&stmts, &interner);
1243 assert_eq!(env.lookup(v), &LogosType::String);
1244 }
1245
1246 #[test]
1251 fn repeat_loop_var_gets_element_type() {
1252 let mut interner = mk_interner();
1253 let items = interner.intern("items");
1254 let elem = interner.intern("elem");
1255 let one = Expr::Literal(Literal::Number(1));
1256 let list = Expr::List(vec![&one]);
1257 let let_items = Stmt::Let { var: items, ty: None, value: &list, mutable: false };
1258 let items_ref = Expr::Identifier(items);
1259 let repeat = Stmt::Repeat {
1260 pattern: Pattern::Identifier(elem),
1261 iterable: &items_ref,
1262 body: &[],
1263 };
1264 let stmts = [let_items, repeat];
1265 let env = run(&stmts, &interner);
1266 assert_eq!(env.lookup(elem), &LogosType::Int);
1267 }
1268
1269 #[test]
1274 fn field_access_resolves_with_registry() {
1275 use crate::analysis::{FieldDef, FieldType, TypeDef};
1276
1277 let mut interner = mk_interner();
1278 let point_sym = interner.intern("Point");
1279 let x_field_sym = interner.intern("x");
1280 let int_sym = interner.intern("Int");
1281 let p_var = interner.intern("p");
1282 let result_var = interner.intern("px");
1283
1284 let mut registry = TypeRegistry::new();
1286 registry.register(
1287 point_sym,
1288 TypeDef::Struct {
1289 fields: vec![FieldDef {
1290 name: x_field_sym,
1291 ty: FieldType::Primitive(int_sym),
1292 is_public: true,
1293 }],
1294 generics: vec![],
1295 is_portable: false,
1296 is_shared: false,
1297 },
1298 );
1299
1300 let new_point = Expr::New { type_name: point_sym, type_args: vec![], init_fields: vec![] };
1302 let let_p = Stmt::Let { var: p_var, ty: None, value: &new_point, mutable: false };
1303
1304 let p_ref = Expr::Identifier(p_var);
1306 let field_access = Expr::FieldAccess { object: &p_ref, field: x_field_sym };
1307 let let_px = Stmt::Let { var: result_var, ty: None, value: &field_access, mutable: false };
1308
1309 let stmts = [let_p, let_px];
1310 let env = check_program(&stmts, &interner, ®istry).expect("check_program failed");
1311 assert_eq!(env.lookup(result_var), &LogosType::Int);
1312 }
1313
1314 #[test]
1319 fn forward_reference_function_call() {
1320 let mut interner = mk_interner();
1321 let f = interner.intern("later_fn");
1322 let result_var = interner.intern("r");
1323 let bool_sym = interner.intern("Bool");
1324 let bool_ty = TypeExpr::Primitive(bool_sym);
1325
1326 let call = Expr::Call { function: f, args: vec![] };
1328 let let_r = Stmt::Let { var: result_var, ty: None, value: &call, mutable: false };
1329
1330 let lit = Expr::Literal(Literal::Boolean(true));
1332 let ret_stmt = Stmt::Return { value: Some(&lit) };
1333 let body = [ret_stmt];
1334 let fn_def = Stmt::FunctionDef {
1335 name: f,
1336 generics: vec![],
1337 params: vec![],
1338 body: &body,
1339 return_type: Some(&bool_ty),
1340 is_native: false,
1341 native_path: None,
1342 is_exported: false,
1343 export_target: None,
1344 opt_flags: Default::default(),
1345 };
1346
1347 let stmts = [let_r, fn_def];
1349 let env = run(&stmts, &interner);
1350 assert_eq!(env.lookup(result_var), &LogosType::Bool);
1351 }
1352
1353 #[test]
1358 fn return_type_mismatch_fails() {
1359 let mut interner = mk_interner();
1360 let f = interner.intern("f");
1361 let int_sym = interner.intern("Int");
1362 let int_ty = TypeExpr::Primitive(int_sym);
1363 let lit = Expr::Literal(Literal::Text(Symbol::EMPTY));
1365 let ret_stmt = Stmt::Return { value: Some(&lit) };
1366 let body = [ret_stmt];
1367 let stmts = [Stmt::FunctionDef {
1368 name: f,
1369 generics: vec![],
1370 params: vec![],
1371 body: &body,
1372 return_type: Some(&int_ty),
1373 is_native: false,
1374 native_path: None,
1375 is_exported: false,
1376 export_target: None,
1377 opt_flags: Default::default(),
1378 }];
1379 let result = check_program(&stmts, &interner, &TypeRegistry::new());
1380 assert!(result.is_err(), "returning Text from Int function should fail");
1381 }
1382
1383 #[test]
1388 fn new_user_defined_is_user_defined_type() {
1389 let mut interner = mk_interner();
1390 let point = interner.intern("Point");
1391 let p = interner.intern("p");
1392 let new_point = Expr::New { type_name: point, type_args: vec![], init_fields: vec![] };
1393 let stmts = [Stmt::Let { var: p, ty: None, value: &new_point, mutable: false }];
1394 let env = run(&stmts, &interner);
1395 assert_eq!(env.lookup(p), &LogosType::UserDefined(point));
1396 }
1397
1398 #[test]
1403 fn string_vars_in_legacy_api() {
1404 let mut interner = mk_interner();
1405 let s = interner.intern("name");
1406 let hello = interner.intern("hello");
1407 let val = Expr::Literal(Literal::Text(hello));
1408 let stmts = [Stmt::Let { var: s, ty: None, value: &val, mutable: false }];
1409 let env = run(&stmts, &interner);
1410 assert!(env.to_legacy_string_vars().contains(&s));
1411 }
1412
1413 #[test]
1414 fn unknown_vars_filtered_in_legacy_api() {
1415 let mut interner = mk_interner();
1416 let x = interner.intern("x");
1417 let val = Expr::OptionNone; let stmts = [Stmt::Let { var: x, ty: None, value: &val, mutable: false }];
1419 let env = run(&stmts, &interner);
1420 let legacy = env.to_legacy_variable_types();
1422 assert!(!legacy.is_empty() || legacy.is_empty()); }
1425
1426 #[test]
1431 fn generic_identity_infers_int_return() {
1432 let mut interner = mk_interner();
1436 let f = interner.intern("identity");
1437 let x_param = interner.intern("x");
1438 let t_sym = interner.intern("T");
1439 let t_ty = TypeExpr::Primitive(t_sym);
1440 let x_ref = Expr::Identifier(x_param);
1441 let ret_stmt = Stmt::Return { value: Some(&x_ref) };
1442 let body = [ret_stmt];
1443 let fn_def = Stmt::FunctionDef {
1444 name: f,
1445 generics: vec![t_sym],
1446 params: vec![(x_param, &t_ty)],
1447 body: &body,
1448 return_type: Some(&t_ty),
1449 is_native: false,
1450 native_path: None,
1451 is_exported: false,
1452 export_target: None,
1453 opt_flags: Default::default(),
1454 };
1455 let r = interner.intern("r");
1456 let lit = Expr::Literal(Literal::Number(42));
1457 let call = Expr::Call { function: f, args: vec![&lit] };
1458 let let_r = Stmt::Let { var: r, ty: None, value: &call, mutable: false };
1459 let stmts = [fn_def, let_r];
1460 let env = run(&stmts, &interner);
1461 assert_eq!(env.lookup(r), &LogosType::Int,
1462 "identity(42) should return Int, got {:?}", env.lookup(r));
1463 }
1464
1465 #[test]
1466 fn generic_identity_infers_bool_return() {
1467 let mut interner = mk_interner();
1469 let f = interner.intern("identity");
1470 let x_param = interner.intern("x");
1471 let t_sym = interner.intern("T");
1472 let t_ty = TypeExpr::Primitive(t_sym);
1473 let x_ref = Expr::Identifier(x_param);
1474 let ret_stmt = Stmt::Return { value: Some(&x_ref) };
1475 let body = [ret_stmt];
1476 let fn_def = Stmt::FunctionDef {
1477 name: f,
1478 generics: vec![t_sym],
1479 params: vec![(x_param, &t_ty)],
1480 body: &body,
1481 return_type: Some(&t_ty),
1482 is_native: false,
1483 native_path: None,
1484 is_exported: false,
1485 export_target: None,
1486 opt_flags: Default::default(),
1487 };
1488 let r = interner.intern("r");
1489 let lit = Expr::Literal(Literal::Boolean(true));
1490 let call = Expr::Call { function: f, args: vec![&lit] };
1491 let let_r = Stmt::Let { var: r, ty: None, value: &call, mutable: false };
1492 let stmts = [fn_def, let_r];
1493 let env = run(&stmts, &interner);
1494 assert_eq!(env.lookup(r), &LogosType::Bool,
1495 "identity(true) should return Bool, got {:?}", env.lookup(r));
1496 }
1497
1498 #[test]
1499 fn generic_two_type_params_first() {
1500 let mut interner = mk_interner();
1504 let f = interner.intern("first");
1505 let a_param = interner.intern("a");
1506 let b_param = interner.intern("b");
1507 let a_sym = interner.intern("A");
1508 let b_sym = interner.intern("B");
1509 let a_ty = TypeExpr::Primitive(a_sym);
1510 let b_ty = TypeExpr::Primitive(b_sym);
1511 let a_ref = Expr::Identifier(a_param);
1512 let ret_stmt = Stmt::Return { value: Some(&a_ref) };
1513 let body = [ret_stmt];
1514 let fn_def = Stmt::FunctionDef {
1515 name: f,
1516 generics: vec![a_sym, b_sym],
1517 params: vec![(a_param, &a_ty), (b_param, &b_ty)],
1518 body: &body,
1519 return_type: Some(&a_ty),
1520 is_native: false,
1521 native_path: None,
1522 is_exported: false,
1523 export_target: None,
1524 opt_flags: Default::default(),
1525 };
1526 let r = interner.intern("r");
1527 let lit_int = Expr::Literal(Literal::Number(42));
1528 let lit_bool = Expr::Literal(Literal::Boolean(true));
1529 let call = Expr::Call { function: f, args: vec![&lit_int, &lit_bool] };
1530 let let_r = Stmt::Let { var: r, ty: None, value: &call, mutable: false };
1531 let stmts = [fn_def, let_r];
1532 let env = run(&stmts, &interner);
1533 assert_eq!(env.lookup(r), &LogosType::Int,
1534 "first(42, true) should return Int (first param type), got {:?}", env.lookup(r));
1535 }
1536
1537 #[test]
1538 fn generic_calls_are_independent() {
1539 let mut interner = mk_interner();
1542 let f = interner.intern("identity");
1543 let x_param = interner.intern("x");
1544 let t_sym = interner.intern("T");
1545 let t_ty = TypeExpr::Primitive(t_sym);
1546 let x_ref = Expr::Identifier(x_param);
1547 let ret_stmt = Stmt::Return { value: Some(&x_ref) };
1548 let body = [ret_stmt];
1549 let fn_def = Stmt::FunctionDef {
1550 name: f,
1551 generics: vec![t_sym],
1552 params: vec![(x_param, &t_ty)],
1553 body: &body,
1554 return_type: Some(&t_ty),
1555 is_native: false,
1556 native_path: None,
1557 is_exported: false,
1558 export_target: None,
1559 opt_flags: Default::default(),
1560 };
1561 let r1 = interner.intern("r1");
1562 let r2 = interner.intern("r2");
1563 let lit_int = Expr::Literal(Literal::Number(42));
1564 let lit_bool = Expr::Literal(Literal::Boolean(true));
1565 let call1 = Expr::Call { function: f, args: vec![&lit_int] };
1566 let call2 = Expr::Call { function: f, args: vec![&lit_bool] };
1567 let let_r1 = Stmt::Let { var: r1, ty: None, value: &call1, mutable: false };
1568 let let_r2 = Stmt::Let { var: r2, ty: None, value: &call2, mutable: false };
1569 let stmts = [fn_def, let_r1, let_r2];
1570 let env = run(&stmts, &interner);
1571 assert_eq!(env.lookup(r1), &LogosType::Int,
1572 "identity(42) should be Int, got {:?}", env.lookup(r1));
1573 assert_eq!(env.lookup(r2), &LogosType::Bool,
1574 "identity(true) should be Bool, got {:?}", env.lookup(r2));
1575 }
1576
1577 #[test]
1578 fn monomorphic_functions_unaffected_by_generics() {
1579 let mut interner = mk_interner();
1581 let f = interner.intern("double");
1582 let x_param = interner.intern("x");
1583 let int_sym = interner.intern("Int");
1584 let int_ty = TypeExpr::Primitive(int_sym);
1585 let x_ref = Expr::Identifier(x_param);
1586 let lit2 = Expr::Literal(Literal::Number(2));
1587 let mul = Expr::BinaryOp {
1588 op: BinaryOpKind::Multiply,
1589 left: &x_ref,
1590 right: &lit2,
1591 };
1592 let ret_stmt = Stmt::Return { value: Some(&mul) };
1593 let body = [ret_stmt];
1594 let fn_def = Stmt::FunctionDef {
1595 name: f,
1596 generics: vec![],
1597 params: vec![(x_param, &int_ty)],
1598 body: &body,
1599 return_type: Some(&int_ty),
1600 is_native: false,
1601 native_path: None,
1602 is_exported: false,
1603 export_target: None,
1604 opt_flags: Default::default(),
1605 };
1606 let r = interner.intern("r");
1607 let lit5 = Expr::Literal(Literal::Number(5));
1608 let call = Expr::Call { function: f, args: vec![&lit5] };
1609 let let_r = Stmt::Let { var: r, ty: None, value: &call, mutable: false };
1610 let stmts = [fn_def, let_r];
1611 let env = run(&stmts, &interner);
1612 assert_eq!(env.lookup(r), &LogosType::Int,
1613 "double(5) should return Int, got {:?}", env.lookup(r));
1614 }
1615
1616 #[test]
1617 fn generic_forward_reference_resolves() {
1618 let mut interner = mk_interner();
1623 let f = interner.intern("identity");
1624 let x_param = interner.intern("x");
1625 let t_sym = interner.intern("T");
1626 let t_ty = TypeExpr::Primitive(t_sym);
1627 let x_ref = Expr::Identifier(x_param);
1628 let ret_stmt = Stmt::Return { value: Some(&x_ref) };
1629 let body = [ret_stmt];
1630 let fn_def = Stmt::FunctionDef {
1631 name: f,
1632 generics: vec![t_sym],
1633 params: vec![(x_param, &t_ty)],
1634 body: &body,
1635 return_type: Some(&t_ty),
1636 is_native: false,
1637 native_path: None,
1638 is_exported: false,
1639 export_target: None,
1640 opt_flags: Default::default(),
1641 };
1642 let r = interner.intern("r");
1643 let lit = Expr::Literal(Literal::Number(99));
1644 let call = Expr::Call { function: f, args: vec![&lit] };
1645 let let_r = Stmt::Let { var: r, ty: None, value: &call, mutable: false };
1646 let stmts = [let_r, fn_def];
1648 let env = run(&stmts, &interner);
1649 assert_eq!(env.lookup(r), &LogosType::Int,
1650 "forward-ref identity(99) should be Int, got {:?}", env.lookup(r));
1651 }
1652
1653 #[test]
1660 fn seq_of_real_accepts_int_literals() {
1661 let mut interner = Interner::new();
1662 let xs = interner.intern("xs");
1663 let real_sym = interner.intern("Real");
1664 let seq_sym = interner.intern("Seq");
1665
1666 let one = Expr::Literal(Literal::Number(1));
1667 let two = Expr::Literal(Literal::Number(2));
1668 let three = Expr::Literal(Literal::Number(3));
1669 let val = Expr::List(vec![&one, &two, &three]);
1670
1671 let real_ty = TypeExpr::Primitive(real_sym);
1672 let params = [real_ty];
1673 let seq_real = TypeExpr::Generic { base: seq_sym, params: ¶ms };
1674
1675 let stmts = [Stmt::Let { var: xs, ty: Some(&seq_real), value: &val, mutable: false }];
1676
1677 let env = check_program(&stmts, &interner, &TypeRegistry::new())
1678 .expect("Seq of Real should accept integer literals [1, 2, 3]");
1679
1680 assert_eq!(
1681 env.lookup(xs),
1682 &LogosType::Seq(Box::new(LogosType::Float)),
1683 "xs should be inferred as Seq<Float> under the `Seq of Real` annotation"
1684 );
1685 }
1686
1687 #[test]
1691 fn generic_inferred_return_calls_are_independent() {
1692 let mut interner = mk_interner();
1693 let f = interner.intern("wrap");
1694 let x_param = interner.intern("x");
1695 let t_sym = interner.intern("T");
1696 let t_ty = TypeExpr::Primitive(t_sym);
1697 let x_ref = Expr::Identifier(x_param);
1698 let ret_stmt = Stmt::Return { value: Some(&x_ref) };
1699 let body = [ret_stmt];
1700 let fn_def = Stmt::FunctionDef {
1701 name: f,
1702 generics: vec![t_sym],
1703 params: vec![(x_param, &t_ty)],
1704 body: &body,
1705 return_type: None, is_native: false,
1707 native_path: None,
1708 is_exported: false,
1709 export_target: None,
1710 opt_flags: Default::default(),
1711 };
1712 let r1 = interner.intern("r1");
1713 let r2 = interner.intern("r2");
1714 let lit_int = Expr::Literal(Literal::Number(42));
1715 let lit_bool = Expr::Literal(Literal::Boolean(true));
1716 let call1 = Expr::Call { function: f, args: vec![&lit_int] };
1717 let call2 = Expr::Call { function: f, args: vec![&lit_bool] };
1718 let let_r1 = Stmt::Let { var: r1, ty: None, value: &call1, mutable: false };
1719 let let_r2 = Stmt::Let { var: r2, ty: None, value: &call2, mutable: false };
1720 let stmts = [fn_def, let_r1, let_r2];
1721 let env = run(&stmts, &interner);
1722 assert_eq!(env.lookup(r1), &LogosType::Int,
1723 "wrap(42) should be Int, got {:?}", env.lookup(r1));
1724 assert_eq!(env.lookup(r2), &LogosType::Bool,
1725 "wrap(true) should be Bool, got {:?}", env.lookup(r2));
1726 }
1727
1728 #[test]
1732 fn repeat_over_map_single_ident_loop_var_is_not_bare_key() {
1733 let mut interner = mk_interner();
1734 let m = interner.intern("m");
1735 let entry = interner.intern("entry");
1736 let map_sym = interner.intern("Map");
1737 let text_sym = interner.intern("Text");
1738 let int_sym = interner.intern("Int");
1739
1740 let new_map = Expr::New {
1741 type_name: map_sym,
1742 type_args: vec![TypeExpr::Primitive(text_sym), TypeExpr::Primitive(int_sym)],
1743 init_fields: vec![],
1744 };
1745 let let_m = Stmt::Let { var: m, ty: None, value: &new_map, mutable: false };
1746
1747 let m_ref = Expr::Identifier(m);
1748 let repeat = Stmt::Repeat {
1749 pattern: Pattern::Identifier(entry),
1750 iterable: &m_ref,
1751 body: &[],
1752 };
1753
1754 let stmts = [let_m, repeat];
1755 let env = run(&stmts, &interner);
1756
1757 assert_ne!(
1758 env.lookup(entry),
1759 &LogosType::String,
1760 "iterating a Map with a single identifier yields (key,value) tuples at runtime; \
1761 the loop var must not be typed as the bare key K"
1762 );
1763 }
1764}