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
61fn 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 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 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}