summaryrefslogtreecommitdiff
path: root/src/mir/mir_builder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mir/mir_builder.cpp')
-rw-r--r--src/mir/mir_builder.cpp1408
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 << "])";
)
)