[clang] 25d9688 - [Clang] Extend lifetime of temporaries in mem-default-init for P2718R0 (#86960)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 10 09:04:06 PDT 2024


Author: yronglin
Date: 2024-10-11T00:04:02+08:00
New Revision: 25d9688c43d37c0c918e9b8ab2f67be35b0fb75f

URL: https://github.com/llvm/llvm-project/commit/25d9688c43d37c0c918e9b8ab2f67be35b0fb75f
DIFF: https://github.com/llvm/llvm-project/commit/25d9688c43d37c0c918e9b8ab2f67be35b0fb75f.diff

LOG: [Clang] Extend lifetime of temporaries in mem-default-init for P2718R0 (#86960)

Depends on [CWG1815](https://github.com/llvm/llvm-project/pull/108039).
Fixes https://github.com/llvm/llvm-project/issues/85613.

In [[Clang] Implement P2718R0 "Lifetime extension in range-based for
loops"](https://github.com/llvm/llvm-project/pull/76361), we've not
implement the lifetime extensions for the temporaries which in
`CXXDefaultInitExpr`. As the confirmation in
https://github.com/llvm/llvm-project/issues/85613, we should extend
lifetime for that.

To avoid modifying current CodeGen rules, in a lifetime extension
context, the cleanup of `CXXDefaultInitExpr` was ignored.

---------

Signed-off-by: yronglin <yronglin777 at gmail.com>

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Sema/SemaInit.cpp
    clang/test/AST/ast-dump-for-range-lifetime.cpp
    clang/test/CXX/special/class.temporary/p6.cpp
    clang/www/cxx_status.html

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c0019cfe4658d7..e48835d4738007 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
 ^^^^^^^^^^^^^^^^^^^^^
 

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e2141e03ca4230..4e37385710af5e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5649,6 +5649,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 edd1fe40fdf278..5d6a586fe5a2cf 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/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 a6d2adfd1fd2c5..2b1b531b7172ca 100644
--- a/clang/test/CXX/special/class.temporary/p6.cpp
+++ b/clang/test/CXX/special/class.temporary/p6.cpp
@@ -463,6 +463,80 @@ 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
+
+// -- 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; }
@@ -579,5 +653,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>


        


More information about the cfe-commits mailing list