From: Lishin <[email protected]>

Generate runtime Drop flags for Conditional Drops.
Use BIR analysis results to identify locals that need Drop flags and the moves 
that clear those flags.
The HIR backend sets a flag when initialization, clears it after a move, and 
checks it before cleanup.

gcc/rust/ChangeLog:

        * backend/rust-compile-context.h
        (Context::insert_drop_flag): New function.
        (Context::lookup_drop_flag): Likewise.
        (Context::drop_flags): New member.
        * backend/rust-compile-drop-builder.cc
        (DropBuilder::maybe_create_drop_flag): New function.
        (DropBuilder::drop_flag_assignment): Likewise.
        * backend/rust-compile-drop-builder.h
        (DropBuilder::maybe_create_drop_flag): New declaration.
        (DropBuilder::drop_flag_assignment): Likewise.
        * backend/rust-compile-drop.cc
        (CompileDrop::build_current_scope_drop_cleanup): Check Drop flags before
        running conditional Drops.
        * backend/rust-compile-pattern.cc
        (CompilePatternLet::visit): Set the Drop flag after initialization.
        * backend/rust-compile-stmt.cc
        (CompileStmt::visit): Create Drop flags and clear them after moves.
        * checks/errors/borrowck/rust-bir-builder-expr-stmt.cc
        (ExprStmtBuilder::visit): Pass expression HirIds to BIR.
        * checks/errors/borrowck/rust-bir-builder-internal.h
        (AbstractBuilder::push_assignment): Pass move-site HirIds.
        (AbstractExprBuilder::return_place): Likewise.
        * checks/errors/borrowck/rust-bir-drop-analysis.cc
        (is_straight_line): Remove.
        (record_drop_for_backend): Rename from
        record_drop_for_straight_line_backend.
        (annotate_drop_statements): Record conditional Drops and move
        sources.
        (DropAnalysis::clear): Clear the new analysis results.
        (DropAnalysis::needs_drop_flag): New function.
        (DropAnalysis::lookup_move_source): Likewise.
        (DropAnalysis::analyze): Record results for the backend.
        * checks/errors/borrowck/rust-bir-drop-analysis.h
        (DropAnalysis::needs_drop_flag): New declaration.
        (DropAnalysis::lookup_move_source): Likewise.
        (DropAnalysis::conditionally_dropped): New member.
        (DropAnalysis::move_sources): Likewise.
        * checks/errors/borrowck/rust-bir.h
        (Statement::make_assignment): Accept a move-site HirId.
        (Statement::Statement): Likewise.
        (Statement::get_move_site): New function.
        (Statement::move_site): New member.

gcc/testsuite/ChangeLog:

        * rust/execute/drop-conditional-move.rs: New test.
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/074613a4207f67b622a5ebca38c9441bce0006e5

The commit has NOT been mentioned in any issue.

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4798

 gcc/rust/backend/rust-compile-context.h       |  16 +++
 gcc/rust/backend/rust-compile-drop-builder.cc |  36 ++++-
 gcc/rust/backend/rust-compile-drop-builder.h  |   4 +-
 gcc/rust/backend/rust-compile-drop.cc         |  16 ++-
 gcc/rust/backend/rust-compile-pattern.cc      |   8 +-
 gcc/rust/backend/rust-compile-stmt.cc         |  18 +++
 .../borrowck/rust-bir-builder-expr-stmt.cc    |   3 +-
 .../borrowck/rust-bir-builder-internal.h      |  15 +-
 .../errors/borrowck/rust-bir-drop-analysis.cc | 132 +++++++++++-------
 .../errors/borrowck/rust-bir-drop-analysis.h  |  10 +-
 gcc/rust/checks/errors/borrowck/rust-bir.h    |  20 ++-
 .../rust/execute/drop-conditional-move.rs     | 100 +++++++++++++
 12 files changed, 312 insertions(+), 66 deletions(-)
 create mode 100644 gcc/testsuite/rust/execute/drop-conditional-move.rs

diff --git a/gcc/rust/backend/rust-compile-context.h 
b/gcc/rust/backend/rust-compile-context.h
index 2c71f7778..d3d4ec744 100644
--- a/gcc/rust/backend/rust-compile-context.h
+++ b/gcc/rust/backend/rust-compile-context.h
@@ -145,6 +145,21 @@ public:
     return true;
   }
 
