Skip to main content

dada_ir_sym/check/
statements.rs

1use dada_ir_ast::{ast::AstStatement, span::Span};
2use dada_util::boxed_async_fn;
3
4use crate::{
5    check::{CheckExprInEnv, env::Env, report::InvalidInitializerType},
6    ir::{
7        exprs::{SymExpr, SymExprKind},
8        types::SymTy,
9        variables::SymVariable,
10    },
11};
12
13use super::{CheckTyInEnv, live_places::LivePlaces};
14
15#[boxed_async_fn]
16pub async fn check_block_statements<'db>(
17    env: &mut Env<'db>,
18    live_after: LivePlaces,
19    block_span: Span<'db>,
20    statements: &[AstStatement<'db>],
21) -> SymExpr<'db> {
22    let db = env.db();
23
24    let Some((first, rest)) = statements.split_first() else {
25        return SymExpr::new(db, block_span, SymTy::unit(db), SymExprKind::Tuple(vec![]));
26    };
27
28    match first {
29        AstStatement::Let(s) => {
30            let lv = SymVariable::new_local(db, s.name(db).id, s.name(db).span);
31
32            // For explicit local variables, we compute their type as a full symbol type first.
33            let ty = match s.ty(db) {
34                Some(ty) => ty.check_in_env(env).await,
35                None => env.fresh_ty_inference_var(s.name(db).span),
36            };
37
38            let (initializer, body) = env
39                .join(
40                    async |env| match s.initializer(db) {
41                        Some(initializer) => {
42                            let initializer = initializer
43                                .check_in_env(env, LivePlaces::fixme())
44                                .await
45                                .into_expr_with_enclosed_temporaries(env);
46                            env.spawn_require_assignable_type(
47                                LivePlaces::fixme(),
48                                initializer.ty(db),
49                                ty,
50                                &InvalidInitializerType::new(lv, s.name(db).span, ty, initializer),
51                            );
52                            Some(initializer)
53                        }
54
55                        None => None,
56                    },
57                    async |env| {
58                        env.push_program_variable_with_ty(lv, ty);
59                        check_block_statements(env, LivePlaces::fixme(), block_span, rest).await
60                    },
61                )
62                .await;
63
64            // Create `let lv: ty = lv = initializer; remainder`
65            let span = s.span(db).to(db, body.span(db));
66            SymExpr::new(
67                db,
68                span,
69                body.ty(db),
70                SymExprKind::LetIn {
71                    lv,
72                    ty,
73                    initializer,
74                    body,
75                },
76            )
77        }
78
79        AstStatement::Expr(e) => {
80            let check_e = async |env: &mut Env<'db>| {
81                e.check_in_env(env, LivePlaces::fixme())
82                    .await
83                    .into_expr_with_enclosed_temporaries(env)
84            };
85            if rest.is_empty() {
86                // Subtle-ish: if this is the last statement in the block,
87                // it becomes the result of the block.
88                check_e(env).await
89            } else {
90                let (ce, re) = env
91                    .join(check_e, async |env| {
92                        check_block_statements(env, live_after, block_span, rest).await
93                    })
94                    .await;
95                SymExpr::new(
96                    db,
97                    ce.span(db).to(db, re.span(db)),
98                    re.ty(db),
99                    SymExprKind::Semi(ce, re),
100                )
101            }
102        }
103    }
104}