https://gcc.gnu.org/g:2a1a37344f0db01900e73f05fca71cdcf7a559c2

commit 2a1a37344f0db01900e73f05fca71cdcf7a559c2
Author: Jakub Dupak <d...@jakubdupak.com>
Date:   Thu Oct 19 15:26:35 2023 +0200

    borrowck: Refactor and BIR improvements
    
    gcc/rust/ChangeLog:
    
            * checks/errors/borrowck/rust-bir-builder-expr-stmt.cc 
(ExprStmtBuilder::setup_loop): Move.
            (ExprStmtBuilder::get_label_ctx): Move.
            (ExprStmtBuilder::get_unnamed_loop_ctx): Moved.
            (ExprStmtBuilder::visit): BIR improvements.
            * checks/errors/borrowck/rust-bir-builder-expr-stmt.h: Refactor.
            * checks/errors/borrowck/rust-bir-builder-internal.h (class 
LifetimeResolver):
            Refactor.
            (struct BuilderContext): Move.Refactor.
            (optional_from_ptr): Map on null ptr.
            * checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h (class 
LazyBooleanExprBuilder):
            Refactor.
            * checks/errors/borrowck/rust-bir-builder-pattern.h: Refactor.
            * checks/errors/borrowck/rust-bir-builder-struct.h (class 
StructBuilder): Refactor.
            * checks/errors/borrowck/rust-bir-builder.h: Refactor.
            * checks/errors/borrowck/rust-bir-dump.cc (Dump::go): Refactor.
            (Dump::visit): Refactor.
            (Dump::visit_place): Refactor.
            (Dump::visit_move_place): Refactor.
            (Dump::visit_lifetime): Refactor.
            * checks/errors/borrowck/rust-bir-dump.h: Refactor.
            * checks/errors/borrowck/rust-bir-place.h: Refactor.
    
    Signed-off-by: Jakub Dupak <d...@jakubdupak.com>

Diff:
---
 .../errors/borrowck/rust-bir-builder-expr-stmt.cc  | 285 +++++++++---------
 .../errors/borrowck/rust-bir-builder-expr-stmt.h   |  28 +-
 .../errors/borrowck/rust-bir-builder-internal.h    | 319 ++++++++++++---------
 .../borrowck/rust-bir-builder-lazyboolexpr.h       |  89 +++---
 .../errors/borrowck/rust-bir-builder-pattern.h     |  36 ++-
 .../errors/borrowck/rust-bir-builder-struct.h      |  22 +-
 gcc/rust/checks/errors/borrowck/rust-bir-builder.h |  10 +-
 gcc/rust/checks/errors/borrowck/rust-bir-dump.cc   |  57 ++--
 gcc/rust/checks/errors/borrowck/rust-bir-dump.h    |   5 +-
 gcc/rust/checks/errors/borrowck/rust-bir-place.h   |  72 +++--
 10 files changed, 508 insertions(+), 415 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 367aea7c2da3..96bc738964ed 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
