summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Hodge <tpg@mutabah.net>2016-08-21 16:51:40 +0800
committerJohn Hodge <tpg@mutabah.net>2016-08-21 16:51:40 +0800
commit18be0631d249ed11a1ee433cfb39583dcc342687 (patch)
treebdb7e97cc0b2f631d3aabe8c6880056ca2a67381
parentaa2ad69f635eeb57f58775ca907cac11b6cf48a4 (diff)
downloadmrust-18be0631d249ed11a1ee433cfb39583dcc342687.tar.gz
MIR Gen - Move `MirBuilder` class to its own file
-rw-r--r--Makefile2
-rw-r--r--src/mir/from_hir.cpp652
-rw-r--r--src/mir/mir_builder.cpp663
3 files changed, 664 insertions, 653 deletions
diff --git a/Makefile b/Makefile
index 013bcdf3..a0632941 100644
--- a/Makefile
+++ b/Makefile
@@ -64,7 +64,7 @@ OBJ += hir_typeck/expr_check.o
OBJ += hir_expand/annotate_value_usage.o hir_expand/closures.o hir_expand/ufcs_everything.o
OBJ += mir/mir.o mir/mir_ptr.o
OBJ += mir/dump.o
-OBJ += mir/from_hir.o mir/from_hir_match.o
+OBJ += mir/from_hir.o mir/from_hir_match.o mir/mir_builder.o
PCHS := ast/ast.hpp
diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp
index 57e25b9c..01eda781 100644
--- a/src/mir/from_hir.cpp
+++ b/src/mir/from_hir.cpp
@@ -1404,658 +1404,6 @@ namespace {
}
// --------------------------------------------------------------------
-// MirBuilder
-// --------------------------------------------------------------------
-MirBuilder::MirBuilder(::MIR::Function& output):
- m_output(output),
- m_block_active(false),
- m_result_valid(false),
- m_fcn_scope(*this, 0)
-{
- set_cur_block( new_bb_unlinked() );
- m_scopes.push_back( ScopeDef {} );
- m_scope_stack.push_back( 0 );
-
- m_scopes.push_back( ScopeDef { false, ScopeType::make_Temporaries({}) } );
- m_scope_stack.push_back( 1 );
-}
-MirBuilder::~MirBuilder()
-{
- if( has_result() )
- {
- push_stmt_assign( ::MIR::LValue::make_Return({}), get_result(Span()) );
- }
- if( block_active() )
- {
- terminate_scope( Span(), ScopeHandle { *this, 1 } );
- terminate_scope( Span(), mv$(m_fcn_scope) );
- end_block( ::MIR::Terminator::make_Return({}) );
- }
-}
-
-void MirBuilder::define_variable(unsigned int idx)
-{
- DEBUG("DEFINE var" << idx /* << ": " << m_output.named_variables.at(idx)*/);
- for( auto scope_idx : ::reverse(m_scope_stack) )
- {
- auto& scope_def = m_scopes.at(scope_idx);
- TU_MATCH_DEF( ScopeType, (scope_def.data), (e),
- (
- ),
- (Variables,
- auto it = ::std::find(e.vars.begin(), e.vars.end(), idx);
- assert(it == e.vars.end());
- e.vars.push_back( idx );
- e.var_states.push_back( VarState::Uninit );
- return ;
- ),
- (Split,
- BUG(Span(), "Variable " << idx << " introduced within a Split");
- )
- )
- }
- BUG(Span(), "Variable " << idx << " introduced with no Variable scope");
-}
-::MIR::LValue MirBuilder::new_temporary(const ::HIR::TypeRef& ty)
-{
- unsigned int rv = m_output.temporaries.size();
- DEBUG("DEFINE tmp" << rv << ": " << ty);
-
- m_output.temporaries.push_back( ty.clone() );
- temporaries_valid.push_back(false);
-
- ScopeDef* top_scope = nullptr;
- for(unsigned int i = m_scope_stack.size(); i --; )
- {
- auto idx = m_scope_stack[i];
- if( m_scopes.at( idx ).data.is_Temporaries() ) {
- top_scope = &m_scopes.at(idx);
- break ;
- }
- }
- assert( top_scope );
- auto& tmp_scope = top_scope->data.as_Temporaries();
- tmp_scope.temporaries.push_back( rv );
- tmp_scope.states.push_back( VarState::Uninit );
- return ::MIR::LValue::make_Temporary({rv});
-}
-::MIR::LValue MirBuilder::lvalue_or_temp(const ::HIR::TypeRef& ty, ::MIR::RValue val)
-{
- TU_IFLET(::MIR::RValue, val, Use, e,
- return mv$(e);
- )
- else {
- auto temp = new_temporary(ty);
- push_stmt_assign( ::MIR::LValue(temp.as_Temporary()), mv$(val) );
- return temp;
- }
-}
-
-::MIR::RValue MirBuilder::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 MirBuilder::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 MirBuilder::set_result(const Span& sp, ::MIR::RValue val)
-{
- if(m_result_valid) {
- BUG(sp, "Pushing a result over an existing result");
- }
- m_result = mv$(val);
- m_result_valid = true;
-}
-
-void MirBuilder::push_stmt_assign(::MIR::LValue dst, ::MIR::RValue val)
-{
- ASSERT_BUG(Span(), m_block_active, "Pushing statement with no active block");
- ASSERT_BUG(Span(), dst.tag() != ::MIR::LValue::TAGDEAD, "");
- ASSERT_BUG(Span(), val.tag() != ::MIR::RValue::TAGDEAD, "");
-
- TU_MATCHA( (val), (e),
- (Use,
- this->moved_lvalue(e);
- ),
- (Constant,
- ),
- (SizedArray,
- this->moved_lvalue(e.val);
- ),
- (Borrow,
- if( e.type == ::HIR::BorrowType::Owned ) {
- TODO(Span(), "Move using &move");
- // Likely would require a marker that ensures that the memory isn't reused.
- this->moved_lvalue(e.val);
- }
- else {
- // Doesn't move
- }
- ),
- (Cast,
- this->moved_lvalue(e.val);
- ),
- (BinOp,
- switch(e.op)
- {
- case ::MIR::eBinOp::EQ:
- case ::MIR::eBinOp::NE:
- case ::MIR::eBinOp::GT:
- case ::MIR::eBinOp::GE:
- case ::MIR::eBinOp::LT:
- case ::MIR::eBinOp::LE:
- // Takes an implicit borrow... and only works on copy, so why is this block here?
- break;
- default:
- this->moved_lvalue(e.val_l);
- this->moved_lvalue(e.val_r);
- break;
- }
- ),
- (UniOp,
- this->moved_lvalue(e.val);
- ),
- (DstMeta,
- // Doesn't move
- ),
- (MakeDst,
- // Doesn't move ptr_val
- this->moved_lvalue(e.meta_val);
- ),
- (Tuple,
- for(const auto& val : e.vals)
- this->moved_lvalue(val);
- ),
- (Array,
- for(const auto& val : e.vals)
- this->moved_lvalue(val);
- ),
- (Struct,
- for(const auto& val : e.vals)
- this->moved_lvalue(val);
- )
- )
-
- // Drop target if populated
- TU_MATCH_DEF(::MIR::LValue, (dst), (e),
- (
- ),
- (Temporary,
- switch(get_temp_state(e.idx))
- {
- case VarState::Uninit:
- break;
- case VarState::Dropped:
- // ERROR?
- break;
- case VarState::Moved:
- case VarState::MaybeMoved:
- // ERROR?
- break;
- case VarState::Init:
- // ERROR.
- break;
- }
- set_temp_state(e.idx, VarState::Init);
- ),
- (Return,
- // Don't drop.
- //m_return_valid = true;
- ),
- (Variable,
- switch( get_variable_state(e) )
- {
- case VarState::Uninit:
- case VarState::Moved:
- break;
- case VarState::Dropped:
- // TODO: Is this an error?
- break;
- case VarState::Init:
- // 1. Must be mut
- // 2. Drop
- push_stmt_drop( dst.clone() );
- break;
- case VarState::MaybeMoved:
- // TODO: Conditional drop
- break;
- }
- set_variable_state(e, VarState::Init);
- )
- )
- m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Assign({ mv$(dst), mv$(val) }) );
-}
-void MirBuilder::push_stmt_drop(::MIR::LValue val)
-{
- ASSERT_BUG(Span(), m_block_active, "Pushing statement with no active block");
- ASSERT_BUG(Span(), val.tag() != ::MIR::LValue::TAGDEAD, "");
- m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Drop({ ::MIR::eDropKind::DEEP, mv$(val) }) );
-}
-
-void MirBuilder::set_cur_block(unsigned int new_block)
-{
- if( m_block_active ) {
- BUG(Span(), "Updating block when previous is active");
- }
- DEBUG("BB" << new_block << " START");
- m_current_block = new_block;
- m_block_active = true;
-}
-void MirBuilder::end_block(::MIR::Terminator term)
-{
- if( !m_block_active ) {
- BUG(Span(), "Terminating block when none active");
- }
- DEBUG("BB" << m_current_block << " END -> " << term);
- m_output.blocks.at(m_current_block).terminator = mv$(term);
- m_block_active = false;
- m_current_block = 0;
-}
-void MirBuilder::pause_cur_block()
-{
- if( !m_block_active ) {
- BUG(Span(), "Pausing block when none active");
- }
- DEBUG("BB" << m_current_block << " PAUSE");
- m_block_active = false;
- m_current_block = 0;
-}
-::MIR::BasicBlockId MirBuilder::new_bb_linked()
-{
- auto rv = new_bb_unlinked();
- DEBUG("BB" << rv);
- end_block( ::MIR::Terminator::make_Goto(rv) );
- set_cur_block(rv);
- return rv;
-}
-::MIR::BasicBlockId MirBuilder::new_bb_unlinked()
-{
- auto rv = m_output.blocks.size();
- DEBUG("BB" << rv);
- m_output.blocks.push_back({});
- return rv;
-}
-
-ScopeHandle MirBuilder::new_scope_var(const Span& sp)
-{
- unsigned int idx = m_scopes.size();
- m_scopes.push_back( {false, ScopeType::make_Variables({})} );
- m_scope_stack.push_back( idx );
- DEBUG("START (var) scope " << idx);
- return ScopeHandle { *this, idx };
-}
-ScopeHandle MirBuilder::new_scope_temp(const Span& sp)
-{
- unsigned int idx = m_scopes.size();
- m_scopes.push_back( {false, ScopeType::make_Temporaries({})} );
- m_scope_stack.push_back( idx );
- DEBUG("START (temp) scope " << idx);
- return ScopeHandle { *this, idx };
-}
-ScopeHandle MirBuilder::new_scope_split(const Span& sp)
-{
- unsigned int idx = m_scopes.size();
- m_scopes.push_back( {false, ScopeType::make_Split({})} );
- m_scopes.back().data.as_Split().arms.push_back( {} );
- m_scope_stack.push_back( idx );
- DEBUG("START (split) scope " << idx);
- return ScopeHandle { *this, idx };
-}
-ScopeHandle MirBuilder::new_scope_loop(const Span& sp)
-{
- unsigned int idx = m_scopes.size();
- m_scopes.push_back( {false, ScopeType::make_Loop({})} );
- m_scope_stack.push_back( idx );
- DEBUG("START (loop) scope " << idx);
- return ScopeHandle { *this, idx };
-}
-void MirBuilder::terminate_scope(const Span& sp, ScopeHandle scope)
-{
- DEBUG("DONE scope " << scope.idx);
- // 1. Check that this is the current scope (at the top of the stack)
- if( m_scope_stack.back() != scope.idx )
- {
- DEBUG("m_scope_stack = " << m_scope_stack);
- auto it = ::std::find( m_scope_stack.begin(), m_scope_stack.end(), scope.idx );
- if( it == m_scope_stack.end() )
- BUG(sp, "Terminating scope not on the stack - scope " << scope.idx);
- BUG(sp, "Terminating scope " << scope.idx << " when not at top of stack, " << (m_scope_stack.end() - it - 1) << " scopes in the way");
- }
-
- auto& scope_def = m_scopes.at(scope.idx);
- ASSERT_BUG( sp, scope_def.complete == false, "Terminating scope which is already terminated" );
-
- complete_scope(scope_def);
-
- // 2. Emit drops for all non-moved variables (share with below)
- drop_scope_values(scope_def);
-
- // 3. Pop scope (last because `drop_scope_values` uses the stack)
- m_scope_stack.pop_back();
-}
-void MirBuilder::terminate_scope_early(const Span& sp, const ScopeHandle& scope)
-{
- DEBUG("EARLY scope " << scope.idx);
-
- // 1. Ensure that this block is in the stack
- auto it = ::std::find( m_scope_stack.begin(), m_scope_stack.end(), scope.idx );
- if( it == m_scope_stack.end() ) {
- BUG(sp, "Early-terminating scope not on the stack");
- }
- unsigned int slot = it - m_scope_stack.begin();
-
- bool is_conditional = false;
- for(unsigned int i = m_scope_stack.size(); i-- > slot; )
- {
- auto idx = m_scope_stack[i];
- auto& scope_def = m_scopes.at( idx );
-
- // If a conditional block is hit, prevent full termination of the rest
- if( scope_def.data.is_Split() )
- is_conditional = true;
-
- if( !is_conditional ) {
- DEBUG("Complete scope " << idx);
- complete_scope(scope_def);
- drop_scope_values(scope_def);
- m_scope_stack.pop_back();
- }
- else {
- // Mark patial within this scope?
- DEBUG("Drop part of scope " << idx);
-
- // Emit drops for dropped values within this scope
- drop_scope_values(scope_def);
- // Inform the scope that it's been early-exited
- TU_IFLET( ScopeType, scope_def.data, Split, e,
- e.arms.back().has_early_terminated = true;
- )
- }
- }
-}
-void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool reachable)
-{
- ASSERT_BUG(sp, handle.idx < m_scopes.size(), "");
- auto& sd = m_scopes.at( handle.idx );
- ASSERT_BUG(sp, sd.data.is_Split(), "");
- auto& sd_split = sd.data.as_Split();
- ASSERT_BUG(sp, !sd_split.arms.empty(), "");
-
- sd_split.arms.back().always_early_terminated = /*sd_split.arms.back().has_early_terminated &&*/ !reachable;
-
- // TODO: Undo moves from within the other scope
- sd_split.arms.push_back( {} );
-}
-void MirBuilder::complete_scope(ScopeDef& sd)
-{
- sd.complete = true;
- TU_MATCHA( (sd.data), (e),
- (Temporaries,
- DEBUG("Temporaries " << e.temporaries);
- ),
- (Variables,
- DEBUG("Variables " << e.vars);
- ),
- (Loop,
- DEBUG("Loop");
- ),
- (Split,
- assert( e.arms.size() > 1 );
- DEBUG("Split - " << (e.arms.size() - 1) << " arms");
- e.arms.pop_back();
-
- // Merge all arms and apply upwards
- size_t var_count = 0;
- for(const auto& arm : e.arms)
- {
- var_count = ::std::max(var_count, arm.var_states.size());
- }
- for(const auto& arm : e.arms)
- {
- DEBUG("><");
- assert( arm.changed_var_states.size() == arm.var_states.size() );
- for(unsigned int i = 0; i < arm.var_states.size(); i ++ )
- {
- if( arm.changed_var_states[i] )
- {
- auto new_state = arm.var_states[i];
- // - NOTE: This scope should be off the stack now, so this call will get the original state
- auto old_state = get_variable_state(i);
- DEBUG("old_state = " << old_state << ", new_state = " << new_state);
- // TODO: Need to build up a merged copy of the states? Or just apply upwards directly?
- }
- }
- }
- )
- )
-}
-
-
-VarState MirBuilder::get_variable_state(unsigned int idx) const
-{
- for( auto scope_idx : ::reverse(m_scope_stack) )
- {
- const auto& scope_def = m_scopes.at(scope_idx);
- TU_MATCH_DEF( ScopeType, (scope_def.data), (e),
- (
- ),
- (Variables,
- auto it = ::std::find(e.vars.begin(), e.vars.end(), idx);
- if( it != e.vars.end() ) {
- unsigned int sub_idx = it - e.vars.begin();
- assert(sub_idx < e.var_states.size());
- return e.var_states[sub_idx];
- }
- ),
- (Split,
- const auto& cur_arm = e.arms.back();
- if( idx < cur_arm.changed_var_states.size() && cur_arm.changed_var_states[idx] )
- {
- assert( idx < cur_arm.var_states.size() );
- return cur_arm.var_states[idx];
- }
- )
- )
- }
-
- BUG(Span(), "Variable " << idx << " not found in stack");
-}
-void MirBuilder::set_variable_state(unsigned int idx, VarState state)
-{
- for( auto scope_idx : ::reverse(m_scope_stack) )
- {
- auto& scope_def = m_scopes.at(scope_idx);
- TU_MATCH_DEF( ScopeType, (scope_def.data), (e),
- (
- ),
- (Variables,
- auto it = ::std::find(e.vars.begin(), e.vars.end(), idx);
- if( it != e.vars.end() ) {
- unsigned int sub_idx = it - e.vars.begin();
- assert(sub_idx < e.var_states.size());
- e.var_states[sub_idx] = state;
- return ;
- }
- ),
- (Split,
- auto& cur_arm = e.arms.back();
- if( idx < cur_arm.changed_var_states.size() )
- {
- assert( idx < cur_arm.var_states.size() );
- cur_arm.changed_var_states[idx] = true;
- cur_arm.var_states[idx] = state;
- return ;
- }
- )
- )
- }
-
- BUG(Span(), "Variable " << idx << " not found in stack");
-}
-VarState MirBuilder::get_temp_state(unsigned int idx) const
-{
- for( auto scope_idx : ::reverse(m_scope_stack) )
- {
- const auto& scope_def = m_scopes.at(scope_idx);
- TU_MATCH_DEF( ScopeType, (scope_def.data), (e),
- (
- ),
- (Temporaries,
- auto it = ::std::find(e.temporaries.begin(), e.temporaries.end(), idx);
- if( it != e.temporaries.end() ) {
- unsigned int sub_idx = it - e.temporaries.begin();
- ASSERT_BUG(Span(), sub_idx < e.states.size(), "Temporary list sizes invalid - " << sub_idx << " >= " << e.states.size());
- return e.states[sub_idx];
- }
- ),
- (Split,
- // TODO: Does split account for temps? It should.
- )
- )
- }
-
- BUG(Span(), "Temporary " << idx << " not found in stack");
-}
-void MirBuilder::set_temp_state(unsigned int idx, VarState state)
-{
- for( auto scope_idx : ::reverse(m_scope_stack) )
- {
- auto& scope_def = m_scopes.at(scope_idx);
- TU_MATCH_DEF( ScopeType, (scope_def.data), (e),
- (
- ),
- (Temporaries,
- auto it = ::std::find(e.temporaries.begin(), e.temporaries.end(), idx);
- if( it != e.temporaries.end() ) {
- unsigned int sub_idx = it - e.temporaries.begin();
- assert(sub_idx < e.states.size());
- e.states[sub_idx] = state;
- return ;
- }
- ),
- (Split,
- // TODO: Does split account for temps? It should.
- )
- )
- }
-}
-
-void MirBuilder::drop_scope_values(const ScopeDef& sd)
-{
- TU_MATCHA( (sd.data), (e),
- (Temporaries,
- for(auto tmp_idx : e.temporaries)
- {
- if( get_temp_state(tmp_idx) == VarState::Init ) {
- push_stmt_drop( ::MIR::LValue::make_Temporary({ tmp_idx }) );
- set_temp_state(tmp_idx, VarState::Dropped);
- }
- }
- ),
- (Variables,
- for(auto var_idx : e.vars)
- {
- switch( get_variable_state(var_idx) )
- {
- case VarState::Uninit:
- case VarState::Dropped:
- case VarState::Moved:
- break;
- case VarState::Init:
- push_stmt_drop( ::MIR::LValue::make_Variable(var_idx) );
- break;
- case VarState::MaybeMoved:
- // TODO: Drop flags
- break;
- }
- }
- ),
- (Split,
- // No values, controls parent
- ),
- (Loop,
- // No values
- )
- )
-}
-
-void MirBuilder::moved_lvalue(const ::MIR::LValue& lv)
-{
- TU_MATCHA( (lv), (e),
- (Variable,
- set_variable_state(e, VarState::Moved);
- ),
- (Temporary,
- //TODO(Span(), "Mark temp as moved");
- ),
- (Argument,
- //TODO(Span(), "Mark argument as moved");
- ),
- (Static,
- //TODO(Span(), "Static - Assert that type is Copy");
- ),
- (Return,
- BUG(Span(), "Read of return value");
- ),
- (Field,
- //TODO(Span(), "Mark field of an lvalue as moved (if not Copy) - Partially moved structs?");
- //moved_lvalue(*e.val);
- ),
- (Deref,
- //TODO(Span(), "Mark deref of an lvalue as moved (if not Copy) - Req. DerefMove/&move otherwise");
- //moved_lvalue(*e.val);
- ),
- (Index,
- //TODO(Span(), "Mark index of an lvalue as moved (if not Copy) - Req. IndexGet/IndexMove");
- //moved_lvalue(*e.val);
-
- moved_lvalue(*e.idx);
- ),
- (Downcast,
- // TODO: What if the inner is Copy? What if the inner is a hidden pointer?
- moved_lvalue(*e.val);
- )
- )
-}
-
-ScopeHandle::~ScopeHandle()
-{
- if( idx != ~0u )
- {
- ASSERT_BUG(Span(), m_builder.m_scopes.size() > idx, "Scope invalid");
- ASSERT_BUG(Span(), m_builder.m_scopes.at(idx).complete, "Scope " << idx << " not completed");
- }
-}
-
-::std::ostream& operator<<(::std::ostream& os, VarState x)
-{
- switch(x)
- {
- #define _(V) case VarState::V: os << #V; break;
- _(Uninit)
- _(Init)
- _(MaybeMoved)
- _(Moved)
- _(Dropped)
- #undef _
- }
- return os;
-}
-
-// --------------------------------------------------------------------
void HIR_GenerateMIR(::HIR::Crate& crate)
{
diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp
new file mode 100644
index 00000000..f3b9c263
--- /dev/null
+++ b/src/mir/mir_builder.cpp
@@ -0,0 +1,663 @@
+/*
+ * MRustC - Rust Compiler
+ * - By John Hodge (Mutabah/thePowersGang)
+ *
+ * mir/mir_builder.cpp
+ * - MIR Building Helper
+ */
+#include <algorithm>
+#include "from_hir.hpp"
+
+// --------------------------------------------------------------------
+// MirBuilder
+// --------------------------------------------------------------------
+MirBuilder::MirBuilder(::MIR::Function& output):
+ m_output(output),
+ m_block_active(false),
+ m_result_valid(false),
+ m_fcn_scope(*this, 0)
+{
+ set_cur_block( new_bb_unlinked() );
+ m_scopes.push_back( ScopeDef {} );
+ m_scope_stack.push_back( 0 );
+
+ m_scopes.push_back( ScopeDef { false, ScopeType::make_Temporaries({}) } );
+ m_scope_stack.push_back( 1 );
+}
+MirBuilder::~MirBuilder()
+{
+ if( has_result() )
+ {
+ push_stmt_assign( ::MIR::LValue::make_Return({}), get_result(Span()) );
+ }
+ if( block_active() )
+ {
+ terminate_scope( Span(), ScopeHandle { *this, 1 } );
+ terminate_scope( Span(), mv$(m_fcn_scope) );
+ end_block( ::MIR::Terminator::make_Return({}) );
+ }
+}
+
+void MirBuilder::define_variable(unsigned int idx)
+{
+ DEBUG("DEFINE var" << idx /* << ": " << m_output.named_variables.at(idx)*/);
+ for( auto scope_idx : ::reverse(m_scope_stack) )
+ {
+ auto& scope_def = m_scopes.at(scope_idx);
+ TU_MATCH_DEF( ScopeType, (scope_def.data), (e),
+ (
+ ),
+ (Variables,
+ auto it = ::std::find(e.vars.begin(), e.vars.end(), idx);
+ assert(it == e.vars.end());
+ e.vars.push_back( idx );
+ e.var_states.push_back( VarState::Uninit );
+ return ;
+ ),
+ (Split,
+ BUG(Span(), "Variable " << idx << " introduced within a Split");
+ )
+ )
+ }
+ BUG(Span(), "Variable " << idx << " introduced with no Variable scope");
+}
+::MIR::LValue MirBuilder::new_temporary(const ::HIR::TypeRef& ty)
+{
+ unsigned int rv = m_output.temporaries.size();
+ DEBUG("DEFINE tmp" << rv << ": " << ty);
+
+ m_output.temporaries.push_back( ty.clone() );
+ temporaries_valid.push_back(false);
+
+ ScopeDef* top_scope = nullptr;
+ for(unsigned int i = m_scope_stack.size(); i --; )
+ {
+ auto idx = m_scope_stack[i];
+ if( m_scopes.at( idx ).data.is_Temporaries() ) {
+ top_scope = &m_scopes.at(idx);
+ break ;
+ }
+ }
+ assert( top_scope );
+ auto& tmp_scope = top_scope->data.as_Temporaries();
+ tmp_scope.temporaries.push_back( rv );
+ tmp_scope.states.push_back( VarState::Uninit );
+ return ::MIR::LValue::make_Temporary({rv});
+}
+::MIR::LValue MirBuilder::lvalue_or_temp(const ::HIR::TypeRef& ty, ::MIR::RValue val)
+{
+ TU_IFLET(::MIR::RValue, val, Use, e,
+ return mv$(e);
+ )
+ else {
+ auto temp = new_temporary(ty);
+ push_stmt_assign( ::MIR::LValue(temp.as_Temporary()), mv$(val) );
+ return temp;
+ }
+}
+
+::MIR::RValue MirBuilder::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 MirBuilder::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 MirBuilder::set_result(const Span& sp, ::MIR::RValue val)
+{
+ if(m_result_valid) {
+ BUG(sp, "Pushing a result over an existing result");
+ }
+ m_result = mv$(val);
+ m_result_valid = true;
+}
+
+void MirBuilder::push_stmt_assign(::MIR::LValue dst, ::MIR::RValue val)
+{
+ ASSERT_BUG(Span(), m_block_active, "Pushing statement with no active block");
+ ASSERT_BUG(Span(), dst.tag() != ::MIR::LValue::TAGDEAD, "");
+ ASSERT_BUG(Span(), val.tag() != ::MIR::RValue::TAGDEAD, "");
+
+ TU_MATCHA( (val), (e),
+ (Use,
+ this->moved_lvalue(e);
+ ),
+ (Constant,
+ ),
+ (SizedArray,
+ this->moved_lvalue(e.val);
+ ),
+ (Borrow,
+ if( e.type == ::HIR::BorrowType::Owned ) {
+ TODO(Span(), "Move using &move");
+ // Likely would require a marker that ensures that the memory isn't reused.
+ this->moved_lvalue(e.val);
+ }
+ else {
+ // Doesn't move
+ }
+ ),
+ (Cast,
+ this->moved_lvalue(e.val);
+ ),
+ (BinOp,
+ switch(e.op)
+ {
+ case ::MIR::eBinOp::EQ:
+ case ::MIR::eBinOp::NE:
+ case ::MIR::eBinOp::GT:
+ case ::MIR::eBinOp::GE:
+ case ::MIR::eBinOp::LT:
+ case ::MIR::eBinOp::LE:
+ // Takes an implicit borrow... and only works on copy, so why is this block here?
+ break;
+ default:
+ this->moved_lvalue(e.val_l);
+ this->moved_lvalue(e.val_r);
+ break;
+ }
+ ),
+ (UniOp,
+ this->moved_lvalue(e.val);
+ ),
+ (DstMeta,
+ // Doesn't move
+ ),
+ (MakeDst,
+ // Doesn't move ptr_val
+ this->moved_lvalue(e.meta_val);
+ ),
+ (Tuple,
+ for(const auto& val : e.vals)
+ this->moved_lvalue(val);
+ ),
+ (Array,
+ for(const auto& val : e.vals)
+ this->moved_lvalue(val);
+ ),
+ (Struct,
+ for(const auto& val : e.vals)
+ this->moved_lvalue(val);
+ )
+ )
+
+ // Drop target if populated
+ TU_MATCH_DEF(::MIR::LValue, (dst), (e),
+ (
+ ),
+ (Temporary,
+ switch(get_temp_state(e.idx))
+ {
+ case VarState::Uninit:
+ break;
+ case VarState::Dropped:
+ // ERROR?
+ break;
+ case VarState::Moved:
+ case VarState::MaybeMoved:
+ // ERROR?
+ break;
+ case VarState::Init:
+ // ERROR.
+ break;
+ }
+ set_temp_state(e.idx, VarState::Init);
+ ),
+ (Return,
+ // Don't drop.
+ //m_return_valid = true;
+ ),
+ (Variable,
+ switch( get_variable_state(e) )
+ {
+ case VarState::Uninit:
+ case VarState::Moved:
+ break;
+ case VarState::Dropped:
+ // TODO: Is this an error?
+ break;
+ case VarState::Init:
+ // 1. Must be mut
+ // 2. Drop
+ push_stmt_drop( dst.clone() );
+ break;
+ case VarState::MaybeMoved:
+ // TODO: Conditional drop
+ break;
+ }
+ set_variable_state(e, VarState::Init);
+ )
+ )
+ m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Assign({ mv$(dst), mv$(val) }) );
+}
+void MirBuilder::push_stmt_drop(::MIR::LValue val)
+{
+ ASSERT_BUG(Span(), m_block_active, "Pushing statement with no active block");
+ ASSERT_BUG(Span(), val.tag() != ::MIR::LValue::TAGDEAD, "");
+ m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Drop({ ::MIR::eDropKind::DEEP, mv$(val) }) );
+}
+
+void MirBuilder::set_cur_block(unsigned int new_block)
+{
+ if( m_block_active ) {
+ BUG(Span(), "Updating block when previous is active");
+ }
+ DEBUG("BB" << new_block << " START");
+ m_current_block = new_block;
+ m_block_active = true;
+}
+void MirBuilder::end_block(::MIR::Terminator term)
+{
+ if( !m_block_active ) {
+ BUG(Span(), "Terminating block when none active");
+ }
+ DEBUG("BB" << m_current_block << " END -> " << term);
+ m_output.blocks.at(m_current_block).terminator = mv$(term);
+ m_block_active = false;
+ m_current_block = 0;
+}
+void MirBuilder::pause_cur_block()
+{
+ if( !m_block_active ) {
+ BUG(Span(), "Pausing block when none active");
+ }
+ DEBUG("BB" << m_current_block << " PAUSE");
+ m_block_active = false;
+ m_current_block = 0;
+}
+::MIR::BasicBlockId MirBuilder::new_bb_linked()
+{
+ auto rv = new_bb_unlinked();
+ DEBUG("BB" << rv);
+ end_block( ::MIR::Terminator::make_Goto(rv) );
+ set_cur_block(rv);
+ return rv;
+}
+::MIR::BasicBlockId MirBuilder::new_bb_unlinked()
+{
+ auto rv = m_output.blocks.size();
+ DEBUG("BB" << rv);
+ m_output.blocks.push_back({});
+ return rv;
+}
+
+ScopeHandle MirBuilder::new_scope_var(const Span& sp)
+{
+ unsigned int idx = m_scopes.size();
+ m_scopes.push_back( {false, ScopeType::make_Variables({})} );
+ m_scope_stack.push_back( idx );
+ DEBUG("START (var) scope " << idx);
+ return ScopeHandle { *this, idx };
+}
+ScopeHandle MirBuilder::new_scope_temp(const Span& sp)
+{
+ unsigned int idx = m_scopes.size();
+ m_scopes.push_back( {false, ScopeType::make_Temporaries({})} );
+ m_scope_stack.push_back( idx );
+ DEBUG("START (temp) scope " << idx);
+ return ScopeHandle { *this, idx };
+}
+ScopeHandle MirBuilder::new_scope_split(const Span& sp)
+{
+ unsigned int idx = m_scopes.size();
+ m_scopes.push_back( {false, ScopeType::make_Split({})} );
+ m_scopes.back().data.as_Split().arms.push_back( {} );
+ m_scope_stack.push_back( idx );
+ DEBUG("START (split) scope " << idx);
+ return ScopeHandle { *this, idx };
+}
+ScopeHandle MirBuilder::new_scope_loop(const Span& sp)
+{
+ unsigned int idx = m_scopes.size();
+ m_scopes.push_back( {false, ScopeType::make_Loop({})} );
+ m_scope_stack.push_back( idx );
+ DEBUG("START (loop) scope " << idx);
+ return ScopeHandle { *this, idx };
+}
+void MirBuilder::terminate_scope(const Span& sp, ScopeHandle scope)
+{
+ DEBUG("DONE scope " << scope.idx);
+ // 1. Check that this is the current scope (at the top of the stack)
+ if( m_scope_stack.back() != scope.idx )
+ {
+ DEBUG("m_scope_stack = " << m_scope_stack);
+ auto it = ::std::find( m_scope_stack.begin(), m_scope_stack.end(), scope.idx );
+ if( it == m_scope_stack.end() )
+ BUG(sp, "Terminating scope not on the stack - scope " << scope.idx);
+ BUG(sp, "Terminating scope " << scope.idx << " when not at top of stack, " << (m_scope_stack.end() - it - 1) << " scopes in the way");
+ }
+
+ auto& scope_def = m_scopes.at(scope.idx);
+ ASSERT_BUG( sp, scope_def.complete == false, "Terminating scope which is already terminated" );
+
+ complete_scope(scope_def);
+
+ // 2. Emit drops for all non-moved variables (share with below)
+ drop_scope_values(scope_def);
+
+ // 3. Pop scope (last because `drop_scope_values` uses the stack)
+ m_scope_stack.pop_back();
+}
+void MirBuilder::terminate_scope_early(const Span& sp, const ScopeHandle& scope)
+{
+ DEBUG("EARLY scope " << scope.idx);
+
+ // 1. Ensure that this block is in the stack
+ auto it = ::std::find( m_scope_stack.begin(), m_scope_stack.end(), scope.idx );
+ if( it == m_scope_stack.end() ) {
+ BUG(sp, "Early-terminating scope not on the stack");
+ }
+ unsigned int slot = it - m_scope_stack.begin();
+
+ bool is_conditional = false;
+ for(unsigned int i = m_scope_stack.size(); i-- > slot; )
+ {
+ auto idx = m_scope_stack[i];
+ auto& scope_def = m_scopes.at( idx );
+
+ // If a conditional block is hit, prevent full termination of the rest
+ if( scope_def.data.is_Split() )
+ is_conditional = true;
+
+ if( !is_conditional ) {
+ DEBUG("Complete scope " << idx);
+ complete_scope(scope_def);
+ drop_scope_values(scope_def);
+ m_scope_stack.pop_back();
+ }
+ else {
+ // Mark patial within this scope?
+ DEBUG("Drop part of scope " << idx);
+
+ // Emit drops for dropped values within this scope
+ drop_scope_values(scope_def);
+ // Inform the scope that it's been early-exited
+ TU_IFLET( ScopeType, scope_def.data, Split, e,
+ e.arms.back().has_early_terminated = true;
+ )
+ }
+ }
+}
+void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool reachable)
+{
+ ASSERT_BUG(sp, handle.idx < m_scopes.size(), "");
+ auto& sd = m_scopes.at( handle.idx );
+ ASSERT_BUG(sp, sd.data.is_Split(), "");
+ auto& sd_split = sd.data.as_Split();
+ ASSERT_BUG(sp, !sd_split.arms.empty(), "");
+
+ sd_split.arms.back().always_early_terminated = /*sd_split.arms.back().has_early_terminated &&*/ !reachable;
+
+ // TODO: Undo moves from within the other scope
+ sd_split.arms.push_back( {} );
+}
+void MirBuilder::complete_scope(ScopeDef& sd)
+{
+ sd.complete = true;
+ TU_MATCHA( (sd.data), (e),
+ (Temporaries,
+ DEBUG("Temporaries " << e.temporaries);
+ ),
+ (Variables,
+ DEBUG("Variables " << e.vars);
+ ),
+ (Loop,
+ DEBUG("Loop");
+ ),
+ (Split,
+ assert( e.arms.size() > 1 );
+ DEBUG("Split - " << (e.arms.size() - 1) << " arms");
+ e.arms.pop_back();
+
+ // Merge all arms and apply upwards
+ size_t var_count = 0;
+ for(const auto& arm : e.arms)
+ {
+ var_count = ::std::max(var_count, arm.var_states.size());
+ }
+ for(const auto& arm : e.arms)
+ {
+ DEBUG("><");
+ assert( arm.changed_var_states.size() == arm.var_states.size() );
+ for(unsigned int i = 0; i < arm.var_states.size(); i ++ )
+ {
+ if( arm.changed_var_states[i] )
+ {
+ auto new_state = arm.var_states[i];
+ // - NOTE: This scope should be off the stack now, so this call will get the original state
+ auto old_state = get_variable_state(i);
+ DEBUG("old_state = " << old_state << ", new_state = " << new_state);
+ // TODO: Need to build up a merged copy of the states? Or just apply upwards directly?
+ }
+ }
+ }
+ )
+ )
+}
+
+
+VarState MirBuilder::get_variable_state(unsigned int idx) const
+{
+ for( auto scope_idx : ::reverse(m_scope_stack) )
+ {
+ const auto& scope_def = m_scopes.at(scope_idx);
+ TU_MATCH_DEF( ScopeType, (scope_def.data), (e),
+ (
+ ),
+ (Variables,
+ auto it = ::std::find(e.vars.begin(), e.vars.end(), idx);
+ if( it != e.vars.end() ) {
+ unsigned int sub_idx = it - e.vars.begin();
+ assert(sub_idx < e.var_states.size());
+ return e.var_states[sub_idx];
+ }
+ ),
+ (Split,
+ const auto& cur_arm = e.arms.back();
+ if( idx < cur_arm.changed_var_states.size() && cur_arm.changed_var_states[idx] )
+ {
+ assert( idx < cur_arm.var_states.size() );
+ return cur_arm.var_states[idx];
+ }
+ )
+ )
+ }
+
+ BUG(Span(), "Variable " << idx << " not found in stack");
+}
+void MirBuilder::set_variable_state(unsigned int idx, VarState state)
+{
+ for( auto scope_idx : ::reverse(m_scope_stack) )
+ {
+ auto& scope_def = m_scopes.at(scope_idx);
+ TU_MATCH_DEF( ScopeType, (scope_def.data), (e),
+ (
+ ),
+ (Variables,
+ auto it = ::std::find(e.vars.begin(), e.vars.end(), idx);
+ if( it != e.vars.end() ) {
+ unsigned int sub_idx = it - e.vars.begin();
+ assert(sub_idx < e.var_states.size());
+ e.var_states[sub_idx] = state;
+ return ;
+ }
+ ),
+ (Split,
+ auto& cur_arm = e.arms.back();
+ if( idx < cur_arm.changed_var_states.size() )
+ {
+ assert( idx < cur_arm.var_states.size() );
+ cur_arm.changed_var_states[idx] = true;
+ cur_arm.var_states[idx] = state;
+ return ;
+ }
+ )
+ )
+ }
+
+ BUG(Span(), "Variable " << idx << " not found in stack");
+}
+VarState MirBuilder::get_temp_state(unsigned int idx) const
+{
+ for( auto scope_idx : ::reverse(m_scope_stack) )
+ {
+ const auto& scope_def = m_scopes.at(scope_idx);
+ TU_MATCH_DEF( ScopeType, (scope_def.data), (e),
+ (
+ ),
+ (Temporaries,
+ auto it = ::std::find(e.temporaries.begin(), e.temporaries.end(), idx);
+ if( it != e.temporaries.end() ) {
+ unsigned int sub_idx = it - e.temporaries.begin();
+ ASSERT_BUG(Span(), sub_idx < e.states.size(), "Temporary list sizes invalid - " << sub_idx << " >= " << e.states.size());
+ return e.states[sub_idx];
+ }
+ ),
+ (Split,
+ // TODO: Does split account for temps? It should.
+ )
+ )
+ }
+
+ BUG(Span(), "Temporary " << idx << " not found in stack");
+}
+void MirBuilder::set_temp_state(unsigned int idx, VarState state)
+{
+ for( auto scope_idx : ::reverse(m_scope_stack) )
+ {
+ auto& scope_def = m_scopes.at(scope_idx);
+ TU_MATCH_DEF( ScopeType, (scope_def.data), (e),
+ (
+ ),
+ (Temporaries,
+ auto it = ::std::find(e.temporaries.begin(), e.temporaries.end(), idx);
+ if( it != e.temporaries.end() ) {
+ unsigned int sub_idx = it - e.temporaries.begin();
+ assert(sub_idx < e.states.size());
+ e.states[sub_idx] = state;
+ return ;
+ }
+ ),
+ (Split,
+ // TODO: Does split account for temps? It should.
+ )
+ )
+ }
+}
+
+void MirBuilder::drop_scope_values(const ScopeDef& sd)
+{
+ TU_MATCHA( (sd.data), (e),
+ (Temporaries,
+ for(auto tmp_idx : e.temporaries)
+ {
+ if( get_temp_state(tmp_idx) == VarState::Init ) {
+ push_stmt_drop( ::MIR::LValue::make_Temporary({ tmp_idx }) );
+ set_temp_state(tmp_idx, VarState::Dropped);
+ }
+ }
+ ),
+ (Variables,
+ for(auto var_idx : e.vars)
+ {
+ switch( get_variable_state(var_idx) )
+ {
+ case VarState::Uninit:
+ case VarState::Dropped:
+ case VarState::Moved:
+ break;
+ case VarState::Init:
+ push_stmt_drop( ::MIR::LValue::make_Variable(var_idx) );
+ break;
+ case VarState::MaybeMoved:
+ // TODO: Drop flags
+ break;
+ }
+ }
+ ),
+ (Split,
+ // No values, controls parent
+ ),
+ (Loop,
+ // No values
+ )
+ )
+}
+
+void MirBuilder::moved_lvalue(const ::MIR::LValue& lv)
+{
+ TU_MATCHA( (lv), (e),
+ (Variable,
+ set_variable_state(e, VarState::Moved);
+ ),
+ (Temporary,
+ set_temp_state(e.idx, VarState::Moved);
+ ),
+ (Argument,
+ //TODO(Span(), "Mark argument as moved");
+ ),
+ (Static,
+ //TODO(Span(), "Static - Assert that type is Copy");
+ ),
+ (Return,
+ BUG(Span(), "Read of return value");
+ ),
+ (Field,
+ //TODO(Span(), "Mark field of an lvalue as moved (if not Copy) - Partially moved structs?");
+ //moved_lvalue(*e.val);
+ ),
+ (Deref,
+ //TODO(Span(), "Mark deref of an lvalue as moved (if not Copy) - Req. DerefMove/&move otherwise");
+ //moved_lvalue(*e.val);
+ ),
+ (Index,
+ //TODO(Span(), "Mark index of an lvalue as moved (if not Copy) - Req. IndexGet/IndexMove");
+ //moved_lvalue(*e.val);
+
+ moved_lvalue(*e.idx);
+ ),
+ (Downcast,
+ // TODO: What if the inner is Copy? What if the inner is a hidden pointer?
+ moved_lvalue(*e.val);
+ )
+ )
+}
+
+// --------------------------------------------------------------------
+
+ScopeHandle::~ScopeHandle()
+{
+ if( idx != ~0u )
+ {
+ ASSERT_BUG(Span(), m_builder.m_scopes.size() > idx, "Scope invalid");
+ ASSERT_BUG(Span(), m_builder.m_scopes.at(idx).complete, "Scope " << idx << " not completed");
+ }
+}
+
+::std::ostream& operator<<(::std::ostream& os, VarState x)
+{
+ switch(x)
+ {
+ #define _(V) case VarState::V: os << #V; break;
+ _(Uninit)
+ _(Init)
+ _(MaybeMoved)
+ _(Moved)
+ _(Dropped)
+ #undef _
+ }
+ return os;
+}