summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/hir/expr.hpp1
-rw-r--r--src/hir_typeck/expr_cs.cpp27
-rw-r--r--src/mir/from_hir.cpp36
-rw-r--r--src/mir/from_hir.hpp2
-rw-r--r--src/mir/mir_builder.cpp11
5 files changed, 58 insertions, 19 deletions
diff --git a/src/hir/expr.hpp b/src/hir/expr.hpp
index eabfa695..82cdca2d 100644
--- a/src/hir/expr.hpp
+++ b/src/hir/expr.hpp
@@ -105,6 +105,7 @@ struct ExprNode_Loop:
{
::std::string m_label;
::HIR::ExprNodeP m_code;
+ bool m_diverges = false;
ExprNode_Loop(Span sp, ::std::string label, ::HIR::ExprNodeP code):
//ExprNode(mv$(sp), ::HIR::TypeRef::new_unit()),
diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp
index 328ca61f..dd31dbe0 100644
--- a/src/hir_typeck/expr_cs.cpp
+++ b/src/hir_typeck/expr_cs.cpp
@@ -482,7 +482,7 @@ namespace {
::std::vector<bool> inner_coerce_enabled_stack;
- ::std::vector<const ::HIR::ExprNode_Loop*> loop_blocks; // Used for `break` type markings
+ ::std::vector< ::HIR::ExprNode_Loop*> loop_blocks; // Used for `break` type markings
// TEMP: List of in-scope traits for buildup
::HIR::t_trait_list m_traits;
@@ -579,12 +579,11 @@ namespace {
void visit(::HIR::ExprNode_Loop& node) override
{
auto _ = this->push_inner_coerce_scoped(false);
- TRACE_FUNCTION_F(&node << " loop { ... }");
+ TRACE_FUNCTION_F(&node << " loop ('" << node.m_label << ") { ... }");
// Push this node to a stack so `break` statements can update the yeilded value
this->loop_blocks.push_back( &node );
+ node.m_diverges = true; // Set to `false` if a break is hit
- // TODO: This only yields unit if it terminates - otherwise it's !
- // - There's an RFC proposal (that's on track to be accepted) that allows `break value;`
// NOTE: This doesn't set the ivar to !, but marks it as a ! ivar (similar to the int/float markers)
this->context.equate_types(node.span(), node.m_res_type, ::HIR::TypeRef::new_diverge());
@@ -593,6 +592,10 @@ namespace {
node.m_code->visit( *this );
this->loop_blocks.pop_back( );
+
+ if( node.m_diverges ) {
+ DEBUG("Loop diverged");
+ }
}
void visit(::HIR::ExprNode_LoopControl& node) override
{
@@ -603,20 +606,28 @@ namespace {
if( this->loop_blocks.empty() ) {
ERROR(node.span(), E0000, "Break statement with no acive loop");
}
+
+ // NOTE: There's an RFC proposal (that's on track to be accepted) that allows `break value;`
+ auto break_type = ::HIR::TypeRef::new_unit();
+
+ ::HIR::ExprNode_Loop* loop_node_ptr;
if( node.m_label != "" )
{
auto it = ::std::find_if(this->loop_blocks.rbegin(), this->loop_blocks.rend(), [&](const auto& np){ return np->m_label == node.m_label; });
if( it == this->loop_blocks.rend() ) {
ERROR(node.span(), E0000, "Could not find loop '" << node.m_label << " for break");
}
- const auto& loop_node = **it;
- this->context.equate_types(node.span(), loop_node.m_res_type, ::HIR::TypeRef::new_unit());
+ loop_node_ptr = &**it;
}
else
{
- const auto& loop_node = *this->loop_blocks.back();
- this->context.equate_types(node.span(), loop_node.m_res_type, ::HIR::TypeRef::new_unit());
+ loop_node_ptr = this->loop_blocks.back();
}
+
+ DEBUG("Break out of loop " << loop_node_ptr);
+ auto& loop_node = *loop_node_ptr;
+ loop_node.m_diverges = false;
+ this->context.equate_types(node.span(), loop_node.m_res_type, break_type);
}
}
diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp
index 68aa0ea2..29d8d42c 100644
--- a/src/mir/from_hir.cpp
+++ b/src/mir/from_hir.cpp
@@ -325,6 +325,8 @@ namespace {
auto stmt_scope = m_builder.new_scope_temp(sp);
this->visit_node_ptr(subnode);
if( m_builder.has_result() || m_builder.block_active() ) {
+ ASSERT_BUG(sp, m_builder.block_active(), "Result yielded, but no active block");
+ ASSERT_BUG(sp, m_builder.has_result(), "Active block but no result yeilded");
// PROBLEM:
res = m_builder.get_result(sp);
m_builder.terminate_scope(sp, mv$(stmt_scope));
@@ -377,7 +379,7 @@ namespace {
}
void visit(::HIR::ExprNode_Loop& node) override
{
- TRACE_FUNCTION_F("_Loop");
+ TRACE_FUNCTION_FR("_Loop", "_Loop");
auto loop_body_scope = m_builder.new_scope_loop(node.span());
auto loop_block = m_builder.new_bb_linked();
auto loop_next = m_builder.new_bb_unlinked();
@@ -390,22 +392,44 @@ namespace {
// If there's a stray result, drop it
if( m_builder.has_result() ) {
assert( m_builder.block_active() );
+ // TODO: Properly drop this? Or just discard it?
+ m_builder.get_result(node.span());
}
// Terminate block with a jump back to the start
// - Also inserts the jump if this didn't uncondtionally diverge
if( m_builder.block_active() )
{
- // TODO: Insert drop of all scopes within the current scope
+ DEBUG("- Reached end, loop back");
+ // Insert drop of all scopes within the current scope
m_builder.terminate_scope( node.span(), mv$(loop_scope) );
m_builder.end_block( ::MIR::Terminator::make_Goto(loop_block) );
+ }
+ else
+ {
+ // Terminate scope without emitting cleanup (cleanup was handled by `break`)
+ m_builder.terminate_scope( node.span(), mv$(loop_scope), false );
+ }
+
+ if( ! node.m_diverges )
+ {
+ DEBUG("- Doesn't diverge");
+ m_builder.set_cur_block(loop_next);
+ m_builder.set_result(node.span(), ::MIR::RValue::make_Tuple({{}}));
+ }
+ else
+ {
+ DEBUG("- Diverges");
+ assert( !m_builder.has_result() );
- // -
m_builder.set_cur_block(loop_next);
+ m_builder.end_split_arm_early(node.span());
+ assert( !m_builder.has_result() );
+ m_builder.end_block( ::MIR::Terminator::make_Diverge({}) );
}
}
void visit(::HIR::ExprNode_LoopControl& node) override
{
- TRACE_FUNCTION_F("_LoopControl");
+ TRACE_FUNCTION_F("_LoopControl \"" << node.m_label << "\"");
if( m_loop_stack.size() == 0 ) {
BUG(node.span(), "Loop control outside of a loop");
}
@@ -431,7 +455,7 @@ namespace {
void visit(::HIR::ExprNode_Match& node) override
{
- TRACE_FUNCTION_F("_Match");
+ TRACE_FUNCTION_FR("_Match", "_Match");
this->visit_node_ptr(node.m_value);
auto match_val = m_builder.get_result_in_lvalue(node.m_value->span(), node.m_value->m_res_type);
@@ -462,7 +486,7 @@ namespace {
void visit(::HIR::ExprNode_If& node) override
{
- TRACE_FUNCTION_F("_If");
+ TRACE_FUNCTION_FR("_If", "_If");
this->visit_node_ptr(node.m_cond);
auto decision_val = m_builder.get_result_in_lvalue(node.m_cond->span(), node.m_cond->m_res_type);
diff --git a/src/mir/from_hir.hpp b/src/mir/from_hir.hpp
index 7e48971f..66877913 100644
--- a/src/mir/from_hir.hpp
+++ b/src/mir/from_hir.hpp
@@ -164,7 +164,7 @@ public:
ScopeHandle new_scope_temp(const Span& sp);
ScopeHandle new_scope_split(const Span& sp);
ScopeHandle new_scope_loop(const Span& sp);
- void terminate_scope(const Span& sp, ScopeHandle );
+ void terminate_scope(const Span& sp, ScopeHandle , bool cleanup=true);
void terminate_scope_early(const Span& sp, const ScopeHandle& );
void end_split_arm(const Span& sp, const ScopeHandle& , bool reachable);
void end_split_arm_early(const Span& sp);
diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp
index 386bd338..93abe229 100644
--- a/src/mir/mir_builder.cpp
+++ b/src/mir/mir_builder.cpp
@@ -403,7 +403,7 @@ ScopeHandle MirBuilder::new_scope_loop(const Span& sp)
DEBUG("START (loop) scope " << idx);
return ScopeHandle { *this, idx };
}
-void MirBuilder::terminate_scope(const Span& sp, ScopeHandle scope)
+void MirBuilder::terminate_scope(const Span& sp, ScopeHandle scope, bool emit_cleanup/*=true*/)
{
DEBUG("DONE scope " << scope.idx);
// 1. Check that this is the current scope (at the top of the stack)
@@ -419,8 +419,11 @@ void MirBuilder::terminate_scope(const Span& sp, ScopeHandle scope)
auto& scope_def = m_scopes.at(scope.idx);
ASSERT_BUG( sp, scope_def.complete == false, "Terminating scope which is already terminated" );
- // 2. Emit drops for all non-moved variables (share with below)
- drop_scope_values(scope_def);
+ if( emit_cleanup )
+ {
+ // 2. Emit drops for all non-moved variables (share with below)
+ drop_scope_values(scope_def);
+ }
// 3. Pop scope (last because `drop_scope_values` uses the stack)
m_scope_stack.pop_back();
@@ -445,7 +448,7 @@ void MirBuilder::terminate_scope_early(const Span& sp, const ScopeHandle& scope)
auto& scope_def = m_scopes.at( idx );
// If a conditional block is hit, prevent full termination of the rest
- if( scope_def.data.is_Split() )
+ if( scope_def.data.is_Split() || scope_def.data.is_Loop() )
is_conditional = true;
if( !is_conditional ) {