Skip to main content

dada_ir_sym/check/
functions.rs

1use crate::{
2    check::{CheckTyInEnv, signature::PreparedEnv},
3    ir::{
4        classes::SymAggregate,
5        functions::{SymFunction, SymFunctionSource},
6    },
7};
8use dada_ir_ast::{
9    ast::{AstAggregate, AstBlock, AstMainFunction},
10    diagnostic::{Diagnostic, Err, Level},
11};
12use dada_parser::prelude::FunctionBlock;
13
14use crate::{
15    check::runtime::Runtime,
16    check::signature::prepare_env,
17    ir::exprs::{SymExpr, SymExprKind, SymPlaceExpr, SymPlaceExprKind},
18};
19
20use super::{
21    CheckExprInEnv, env::Env, live_places::LivePlaces, report::InvalidReturnValue,
22    resolve::Resolver, statements::check_block_statements,
23};
24
25pub(crate) fn check_function_body<'db>(
26    db: &'db dyn crate::Db,
27    function: SymFunction<'db>,
28) -> Option<SymExpr<'db>> {
29    match function.source(db) {
30        SymFunctionSource::Function(ast_function) => {
31            let block = ast_function.body_block(db)?;
32            Some(check_function_body_ast_block(db, function, block))
33        }
34        SymFunctionSource::MainFunction(mfunc) => Some(check_main_function(db, function, mfunc)),
35        SymFunctionSource::Constructor(sym_class, ast_class_item) => Some(
36            check_function_body_class_constructor(db, function, sym_class, ast_class_item),
37        ),
38    }
39}
40
41fn check_main_function<'db>(
42    db: &'db dyn crate::Db,
43    function: SymFunction<'db>,
44    mfunc: AstMainFunction<'db>,
45) -> SymExpr<'db> {
46    Runtime::execute(
47        db,
48        mfunc.statements(db).span,
49        "check_main_function",
50        &[&mfunc],
51        async move |runtime| -> SymExpr<'db> {
52            let mut env: Env<'db> = Env::new(runtime, function.scope(db));
53            let statements = mfunc.statements(db);
54            let live_after = LivePlaces::none(&env);
55            check_block_statements(&mut env, live_after, statements.span, statements).await
56        },
57        |expr| expr,
58    )
59}
60
61/// Check the automatic construct that results when user writes parentheses, like `class Foo(...)`.
62fn check_function_body_class_constructor<'db>(
63    db: &'db dyn crate::Db,
64    function: SymFunction<'db>,
65    sym_class: SymAggregate<'db>,
66    ast_class_item: AstAggregate<'db>,
67) -> SymExpr<'db> {
68    Runtime::execute(
69        db,
70        function.name_span(db),
71        "check_function_body_class_constructor",
72        &[&function, &sym_class, &ast_class_item],
73        async move |runtime| -> SymExpr<'db> {
74            let PreparedEnv {
75                ref mut env,
76                input_symbols,
77                input_tys,
78                ..
79            } = prepare_env(db, runtime, function).await;
80
81            let scope = env.scope.clone();
82            let self_ty = sym_class.self_ty(db, &scope).check_in_env(env).await;
83            let span = ast_class_item.inputs(db).as_ref().unwrap().span;
84            let fields = sym_class.fields(db).collect::<Vec<_>>();
85            assert_eq!(input_symbols.len(), input_tys.len());
86
87            // Careful: Not allowed to declare other fields.
88            let parameter_exprs = input_symbols.iter().zip(&input_tys).map(|(&v, &ty)| {
89                SymPlaceExpr::new(db, v.span(db), ty, SymPlaceExprKind::Var(v)).give(db)
90            });
91
92            // The first N fields will be the inputs declared in parentheses.
93            // But if user declared additional fields, that's an error for now.
94            // Eventually perhaps we can support default values.
95            let other_exprs = fields[input_symbols.len()..].iter().map(|sym_field| {
96                SymExpr::err(
97                    db,
98                    Diagnostic::error(
99                        db,
100                        sym_field.name_span(db),
101                        "cannot have both explicit fields and an automatic constructor".to_string(),
102                    )
103                    .label(
104                        db,
105                        Level::Error,
106                        sym_field.name_span(db),
107                        "I found an explicit field declaration here".to_string(),
108                    )
109                    .label(
110                        db,
111                        Level::Info,
112                        span,
113                        "I also found an automatic class constructor here",
114                    )
115                    .report(db),
116                )
117            });
118
119            SymExpr::new(
120                db,
121                span,
122                self_ty,
123                SymExprKind::Aggregate {
124                    ty: self_ty,
125                    fields: parameter_exprs.chain(other_exprs).collect(),
126                },
127            )
128        },
129        |expr| expr,
130    )
131}
132
133fn check_function_body_ast_block<'db>(
134    db: &'db dyn crate::Db,
135    function: SymFunction<'db>,
136    body: AstBlock<'db>,
137) -> SymExpr<'db> {
138    Runtime::execute(
139        db,
140        function.name_span(db),
141        "check_function_body_ast_block",
142        &[&function, &body],
143        async move |runtime| {
144            let PreparedEnv {
145                mut env,
146                output_ty_body,
147                ..
148            } = prepare_env(db, runtime, function).await;
149
150            env.log("check_function_body_ast_block", &[&function, &body]);
151            let live_after = LivePlaces::none(&env);
152            let expr = body.check_in_env(&mut env, live_after).await;
153            env.spawn_require_assignable_type(
154                live_after,
155                expr.ty(db),
156                output_ty_body,
157                &InvalidReturnValue::new(expr, output_ty_body),
158            );
159            (env, expr)
160        },
161        |(mut env, expr)| Resolver::new(&mut env).resolve(expr),
162    )
163}