https://gcc.gnu.org/g:85f924f470682b5c71b8bf501e6a143701303a93
commit 85f924f470682b5c71b8bf501e6a143701303a93 Author: Jakub Dupak <d...@jakubdupak.com> Date: Wed Oct 18 22:54:37 2023 +0200 borrowck: BIR: handle break gcc/rust/ChangeLog: * checks/errors/borrowck/rust-bir-builder-expr-stmt.cc (ExprStmtBuilder::visit): Push ctx. (ExprStmtBuilder::setup_loop): Common loop infractructure setup. * checks/errors/borrowck/rust-bir-builder-expr-stmt.h: Loop ctx. * checks/errors/borrowck/rust-bir-builder-internal.h (struct BuilderContext): Loop ctx. Signed-off-by: Jakub Dupak <d...@jakubdupak.com> Diff: --- .../errors/borrowck/rust-bir-builder-expr-stmt.cc | 168 +++++++++++++++------ .../errors/borrowck/rust-bir-builder-expr-stmt.h | 2 + .../errors/borrowck/rust-bir-builder-internal.h | 66 +++++--- 3 files changed, 168 insertions(+), 68 deletions(-) diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc index 1487c853b49e..67b98e2254f9 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc @@ -258,11 +258,45 @@ ExprStmtBuilder::visit (HIR::FieldAccessExpr &expr) void ExprStmtBuilder::visit (HIR::BlockExpr &block) { + BasicBlockId end_bb; + + if (block.has_label ()) + { + end_bb = new_bb (); + NodeId label + = block.get_label ().get_lifetime ().get_mappings ().get_nodeid (); + PlaceId label_var = ctx.place_db.add_temporary (lookup_type (block)); + ctx.loop_and_label_stack.push_back ({false, label, label_var, end_bb, 0}); + } + + bool unreachable = false; for (auto &stmt : block.get_statements ()) { + if (unreachable) + break; stmt->accept_vis (*this); + if (ctx.get_current_bb ().is_terminated ()) + unreachable = true; + } + + if (block.has_label ()) + { + auto label_info = ctx.loop_and_label_stack.back (); + if (block.has_expr () && !unreachable) + { + push_assignment (label_info.label_var, + visit_expr (*block.get_final_expr ())); + } + if (!ctx.get_current_bb ().is_terminated ()) + { + add_jump_to (end_bb); + } + ctx.current_bb = end_bb; + ctx.loop_and_label_stack.pop_back (); + + return_place (label_info.label_var); } - if (block.has_expr ()) + else if (block.has_expr () && !unreachable) { return_place (visit_expr (*block.get_final_expr ())); } @@ -290,25 +324,36 @@ ExprStmtBuilder::visit (HIR::ContinueExpr &cont) void ExprStmtBuilder::visit (HIR::BreakExpr &brk) { - // BuilderContext::LabelledBlockCtx block_ctx{}; - // NodeId label = UNKNOWN_NODEID; - // if (brk.has_label ()) - // { - // if (!resolve_label (brk.get_label (), label)) - // return; - // } - // if (!find_block_ctx (label, block_ctx)) - // { - // rust_error_at (brk.get_locus (), "unresolved labelled block"); - // } - // - // if (brk.has_break_expr ()) - // { - // brk.get_expr ()->accept_vis (*this); - // push_assignment (block_ctx.label_var, new Operator<1> ({translated})); - // } - // - // add_jump_to (block_ctx.break_bb); + BuilderContext::LoopAndLabelInfo info; + if (brk.has_label ()) + { + NodeId label = resolve_label (brk.get_label ()); + auto lookup + = std::find_if (ctx.loop_and_label_stack.rbegin (), + ctx.loop_and_label_stack.rend (), + [label] (const BuilderContext::LoopAndLabelInfo &info) { + return info.label == label; + }); + rust_assert (lookup != ctx.loop_and_label_stack.rend ()); + info = *lookup; + } + else + { + auto lookup + = std::find_if (ctx.loop_and_label_stack.rbegin (), + ctx.loop_and_label_stack.rend (), + [] (const BuilderContext::LoopAndLabelInfo &info) { + return info.is_loop; + }); + rust_assert (lookup != ctx.loop_and_label_stack.rend ()); + info = *lookup; + } + if (brk.has_break_expr ()) + { + push_assignment (info.label_var, visit_expr (*brk.get_expr ())); + } + add_jump_to (info.break_bb); + // No code allowed after break. No BB starts - would be empty. } void @@ -370,60 +415,87 @@ ExprStmtBuilder::visit (HIR::UnsafeBlockExpr &expr) rust_sorry_at (expr.get_locus (), "unsafe blocks are not supported"); } +BuilderContext::LoopAndLabelInfo & +ExprStmtBuilder::setup_loop (HIR::BaseLoopExpr &expr) +{ + NodeId label = (expr.has_loop_label ()) + ? resolve_label (expr.get_loop_label ()) + : UNKNOWN_NODEID; + PlaceId label_var = ctx.place_db.add_temporary (lookup_type (expr)); + + BasicBlockId continue_bb = new_bb (); + BasicBlockId break_bb = new_bb (); + ctx.loop_and_label_stack.push_back ( + {true, label, label_var, break_bb, continue_bb}); + return ctx.loop_and_label_stack.back (); +} + void ExprStmtBuilder::visit (HIR::LoopExpr &expr) { - // PlaceId label_var = ctx.place_db.add_temporary (nullptr); - // NodeId label; - // if (!resolve_label (expr.get_loop_label (), label)) - // return; - // ctx.label_place_map.emplace (label, label_var); - // - // expr.get_loop_block ()->accept_vis (*this); - // - // translated = label_var; + auto loop = setup_loop (expr); + + add_jump_to (loop.continue_bb); + + ctx.current_bb = loop.continue_bb; + (void) visit_expr (*expr.get_loop_block ()); + add_jump_to (loop.continue_bb); + + ctx.current_bb = loop.break_bb; } + void ExprStmtBuilder::visit (HIR::WhileLoopExpr &expr) { - // // TODO: Desugar in AST->HIR ??? - // PlaceId label_var = ctx.place_db.add_temporary (nullptr); - // NodeId label; - // if (!resolve_label (expr.get_loop_label (), label)) - // return; - // ctx.label_place_map.emplace (label, label_var); - // - // expr.get_predicate_expr ()->accept_vis (*this); - // - // expr.get_loop_block ()->accept_vis (*this); - // - // translated = label_var; + auto loop = setup_loop (expr); + + add_jump_to (loop.continue_bb); + + ctx.current_bb = loop.continue_bb; + auto cond_val = visit_expr (*expr.get_predicate_expr ()); + auto body_bb = new_bb (); + push_switch (cond_val, {body_bb, loop.break_bb}); + + ctx.current_bb = body_bb; + (void) visit_expr (*expr.get_loop_block ()); + add_jump_to (loop.continue_bb); + + ctx.current_bb = loop.break_bb; } + void ExprStmtBuilder::visit (HIR::WhileLetLoopExpr &expr) { // TODO: Desugar in AST->HIR + rust_sorry_at (expr.get_locus (), "while let loops are not yet supported"); } + void ExprStmtBuilder::visit (HIR::IfExpr &expr) { // If without else cannot return a non-unit value (see [E0317]). + if (expr.get_if_block ()->statements.empty ()) + return; + push_switch (visit_expr (*expr.get_if_condition ())); BasicBlockId if_block = ctx.current_bb; ctx.current_bb = new_bb (); + BasicBlockId then_start_block = ctx.current_bb; (void) visit_expr (*expr.get_if_block ()); - BasicBlockId then_block = ctx.current_bb; + BasicBlockId then_end_block = ctx.current_bb; ctx.current_bb = new_bb (); BasicBlockId final_block = ctx.current_bb; return_unit (expr); // Jumps are added at the end to match rustc MIR order for easier comparison. - add_jump (if_block, then_block); + add_jump (if_block, then_start_block); add_jump (if_block, final_block); - add_jump (then_block, final_block); + + if (!ctx.basic_blocks[then_end_block].is_terminated ()) + add_jump (then_end_block, final_block); } void @@ -451,8 +523,11 @@ ExprStmtBuilder::visit (HIR::IfExprConseqElse &expr) // Jumps are added at the end to match rustc MIR order for easier comparison. add_jump (if_block, then_block); add_jump (if_block, else_block); - add_jump (then_block, final_block); - add_jump (else_block, final_block); + + if (!ctx.basic_blocks[then_block].is_terminated ()) + add_jump (then_block, final_block); + if (!ctx.basic_blocks[else_block].is_terminated ()) + add_jump (else_block, final_block); } void ExprStmtBuilder::visit (HIR::IfLetExpr &expr) @@ -573,6 +648,5 @@ ExprStmtBuilder::visit (HIR::ExprStmt &stmt) { (void) visit_expr (*stmt.get_expr ()); } - } // namespace BIR } // namespace Rust \ No newline at end of file diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h index f46cba5f9684..e5707c31f8d3 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h @@ -46,6 +46,8 @@ private: return result; } + BuilderContext::LoopAndLabelInfo &setup_loop (HIR::BaseLoopExpr &expr); + protected: // Expr // TODO: test when compiles void visit (HIR::ClosureExpr &expr) override; diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h index 48116d8c351c..ef48cba7b80b 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h @@ -55,12 +55,21 @@ public: struct BuilderContext { - struct LabelledBlockCtx + struct LoopAndLabelInfo { - NodeId label; // UNKNOWN_NODEID if no label - PlaceId label_var; + bool is_loop; // Loop or labelled block + NodeId label; // UNKNOWN_NODEID if no label (loop) + PlaceId label_var; // For break with value. BasicBlockId break_bb; BasicBlockId continue_bb; // Only valid for loops + + LoopAndLabelInfo (bool is_loop = false, NodeId label = UNKNOWN_NODEID, + PlaceId label_var = INVALID_PLACE, + BasicBlockId break_bb = INVALID_BB, + BasicBlockId continue_bb = INVALID_BB) + : is_loop (is_loop), label (label), label_var (label_var), + break_bb (break_bb), continue_bb (continue_bb) + {} }; // Context @@ -83,7 +92,7 @@ struct BuilderContext */ std::unordered_map<NodeId, PlaceId> label_place_map; - std::vector<LabelledBlockCtx> loop_stack; + std::vector<LoopAndLabelInfo> loop_and_label_stack; public: BuilderContext () @@ -115,6 +124,18 @@ public: } rust_unreachable (); }; + + const LoopAndLabelInfo &lookup_label (NodeId label) + { + auto label_match = [label] (const LoopAndLabelInfo &info) { + return info.label != UNKNOWN_NODEID && info.label == label; + }; + + auto found = std::find_if (loop_and_label_stack.rbegin (), + loop_and_label_stack.rend (), label_match); + rust_assert (found != loop_and_label_stack.rend ()); + return *found; + } }; // Common infrastructure for building BIR from HIR. @@ -195,10 +216,13 @@ protected: push_assignment (tmp, rhs); } - void push_switch (PlaceId switch_val) + void push_switch (PlaceId switch_val, + std::initializer_list<BasicBlockId> destinations = {}) { ctx.get_current_bb ().statements.emplace_back (Node::Kind::SWITCH, switch_val); + ctx.get_current_bb ().successors.insert ( + ctx.get_current_bb ().successors.end (), destinations); } void push_storage_dead (PlaceId place) @@ -235,15 +259,14 @@ protected: void add_jump_to (BasicBlockId bb) { add_jump (ctx.current_bb, bb); } protected: - template <typename T> bool resolve_label (T &label, NodeId &resolved_label) + template <typename T> NodeId resolve_label (T &expr) { - if (!ctx.resolver.lookup_resolved_label ( - label.get_mappings ().get_nodeid (), &resolved_label)) - { - rust_error_at (label.get_locus (), "unresolved label"); - return false; - } - return true; + NodeId resolved_label; + bool ok + = ctx.resolver.lookup_resolved_label (expr.get_mappings ().get_nodeid (), + &resolved_label); + rust_assert (ok); + return resolved_label; } template <typename T> @@ -261,21 +284,22 @@ protected: return true; } - bool find_block_ctx (NodeId label, BuilderContext::LabelledBlockCtx &block) + bool find_block_ctx (NodeId label, BuilderContext::LoopAndLabelInfo &block) { - if (ctx.loop_stack.empty ()) + if (ctx.loop_and_label_stack.empty ()) return false; if (label == UNKNOWN_NODEID) { - block = ctx.loop_stack.back (); + block = ctx.loop_and_label_stack.back (); return true; } auto found - = std::find_if (ctx.loop_stack.rbegin (), ctx.loop_stack.rend (), - [&label] (const BuilderContext::LabelledBlockCtx &block) { + = std::find_if (ctx.loop_and_label_stack.rbegin (), + ctx.loop_and_label_stack.rend (), + [&label] (const BuilderContext::LoopAndLabelInfo &block) { return block.label == label; }); - if (found == ctx.loop_stack.rend ()) + if (found == ctx.loop_and_label_stack.rend ()) return false; block = *found; return true; @@ -318,8 +342,8 @@ protected: } } - /** Dereferences the `translated` place until it is at most one reference and - * return the base type. */ + /** Dereferences the `translated` place until it is at most one reference + * and return the base type. */ TyTy::BaseType *autoderef (PlaceId &place) { auto ty = ctx.place_db[place].tyty;