+  void insert_drop_flag (HirId id, ::Bvariable *flag)
+  {
+    drop_flags[{peek_fn ().fndecl, id}] = flag;
+  }
+
+  bool lookup_drop_flag (HirId id, ::Bvariable **flag)
+  {
+    auto it = drop_flags.find ({peek_fn ().fndecl, id});
+    if (it == drop_flags.end ())
+      return false;
+
+    *flag = it->second;
+    return true;
+  }
+
   void insert_function_decl (const TyTy::FnType *ref, tree fn)
   {
     auto id = ref->get_ty_ref ();
@@ -504,6 +519,7 @@ private:
   // state
   std::vector<fncontext> fn_stack;
   std::map<HirId, ::Bvariable *> compiled_var_decls;
+  std::map<std::pair<tree, HirId>, ::Bvariable *> drop_flags;
   std::map<hashval_t, tree> compiled_type_map;
   std::map<HirId, tree> compiled_fn_map;
   std::map<HirId, tree> compiled_consts;
diff --git a/gcc/rust/backend/rust-compile-drop-builder.cc 
b/gcc/rust/backend/rust-compile-drop-builder.cc
index e0947a787..8cd2bb9f2 100644
--- a/gcc/rust/backend/rust-compile-drop-builder.cc
+++ b/gcc/rust/backend/rust-compile-drop-builder.cc
@@ -18,6 +18,7 @@
 
 #include "rust-compile-drop-builder.h"
 #include "rust-compile-context.h"
+#include "rust-bir-drop-analysis.h"
 
 namespace Rust {
 namespace Compile {
@@ -31,6 +32,39 @@ DropBuilder::note_simple_drop_candidate (HirId hirid, 
location_t locus)
   ctx.block_drop_candidates.back ().emplace_back (hirid, locus);
 }
 
+void
+DropBuilder::maybe_create_drop_flag (HirId hirid, location_t locus,
+                                    bool initialized)
+{
+  if (!BIR::DropAnalysis::get ().needs_drop_flag (hirid))
+    return;
+
+  Bvariable *existing = nullptr;
+  if (ctx.lookup_drop_flag (hirid, &existing))
+    return;
+
+  tree declaration = nullptr;
+  Bvariable *flag = Backend::temporary_variable (
+    ctx.peek_fn ().fndecl, nullptr, boolean_type_node,
+    Backend::boolean_constant_expression (initialized), false, locus,
+    &declaration);
+  ctx.add_statement (declaration);
+  ctx.insert_drop_flag (hirid, flag);
+}
+
+tree
+DropBuilder::drop_flag_assignment (HirId hirid, bool value, location_t locus)
+{
+  Bvariable *flag = nullptr;
+  if (!ctx.lookup_drop_flag (hirid, &flag))
+    return nullptr;
+
+  return Backend::assignment_statement (Backend::var_expression (flag, locus),
+                                       Backend::boolean_constant_expression (
+                                         value),
+                                       locus);
+}
+
 std::vector<DropCandidate> &
 DropBuilder::peek_block_drop_candidates ()
 {
@@ -39,4 +73,4 @@ DropBuilder::peek_block_drop_candidates ()
 }
 
 } // namespace Compile
-} // namespace Rust
\ No newline at end of file
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-drop-builder.h 
b/gcc/rust/backend/rust-compile-drop-builder.h
index 36e3cdc1e..6d91d3124 100644
--- a/gcc/rust/backend/rust-compile-drop-builder.h
+++ b/gcc/rust/backend/rust-compile-drop-builder.h
@@ -32,6 +32,8 @@ public:
   DropBuilder (Context &ctx);
 
   void note_simple_drop_candidate (HirId hirid, location_t locus);
+  void maybe_create_drop_flag (HirId hirid, location_t locus, bool 
initialized);
+  tree drop_flag_assignment (HirId hirid, bool value, location_t locus);
   std::vector<DropCandidate> &peek_block_drop_candidates ();
 
 private:
@@ -41,4 +43,4 @@ private:
 } // namespace Compile
 } // namespace Rust
 
-#endif // RUST_COMPILE_DROP_BUILDER_H
\ No newline at end of file
+#endif // RUST_COMPILE_DROP_BUILDER_H
diff --git a/gcc/rust/backend/rust-compile-drop.cc 
b/gcc/rust/backend/rust-compile-drop.cc
index ca4e63f88..7e54b4cf9 100644
--- a/gcc/rust/backend/rust-compile-drop.cc
+++ b/gcc/rust/backend/rust-compile-drop.cc
@@ -115,7 +115,21 @@ CompileDrop::build_current_scope_drop_cleanup ()
 
       tree drop_call = compile_drop_call (var, ty, it->locus);
       if (drop_call != NULL_TREE)
-       drop_stmts.push_back (convert_to_void (drop_call, ICV_STATEMENT));
+       {
+         tree drop_stmt = convert_to_void (drop_call, ICV_STATEMENT);
+         Bvariable *flag = nullptr;
+         if (ctx->lookup_drop_flag (it->hirid, &flag))
+           {
+             tree condition = Backend::var_expression (flag, it->locus);
+             tree clear = drop_builder.drop_flag_assignment (it->hirid, false,
+                                                             it->locus);
+             tree guarded_drop = Backend::statement_list ({clear, drop_stmt});
+             drop_stmt
+               = Backend::if_statement (ctx->peek_fn ().fndecl, condition,
+                                        guarded_drop, NULL_TREE, it->locus);
+           }
+         drop_stmts.push_back (drop_stmt);
+       }
     }
 
   if (drop_stmts.empty ())
diff --git a/gcc/rust/backend/rust-compile-pattern.cc 
b/gcc/rust/backend/rust-compile-pattern.cc
index 9d722358b..82cabef78 100644
--- a/gcc/rust/backend/rust-compile-pattern.cc
+++ b/gcc/rust/backend/rust-compile-pattern.cc
@@ -1353,6 +1353,13 @@ CompilePatternLet::visit (HIR::IdentifierPattern 
&pattern)
       ctx->add_statement (s);
     }
 
+  DropBuilder drop_builder (*ctx);
+  tree set_drop_flag
+    = drop_builder.drop_flag_assignment (pattern.get_mappings ().get_hirid (),
+                                        true, pattern.get_locus ());
+  if (set_drop_flag != nullptr)
+    ctx->add_statement (set_drop_flag);
+
   TyTy::BaseType *drop_ty = ty;
   if (pattern.get_is_ref ())
     {
@@ -1366,7 +1373,6 @@ CompilePatternLet::visit (HIR::IdentifierPattern &pattern)
 
   if (!pattern.has_subpattern () && !pattern.get_is_ref ())
     {
-      DropBuilder drop_builder (*ctx);
       drop_builder.note_simple_drop_candidate (
        pattern.get_mappings ().get_hirid (), pattern.get_locus ());
     }
diff --git a/gcc/rust/backend/rust-compile-stmt.cc 
b/gcc/rust/backend/rust-compile-stmt.cc
index 7f6e81ff8..78b87b5b4 100644
--- a/gcc/rust/backend/rust-compile-stmt.cc
+++ b/gcc/rust/backend/rust-compile-stmt.cc
@@ -21,6 +21,9 @@
 #include "rust-compile-expr.h"
 #include "rust-compile-type.h"
 #include "rust-compile-var-decl.h"
+#include "rust-compile-drop.h"
+#include "rust-compile-drop-builder.h"
+#include "rust-bir-drop-analysis.h"
 
 namespace Rust {
 namespace Compile {
@@ -67,6 +70,11 @@ CompileStmt::visit (HIR::LetStmt &stmt)
   tree translated_type = TyTyResolveCompile::compile (ctx, ty);
   CompileVarDecl::compile (fndecl, translated_type, &stmt_pattern, ctx);
 
+  if (stmt_pattern.get_pattern_type () == HIR::Pattern::IDENTIFIER
+      && CompileDrop (ctx).type_has_drop_impl (ty))
+    DropBuilder (*ctx).maybe_create_drop_flag (stmt_id, stmt.get_locus (),
+                                              false);
+
   // nothing to do
   if (!stmt.has_init_expr ())
     return;
@@ -89,6 +97,16 @@ CompileStmt::visit (HIR::LetStmt &stmt)
                        expected, lvalue_locus, rvalue_locus);
 
   CompilePatternLet::Compile (&stmt_pattern, init, ty, rvalue_locus, ctx);
+
+  HirId source = UNKNOWN_HIRID;
+  if (BIR::DropAnalysis::get ().lookup_move_source (
+       stmt.get_init_expr ().get_mappings ().get_hirid (), &source))
+    {
+      tree clear
+       = DropBuilder (*ctx).drop_flag_assignment (source, false, rvalue_locus);
+      if (clear != nullptr)
+       ctx->add_statement (clear);
+    }
 }
 
 } // namespace Compile
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 b58f2cfe9..889e4611e 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
@@ -703,7 +703,8 @@ ExprStmtBuilder::visit (HIR::PathInExpression &expr)
 {
   // Note: Type is only stored for the expr, not the segment.
   PlaceId result = resolve_variable_or_fn (expr, lookup_type (expr));
-  return_place (result, expr.get_locus ());
+  return_place (result, expr.get_locus (), false,
+               expr.get_mappings ().get_hirid ());
 }
 
 void
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 eabfdc6af..b5644741d 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h
@@ -265,16 +265,18 @@ protected:
   }
 
 protected: // Helpers to add BIR statements
