Skip to main content

rl_lang/checker/statements/
statement.rs

1//! Statement type checking - walks every [`StatementKind`] variant,
2//! declares names into scope, and validates control flow constraints.
3
4use 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    // checks the current statement and push errors via error() if any found
16    pub fn check_statement(&mut self, statement: &crate::ast::statements::Statement) {
17        match &statement.kind {
18            // checks if the type null or same type then declare it as variable
19            // otherwise pushs error
20            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            // checks if the type is null or same type and declares it as
43            // constant otherwise pushs error
44            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            // checks the array if valid or not and declares it with correct
67            // type (weather constant or variable) otherwise pushs error
68            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            // checks map entries against the declared key/value types and
179            // declares it with the correct mutability.
180            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            // offloads to expression checker
286            StatementKind::Expression(expr) => {
287                self.check_expression(*expr);
288            }
289
290            // loops checker
291            StatementKind::While { condition, body } => {
292                // is condition type is bool?
293                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                // add loop depth
309                self.enter_loop();
310                // checks the blocks
311                self.check_block(body);
312                // remove loop depth
313                self.exit_loop();
314            }
315
316            StatementKind::For {
317                initializer,
318                condition,
319                increment,
320                body,
321            } => {
322                // add scope level
323                self.push_scope();
324                // is the initializer correct?
325                self.check_statement(initializer);
326                // is the condition bool?
327                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                // is the increment correct?
340                self.check_expression(*increment);
341                // add loop depth
342                self.enter_loop();
343                // is body correct?
344                for stmt in body {
345                    self.check_statement(stmt);
346                }
347                // remove loop depth
348                self.exit_loop();
349                // remove scope level
350                self.pop_scope();
351            }
352
353            StatementKind::ForRange {
354                variable,
355                range,
356                body,
357            } => {
358                // range for StatementKind::Range
359                let _ = range;
360                // add loop depth
361                self.enter_loop();
362                // add scope level
363                self.push_scope();
364                // declare the range variable
365                self.declare(
366                    variable.clone(),
367                    CheckType::Known(TypeAnnotation::Int),
368                    false,
369                    statement.span,
370                );
371                // is the body correct?
372                for stmt in body {
373                    self.check_statement(stmt);
374                }
375                // remove scope level
376                self.pop_scope();
377                // remove loop depth
378                self.exit_loop();
379            }
380
381            StatementKind::ForEach {
382                variable,
383                iterable,
384                body,
385            } => {
386                // is the iterable correct?
387                let iter_type = self.check_expression(*iterable);
388                // is the ieterable items correct?
389                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                // add loop depth
405                self.enter_loop();
406                // add scope depth
407                self.push_scope();
408                // declares the iterable variable
409                self.declare(variable.clone(), item_type, false, statement.span);
410                // is body correct?
411                for stmt in body {
412                    self.check_statement(stmt);
413                }
414                // remove scope depth
415                self.pop_scope();
416                // remove loop depth
417                self.exit_loop();
418            }
419
420            StatementKind::Range(_) => {}
421
422            // if - else if - else
423            StatementKind::ConditionalBranch {
424                condition, body, ..
425            } => {
426                // is there condition? or is it else?
427                if let Some(cond) = condition {
428                    // is the condition bool?
429                    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                // is the body correect?
443                self.check_block(body);
444            }
445
446            StatementKind::Conditional {
447                if_branch,
448                else_branch,
449            } => {
450                // is the branch correct?
451                self.check_statement(if_branch);
452                // if there is another branch is it correct?
453                if let Some(branch) = else_branch {
454                    self.check_statement(branch);
455                }
456            }
457
458            // functions and lambdas
459            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                // is the expression a valid type? otherwise null
483                let actual_type = match expr {
484                    Some(e) => self.check_expression(*e),
485                    None => CheckType::Known(TypeAnnotation::Null),
486                };
487                // is the actual type same as the expected return one?
488                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                    // return outside a function
511                    self.error("return outside of function", statement.span);
512                }
513            }
514
515            // checks weather break or continue used outside of loops
516            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            // runtime job maybe revisting later
524            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}