From 4a84f78dfed0a3ef0fbe2aa8eb2ad102b4bc87e3 Mon Sep 17 00:00:00 2001 From: Mohit Mishra Date: Sun, 25 Oct 2026 11:51:16 +0530 Subject: [PATCH 1/2] Make returning a reference bound to a temporary an error in C++26. [GH #106] --- src/overload.c | 18 +++++++++++---- .../gh-106-return-ref-to-temporary.txt | 11 ++++++++++ .../tests/changes/.GH106.rto/default.1.1.txt | 18 +++++++++++++++ .../tests/changes/.GH106.rto/default.2.1.txt | 22 +++++++++++++++++++ .../tests/changes/.GH106.rto/default.3.1.txt | 22 +++++++++++++++++++ .../changes/.GH106_a.rto/default.1.1.txt | 17 ++++++++++++++ .../changes/.GH106_a.rto/default.2.1.txt | 21 ++++++++++++++++++ .../changes/.GH106_a.rto/default.3.1.txt | 21 ++++++++++++++++++ tests/tests/changes/GH106.sft.cpp | 15 +++++++++++++ tests/tests/changes/GH106_a.sft.cpp | 14 ++++++++++++ 10 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 src/pending_changes/gh-106-return-ref-to-temporary.txt create mode 100644 tests/tests/changes/.GH106.rto/default.1.1.txt create mode 100644 tests/tests/changes/.GH106.rto/default.2.1.txt create mode 100644 tests/tests/changes/.GH106.rto/default.3.1.txt create mode 100644 tests/tests/changes/.GH106_a.rto/default.1.1.txt create mode 100644 tests/tests/changes/.GH106_a.rto/default.2.1.txt create mode 100644 tests/tests/changes/.GH106_a.rto/default.3.1.txt create mode 100644 tests/tests/changes/GH106.sft.cpp create mode 100644 tests/tests/changes/GH106_a.sft.cpp diff --git a/src/overload.c b/src/overload.c index 866e72bb56..f29c998e00 100644 --- a/src/overload.c +++ b/src/overload.c @@ -25428,7 +25428,8 @@ non-const to the indicated (rvalue) operand. static void check_for_returning_reference_to_local_entity(an_operand *operand) /* operand is the entity being bound to a reference in a return statement. -Issue a warning if it is a local entity. +Issue a warning if it is a local entity. Binding the returned reference to +a temporary is an error in C++26 (P2748). */ { a_boolean is_temp; @@ -25439,9 +25440,18 @@ Issue a warning if it is a local entity. /* The expression is a glvalue or class prvalue (object) for a local entity. Use a different message for temporaries and local variables. */ - expr_pos_warning(is_temp ? ec_return_ref_init_requires_temp : - ec_returning_ref_to_local_variable, - &operand->position); + if (!is_temp) { + expr_pos_warning(ec_returning_ref_to_local_variable, + &operand->position); + } else if (cpp26_mode && !gpp_version_is(<140000) && + !clangcpp_version_is(<190000)) { + /* GCC 14 and Clang 19 implement P2748. Earlier versions only issue + a warning. */ + expr_pos_error(ec_return_ref_init_requires_temp, &operand->position); + } else { + expr_pos_warning(ec_return_ref_init_requires_temp, + &operand->position); + } /* if */ } /* if */ } /* if */ } /* check_for_returning_reference_to_local_entity */ diff --git a/src/pending_changes/gh-106-return-ref-to-temporary.txt b/src/pending_changes/gh-106-return-ref-to-temporary.txt new file mode 100644 index 0000000000..393a1f2bc5 --- /dev/null +++ b/src/pending_changes/gh-106-return-ref-to-temporary.txt @@ -0,0 +1,11 @@ +[GH #106] +C++26: returning a reference bound to a temporary is not allowed + +A return statement that binds the returned reference to a temporary is not +allowed in C++26 (P2748). The front end only issued a warning. It is now an +error in C++26 (still a warning when emulating GCC versions before 14 or +Clang versions before 19, which do the same). For example, with --c++26: + + const int& f() { return 1; } // Now an error. + +Returning a reference to a local variable is still only a warning. diff --git a/tests/tests/changes/.GH106.rto/default.1.1.txt b/tests/tests/changes/.GH106.rto/default.1.1.txt new file mode 100644 index 0000000000..17b862472b --- /dev/null +++ b/tests/tests/changes/.GH106.rto/default.1.1.txt @@ -0,0 +1,18 @@ +fe_only -DTEST_NUMBER=1 --c++26 Test_name.c +"Test_name.c", line 11: error: returning reference to local temporary + const int& f1() { return 1; } // Temporary: diagnosed. + ^ + +"Test_name.c", line 12: error: returning reference to local temporary + const int& f2(long l) { return l; } // Temporary from a conversion. + ^ + +"Test_name.c", line 14: error: returning reference to local temporary + int&& f4() { return 1; } // Rvalue reference to a temporary. + ^ + +"Test_name.c", line 15: warning: returning reference to local variable + const int& f5() { int i = 0; return i; } // Local variable: only a warning. + ^ + +3 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.GH106.rto/default.2.1.txt b/tests/tests/changes/.GH106.rto/default.2.1.txt new file mode 100644 index 0000000000..bb6b9f4ffc --- /dev/null +++ b/tests/tests/changes/.GH106.rto/default.2.1.txt @@ -0,0 +1,22 @@ +fe_only -DTEST_NUMBER=2 --c++26 --gnu_version 140000 Test_name.c +"Test_name.c", line 11: error: returning reference to local temporary + const int& f1() { return 1; } // Temporary: diagnosed. + ^ + +"Test_name.c", line 12: error: returning reference to local temporary + const int& f2(long l) { return l; } // Temporary from a conversion. + ^ + +"Test_name.c", line 13: error: returning reference to local temporary + const int& f3() { return S().x; } // Member of a temporary. + ^ + +"Test_name.c", line 14: error: returning reference to local temporary + int&& f4() { return 1; } // Rvalue reference to a temporary. + ^ + +"Test_name.c", line 15: warning: returning reference to local variable + const int& f5() { int i = 0; return i; } // Local variable: only a warning. + ^ + +4 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.GH106.rto/default.3.1.txt b/tests/tests/changes/.GH106.rto/default.3.1.txt new file mode 100644 index 0000000000..66b9ada943 --- /dev/null +++ b/tests/tests/changes/.GH106.rto/default.3.1.txt @@ -0,0 +1,22 @@ +fe_only -DTEST_NUMBER=3 --c++26 --clang_version 190000 Test_name.c +"Test_name.c", line 11: error: returning reference to local temporary + const int& f1() { return 1; } // Temporary: diagnosed. + ^ + +"Test_name.c", line 12: error: returning reference to local temporary + const int& f2(long l) { return l; } // Temporary from a conversion. + ^ + +"Test_name.c", line 13: error: returning reference to local temporary + const int& f3() { return S().x; } // Member of a temporary. + ^ + +"Test_name.c", line 14: error: returning reference to local temporary + int&& f4() { return 1; } // Rvalue reference to a temporary. + ^ + +"Test_name.c", line 15: warning: returning reference to local variable + const int& f5() { int i = 0; return i; } // Local variable: only a warning. + ^ + +4 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.GH106_a.rto/default.1.1.txt b/tests/tests/changes/.GH106_a.rto/default.1.1.txt new file mode 100644 index 0000000000..92aebb12ce --- /dev/null +++ b/tests/tests/changes/.GH106_a.rto/default.1.1.txt @@ -0,0 +1,17 @@ +fe_only -DTEST_NUMBER=1 --c++23 Test_name.c +"Test_name.c", line 10: warning: returning reference to local temporary + const int& f1() { return 1; } // Temporary: diagnosed. + ^ + +"Test_name.c", line 11: warning: returning reference to local temporary + const int& f2(long l) { return l; } // Temporary from a conversion. + ^ + +"Test_name.c", line 13: warning: returning reference to local temporary + int&& f4() { return 1; } // Rvalue reference to a temporary. + ^ + +"Test_name.c", line 14: warning: returning reference to local variable + const int& f5() { int i = 0; return i; } // Local variable: only a warning. + ^ + diff --git a/tests/tests/changes/.GH106_a.rto/default.2.1.txt b/tests/tests/changes/.GH106_a.rto/default.2.1.txt new file mode 100644 index 0000000000..9a3ff4afba --- /dev/null +++ b/tests/tests/changes/.GH106_a.rto/default.2.1.txt @@ -0,0 +1,21 @@ +fe_only -DTEST_NUMBER=2 --c++26 --gnu_version 130000 Test_name.c +"Test_name.c", line 10: warning: returning reference to local temporary + const int& f1() { return 1; } // Temporary: diagnosed. + ^ + +"Test_name.c", line 11: warning: returning reference to local temporary + const int& f2(long l) { return l; } // Temporary from a conversion. + ^ + +"Test_name.c", line 12: warning: returning reference to local temporary + const int& f3() { return S().x; } // Member of a temporary. + ^ + +"Test_name.c", line 13: warning: returning reference to local temporary + int&& f4() { return 1; } // Rvalue reference to a temporary. + ^ + +"Test_name.c", line 14: warning: returning reference to local variable + const int& f5() { int i = 0; return i; } // Local variable: only a warning. + ^ + diff --git a/tests/tests/changes/.GH106_a.rto/default.3.1.txt b/tests/tests/changes/.GH106_a.rto/default.3.1.txt new file mode 100644 index 0000000000..c951e5dd47 --- /dev/null +++ b/tests/tests/changes/.GH106_a.rto/default.3.1.txt @@ -0,0 +1,21 @@ +fe_only -DTEST_NUMBER=3 --c++26 --clang_version 180000 Test_name.c +"Test_name.c", line 10: warning: returning reference to local temporary + const int& f1() { return 1; } // Temporary: diagnosed. + ^ + +"Test_name.c", line 11: warning: returning reference to local temporary + const int& f2(long l) { return l; } // Temporary from a conversion. + ^ + +"Test_name.c", line 12: warning: returning reference to local temporary + const int& f3() { return S().x; } // Member of a temporary. + ^ + +"Test_name.c", line 13: warning: returning reference to local temporary + int&& f4() { return 1; } // Rvalue reference to a temporary. + ^ + +"Test_name.c", line 14: warning: returning reference to local variable + const int& f5() { int i = 0; return i; } // Local variable: only a warning. + ^ + diff --git a/tests/tests/changes/GH106.sft.cpp b/tests/tests/changes/GH106.sft.cpp new file mode 100644 index 0000000000..c6fd59531b --- /dev/null +++ b/tests/tests/changes/GH106.sft.cpp @@ -0,0 +1,15 @@ +//type:fn +//options:--c++26:--c++26 --gnu_version 140000:--c++26 --clang_version 190000 +//remark:[GH #106] C++26: returning a reference bound to a temporary +// +// P2748 makes a return statement that binds the returned reference to a +// temporary ill-formed in C++26. Returning a reference to a local variable +// is still only a warning. + +struct S { int x; }; + +const int& f1() { return 1; } // Temporary: diagnosed. +const int& f2(long l) { return l; } // Temporary from a conversion. +const int& f3() { return S().x; } // Member of a temporary. +int&& f4() { return 1; } // Rvalue reference to a temporary. +const int& f5() { int i = 0; return i; } // Local variable: only a warning. diff --git a/tests/tests/changes/GH106_a.sft.cpp b/tests/tests/changes/GH106_a.sft.cpp new file mode 100644 index 0000000000..24041c0121 --- /dev/null +++ b/tests/tests/changes/GH106_a.sft.cpp @@ -0,0 +1,14 @@ +//type:fp +//options:--c++23:--c++26 --gnu_version 130000:--c++26 --clang_version 180000 +//remark:[GH #106] Returning a reference bound to a temporary before C++26 +// +// Before C++26, and in C++26 when emulating GCC before 14 or Clang before +// 19, returning a reference bound to a temporary is only a warning. + +struct S { int x; }; + +const int& f1() { return 1; } // Temporary: diagnosed. +const int& f2(long l) { return l; } // Temporary from a conversion. +const int& f3() { return S().x; } // Member of a temporary. +int&& f4() { return 1; } // Rvalue reference to a temporary. +const int& f5() { int i = 0; return i; } // Local variable: only a warning. From e5e470022b7d210edbbd3a75c4d52389e9435899 Mon Sep 17 00:00:00 2001 From: Mohit Mishra Date: Sun, 25 Oct 2026 18:08:43 +0530 Subject: [PATCH 2/2] Keep the C++26 return-temporary error off direct bindings to similar types, and update recordings. [GH #106] --- src/overload.c | 22 ++++++++++++++----- tests/expectations/edg_x86_64/imported.log | 1 - .../tests/changes/.GH106.rto/default.2.1.txt | 4 ++++ .../tests/changes/.GH106.rto/default.3.1.txt | 4 ++++ .../changes/.GH106_a.rto/default.2.1.txt | 4 ++++ .../changes/.GH106_a.rto/default.3.1.txt | 4 ++++ tests/tests/changes/GH106.sft.cpp | 5 +++++ tests/tests/changes/GH106_a.sft.cpp | 5 +++++ tests/tests/cwg/.p2748R5.rto/default.1.1.txt | 6 ++--- .../stmt.return/.p6.rto/default.1.1.txt | 5 +++-- 10 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/overload.c b/src/overload.c index f29c998e00..18c124fc7d 100644 --- a/src/overload.c +++ b/src/overload.c @@ -25425,11 +25425,16 @@ non-const to the indicated (rvalue) operand. } /* microsoft_can_bind_ref_to_rvalue */ -static void check_for_returning_reference_to_local_entity(an_operand *operand) +static void check_for_returning_reference_to_local_entity( + an_operand *operand, + a_boolean similar_lvalue_source) /* operand is the entity being bound to a reference in a return statement. Issue a warning if it is a local entity. Binding the returned reference to -a temporary is an error in C++26 (P2748). +a temporary is an error in C++26 (P2748). similar_lvalue_source is TRUE if +the original initializer was an lvalue of a type similar to the referenced +type; the reference should then bind to it directly (Core issue 2352), so +a temporary found here only gets the warning. */ { a_boolean is_temp; @@ -25443,8 +25448,8 @@ a temporary is an error in C++26 (P2748). if (!is_temp) { expr_pos_warning(ec_returning_ref_to_local_variable, &operand->position); - } else if (cpp26_mode && !gpp_version_is(<140000) && - !clangcpp_version_is(<190000)) { + } else if (cpp26_mode && !similar_lvalue_source && + !gpp_version_is(<140000) && !clangcpp_version_is(<190000)) { /* GCC 14 and Clang 19 implement P2748. Earlier versions only issue a warning. */ expr_pos_error(ec_return_ref_init_requires_temp, &operand->position); @@ -25624,6 +25629,7 @@ the conversion. a_boolean ref_to_const = FALSE; a_boolean ref_to_const_volatile = FALSE; a_boolean operand_was_rvalue, operand_is_function_lvalue; + a_boolean similar_lvalue_source; a_boolean warn = FALSE, template_case = FALSE; a_boolean initializing_variable = (conv_context & CCO_INITIALIZING_VARIABLE) != 0; @@ -25721,6 +25727,11 @@ the conversion. } /* if */ base_dest_type = type_pointed_to(dest_type); operand_was_rvalue = is_an_rvalue(source_operand); + /* An lvalue whose type is similar to the referenced type, e.g., an "int **" + lvalue for a "const int *const *const &". The reference binds directly + to it (Core issue 2352). */ + similar_lvalue_source = !operand_was_rvalue && + types_are_similar(orig_source_type, base_dest_type); operand_is_function_lvalue = is_a_function_designator(source_operand); adj_base_dest_type = base_dest_type; if (bitwise_assignment_param) { @@ -26326,7 +26337,8 @@ the conversion. } else if ((conv_context & CCO_INITIALIZING_RETURN_VALUE) && !template_case) { /* Check for returning a reference to a local entity. */ - check_for_returning_reference_to_local_entity(source_operand); + check_for_returning_reference_to_local_entity(source_operand, + similar_lvalue_source); } /* if */ if (!leave_as_object) { /* Final step: add the reference-to to turn the glvalue or class prvalue diff --git a/tests/expectations/edg_x86_64/imported.log b/tests/expectations/edg_x86_64/imported.log index 2c384ac8c4..763de4728e 100644 --- a/tests/expectations/edg_x86_64/imported.log +++ b/tests/expectations/edg_x86_64/imported.log @@ -858,7 +858,6 @@ clang/cpp/CXX/special/class.ctor/p1.sft.cpp:fn:-DTEST_NUMBER=1:FAIL: clang/cpp/CXX/special/class.temporary/p1.sft.cpp:fn:-DTEST_NUMBER=1 --c++11:FAIL: clang/cpp/CXX/special/class.temporary/p6.sft.cpp:fp:-DTEST_NUMBER=1 --c++17:FAIL: clang/cpp/CXX/special/class.temporary/p6.sft.cpp:fp:-DTEST_NUMBER=2 --c++23:FAIL: -clang/cpp/CXX/stmt.stmt/stmt.return/p6.sft.cpp:fn:-DTEST_NUMBER=1 --c++26:FAIL: clang/cpp/CXX/stmt.stmt/stmt.select/stmt.if/p2.sft.cpp:fn:-DTEST_NUMBER=2 --c++17 -DUNDEFINED:FAIL: clang/cpp/CXX/stmt.stmt/stmt.select/stmt.switch/p4.sft.cpp:fn:-DTEST_NUMBER=1:FAIL: clang/cpp/CXX/temp/temp.constr/temp.constr.order/non-template-functions.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL: diff --git a/tests/tests/changes/.GH106.rto/default.2.1.txt b/tests/tests/changes/.GH106.rto/default.2.1.txt index bb6b9f4ffc..c651deb3b9 100644 --- a/tests/tests/changes/.GH106.rto/default.2.1.txt +++ b/tests/tests/changes/.GH106.rto/default.2.1.txt @@ -19,4 +19,8 @@ fe_only -DTEST_NUMBER=2 --c++26 --gnu_version 140000 Test_name.c const int& f5() { int i = 0; return i; } // Local variable: only a warning. ^ +"Test_name.c", line 20: warning: returning reference to local temporary + const int *const *const &f6() { return pp; } + ^ + 4 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.GH106.rto/default.3.1.txt b/tests/tests/changes/.GH106.rto/default.3.1.txt index 66b9ada943..f23f451579 100644 --- a/tests/tests/changes/.GH106.rto/default.3.1.txt +++ b/tests/tests/changes/.GH106.rto/default.3.1.txt @@ -19,4 +19,8 @@ fe_only -DTEST_NUMBER=3 --c++26 --clang_version 190000 Test_name.c const int& f5() { int i = 0; return i; } // Local variable: only a warning. ^ +"Test_name.c", line 20: warning: returning reference to local temporary + const int *const *const &f6() { return pp; } + ^ + 4 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/changes/.GH106_a.rto/default.2.1.txt b/tests/tests/changes/.GH106_a.rto/default.2.1.txt index 9a3ff4afba..355536643c 100644 --- a/tests/tests/changes/.GH106_a.rto/default.2.1.txt +++ b/tests/tests/changes/.GH106_a.rto/default.2.1.txt @@ -19,3 +19,7 @@ fe_only -DTEST_NUMBER=2 --c++26 --gnu_version 130000 Test_name.c const int& f5() { int i = 0; return i; } // Local variable: only a warning. ^ +"Test_name.c", line 19: warning: returning reference to local temporary + const int *const *const &f6() { return pp; } + ^ + diff --git a/tests/tests/changes/.GH106_a.rto/default.3.1.txt b/tests/tests/changes/.GH106_a.rto/default.3.1.txt index c951e5dd47..cc979b93b9 100644 --- a/tests/tests/changes/.GH106_a.rto/default.3.1.txt +++ b/tests/tests/changes/.GH106_a.rto/default.3.1.txt @@ -19,3 +19,7 @@ fe_only -DTEST_NUMBER=3 --c++26 --clang_version 180000 Test_name.c const int& f5() { int i = 0; return i; } // Local variable: only a warning. ^ +"Test_name.c", line 19: warning: returning reference to local temporary + const int *const *const &f6() { return pp; } + ^ + diff --git a/tests/tests/changes/GH106.sft.cpp b/tests/tests/changes/GH106.sft.cpp index c6fd59531b..81f1c053ea 100644 --- a/tests/tests/changes/GH106.sft.cpp +++ b/tests/tests/changes/GH106.sft.cpp @@ -13,3 +13,8 @@ const int& f2(long l) { return l; } // Temporary from a conversion. const int& f3() { return S().x; } // Member of a temporary. int&& f4() { return 1; } // Rvalue reference to a temporary. const int& f5() { int i = 0; return i; } // Local variable: only a warning. + +// Core issue 2352: the reference binds directly to pp, so this is not +// diagnosed as an error. +int **pp; +const int *const *const &f6() { return pp; } diff --git a/tests/tests/changes/GH106_a.sft.cpp b/tests/tests/changes/GH106_a.sft.cpp index 24041c0121..7fd7a4c45f 100644 --- a/tests/tests/changes/GH106_a.sft.cpp +++ b/tests/tests/changes/GH106_a.sft.cpp @@ -12,3 +12,8 @@ const int& f2(long l) { return l; } // Temporary from a conversion. const int& f3() { return S().x; } // Member of a temporary. int&& f4() { return 1; } // Rvalue reference to a temporary. const int& f5() { int i = 0; return i; } // Local variable: only a warning. + +// Core issue 2352: the reference binds directly to pp, so this is not +// diagnosed as an error. +int **pp; +const int *const *const &f6() { return pp; } diff --git a/tests/tests/cwg/.p2748R5.rto/default.1.1.txt b/tests/tests/cwg/.p2748R5.rto/default.1.1.txt index 10b91fd5da..ccf5fc1f7b 100644 --- a/tests/tests/cwg/.p2748R5.rto/default.1.1.txt +++ b/tests/tests/cwg/.p2748R5.rto/default.1.1.txt @@ -1,9 +1,9 @@ fe_only -DTEST_NUMBER=1 --c++26 Test_name.c -"Test_name.c", line 4: warning: returning reference to local temporary +"Test_name.c", line 4: error: returning reference to local temporary return 42; // ill-formed ^ -"Test_name.c", line 8: warning: returning reference to local temporary +"Test_name.c", line 8: error: returning reference to local temporary return x; // ill-formed ^ @@ -12,4 +12,4 @@ At end of source: error: expected a "}" auto&& f3() { ^ -1 error detected in the compilation of "Test_name.c". +3 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/imported/clang/cpp/CXX/stmt.stmt/stmt.return/.p6.rto/default.1.1.txt b/tests/tests/imported/clang/cpp/CXX/stmt.stmt/stmt.return/.p6.rto/default.1.1.txt index c94298e1d4..e10d0b92a3 100644 --- a/tests/tests/imported/clang/cpp/CXX/stmt.stmt/stmt.return/.p6.rto/default.1.1.txt +++ b/tests/tests/imported/clang/cpp/CXX/stmt.stmt/stmt.return/.p6.rto/default.1.1.txt @@ -1,9 +1,10 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_version 999999 --c++26 Test_name.c -"Test_name.c", line 6: warning: returning reference to local temporary +"Test_name.c", line 6: error: returning reference to local temporary return 42; // expected-error{{returning reference to local temporary object}} ^ -"Test_name.c", line 10: warning: returning reference to local temporary +"Test_name.c", line 10: error: returning reference to local temporary return x; // expected-error{{returning reference to local temporary object}} ^ +2 errors detected in the compilation of "Test_name.c".