-  void push_assignment (PlaceId lhs, AbstractExpr *rhs, location_t location)
+  void push_assignment (PlaceId lhs, AbstractExpr *rhs, location_t location,
+                       tl::optional<HirId> move_site = tl::nullopt)
   {
     ctx.get_current_bb ().statements.push_back (
-      Statement::make_assignment (lhs, rhs, location));
+      Statement::make_assignment (lhs, rhs, location, move_site));
     translated = lhs;
   }
 
-  void push_assignment (PlaceId lhs, PlaceId rhs, location_t location)
+  void push_assignment (PlaceId lhs, PlaceId rhs, location_t location,
+                       tl::optional<HirId> move_site = tl::nullopt)
   {
-    push_assignment (lhs, new Assignment (rhs), location);
+    push_assignment (lhs, new Assignment (rhs), location, move_site);
   }
 
   void push_tmp_assignment (AbstractExpr *rhs, TyTy::BaseType *tyty,
@@ -600,12 +602,13 @@ protected:
   }
 
   /** Mark place to be a result of processed subexpression. */
-  void return_place (PlaceId place, location_t location, bool can_panic = 
false)
+  void return_place (PlaceId place, location_t location, bool can_panic = 
false,
+                    tl::optional<HirId> move_site = tl::nullopt)
   {
     if (expr_return_place != INVALID_PLACE)
       {
        // Return place is already allocated, no need to defer assignment.
-       push_assignment (expr_return_place, place, location);
+       push_assignment (expr_return_place, place, location, move_site);
       }
     else
       {
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-drop-analysis.cc 
b/gcc/rust/checks/errors/borrowck/rust-bir-drop-analysis.cc
index 2bdf5e5e4..22be1553e 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-drop-analysis.cc
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-drop-analysis.cc
@@ -38,31 +38,13 @@ struct BlockInitializationState
   bool reachable;
 };
 
-static bool
-is_straight_line (const Function &function)
+struct DropAnalysisResults
 {
-  std::set<BasicBlockId> visited;
-  BasicBlockId current = ENTRY_BASIC_BLOCK;
-
-  while (current != INVALID_BB)
-    {
-      // Revisiting a block means that the CFG contains a cycle.
-      if (!visited.insert (current).second)
-       return false;
-
-      const BasicBlock &block = function.basic_blocks[current];
-
-      if (block.successors.empty ())
-       return true;
-
-      if (block.successors.size () != 1)
-       return false;
-
-      current = block.successors.front ();
-    }
-
-  return true;
-}
+  std::unordered_set<HirId> dead_drop_hir_ids;
+  std::unordered_set<HirId> static_drop_hir_ids;
+  std::unordered_set<HirId> conditional_drop_hir_ids;
+  std::unordered_map<HirId, HirId> move_sources;
+};
 
 static void
 set_initialized (BlockInitializationState &state, PlaceId place)
