diff options
author | John Hodge <tpg@mutabah.net> | 2016-08-21 15:55:36 +0800 |
---|---|---|
committer | John Hodge <tpg@mutabah.net> | 2016-08-21 15:55:36 +0800 |
commit | 8ae6dada7551e74c064ee8d18bcfd900fcd3aaf3 (patch) | |
tree | e98f23eb9ee6aa18b4892f88e795c7d4d64ae3b2 | |
parent | 025ebf7a067d27c07071e46998ac556ea225948c (diff) | |
download | mrust-8ae6dada7551e74c064ee8d18bcfd900fcd3aaf3.tar.gz |
MIR Gen - Rework to have proper drop scopes (INCOMPLETE)
-rw-r--r-- | src/mir/from_hir.cpp | 571 | ||||
-rw-r--r-- | src/mir/from_hir.hpp | 69 | ||||
-rw-r--r-- | src/mir/from_hir_match.cpp | 80 |
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] ); } |