summaryrefslogtreecommitdiff
path: root/src/mir/from_hir.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mir/from_hir.cpp')
-rw-r--r--src/mir/from_hir.cpp281
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));
+ }
}
};
}