@@ -247,10 +229,9 @@ compute_entry_states (Function &function)
 }
 
 static void
-record_drop_for_straight_line_backend (const Function &function, PlaceId place,
-                                      Statement::DropStyle drop_style,
-                                      std::set<HirId> &dead_drop_hir_ids,
-                                      std::set<HirId> &non_dead_drop_hir_ids)
+record_drop_for_backend (const Function &function, PlaceId place,
+                        Statement::DropStyle drop_style,
+                        DropAnalysisResults &results)
 {
   const Place &dropped_place = function.place_db[place];
 
@@ -263,10 +244,23 @@ record_drop_for_straight_line_backend (const Function 
&function, PlaceId place,
   if (!hir_id.has_value ())
     return;
 
-  if (drop_style == Statement::DropStyle::DEAD)
-    dead_drop_hir_ids.insert (hir_id.value ());
-  else
-    non_dead_drop_hir_ids.insert (hir_id.value ());
+  switch (drop_style)
+    {
+    case Statement::DropStyle::UNCLASSIFIED:
+      break;
+
+    case Statement::DropStyle::DEAD:
+      results.dead_drop_hir_ids.insert (hir_id.value ());
+      break;
+
+    case Statement::DropStyle::STATIC:
+      results.static_drop_hir_ids.insert (hir_id.value ());
+      break;
+
+    case Statement::DropStyle::CONDITIONAL:
+      results.conditional_drop_hir_ids.insert (hir_id.value ());
+      break;
+    }
 }
 
 // Walk each reachable block forward from its stable entry state and classify
@@ -274,8 +268,7 @@ record_drop_for_straight_line_backend (const Function 
&function, PlaceId place,
 static void
 annotate_drop_statements (
   Function &function, const std::vector<BlockInitializationState> 
&entry_states,
-  bool record_straight_line_backend_drops, std::set<HirId> &dead_drop_hir_ids,
-  std::set<HirId> &non_dead_drop_hir_ids)
+  DropAnalysisResults &results)
 {
   const size_t block_count = function.basic_blocks.size ();
 
@@ -291,6 +284,29 @@ annotate_drop_statements (
 
       for (Statement &statement : block.statements)
        {
+         const auto &move_site = statement.get_move_site ();
+         if (statement.get_kind () == Statement::Kind::ASSIGNMENT
+             && move_site.has_value ())
+           {
+             AbstractExpr &expr = statement.get_expr ();
+             if (expr.get_kind () == ExprKind::ASSIGNMENT)
+               {
+                 PlaceId rhs = static_cast<Assignment &> (expr).get_rhs ();
+                 const Place &rhs_place = function.place_db[rhs];
+                 if (rhs_place.kind == Place::VARIABLE
+                     && rhs_place.should_be_moved ())
+                   {
+                     auto hirid
+                       = Analysis::Mappings::get ().lookup_node_to_hir (
+                         static_cast<NodeId> (
+                           rhs_place.variable_or_field_index));
+                     if (hirid.has_value ())
+                       results.move_sources[move_site.value ()]
+                         = hirid.value ();
+                   }
+               }
+           }
+
          // A Drop is classified using the state before it executes.
          if (statement.get_kind () == Statement::Kind::DROP)
            {
@@ -299,11 +315,7 @@ annotate_drop_statements (
 
              statement.set_drop_style (drop_style);
 
-             if (record_straight_line_backend_drops)
-               record_drop_for_straight_line_backend (function, place,
-                                                      drop_style,
-                                                      dead_drop_hir_ids,
-                                                      non_dead_drop_hir_ids);
+             record_drop_for_backend (function, place, drop_style, results);
            }
 
          // Update the state for the following statement.
@@ -325,6 +337,8 @@ void
 DropAnalysis::clear ()
 {
   definitely_dead.clear ();
+  conditionally_dropped.clear ();
+  move_sources.clear ();
 }
 
 bool
@@ -333,26 +347,44 @@ DropAnalysis::is_definitely_dead (HirId id) const
   return definitely_dead.find (id) != definitely_dead.end ();
 }
 
+bool
+DropAnalysis::needs_drop_flag (HirId id) const
+{
+  return conditionally_dropped.find (id) != conditionally_dropped.end ();
+}
+
+bool
+DropAnalysis::lookup_move_source (HirId move_site, HirId *source) const
+{
+  auto it = move_sources.find (move_site);
+  if (it == move_sources.end ())
+    return false;
+
+  *source = it->second;
+  return true;
+}
+
 void
 DropAnalysis::analyze (Function &function)
 {
   std::vector<BlockInitializationState> entry_states
     = compute_entry_states (function);
 
-  // Keep the existing backend handling for straight-line CFGs.
-  const bool record_straight_line_backend_drops = is_straight_line (function);
-
-  std::set<HirId> dead_drop_hir_ids;
-  std::set<HirId> non_dead_drop_hir_ids;
-
-  annotate_drop_statements (function, entry_states,
-                           record_straight_line_backend_drops,
-                           dead_drop_hir_ids, non_dead_drop_hir_ids);
+  DropAnalysisResults results;
+  annotate_drop_statements (function, entry_states, results);
 
   // A local is definitely dead only when all of its Drops are dead.
-  for (HirId hir_id : dead_drop_hir_ids)
-    if (non_dead_drop_hir_ids.find (hir_id) == non_dead_drop_hir_ids.end ())
+  for (HirId hir_id : results.dead_drop_hir_ids)
+    if (results.static_drop_hir_ids.find (hir_id)
+         == results.static_drop_hir_ids.end ()
+       && results.conditional_drop_hir_ids.find (hir_id)
+            == results.conditional_drop_hir_ids.end ())
       definitely_dead.insert (hir_id);
+
+  conditionally_dropped.insert (results.conditional_drop_hir_ids.begin (),
+                               results.conditional_drop_hir_ids.end ());
+  move_sources.insert (results.move_sources.begin (),
+                      results.move_sources.end ());
 }
 
 } // namespace BIR
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-drop-analysis.h 
b/gcc/rust/checks/errors/borrowck/rust-bir-drop-analysis.h
index 17a175e08..45cb1e2f8 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir-drop-analysis.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir-drop-analysis.h
@@ -19,6 +19,7 @@
 #ifndef RUST_BIR_DROP_ANALYSIS_H
 #define RUST_BIR_DROP_ANALYSIS_H
 
+#include "rust-system.h"
 #include "rust-bir.h"
 
 namespace Rust {
@@ -39,9 +40,16 @@ public:
   void analyze (Function &function);
 
   bool is_definitely_dead (HirId id) const;
+  bool needs_drop_flag (HirId id) const;
+  bool lookup_move_source (HirId move_site, HirId *source) const;
 
 private:
-  std::set<HirId> definitely_dead;
+  std::unordered_set<HirId> definitely_dead;
+  std::unordered_set<HirId> conditionally_dropped;
+  // This may need to be extended to
+  // std::unordered_map<HirId, std::unordered_set<HirId>>
+  // to support product moves in the future.
+  std::unordered_map<HirId, HirId> move_sources;
 };
 
 } // namespace BIR
diff --git a/gcc/rust/checks/errors/borrowck/rust-bir.h 
b/gcc/rust/checks/errors/borrowck/rust-bir.h
index 581f66165..f03cdcc14 100644
--- a/gcc/rust/checks/errors/borrowck/rust-bir.h
+++ b/gcc/rust/checks/errors/borrowck/rust-bir.h
@@ -21,6 +21,7 @@
 
 #include "rust-bir-place.h"
 #include "rust-bir-visitor.h"
+#include "optional.h"
 
 #include "polonius/rust-polonius-ffi.h"
 #include "rust-tyty-variance-analysis.h"
@@ -113,12 +114,17 @@ private:
   // currently only available when kind is ASSIGNMENT | RETURN
   // FIXME: Add location for other statement kinds
   location_t location;
+  // HIR expression which consumes the RHS of an assignment.  This is used to
+  // attach backend drop-flag updates to the corresponding expression.
+  tl::optional<HirId> move_site;
 
 public:
   static Statement make_assignment (PlaceId place, AbstractExpr *rhs,
-                                   location_t location)
+                                   location_t location,
+                                   tl::optional<HirId> move_site = tl::nullopt)
   {
-    return Statement (Kind::ASSIGNMENT, place, rhs, nullptr, location);
+    return Statement (Kind::ASSIGNMENT, place, rhs, nullptr, location,
+                     move_site);
   }
   static Statement make_switch (PlaceId place)
   {
@@ -155,8 +161,10 @@ private:
   // compelete constructor, used by make_* functions
   Statement (Kind kind, PlaceId place = INVALID_PLACE,
             AbstractExpr *rhs = nullptr, TyTy::BaseType *type = nullptr,
-            location_t location = UNKNOWN_LOCATION)
-    : kind (kind), place (place), expr (rhs), type (type), location (location)
+            location_t location = UNKNOWN_LOCATION,
+            tl::optional<HirId> move_site = tl::nullopt)
+    : kind (kind), place (place), expr (rhs), type (type), location (location),
+      move_site (move_site)
   {}
 
 public:
@@ -167,6 +175,10 @@ public:
   WARN_UNUSED_RESULT AbstractExpr &get_expr () const { return *expr; }
   WARN_UNUSED_RESULT TyTy::BaseType *get_type () const { return type; }
   WARN_UNUSED_RESULT location_t get_location () const { return location; }
+  WARN_UNUSED_RESULT const tl::optional<HirId> &get_move_site () const
+  {
+    return move_site;
+  }
 };
 
 struct BasicBlock
