ruka_mir_lower/
lowerer.rs

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/// MIR lowering error produced while translating checked HIR.
29#[derive(Debug, Error, Clone, PartialEq, Eq)]
30pub enum LowerError {
31    /// Call callee expression was not an identifier.
32    #[error("call target must be an identifier")]
33    InvalidCallTarget,
34    /// Called runtime function could not be resolved.
35    #[error("unknown runtime function `{name}`")]
36    UnknownFunction {
37        /// Function name that failed to resolve.
38        name: String,
39    },
40    /// Referenced local identifier was not found.
41    #[error("unknown identifier `{name}`")]
42    UnknownIdent {
43        /// Identifier name that failed to resolve.
44        name: String,
45    },
46    /// Move prefix was used with a non-binding operand.
47    #[error("`<-` move requires a named binding")]
48    MoveRequiresBinding,
49    /// Borrow prefix was used with a non-binding operand.
50    #[error("`&` borrow requires a named binding")]
51    BorrowRequiresBinding,
52    /// Prefix operator appeared outside call argument position.
53    #[error("prefix `{op}` is only valid in call arguments")]
54    PrefixOnlyValidInCallArgs {
55        /// Prefix operator name.
56        op: &'static str,
57    },
58    /// Meta splice reached runtime lowering unexpectedly.
59    #[error("meta splice survived to runtime lowering")]
60    UnexpectedRuntimeSplice,
61    /// Checker metadata required by lowering was missing.
62    #[error("missing checker type information for `{name}`")]
63    MissingTypeInfo {
64        /// Name of the expression or function lacking type info.
65        name: String,
66    },
67    /// Enum variant constructor name could not be resolved.
68    #[error("unknown enum variant constructor `{name}`")]
69    UnknownEnumVariant {
70        /// Variant constructor name.
71        name: String,
72    },
73    /// Checker/lowering compatibility mismatch was observed for one call argument.
74    #[error(
75        "call argument `{param}` to `{function}` is incompatible during lowering (expected `{expected}`, actual `{actual}`)"
76    )]
77    CallArgIncompatible {
78        /// Function name being lowered.
79        function: String,
80        /// Parameter name associated with the argument.
81        param: String,
82        /// Expected semantic type text.
83        expected: String,
84        /// Actual semantic type text.
85        actual: String,
86    },
87    /// Coercion requires a lowering plan not implemented in this stage.
88    #[error(
89        "call argument `{param}` to `{function}` needs coercion plan not implemented yet (expected `{expected}`, actual `{actual}`)"
90    )]
91    CallArgCoercionPlanUnsupported {
92        /// Function name being lowered.
93        function: String,
94        /// Parameter name associated with the argument.
95        param: String,
96        /// Expected semantic type text.
97        expected: String,
98        /// Actual semantic type text.
99        actual: String,
100    },
101    /// Function return type used an ownership sigil that should have been rejected earlier.
102    #[error("function `{function}` return type cannot use ownership sigils")]
103    ReturnTypeOwnershipNotAllowed {
104        /// Function name being lowered.
105        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, &param_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}