diff options
author | John Hodge (bugs) <tpg@mutabah.net> | 2017-05-14 12:01:45 +0800 |
---|---|---|
committer | John Hodge (bugs) <tpg@mutabah.net> | 2017-05-14 12:01:45 +0800 |
commit | 1d2fe7681219700998c8ecbdb8ed5acab66578df (patch) | |
tree | 0d38e8ab5fd80c890d3dec67a0596abfc7a4f0cd /src/mir/mir_builder.cpp | |
parent | c6fca061dd134068c831aefd88d9535a30f423ed (diff) | |
parent | fde22b3f03d802231985b8ded567cba16cb5aa00 (diff) | |
download | mrust-1d2fe7681219700998c8ecbdb8ed5acab66578df.tar.gz |
Merge branch 'master' of https://github.com/thepowersgang/mrustc
# Conflicts:
# src/common.hpp
# src/hir/deserialise.cpp
# src/hir_typeck/static.cpp
# src/mir/from_hir.cpp
# src/mir/from_hir.hpp
# src/mir/from_hir_match.cpp
# src/mir/helpers.hpp
# src/mir/mir_builder.cpp
Diffstat (limited to 'src/mir/mir_builder.cpp')
-rw-r--r-- | src/mir/mir_builder.cpp | 1408 |
1 files changed, 1008 insertions, 400 deletions
diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp index 296dfa91..eb50e9f7 100644 --- a/src/mir/mir_builder.cpp +++ b/src/mir/mir_builder.cpp @@ -32,8 +32,14 @@ MirBuilder::MirBuilder(const Span& sp, const StaticTraitResolve& resolve, const m_scopes.push_back( ScopeDef { sp, ScopeType::make_Temporaries({}) } ); m_scope_stack.push_back( 1 ); + + m_if_cond_lval = this->new_temporary(::HIR::CoreType::Bool); + + m_arg_states.reserve( args.size() ); + for(size_t i = 0; i < args.size(); i ++ ) + m_arg_states.push_back( VarState::make_Valid({}) ); m_variable_states.reserve( output.named_variables.size() ); - for(unsigned int i = 0; i < output.named_variables.size(); i ++ ) + for(size_t i = 0; i < output.named_variables.size(); i ++ ) m_variable_states.push_back( VarState::make_Invalid(InvalidType::Uninit) ); } MirBuilder::~MirBuilder() @@ -116,6 +122,22 @@ void MirBuilder::define_variable(unsigned int idx) top_scope = &m_scopes.at(idx); break ; } + else if( m_scopes.at(idx).data.is_Loop() ) + { + // Newly created temporary within a loop, if there is a saved + // state this temp needs a drop flag. + // TODO: ^ + } + else if( m_scopes.at(idx).data.is_Split() ) + { + // Newly created temporary within a split, if there is a saved + // state this temp needs a drop flag. + // TODO: ^ + } + else + { + // Nothign. + } } assert( top_scope ); auto& tmp_scope = top_scope->data.as_Temporaries(); @@ -190,7 +212,7 @@ void MirBuilder::define_variable(unsigned int idx) { auto temp = new_temporary(ty); push_stmt_assign( sp, ::MIR::LValue(temp.clone()), mv$(rv) ); - return ::MIR::Param( mv$(temp) ); + return temp; } } void MirBuilder::set_result(const Span& sp, ::MIR::RValue val) @@ -263,7 +285,7 @@ void MirBuilder::push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RVal // Doesn't move ), (MakeDst, - // Doesn't move ptr_val + moved_param(e.ptr_val); moved_param(e.meta_val); ), (Tuple, @@ -285,7 +307,7 @@ void MirBuilder::push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RVal // Drop target if populated mark_value_assigned(sp, dst); - m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Assign({ mv$(dst), mv$(val) }) ); + this->push_stmt( sp, ::MIR::Statement::make_Assign({ mv$(dst), mv$(val) }) ); } void MirBuilder::push_stmt_drop(const Span& sp, ::MIR::LValue val, unsigned int flag/*=~0u*/) { @@ -297,11 +319,13 @@ void MirBuilder::push_stmt_drop(const Span& sp, ::MIR::LValue val, unsigned int return ; } - DEBUG("DROP " << val); - - auto stmt = ::MIR::Statement::make_Drop({ ::MIR::eDropKind::DEEP, mv$(val), flag }); + this->push_stmt(sp, ::MIR::Statement::make_Drop({ ::MIR::eDropKind::DEEP, mv$(val), flag })); - m_output.blocks.at(m_current_block).statements.push_back( mv$(stmt) ); + if( flag != ~0u ) + { + // Reset flag value back to default. + push_stmt_set_dropflag_val(sp, flag, m_output.drop_flags.at(flag)); + } } void MirBuilder::push_stmt_drop_shallow(const Span& sp, ::MIR::LValue val, unsigned int flag/*=~0u*/) { @@ -310,8 +334,13 @@ void MirBuilder::push_stmt_drop_shallow(const Span& sp, ::MIR::LValue val, unsig // TODO: Ensure that the type is a Box? - DEBUG("DROP shallow " << val); - m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Drop({ ::MIR::eDropKind::SHALLOW, mv$(val), flag }) ); + this->push_stmt(sp, ::MIR::Statement::make_Drop({ ::MIR::eDropKind::SHALLOW, mv$(val), flag })); + + if( flag != ~0u ) + { + // Reset flag value back to default. + push_stmt_set_dropflag_val(sp, flag, m_output.drop_flags.at(flag)); + } } void MirBuilder::push_stmt_asm(const Span& sp, ::MIR::Statement::Data_Asm data) { @@ -322,17 +351,25 @@ void MirBuilder::push_stmt_asm(const Span& sp, ::MIR::Statement::Data_Asm data) mark_value_assigned(sp, v.second); // 2. Push - m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Asm( mv$(data) ) ); + this->push_stmt(sp, ::MIR::Statement::make_Asm( mv$(data) )); } void MirBuilder::push_stmt_set_dropflag_val(const Span& sp, unsigned int idx, bool value) { - ASSERT_BUG(sp, m_block_active, "Pushing statement with no active block"); - m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_SetDropFlag({ idx, value }) ); + this->push_stmt(sp, ::MIR::Statement::make_SetDropFlag({ idx, value })); } void MirBuilder::push_stmt_set_dropflag_other(const Span& sp, unsigned int idx, unsigned int other) { + this->push_stmt(sp, ::MIR::Statement::make_SetDropFlag({ idx, false, other })); +} +void MirBuilder::push_stmt_set_dropflag_default(const Span& sp, unsigned int idx) +{ + this->push_stmt(sp, ::MIR::Statement::make_SetDropFlag({ idx, this->get_drop_flag_default(sp, idx) })); +} +void MirBuilder::push_stmt(const Span& sp, ::MIR::Statement stmt) +{ ASSERT_BUG(sp, m_block_active, "Pushing statement with no active block"); - m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_SetDropFlag({ idx, false, other }) ); + DEBUG(stmt); + m_output.blocks.at(m_current_block).statements.push_back( mv$(stmt) ); } void MirBuilder::mark_value_assigned(const Span& sp, const ::MIR::LValue& dst) @@ -374,32 +411,56 @@ void MirBuilder::mark_value_assigned(const Span& sp, const ::MIR::LValue& dst) } } -void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const ScopeHandle& scope) +void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const ScopeHandle& scope, bool to_above/*=false*/) { TRACE_FUNCTION_F(val); TU_MATCH_DEF(::MIR::LValue, (val), (e), ( + // No raising of these source values? + return ; ), // TODO: This may not be correct, because it can change the drop points and ordering // HACK: Working around cases where values are dropped while the result is not yet used. + (Index, + raise_variables(sp, *e.val, scope, to_above); + raise_variables(sp, *e.idx, scope, to_above); + return ; + ), (Deref, - raise_variables(sp, *e.val, scope); + raise_variables(sp, *e.val, scope, to_above); + return ; ), (Field, - raise_variables(sp, *e.val, scope); + raise_variables(sp, *e.val, scope, to_above); + return ; ), (Downcast, - raise_variables(sp, *e.val, scope); + raise_variables(sp, *e.val, scope, to_above); + return ; ), // Actual value types (Variable, - auto idx = e; - auto scope_it = m_scope_stack.rbegin(); - while( scope_it != m_scope_stack.rend() ) + ), + (Temporary, + ) + ) + ASSERT_BUG(sp, val.is_Variable() || val.is_Temporary(), "Hit value raising code with non-variable value - " << val); + + // Find controlling scope + auto scope_it = m_scope_stack.rbegin(); + while( scope_it != m_scope_stack.rend() ) + { + auto& scope_def = m_scopes.at(*scope_it); + + if( *scope_it == scope.idx && !to_above ) { - auto& scope_def = m_scopes.at(*scope_it); + DEBUG(val << " defined in or above target (scope " << scope << ")"); + } - TU_IFLET( ScopeType, scope_def.data, Variables, e, + TU_IFLET( ScopeType, scope_def.data, Variables, e, + if( const auto* ve = val.opt_Variable() ) + { + auto idx = *ve; auto tmp_it = ::std::find( e.vars.begin(), e.vars.end(), idx ); if( tmp_it != e.vars.end() ) { @@ -407,41 +468,12 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const DEBUG("Raise variable " << idx << " from " << *scope_it); break ; } - ) - // If the variable was defined above the desired scope (i.e. this didn't find it), return - if( *scope_it == scope.idx ) - return ; - ++scope_it; - } - if( scope_it == m_scope_stack.rend() ) - { - // Temporary wasn't defined in a visible scope? - return ; - } - ++scope_it; - - while( scope_it != m_scope_stack.rend() ) - { - auto& scope_def = m_scopes.at(*scope_it); - - TU_IFLET( ScopeType, scope_def.data, Variables, e, - e.vars.push_back( idx ); - DEBUG("- to " << *scope_it); - return ; - ) - ++scope_it; - } - - DEBUG("- top"); - ), - (Temporary, - auto idx = e.idx; - auto scope_it = m_scope_stack.rbegin(); - while( scope_it != m_scope_stack.rend() ) - { - auto& scope_def = m_scopes.at(*scope_it); - - TU_IFLET( ScopeType, scope_def.data, Temporaries, e, + } + ) + else TU_IFLET( ScopeType, scope_def.data, Temporaries, e, + if( const auto* ve = val.opt_Temporary() ) + { + auto idx = ve->idx; auto tmp_it = ::std::find( e.temporaries.begin(), e.temporaries.end(), idx ); if( tmp_it != e.temporaries.end() ) { @@ -449,45 +481,169 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const DEBUG("Raise temporary " << idx << " from " << *scope_it); break ; } - ) - - // If the temporary was defined above the desired scope (i.e. this didn't find it), return - if( *scope_it == scope.idx ) - return ; - ++scope_it; + } + ) + else + { + // TODO: Does this need to handle this value being set in the + // split scopes? } - if( scope_it == m_scope_stack.rend() ) + // If the variable was defined above the desired scope (i.e. this didn't find it), return + if( *scope_it == scope.idx ) { - // Temporary wasn't defined in a visible scope? + DEBUG("Value " << val << " is defined above the target (scope " << scope << ")"); return ; } ++scope_it; + } + if( scope_it == m_scope_stack.rend() ) + { + // Temporary wasn't defined in a visible scope? + BUG(sp, val << " wasn't defined in a visible scope"); + return ; + } - while( scope_it != m_scope_stack.rend() ) - { - auto& scope_def = m_scopes.at(*scope_it); + // If the definition scope was the target scope + bool target_seen = false; + if( *scope_it == scope.idx ) + { + if( to_above ) { + // Want to shift to any above (but not including) it + ++ scope_it; + } + else { + // Want to shift to it or above. + } - TU_IFLET( ScopeType, scope_def.data, Temporaries, e, - e.temporaries.push_back( idx ); - DEBUG("- to " << *scope_it); - return ; - ) - ++scope_it; + target_seen = true; + } + else + { + // Don't bother searching the original definition scope + ++scope_it; + } + + // Iterate stack until: + // - The target scope is seen + // - AND a scope was found for it + for( ; scope_it != m_scope_stack.rend(); ++ scope_it ) + { + auto& scope_def = m_scopes.at(*scope_it); + DEBUG("> Cross " << *scope_it << " - " << scope_def.data.tag_str()); + + if( *scope_it == scope.idx ) + { + target_seen = true; } - DEBUG("- top"); + TU_IFLET( ScopeType, scope_def.data, Variables, e, + if( target_seen ) + { + if( const auto* ve = val.opt_Variable() ) + { + e.vars.push_back( *ve ); + DEBUG("- to " << *scope_it); + return ; + } + } ) - ) + else TU_IFLET( ScopeType, scope_def.data, Temporaries, e, + if( target_seen ) + { + if( const auto* ve = val.opt_Temporary() ) + { + e.temporaries.push_back( ve->idx ); + DEBUG("- to " << *scope_it); + return ; + } + } + ) + else if( auto* sd_loop = scope_def.data.opt_Loop() ) + { + // If there is an exit state present, ensure that this variable is + // present in that state (as invalid, as it can't have been valid + // externally) + if( sd_loop->exit_state_valid ) + { + DEBUG("Adding " << val << " as unset to loop exit state"); + if( const auto* ve = val.opt_Variable() ) + { + auto v = sd_loop->exit_state.var_states.insert( ::std::make_pair(*ve, VarState(InvalidType::Uninit)) ); + ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); + } + else if( const auto* ve = val.opt_Temporary() ) + { + auto v = sd_loop->exit_state.tmp_states.insert( ::std::make_pair(ve->idx, VarState(InvalidType::Uninit)) ); + ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); + } + else { + BUG(sp, "Impossible raise value"); + } + } + else + { + DEBUG("Crossing loop with no existing exit state"); + } + } + else if( auto* sd_split = scope_def.data.opt_Split() ) + { + // If the split has already registered an exit state, ensure that + // this variable is present in it. (as invalid) + if( sd_split->end_state_valid ) + { + DEBUG("Adding " << val << " as unset to loop exit state"); + if( const auto* ve = val.opt_Variable() ) + { + auto v = sd_split->end_state.var_states.insert( ::std::make_pair(*ve, VarState(InvalidType::Uninit)) ); + ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); + } + else if( const auto* ve = val.opt_Temporary() ) + { + auto v = sd_split->end_state.tmp_states.insert( ::std::make_pair(ve->idx, VarState(InvalidType::Uninit)) ); + ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); + } + else { + BUG(sp, "Impossible raise value"); + } + } + else + { + DEBUG("Crossing split with no existing end state"); + } + + // TODO: This should update the outer state to unset. + auto& arm = sd_split->arms.back(); + if( const auto* ve = val.opt_Variable() ) + { + arm.var_states.insert(::std::make_pair( *ve, get_variable_state(sp, *ve).clone() )); + m_variable_states.at(*ve) = VarState(InvalidType::Uninit); + } + else if( const auto* ve = val.opt_Temporary() ) + { + arm.tmp_states.insert(::std::make_pair( ve->idx, get_temp_state(sp, ve->idx).clone() )); + m_temporary_states.at(ve->idx) = VarState(InvalidType::Uninit); + } + else + { + BUG(sp, "Impossible raise value"); + } + } + else + { + BUG(sp, "Crossing unknown scope type - " << scope_def.data.tag_str()); + } + } + BUG(sp, "Couldn't find a scope to raise " << val << " into"); } -void MirBuilder::raise_variables(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope) +void MirBuilder::raise_variables(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope, bool to_above/*=false*/) { auto raise_vars = [&](const ::MIR::Param& p) { if( const auto* e = p.opt_LValue() ) - this->raise_variables(sp, *e, scope); + this->raise_variables(sp, *e, scope, to_above); }; TU_MATCHA( (rval), (e), (Use, - this->raise_variables(sp, e, scope); + this->raise_variables(sp, e, scope, to_above); ), (Constant, ), @@ -496,23 +652,23 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::RValue& rval, cons ), (Borrow, // TODO: Wait, is this valid? - this->raise_variables(sp, e.val, scope); + this->raise_variables(sp, e.val, scope, to_above); ), (Cast, - this->raise_variables(sp, e.val, scope); + this->raise_variables(sp, e.val, scope, to_above); ), (BinOp, raise_vars(e.val_l); raise_vars(e.val_r); ), (UniOp, - this->raise_variables(sp, e.val, scope); + this->raise_variables(sp, e.val, scope, to_above); ), (DstMeta, - this->raise_variables(sp, e.val, scope); + this->raise_variables(sp, e.val, scope, to_above); ), (DstPtr, - this->raise_variables(sp, e.val, scope); + this->raise_variables(sp, e.val, scope, to_above); ), (MakeDst, raise_vars(e.ptr_val); @@ -656,6 +812,21 @@ void MirBuilder::terminate_scope(const Span& sp, ScopeHandle scope, bool emit_cl { // 2. Emit drops for all non-moved variables (share with below) drop_scope_values(scope_def); + + // Emit ScopeEnd for all controlled values + ::MIR::Statement::Data_ScopeEnd se; + if(const auto* e = scope_def.data.opt_Variables() ) { + se.vars = e->vars; + } + else if(const auto* e = scope_def.data.opt_Temporaries()) { + se.tmps = e->temporaries; + } + else { + } + // Only push the ScopeEnd if there were variables to end + if( !se.vars.empty() || !se.tmps.empty() ) { + this->push_stmt(sp, ::MIR::Statement( mv$(se) )); + } } // 3. Pop scope (last because `drop_scope_values` uses the stack) @@ -663,6 +834,107 @@ void MirBuilder::terminate_scope(const Span& sp, ScopeHandle scope, bool emit_cl complete_scope(scope_def); } +void MirBuilder::raise_all(const Span& sp, ScopeHandle source, const ScopeHandle& target) +{ + TRACE_FUNCTION_F("scope " << source.idx << " => " << target.idx); + + // 1. Check that this is the current scope (at the top of the stack) + if( m_scope_stack.empty() || m_scope_stack.back() != source.idx ) + { + DEBUG("- m_scope_stack = [" << m_scope_stack << "]"); + auto it = ::std::find( m_scope_stack.begin(), m_scope_stack.end(), source.idx ); + if( it == m_scope_stack.end() ) + BUG(sp, "Terminating scope not on the stack - scope " << source.idx); + BUG(sp, "Terminating scope " << source.idx << " when not at top of stack, " << (m_scope_stack.end() - it - 1) << " scopes in the way"); + } + auto& src_scope_def = m_scopes.at(source.idx); + +#if 1 + ASSERT_BUG(sp, src_scope_def.data.is_Temporaries(), "Rasising scopes can only be done on temporaries (source)"); + auto& src_list = src_scope_def.data.as_Temporaries().temporaries; + for(auto idx : src_list) + { + DEBUG("> Raising " << ::MIR::LValue::make_Temporary({ idx })); + } + + // Seek up stack until the target scope is seen + auto it = m_scope_stack.rbegin() + 1; + for( ; it != m_scope_stack.rend() && *it != target.idx; ++it) + { + auto& scope_def = m_scopes.at(*it); + + if(auto* sd_loop = scope_def.data.opt_Loop()) + { + if(sd_loop->exit_state_valid) + { + DEBUG("Crossing loop with existing end state"); + // Insert these values as Invalid, both in the existing exit state, and in the changed list + for(auto idx : src_list) + { + auto v = sd_loop->exit_state.tmp_states.insert(::std::make_pair( idx, VarState(InvalidType::Uninit) )); + ASSERT_BUG(sp, v.second, ""); + } + } + else + { + DEBUG("Crossing loop with no end state"); + } + + for(auto idx : src_list) + { + auto v2 = sd_loop->changed_tmps.insert(::std::make_pair( idx, VarState(InvalidType::Uninit) )); + ASSERT_BUG(sp, v2.second, ""); + } + } + else if(auto* sd_split = scope_def.data.opt_Split()) + { + if(sd_split->end_state_valid) + { + DEBUG("Crossing split with existing end state"); + // Insert these indexes as Invalid + for(auto idx : src_list) + { + auto v = sd_split->end_state.tmp_states.insert(::std::make_pair( idx, VarState(InvalidType::Uninit) )); + ASSERT_BUG(sp, v.second, ""); + } + } + else + { + DEBUG("Crossing split with no end state"); + } + + // TODO: Insert current state in the current arm + assert(!sd_split->arms.empty()); + auto& arm = sd_split->arms.back(); + for(auto idx : src_list) + { + arm.tmp_states.insert(::std::make_pair( idx, mv$(m_temporary_states.at(idx)) )); + m_temporary_states.at(idx) = VarState(InvalidType::Uninit); + } + } + } + if(it == m_scope_stack.rend()) + { + BUG(sp, "Moving values to a scope not on the stack - scope " << target.idx); + } + auto& tgt_scope_def = m_scopes.at(target.idx); + ASSERT_BUG(sp, tgt_scope_def.data.is_Temporaries(), "Rasising scopes can only be done on temporaries (target)"); + + // Move all defined variables from one to the other + auto& tgt_list = tgt_scope_def.data.as_Temporaries().temporaries; + tgt_list.insert( tgt_list.end(), src_list.begin(), src_list.end() ); +#else + auto list = src_scope_def.data.as_Temporaries().temporaries; + for(auto idx : list) + { + this->raise_variables(sp, ::MIR::LValue::make_Temporary({ idx }), target); + } +#endif + + // Scope completed + m_scope_stack.pop_back(); + src_scope_def.complete = true; +} void MirBuilder::terminate_scope_early(const Span& sp, const ScopeHandle& scope, bool loop_exit/*=false*/) { @@ -717,7 +989,7 @@ namespace { static void merge_state(const Span& sp, MirBuilder& builder, const ::MIR::LValue& lv, VarState& old_state, const VarState& new_state) { - DEBUG(lv << " : " << old_state << " <= " << new_state); + TRACE_FUNCTION_FR(lv << " : " << old_state << " <= " << new_state, lv << " : " << old_state); switch(old_state.tag()) { case VarState::TAGDEAD: throw ""; @@ -735,61 +1007,104 @@ namespace case VarState::TAG_Optional: { // Was invalid, now optional. auto flag_idx = new_state.as_Optional(); - if( builder.get_drop_flag_default(sp, flag_idx) != false ) { + if( true || builder.get_drop_flag_default(sp, flag_idx) != false ) { #if 1 auto new_flag = builder.new_drop_flag(false); builder.push_stmt_set_dropflag_other(sp, new_flag, flag_idx); + builder.push_stmt_set_dropflag_default(sp, flag_idx); old_state = VarState::make_Optional( new_flag ); #else // TODO: Rewrite history. I.e. visit all previous branches and set this drop flag to `false` in all of them TODO(sp, "Drop flag default not false when going Invalid->Optional"); #endif } - old_state = VarState::make_Optional( flag_idx ); + else { + old_state = VarState::make_Optional( flag_idx ); + } return ; } - case VarState::TAG_Partial: { - const auto& nse = new_state.as_Partial(); + case VarState::TAG_MovedOut: { + const auto& nse = new_state.as_MovedOut(); + + // Create a new state that is internally valid and uses the same drop flag + old_state = VarState::make_MovedOut({ box$(old_state.clone()), nse.outer_flag }); + auto& ose = old_state.as_MovedOut(); + if( ose.outer_flag != ~0u ) + { + // If the flag's default isn't false, then create a new flag that does have such a default + // - Other arm (old_state) uses default, this arm (new_state) can be manipulated + if( builder.get_drop_flag_default(sp, ose.outer_flag) != false ) + { + auto new_flag = builder.new_drop_flag(false); + builder.push_stmt_set_dropflag_other(sp, new_flag, nse.outer_flag); + builder.push_stmt_set_dropflag_default(sp, nse.outer_flag); + ose.outer_flag = new_flag; + } + } + else + { + // In the old arm, the container isn't valid. Create a drop flag with a default of false and set it to true + ose.outer_flag = builder.new_drop_flag(false); + builder.push_stmt_set_dropflag_val(sp, ose.outer_flag, true); + } + bool is_box = false; - builder.with_val_type(sp, lv, [&](const auto& ty){ is_box = builder.is_type_owned_box(ty); }); - if( is_box ) { - ASSERT_BUG(sp, nse.inner_states.size() == 1, ""); + builder.with_val_type(sp, lv, [&](const auto& ty){ + is_box = builder.is_type_owned_box(ty); + }); + if( is_box ) + { + merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, *nse.inner_state); } - if( nse.outer_flag != ~0u ) { - // Set the outer flag to `true` if its default isn't true - if( builder.get_drop_flag_default(sp, nse.outer_flag) != false ) { - builder.push_stmt_set_dropflag_val(sp, nse.outer_flag, false); - } + else + { + BUG(sp, "Handle MovedOut on non-Box"); + } + return ; } + case VarState::TAG_Partial: { + const auto& nse = new_state.as_Partial(); + bool is_enum = false; + builder.with_val_type(sp, lv, [&](const auto& ty){ + is_enum = ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum(); + }); - auto out = new_state.clone(); - auto& ose = out.as_Partial(); - if( ose.outer_flag == ~0u ) + // Create a partial filled with Invalid { - ose.outer_flag = builder.new_drop_flag_and_set(sp, true); // Only in this arm is the container valid + ::std::vector<VarState> inner; inner.reserve( nse.inner_states.size() ); + for(size_t i = 0; i < nse.inner_states.size(); i++) + inner.push_back( old_state.clone() ); + old_state = VarState::make_Partial({ mv$(inner) }); } - if( is_box ) { - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), ose.inner_states[0], old_state); + auto& ose = old_state.as_Partial(); + if( is_enum ) { + for(size_t i = 0; i < ose.inner_states.size(); i ++) + { + merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast<unsigned int>(i) }), ose.inner_states[i], nse.inner_states[i]); + } } else { for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) { - merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], old_state); + merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], nse.inner_states[i]); } } - old_state = mv$(out); } return; } break; + // Valid <= ... case VarState::TAG_Valid: switch( new_state.tag() ) { case VarState::TAGDEAD: throw ""; + // Valid <= Invalid case VarState::TAG_Invalid: old_state = VarState::make_Optional( builder.new_drop_flag_and_set(sp, false) ); return ; + // Valid <= Valid case VarState::TAG_Valid: return ; + // Valid <= Optional case VarState::TAG_Optional: { auto flag_idx = new_state.as_Optional(); // Was valid, now optional. @@ -798,6 +1113,7 @@ namespace #if 1 auto new_flag = builder.new_drop_flag(true); builder.push_stmt_set_dropflag_other(sp, new_flag, flag_idx); + builder.push_stmt_set_dropflag_default(sp, flag_idx); old_state = VarState::make_Optional( new_flag ); #else // OR: Push an assign of this flag to every other completed arm @@ -816,39 +1132,79 @@ namespace } return ; } - case VarState::TAG_Partial: { - const auto& nse = new_state.as_Partial(); + // Valid <= MovedOut + case VarState::TAG_MovedOut: { + const auto& nse = new_state.as_MovedOut(); + + // Create a new staet that is internally valid and uses the same drop flag + old_state = VarState::make_MovedOut({ box$(VarState::make_Valid({})), nse.outer_flag }); + auto& ose = old_state.as_MovedOut(); + if( ose.outer_flag != ~0u ) + { + // If the flag's default isn't true, then create a new flag that does have such a default + // - Other arm (old_state) uses default, this arm (new_state) can be manipulated + if( builder.get_drop_flag_default(sp, ose.outer_flag) != true ) + { + auto new_flag = builder.new_drop_flag(true); + builder.push_stmt_set_dropflag_other(sp, new_flag, nse.outer_flag); + builder.push_stmt_set_dropflag_default(sp, nse.outer_flag); + ose.outer_flag = new_flag; + } + } + else + { + // In both arms, the container is valid. No need for a drop flag + } + bool is_box = false; - builder.with_val_type(sp, lv, [&](const auto& ty){ is_box = builder.is_type_owned_box(ty); }); + builder.with_val_type(sp, lv, [&](const auto& ty){ + is_box = builder.is_type_owned_box(ty); + }); + if( is_box ) { - ASSERT_BUG(sp, nse.inner_states.size() == 1, ""); + merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, *nse.inner_state); } - if( nse.outer_flag != ~0u ) { - // Set the outer flag to `true` if its default isn't true - if( builder.get_drop_flag_default(sp, nse.outer_flag) != true ) { - builder.push_stmt_set_dropflag_val(sp, nse.outer_flag, true); - } + else { + BUG(sp, "MovedOut on non-Box"); + } + return; } + // Valid <= Partial + case VarState::TAG_Partial: { + const auto& nse = new_state.as_Partial(); + bool is_enum = false; + builder.with_val_type(sp, lv, [&](const auto& ty){ + is_enum = ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum(); + }); - auto out = new_state.clone(); - auto& ose = out.as_Partial(); - if( ose.outer_flag == ~0u ) + // Create a partial filled with Valid { - ose.outer_flag = builder.new_drop_flag(true); // In both arms, the container is valid + ::std::vector<VarState> inner; inner.reserve( nse.inner_states.size() ); + for(size_t i = 0; i < nse.inner_states.size(); i++) + inner.push_back( VarState::make_Valid({}) ); + old_state = VarState::make_Partial({ mv$(inner) }); } - if( is_box ) { - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), ose.inner_states[0], old_state); + auto& ose = old_state.as_Partial(); + if( is_enum ) { + auto ilv = ::MIR::LValue::make_Downcast({ box$(lv.clone()), 0 }); + for(size_t i = 0; i < ose.inner_states.size(); i ++) + { + merge_state(sp, builder, ilv, ose.inner_states[i], nse.inner_states[i]); + ilv.as_Downcast().variant_index ++; + } } else { + auto ilv = ::MIR::LValue::make_Field({ box$(lv.clone()), 0 }); for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) { - merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], old_state); + merge_state(sp, builder, ilv, ose.inner_states[i], nse.inner_states[i]); + ilv.as_Field().field_index ++; } } - old_state = mv$(out); } return; } break; + // Optional <= ... case VarState::TAG_Optional: switch( new_state.tag() ) { @@ -863,46 +1219,134 @@ namespace if( old_state.as_Optional() != new_state.as_Optional() ) { #if 1 builder.push_stmt_set_dropflag_other(sp, old_state.as_Optional(), new_state.as_Optional()); + builder.push_stmt_set_dropflag_default(sp, new_state.as_Optional()); #else // TODO: Rewrite history replacing one flag with another (if they have the same default) #endif } return ; - case VarState::TAG_Partial: - TODO(sp, "Handle Optional->Partial in split scope"); + case VarState::TAG_MovedOut: + TODO(sp, "Handle Optional->MovedOut in split scope"); + case VarState::TAG_Partial: { + const auto& nse = new_state.as_Partial(); + bool is_enum = false; + builder.with_val_type(sp, lv, [&](const auto& ty){ + assert( !builder.is_type_owned_box(ty) ); + is_enum = ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum(); + }); + // Create a Partial filled with copies of the Optional + { + ::std::vector<VarState> inner; + inner.reserve( nse.inner_states.size() ); + for(size_t i = 0; i < nse.inner_states.size(); i ++) + inner.push_back(old_state.clone()); + old_state = VarState::make_Partial({ mv$(inner) }); + } + auto& ose = old_state.as_Partial(); + // Propagate to inners + if( is_enum ) { + for(size_t i = 0; i < ose.inner_states.size(); i ++) + { + merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast<unsigned int>(i) }), ose.inner_states[i], nse.inner_states[i]); + } + } + else { + for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) + { + merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], nse.inner_states[i]); + } + } + return; } } break; - case VarState::TAG_Partial: { - auto& ose = old_state.as_Partial(); + case VarState::TAG_MovedOut: { + auto& ose = old_state.as_MovedOut(); bool is_box = false; - builder.with_val_type(sp, lv, [&](const auto& ty){ is_box = builder.is_type_owned_box(ty); }); - if( is_box ) { - ASSERT_BUG(sp, ose.inner_states.size() == 1, ""); + builder.with_val_type(sp, lv, [&](const auto& ty){ + is_box = builder.is_type_owned_box(ty); + }); + if( !is_box ) { + BUG(sp, "MovedOut on non-Box"); } - // Need to tag for conditional shallow drop? Or just do that at the end of the split? - // - End of the split means that the only optional state is outer drop. switch( new_state.tag() ) { case VarState::TAGDEAD: throw ""; case VarState::TAG_Invalid: - if( ose.outer_flag != ~0u ) { - // Set the outer flag to `false` if its default isn't false - if( builder.get_drop_flag_default(sp, ose.outer_flag) != false ) { + case VarState::TAG_Valid: { + bool is_valid = new_state.is_Valid(); + if( ose.outer_flag == ~0u ) + { + // If not valid in new arm, then the outer state is conditional + if( !is_valid ) + { + ose.outer_flag = builder.new_drop_flag(true); builder.push_stmt_set_dropflag_val(sp, ose.outer_flag, false); } } - if( 0 ) - // - Fall through - case VarState::TAG_Valid: - if( ose.outer_flag != ~0u ) { - // Set the outer flag to `true` if its default isn't true - if( builder.get_drop_flag_default(sp, ose.outer_flag) != true ) { - builder.push_stmt_set_dropflag_val(sp, ose.outer_flag, true); + else + { + builder.push_stmt_set_dropflag_val(sp, ose.outer_flag, is_valid); + } + + merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, new_state); + return ; } + case VarState::TAG_Optional: { + const auto& nse = new_state.as_Optional(); + if( ose.outer_flag == ~0u ) + { + if( ! builder.get_drop_flag_default(sp, nse) ) + { + // Default wasn't true, need to make a new flag that does have a default of true + auto new_flag = builder.new_drop_flag(true); + builder.push_stmt_set_dropflag_other(sp, new_flag, nse); + builder.push_stmt_set_dropflag_default(sp, nse); + ose.outer_flag = new_flag; + } + else + { + ose.outer_flag = nse; } } - - if( is_box ) { - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), ose.inner_states[0], new_state); + else + { + // In this arm, assign the outer state to this drop flag + builder.push_stmt_set_dropflag_other(sp, ose.outer_flag, nse); + builder.push_stmt_set_dropflag_default(sp, nse); + } + merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, new_state); + return; } + case VarState::TAG_MovedOut: { + const auto& nse = new_state.as_MovedOut(); + if( ose.outer_flag != nse.outer_flag ) + { + TODO(sp, "Handle mismatched flags in MovedOut"); + } + merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, *nse.inner_state); + return; } + case VarState::TAG_Partial: + BUG(sp, "MovedOut->Partial not valid"); + } + break; } + case VarState::TAG_Partial: { + auto& ose = old_state.as_Partial(); + bool is_enum = false; + builder.with_val_type(sp, lv, [&](const auto& ty){ + assert( !builder.is_type_owned_box(ty) ); + is_enum = ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum(); + }); + // Need to tag for conditional shallow drop? Or just do that at the end of the split? + // - End of the split means that the only optional state is outer drop. + switch( new_state.tag() ) + { + case VarState::TAGDEAD: throw ""; + case VarState::TAG_Invalid: + case VarState::TAG_Valid: + case VarState::TAG_Optional: + if( is_enum ) { + for(size_t i = 0; i < ose.inner_states.size(); i ++) + { + merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast<unsigned int>(i) }), ose.inner_states[i], new_state); + } } else { for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) @@ -911,17 +1355,16 @@ namespace } } return ; - case VarState::TAG_Optional: { - //auto flag_idx = new_state.as_Optional(); - TODO(sp, "Handle Partial->Optional in split scope"); - } return; + case VarState::TAG_MovedOut: + BUG(sp, "Partial->MovedOut not valid"); case VarState::TAG_Partial: { const auto& nse = new_state.as_Partial(); ASSERT_BUG(sp, ose.inner_states.size() == nse.inner_states.size(), "Partial->Partial with mismatched sizes - " << old_state << " <= " << new_state); - ASSERT_BUG(sp, ose.outer_flag == nse.outer_flag, "Partial->Partial with mismatched drop flags - " << old_state << " <= " << new_state); - if( is_box ) { - ASSERT_BUG(sp, nse.inner_states.size() == 1, ""); - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), ose.inner_states[0], nse.inner_states[0]); + if( is_enum ) { + for(size_t i = 0; i < ose.inner_states.size(); i ++) + { + merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast<unsigned int>(i) }), ose.inner_states[i], nse.inner_states[i]); + } } else { for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) @@ -983,11 +1426,11 @@ void MirBuilder::terminate_loop_early(const Span& sp, ScopeType::Data_Loop& sd_l void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool reachable) { - ASSERT_BUG(sp, handle.idx < m_scopes.size(), ""); + ASSERT_BUG(sp, handle.idx < m_scopes.size(), "Handle passed to end_split_arm is invalid"); auto& sd = m_scopes.at( handle.idx ); - ASSERT_BUG(sp, sd.data.is_Split(), ""); + ASSERT_BUG(sp, sd.data.is_Split(), "Ending split arm on non-Split arm - " << sd.data.tag_str()); auto& sd_split = sd.data.as_Split(); - ASSERT_BUG(sp, !sd_split.arms.empty(), ""); + ASSERT_BUG(sp, !sd_split.arms.empty(), "Split arm list is empty (impossible)"); TRACE_FUNCTION_F("end split scope " << handle.idx << " arm " << (sd_split.arms.size()-1)); if( reachable ) @@ -1092,10 +1535,10 @@ void MirBuilder::complete_scope(ScopeDef& sd) TU_MATCHA( (sd.data), (e), (Temporaries, - DEBUG("Temporaries " << e.temporaries); + DEBUG("Temporaries - " << e.temporaries); ), (Variables, - DEBUG("Variables " << e.vars); + DEBUG("Variables - " << e.vars); ), (Loop, DEBUG("Loop"); @@ -1282,37 +1725,62 @@ void MirBuilder::with_val_type(const Span& sp, const ::MIR::LValue& val, ::std:: BUG(sp, "Downcast on unexpected type - " << ty); ), (Path, - //ASSERT_BUG(sp, !te.binding.is_Unbound(), "Unbound path " << ty << " encountered"); - ASSERT_BUG(sp, te.binding.is_Enum(), "Downcast on non-Enum - " << ty << " for " << val); - const auto& enm = *te.binding.as_Enum(); - const auto& variants = enm.m_variants; - ASSERT_BUG(sp, e.variant_index < variants.size(), "Variant index out of range"); - const auto& variant = variants[e.variant_index]; - // TODO: Make data variants refer to associated types (unify enum and struct handling) - TU_MATCHA( (variant.second), (ve), - (Value, - ), - (Unit, - ), - (Tuple, - // HACK! Create tuple. - ::std::vector< ::HIR::TypeRef> tys; - for(const auto& fld : ve) - tys.push_back( monomorphise_type(sp, enm.m_params, te.path.m_data.as_Generic().m_params, fld.ent) ); - ::HIR::TypeRef tup( mv$(tys) ); - m_resolve.expand_associated_types(sp, tup); - cb(tup); - ), - (Struct, - // HACK! Create tuple. - ::std::vector< ::HIR::TypeRef> tys; - for(const auto& fld : ve) - tys.push_back( monomorphise_type(sp, enm.m_params, te.path.m_data.as_Generic().m_params, fld.second.ent) ); - ::HIR::TypeRef tup( mv$(tys) ); - m_resolve.expand_associated_types(sp, tup); - cb(tup); + // TODO: Union? + if( const auto* pbe = te.binding.opt_Enum() ) + { + const auto& enm = **pbe; + const auto& variants = enm.m_variants; + ASSERT_BUG(sp, e.variant_index < variants.size(), "Variant index out of range"); + const auto& variant = variants[e.variant_index]; + // TODO: Make data variants refer to associated types (unify enum and struct handling) + TU_MATCHA( (variant.second), (ve), + (Value, + DEBUG(""); + cb(::HIR::TypeRef::new_unit()); + ), + (Unit, + cb(::HIR::TypeRef::new_unit()); + ), + (Tuple, + // HACK! Create tuple. + ::std::vector< ::HIR::TypeRef> tys; + for(const auto& fld : ve) + tys.push_back( monomorphise_type(sp, enm.m_params, te.path.m_data.as_Generic().m_params, fld.ent) ); + ::HIR::TypeRef tup( mv$(tys) ); + m_resolve.expand_associated_types(sp, tup); + cb(tup); + ), + (Struct, + // HACK! Create tuple. + ::std::vector< ::HIR::TypeRef> tys; + for(const auto& fld : ve) + tys.push_back( monomorphise_type(sp, enm.m_params, te.path.m_data.as_Generic().m_params, fld.second.ent) ); + ::HIR::TypeRef tup( mv$(tys) ); + m_resolve.expand_associated_types(sp, tup); + cb(tup); + ) ) - ) + } + else if( const auto* pbe = te.binding.opt_Union() ) + { + const auto& unm = **pbe; + ASSERT_BUG(sp, e.variant_index < unm.m_variants.size(), "Variant index out of range"); + const auto& variant = unm.m_variants.at(e.variant_index); + const auto& fld = variant.second; + + if( monomorphise_type_needed(fld.ent) ) { + auto sty = monomorphise_type(sp, unm.m_params, te.path.m_data.as_Generic().m_params, fld.ent); + m_resolve.expand_associated_types(sp, sty); + cb(sty); + } + else { + cb(fld.ent); + } + } + else + { + BUG(sp, "Downcast on non-Enum/Union - " << ty << " for " << val); + } ) ) }); @@ -1327,185 +1795,380 @@ bool MirBuilder::lvalue_is_copy(const Span& sp, const ::MIR::LValue& val) const DEBUG("[lvalue_is_copy] ty="<<ty); rv = (m_resolve.type_is_copy(sp, ty) ? 2 : 1); }); - assert(rv != 0); + ASSERT_BUG(sp, rv != 0, "Type for " << val << " can't be determined"); return rv == 2; } -const VarState& MirBuilder::get_variable_state(const Span& sp, unsigned int idx, unsigned int skip_count) const +const VarState& MirBuilder::get_slot_state(const Span& sp, VarGroup ty, unsigned int idx, unsigned int skip_count/*=0*/) const { + // 1. Find an applicable Split scope 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, + if( ty == VarGroup::Temporary ) + { + auto it = ::std::find(e.temporaries.begin(), e.temporaries.end(), idx); + if( it != e.temporaries.end() ) { + break ; + } + } + ), (Variables, - auto it = ::std::find(e.vars.begin(), e.vars.end(), idx); - if( it != e.vars.end() ) { - // If controlled by this block, exit early (won't find it elsewhere) - break ; + if( ty == VarGroup::Variable ) + { + auto it = ::std::find(e.vars.begin(), e.vars.end(), idx); + if( it != e.vars.end() ) { + // If controlled by this block, exit early (won't find it elsewhere) + break ; + } } ), (Split, const auto& cur_arm = e.arms.back(); - auto it = cur_arm.var_states.find(idx); - if( it != cur_arm.var_states.end() ) + if( ty == VarGroup::Variable ) { - if( ! skip_count -- ) + auto it = cur_arm.var_states.find(idx); + if( it != cur_arm.var_states.end() ) { - return it->second; + if( ! skip_count -- ) + { + return it->second; + } + } + } + else if( ty == VarGroup::Temporary ) + { + auto it = cur_arm.tmp_states.find(idx); + if( it != cur_arm.tmp_states.end() ) + { + if( ! skip_count -- ) + { + return it->second; + } } } ) ) } - - ASSERT_BUG(sp, idx < m_variable_states.size(), "Variable " << idx << " out of range for state table"); - return m_variable_states[idx]; + switch(ty) + { + case VarGroup::Return: + return m_return_state; + case VarGroup::Argument: + ASSERT_BUG(sp, idx < m_arg_states.size(), "Argument " << idx << " out of range for state table"); + return m_arg_states.at(idx); + case VarGroup::Variable: + ASSERT_BUG(sp, idx < m_variable_states.size(), "Variable " << idx << " out of range for state table"); + return m_variable_states[idx]; + case VarGroup::Temporary: + ASSERT_BUG(sp, idx < m_temporary_states.size(), "Temporary " << idx << " out of range for state table"); + return m_temporary_states[idx]; + } + BUG(sp, "Fell off the end of get_slot_state"); } -VarState& MirBuilder::get_variable_state_mut(const Span& sp, unsigned int idx) +VarState& MirBuilder::get_slot_state_mut(const Span& sp, VarGroup ty, unsigned int idx) { VarState* ret = nullptr; for( auto scope_idx : ::reverse(m_scope_stack) ) { auto& scope_def = m_scopes.at(scope_idx); - if( scope_def.data.is_Variables() ) + if( const auto* e = scope_def.data.opt_Variables() ) { - const auto& e = scope_def.data.as_Variables(); - auto it = ::std::find(e.vars.begin(), e.vars.end(), idx); - if( it != e.vars.end() ) { - break ; + if( ty == VarGroup::Variable ) + { + auto it = ::std::find(e->vars.begin(), e->vars.end(), idx); + if( it != e->vars.end() ) { + break ; + } + } + } + else if( const auto* e = scope_def.data.opt_Temporaries() ) + { + if( ty == VarGroup::Temporary ) + { + auto it = ::std::find(e->temporaries.begin(), e->temporaries.end(), idx); + if( it != e->temporaries.end() ) { + break ; + } } } else if( scope_def.data.is_Split() ) { + auto& e = scope_def.data.as_Split(); + auto& cur_arm = e.arms.back(); if( ! ret ) { - auto& e = scope_def.data.as_Split(); - auto& cur_arm = e.arms.back(); - auto it = cur_arm.var_states.find(idx); - if( it == cur_arm.var_states.end() ) + ::std::map<unsigned int, VarState>* states; + switch(ty) { - DEBUG("Split new (scope " << scope_idx << ")"); - ret = &(cur_arm.var_states[idx] = get_variable_state(sp, idx).clone()); + case VarGroup::Return: states = nullptr; break; + case VarGroup::Argument: BUG(sp, "Mutating state of argument"); break; + case VarGroup::Variable: states = &cur_arm.var_states; break; + case VarGroup::Temporary: states = &cur_arm.tmp_states; break; } - else + + if( states ) { - DEBUG("Split existing (scope " << scope_idx << ")"); - ret = &it->second; + auto it = states->find(idx); + if( it == states->end() ) + { + DEBUG("Split new (scope " << scope_idx << ")"); + ret = &( (*states)[idx] = get_slot_state(sp, ty, idx).clone() ); + } + else + { + DEBUG("Split existing (scope " << scope_idx << ")"); + ret = &it->second; + } } } } else if( scope_def.data.is_Loop() ) { auto& e = scope_def.data.as_Loop(); - if( e.changed_vars.count(idx) == 0 ) + ::std::map<unsigned int, VarState>* states = nullptr; + switch(ty) { - auto state = e.exit_state_valid ? get_variable_state(sp, idx).clone() : VarState::make_Valid({}); - e.changed_vars.insert(::std::make_pair( idx, mv$(state) )); + case VarGroup::Return: states = nullptr; break; + case VarGroup::Argument: BUG(sp, "Mutating state of argument"); break; + case VarGroup::Variable: states = &e.changed_vars; break; + case VarGroup::Temporary: states = &e.changed_tmps; break; + } + + if( states ) + { + if( states->count(idx) == 0 ) + { + auto state = e.exit_state_valid ? get_slot_state(sp, ty, idx).clone() : VarState::make_Valid({}); + states->insert(::std::make_pair( idx, mv$(state) )); + } } } else { } } - - if( !ret ) + if( ret ) { - DEBUG("Outer"); - ASSERT_BUG(sp, idx < m_variable_states.size(), "Variable " << idx << " out of range for state table"); - return m_variable_states[idx]; + return *ret; } else { - return *ret; + switch(ty) + { + case VarGroup::Return: + return m_return_state; + case VarGroup::Argument: + ASSERT_BUG(sp, idx < m_arg_states.size(), "Argument " << idx << " out of range for state table"); + return m_arg_states.at(idx); + case VarGroup::Variable: + ASSERT_BUG(sp, idx < m_variable_states.size(), "Variable " << idx << " out of range for state table"); + return m_variable_states[idx]; + case VarGroup::Temporary: + ASSERT_BUG(sp, idx < m_temporary_states.size(), "Temporary " << idx << " out of range for state table"); + return m_temporary_states[idx]; + } + BUG(sp, "Fell off the end of get_slot_state_mut"); } } +const VarState& MirBuilder::get_variable_state(const Span& sp, unsigned int idx, unsigned int skip_count) const +{ + return get_slot_state(sp, VarGroup::Variable, idx, skip_count); +} +VarState& MirBuilder::get_variable_state_mut(const Span& sp, unsigned int idx) +{ + return get_slot_state_mut(sp, VarGroup::Variable, idx); +} const VarState& MirBuilder::get_temp_state(const Span& sp, unsigned int idx, unsigned int skip_count) const { - for( auto scope_idx : ::reverse(m_scope_stack) ) - { - const auto& scope_def = m_scopes.at(scope_idx); - if( scope_def.data.is_Temporaries() ) + return get_slot_state(sp, VarGroup::Temporary, idx, skip_count); +} +VarState& MirBuilder::get_temp_state_mut(const Span& sp, unsigned int idx) +{ + return get_slot_state_mut(sp, VarGroup::Temporary, idx); +} + +const VarState& MirBuilder::get_val_state(const Span& sp, const ::MIR::LValue& lv, unsigned int skip_count) +{ + TODO(sp, ""); +} +VarState& MirBuilder::get_val_state_mut(const Span& sp, const ::MIR::LValue& lv) +{ + TRACE_FUNCTION_F(lv); + TU_MATCHA( (lv), (e), + (Variable, + return get_slot_state_mut(sp, VarGroup::Variable, e); + ), + (Temporary, + return get_slot_state_mut(sp, VarGroup::Temporary, e.idx); + ), + (Argument, + return get_slot_state_mut(sp, VarGroup::Argument, e.idx); + ), + (Static, + BUG(sp, "Attempting to mutate state of a static"); + ), + (Return, + BUG(sp, "Move of return value"); + return get_slot_state_mut(sp, VarGroup::Return, 0); + ), + (Field, + auto& ivs = get_val_state_mut(sp, *e.val); + VarState tpl; + TU_MATCHA( (ivs), (ivse), + (Invalid, + //BUG(sp, "Mutating inner state of an invalidated composite - " << lv); + tpl = VarState::make_Valid({}); + ), + (MovedOut, + BUG(sp, "Field on value with MovedOut state - " << lv); + ), + (Partial, + ), + (Optional, + tpl = ivs.clone(); + ), + (Valid, + tpl = VarState::make_Valid({}); + ) + ) + if( !ivs.is_Partial() ) { - const auto& e = scope_def.data.as_Temporaries(); - auto it = ::std::find(e.temporaries.begin(), e.temporaries.end(), idx); - if( it != e.temporaries.end() ) { - break ; - } + size_t n_flds = 0; + with_val_type(sp, *e.val, [&](const auto& ty) { + DEBUG("ty = " << ty); + if(const auto* e = ty.m_data.opt_Path()) { + ASSERT_BUG(sp, e->binding.is_Struct(), ""); + const auto& str = *e->binding.as_Struct(); + TU_MATCHA( (str.m_data), (se), + (Unit, + BUG(sp, "Field access of unit-like struct"); + ), + (Tuple, + n_flds = se.size(); + ), + (Named, + n_flds = se.size(); + ) + ) + } + else if(const auto* e = ty.m_data.opt_Tuple()) { + n_flds = e->size(); + } + else if(const auto* e = ty.m_data.opt_Array()) { + n_flds = e->size_val; + } + else { + TODO(sp, "Determine field count for " << ty); + } + }); + ::std::vector<VarState> inner_vs; inner_vs.reserve(n_flds); + for(size_t i = 0; i < n_flds; i++) + inner_vs.push_back( tpl.clone() ); + ivs = VarState::make_Partial({ mv$(inner_vs) }); } - else if( scope_def.data.is_Split() ) + return ivs.as_Partial().inner_states.at(e.field_index); + ), + (Deref, + // HACK: If the dereferenced type is a Box ("owned_box") then hack in move and shallow drop + bool is_box = false; + if( this->m_lang_Box ) { - const auto& e = scope_def.data.as_Split(); - const auto& cur_arm = e.arms.back(); - auto it = cur_arm.tmp_states.find(idx); - if( it != cur_arm.tmp_states.end() ) - { - if( ! skip_count -- ) - { - return it->second; - } - } + with_val_type(sp, *e.val, [&](const auto& ty){ + DEBUG("ty = " << ty); + is_box = this->is_type_owned_box(ty); + }); } - } - ASSERT_BUG(sp, idx < m_temporary_states.size(), "Temporary " << idx << " out of range for state table"); - return m_temporary_states[idx]; -} -VarState& MirBuilder::get_temp_state_mut(const Span& sp, unsigned int idx) -{ - VarState* ret = nullptr; - for( auto scope_idx : ::reverse(m_scope_stack) ) - { - auto& scope_def = m_scopes.at(scope_idx); - if( scope_def.data.is_Temporaries() ) + + if( is_box ) { - const auto& e = scope_def.data.as_Temporaries(); - auto it = ::std::find(e.temporaries.begin(), e.temporaries.end(), idx); - if( it != e.temporaries.end() ) { - break ; + ::MIR::LValue inner_lv; + // 1. If the inner lvalue isn't a slot with move information, move out of the lvalue into a temporary (with standard temp scope) + TU_MATCH_DEF( ::MIR::LValue, (*e.val), (ei), + ( + with_val_type(sp, *e.val, [&](const auto& ty){ inner_lv = this->new_temporary(ty); }); + this->push_stmt_assign(sp, inner_lv.clone(), ::MIR::RValue( mv$(*e.val) )); + *e.val = inner_lv.clone(); + ), + (Variable, + inner_lv = ::MIR::LValue(ei); + ), + (Temporary, + inner_lv = ::MIR::LValue(ei); + ), + (Argument, + inner_lv = ::MIR::LValue(ei); + ) + ) + // 2. Mark the slot as requiring only a shallow drop + ::std::vector<VarState> inner; + inner.push_back(VarState::make_Valid({})); + auto& ivs = get_val_state_mut(sp, inner_lv); + if( ! ivs.is_MovedOut() ) + { + unsigned int drop_flag = (ivs.is_Optional() ? ivs.as_Optional() : ~0u); + ivs = VarState::make_MovedOut({ box$(VarState::make_Valid({})), drop_flag }); } + return *ivs.as_MovedOut().inner_state; } - else if( scope_def.data.is_Split() ) + else { - if( ! ret ) - { - auto& e = scope_def.data.as_Split(); - auto& cur_arm = e.arms.back(); - auto it = cur_arm.tmp_states.find(idx); - if(it == cur_arm.tmp_states.end()) + BUG(sp, "Move out of deref with non-Copy values - &move? - " << lv << " : " << FMT_CB(ss, this->with_val_type(sp, lv, [&](const auto& ty){ss<<ty;});) ); + } + ), + (Index, + BUG(sp, "Move out of index with non-Copy values - Partial move?"); + ), + (Downcast, + // TODO: What if the inner is Copy? What if the inner is a hidden pointer? + auto& ivs = get_val_state_mut(sp, *e.val); + //static VarState ivs; ivs = VarState::make_Valid({}); + + if( !ivs.is_Partial() ) + { + ASSERT_BUG(sp, !ivs.is_MovedOut(), "Downcast of a MovedOut value"); + + size_t var_count = 0; + with_val_type(sp, *e.val, [&](const auto& ty){ + DEBUG("ty = " << ty); + ASSERT_BUG(sp, ty.m_data.is_Path(), "Downcast on non-Path type - " << ty); + const auto& pb = ty.m_data.as_Path().binding; + // TODO: What about unions? + // - Iirc, you can't move out of them so they will never have state mutated + if( pb.is_Enum() ) { - ret = &(cur_arm.tmp_states[idx] = get_temp_state(sp, idx).clone()); + const auto& enm = *pb.as_Enum(); + var_count = enm.m_variants.size(); + } + else if( const auto* pbe = pb.opt_Union() ) + { + const auto& unm = **pbe; + var_count = unm.m_variants.size(); } else { - ret = &it->second; + BUG(sp, "Downcast on non-Enum/Union - " << ty); } - } - } - else if( scope_def.data.is_Loop() ) - { - auto& e = scope_def.data.as_Loop(); - if( e.changed_tmps.count(idx) == 0 ) + }); + + ::std::vector<VarState> inner; + for(size_t i = 0; i < var_count; i ++) { - auto state = e.exit_state_valid ? get_temp_state(sp, idx).clone() : VarState::make_Valid({}); - e.changed_tmps.insert(::std::make_pair( idx, mv$(state) )); + inner.push_back( VarState::make_Invalid(InvalidType::Uninit) ); } + inner[e.variant_index] = mv$(ivs); + ivs = VarState::make_Partial({ mv$(inner) }); } - else - { - } - } - if( !ret ) - { - ASSERT_BUG(sp, idx < m_temporary_states.size(), "Temporary " << idx << " out of range for state table"); - return m_temporary_states[idx]; - } - else - { - return *ret; - } + return ivs.as_Partial().inner_states.at(e.variant_index); + ) + ) + BUG(sp, "Fell off send of get_val_state_mut"); } void MirBuilder::drop_value_from_state(const Span& sp, const VarState& vs, ::MIR::LValue lv) @@ -1516,12 +2179,41 @@ void MirBuilder::drop_value_from_state(const Span& sp, const VarState& vs, ::MIR (Valid, push_stmt_drop(sp, mv$(lv)); ), + (MovedOut, + bool is_box = false; + with_val_type(sp, lv, [&](const auto& ty){ + is_box = this->is_type_owned_box(ty); + }); + if( is_box ) + { + drop_value_from_state(sp, *vse.inner_state, ::MIR::LValue::make_Deref({ box$(lv.clone()) })); + push_stmt_drop_shallow(sp, mv$(lv), vse.outer_flag); + } + else + { + TODO(sp, ""); + } + ), (Partial, - // TODO: Actual destructuring based on the type - with_val_type(sp, lv, [&](const auto& ty){ ASSERT_BUG(sp, this->is_type_owned_box(ty), "TODO: Partial on non-Box"); }); - assert( vse.inner_states.size() == 1 ); - drop_value_from_state(sp, vse.inner_states[0], ::MIR::LValue::make_Deref({ box$(lv.clone()) })); - push_stmt_drop_shallow(sp, mv$(lv), vse.outer_flag); + bool is_enum = false; + with_val_type(sp, lv, [&](const auto& ty){ + is_enum = ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum(); + }); + if(is_enum) + { + DEBUG("TODO: Switch based on enum value"); + //for(size_t i = 0; i < vse.inner_states.size(); i ++) + //{ + // drop_value_from_state(sp, vse.inner_states[i], ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast<unsigned int>(i) })); + //} + } + else + { + for(size_t i = 0; i < vse.inner_states.size(); i ++) + { + drop_value_from_state(sp, vse.inner_states[i], ::MIR::LValue::make_Field({ box$(lv.clone()), static_cast<unsigned int>(i) })); + } + } ), (Optional, push_stmt_drop(sp, mv$(lv), vse); @@ -1536,6 +2228,7 @@ void MirBuilder::drop_scope_values(const ScopeDef& sd) for(auto tmp_idx : ::reverse(e.temporaries)) { const auto& vs = get_temp_state(sd.span, tmp_idx); + DEBUG("tmp" << tmp_idx << " - " << vs); drop_value_from_state( sd.span, vs, ::MIR::LValue::make_Temporary({ tmp_idx }) ); } ), @@ -1543,6 +2236,7 @@ void MirBuilder::drop_scope_values(const ScopeDef& sd) for(auto var_idx : ::reverse(e.vars)) { const auto& vs = get_variable_state(sd.span, var_idx); + DEBUG("var" << var_idx << " - " << vs); drop_value_from_state( sd.span, vs, ::MIR::LValue::make_Variable(var_idx) ); } ), @@ -1555,109 +2249,13 @@ void MirBuilder::drop_scope_values(const ScopeDef& sd) ) } + void MirBuilder::moved_lvalue(const Span& sp, const ::MIR::LValue& lv) { - TRACE_FUNCTION_F(lv); - TU_MATCHA( (lv), (e), - (Variable, - if( !lvalue_is_copy(sp, lv) ) { - get_variable_state_mut(sp, e) = VarState::make_Invalid(InvalidType::Moved); - } - ), - (Temporary, - if( !lvalue_is_copy(sp, lv) ) { - get_temp_state_mut(sp, e.idx) = VarState::make_Invalid(InvalidType::Moved); - } - ), - (Argument, - //TODO(sp, "Mark argument as moved"); - ), - (Static, - //TODO(sp, "Static - Assert that type is Copy"); - ), - (Return, - BUG(sp, "Read of return value"); - ), - (Field, - if( lvalue_is_copy(sp, lv) ) { - } - else { - // TODO: Partial moves. - moved_lvalue(sp, *e.val); - } - ), - (Deref, - if( lvalue_is_copy(sp, lv) ) { - } - else { - // HACK: If the dereferenced type is a Box ("owned_box") then hack in move and shallow drop - if( this->m_lang_Box ) - { - bool is_box = false; - with_val_type(sp, *e.val, [&](const auto& ty){ - DEBUG("ty = " << ty); - is_box = this->is_type_owned_box(ty); - }); - if( is_box ) - { - ::MIR::LValue inner_lv; - // 1. If the inner lvalue isn't a slot with move information, move out of the lvalue into a temporary (with standard temp scope) - TU_MATCH_DEF( ::MIR::LValue, (*e.val), (ei), - ( - with_val_type(sp, *e.val, [&](const auto& ty){ inner_lv = this->new_temporary(ty); }); - this->push_stmt_assign(sp, inner_lv.clone(), ::MIR::RValue( mv$(*e.val) )); - *e.val = inner_lv.clone(); - ), - (Variable, - inner_lv = ::MIR::LValue(ei); - ), - (Temporary, - inner_lv = ::MIR::LValue(ei); - ), - (Argument, - inner_lv = ::MIR::LValue(ei); - ) - ) - // 2. Mark the slot as requiring only a shallow drop - // - TODO: Have a drop flag attached to the - ::std::vector<VarState> ivs; - ivs.push_back(VarState::make_Invalid(InvalidType::Moved)); - TU_MATCH_DEF( ::MIR::LValue, (inner_lv), (ei), - ( - BUG(sp, "Box move out of invalid LValue " << inner_lv << " - should have been moved"); - ), - (Variable, - get_variable_state_mut(sp, ei) = VarState::make_Partial({ mv$(ivs), ~0u }); - ), - (Temporary, - get_temp_state_mut(sp, ei.idx) = VarState::make_Partial({ mv$(ivs), ~0u }); - ), - (Argument, - TODO(sp, "Mark arg " << ei.idx << " for shallow drop"); - ) - ) - // Early return! - return ; - } - } - BUG(sp, "Move out of deref with non-Copy values - &move? - " << lv << " : " << FMT_CB(ss, this->with_val_type(sp, lv, [&](const auto& ty){ss<<ty;});) ); - moved_lvalue(sp, *e.val); - } - ), - (Index, - if( lvalue_is_copy(sp, lv) ) { - } - else { - BUG(sp, "Move out of index with non-Copy values - Partial move?"); - moved_lvalue(sp, *e.val); - } - moved_lvalue(sp, *e.idx); - ), - (Downcast, - // TODO: What if the inner is Copy? What if the inner is a hidden pointer? - moved_lvalue(sp, *e.val); - ) - ) + if( !lvalue_is_copy(sp, lv) ) { + auto& vs = get_val_state_mut(sp, lv); + vs = VarState::make_Invalid(InvalidType::Moved); + } } const ::MIR::LValue& MirBuilder::get_ptr_to_dst(const Span& sp, const ::MIR::LValue& lv) const @@ -1702,12 +2300,15 @@ VarState VarState::clone() const (Optional, return VarState(e); ), + (MovedOut, + return VarState::make_MovedOut({ box$(e.inner_state->clone()), e.outer_flag }); + ), (Partial, ::std::vector<VarState> n; n.reserve(e.inner_states.size()); for(const auto& a : e.inner_states) n.push_back( a.clone() ); - return VarState::make_Partial({ mv$(n), e.outer_flag }); + return VarState::make_Partial({ mv$(n) }); ) ) throw ""; @@ -1726,9 +2327,12 @@ bool VarState::operator==(VarState& x) const (Optional, return te == xe; ), - (Partial, + (MovedOut, if( te.outer_flag != xe.outer_flag ) return false; + return *te.inner_state == *xe.inner_state; + ), + (Partial, if( te.inner_states.size() != xe.inner_states.size() ) return false; for(unsigned int i = 0; i < te.inner_states.size(); i ++) @@ -1758,12 +2362,16 @@ bool VarState::operator==(VarState& x) const (Optional, os << "Optional(" << e << ")"; ), - (Partial, - os << "Partial("; + (MovedOut, + os << "MovedOut("; if( e.outer_flag == ~0u ) os << "-"; else os << "df" << e.outer_flag; + os << " " << *e.inner_state <<")"; + ), + (Partial, + os << "Partial("; os << ", [" << e.inner_states << "])"; ) ) |