diff --git a/gcc/testsuite/rust/execute/drop-conditional-move.rs 
b/gcc/testsuite/rust/execute/drop-conditional-move.rs
new file mode 100644
index 000000000..cb28625f9
--- /dev/null
+++ b/gcc/testsuite/rust/execute/drop-conditional-move.rs
@@ -0,0 +1,100 @@
+// { dg-output "^drop\r*\nafter conditional\r*\nafter 
conditional\r*\ndrop\r*\nafter static\r*\ndrop\r*\nafter 
static\r*\ndrop\r*\ndrop\r*\ndrop\r*\n$" }
+// { dg-additional-options "-frust-borrowcheck -w" }
+
+#![feature(no_core)]
+#![feature(lang_items)]
+#![no_core]
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "drop"]
+pub trait Drop {
+    fn drop(&mut self);
+}
+
+struct Droppable {
+    value: i32,
+}
+
+struct AfterConditional {
+    value: i32,
+}
+
+struct AfterStatic {
+    value: i32,
+}
+
+impl Drop for Droppable {
+    fn drop(&mut self) {
+        let msg = "drop\n\0" as *const str as *const i8;
+        unsafe {
+            printf(msg);
+        }
+    }
+}
+
+impl Drop for AfterConditional {
+    fn drop(&mut self) {
+        let msg = "after conditional\n\0" as *const str as *const i8;
+        unsafe {
+            printf(msg);
+        }
+    }
+}
+
+impl Drop for AfterStatic {
+    fn drop(&mut self) {
+        let msg = "after static\n\0" as *const str as *const i8;
+        unsafe {
+            printf(msg);
+        }
+    }
+}
+
+fn conditional_move(condition: bool) {
+    let x = Droppable { value: 1 };
+
+    if condition {
+        let _y = x;
+    }
+
+    let _after = AfterConditional { value: 0 };
+}
+
+fn static_after_join(condition: bool) {
+    let _x = Droppable { value: 2 };
+
+    if condition {
+        let _n = 1;
+    }
+
+    let _after = AfterStatic { value: 0 };
+}
+
+fn move_on_both_branches(condition: bool) {
+    let x = Droppable { value: 3 };
+
+    if condition {
+        let _y = x;
+    } else {
+        let _z = x;
+    }
+}
+
+fn main() -> i32 {
+    conditional_move(true);
+    conditional_move(false);
+
+    static_after_join(true);
+    static_after_join(false);
+
+    move_on_both_branches(true);
+    move_on_both_branches(false);
+
+    0
+}
\ No newline at end of file

base-commit: 63ea23dca8c1175c3e2b6a069f3ce1396196cee9
-- 
2.55.0

Reply via email to