diff options
-rw-r--r-- | src/mir/from_hir.cpp | 281 |
1 files changed, 260 insertions, 21 deletions
diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index 487942b5..d7c7992c 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -5,6 +5,7 @@ * mir/from_hir.hpp * - Construction of MIR from the HIR expression tree */ +#include <type_traits> // for TU_MATCHA #include "mir.hpp" #include "mir_ptr.hpp" #include <hir/expr.hpp> @@ -17,8 +18,10 @@ namespace { ::MIR::Function& m_output; unsigned int m_current_block; + bool m_block_active; - unsigned int m_result_tmp_idx; + ::MIR::RValue m_result; + bool m_result_valid; struct LoopDesc { ::std::string label; @@ -36,30 +39,78 @@ namespace { m_output(output) {} + ::MIR::LValue new_temporary(const ::HIR::TypeRef& ty) + { + unsigned int rv = m_output.temporaries.size(); + m_output.temporaries.push_back( ty.clone() ); + return ::MIR::LValue::make_Temporary({rv}); + } + ::MIR::LValue lvalue_or_temp(const ::HIR::TypeRef& ty, ::MIR::RValue val) + { + TU_IFLET(::MIR::RValue, val, Use, e, + return mv$(e); + ) + else { + auto temp = this->new_temporary(ty); + this->push_stmt_assign( ::MIR::LValue(temp.as_Temporary()), mv$(val) ); + return temp; + } + } + + ::MIR::RValue get_result(const Span& sp) + { + if(!m_result_valid) { + BUG(sp, "No value avaliable"); + } + auto rv = mv$(m_result); + m_result_valid = false; + return rv; + } + ::MIR::LValue get_result_lvalue(const Span& sp) + { + auto rv = get_result(sp); + TU_IFLET(::MIR::RValue, rv, Use, e, + return mv$(e); + ) + else { + BUG(sp, "LValue expected, got RValue"); + } + } + void set_result(const Span& sp, ::MIR::RValue val) { + if(m_result_valid) { + BUG(Span(), "Pushing a result over an existing result"); + } + m_result = mv$(val); + m_result_valid = true; + } void push_stmt_assign(::MIR::LValue dst, ::MIR::RValue val) { + assert(m_block_active); m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Assign({ mv$(dst), mv$(val) }) ); } void push_stmt_drop(::MIR::LValue val) { + assert(m_block_active); m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Drop({ ::MIR::eDropKind::DEEP, mv$(val) }) ); } void end_block(::MIR::Terminator term) { - if( m_current_block == 0 && (m_output.blocks.size() > 2 || ! m_output.blocks[0].terminator.is_Return()) ) { + if( !m_block_active ) { BUG(Span(), "Terminating block when none active"); } m_output.blocks.at(m_current_block).terminator = mv$(term); + m_block_active = false; m_current_block = 0; } void set_cur_block(unsigned int new_block) { - if( m_current_block != 0 || m_output.blocks.size() <= 1) { + if( m_block_active ) { BUG(Span(), "Updating block when previous is active"); } m_current_block = new_block; + m_block_active = true; } ::MIR::BasicBlockId new_bb_linked() { @@ -75,27 +126,28 @@ namespace { return rv; } - void destructure_from(const Span& sp, const ::HIR::Pattern& pat, unsigned int temp_idx) + void destructure_from(const Span& sp, const ::HIR::Pattern& pat, ::MIR::LValue lval) { // TODO: Destructure + TODO(sp, "Destructure using " << pat); } // -- ExprVisitor void visit(::HIR::ExprNode_Block& node) override { - // TODO: Does this actually need to create a new BB? - // Creates a BB, all expressions end up as part of it (with all but the final expression having their results dropped) + // NOTE: This doesn't create a BB, as BBs are not needed for scoping if( node.m_nodes.size() > 0 ) { m_block_stack.push_back( {} ); for(unsigned int i = 0; i < node.m_nodes.size()-1; i ++) { - this->visit_node_ptr(node.m_nodes.back()); - this->push_stmt_drop( ::MIR::LValue::make_Temporary({m_result_tmp_idx}) ); + const Span& sp = node.m_nodes[i]->span(); + this->visit_node_ptr(node.m_nodes[i]); + this->push_stmt_drop( this->get_result_lvalue(sp) ); } this->visit_node_ptr(node.m_nodes.back()); - auto ret = m_result_tmp_idx; + auto ret = this->get_result(node.m_nodes.back()->span()); auto bd = mv$( m_block_stack.back() ); m_block_stack.pop_back(); @@ -105,7 +157,7 @@ namespace { this->push_stmt_drop( ::MIR::LValue::make_Variable(var_idx) ); } - m_result_tmp_idx = ret; + this->set_result(node.span(), mv$(ret)); } else { @@ -116,10 +168,8 @@ namespace { { this->visit_node_ptr(node.m_value); - this->push_stmt_assign( ::MIR::LValue::make_Return({}), ::MIR::RValue::make_Use( ::MIR::LValue::make_Temporary({m_result_tmp_idx}) ) ); + this->push_stmt_assign( ::MIR::LValue::make_Return({}), this->get_result(node.span()) ); this->end_block( ::MIR::Terminator::make_Return({}) ); - - m_result_tmp_idx = 0; } void visit(::HIR::ExprNode_Let& node) override { @@ -127,9 +177,8 @@ namespace { { this->visit_node_ptr(node.m_value); - this->destructure_from(node.span(), node.m_pattern, m_result_tmp_idx); + this->destructure_from(node.span(), node.m_pattern, this->lvalue_or_temp(node.m_type, this->get_result(node.span()) )); } - m_result_tmp_idx = 0; } void visit(::HIR::ExprNode_Loop& node) override { @@ -142,8 +191,6 @@ namespace { this->end_block( ::MIR::Terminator::make_Goto(loop_block) ); this->set_cur_block(loop_next); - - m_result_tmp_idx = 0; } void visit(::HIR::ExprNode_LoopControl& node) override { @@ -166,16 +213,208 @@ namespace { else { this->end_block( ::MIR::Terminator::make_Goto(target_block->next) ); } - m_result_tmp_idx = 0; } void visit(::HIR::ExprNode_Match& node) override { this->visit_node_ptr(node.m_value); - //auto match_val = m_result_tmp_idx; + //auto match_val = this->get_result(); - // TODO: How to convert an arbitary match into a MIR construct. - TODO(node.span(), "Convert match into MIR"); + if( node.m_arms.size() == 0 ) { + // Nothing + TODO(node.span(), "Handle zero-arm match"); + } + else if( node.m_arms.size() == 1 ) { + // - Shortcut: Single-arm match + TODO(node.span(), "Convert single-arm match"); + } + else { + // TODO: Convert patterns into sequence of switches and comparisons. + + // Build up a sorted vector of MIR pattern rules + TAGGED_UNION(PatternRule, Any, + (Any, struct {}), + (EnumVariant, unsigned int), + (Value, ::MIR::Constant) + ); + struct PatternRuleset { + ::std::vector<PatternRule> m_rules; + }; + struct PatternRulesetBuilder { + ::std::vector<PatternRule> m_rules; + void append_from(const Span& sp, const ::HIR::Pattern& pat, const ::HIR::TypeRef& ty) { + TU_MATCHA( (ty.m_data), (e), + (Infer, BUG(sp, "Ivar for in match type"); ), + (Diverge, BUG(sp, "Diverge in match type"); ), + (Primitive, + TU_MATCH_DEF(::HIR::Pattern::Data, (pat.m_data), (pe), + ( throw ""; ), + (Any, + m_rules.push_back( PatternRule::make_Any({}) ); + ), + (Value, + switch(e) + { + case ::HIR::CoreType::F32: + case ::HIR::CoreType::F64: + TODO(sp, "Match value float"); + break; + case ::HIR::CoreType::U8: + case ::HIR::CoreType::U16: + case ::HIR::CoreType::U32: + case ::HIR::CoreType::U64: + case ::HIR::CoreType::Usize: + TODO(sp, "Match value unsigned"); + break; + case ::HIR::CoreType::I8: + case ::HIR::CoreType::I16: + case ::HIR::CoreType::I32: + case ::HIR::CoreType::I64: + case ::HIR::CoreType::Isize: + TODO(sp, "Match value signed"); + break; + case ::HIR::CoreType::Bool: + TODO(sp, "Match value bool"); + break; + case ::HIR::CoreType::Char: + TODO(sp, "Match value char"); + break; + case ::HIR::CoreType::Str: + BUG(sp, "Hit match over `str` - must be `&str`"); + break; + } + ) + ) + ), + (Tuple, + TU_MATCH_DEF(::HIR::Pattern::Data, (pat.m_data), (pe), + ( throw ""; ), + (Any, + for(const auto& sty : e) + this->append_from(sp, pat, sty); + ), + (Tuple, + assert(e.size() == pe.sub_patterns.size()); + for(unsigned int i = 0; i < e.size(); i ++) + this->append_from(sp, pe.sub_patterns[i], e[i]); + ) + ) + ), + (Path, + // TODO: This is either a destructure or an enum + TODO(sp, "Match over path"); + ), + (Generic, + // TODO: Is this possible? - Single arm has already been handled + ERROR(sp, E0000, "Attempting to match over a generic"); + ), + (TraitObject, + ERROR(sp, E0000, "Attempting to match over a trait object"); + ), + (Array, + // TODO: Slice patterns, sequential comparison/sub-match + TODO(sp, "Match over array"); + ), + (Slice, + BUG(sp, "Hit match over `[T]` - must be `&[T]`"); + ), + (Borrow, + // TODO: Sub-match + TODO(sp, "Match over borrow"); + ), + (Pointer, + ERROR(sp, E0000, "Attempting to match over a pointer"); + ), + (Function, + ERROR(sp, E0000, "Attempting to match over a functon pointer"); + ), + (Closure, + ERROR(sp, E0000, "Attempting to match over a closure"); + ) + ) + } + PatternRuleset into_ruleset() { + return PatternRuleset { mv$(this->m_rules) }; + } + }; + + // Map of arm index to ruleset + ::std::vector< ::std::pair< unsigned int, PatternRuleset > > m_arm_rules; + for(const auto& arm : node.m_arms) { + auto idx = m_arm_rules.size(); + for( const auto& pat : arm.m_patterns ) + { + auto builder = PatternRulesetBuilder {}; + builder.append_from(node.span(), pat, node.m_value->m_res_type); + m_arm_rules.push_back( ::std::make_pair(idx, builder.into_ruleset()) ); + } + + if( arm.m_cond ) { + // - TODO: What to do with contidionals? + TODO(node.span(), "Handle conditional match arms (ordering matters)"); + } + } + + // TODO: Sort ruleset such that wildcards go last (if there's no conditionals) + + // TODO: Generate decision tree based on ruleset + + TODO(node.span(), "Convert match into MIR using ruleset"); + } + } // ExprNode_Match + + void visit(::HIR::ExprNode_If& node) override + { + this->visit_node_ptr(node.m_cond); + auto decision_val = this->lvalue_or_temp(node.m_cond->m_res_type, this->get_result(node.m_cond->span()) ); + + auto true_branch = this->new_bb_unlinked(); + auto false_branch = this->new_bb_unlinked(); + auto next_block = this->new_bb_unlinked(); + this->end_block( ::MIR::Terminator::make_If({ mv$(decision_val), true_branch, false_branch }) ); + + auto result_val = this->new_temporary(node.m_res_type); + + this->set_cur_block(true_branch); + this->visit_node_ptr(node.m_true); + this->end_block( ::MIR::Terminator::make_Goto(next_block) ); + this->push_stmt_assign( ::MIR::LValue::make_Temporary({result_val.as_Temporary().idx}), this->get_result(node.m_true->span()) ); + + this->set_cur_block(false_branch); + if( node.m_false ) + { + this->visit_node_ptr(node.m_false); + this->end_block( ::MIR::Terminator::make_Goto(next_block) ); + this->push_stmt_assign( ::MIR::LValue::make_Temporary({result_val.as_Temporary().idx}), this->get_result(node.m_false->span()) ); + } + else + { + // TODO: Assign `()` to the result + } + + this->set_cur_block(next_block); + this->set_result( node.span(), mv$(result_val) ); + } + + void visit(::HIR::ExprNode_Assign& node) override + { + const auto& sp = node.span(); + + this->visit_node_ptr(node.m_value); + auto val = this->get_result(sp); + + this->visit_node_ptr(node.m_slot); + auto dst = this->get_result_lvalue(sp); + + if( node.m_op != ::HIR::ExprNode_Assign::Op::None ) + { + // TODO: What about += on primitives? + ASSERT_BUG(sp, node.m_op == ::HIR::ExprNode_Assign::Op::None, "Operator overload assignments should already be eliminated"); + } + else + { + this->push_stmt_assign(mv$(dst), mv$(val)); + } } }; } |