1use std::collections::{BTreeMap, BTreeSet};
4
5use cranelift_entity::EntityRef;
6use ruka_frontend::{
7 AssignMode, AssignTarget, BinaryOp, IntrinsicId, OwnershipMode, PrefixOp, RelationalOp,
8 SourceSpan, TypeExpr,
9};
10use ruka_hir::{
11 ExprId, HirEnumDecl, HirExpr, HirExternFunction, HirFunction, HirMatchArm, HirProgram, HirStmt,
12 HirStructDecl, StmtId,
13};
14pub use ruka_types::Ty;
15
16use ruka_types::{can_safely_coerce_numeric, is_truncation_cast};
17
18mod checker_calls;
19mod checker_entry;
20mod checker_expr;
21mod checker_state;
22mod checker_stmt;
23mod decls;
24mod errors;
25mod utils;
26
27use decls::{
28 EnumDef, StructDef, build_enum_defs, build_struct_defs, populate_enum_defs,
29 resolve_extern_signature, resolve_signature, resolve_type_expr, split_variant_path,
30};
31pub use errors::CheckError;
32use utils::*;
33
34#[derive(Debug, Clone)]
36pub struct ParamSig {
37 pub name: String,
39 pub mode: OwnershipMode,
41 pub ty: Ty,
43 pub mutated_in_body: bool,
45}
46
47#[derive(Debug, Clone)]
49pub struct FunctionSig {
50 pub params: Vec<ParamSig>,
52 pub return_ty: Ty,
54}
55
56#[derive(Debug, Clone)]
58pub struct CheckedProgram {
59 pub signatures: BTreeMap<String, FunctionSig>,
61 pub extern_function_names: BTreeSet<String>,
63 expr_types: Vec<Ty>,
64 call_signatures: Vec<Option<FunctionSig>>,
65 local_symbols: Vec<LocalSymbol>,
66 local_symbol_occurrences: Vec<LocalSymbolOccurrence>,
67 expr_local_symbols: Vec<Option<usize>>,
68}
69
70#[derive(Debug, Clone)]
72pub struct LocalSymbol {
73 pub name: String,
75 pub ty: Ty,
77}
78
79#[derive(Debug, Clone)]
81pub struct LocalSymbolOccurrence {
82 pub symbol_id: usize,
84 pub name: String,
86 pub is_declaration: bool,
88 pub span: SourceSpan,
90}
91
92impl CheckedProgram {
93 pub fn expr_ty(&self, expr_id: ExprId) -> &Ty {
95 &self.expr_types[expr_id.index()]
96 }
97
98 pub fn call_signature(&self, expr_id: ExprId) -> Option<&FunctionSig> {
100 self.call_signatures[expr_id.index()].as_ref()
101 }
102
103 pub fn local_symbols(&self) -> &[LocalSymbol] {
105 &self.local_symbols
106 }
107
108 pub fn local_symbol_occurrences(&self) -> &[LocalSymbolOccurrence] {
110 &self.local_symbol_occurrences
111 }
112
113 pub fn expr_local_symbol(&self, expr_id: ExprId) -> Option<usize> {
115 self.expr_local_symbols[expr_id.index()]
116 }
117}
118
119pub fn check_program(program: &HirProgram) -> Result<CheckedProgram, CheckError> {
121 if let Some(duplicate) = program.duplicate_extern_functions.first() {
122 return Err(CheckError::DuplicateExternFunction {
123 name: duplicate.name.clone(),
124 first_decl: duplicate.first_span,
125 redecl: duplicate.duplicate_span,
126 });
127 }
128
129 let mut enum_defs = build_enum_defs(&program.enums)?;
130 let struct_defs = build_struct_defs(&program.structs, &enum_defs)?;
131 populate_enum_defs(&mut enum_defs, &program.enums, &struct_defs)?;
132 utils::validate_finite_value_types(&struct_defs, &enum_defs)?;
133
134 let mut signatures = BTreeMap::new();
135 let mut extern_function_names = BTreeSet::new();
136 for (name, function) in &program.extern_functions {
137 signatures.insert(
138 name.clone(),
139 resolve_extern_signature(function, &struct_defs, &enum_defs)?,
140 );
141 let _ = extern_function_names.insert(name.clone());
142 }
143 for (_, function) in program.functions.iter() {
144 signatures.insert(
145 function.name.clone(),
146 resolve_signature(function, &struct_defs, &enum_defs)?,
147 );
148 }
149
150 let mut checker = Checker {
151 program,
152 signatures,
153 expr_types: vec![Ty::Unit; program.expressions.len()],
154 call_signatures: vec![None; program.expressions.len()],
155 local_symbols: Vec::new(),
156 local_symbol_occurrences: Vec::new(),
157 expr_local_symbols: vec![None; program.expressions.len()],
158 scopes: Vec::new(),
159 loan_scopes: Vec::new(),
160 active_loans: Vec::new(),
161 struct_defs,
162 enum_defs,
163 };
164
165 for (_, function) in program.functions.iter() {
166 checker.check_function(function)?;
167 }
168
169 Ok(CheckedProgram {
170 signatures: checker.signatures,
171 extern_function_names,
172 expr_types: checker.expr_types,
173 call_signatures: checker.call_signatures,
174 local_symbols: checker.local_symbols,
175 local_symbol_occurrences: checker.local_symbol_occurrences,
176 expr_local_symbols: checker.expr_local_symbols,
177 })
178}
179
180struct Checker<'a> {
181 program: &'a HirProgram,
182 signatures: BTreeMap<String, FunctionSig>,
183 expr_types: Vec<Ty>,
184 call_signatures: Vec<Option<FunctionSig>>,
185 local_symbols: Vec<LocalSymbol>,
186 local_symbol_occurrences: Vec<LocalSymbolOccurrence>,
187 expr_local_symbols: Vec<Option<usize>>,
188 scopes: Vec<BTreeMap<String, LocalBinding>>,
189 loan_scopes: Vec<Vec<usize>>,
190 active_loans: Vec<Loan>,
191 struct_defs: BTreeMap<String, StructDef>,
192 enum_defs: BTreeMap<String, EnumDef>,
193}
194
195#[derive(Debug, Clone)]
196struct LocalBinding {
197 ty: Ty,
198 binding_kind: BindingKind,
199 place: Option<Place>,
200 symbol_id: usize,
201 moved: bool,
202 was_mutated: bool,
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206enum LoanKind {
207 Shared,
208 Mutable,
209}
210
211#[derive(Debug, Clone, PartialEq, Eq)]
212enum Projection {
213 Field(String),
214 IndexLike,
215}
216
217#[derive(Debug, Clone, PartialEq, Eq)]
218struct Place {
219 root: String,
220 projections: Vec<Projection>,
221}
222
223#[derive(Debug, Clone)]
224struct Loan {
225 place: Place,
226 kind: LoanKind,
227 owner: String,
228 active: bool,
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232enum BindingKind {
233 ReadOnlyAlias,
234 ReadOnlyOwned,
235 Owned,
236 RefMut,
237}
238
239impl BindingKind {
240 fn allows_assignment(self) -> bool {
242 matches!(self, Self::Owned | Self::RefMut)
243 }
244
245 fn allows_mut_borrow(self) -> bool {
247 matches!(self, Self::Owned | Self::RefMut)
248 }
249
250 fn allows_move(self) -> bool {
252 matches!(self, Self::Owned)
253 }
254}