https://gcc.gnu.org/g:85e795910cf794ea9b32f94f2d987861285685f8
commit r17-4312-g85e795910cf794ea9b32f94f2d987861285685f8 Author: Lishin <[email protected]> Date: Thu Aug 13 17:40:31 2026 +0000 gccrs: Generate backend drop flags for conditional moves 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. Diff: --- 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 +++ .../errors/borrowck/rust-bir-builder-expr-stmt.cc | 3 +- .../errors/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(-) diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h index 2c71f7778a55..d3d4ec744929 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 e0947a787ca4..8cd2bb9f2ac8 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 36e3cdc1e356..6d91d3124f66 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 ca4e63f88274..7e54b4cf9d35 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 9d722358bb2c..82cabef784b0 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 7f6e81ff803d..78b87b5b4dc2 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 b58f2cfe919b..889e4611eca0 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 eabfdc6af4cb..b5644741d9eb 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 2bdf5e5e4dd6..22be1553eee5 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 17a175e08d6f..45cb1e2f8714 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 581f661655dc..f03cdcc14239 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 000000000000..cb28625f976b --- /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
