summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Hodge <tpg@mutabah.net>2016-08-21 15:55:36 +0800
committerJohn Hodge <tpg@mutabah.net>2016-08-21 15:55:36 +0800
commit8ae6dada7551e74c064ee8d18bcfd900fcd3aaf3 (patch)
treee98f23eb9ee6aa18b4892f88e795c7d4d64ae3b2
parent025ebf7a067d27c07071e46998ac556ea225948c (diff)
downloadmrust-8ae6dada7551e74c064ee8d18bcfd900fcd3aaf3.tar.gz
MIR Gen - Rework to have proper drop scopes (INCOMPLETE)
-rw-r--r--src/mir/from_hir.cpp571
-rw-r--r--src/mir/from_hir.hpp69
-rw-r--r--src/mir/from_hir_match.cpp80
3 files changed, 587 insertions, 133 deletions
diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp
index f48c5ba3..57e25b9c 100644
--- a/src/mir/from_hir.cpp
+++ b/src/mir/from_hir.cpp
@@ -35,10 +35,6 @@ namespace {
unsigned int next;
};
::std::vector<LoopDesc> m_loop_stack;
- struct BlockDesc {
- ::std::vector<unsigned int> bindings;
- };
- ::std::vector<BlockDesc> m_block_stack;
public:
ExprVisitor_Conv(::MIR::Function& output, const ::std::vector< ::HIR::TypeRef>& var_types):
@@ -52,6 +48,75 @@ namespace {
destructure_from_ex(sp, pat, mv$(lval), (allow_refutable ? 1 : 0));
}
+ // Brings variables defined in `pat` into scope
+ void define_vars_from(const Span& sp, const ::HIR::Pattern& pat) override
+ {
+ if( pat.m_binding.is_valid() ) {
+ m_builder.define_variable( pat.m_binding.m_slot );
+ }
+
+ TU_MATCHA( (pat.m_data), (e),
+ (Any,
+ ),
+ (Box,
+ TODO(sp, "Destructure using " << pat);
+ ),
+ (Ref,
+ define_vars_from(sp, *e.sub);
+ ),
+ (Tuple,
+ for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
+ {
+ define_vars_from(sp, e.sub_patterns[i]);
+ }
+ ),
+ (SplitTuple,
+ BUG(sp, "Tuple .. should be eliminated");
+ ),
+ (StructValue,
+ // Nothing.
+ ),
+ (StructTuple,
+ for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
+ {
+ define_vars_from(sp, e.sub_patterns[i]);
+ }
+ ),
+ (Struct,
+ for(const auto& fld_pat : e.sub_patterns)
+ {
+ define_vars_from(sp, fld_pat.second);
+ }
+ ),
+ // Refutable
+ (Value,
+ ),
+ (Range,
+ ),
+ (EnumValue,
+ ),
+
+ (EnumTuple,
+ for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
+ {
+ define_vars_from(sp, e.sub_patterns[i]);
+ }
+ ),
+ (EnumStruct,
+ for(const auto& fld_pat : e.sub_patterns)
+ {
+ define_vars_from(sp, fld_pat.second);
+ }
+ ),
+ (Slice,
+ TODO(sp, "Destructure using " << pat);
+ ),
+ (SplitSlice,
+ TODO(sp, "Destructure using " << pat);
+ )
+ )
+ }
+
void destructure_from_ex(const Span& sp, const ::HIR::Pattern& pat, ::MIR::LValue lval, int allow_refutable=0) // 1 : yes, 2 : disallow binding
{
if( allow_refutable != 3 && pat.m_binding.is_valid() ) {
@@ -158,16 +223,17 @@ namespace {
// NOTE: This doesn't create a BB, as BBs are not needed for scoping
if( node.m_nodes.size() > 0 )
{
- auto scope = m_builder.new_scope( node.span() );
+ bool res_valid;
+ auto res = m_builder.new_temporary( node.m_res_type );
+
+ auto scope = m_builder.new_scope_var(node.span());
- m_block_stack.push_back( {} );
for(unsigned int i = 0; i < node.m_nodes.size()-1; i ++)
{
auto& subnode = node.m_nodes[i];
const Span& sp = subnode->span();
- // - Covers just temporaries? (what about variables defined?)
- auto stmt_scope = m_builder.new_scope(sp);
+ auto stmt_scope = m_builder.new_scope_temp(sp);
this->visit_node_ptr(subnode);
if( m_builder.has_result() ) {
m_builder.get_result(sp);
@@ -182,10 +248,22 @@ namespace {
}
// - For the last node, don't bother with a statement scope
- this->visit_node_ptr(node.m_nodes.back());
-
- auto bd = mv$( m_block_stack.back() );
- m_block_stack.pop_back();
+ {
+ auto& subnode = node.m_nodes.back();
+ const Span& sp = subnode->span();
+
+ auto stmt_scope = m_builder.new_scope_temp(sp);
+ this->visit_node_ptr(subnode);
+ if( m_builder.has_result() || m_builder.block_active() ) {
+ m_builder.push_stmt_assign( res.clone(), m_builder.get_result(sp) );
+ m_builder.terminate_scope(sp, mv$(stmt_scope));
+ res_valid = true;
+ }
+ else {
+ auto _ = mv$(stmt_scope);
+ res_valid = false;
+ }
+ }
// Drop all bindings introduced during this block.
if( m_builder.block_active() ) {
@@ -195,7 +273,10 @@ namespace {
auto _ = mv$(scope);
}
- // Result maintained from last node
+ // Result from last node (if it didn't diverge)
+ if( res_valid ) {
+ m_builder.set_result( node.span(), mv$(res) );
+ }
}
else
{
@@ -215,6 +296,7 @@ namespace {
void visit(::HIR::ExprNode_Let& node) override
{
TRACE_FUNCTION_F("_Let");
+ this->define_vars_from(node.span(), node.m_pattern);
if( node.m_value )
{
this->visit_node_ptr(node.m_value);
@@ -226,7 +308,7 @@ namespace {
void visit(::HIR::ExprNode_Loop& node) override
{
TRACE_FUNCTION_F("_Loop");
- auto loop_body_scope = m_builder.new_scope(node.span(), true);
+ auto loop_body_scope = m_builder.new_scope_loop(node.span());
auto loop_block = m_builder.new_bb_linked();
auto loop_next = m_builder.new_bb_unlinked();
@@ -240,13 +322,16 @@ namespace {
assert( m_builder.block_active() );
}
// Terminate block with a jump back to the start
+ // - Also inserts the jump if this didn't uncondtionally diverge
if( m_builder.block_active() )
{
// TODO: Insert drop of all scopes within the current scope
m_builder.terminate_scope( node.span(), mv$(loop_scope) );
m_builder.end_block( ::MIR::Terminator::make_Goto(loop_block) );
+
+ // -
+ m_builder.set_cur_block(loop_next);
}
- m_builder.set_cur_block(loop_next);
}
void visit(::HIR::ExprNode_LoopControl& node) override
{
@@ -282,13 +367,20 @@ namespace {
if( node.m_arms.size() == 0 ) {
// Nothing
- TODO(node.span(), "Handle zero-arm match");
+ TODO(node.span(), "Handle zero-arm match - can only match over ! or similar");
}
else if( node.m_arms.size() == 1 && node.m_arms[0].m_patterns.size() == 1 && ! node.m_arms[0].m_cond ) {
// - Shortcut: Single-arm match
- // TODO: Drop scope
+ auto scope = m_builder.new_scope_var( node.span() );
+ this->define_vars_from(node.span(), node.m_arms[0].m_patterns[0]);
this->destructure_from(node.span(), node.m_arms[0].m_patterns[0], mv$(match_val));
this->visit_node_ptr(node.m_arms[0].m_code);
+ if( m_builder.block_active() ) {
+ m_builder.terminate_scope( node.span(), mv$(scope) );
+ }
+ else {
+ auto _ = mv$(scope);
+ }
}
else {
MIR_LowerHIR_Match(m_builder, *this, node, mv$(match_val));
@@ -309,35 +401,36 @@ namespace {
auto result_val = m_builder.new_temporary(node.m_res_type);
- // TODO: Scope handle cases where one arm moves a value but the other doesn't
+ // Scope handles cases where one arm moves a value but the other doesn't
+ auto scope = m_builder.new_scope_split( node.m_true->span() );
+ // 'true' branch
{
m_builder.set_cur_block(true_branch);
- auto branch_handle = m_builder.new_scope( node.m_true->span(), true );
this->visit_node_ptr(node.m_true);
- if( m_builder.block_active() ) {
+ if( m_builder.block_active() || m_builder.has_result() ) {
m_builder.push_stmt_assign( result_val.clone(), m_builder.get_result(node.m_true->span()) );
- m_builder.terminate_scope(node.m_true->span(), mv$(branch_handle));
m_builder.end_block( ::MIR::Terminator::make_Goto(next_block) );
+ m_builder.end_split_arm(node.span(), scope, true);
}
else {
- auto _ = mv$(branch_handle);
+ m_builder.end_split_arm(node.span(), scope, false);
}
}
+ // 'false' branch
m_builder.set_cur_block(false_branch);
if( node.m_false )
{
- auto branch_scope = m_builder.new_scope( node.m_true->span(), true );
this->visit_node_ptr(node.m_false);
if( m_builder.block_active() )
{
m_builder.push_stmt_assign( result_val.clone(), m_builder.get_result(node.m_false->span()) );
- m_builder.terminate_scope(node.m_true->span(), mv$(branch_scope));
m_builder.end_block( ::MIR::Terminator::make_Goto(next_block) );
+ m_builder.end_split_arm(node.span(), scope, true);
}
else {
- auto _ = mv$(branch_scope);
+ m_builder.end_split_arm(node.span(), scope, false);
}
}
else
@@ -345,8 +438,10 @@ namespace {
// Assign `()` to the result
m_builder.push_stmt_assign( result_val.clone(), ::MIR::RValue::make_Tuple({}) );
m_builder.end_block( ::MIR::Terminator::make_Goto(next_block) );
+ m_builder.end_split_arm(node.span(), scope, true);
}
m_builder.set_cur_block(next_block);
+ m_builder.terminate_scope( node.span(), mv$(scope) );
m_builder.set_result( node.span(), mv$(result_val) );
}
@@ -1123,10 +1218,7 @@ namespace {
values[i] = ::MIR::LValue::make_Field({ box$( base_val.clone() ), i });
}
else {
- // Drop unused part of the base
- if( node.m_base_value) {
- m_builder.push_stmt_drop( ::MIR::LValue::make_Field({ box$( base_val.clone() ), i }) );
- }
+ // Partial move support will handle dropping the rest?
}
}
@@ -1216,6 +1308,9 @@ namespace {
TRACE_FUNCTION;
::MIR::Function fcn;
+ fcn.named_variables.reserve(ptr.m_bindings.size());
+ for(const auto& t : ptr.m_bindings)
+ fcn.named_variables.push_back( t.clone() );
// TODO: Construct builder here and lend to ExprVisitor_Conv
{
@@ -1225,6 +1320,7 @@ namespace {
unsigned int i = 0;
for( const auto& arg : args )
{
+ ev.define_vars_from(ptr->span(), arg.first);
ev.destructure_from(ptr->span(), arg.first, ::MIR::LValue::make_Argument({i}));
}
@@ -1310,6 +1406,19 @@ 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() )
@@ -1318,17 +1427,56 @@ MirBuilder::~MirBuilder()
}
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);
- m_scopes.at( m_scope_stack.back() ).temporaries.push_back( rv );
+
+ 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)
@@ -1388,6 +1536,14 @@ void MirBuilder::push_stmt_assign(::MIR::LValue dst, ::MIR::RValue val)
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);
@@ -1401,6 +1557,7 @@ void MirBuilder::push_stmt_assign(::MIR::LValue dst, ::MIR::RValue val)
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);
@@ -1432,29 +1589,53 @@ void MirBuilder::push_stmt_assign(::MIR::LValue dst, ::MIR::RValue val)
)
)
- // Drop target if not a temporary
- TU_IFLET( ::MIR::LValue, dst, Temporary, e,
- if( temporaries_valid.size() <= e.idx ) {
- temporaries_valid.resize(e.idx + 1);
- }
- //assert( !temporaries_valid[e.idx] );
- temporaries_valid[e.idx] = true;
- )
- else TU_IFLET( ::MIR::LValue, dst, Return, _e,
- )
- else TU_IFLET( ::MIR::LValue, dst, Variable, e,
- if( variables_valid.size() <= e ) {
- variables_valid.resize(e+1);
+ // 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;
}
- if( variables_valid[e] ) {
- //this->push_stmt_drop( dst.clone() );
+ 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;
}
- variables_valid[e] = true;
+ set_variable_state(e, VarState::Init);
+ )
)
- else {
- //this->push_stmt_drop( dst.clone() );
- }
-
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)
@@ -1508,13 +1689,37 @@ void MirBuilder::pause_cur_block()
return rv;
}
-ScopeHandle MirBuilder::new_scope(const Span& sp, bool is_conditional)
+ScopeHandle MirBuilder::new_scope_var(const Span& sp)
{
unsigned int idx = m_scopes.size();
- m_scopes.push_back( {} );
- m_scopes.back().is_conditional = is_conditional;
+ m_scopes.push_back( {false, ScopeType::make_Variables({})} );
m_scope_stack.push_back( idx );
- DEBUG("START scope " << 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)
@@ -1527,16 +1732,19 @@ void MirBuilder::terminate_scope(const Span& sp, ScopeHandle scope)
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) << " in the way");
+ BUG(sp, "Terminating scope " << scope.idx << " when not at top of stack, " << (m_scope_stack.end() - it - 1) << " scopes in the way");
}
- m_scope_stack.pop_back();
auto& scope_def = m_scopes.at(scope.idx);
ASSERT_BUG( sp, scope_def.complete == false, "Terminating scope which is already terminated" );
- scope_def.complete = true;
+
+ 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)
{
@@ -1549,48 +1757,246 @@ void MirBuilder::terminate_scope_early(const Span& sp, const ScopeHandle& scope)
}
unsigned int slot = it - m_scope_stack.begin();
- for(unsigned int i = slot; i < m_scope_stack.size(); i ++)
- {
- }
-
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);
- scope_def.complete = true;
+ 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);
- // TODO: Mark the optional move/termination
+
+ // 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;
+ )
}
- // If a conditional block is hit, prevent full termination of the rest
- if( scope_def.is_conditional )
- is_conditional = 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();
- // 2. Emit drops for all non-moved variables within this branch
- drop_scope_values(scope_def);
+ // 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::drop_scope_values(const ScopeDef& sd)
+void MirBuilder::set_variable_state(unsigned int idx, VarState state)
{
- for(auto tmp_idx : sd.temporaries)
+ for( auto scope_idx : ::reverse(m_scope_stack) )
{
- if( temporaries_valid.at(tmp_idx) ) {
- //push_stmt_drop( ::MIR::LValue::make_Temporary({ tmp_idx }) );
- temporaries_valid[tmp_idx] = false;
- }
+ 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,
- //TODO(Span(), "Mark var as moved");
+ set_variable_state(e, VarState::Moved);
),
(Temporary,
//TODO(Span(), "Mark temp as moved");
@@ -1634,6 +2040,21 @@ ScopeHandle::~ScopeHandle()
}
}
+::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/from_hir.hpp b/src/mir/from_hir.hpp
index d4690936..f899bc29 100644
--- a/src/mir/from_hir.hpp
+++ b/src/mir/from_hir.hpp
@@ -37,6 +37,38 @@ public:
~ScopeHandle();
};
+enum class VarState {
+ Uninit, // No value assigned yet
+ Init, // Initialised and valid at this point
+ MaybeMoved, // Possibly has been moved
+ Moved, // Definitely moved
+ Dropped, // Dropped (out of scope)
+};
+extern ::std::ostream& operator<<(::std::ostream& os, VarState x);
+
+struct SplitArm {
+ bool has_early_terminated = false;
+ bool always_early_terminated = false; // Populated on completion
+ ::std::vector<bool> changed_var_states; // Indexed by binding bumber
+ ::std::vector<VarState> var_states;
+};
+
+TAGGED_UNION(ScopeType, Variables,
+ (Variables, struct {
+ ::std::vector<unsigned int> vars; // List of owned variables
+ ::std::vector<VarState> var_states; // Indexed by position in above list
+ }),
+ (Temporaries, struct {
+ ::std::vector<unsigned int> temporaries; // Controlled temporaries
+ ::std::vector<VarState> states; // Indexed by position in above list
+ }),
+ (Split, struct {
+ ::std::vector<SplitArm> arms;
+ }),
+ (Loop, struct {
+ })
+ );
+
/// Helper class to construct MIR
class MirBuilder
{
@@ -56,31 +88,14 @@ class MirBuilder
struct ScopeDef
{
bool complete = false;
- ::std::vector<unsigned int> variables; // Variables to be dropped at the end of this scope
- ::std::vector<unsigned int> temporaries; // Temporaries introduced during this scope
-
- bool is_conditional = false;
- #if 0
- ::std::vector<bool> moved_vars; // Bitmap of variables moved in the current branch
- ::std::vector<bool> all_moved_vars; // Bitmap of variables moved in at least one branch
- ::std::vector<bool> all_unmoved_vars; // Bitmap of variables NOT moved in at least one branch
- #endif
+ ScopeType data;
};
::std::vector<ScopeDef> m_scopes;
::std::vector<unsigned int> m_scope_stack;
ScopeHandle m_fcn_scope;
public:
- 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 );
- }
+ MirBuilder(::MIR::Function& output);
~MirBuilder();
// - Values
@@ -113,17 +128,28 @@ public:
::MIR::BasicBlockId new_bb_unlinked();
// --- Scopes ---
- /// `is_conditional`: If true, this scope is the contents of a conditional branch of the parent scope
- ScopeHandle new_scope(const Span& sp, bool is_conditional=false);
+ ScopeHandle new_scope_var(const Span& sp);
+ ScopeHandle new_scope_temp(const Span& sp);
+ ScopeHandle new_scope_split(const Span& sp);
+ ScopeHandle new_scope_loop(const Span& sp);
void terminate_scope(const Span& sp, ScopeHandle );
void terminate_scope_early(const Span& sp, const ScopeHandle& );
+ void end_split_arm(const Span& sp, const ScopeHandle& , bool reachable);
const ScopeHandle& fcn_scope() const {
return m_fcn_scope;
}
+ /// Introduce a new variable within the current scope
+ void define_variable(unsigned int idx);
private:
+ VarState get_variable_state(unsigned int idx) const;
+ void set_variable_state(unsigned int idx, VarState state);
+ VarState get_temp_state(unsigned int idx) const;
+ void set_temp_state(unsigned int idx, VarState state);
+
void drop_scope_values(const ScopeDef& sd);
+ void complete_scope(ScopeDef& sd);
// Helper - Marks a variable/... as moved (and checks if the move is valid)
void moved_lvalue(const ::MIR::LValue& lv);
};
@@ -133,6 +159,7 @@ class MirConverter:
{
public:
virtual void destructure_from(const Span& sp, const ::HIR::Pattern& pat, ::MIR::LValue lval, bool allow_refutable=false) = 0;
+ virtual void define_vars_from(const Span& sp, const ::HIR::Pattern& pat) = 0;
};
extern void MIR_LowerHIR_Match(MirBuilder& builder, MirConverter& conv, ::HIR::ExprNode_Match& node, ::MIR::LValue match_val);
diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp
index d1dbfd4d..26035fab 100644
--- a/src/mir/from_hir_match.cpp
+++ b/src/mir/from_hir_match.cpp
@@ -49,6 +49,7 @@ struct ArmCode {
bool has_condition;
::MIR::BasicBlockId cond_code; // NOTE: Incomplete, requires terminating If
::MIR::LValue cond_lval;
+ ::std::vector< ::MIR::BasicBlockId> destructures;
};
typedef ::std::vector<PatternRuleset> t_arm_rules;
@@ -83,7 +84,7 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod
auto first_cmp_block = builder.new_bb_unlinked();
builder.end_block( ::MIR::Terminator::make_Goto(first_cmp_block) );
- auto match_scope = builder.new_scope(node.span());
+ auto match_scope = builder.new_scope_split(node.span());
// Map of arm index to ruleset
::std::vector< ArmCode> arm_code;
@@ -94,43 +95,32 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod
ArmCode ac;
// Register introduced bindings to be dropped on return/diverge within this scope
- auto drop_scope = builder.new_scope( arm.m_code->span(), true );
+ auto drop_scope = builder.new_scope_var( arm.m_code->span() );
+ // - Define variables from the first pattern
+ conv.define_vars_from(node.span(), arm.m_patterns.front());
unsigned int pat_idx = 0;
for( const auto& pat : arm.m_patterns )
{
+ // - Convert HIR pattern into ruleset
auto pat_builder = PatternRulesetBuilder {};
pat_builder.append_from(node.span(), pat, node.m_value->m_res_type);
arm_rules.push_back( PatternRuleset { arm_idx, pat_idx, mv$(pat_builder.m_rules) } );
DEBUG("(" << arm_idx << "," << pat_idx << ") " << pat << " ==> [" << arm_rules.back().m_rules << "]");
+ // - Emit code to destructure the matched pattern
+ ac.destructures.push_back( builder.new_bb_unlinked() );
+ builder.set_cur_block( ac.destructures.back() );
+ conv.destructure_from( arm.m_code->span(), pat, match_val.clone(), true );
+ builder.pause_cur_block();
+ // NOTE: Paused block resumed upon successful match
+
pat_idx += 1;
}
-
- // Code
- ac.code = builder.new_bb_unlinked();
- builder.set_cur_block( ac.code );
- conv.visit_node_ptr( arm.m_code );
- if( !builder.block_active() && !builder.has_result() ) {
- DEBUG("Arm diverged");
- // Nothing need be done, as the block diverged.
- // - Drops were handled by the diverging block (if not, the below will panic)
- auto _ = mv$(drop_scope);
- }
- else {
- DEBUG("Arm result");
- // - Set result
- builder.push_stmt_assign( result_val.clone(), builder.get_result(arm.m_code->span()) );
- // - Drop all non-moved values from this scope
- builder.terminate_scope( arm.m_code->span(), mv$(drop_scope) );
- // - Go to the next block
- builder.end_block( ::MIR::Terminator::make_Goto(next_block) );
- }
// Condition
- // NOTE: The condition isn't covered by the drop scope, because the only values that can be used within it are
- // Copy (otherwise they'd move out and invalidate on failure).
+ // NOTE: Lack of drop due to early exit from this arm isn't an issue. All captures must be Copy
// - The above is rustc E0008 "cannot bind by-move into a pattern guard"
if(arm.m_cond)
{
@@ -152,6 +142,29 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod
ac.has_condition = false;
}
+ // Code
+ ac.code = builder.new_bb_unlinked();
+ builder.set_cur_block( ac.code );
+ conv.visit_node_ptr( arm.m_code );
+ if( !builder.block_active() && !builder.has_result() ) {
+ DEBUG("Arm diverged");
+ // Nothing need be done, as the block diverged.
+ // - Drops were handled by the diverging block (if not, the below will panic)
+ auto _ = mv$(drop_scope);
+ builder.end_split_arm( arm.m_code->span(), match_scope, false );
+ }
+ else {
+ DEBUG("Arm result");
+ // - Set result
+ builder.push_stmt_assign( result_val.clone(), builder.get_result(arm.m_code->span()) );
+ // - Drop all non-moved values from this scope
+ builder.terminate_scope( arm.m_code->span(), mv$(drop_scope) );
+ // - Go to the next block
+ builder.end_block( ::MIR::Terminator::make_Goto(next_block) );
+
+ builder.end_split_arm( arm.m_code->span(), match_scope, true );
+ }
+
arm_code.push_back( mv$(ac) );
}
@@ -192,14 +205,13 @@ void MIR_LowerHIR_Match_Simple( MirBuilder& builder, MirConverter& conv, ::HIR::
for( unsigned int i = 0; i < arm.m_patterns.size(); i ++ )
{
const auto& pat_rule = arm_rules[rule_idx];
- const auto& pat = arm.m_patterns[i];
bool is_last_pat = (i+1 == arm.m_patterns.size());
auto next_pattern_bb = (!is_last_pat ? builder.new_bb_unlinked() : next_arm_bb);
// 1. Check
MIR_LowerHIR_Match_Simple__GeneratePattern(builder, arm.m_code->span(), pat_rule.m_rules.data(), pat_rule.m_rules.size(), node.m_value->m_res_type, match_val, next_pattern_bb);
- // 2. Destructure
- conv.destructure_from( arm.m_code->span(), pat, match_val.clone(), true );
+ builder.end_block( ::MIR::Terminator::make_Goto(arm_code.destructures[i]) );
+ builder.set_cur_block( arm_code.destructures[i] );
// - Go to code/condition check
if( arm_code.has_condition )
@@ -739,17 +751,11 @@ void MIR_LowerHIR_Match_DecisionTree( MirBuilder& builder, MirConverter& conv, :
::std::vector< ::MIR::BasicBlockId> rule_blocks;
for(const auto& rule : arm_rules)
{
- rule_blocks.push_back( builder.new_bb_unlinked() );
- builder.set_cur_block(rule_blocks.back());
-
- const auto& arm = node.m_arms[ rule.arm_idx ];
- const auto& pat = arm.m_patterns[rule.pat_idx];
-
- // Assign bindings (drop registration happens in previous loop) - Allow refutable patterns
- conv.destructure_from( arm.m_code->span(), pat, match_val.clone(), true );
+ const auto& arm_code = arms_code[rule.arm_idx];
+ ASSERT_BUG(node.span(), !arm_code.has_condition, "Decision tree doesn't (yet) support conditionals");
- ASSERT_BUG(node.span(), ! arms_code[rule.arm_idx].has_condition, "Decision tree doesn't (yet) support conditionals");
- builder.end_block( ::MIR::Terminator::make_Goto( arms_code[rule.arm_idx].code ) );
+ assert( rule.pat_idx < arm_code.destructures.size() );
+ rule_blocks.push_back( arm_code.destructures[rule.pat_idx] );
}