diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/hir/expr.hpp | 1 | ||||
-rw-r--r-- | src/hir_typeck/expr_cs.cpp | 27 | ||||
-rw-r--r-- | src/mir/from_hir.cpp | 36 | ||||
-rw-r--r-- | src/mir/from_hir.hpp | 2 | ||||
-rw-r--r-- | src/mir/mir_builder.cpp | 11 |
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 ) { |