1use std::collections::{BTreeMap, BTreeSet};
2
3use cranelift_entity::PrimaryMap;
4use thiserror::Error;
5
6use crate::cfg::build_cfg_from_body;
7use ruka_check::{CheckedProgram, Ty};
8use ruka_frontend::{
9 AssignMode, BinaryOp, IntrinsicId, MatchPattern, OwnershipMode, PrefixOp, RelationalOp,
10};
11use ruka_hir::{ExprId, HirExpr, HirFunction, HirProgram, HirStmt, StmtId};
12use ruka_mir::{
13 LocalInfo, LocalKind, MirAggregateArg, MirArgMode, MirCallArg, MirFuncId, MirFunction,
14 MirHeapOwnership, MirInstr, MirIntrinsic, MirLocalId, MirLocalRepr, MirOwnershipMode,
15 MirSourcePos, MirStmt, infer_heap_ownership,
16};
17
18mod call_args;
19mod expr;
20mod helpers;
21mod locals;
22mod stmt;
23mod type_lowering;
24
25use self::helpers::*;
26pub(super) use self::type_lowering::lower_struct_field_type;
27
28#[derive(Debug, Error, Clone, PartialEq, Eq)]
30pub enum LowerError {
31 #[error("call target must be an identifier")]
33 InvalidCallTarget,
34 #[error("unknown runtime function `{name}`")]
36 UnknownFunction {
37 name: String,
39 },
40 #[error("unknown identifier `{name}`")]
42 UnknownIdent {
43 name: String,
45 },
46 #[error("`<-` move requires a named binding")]
48 MoveRequiresBinding,
49 #[error("`&` borrow requires a named binding")]
51 BorrowRequiresBinding,
52 #[error("prefix `{op}` is only valid in call arguments")]
54 PrefixOnlyValidInCallArgs {
55 op: &'static str,
57 },
58 #[error("meta splice survived to runtime lowering")]
60 UnexpectedRuntimeSplice,
61 #[error("missing checker type information for `{name}`")]
63 MissingTypeInfo {
64 name: String,
66 },
67 #[error("unknown enum variant constructor `{name}`")]
69 UnknownEnumVariant {
70 name: String,
72 },
73 #[error(
75 "call argument `{param}` to `{function}` is incompatible during lowering (expected `{expected}`, actual `{actual}`)"
76 )]
77 CallArgIncompatible {
78 function: String,
80 param: String,
82 expected: String,
84 actual: String,
86 },
87 #[error(
89 "call argument `{param}` to `{function}` needs coercion plan not implemented yet (expected `{expected}`, actual `{actual}`)"
90 )]
91 CallArgCoercionPlanUnsupported {
92 function: String,
94 param: String,
96 expected: String,
98 actual: String,
100 },
101 #[error("function `{function}` return type cannot use ownership sigils")]
103 ReturnTypeOwnershipNotAllowed {
104 function: String,
106 },
107}
108
109pub(super) struct FunctionLowerer<'a> {
110 source_program: &'a HirProgram,
111 checked: &'a CheckedProgram,
112 source_function: &'a HirFunction,
113 function_names: &'a BTreeMap<String, MirFuncId>,
114 locals: PrimaryMap<MirLocalId, LocalInfo>,
115 scopes: Vec<ScopeFrame>,
116 moved_locals: BTreeSet<u32>,
117}
118
119struct ScopeFrame {
120 names: BTreeMap<String, MirLocalId>,
121 owned_locals: Vec<MirLocalId>,
122}
123
124impl<'a> FunctionLowerer<'a> {
125 pub(super) fn new(
126 source_program: &'a HirProgram,
127 checked: &'a CheckedProgram,
128 function_names: &'a BTreeMap<String, MirFuncId>,
129 source_function: &'a HirFunction,
130 ) -> Self {
131 Self {
132 source_program,
133 checked,
134 source_function,
135 function_names,
136 locals: PrimaryMap::new(),
137 scopes: vec![ScopeFrame {
138 names: BTreeMap::new(),
139 owned_locals: Vec::new(),
140 }],
141 moved_locals: BTreeSet::new(),
142 }
143 }
144
145 pub(super) fn lower(mut self) -> Result<MirFunction, LowerError> {
146 if self.source_function.return_type.mode != OwnershipMode::View {
147 return Err(LowerError::ReturnTypeOwnershipNotAllowed {
148 function: self.source_function.name.clone(),
149 });
150 }
151 let signature = self
152 .checked
153 .signatures
154 .get(&self.source_function.name)
155 .ok_or_else(|| LowerError::MissingTypeInfo {
156 name: self.source_function.name.clone(),
157 })?;
158 let mut params = Vec::with_capacity(self.source_function.params.len());
159 for (param, param_sig) in self
160 .source_function
161 .params
162 .iter()
163 .zip(signature.params.iter())
164 {
165 let (local_ty, repr) = lowered_param_local(param.ty.mode, ¶m_sig.ty)?;
166 let local = self.new_local(Some(param.name.clone()), LocalKind::Param, local_ty, repr);
167 self.declare_local(param.name.clone(), local);
168 params.push(local);
169 }
170
171 let (mut body, value) = self.lower_stmt_list(&self.source_function.body, true)?;
172 let value = value.expect("function bodies always produce a value");
173 self.push_release_locals(&mut body, self.params_to_release(value));
174 body.push(MirStmt::Return { value });
175
176 let (entry, blocks) = build_cfg_from_body(&body);
177
178 let function = MirFunction {
179 name: self.source_function.name.clone(),
180 arity: self.source_function.params.len(),
181 return_ty: signature.return_ty.clone(),
182 locals: self.locals,
183 params,
184 param_modes: self
185 .source_function
186 .params
187 .iter()
188 .map(|param| lower_param_mode(param.ty.mode))
189 .collect(),
190 entry,
191 blocks,
192 };
193 function.assert_valid();
194 Ok(function)
195 }
196}