https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/86960
>From a8a87484760874d3b36673970e7158f0247df69b Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Thu, 26 Sep 2024 22:28:07 +0800 Subject: [PATCH 1/3] [clang][C++23] Extend lifetime of temporaries in mem-default-init for P2718R0 Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/lib/Sema/SemaExpr.cpp | 2 + clang/lib/Sema/SemaInit.cpp | 2 + clang/test/CXX/special/class.temporary/p6.cpp | 106 +++++++++++++++++- clang/www/cxx_status.html | 9 +- 4 files changed, 110 insertions(+), 9 deletions(-) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 66df9c969256a2..bde73670b0b07a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5639,6 +5639,8 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { runWithSufficientStackSpace(Loc, [&] { MarkDeclarationsReferencedInExpr(E, /*SkipLocalVariables=*/false); }); + if (isInLifetimeExtendingContext()) + DiscardCleanupsInEvaluationContext(); // C++11 [class.base.init]p7: // The initialization of each base and member constitutes a // full-expression. diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 4d11f2a43fcc6b..a5a716e7499f38 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -763,6 +763,8 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, SemaRef.currentEvaluationContext().DelayedDefaultInitializationContext = SemaRef.parentEvaluationContext() .DelayedDefaultInitializationContext; + SemaRef.currentEvaluationContext().InLifetimeExtendingContext = + SemaRef.parentEvaluationContext().InLifetimeExtendingContext; DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); } if (DIE.isInvalid()) { diff --git a/clang/test/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp index a6d2adfd1fd2c5..6c79de5dcbfae3 100644 --- a/clang/test/CXX/special/class.temporary/p6.cpp +++ b/clang/test/CXX/special/class.temporary/p6.cpp @@ -304,6 +304,26 @@ void check_dr1815() { // dr1815: yes } namespace P2718R0 { +extern void block_scope_begin_function(); +extern void block_scope_end_function(); + +template <typename E> using T2 = std::list<E>; +template <typename E> const T2<E> &f1_temp(const T2<E> &t) { return t; } +template <typename E> const T2<E> &f2_temp(T2<E> t) { return t; } +template <typename E> T2<E> g_temp() { return T2<E>{}; } + +// -- Examples from https://wg21.link/p2718r0 +namespace std_examples { +using T = std::list<int>; +const T& f1(const T& t) { return t; } +const T& f2(T t) { return t; } +T g(); +void foo() { + for (auto e : f1(g())) {} // OK, lifetime of return value of g() extended + for (auto e : f2(g())) {} // undefined behavior +} +} // namespace std_examples + namespace basic { template <typename E> using T2 = std::list<E>; template <typename E> const T2<E> &f1_temp(const T2<E> &t) { return t; } @@ -463,6 +483,44 @@ template void default_arg_dependent_context2<int>(); template void default_arg_dependent_context3<int>(); } // namespace default_arg +namespace default_init { +template <class T> +struct DepA { + T arr[1]; + ~DepA() {} +}; + +template <class T> +struct DepB { + int x; + const DepA<T> &a = DepA<T>{{0}}; + ~DepB() {} + const int *begin() { return a.arr; } + const int *end() { return &a.arr[1]; } +}; + +template <typename T> +void default_init1_dependent() { + // CHECK-CXX23: void @_ZN7P2718R012default_init23default_init1_dependentINS0_4DepBIiEEEEvv() + // CHECK-CXX23-LABEL: for.cond.cleanup: + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepBIiED1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepAIiED1Ev( + for (auto &&x : T{0}) {} +} + +template <typename T> +void default_init2_dependent() { + // CHECK-CXX23: void @_ZN7P2718R012default_init23default_init2_dependentINS0_4DepBIiEEEEvv() + // CHECK-CXX23-LABEL: for.cond.cleanup: + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepBIiED1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepAIiED1Ev( + for (auto &&x : T{0}.a.arr) {} +} + +template void default_init1_dependent<DepB<int>>(); +template void default_init2_dependent<DepB<int>>(); +} // namespace default_init + namespace basic { using T = std::list<int>; const T& f1(const T& t) { return t; } @@ -579,5 +637,51 @@ void default_arg3() { for (auto e : C(0, C(0, C(0, C())))) {} } } // namespace default_arg -} // namespace P2718R0 +namespace default_init { +struct X { + int x; + ~X() {} +}; + +struct Y { + int y; + const X &x = X{1}; + ~Y() {} +}; + +struct A { + int arr[1]; + const Y &y = Y{1}; + ~A() {} +}; + +struct B { + int x; + const A &a = A{{0}}; + ~B() {} + const int *begin() { return a.arr; } + const int *end() { return &a.arr[1]; } +}; + +void default_init1() { + // CHECK-CXX23: void @_ZN7P2718R012default_init13default_init1Ev() + // CHECK-CXX23-LABEL: for.cond.cleanup: + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1BD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1AD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1YD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1XD1Ev( + for (auto &&x : B{0}) {} +} + +void default_init2() { + // CHECK-CXX23: void @_ZN7P2718R012default_init13default_init2Ev() + // CHECK-CXX23-LABEL: for.cond.cleanup: + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1BD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1AD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1YD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1XD1Ev( + for (auto &&x : B{0}.a.arr) {} +} +} // namespace default_init +} // namespace P2718R0 diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 3f6a46c08c8514..d59cbbbbec1b5b 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -475,14 +475,7 @@ <h2 id="cxx23">C++23 implementation status</h2> <tr> <td>Lifetime extension in range-based for loops</td> <td><a href="https://wg21.link/P2718R0">P2718R0</a></td> - <td class="partial" align="center"> - <details> - <summary>Clang 19 (Partial)</summary> - The lifetime extension of temporaries bound to member references - by default member initializers in aggregate initialization was - not supported now. - </details> - </td> + <td class="full" align="center">Clang 20</td> </tr> <!--Issaquah 2023 papers--> <tr> >From c2d5f790641f5297e557cf6af359123eedbe93fb Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Sun, 29 Sep 2024 22:42:15 +0800 Subject: [PATCH 2/3] Add more test Signed-off-by: yronglin <yronglin...@gmail.com> --- .../test/AST/ast-dump-for-range-lifetime.cpp | 59 +++++++++++++++++++ clang/test/CXX/special/class.temporary/p6.cpp | 56 +++++++++++------- 2 files changed, 95 insertions(+), 20 deletions(-) diff --git a/clang/test/AST/ast-dump-for-range-lifetime.cpp b/clang/test/AST/ast-dump-for-range-lifetime.cpp index 0e92b6990ed504..ee046be19ab632 100644 --- a/clang/test/AST/ast-dump-for-range-lifetime.cpp +++ b/clang/test/AST/ast-dump-for-range-lifetime.cpp @@ -449,4 +449,63 @@ void test13() { for (auto e : dg<A>().r().g().r().g().r().g()) bar(e); } + +extern "C" void exit(int); + +struct A14 { + int arr[1]; + ~A14() noexcept(false) { throw 42; } +}; + +struct B14 { + int x; + const A14 &a = A14{{0}}; + const int *begin() { return a.arr; } + const int *end() { return &a.arr[1]; } +}; + +void test14() { + // The ExprWithCleanups in CXXDefaultInitExpr will be ignored. + + // CHECK: FunctionDecl {{.*}} test14 'void ()' + // CHECK: -CXXForRangeStmt {{.*}} + // CHECK-NEXT: |-<<<NULL>>> + // CHECK-NEXT: |-DeclStmt {{.*}} + // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'const int (&)[1]' cinit + // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'const int[1]' lvalue + // CHECK-NEXT: | `-MemberExpr {{.*}} 'const int[1]' lvalue .arr {{.*}} + // CHECK-NEXT: | `-MemberExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue .a {{.*}} + // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'B14':'P2718R0::B14' xvalue extended by Var {{.*}} '__range1' 'const int (&)[1]' + // CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'B14':'P2718R0::B14' functional cast to B14 <NoOp> + // CHECK-NEXT: | `-InitListExpr {{.*}} 'B14':'P2718R0::B14' + // CHECK-NEXT: | |-IntegerLiteral {{.*}} 'int' 0 + // CHECK-NEXT: | `-CXXDefaultInitExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue has rewritten init + // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue extended by Var {{.*}} '__range1' 'const int (&)[1]' + // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const A14':'const P2718R0::A14' <NoOp> + // CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'A14':'P2718R0::A14' functional cast to A14 <NoOp> + // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A14':'P2718R0::A14' (CXXTemporary {{.*}}) + // CHECK-NEXT: | `-InitListExpr {{.*}} 'A14':'P2718R0::A14' + // CHECK-NEXT: | `-InitListExpr {{.*}} 'int[1]' + // CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 0 + for (auto &&x : B14{0}.a.arr) { exit(0); } + + // CHECK: -CXXForRangeStmt {{.*}} + // CHECK-NEXT: |-<<<NULL>>> + // CHECK-NEXT: |-DeclStmt {{.*}} + // CHECK-NEXT: | `-VarDecl {{.*}} col:19 implicit used __range1 'B14 &&' cinit + // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'B14':'P2718R0::B14' xvalue + // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'B14':'P2718R0::B14' xvalue extended by Var {{.*}} '__range1' 'B14 &&' + // CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'B14':'P2718R0::B14' functional cast to B14 <NoOp> + // CHECK-NEXT: | `-InitListExpr {{.*}} 'B14':'P2718R0::B14' + // CHECK-NEXT: | |-IntegerLiteral {{.*}} 'int' 0 + // CHECK-NEXT: | `-CXXDefaultInitExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue has rewritten init + // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue extended by Var {{.*}} '__range1' 'B14 &&' + // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const A14':'const P2718R0::A14' <NoOp> + // CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'A14':'P2718R0::A14' functional cast to A14 <NoOp> + // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A14':'P2718R0::A14' (CXXTemporary {{.*}}) + // CHECK-NEXT: | `-InitListExpr {{.*}} 'A14':'P2718R0::A14' + // CHECK-NEXT: | `-InitListExpr {{.*}} 'int[1]' + // CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 0 + for (auto &&x : B14{0}) { exit(0); } +} } // namespace P2718R0 diff --git a/clang/test/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp index 6c79de5dcbfae3..2b1b531b7172ca 100644 --- a/clang/test/CXX/special/class.temporary/p6.cpp +++ b/clang/test/CXX/special/class.temporary/p6.cpp @@ -304,26 +304,6 @@ void check_dr1815() { // dr1815: yes } namespace P2718R0 { -extern void block_scope_begin_function(); -extern void block_scope_end_function(); - -template <typename E> using T2 = std::list<E>; -template <typename E> const T2<E> &f1_temp(const T2<E> &t) { return t; } -template <typename E> const T2<E> &f2_temp(T2<E> t) { return t; } -template <typename E> T2<E> g_temp() { return T2<E>{}; } - -// -- Examples from https://wg21.link/p2718r0 -namespace std_examples { -using T = std::list<int>; -const T& f1(const T& t) { return t; } -const T& f2(T t) { return t; } -T g(); -void foo() { - for (auto e : f1(g())) {} // OK, lifetime of return value of g() extended - for (auto e : f2(g())) {} // undefined behavior -} -} // namespace std_examples - namespace basic { template <typename E> using T2 = std::list<E>; template <typename E> const T2<E> &f1_temp(const T2<E> &t) { return t; } @@ -521,6 +501,42 @@ template void default_init1_dependent<DepB<int>>(); template void default_init2_dependent<DepB<int>>(); } // namespace default_init +// -- Examples from https://wg21.link/p2718r0 +extern void block_scope_begin_function(); +extern void block_scope_end_function(); +namespace std_examples { +using T = std::list<int>; +const T& f1(const T& t) { return t; } +const T& f2(T t) { return t; } +T g(); +void foo() { + // CHECK-CXX23: define {{.*}} void @_ZN7P2718R012std_examples3fooEv() + // CHECK-CXX23: call void @_ZN7P2718R026block_scope_begin_functionEv + block_scope_begin_function(); + { + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012std_examples1gEv + // CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R012std_examples2f1ERKSt4listIiE + // CHECK-CXX23: for.cond.cleanup: + // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev + for (auto e : f1(g())) {} // OK, lifetime of return value of g() extended + } + // CHECK-CXX23: call void @_ZN7P2718R024block_scope_end_functionEv + block_scope_end_function(); + + // The lifetime of temporary returned by g() in this case will not be extended. + // CHECK-CXX23: call void @_ZN7P2718R026block_scope_begin_functionEv + block_scope_begin_function(); + { + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012std_examples1gEv + // CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R012std_examples2f2ESt4listIiE + // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev + for (auto e : f2(g())) {} // undefined behavior + } + // CHECK-CXX23: call void @_ZN7P2718R024block_scope_end_functionEv + block_scope_end_function(); +} +} // namespace std_examples + namespace basic { using T = std::list<int>; const T& f1(const T& t) { return t; } >From 64f1ad8050024506e8c98d3944cd1a7c27b10839 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Sun, 6 Oct 2024 20:16:46 +0800 Subject: [PATCH 3/3] [clang] Add Release Notes Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/docs/ReleaseNotes.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 44d5f348ed2d54..f136d38e14f16b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -171,6 +171,9 @@ C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ - Removed the restriction to literal types in constexpr functions in C++23 mode. +- Extend lifetime of temporaries in mem-default-init for P2718R0. Clang now fully + supported `P2718R0 Lifetime extension in range-based for loops <https://wg21.link/P2718R0>`_. + C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits