https://gcc.gnu.org/g:93442d52b78f281a1a9764e86c387662de04c4d8
commit r17-3089-g93442d52b78f281a1a9764e86c387662de04c4d8 Author: Lishin <[email protected]> Date: Wed Jun 24 13:02:20 2026 +0000 gccrs: Evaluate non-unit tail expressions before drops A non-unit tail call could run after local drops. Store the tail expression result before emitting drops, then return the stored value. gcc/rust/ChangeLog: * backend/rust-compile-base.cc (HIRCompileBase::compile_function_body): Store non-unit tail expression results before emitting current scope drops. gcc/testsuite/ChangeLog: * rust/execute/drop-function-scope-non-unit-tail.rs: New test. Signed-off-by: Lishin <[email protected]> Diff: --- gcc/rust/backend/rust-compile-base.cc | 22 ++++++++++- .../execute/drop-function-scope-non-unit-tail.rs | 46 ++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/gcc/rust/backend/rust-compile-base.cc b/gcc/rust/backend/rust-compile-base.cc index 1a6ce99f42f2..e049e18a4604 100644 --- a/gcc/rust/backend/rust-compile-base.cc +++ b/gcc/rust/backend/rust-compile-base.cc @@ -708,10 +708,30 @@ HIRCompileBase::compile_function_body (tree fndecl, return_value = coercion_site (id, return_value, actual, expected, lvalue_locus, rvalue_locus); + /* Save the non-unit tail expression result before emitting scope + drops, so a tail call like foo() is evaluated before locals are + dropped. Conceptually, this changes lowering from: + + drop (_x); + return foo (); + + to: + + ret_slot = foo (); + drop (_x); + return ret_slot; */ + fncontext fnctx = ctx->peek_fn (); + tree result_reference + = Backend::var_expression (fnctx.ret_addr, lvalue_locus); + tree assignment = Backend::assignment_statement (result_reference, + return_value, locus); + ctx->add_statement (assignment); + CompileDrop (ctx).emit_current_scope_drop_calls (); + result_reference = Backend::var_expression (fnctx.ret_addr, locus); tree return_stmt - = Backend::return_statement (fndecl, return_value, locus); + = Backend::return_statement (fndecl, result_reference, locus); ctx->add_statement (return_stmt); } else diff --git a/gcc/testsuite/rust/execute/drop-function-scope-non-unit-tail.rs b/gcc/testsuite/rust/execute/drop-function-scope-non-unit-tail.rs new file mode 100644 index 000000000000..71df1615cbed --- /dev/null +++ b/gcc/testsuite/rust/execute/drop-function-scope-non-unit-tail.rs @@ -0,0 +1,46 @@ +// { dg-output "f\r*\nd\r*\n" } +// { dg-additional-options "-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; + +impl Drop for Droppable { + fn drop(&mut self) { + let msg = "d\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + } +} + +fn foo() -> i32 { + let msg = "f\n\0" as *const str as *const i8; + unsafe { + printf(msg); + } + + 0 +} + +fn f() -> i32 { + let _x = Droppable; + foo() +} + +fn main() -> i32 { + f() +}