@@ -25,6 +25,48 @@
 namespace Rust {
 namespace BIR {
 
+using LoopAndLabelCtx = BuilderContext::LoopAndLabelCtx;
+
+BuilderContext::LoopAndLabelCtx &
+ExprStmtBuilder::setup_loop (HIR::BaseLoopExpr &expr)
+{
+  NodeId label
+    = (expr.has_loop_label ())
+       ? expr.get_loop_label ().get_lifetime ().get_mappings ().get_nodeid ()
+       : UNKNOWN_NODEID;
+  PlaceId label_var = take_or_create_return_place (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 ();
+}
+
+BuilderContext::LoopAndLabelCtx &
+ExprStmtBuilder::get_label_ctx (HIR::Lifetime &label)
+{
+  NodeId label_id = resolve_label (label);
+  auto lookup = std::find_if (ctx.loop_and_label_stack.rbegin (),
+                             ctx.loop_and_label_stack.rend (),
+                             [label_id] (LoopAndLabelCtx &info) {
+                               return info.label == label_id;
+                             });
+  rust_assert (lookup != ctx.loop_and_label_stack.rend ());
+  return *lookup;
+}
+
+LoopAndLabelCtx &
+ExprStmtBuilder::get_unnamed_loop_ctx ()
+{
+  auto lookup
+    = std::find_if (ctx.loop_and_label_stack.rbegin (),
+                   ctx.loop_and_label_stack.rend (),
+                   [] (LoopAndLabelCtx &info) { return info.is_loop; });
+  rust_assert (lookup != ctx.loop_and_label_stack.rend ());
+  return *lookup;
+}
+
 void
 ExprStmtBuilder::visit (HIR::ClosureExpr &expr)
 {
@@ -34,8 +76,9 @@ ExprStmtBuilder::visit (HIR::ClosureExpr &expr)
     {
       captures.push_back (ctx.place_db.lookup_variable (capture));
     }
+  make_args (captures);
 
-  // Not a coercion site.
+  // Note: Not a coercion site for captures.
   return_expr (new InitializerExpr (std::move (captures)), lookup_type (expr));
 }
 
@@ -45,6 +88,7 @@ ExprStmtBuilder::visit (HIR::StructExprStructFields &fields)
   auto struct_ty
     = lookup_type (fields)->as<TyTy::ADTType> ()->get_variants ().at (0);
   auto init_values = StructBuilder (ctx, struct_ty).build (fields);
+  make_args (init_values);
   return_expr (new InitializerExpr (std::move (init_values)),
               lookup_type (fields));
 }
@@ -59,7 +103,7 @@ ExprStmtBuilder::visit (HIR::StructExprStruct &expr)
 void
 ExprStmtBuilder::visit (HIR::LiteralExpr &expr)
 {
-  // Different literal values of the same type are not distinguished.
+  // Different literal values of the same type are not distinguished in BIR.
   return_place (ctx.place_db.get_constant (lookup_type (expr)));
 }
 
@@ -74,12 +118,14 @@ void
 ExprStmtBuilder::visit (HIR::DereferenceExpr &expr)
 {
   auto operand = visit_expr (*expr.get_expr ());
-  return_place (operand);
+  return_place (ctx.place_db.lookup_or_add_path (Place::DEREF,
+                                                lookup_type (expr), operand));
 }
 
 void
 ExprStmtBuilder::visit (HIR::ErrorPropagationExpr &expr)
 {
+  // TODO: desugar in AST->HIR
   rust_sorry_at (expr.get_locus (), "error propagation is not supported");
 }
 
@@ -87,7 +133,7 @@ void
 ExprStmtBuilder::visit (HIR::NegationExpr &expr)
 {
   PlaceId operand = visit_expr (*expr.get_expr ());
-  return_expr (new Operator<1> ({operand}), lookup_type (expr));
+  return_expr (new Operator<1> ({make_arg (operand)}), lookup_type (expr));
 }
 
 void
@@ -95,7 +141,8 @@ ExprStmtBuilder::visit (HIR::ArithmeticOrLogicalExpr &expr)
 {
   PlaceId lhs = visit_expr (*expr.get_lhs ());
   PlaceId rhs = visit_expr (*expr.get_rhs ());
-  return_expr (new Operator<2> ({lhs, rhs}), lookup_type (expr));
+  return_expr (new Operator<2> ({make_arg (lhs), make_arg (rhs)}),
+              lookup_type (expr));
 }
 
 void
@@ -103,19 +150,23 @@ ExprStmtBuilder::visit (HIR::ComparisonExpr &expr)
 {
   PlaceId lhs = visit_expr (*expr.get_lhs ());
   PlaceId rhs = visit_expr (*expr.get_rhs ());
-  return_expr (new Operator<2> ({lhs, rhs}), lookup_type (expr));
+  return_expr (new Operator<2> ({make_arg (lhs), make_arg (rhs)}),
+              lookup_type (expr));
 }
 
 void
 ExprStmtBuilder::visit (HIR::LazyBooleanExpr &expr)
 {
-  return_place (LazyBooleanExprBuilder (ctx).build (expr));
+  return_place (LazyBooleanExprBuilder (ctx, take_or_create_return_place (
+                                              lookup_type (expr)))
+                 .build (expr));
 }
 
 void
 ExprStmtBuilder::visit (HIR::TypeCastExpr &expr)
 {
-  return_place (visit_expr (*expr.get_expr ()));
+  auto operand = visit_expr (*expr.get_expr ());
+  return_expr (new Operator<1> ({operand}), lookup_type (expr));
 }
 
 void
@@ -132,34 +183,31 @@ ExprStmtBuilder::visit (HIR::CompoundAssignmentExpr &expr)
   auto lhs = visit_expr (*expr.get_lhs ());
   auto rhs = visit_expr (*expr.get_rhs ());
   push_assignment (lhs, new Operator<2> ({lhs, rhs}));
-  // TODO: (philip) nicer unit?
-  return_place (ctx.place_db.get_constant (lookup_type (expr)));
 }
 
 void
 ExprStmtBuilder::visit (HIR::GroupedExpr &expr)
 {
-  // Uses accept_vis directly to avoid creating n new temporary.
-  expr.get_expr_in_parens ()->accept_vis (*this);
+  return_place (visit_expr (*expr.get_expr_in_parens ()));
 }
 
 void
 ExprStmtBuilder::visit (HIR::ArrayExpr &expr)
 {
-  switch (expr.get_internal_elements ()->get_array_expr_type ())
+  auto &elems = expr.get_internal_elements ();
+  switch (elems->get_array_expr_type ())
     {
       case HIR::ArrayElems::VALUES: {
-       auto init_values = visit_list ((static_cast<HIR::ArrayElemsValues *> (
-                                         expr.get_internal_elements ().get ()))
-                                        ->get_values ());
+       auto &elem_vals = (static_cast<HIR::ArrayElemsValues &> (*elems));
+       auto init_values = visit_list (elem_vals.get_values ());
+       make_args (init_values);
        return_expr (new InitializerExpr (std::move (init_values)),
                     lookup_type (expr));
        break;
       }
       case HIR::ArrayElems::COPIED: {
-       auto init = visit_expr (*(static_cast<HIR::ArrayElemsCopied *> (
-                                   expr.get_internal_elements ().get ()))
-                                  ->get_elem_to_copy ());
+       auto &elem_copied = (static_cast<HIR::ArrayElemsCopied &> (*elems));
+       auto init = visit_expr (*elem_copied.get_elem_to_copy ());
        return_expr (new InitializerExpr ({init}), lookup_type (expr));
        break;
       }
@@ -171,7 +219,7 @@ ExprStmtBuilder::visit (HIR::ArrayIndexExpr &expr)
 {
   auto lhs = visit_expr (*expr.get_array_expr ());
   auto rhs = visit_expr (*expr.get_index_expr ());
-  // The Index is not tracked in BIR.
+  // The index is not tracked in BIR.
   (void) rhs;
   return_place (
     ctx.place_db.lookup_or_add_path (Place::INDEX, lookup_type (expr), lhs));
@@ -181,14 +229,6 @@ void
 ExprStmtBuilder::visit (HIR::TupleExpr &expr)
 {
   std::vector<PlaceId> init_values = visit_list (expr.get_tuple_elems ());
-  if (std::any_of (init_values.begin (), init_values.end (),
-                  [this] (PlaceId id) {
-                    return ctx.place_db[id].lifetime.has_lifetime ();
-                  }))
-    {
-      ctx.place_db[expr_return_place].lifetime
-       = {ctx.lifetime_interner.get_anonymous ()};
-    }
   return_expr (new InitializerExpr (std::move (init_values)),
               lookup_type (expr));
 }
@@ -208,7 +248,7 @@ ExprStmtBuilder::visit (HIR::CallExpr &expr)
   PlaceId fn = visit_expr (*expr.get_fnexpr ());
   std::vector<PlaceId> arguments = visit_list (expr.get_arguments ());
 
-  TyTy::BaseType *call_type = ctx.place_db[fn].tyty;
+  auto *call_type = ctx.place_db[fn].tyty;
   if (auto fn_type = call_type->try_as<TyTy::FnType> ())
     {
       for (size_t i = 0; i < fn_type->get_params ().size (); ++i)
@@ -233,6 +273,10 @@ ExprStmtBuilder::visit (HIR::CallExpr &expr)
               true);
 }
 
+void
+ExprStmtBuilder::visit (HIR::MethodCallExpr &expr)
+{}
+
 void
 ExprStmtBuilder::visit (HIR::FieldAccessExpr &expr)
 {
@@ -242,12 +286,12 @@ ExprStmtBuilder::visit (HIR::FieldAccessExpr &expr)
   auto adt = type->as<TyTy::ADTType> ();
   rust_assert (!adt->is_enum ());
   rust_assert (adt->number_of_variants () == 1);
-  TyTy::VariantDef *variant = adt->get_variants ().at (0);
+  auto struct_ty = adt->get_variants ().at (0);
 
   TyTy::StructFieldType *field_ty = nullptr;
   size_t field_index = 0;
-  bool ok = variant->lookup_field (expr.get_field_name ().as_string (),
-                                  &field_ty, &field_index);
+  bool ok = struct_ty->lookup_field (expr.get_field_name ().as_string (),
+                                    &field_ty, &field_index);
   rust_assert (ok);
 
   return_place (ctx.place_db.lookup_or_add_path (Place::FIELD,
@@ -258,114 +302,71 @@ 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});
+      PlaceId label_var = take_or_create_return_place (lookup_type (block));
+      ctx.loop_and_label_stack.push_back (
+       {false, label, label_var, new_bb (), INVALID_BB});
     }
 
+  // Eliminates dead code after break, continue, return.
   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;
+       {
+         unreachable = true;
+         break;
+       }
     }
 
   if (block.has_label ())
     {
-      auto label_info = ctx.loop_and_label_stack.back ();
+      auto block_ctx = ctx.loop_and_label_stack.back ();
       if (block.has_expr () && !unreachable)
        {
-         push_assignment (label_info.label_var,
+         push_assignment (block_ctx.label_var,
                           visit_expr (*block.get_final_expr ()));
        }
       if (!ctx.get_current_bb ().is_terminated ())
        {
-         add_jump_to (end_bb);
+         push_goto (block_ctx.break_bb);
        }
-      ctx.current_bb = end_bb;
+      ctx.current_bb = block_ctx.break_bb;
       ctx.loop_and_label_stack.pop_back ();
 
-      return_place (label_info.label_var);
+      return_place (block_ctx.label_var);
     }
   else if (block.has_expr () && !unreachable)
     {
-      return_place (visit_expr (*block.get_final_expr ()));
+      return_place (visit_expr (*block.get_final_expr (),
+                               take_or_create_return_place (
+                                 lookup_type (*block.get_final_expr ()))));
     }
 }
 
 void
 ExprStmtBuilder::visit (HIR::ContinueExpr &cont)
 {
-  BuilderContext::LoopAndLabelInfo info;
-  if (cont.has_label ())
-    {
-      NodeId label = resolve_label (cont.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;
-    }
+  LoopAndLabelCtx info = cont.has_label () ? get_label_ctx (cont.get_label ())
+                                          : get_unnamed_loop_ctx ();
   push_goto (info.continue_bb);
-  // No code allowed after continue. No BB starts - would be empty.
+  // No code allowed after continue. Handled in BlockExpr.
 }
 
 void
 ExprStmtBuilder::visit (HIR::BreakExpr &brk)
 {
-  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;
-    }
+  LoopAndLabelCtx info = brk.has_label () ? get_label_ctx (brk.get_label ())
+                                         : get_unnamed_loop_ctx ();
   if (brk.has_break_expr ())
-    {
-      push_assignment (info.label_var, visit_expr (*brk.get_expr ()));
-    }
+    push_assignment (info.label_var, visit_expr (*brk.get_expr ()));
+
   push_goto (info.break_bb);
-  // No code allowed after break. No BB starts - would be empty.
+  // No code allowed after continue. Handled in BlockExpr.
 }
 
 void
@@ -427,22 +428,6 @@ 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 ())
-       ? expr.get_loop_label ().get_lifetime ().get_mappings ().get_nodeid ()
-       : 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)
 {
@@ -518,40 +503,40 @@ ExprStmtBuilder::visit (HIR::IfExpr &expr)
 void
 ExprStmtBuilder::visit (HIR::IfExprConseqElse &expr)
 {
-  PlaceId result = ctx.place_db.add_temporary (lookup_type (expr));
+  push_switch (make_arg (visit_expr (*expr.get_if_condition ())));
+  BasicBlockId if_end_bb = ctx.current_bb;
 
-  push_switch (visit_expr (*expr.get_if_condition ()));
-  BasicBlockId if_block = ctx.current_bb;
+  PlaceId result = take_or_create_return_place (lookup_type (expr));
 
   ctx.current_bb = new_bb ();
-  auto then_res = visit_expr (*expr.get_if_block ());
-  push_assignment (result, then_res);
+  BasicBlockId then_start_bb = ctx.current_bb;
+  (void) visit_expr (*expr.get_if_block (), result);
   if (!ctx.get_current_bb ().is_terminated ())
     push_goto (INVALID_BB); // Resolved later.
-  BasicBlockId then_block = ctx.current_bb;
+  BasicBlockId then_end_bb = ctx.current_bb;
 
   ctx.current_bb = new_bb ();
-  auto else_res = visit_expr (*expr.get_else_block ());
-  push_assignment (result, else_res);
+  BasicBlockId else_start_bb = ctx.current_bb;
+  (void) visit_expr (*expr.get_else_block (), result);
   if (!ctx.get_current_bb ().is_terminated ())
     push_goto (INVALID_BB); // Resolved later.
-  BasicBlockId else_block = ctx.current_bb;
+  BasicBlockId else_end_bb = ctx.current_bb;
 
   ctx.current_bb = new_bb ();
-  BasicBlockId final_block = ctx.current_bb;
+  BasicBlockId final_start_bb = ctx.current_bb;
   return_place (result);
 
   // 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 (if_end_bb, then_start_bb);
+  add_jump (if_end_bb, else_start_bb);
 
-  auto &then_bb = ctx.basic_blocks[then_block];
+  auto &then_bb = ctx.basic_blocks[then_end_bb];
   if (then_bb.is_goto_terminated () && then_bb.successors.empty ())
-    add_jump (then_block, final_block);
+    add_jump (then_end_bb, final_start_bb);
 
-  auto &else_bb = ctx.basic_blocks[else_block];
+  auto &else_bb = ctx.basic_blocks[else_end_bb];
   if (else_bb.is_goto_terminated () && else_bb.successors.empty ())
-    add_jump (else_block, final_block);
+    add_jump (else_end_bb, final_start_bb);
 }
 void
 ExprStmtBuilder::visit (HIR::IfLetExpr &expr)
@@ -566,6 +551,7 @@ ExprStmtBuilder::visit (HIR::IfLetExprConseqElse &expr)
 void
 ExprStmtBuilder::visit (HIR::MatchExpr &expr)
 {
+  rust_sorry_at (expr.get_locus (), "match expressions are not supported");
   //  // TODO
   //  expr.get_scrutinee_expr ()->accept_vis (*this);
   //  PlaceId scrutinee = translated;
@@ -621,44 +607,47 @@ ExprStmtBuilder::visit (HIR::AsyncBlockExpr &expr)
 void
 ExprStmtBuilder::visit (HIR::QualifiedPathInExpression &expr)
 {
-  PlaceId result;
   // Note: Type is only stored for the expr, not the segment.
-  bool ok = resolve_variable (expr.get_final_segment (), result);
-  rust_assert (ok);
+  PlaceId result
+    = resolve_variable_or_fn (expr.get_final_segment (), lookup_type (expr));
   return_place (result);
 }
 
 void
 ExprStmtBuilder::visit (HIR::PathInExpression &expr)
 {
-  PlaceId result;
   // Note: Type is only stored for the expr, not the segment.
-  bool ok = resolve_variable (expr.get_final_segment (), result);
-  rust_assert (ok);
+  PlaceId result
+    = resolve_variable_or_fn (expr.get_final_segment (), lookup_type (expr));
   return_place (result);
 }
 
 void
 ExprStmtBuilder::visit (HIR::LetStmt &stmt)
 {
-  if (stmt.has_init_expr ())
-    {
-      auto init = visit_expr (*stmt.get_init_expr ());
-      PatternBindingBuilder (ctx, init, stmt.get_type ().get ())
-       .go (*stmt.get_pattern ());
-    }
-  else if (stmt.get_pattern ()->get_pattern_type () == 
HIR::Pattern::IDENTIFIER)
+  if (stmt.get_pattern ()->get_pattern_type () == HIR::Pattern::IDENTIFIER)
     {
+      // Only if a pattern is just an identifier, no destructuring is needed.
+      // Hoverer PatternBindingBuilder cannot change existing temporary
+      // (init expr is evaluated before pattern binding) into a
+      // variable, so it would emit extra assignment.
       auto var = declare_variable (stmt.get_pattern ()->get_mappings ());
       auto &var_place = ctx.place_db[var];
       if (var_place.tyty->get_kind () == TyTy::REF)
        {
-         auto p_type = tl::optional<HIR::ReferenceType *> (
-           static_cast<HIR::ReferenceType *> (stmt.get_type ().get ()));
          var_place.lifetime = ctx.lookup_lifetime (
-           p_type.map (&HIR::ReferenceType::get_lifetime));
+           optional_from_ptr (
+             static_cast<HIR::ReferenceType *> (stmt.get_type ().get ()))
+             .map (&HIR::ReferenceType::get_lifetime));
        }
-      return;
+      if (stmt.has_init_expr ())
+       (void) visit_expr (*stmt.get_init_expr (), var);
+    }
+  else if (stmt.has_init_expr ())
+    {
+      auto init = visit_expr (*stmt.get_init_expr ());
+      PatternBindingBuilder (ctx, init, stmt.get_type ().get ())
+       .go (*stmt.get_pattern ());
     }
   else
     {
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 e5707c31f8d3..1352965b725e 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
@@ -25,14 +25,21 @@
 namespace Rust {
 namespace BIR {
 
+/**
+ * Compiles expressions into a BIR place.
+ * See AbstractExprBuilder for API usage docs (mainly `return_place` and
+ * `return_expr`).
+ */
 class ExprStmtBuilder : public AbstractExprBuilder, public HIR::HIRStmtVisitor
 {
-  PlaceId expr_return_place = INVALID_PLACE;
-
 public:
   explicit ExprStmtBuilder (BuilderContext &ctx) : AbstractExprBuilder (ctx) {}
 
-  PlaceId build (HIR::Expr &expr) { return visit_expr (expr); }
+  /** Entry point. */
+  PlaceId build (HIR::Expr &expr, PlaceId place = INVALID_PLACE)
+  {
+    return visit_expr (expr, place);
+  }
 
 private:
   template <typename T>
@@ -46,17 +53,19 @@ private:
     return result;
   }
 
-  BuilderContext::LoopAndLabelInfo &setup_loop (HIR::BaseLoopExpr &expr);
+  /** Common infrastructure for loops. */
+  BuilderContext::LoopAndLabelCtx &setup_loop (HIR::BaseLoopExpr &expr);
+
+  BuilderContext::LoopAndLabelCtx &get_label_ctx (HIR::Lifetime &label);
+  BuilderContext::LoopAndLabelCtx &get_unnamed_loop_ctx ();
 
 protected: // Expr
-  // TODO: test when compiles
   void visit (HIR::ClosureExpr &expr) override;
   void visit (HIR::StructExprStructFields &fields) override;
   void visit (HIR::StructExprStruct &expr) override;
   void visit (HIR::LiteralExpr &expr) override;
   void visit (HIR::BorrowExpr &expr) override;
   void visit (HIR::DereferenceExpr &expr) override;
-  // TODO: desugar in AST->HIR
   void visit (HIR::ErrorPropagationExpr &expr) override;
   void visit (HIR::NegationExpr &expr) override;
   void visit (HIR::ArithmeticOrLogicalExpr &expr) override;
@@ -71,7 +80,7 @@ protected: // Expr
   void visit (HIR::TupleExpr &expr) override;
   void visit (HIR::TupleIndexExpr &expr) override;
   void visit (HIR::CallExpr &expr) override;
-  void visit (HIR::MethodCallExpr &expr) override {}
+  void visit (HIR::MethodCallExpr &expr) override;
   void visit (HIR::FieldAccessExpr &expr) override;
   void visit (HIR::BlockExpr &block) override;
   void visit (HIR::ContinueExpr &cont) override;
@@ -96,7 +105,7 @@ protected: // Expr
   void visit (HIR::AwaitExpr &expr) override;
   void visit (HIR::AsyncBlockExpr &expr) override;
 
-  // Nodes not containing executable code. Nothing to do.
+protected: // Nodes not containing executable code. Nothing to do.
   void visit (HIR::QualifiedPathInExpression &expr) override;
   void visit (HIR::PathInExpression &expr) override;
 
@@ -119,9 +128,8 @@ protected: // Stmt
 
   void visit (HIR::ExprStmt &stmt) override;
 
-protected: // Unused
+protected: // Ignored.
   // Only executable code of a single function/method is translated.
-  // All other items are ignored.
   void visit (HIR::EnumItemTuple &tuple) override {}
   void visit (HIR::EnumItemStruct &a_struct) override {}
   void visit (HIR::EnumItem &item) 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 663b6ad7fae6..f33eb0752446 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h
@@ -31,31 +31,35 @@ namespace Rust {
 
 namespace BIR {
 
-class LifetimeResolver
+/** Holds the context of BIR building so that it can be shared/passed between
+ * different builders. */
+struct BuilderContext
 {
-  using Index = uint32_t;
-  using Value = std::string;
+  class LifetimeResolver
+  {
+    using Index = uint32_t;
+    using Value = std::string;
 
-  Index next_index = FIRST_NORMAL_LIFETIME_ID;
-  std::unordered_map<Value, Index> value_to_index;
+    Index next_index = FIRST_NORMAL_LIFETIME_ID;
+    std::unordered_map<Value, Index> value_to_index;
 
-public:
-  Index intern (const Value &value)
-  {
-    auto found = value_to_index.find (value);
-    if (found != value_to_index.end ())
-      {
-       return found->second;
-      }
-    value_to_index.emplace (value, next_index);
-    return next_index++;
-  }
-  Index get_anonymous () { return next_index++; }
-};
+  public:
+    Index resolve (const Value &value)
+    {
+      auto found = value_to_index.find (value);
+      if (found != value_to_index.end ())
+       {
+         return found->second;
+       }
+      value_to_index.emplace (value, next_index);
+      return next_index++;
+    }
 
-struct BuilderContext
-{
-  struct LoopAndLabelInfo
+    /** Returns a new anonymous lifetime. */
+    Index get_anonymous () { return next_index++; }
+  };
+
+  struct LoopAndLabelCtx
   {
     bool is_loop;      // Loop or labelled block
     NodeId label;      // UNKNOWN_NODEID if no label (loop)
@@ -63,19 +67,20 @@ struct BuilderContext
     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)
+    LoopAndLabelCtx (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
+  // External context.
   Resolver::TypeCheckContext &tyctx;
   Resolver::Resolver &resolver;
 
+  // BIR output
   std::vector<BasicBlock> basic_blocks;
   size_t current_bb = 0;
 
@@ -85,6 +90,7 @@ struct BuilderContext
    */
   PlaceDB place_db;
   LifetimeResolver lifetime_interner;
+  // Used for cleaner dump.
   std::vector<PlaceId> arguments;
   /**
    * Since labels can be used to return values, we need to reserve a place for
@@ -92,7 +98,8 @@ struct BuilderContext
    */
   std::unordered_map<NodeId, PlaceId> label_place_map;
 
-  std::vector<LoopAndLabelInfo> loop_and_label_stack;
+  /** Context for current situation (loop, label, etc.) */
+  std::vector<LoopAndLabelCtx> loop_and_label_stack;
 
 public:
   BuilderContext ()
@@ -111,7 +118,7 @@ public:
     switch (lifetime->get_lifetime_type ())
       {
        case AST::Lifetime::NAMED: {
-         return {lifetime_interner.intern (lifetime->get_name ())};
+         return {lifetime_interner.resolve (lifetime->get_name ())};
        }
        case AST::Lifetime::STATIC: {
          return STATIC_LIFETIME;
@@ -125,9 +132,9 @@ public:
     rust_unreachable ();
   };
 
-  const LoopAndLabelInfo &lookup_label (NodeId label)
+  const LoopAndLabelCtx &lookup_label (NodeId label)
   {
-    auto label_match = [label] (const LoopAndLabelInfo &info) {
+    auto label_match = [label] (const LoopAndLabelCtx &info) {
       return info.label != UNKNOWN_NODEID && info.label == label;
     };
 
@@ -138,67 +145,39 @@ public:
   }
 };
 
-// Common infrastructure for building BIR from HIR.
+/** Common infrastructure for building BIR from HIR. */
 class AbstractBuilder
 {
 protected:
   BuilderContext &ctx;
 
-  // This emulates the return value of the visitor, to be able to use the
-  // current visitor infrastructure, where the return value is forced to be
-  // void.
+  /**
+   * This emulates the return value of the visitor, to be able to use the
+   * current visitor infrastructure, where the return value is forced to be
+   * void.
+   */
   PlaceId translated = INVALID_PLACE;
 
 protected:
   explicit AbstractBuilder (BuilderContext &ctx) : ctx (ctx) {}
 
-  WARN_UNUSED_RESULT static NodeId get_nodeid (HIR::Expr &expr)
-  {
-    return expr.get_mappings ().get_nodeid ();
-  }
-
-  WARN_UNUSED_RESULT static NodeId get_nodeid (HIR::Pattern &pattern)
-  {
-    return pattern.get_mappings ().get_nodeid ();
-  }
-
-  template <typename T>
-  WARN_UNUSED_RESULT TyTy::BaseType *lookup_type (T &pattern) const
-  {
-    return lookup_type (pattern.get_mappings ().get_hirid ());
-  }
-
-  WARN_UNUSED_RESULT TyTy::BaseType *lookup_type (HirId hirid) const
-  {
-    TyTy::BaseType *type = nullptr;
-    bool ok = ctx.tyctx.lookup_type (hirid, &type);
-    rust_assert (ok);
-    rust_assert (type != nullptr);
-    return type;
-  }
-
   PlaceId declare_variable (const Analysis::NodeMapping &node)
   {
-    const NodeId nodeid = node.get_nodeid ();
-    const HirId hirid = node.get_hirid ();
-
-    // In debug mode check that the variable is not already declared.
-    rust_assert (ctx.place_db.lookup_variable (nodeid) == INVALID_PLACE);
-
-    return ctx.place_db.add_variable (nodeid, lookup_type (hirid));
+    return declare_variable (node, lookup_type (node.get_hirid ()));
   }
 
   PlaceId declare_variable (const Analysis::NodeMapping &node,
-                           TyTy::BaseType *type)
+                           TyTy::BaseType *ty)
   {
     const NodeId nodeid = node.get_nodeid ();
 
-    // In debug mode check that the variable is not already declared.
+    // In debug mode, check that the variable is not already declared.
     rust_assert (ctx.place_db.lookup_variable (nodeid) == INVALID_PLACE);
 
-    return ctx.place_db.add_variable (nodeid, type);
+    return ctx.place_db.add_variable (nodeid, ty);
   }
 
+protected: // Helpers to add BIR nodes
   void push_assignment (PlaceId lhs, AbstractExpr *rhs)
   {
     ctx.get_current_bb ().statements.emplace_back (lhs, rhs);
@@ -219,8 +198,8 @@ protected:
   void push_switch (PlaceId switch_val,
                    std::initializer_list<BasicBlockId> destinations = {})
   {
-    ctx.get_current_bb ().statements.emplace_back (Node::Kind::SWITCH,
-                                                  switch_val);
+    auto copy = make_arg (switch_val);
+    ctx.get_current_bb ().statements.emplace_back (Node::Kind::SWITCH, copy);
     ctx.get_current_bb ().successors.insert (
       ctx.get_current_bb ().successors.end (), destinations);
   }
@@ -232,25 +211,40 @@ protected:
       ctx.get_current_bb ().successors.push_back (bb);
   }
 
-  void push_storage_dead (PlaceId place)
+  PlaceId declare_rvalue (PlaceId place)
+  {
+    ctx.place_db[place].is_rvalue = true;
+    return place;
+  }
+
+  void declare_rvalues (std::vector<PlaceId> &places)
+  {
+    for (auto &place : places)
+      declare_rvalue (place);
+  }
+
+  PlaceId make_arg (PlaceId arg)
   {
-    ctx.get_current_bb ().statements.emplace_back (Node::Kind::STORAGE_DEAD,
-                                                  place);
+    auto copy = ctx.place_db.into_rvalue (arg);
+    if (copy != arg)
+      push_assignment (copy, arg);
+    return copy;
   }
 
-  void push_storage_live (PlaceId place)
+  void make_args (std::vector<PlaceId> &args)
   {
-    ctx.get_current_bb ().statements.emplace_back (Node::Kind::STORAGE_LIVE,
-                                                  place);
+    std::transform (args.begin (), args.end (), args.begin (),
+                   [this] (PlaceId arg) { return make_arg (arg); });
   }
 
+protected: // CFG helpers
   BasicBlockId new_bb ()
   {
     ctx.basic_blocks.emplace_back ();
     return ctx.basic_blocks.size () - 1;
   }
 
-  BasicBlockId start_new_subsequent_bb ()
+  BasicBlockId start_new_consecutive_bb ()
   {
     BasicBlockId bb = new_bb ();
     ctx.get_current_bb ().successors.emplace_back (bb);
@@ -265,7 +259,22 @@ protected:
 
   void add_jump_to (BasicBlockId bb) { add_jump (ctx.current_bb, bb); }
 
-protected:
+protected: // HIR resolution helpers
+  template <typename T>
+  [[nodiscard]] TyTy::BaseType *lookup_type (T &hir_node) const
+  {
+    return lookup_type (hir_node.get_mappings ().get_hirid ());
+  }
+
+  [[nodiscard]] TyTy::BaseType *lookup_type (HirId hirid) const
+  {
+    TyTy::BaseType *type = nullptr;
+    bool ok = ctx.tyctx.lookup_type (hirid, &type);
+    rust_assert (ok);
+    rust_assert (type != nullptr);
+    return type;
+  }
+
   template <typename T> NodeId resolve_label (T &expr)
   {
     NodeId resolved_label;
@@ -276,42 +285,30 @@ protected:
     return resolved_label;
   }
 
-  template <typename T>
-  bool resolve_variable (T &variable, PlaceId &resolved_variable)
+  template <typename T> PlaceId resolve_variable (T &variable)
   {
     NodeId variable_id;
-    if (!ctx.resolver.lookup_resolved_name (
-         variable.get_mappings ().get_nodeid (), &variable_id))
-      {
-       // TODO: should this be assert? (should be caught by typecheck)
-       rust_error_at (variable.get_locus (), "unresolved variable");
-       return false;
-      }
-    resolved_variable = ctx.place_db.lookup_variable (variable_id);
-    return true;
+    bool ok = ctx.resolver.lookup_resolved_name (
+      variable.get_mappings ().get_nodeid (), &variable_id);
+    rust_assert (ok);
+    return ctx.place_db.lookup_variable (variable_id);
   }
 
-  bool find_block_ctx (NodeId label, BuilderContext::LoopAndLabelInfo &block)
+  template <typename T>
+  PlaceId resolve_variable_or_fn (T &variable, TyTy::BaseType *ty)
   {
-    if (ctx.loop_and_label_stack.empty ())
-      return false;
-    if (label == UNKNOWN_NODEID)
-      {
-       block = ctx.loop_and_label_stack.back ();
-       return true;
-      }
-    auto found
-      = 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_and_label_stack.rend ())
-      return false;
-    block = *found;
-    return true;
+    // Unlike variables,
+    // functions do not have to be declared in PlaceDB before use.
+    NodeId variable_id;
+    bool ok = ctx.resolver.lookup_resolved_name (
+      variable.get_mappings ().get_nodeid (), &variable_id);
+    rust_assert (ok);
+    return ctx.place_db.lookup_or_add_variable (variable_id,
+                                               (ty) ? ty
+                                                    : lookup_type (variable));
   }
 
+protected: // Implicit conversions.
   /**
    * Performs implicit coercions on the `translated` place defined for a
    * coercion site.
@@ -362,7 +359,6 @@ protected:
     return ty;
   }
 
-  /** For operator  */
   void autoref ()
   {
     if (ctx.place_db[translated].tyty->get_kind () != TyTy::REF)
@@ -380,67 +376,108 @@ class AbstractExprBuilder : public AbstractBuilder,
                            public HIR::HIRExpressionVisitor
 {
 protected:
-  // Exactly one of this and `translated` is used by each visitor.
-  AbstractExpr *expr_return_expr = nullptr;
+  /**
+   * Optional place for the result of the evaluated expression.
+   * Valid if value is not `INVALID_PLACE`.
+   * Used when return place must be created by caller (return for if-else).
+   */
+  PlaceId expr_return_place = INVALID_PLACE;
 
 protected:
-  explicit AbstractExprBuilder (BuilderContext &ctx) : AbstractBuilder (ctx) {}
+  explicit AbstractExprBuilder (BuilderContext &ctx,
+                               PlaceId expr_return_place = INVALID_PLACE)
+    : AbstractBuilder (ctx), expr_return_place (expr_return_place)
+  {}
 
-  PlaceId visit_expr (HIR::Expr &expr)
+  /**
+   * Wrapper that provides return value based API inside a visitor which has to
+   * use global state to pass the data around.
+   * @param dst_place Place to assign the produced value to, optionally
+   * allocated by the caller.
+   * */
+  PlaceId visit_expr (HIR::Expr &expr, PlaceId dst_place = INVALID_PLACE)
   {
-    // Reset return places.
+    // Save to support proper recursion.
+    auto saved = expr_return_place;
+    expr_return_place = dst_place;
     translated = INVALID_PLACE;
-    expr_return_expr = nullptr;
     expr.accept_vis (*this);
-    if (translated != INVALID_PLACE)
+    expr_return_place = saved;
+    auto result = translated;
+    translated = INVALID_PLACE;
+    return result;
+  }
+
+  /**
+   * Create a return value of a subexpression, which produces an expression.
+   * Use `return_place` for subexpression that only produce a place (look it 
up)
+   * to avoid needless assignments.
+   *
+   * @param can_panic mark that expression can panic to insert jump to cleanup.
+   */
+  void return_expr (AbstractExpr *expr, TyTy::BaseType *ty,
+                   bool can_panic = false)
+  {
+    if (expr_return_place != INVALID_PLACE)
       {
-       auto result = translated;
-       translated = INVALID_PLACE;
-       return result;
+       push_assignment (expr_return_place, expr);
       }
-    else if (expr_return_expr != nullptr)
+    else
       {
-       // Only allocate temporary, if needed.
-       push_tmp_assignment (expr_return_expr, lookup_type (expr));
-       expr_return_expr = nullptr;
-       return translated;
+       push_tmp_assignment (expr, ty);
       }
-    else
+
+    if (can_panic)
       {
-       return ctx.place_db.get_constant (lookup_type (expr));
+       start_new_consecutive_bb ();
       }
   }
 
-  void return_expr (AbstractExpr *expr, TyTy::BaseType *ty,
-                   bool can_panic = false)
+  /** Mark place to be a result of processed subexpression. */
+  void return_place (PlaceId place)
   {
-    rust_assert (expr_return_expr == nullptr);
-    if (can_panic)
+    if (expr_return_place != INVALID_PLACE)
       {
-       push_tmp_assignment (expr, ty);
-       // TODO, cleanup?
-       start_new_subsequent_bb ();
+       // Return place is already allocated, no need to defer assignment.
+       push_assignment (expr_return_place, place);
       }
     else
       {
-       translated = INVALID_PLACE;
-       expr_return_expr = expr;
+       translated = place;
       }
   }
 
-  void return_place (PlaceId place)
+  /** Explicitly return a unit value. Expression produces no value. */
+  void return_unit (HIR::Expr &expr)
   {
-    expr_return_expr = nullptr;
-    translated = place;
+    translated = ctx.place_db.get_constant (lookup_type (expr));
   }
 
-  void return_unit (HIR::Expr &expr)
+  PlaceId take_or_create_return_place (TyTy::BaseType *type)
   {
-    expr_return_expr = nullptr;
-    translated = ctx.place_db.get_constant (lookup_type (expr));
+    auto result = (expr_return_place != INVALID_PLACE)
+                   ? expr_return_place
+                   : ctx.place_db.add_temporary (type);
+    expr_return_place = INVALID_PLACE;
+    return result;
   }
 };
 
+/**
+ * Helper to convert a pointer to an optional. Maps nullptr to nullopt.
+ * Optionals are mainly used here to provide monadic operations (map) over
+ * possibly null pointers.
+ */
+template <typename T>
+tl::optional<T>
+optional_from_ptr (T ptr)
+{
+  if (ptr != nullptr)
+    return {ptr};
+  else
+    return tl::nullopt;
+}
+
 } // namespace BIR
 } // namespace Rust
 
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h 
b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h
index eb6a5efc1cfe..440549eba29b 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h
@@ -30,29 +30,28 @@ namespace BIR {
  * Special builder is needed to store short-circuiting context for directly
  * nested lazy boolean expressions.
  */
-class LazyBooleanExprBuilder : public AbstractBuilder,
-                              public HIR::HIRExpressionVisitor
+class LazyBooleanExprBuilder : public AbstractExprBuilder
 {
   BasicBlockId short_circuit_bb;
 
 public:
-  explicit LazyBooleanExprBuilder (BuilderContext &ctx)
-    : AbstractBuilder (ctx), short_circuit_bb (0)
+  explicit LazyBooleanExprBuilder (BuilderContext &ctx,
+                                  PlaceId expr_return_place = INVALID_PLACE)
+    : AbstractExprBuilder (ctx, expr_return_place), short_circuit_bb (0)
   {}
 
   PlaceId build (HIR::LazyBooleanExpr &expr)
   {
-    PlaceId return_place = ctx.place_db.add_temporary (lookup_type (expr));
+    PlaceId return_place = take_or_create_return_place (lookup_type (expr));
 
     short_circuit_bb = new_bb ();
-    visit (expr);
-    push_assignment (return_place, translated);
+    push_assignment (return_place, visit_expr (expr));
     auto final_bb = new_bb ();
     push_goto (final_bb);
 
     ctx.current_bb = short_circuit_bb;
-    translated = ctx.place_db.get_constant (lookup_type (expr));
-    push_assignment (return_place, translated);
+    push_assignment (return_place,
+                    ctx.place_db.get_constant (lookup_type (expr)));
     push_goto (final_bb);
 
     ctx.current_bb = final_bb;
@@ -62,11 +61,11 @@ public:
 protected:
   void visit (HIR::LazyBooleanExpr &expr) override
   {
-    expr.get_lhs ()->accept_vis (*this);
-    push_switch (translated, {short_circuit_bb});
+    auto lhs = visit_expr (*expr.get_lhs ());
+    push_switch (make_arg (lhs), {short_circuit_bb});
 
-    start_new_subsequent_bb ();
-    expr.get_rhs ()->accept_vis (*this);
+    start_new_consecutive_bb ();
+    return_place (visit_expr (*expr.get_rhs ()));
   }
   void visit (HIR::GroupedExpr &expr) override
   {
@@ -77,15 +76,15 @@ protected:
 public:
   void visit (HIR::QualifiedPathInExpression &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::PathInExpression &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::ClosureExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::StructExprStructFields &fields) override
   {
@@ -97,119 +96,119 @@ public:
   }
   void visit (HIR::LiteralExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::BorrowExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::DereferenceExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::ErrorPropagationExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::NegationExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::ArithmeticOrLogicalExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::ComparisonExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::TypeCastExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::AssignmentExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::CompoundAssignmentExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::ArrayExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::ArrayIndexExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::TupleExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::TupleIndexExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::CallExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::MethodCallExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::FieldAccessExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::BlockExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::UnsafeBlockExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::LoopExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::WhileLoopExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::WhileLetLoopExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::IfExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::IfExprConseqElse &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::IfLetExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::IfLetExprConseqElse &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::MatchExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::AwaitExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
   void visit (HIR::AsyncBlockExpr &expr) override
   {
-    translated = ExprStmtBuilder (ctx).build (expr);
+    return_place (ExprStmtBuilder (ctx).build (expr));
   }
 
 protected: // Illegal at this position.
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h 
b/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h
index 0b4c83eca844..0596264afc51 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h
@@ -23,16 +23,20 @@
 
 namespace Rust {
 namespace BIR {
-// Compiles binding of values into newly created variables.
-// Used in let, match arm, and function parameter patterns.
+
+/**
+ * Compiles binding of values into newly created variables.
+ * Used in let, match arm, and function parameter patterns.
+ */
 class PatternBindingBuilder : protected AbstractBuilder,
                              public HIR::HIRPatternVisitor
 {
+  /** Value of initialization expression. */
   PlaceId init;
-  // This is where lifetime annotations are stored.
+  /** This is where lifetime annotations are stored. */
   tl::optional<HIR::Type *> type;
 
-  // Emulates recursive stack saving and restoring inside a visitor.
+  /** Emulates recursive stack saving and restoring inside a visitor. */
   class SavedState
   {
     PatternBindingBuilder *builder;
@@ -55,21 +59,26 @@ class PatternBindingBuilder : protected AbstractBuilder,
 
 public:
   PatternBindingBuilder (BuilderContext &ctx, PlaceId init, HIR::Type *type)
-    : AbstractBuilder (ctx), init (init),
-      type (type ? tl::optional<HIR::Type *> (type) : tl::nullopt)
+    : AbstractBuilder (ctx), init (init), type (optional_from_ptr (type))
   {}
 
   void go (HIR::Pattern &pattern) { pattern.accept_vis (*this); }
 
-  void visit_identifier (const Analysis::NodeMapping &node, bool is_ref)
+  void visit_identifier (const Analysis::NodeMapping &node, bool is_ref,
+                        bool is_mut = false)
   {
-    translated = declare_variable (node);
     if (is_ref)
       {
+       translated = declare_variable (
+         node, new TyTy::ReferenceType (node.get_hirid (),
+                                        TyTy::TyVar (node.get_hirid ()),
+                                        (is_mut) ? Mutability::Mut
+                                                 : Mutability::Imm));
        push_assignment (translated, new BorrowExpr (init));
       }
     else
       {
+       translated = declare_variable (node);
        push_assignment (translated, init);
       }
     auto &init_place = ctx.place_db[init];
@@ -87,8 +96,10 @@ public:
   {
     // Top-level identifiers are resolved directly to avoid useless temporary
     // (for cleaner BIR).
-    visit_identifier (pattern.get_mappings (), pattern.get_is_ref ());
+    visit_identifier (pattern.get_mappings (), pattern.get_is_ref (),
+                     pattern.is_mut ());
   }
+
   void visit (HIR::ReferencePattern &pattern) override
   {
     SavedState saved (this);
@@ -104,6 +115,7 @@ public:
       = ctx.lookup_lifetime (ref_type.map (&HIR::ReferenceType::get_lifetime));
     pattern.get_referenced_pattern ()->accept_vis (*this);
   }
+
   void visit (HIR::SlicePattern &pattern) override
   {
     SavedState saved (this);
@@ -119,11 +131,13 @@ public:
        item->accept_vis (*this);
       }
   }
+
   void visit (HIR::AltPattern &pattern) override
   {
     rust_sorry_at (pattern.get_locus (),
                   "borrow-checking of alt patterns is not yet implemented");
   }
+
   void visit (HIR::StructPattern &pattern) override
   {
     SavedState saved (this);
@@ -181,12 +195,14 @@ public:
                                                   field_ty->get_field_type (),
                                                   saved.init, field_index);
              visit_identifier (ident_field->get_mappings (),
-                               ident_field->get_has_ref ());
+                               ident_field->get_has_ref (),
+                               ident_field->is_mut ());
              break;
            }
          }
       }
   }
+
   void visit_tuple_fields (std::vector<std::unique_ptr<HIR::Pattern>> &fields,
                           SavedState &saved, size_t &index)
   {
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h 
b/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h
index fa2f5965af68..7df54a4880aa 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h
@@ -29,6 +29,7 @@ namespace BIR {
 class StructBuilder : public AbstractBuilder, public HIR::HIRFullVisitor
 {
   TyTy::VariantDef *struct_ty;
+  /** Output of the builder. */
   std::vector<PlaceId> init_values;
 
 public:
@@ -47,34 +48,33 @@ public:
 
   void visit (HIR::StructExprFieldIdentifier &field) override
   {
-    resolve_variable (field, translated);
-    handle_named_field (field);
+    handle_named_field (field, resolve_variable (field));
   }
   void visit (HIR::StructExprFieldIdentifierValue &field) override
   {
-    translated = ExprStmtBuilder (ctx).build (*field.get_value ());
-    handle_named_field (field);
+    auto value = ExprStmtBuilder (ctx).build (*field.get_value ());
+    handle_named_field (field, value);
   }
   void visit (HIR::StructExprFieldIndexValue &field) override
   {
-    translated = ExprStmtBuilder (ctx).build (*field.get_value ());
-    coercion_site (translated,
+    auto value = ExprStmtBuilder (ctx).build (*field.get_value ());
+    coercion_site (value,
                   struct_ty->get_field_at_index (field.get_tuple_index ())
                     ->get_field_type ());
-    init_values.push_back (translated);
+    init_values.push_back (value);
   }
 
 private:
-  template <typename T> void handle_named_field (T &field)
+  template <typename T> void handle_named_field (T &field, PlaceId value)
   {
     size_t field_index;
     TyTy::StructFieldType *field_type;
     bool ok = struct_ty->lookup_field (field.get_field_name ().as_string (),
                                       &field_type, &field_index);
     rust_assert (ok);
-    rust_assert (translated != INVALID_PLACE);
-    coercion_site (translated, field_type->get_field_type ());
-    init_values.push_back (translated);
+    rust_assert (value != INVALID_PLACE);
+    coercion_site (value, field_type->get_field_type ());
+    init_values.push_back (value);
   }
 
 protected:
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder.h 
b/gcc/rust/checks/errors/borrowck/rust-bir-builder.h
index 322d00d280e5..177b65558899 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-builder.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder.h
@@ -74,12 +74,18 @@ private:
   void handle_body (HIR::BlockExpr &body)
   {
     translated = ExprStmtBuilder (ctx).build (body);
-    if (body.has_expr ())
+    if (body.has_expr () && !lookup_type (body)->is_unit ())
       {
        push_assignment (RETURN_VALUE_PLACE, translated);
        ctx.get_current_bb ().statements.emplace_back (Node::Kind::RETURN);
       }
-  }
+    else if (!ctx.get_current_bb ().is_terminated ())
+      {
+       push_assignment (RETURN_VALUE_PLACE,
+                        ctx.place_db.get_constant (lookup_type (body)));
+       ctx.get_current_bb ().statements.emplace_back (Node::Kind::RETURN);
+      }
+  };
 };
 
 } // namespace BIR
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc 
b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc
index 66870ddeb565..23aa7c6b078b 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc
@@ -114,24 +114,27 @@ Dump::go (bool enable_simplify_cfg)
   stream << "fn " << name << "(";
   print_comma_separated (stream, func.arguments, [this] (PlaceId place_id) {
     stream << "_" << place_map[place_id] << ": "
-          << get_tyty_name (place_db[place_id].tyty);
+          << get_tyty_name (func.place_db[place_id].tyty);
   });
-  stream << ") -> " << get_tyty_name (place_db[RETURN_VALUE_PLACE].tyty)
+  stream << ") -> " << get_tyty_name (func.place_db[RETURN_VALUE_PLACE].tyty)
         << " {\n";
 
-  for (PlaceId id = FIRST_VARIABLE_PLACE; id < place_db.size (); ++id)
+  // Print locals declaration.
+  for (PlaceId id = FIRST_VARIABLE_PLACE; id < func.place_db.size (); ++id)
     {
-      const Place &place = place_db[id];
+      const Place &place = func.place_db[id];
       if (place.kind == Place::VARIABLE || place.kind == Place::TEMPORARY)
        {
          if (std::find (func.arguments.begin (), func.arguments.end (), id)
              != func.arguments.end ())
            continue;
-         stream << indentation << "let _" << place_map[id] << ": "
-                << get_tyty_name (place_db[id].tyty) << ";\n";
+         stream << indentation << "let _";
+         stream << place_map[id] << ": "
+                << get_tyty_name (func.place_db[id].tyty) << ";\n";
        }
     }
 
+  // Print BBs.
   for (node_bb = 0; node_bb < func.basic_blocks.size (); ++node_bb)
     {
       if (bb_fold_map[node_bb] != node_bb)
@@ -141,6 +144,8 @@ Dump::go (bool enable_simplify_cfg)
          && func.basic_blocks[node_bb].successors.empty ())
        continue;
 
+      bb_terminated = false;
+
       BasicBlock &bb = func.basic_blocks[node_bb];
       stream << "\n";
       stream << indentation << "bb" << bb_fold_map[node_bb] << ": {\n";
@@ -150,10 +155,15 @@ Dump::go (bool enable_simplify_cfg)
          visit (stmt);
          stream << ";\n";
        }
+      if (!bb_terminated)
+       {
+         stream << indentation << indentation << "goto -> bb"
+                << bb_fold_map[bb.successors.at (0)] << ";\n";
+       }
       stream << indentation << "}\n";
     }
 
-  stream << "}\n\n";
+  stream << "}\n";
 }
 void
 Dump::visit (Node &node)
@@ -162,7 +172,8 @@ Dump::visit (Node &node)
   switch (node.get_kind ())
     {
       case Node::Kind::ASSIGNMENT: {
-       stream << "_" << place_map[node.get_place ()] << " = ";
+       visit_place (node.get_place ());
+       stream << " = ";
        node.get_expr ().accept_vis (*this);
        break;
       }
@@ -175,13 +186,16 @@ Dump::visit (Node &node)
                               stream << "bb" << bb_fold_map[succ];
                             });
       stream << "]";
+      bb_terminated = true;
       break;
     case Node::Kind::RETURN:
       stream << "return";
+      bb_terminated = true;
       break;
     case Node::Kind::GOTO:
       stream << "goto -> bb"
             << bb_fold_map[func.basic_blocks[node_bb].successors.at (0)];
+      bb_terminated = true;
       break;
     case Node::Kind::STORAGE_DEAD:
       stream << "StorageDead(";
@@ -200,7 +214,7 @@ Dump::visit (Node &node)
 void
 Dump::visit_place (PlaceId place_id)
 {
-  const Place &place = place_db[place_id];
+  const Place &place = func.place_db[place_id];
   switch (place.kind)
     {
     case Place::TEMPORARY:
@@ -237,8 +251,8 @@ Dump::visit_place (PlaceId place_id)
 void
 Dump::visit_move_place (PlaceId place_id)
 {
-  const Place &place = place_db[place_id];
-  if (!place.is_copy)
+  const Place &place = func.place_db[place_id];
+  if (place.is_rvalue || !place.is_copy)
     stream << "move ";
   visit_place (place_id);
 }
@@ -254,7 +268,7 @@ Dump::visit (BorrowExpr &expr)
 void
 Dump::visit_lifetime (PlaceId place_id)
 {
-  const Place &place = place_db[place_id];
+  const Place &place = func.place_db[place_id];
   if (place.lifetime.has_lifetime ())
     {
       if (place.lifetime.id == STATIC_LIFETIME_ID)
@@ -279,7 +293,7 @@ Dump::visit (CallExpr &expr)
 {
   stream << "Call(";
   if (auto fn_type
-      = place_db[expr.get_callable ()].tyty->try_as<TyTy::FnType> ())
+      = func.place_db[expr.get_callable ()].tyty->try_as<TyTy::FnType> ())
     {
       stream << fn_type->get_identifier ();
     }
@@ -288,18 +302,17 @@ Dump::visit (CallExpr &expr)
       visit_move_place (expr.get_callable ());
     }
   stream << ")(";
-  for (auto &place : expr.get_arguments ())
-    {
-      visit_move_place (place);
-      stream << ", ";
-    }
+  print_comma_separated (stream, expr.get_arguments (),
+                        [this] (PlaceId place_id) {
+                          visit_move_place (place_id);
+                        });
   stream << ") -> [";
-  print_comma_separated (stream,
-                        func.basic_blocks[bb_fold_map[node_bb]].successors,
-                        [this] (const BasicBlockId &dst) {
-                          stream << "bb" << bb_fold_map[dst];
+  print_comma_separated (stream, func.basic_blocks[node_bb].successors,
+                        [this] (BasicBlockId succ) {
+                          stream << "bb" << bb_fold_map[succ];
                         });
   stream << "]";
+  bb_terminated = true;
 }
 
 void
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h 
b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h
index edf7d1ea1ec3..1efc0ea2bf46 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h
@@ -28,10 +28,10 @@
 namespace Rust {
 namespace BIR {
 
+/** Prints the BIR to a stream in a format resembling rustc's MIR. */
 class Dump : public Visitor
 {
   std::ostream &stream;
-  const PlaceDB &place_db;
   Function &func;
   const std::string &name;
 
@@ -40,10 +40,11 @@ class Dump : public Visitor
 
   PlaceId node_place = INVALID_PLACE;
   BasicBlockId node_bb = INVALID_BB;
+  bool bb_terminated = false;
 
 public:
   Dump (std::ostream &os, Function &func, const std::string &name)
-    : stream (os), place_db (func.place_db), func (func), name (name),
+    : stream (os), func (func), name (name),
       bb_fold_map (func.basic_blocks.size ()), place_map (func.place_db.size 
())
   {}
   void go (bool enable_simplify_cfg = false);
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-place.h 
b/gcc/rust/checks/errors/borrowck/rust-bir-place.h
index ce32f9262ceb..66b131dee1a7 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-place.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-place.h
@@ -148,30 +148,9 @@ public:
       0);
   }
 
-  PlaceId lookup_variable (NodeId id)
-  {
-    PlaceId current = FIRST_VARIABLE_PLACE;
-
-    while (current != places.size ())
-      {
-       if (places[current].kind == Place::VARIABLE
-           && places[current].variable_or_field_index == id)
-         return current;
-       current++;
-      }
-    return INVALID_PLACE;
-  };
-
-  PlaceId add_temporary (TyTy::BaseType *tyty)
-  {
-    return add_place (
-      {Place::TEMPORARY, 0, {}, is_type_copy (tyty), false, NO_LIFETIME, tyty},
-      0);
-  }
-
-  WARN_UNUSED_RESULT PlaceId lookup_or_add_path (Place::Kind kind,
-                                                TyTy::BaseType *tyty,
-                                                PlaceId parent, size_t id = 0)
+  [[nodiscard]] PlaceId lookup_or_add_path (Place::Kind kind,
+                                           TyTy::BaseType *tyty,
+                                           PlaceId parent, size_t id = 0)
   {
     PlaceId current = 0;
     if (parent < places.size ())
@@ -193,6 +172,13 @@ public:
       current);
   }
 
+  PlaceId add_temporary (TyTy::BaseType *tyty)
+  {
+    return add_place (
+      {Place::TEMPORARY, 0, {}, is_type_copy (tyty), false, NO_LIFETIME, tyty},
+      0);
+  }
+
   PlaceId get_constant (TyTy::BaseType *tyty)
   {
     auto lookup = constants_lookup.find (tyty);
@@ -206,6 +192,44 @@ public:
     return places.size () - 1;
   }
 
+  PlaceId lookup_variable (NodeId id)
+  {
+    PlaceId current = FIRST_VARIABLE_PLACE;
+
+    while (current != places.size ())
+      {
+       if (places[current].kind == Place::VARIABLE
+           && places[current].variable_or_field_index == id)
+         return current;
+       current++;
+      }
+    return INVALID_PLACE;
+  };
+
+  PlaceId lookup_or_add_variable (NodeId id, TyTy::BaseType *tyty)
+  {
+    auto lookup = lookup_variable (id);
+    if (lookup != INVALID_PLACE)
+      return lookup;
+    places.push_back (
+      {Place::VARIABLE, id, {}, is_type_copy (tyty), false, NO_LIFETIME, 
tyty});
+    return places.size () - 1;
+  };
+
+  PlaceId into_rvalue (PlaceId place)
+  {
+    if (places[place].is_rvalue || places[place].kind == Place::CONSTANT
+       || places[place].tyty->get_kind () == TyTy::REF)
+      return place;
+    return add_place ({Place::TEMPORARY,
+                      0,
+                      {},
+                      places[place].is_copy,
+                      true,
+                      NO_LIFETIME,
+                      places[place].tyty});
+  }
+
 private:
   static bool is_type_copy (TyTy::BaseType *ty)
   {

Reply via email to