1use std::path::PathBuf;
5
6use crate::{
7 ast::statements::{MatchPattern, StatementKind, TypeAnnotation},
8 checker::structs::{CheckType, TypeChecker},
9 lexer::tokenizer::Tokenizer,
10 parser::parser_logic::Parser,
11 utils::{source::SourceFile, span::Span},
12};
13
14impl TypeChecker {
15 pub fn check_statement(&mut self, statement: &crate::ast::statements::Statement) {
17 match &statement.kind {
18 StatementKind::VariableDeclaration {
21 name,
22 type_annotation,
23 value,
24 } => {
25 let value_type = self.check_expression(*value);
26 let declared = CheckType::Known(type_annotation.clone());
27
28 if !value_type.matches(&declared) {
29 self.error(
30 format!(
31 "type mismatch: expected {}, got {}",
32 declared.info(),
33 value_type.info()
34 ),
35 statement.span,
36 );
37 }
38
39 self.declare(name.clone(), declared, false, statement.span);
40 }
41
42 StatementKind::ConstantDeclaration {
45 name,
46 type_annotation,
47 value,
48 } => {
49 let value_type = self.check_expression(*value).into_const();
50 let declared = CheckType::Known(type_annotation.clone());
51
52 if !value_type.matches(&declared) {
53 self.error(
54 format!(
55 "type mismatch: expected {}, got {}",
56 declared.info(),
57 value_type.info()
58 ),
59 statement.span,
60 );
61 }
62
63 self.declare(name.clone(), declared, true, statement.span);
64 }
65
66 StatementKind::Array {
69 name,
70 type_annotation,
71 value,
72 } => {
73 for item in value {
74 let item_span = self.ast_arena.exprs.get(*item).span;
75 let item_type = self.check_expression(*item);
76 let expected = CheckType::Known(type_annotation.clone());
77
78 if !item_type.matches(&expected) {
79 self.error(
80 format!(
81 "type mismatch: array expects {}, got {}",
82 expected.info(),
83 item_type.info()
84 ),
85 item_span,
86 );
87 }
88 }
89
90 let array_type =
91 CheckType::Known(TypeAnnotation::Array(Box::new(type_annotation.clone())));
92
93 self.declare(name.clone(), array_type, false, statement.span);
94 }
95 StatementKind::ConstantArray {
96 name,
97 type_annotation,
98 value,
99 } => {
100 for item in value {
101 let item_span = self.ast_arena.exprs.get(*item).span;
102 let item_type = self.check_expression(*item);
103 let expected = CheckType::Known(type_annotation.clone());
104
105 if !item_type.matches(&expected) {
106 self.error(
107 format!(
108 "type mismatch: array expects {}, got {}",
109 expected.info(),
110 item_type.info()
111 ),
112 item_span,
113 );
114 }
115 }
116
117 let array_type =
118 CheckType::Known(TypeAnnotation::CArray(Box::new(type_annotation.clone())));
119
120 self.declare(name.clone(), array_type, true, statement.span);
121 }
122
123 StatementKind::Set {
124 name,
125 type_annotation,
126 items,
127 } => {
128 for item in items {
129 let item_span = self.ast_arena.exprs.get(*item).span;
130 let item_type = self.check_expression(*item);
131 let expected = CheckType::Known(type_annotation.clone());
132
133 if !item_type.matches(&expected) {
134 self.error(
135 format!(
136 "type mismatch: set expects {}, got {}",
137 expected.info(),
138 item_type.info()
139 ),
140 item_span,
141 );
142 }
143 }
144
145 let array_type =
146 CheckType::Known(TypeAnnotation::Set(Box::new(type_annotation.clone())));
147
148 self.declare(name.clone(), array_type, false, statement.span);
149 }
150 StatementKind::ConstantSet {
151 name,
152 type_annotation,
153 items,
154 } => {
155 for item in items {
156 let item_span = self.ast_arena.exprs.get(*item).span;
157 let item_type = self.check_expression(*item);
158 let expected = CheckType::Known(type_annotation.clone());
159
160 if !item_type.matches(&expected) {
161 self.error(
162 format!(
163 "type mismatch: set expects {}, got {}",
164 expected.info(),
165 item_type.info()
166 ),
167 item_span,
168 );
169 }
170 }
171
172 let array_type =
173 CheckType::Known(TypeAnnotation::CSet(Box::new(type_annotation.clone())));
174
175 self.declare(name.clone(), array_type, true, statement.span);
176 }
177
178 StatementKind::Map {
181 name,
182 type_annotation,
183 entries,
184 } => {
185 let (key_ty, value_ty) = match type_annotation {
186 TypeAnnotation::Map(k, v) => (k.as_ref().clone(), v.as_ref().clone()),
187 other => (TypeAnnotation::Null, other.clone()),
188 };
189 let expected_key = CheckType::Known(key_ty.clone());
190 let expected_value = CheckType::Known(value_ty.clone());
191
192 for (key, value) in entries {
193 let key_span = self.ast_arena.exprs.get(*key).span;
194 let value_span = self.ast_arena.exprs.get(*value).span;
195 let key_type = self.check_expression(*key);
196 let value_type = self.check_expression(*value);
197
198 if !key_type.matches(&expected_key) {
199 self.error(
200 format!(
201 "type mismatch: map key expects {}, got {}",
202 expected_key.info(),
203 key_type.info()
204 ),
205 key_span,
206 );
207 }
208 if !value_type.matches(&expected_value) {
209 self.error(
210 format!(
211 "type mismatch: map value expects {}, got {}",
212 expected_value.info(),
213 value_type.info()
214 ),
215 value_span,
216 );
217 }
218 }
219
220 if !Self::is_hashable_key_type(&key_ty) {
221 self.error(
222 format!("type {:?} cannot be used as a map key", key_ty),
223 statement.span,
224 );
225 }
226
227 let map_type =
228 CheckType::Known(TypeAnnotation::Map(Box::new(key_ty), Box::new(value_ty)));
229
230 self.declare(name.clone(), map_type, false, statement.span);
231 }
232 StatementKind::ConstantMap {
233 name,
234 type_annotation,
235 entries,
236 } => {
237 let (key_ty, value_ty) = match type_annotation {
238 TypeAnnotation::CMap(k, v) => (k.as_ref().clone(), v.as_ref().clone()),
239 other => (TypeAnnotation::Null, other.clone()),
240 };
241 let expected_key = CheckType::Known(key_ty.clone());
242 let expected_value = CheckType::Known(value_ty.clone());
243
244 for (key, value) in entries {
245 let key_span = self.ast_arena.exprs.get(*key).span;
246 let value_span = self.ast_arena.exprs.get(*value).span;
247 let key_type = self.check_expression(*key).into_const();
248 let value_type = self.check_expression(*value).into_const();
249
250 if !key_type.matches(&expected_key) {
251 self.error(
252 format!(
253 "type mismatch: map key expects {}, got {}",
254 expected_key.info(),
255 key_type.info()
256 ),
257 key_span,
258 );
259 }
260 if !value_type.matches(&expected_value) {
261 self.error(
262 format!(
263 "type mismatch: map value expects {}, got {}",
264 expected_value.info(),
265 value_type.info()
266 ),
267 value_span,
268 );
269 }
270 }
271
272 if !Self::is_hashable_key_type(&key_ty) {
273 self.error(
274 format!("type {:?} cannot be used as a map key", key_ty),
275 statement.span,
276 );
277 }
278
279 let map_type =
280 CheckType::Known(TypeAnnotation::CMap(Box::new(key_ty), Box::new(value_ty)));
281
282 self.declare(name.clone(), map_type, true, statement.span);
283 }
284
285 StatementKind::Expression(expr) => {
287 self.check_expression(*expr);
288 }
289
290 StatementKind::While { condition, body } => {
292 let condition_type = self.check_expression(*condition);
294 if !matches!(
295 condition_type,
296 CheckType::Known(TypeAnnotation::Bool | TypeAnnotation::CBool)
297 | CheckType::Unknown
298 ) {
299 let condition_span = self.ast_arena.exprs.get(*condition).span;
300 self.error(
301 format!(
302 "while condition must be bool, got {}",
303 condition_type.info()
304 ),
305 condition_span,
306 );
307 }
308 self.enter_loop();
310 self.check_block(body);
312 self.exit_loop();
314 }
315
316 StatementKind::For {
317 initializer,
318 condition,
319 increment,
320 body,
321 } => {
322 self.push_scope();
324 self.check_statement(initializer);
326 let condition_type = self.check_expression(*condition);
328 if !matches!(
329 condition_type,
330 CheckType::Known(TypeAnnotation::Bool | TypeAnnotation::CBool)
331 | CheckType::Unknown
332 ) {
333 let condition_span = self.ast_arena.exprs.get(*condition).span;
334 self.error(
335 format!("for condition must be bool, got {}", condition_type.info()),
336 condition_span,
337 );
338 }
339 self.check_expression(*increment);
341 self.enter_loop();
343 for stmt in body {
345 self.check_statement(stmt);
346 }
347 self.exit_loop();
349 self.pop_scope();
351 }
352
353 StatementKind::ForRange {
354 variable,
355 range,
356 body,
357 } => {
358 let _ = range;
360 self.enter_loop();
362 self.push_scope();
364 self.declare(
366 variable.clone(),
367 CheckType::Known(TypeAnnotation::Int),
368 false,
369 statement.span,
370 );
371 for stmt in body {
373 self.check_statement(stmt);
374 }
375 self.pop_scope();
377 self.exit_loop();
379 }
380
381 StatementKind::ForEach {
382 variable,
383 iterable,
384 body,
385 } => {
386 let iter_type = self.check_expression(*iterable);
388 let item_type = match &iter_type {
390 CheckType::Known(TypeAnnotation::Array(inner))
391 | CheckType::Known(TypeAnnotation::CArray(inner)) => {
392 CheckType::Known((**inner).clone())
393 }
394 CheckType::Unknown => CheckType::Unknown,
395 other => {
396 let iterable_span = self.ast_arena.exprs.get(*iterable).span;
397 self.error(
398 format!("for-each: expected an array, got {}", other.info()),
399 iterable_span,
400 );
401 CheckType::Unknown
402 }
403 };
404 self.enter_loop();
406 self.push_scope();
408 self.declare(variable.clone(), item_type, false, statement.span);
410 for stmt in body {
412 self.check_statement(stmt);
413 }
414 self.pop_scope();
416 self.exit_loop();
418 }
419
420 StatementKind::Range(_) => {}
421
422 StatementKind::ConditionalBranch {
424 condition, body, ..
425 } => {
426 if let Some(cond) = condition {
428 let condition_type = self.check_expression(*cond);
430 if !matches!(
431 condition_type,
432 CheckType::Known(TypeAnnotation::Bool | TypeAnnotation::CBool)
433 | CheckType::Unknown
434 ) {
435 let cond_span = self.ast_arena.exprs.get(*cond).span;
436 self.error(
437 format!("condition must be bool, got {}", condition_type.info()),
438 cond_span,
439 );
440 }
441 }
442 self.check_block(body);
444 }
445
446 StatementKind::Conditional {
447 if_branch,
448 else_branch,
449 } => {
450 self.check_statement(if_branch);
452 if let Some(branch) = else_branch {
454 self.check_statement(branch);
455 }
456 }
457
458 StatementKind::FunctionDeclaration {
460 params,
461 return_type,
462 body,
463 ..
464 } => {
465 self.push_scope();
466 for param in params {
467 self.declare(
468 param.param_name.clone(),
469 CheckType::Known(param.param_type.clone()),
470 false,
471 statement.span,
472 );
473 }
474 self.push_return_type(return_type.clone());
475 for stmt in body {
476 self.check_statement(stmt);
477 }
478 self.pop_return_type();
479 self.pop_scope();
480 }
481 StatementKind::Return(expr) => {
482 let actual_type = match expr {
484 Some(e) => self.check_expression(*e),
485 None => CheckType::Known(TypeAnnotation::Null),
486 };
487 if let Some(expected) = self.current_return_type().cloned() {
489 let widens = matches!(
490 (expected.clone(), actual_type.clone()),
491 (
492 TypeAnnotation::Int | TypeAnnotation::CInt,
493 CheckType::Known(TypeAnnotation::Byte | TypeAnnotation::CByte)
494 )
495 );
496 if expected != TypeAnnotation::Null {
497 let expected_type = CheckType::Known(expected.clone());
498 if !widens && !actual_type.matches(&expected_type) {
499 self.error(
500 format!(
501 "return type mismatch: expected {}, got {}",
502 expected_type.info(),
503 actual_type.info()
504 ),
505 statement.span,
506 );
507 }
508 }
509 } else {
510 self.error("return outside of function", statement.span);
512 }
513 }
514
515 StatementKind::Break if self.loop_depth() == 0 => {
517 self.error("break outside of loop", statement.span);
518 }
519 StatementKind::Continue if self.loop_depth() == 0 => {
520 self.error("continue outside of loop", statement.span);
521 }
522
523 StatementKind::ImportFile { path } | StatementKind::ImportFileNamed { path, .. } => {
525 self.import_module(path, statement.span);
526 }
527 StatementKind::Import { .. } => {}
528
529 StatementKind::DestructureDeclaration { bindings, value } => {
530 let value_type = self.check_expression(*value);
531 let tuple_types = match &value_type {
532 CheckType::Known(
533 TypeAnnotation::Tuple(types) | TypeAnnotation::CTuple(types),
534 ) => Some(types.clone()),
535 CheckType::Unknown => None,
536 other => {
537 self.error(
538 format!(
539 "expected tuple on right side of destructure, got {}",
540 other.info()
541 ),
542 statement.span,
543 );
544 None
545 }
546 };
547 if let Some(types) = tuple_types {
548 if types.len() != bindings.len() {
549 self.error(
550 format!(
551 "destructure mismatch: {} bindings but tuple has {} elements",
552 bindings.len(),
553 types.len()
554 ),
555 statement.span,
556 );
557 } else {
558 for ((type_annotation, name), actual) in bindings.iter().zip(types.iter()) {
559 let declared = CheckType::Known(type_annotation.clone());
560 let actual = CheckType::Known(actual.clone());
561 if !actual.matches(&declared) {
562 self.error(
563 format!(
564 "destructure type mismatch: expected {}, got {}",
565 declared.info(),
566 actual.info()
567 ),
568 statement.span,
569 );
570 }
571 self.declare(name.clone(), declared, false, statement.span);
572 }
573 }
574 } else {
575 for (type_annotation, name) in bindings {
576 self.declare(
577 name.clone(),
578 CheckType::Known(type_annotation.clone()),
579 false,
580 statement.span,
581 );
582 }
583 }
584 }
585
586 StatementKind::Match { value, arms } => {
587 let val_type = self.check_expression(*value);
588 for (pattern, body) in arms {
589 if let MatchPattern::Literal(expr) = pattern {
590 let pat_type = self.check_expression(*expr);
591 if !pat_type.is_unknown() && !val_type.is_unknown() && pat_type != val_type
592 {
593 let expr_span = self.ast_arena.exprs.get(*expr).span;
594 self.error("match pattern type does not match value type", expr_span);
595 }
596 }
597 for stmt in body {
598 self.check_statement(stmt);
599 }
600 }
601 }
602
603 _ => {}
604 }
605 }
606
607 fn import_module(&mut self, path: &[String], span: Span) {
608 let import_name = format!("{}.rl", path.join("/"));
609 let import_path = match &self.base_dir {
610 Some(dir) => dir.join(&import_name),
611 None => PathBuf::from(&import_name),
612 };
613
614 let canonical = import_path
615 .canonicalize()
616 .unwrap_or_else(|_| import_path.clone());
617
618 if self.importing.contains(&canonical) {
619 self.error(format!("import cycle detected: {}", path.join("::")), span);
620 return;
621 }
622 if self.imported.contains(&canonical) {
623 return;
624 }
625
626 let Ok(source_text) = std::fs::read_to_string(&import_path) else {
627 self.error(
628 format!(
629 "cannot find module `{}` ({})",
630 path.join("::"),
631 import_path.display()
632 ),
633 span,
634 );
635 return;
636 };
637
638 let source_file = SourceFile::new(import_path.display().to_string(), source_text);
639 let Ok(tokens) = Tokenizer::lex(source_file.clone()) else {
640 self.error(
641 format!(
642 "module `{}` has syntax error and could not be lexed",
643 path.join("::")
644 ),
645 span,
646 );
647 return;
648 };
649
650 let Ok((imported_ast, stmts)) = Parser::parse(tokens, source_file) else {
651 self.error(
652 format!(
653 "module `{}` has syntax error and could not be parsed",
654 path.join("::")
655 ),
656 span,
657 );
658 return;
659 };
660
661 self.importing.push(canonical.clone());
662
663 let prev_ast = std::mem::replace(&mut self.ast_arena, imported_ast);
664
665 for stmt in &stmts {
666 match &stmt.kind {
667 StatementKind::FunctionDeclaration {
668 name,
669 params,
670 return_type,
671 ..
672 } => {
673 self.declare(
674 name.clone(),
675 CheckType::Function {
676 params: params.iter().map(|p| p.param_type.clone()).collect(),
677 return_type: return_type.clone(),
678 },
679 false,
680 stmt.span,
681 );
682 }
683 StatementKind::VariableDeclaration {
684 name,
685 type_annotation,
686 ..
687 } => {
688 self.declare(
689 name.clone(),
690 CheckType::Known(type_annotation.clone()),
691 false,
692 stmt.span,
693 );
694 }
695 StatementKind::ConstantDeclaration {
696 name,
697 type_annotation,
698 ..
699 } => {
700 self.declare(
701 name.clone(),
702 CheckType::Known(type_annotation.clone()),
703 true,
704 stmt.span,
705 );
706 }
707 StatementKind::Array {
708 name,
709 type_annotation,
710 ..
711 } => {
712 self.declare(
713 name.clone(),
714 CheckType::Known(TypeAnnotation::Array(Box::new(type_annotation.clone()))),
715 false,
716 stmt.span,
717 );
718 }
719 StatementKind::ConstantArray {
720 name,
721 type_annotation,
722 ..
723 } => {
724 self.declare(
725 name.clone(),
726 CheckType::Known(TypeAnnotation::CArray(Box::new(type_annotation.clone()))),
727 true,
728 stmt.span,
729 );
730 }
731
732 StatementKind::Set {
733 name,
734 type_annotation,
735 ..
736 } => {
737 self.declare(
738 name.clone(),
739 CheckType::Known(TypeAnnotation::Set(Box::new(type_annotation.clone()))),
740 false,
741 stmt.span,
742 );
743 }
744 StatementKind::ConstantSet {
745 name,
746 type_annotation,
747 ..
748 } => {
749 self.declare(
750 name.clone(),
751 CheckType::Known(TypeAnnotation::CSet(Box::new(type_annotation.clone()))),
752 true,
753 stmt.span,
754 );
755 }
756
757 StatementKind::Map {
758 name,
759 type_annotation,
760 ..
761 } => {
762 self.declare(
763 name.clone(),
764 CheckType::Known(type_annotation.clone()),
765 false,
766 stmt.span,
767 );
768 }
769 StatementKind::ConstantMap {
770 name,
771 type_annotation,
772 ..
773 } => {
774 self.declare(
775 name.clone(),
776 CheckType::Known(type_annotation.clone()),
777 true,
778 stmt.span,
779 );
780 }
781
782 StatementKind::ImportFile { path: nested }
783 | StatementKind::ImportFileNamed { path: nested, .. } => {
784 self.import_module(nested, stmt.span);
785 }
786
787 _ => {}
788 }
789 }
790
791 self.ast_arena = prev_ast;
792 self.importing.pop();
793 self.imported.insert(canonical);
794 }
795}