|
| 1 | +//! Lowering of `HirMatchExpression` to MIR. |
| 2 | +//! |
| 3 | +//! The HIR provides a scrutinee assignment plus a list of `MatchClause`s, |
| 4 | +//! each made of a sequence of `Test`/`Bind` components and a body. We lower |
| 5 | +//! to a chain of nested `If`s; each clause's components fold into a single |
| 6 | +//! Bool-typed test chain that interleaves binds (as `LVarDecl`) and tests |
| 7 | +//! (as `If(t, rest, false)`), so no expression is duplicated. |
| 8 | +use crate::mir; |
| 9 | +use skc_hir::pattern_match::{Component, MatchClause}; |
| 10 | +use skc_hir::{HirExpression, HirExpressionBase}; |
| 11 | + |
| 12 | +impl<'a> super::Compiler<'a> { |
| 13 | + pub(super) fn convert_match_expr( |
| 14 | + &mut self, |
| 15 | + cond_assign_expr: HirExpression, |
| 16 | + clauses: Vec<MatchClause>, |
| 17 | + ) -> mir::TypedExpr { |
| 18 | + // The cond_assign_expr is always a HirLVarAssign($exprN, scrutinee) |
| 19 | + // produced by skc_ast2hir. Convert it to LVarDecl so insert_allocs |
| 20 | + // emits the alloca for the temp. |
| 21 | + let scrutinee_decl = match cond_assign_expr.node { |
| 22 | + HirExpressionBase::HirLVarAssign { name, rhs } => { |
| 23 | + let mir_rhs = self.convert_expr(*rhs); |
| 24 | + mir::Expr::lvar_decl(name, mir_rhs, false) |
| 25 | + } |
| 26 | + other => panic!( |
| 27 | + "[BUG] expected HirLVarAssign as match cond_assign_expr, got {:?}", |
| 28 | + other |
| 29 | + ), |
| 30 | + }; |
| 31 | + let body = self.build_match_clauses(clauses); |
| 32 | + mir::Expr::exprs(vec![scrutinee_decl, body]) |
| 33 | + } |
| 34 | + |
| 35 | + fn build_match_clauses(&mut self, clauses: Vec<MatchClause>) -> mir::TypedExpr { |
| 36 | + // skc_ast2hir always appends a final unconditional clause whose body |
| 37 | + // panics. Recurse so it sits at the innermost else. |
| 38 | + let mut iter = clauses.into_iter(); |
| 39 | + let first = iter.next().expect("[BUG] match expression with no clauses"); |
| 40 | + if iter.len() == 0 { |
| 41 | + // Just the panic clause; emit its body unconditionally. |
| 42 | + return self.convert_expr(first.body_hir); |
| 43 | + } |
| 44 | + let rest: Vec<_> = iter.collect(); |
| 45 | + let cond = self.build_test_chain(first.components); |
| 46 | + let then_ = self.convert_expr(first.body_hir); |
| 47 | + let else_ = self.build_match_clauses(rest); |
| 48 | + mir::Expr::if_(cond, then_, else_) |
| 49 | + } |
| 50 | + |
| 51 | + fn build_test_chain(&mut self, components: Vec<Component>) -> mir::TypedExpr { |
| 52 | + let mut iter = components.into_iter(); |
| 53 | + match iter.next() { |
| 54 | + None => mir::Expr::pseudo_var(mir::PseudoVar::True), |
| 55 | + Some(Component::Test(t)) => { |
| 56 | + let mir_t = self.convert_expr(t); |
| 57 | + let rest = self.build_test_chain(iter.collect()); |
| 58 | + mir::Expr::if_(mir_t, rest, mir::Expr::pseudo_var(mir::PseudoVar::False)) |
| 59 | + } |
| 60 | + Some(Component::Bind(name, e)) => { |
| 61 | + let mir_e = self.convert_expr(e); |
| 62 | + let decl = mir::Expr::lvar_decl(name, mir_e, false); |
| 63 | + let rest = self.build_test_chain(iter.collect()); |
| 64 | + mir::Expr::exprs(vec![decl, rest]) |
| 65 | + } |
| 66 | + } |
| 67 | + } |
| 68 | +} |
0 commit comments