From aa38e32762bbf87160c6bfd518f6aad10f570d67 Mon Sep 17 00:00:00 2001 From: Daveed Vandevoorde Date: Thu, 15 Oct 2026 23:36:56 +0000 Subject: [PATCH] Attribute cleanup actions at the end of a function body to its closing brace [GH #309] --- src/Changes | 17 ++++++++++++ src/lower_il.c | 27 ++++++++++++++----- .../edg_x86_32_cp/regressions.log | 2 ++ .../edg_x86_64_cp/regressions.log | 2 ++ .../regressions/.gh-309.rto/default.1.1.txt | 4 +++ .../regressions/.gh-309.rto/default.2.1.txt | 4 +++ tests/tests/regressions/gh-309.sft.cpp | 20 ++++++++++++++ 7 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 tests/tests/regressions/.gh-309.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-309.rto/default.2.1.txt create mode 100644 tests/tests/regressions/gh-309.sft.cpp diff --git a/src/Changes b/src/Changes index 52dae5c19a..14be9f92a3 100644 --- a/src/Changes +++ b/src/Changes @@ -1,5 +1,22 @@ Version 7.1 +10/15/26 [GH #309] +Position of (lowered) cleanups at the end of a function body + +The cleanup actions (e.g., destructor calls) performed when control reaches +the end of a function body were attributed to the source position of the +preceding statement instead of to that of the closing brace of the function +body. That is now fixed. For example: + + int v = 0; + struct D { ~D() { v = v + 1; } }; + void f() { + D d; + v = 2; + } // The call of d's destructor is now positioned on this line. + // Previously it was positioned on the line with "v = 2;" above. + + 10/9/26 [GH #260] GNU and Clang C++ modes: members skipped by a designated initializer diff --git a/src/lower_il.c b/src/lower_il.c index 869c4264dc..1d88ea11fb 100644 --- a/src/lower_il.c +++ b/src/lower_il.c @@ -19288,12 +19288,27 @@ Lower an stmk_return statement. } /* if */ if (any_cleanup_on_return) { /* Generate any cleanup actions required on exit from the routine. */ - if (make_block) { - /* Turn the return into a block so that code can be inserted - in front of the return. */ - turn_branch_into_block(statement, &insert_location, &return_statement); - } /* if */ - gen_cleanup_actions(innermost_function_scope->lifetime, &insert_location); + { Value_saver saved_code_pos(&code_pos_for_lowering); + Value_saver saved_error_position(&error_position); + + if (statement->compiler_generated && statement->position.seq == 0) { + a_statement_ptr assoc_block = innermost_function_scope->assoc_block; + if (assoc_block != NULL && + assoc_block->kind == stmk_block && + assoc_block->variant.block.extra_info->final_position.seq != 0) { + code_pos_for_lowering = assoc_block->variant.block.extra_info + ->final_position; + error_position = code_pos_for_lowering; + } /* if */ + } /* if */ + if (make_block) { + /* Turn the return into a block so that code can be inserted + in front of the return. */ + turn_branch_into_block(statement, &insert_location, &return_statement); + } /* if */ + gen_cleanup_actions(innermost_function_scope->lifetime, + &insert_location); + } reset_cleanup_state_at_transfer_of_control(return_statement, statement); } /* if */ /* Maintain a list of all returns in the routine so that epilogue code diff --git a/tests/expectations/edg_x86_32_cp/regressions.log b/tests/expectations/edg_x86_32_cp/regressions.log index 32d50d5385..d8a8fcf92b 100644 --- a/tests/expectations/edg_x86_32_cp/regressions.log +++ b/tests/expectations/edg_x86_32_cp/regressions.log @@ -1,3 +1,5 @@ +gh-309.sft.cpp:fp:-DTEST_NUMBER=1 --g++:REQUIREMENTS NOT MET: +gh-309.sft.cpp:fp:-DTEST_NUMBER=2 --g++ --no_exceptions:REQUIREMENTS NOT MET: immediate-pragma-lost.sft.cpp:cppbe:-DTEST_NUMBER=1:PASS: pragma-il-class-no-lowering.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: pragma-il-class.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: diff --git a/tests/expectations/edg_x86_64_cp/regressions.log b/tests/expectations/edg_x86_64_cp/regressions.log index 32d50d5385..d8a8fcf92b 100644 --- a/tests/expectations/edg_x86_64_cp/regressions.log +++ b/tests/expectations/edg_x86_64_cp/regressions.log @@ -1,3 +1,5 @@ +gh-309.sft.cpp:fp:-DTEST_NUMBER=1 --g++:REQUIREMENTS NOT MET: +gh-309.sft.cpp:fp:-DTEST_NUMBER=2 --g++ --no_exceptions:REQUIREMENTS NOT MET: immediate-pragma-lost.sft.cpp:cppbe:-DTEST_NUMBER=1:PASS: pragma-il-class-no-lowering.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: pragma-il-class.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: diff --git a/tests/tests/regressions/.gh-309.rto/default.1.1.txt b/tests/tests/regressions/.gh-309.rto/default.1.1.txt new file mode 100644 index 0000000000..1a49150d9f --- /dev/null +++ b/tests/tests/regressions/.gh-309.rto/default.1.1.txt @@ -0,0 +1,4 @@ +fe_only -DTEST_NUMBER=1 --g++ Test_name.c +destructor call on line 14 +destructor call on line 18 +destructor call on line 20 diff --git a/tests/tests/regressions/.gh-309.rto/default.2.1.txt b/tests/tests/regressions/.gh-309.rto/default.2.1.txt new file mode 100644 index 0000000000..41f67839f1 --- /dev/null +++ b/tests/tests/regressions/.gh-309.rto/default.2.1.txt @@ -0,0 +1,4 @@ +fe_only -DTEST_NUMBER=2 --g++ --no_exceptions Test_name.c +destructor call on line 14 +destructor call on line 18 +destructor call on line 20 diff --git a/tests/tests/regressions/gh-309.sft.cpp b/tests/tests/regressions/gh-309.sft.cpp new file mode 100644 index 0000000000..ffa6c17647 --- /dev/null +++ b/tests/tests/regressions/gh-309.sft.cpp @@ -0,0 +1,20 @@ +//type:fp +//options:--g++:--g++ --no_exceptions +//require:BACK_END_IS_C_GEN_BE 1 +//filter:awk '/^#(line)? [0-9]+/ { line = $2; next } /_ZN1DD1Ev\(\(/ { print "destructor call on line " line } { line++ }' Test_name.int.c +int sink; + +struct D { + ~D(); +}; + +void f() { + D d; + sink = 1; +} // The destructor call was previously attributed to the line above. + +void g(bool b) { + D d; + if (b) return; + sink = 2; +} // The destructor call was previously attributed to the line above.