[clang] Reapply "[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer" (PR #97308)

via cfe-commits cfe-commits at lists.llvm.org
Sat Sep 7 22:52:16 PDT 2024


https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/97308

>From e6ee9bef17ca8c597f816661f5bdf16eb5a6a982 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 7 Aug 2024 22:55:40 +0800
Subject: [PATCH 01/12] Reapply "[Clang][CWG1815] Support lifetime extension of
 temporary created by aggregate initialization using a default member
 initializer"

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../clang/Basic/DiagnosticSemaKinds.td        |  7 --
 clang/lib/Sema/CheckExprLifetime.cpp          | 18 +----
 clang/lib/Sema/SemaExpr.cpp                   | 31 ++++++--
 clang/lib/Sema/SemaExprCXX.cpp                |  3 -
 clang/lib/Sema/TreeTransform.h                | 12 ++-
 clang/test/AST/ast-dump-default-init-json.cpp |  6 +-
 clang/test/AST/ast-dump-default-init.cpp      |  2 +-
 .../Analysis/lifetime-extended-regions.cpp    |  9 ++-
 clang/test/CXX/drs/cwg16xx.cpp                |  2 -
 clang/test/CXX/drs/cwg18xx.cpp                | 19 +++--
 clang/test/CXX/special/class.temporary/p6.cpp | 34 +++++++++
 clang/test/SemaCXX/constexpr-default-arg.cpp  |  4 +-
 .../cxx11-default-member-initializers.cpp     | 74 +++++++++++++++++++
 clang/test/SemaCXX/eval-crashes.cpp           |  6 +-
 clang/www/cxx_dr_status.html                  |  2 +-
 15 files changed, 168 insertions(+), 61 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 02dc0ffe415943..67b60f3b9b2b0b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10148,13 +10148,6 @@ def warn_dangling_pointer_assignment : Warning<
    "will be destroyed at the end of the full-expression">,
    InGroup<DanglingAssignment>;
 
-def warn_unsupported_lifetime_extension : Warning<
-  "lifetime extension of "
-  "%select{temporary|backing array of initializer list}0 created "
-  "by aggregate initialization using a default member initializer "
-  "is not yet supported; lifetime of %select{temporary|backing array}0 "
-  "will end at the end of the full-expression">, InGroup<Dangling>;
-
 // For non-floating point, expressions of the form x == x or x != x
 // should result in a warning, since these always evaluate to a constant.
 // Array comparisons have similar warnings
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 7389046eaddde1..79cb8b2ffb7202 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -912,11 +912,6 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
 enum PathLifetimeKind {
   /// Lifetime-extend along this path.
   Extend,
-  /// We should lifetime-extend, but we don't because (due to technical
-  /// limitations) we can't. This happens for default member initializers,
-  /// which we don't clone for every use, so we don't have a unique
-  /// MaterializeTemporaryExpr to update.
-  ShouldExtend,
   /// Do not lifetime extend along this path.
   NoExtend
 };
@@ -928,7 +923,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {
   PathLifetimeKind Kind = PathLifetimeKind::Extend;
   for (auto Elem : Path) {
     if (Elem.Kind == IndirectLocalPathEntry::DefaultInit)
-      Kind = PathLifetimeKind::ShouldExtend;
+      return PathLifetimeKind::Extend;
     else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit)
       return PathLifetimeKind::NoExtend;
   }
@@ -1060,17 +1055,6 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
         // Also visit the temporaries lifetime-extended by this initializer.
         return true;
 
-      case PathLifetimeKind::ShouldExtend:
-        // We're supposed to lifetime-extend the temporary along this path (per
-        // the resolution of DR1815), but we don't support that yet.
-        //
-        // FIXME: Properly handle this situation. Perhaps the easiest approach
-        // would be to clone the initializer expression on each use that would
-        // lifetime extend its temporaries.
-        SemaRef.Diag(DiagLoc, diag::warn_unsupported_lifetime_extension)
-            << RK << DiagRange;
-        break;
-
       case PathLifetimeKind::NoExtend:
         // If the path goes through the initialization of a variable or field,
         // it can't possibly reach a temporary created in this full-expression.
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 21c8ae6bad0eae..273bb86478c122 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5493,10 +5493,9 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
         Res = Immediate.TransformInitializer(Param->getInit(),
                                              /*NotCopy=*/false);
       });
-      if (Res.isInvalid())
-        return ExprError();
-      Res = ConvertParamDefaultArgument(Param, Res.get(),
-                                        Res.get()->getBeginLoc());
+      if (Res.isUsable())
+        Res = ConvertParamDefaultArgument(Param, Res.get(),
+                                          Res.get()->getBeginLoc());
       if (Res.isInvalid())
         return ExprError();
       Init = Res.get();
@@ -5532,7 +5531,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   Expr *Init = nullptr;
 
   bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-
+  bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
   EnterExpressionEvaluationContext EvalContext(
       *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
 
@@ -5567,19 +5566,35 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   ImmediateCallVisitor V(getASTContext());
   if (!NestedDefaultChecking)
     V.TraverseDecl(Field);
-  if (V.HasImmediateCalls) {
+
+  // CWG1815
+  // Support lifetime extension of temporary created by aggregate
+  // initialization using a default member initializer. We should always rebuild
+  // the initializer if it contains any temporaries (if the initializer
+  // expression is an ExprWithCleanups). Then make sure the normal lifetime
+  // extension code recurses into the default initializer and does lifetime
+  // extension when warranted.
+  bool ContainsAnyTemporaries =
+      isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
+  if (V.HasImmediateCalls || InLifetimeExtendingContext ||
+      ContainsAnyTemporaries) {
     ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
                                                                    CurContext};
     ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
         NestedDefaultChecking;
-
+    // Pass down lifetime extending flag, and collect temporaries in
+    // CreateMaterializeTemporaryExpr when we rewrite the call argument.
+    keepInLifetimeExtendingContext();
     EnsureImmediateInvocationInDefaultArgs Immediate(*this);
     ExprResult Res;
+
+    // Rebuild CXXDefaultInitExpr might cause diagnostics.
+    SFINAETrap Trap(*this);
     runWithSufficientStackSpace(Loc, [&] {
       Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
                                            /*CXXDirectInit=*/false);
     });
-    if (!Res.isInvalid())
+    if (Res.isUsable())
       Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
     if (Res.isInvalid()) {
       Field->setInvalidDecl();
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 1b56b4cabd133e..4899716905cdf8 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1539,9 +1539,6 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
                                 bool ListInitialization) {
   QualType Ty = TInfo->getType();
   SourceLocation TyBeginLoc = TInfo->getTypeLoc().getBeginLoc();
-
-  assert((!ListInitialization || Exprs.size() == 1) &&
-         "List initialization must have exactly one expression.");
   SourceRange FullRange = SourceRange(TyBeginLoc, RParenOrBraceLoc);
 
   InitializedEntity Entity =
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 8f6f30434af65e..7e6e1e811209e5 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -14402,6 +14402,13 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
     if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
                        &ArgumentChanged))
       return ExprError();
+
+    if (E->isListInitialization() && !E->isStdInitListInitialization()) {
+      ExprResult Res = RebuildInitList(E->getBeginLoc(), Args, E->getEndLoc());
+      if (Res.isInvalid())
+        return ExprError();
+      Args = {Res.get()};
+    }
   }
 
   if (!getDerived().AlwaysRebuild() &&
@@ -14413,12 +14420,9 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
     return SemaRef.MaybeBindToTemporary(E);
   }
 
-  // FIXME: We should just pass E->isListInitialization(), but we're not
-  // prepared to handle list-initialization without a child InitListExpr.
   SourceLocation LParenLoc = T->getTypeLoc().getEndLoc();
   return getDerived().RebuildCXXTemporaryObjectExpr(
-      T, LParenLoc, Args, E->getEndLoc(),
-      /*ListInitialization=*/LParenLoc.isInvalid());
+      T, LParenLoc, Args, E->getEndLoc(), E->isListInitialization());
 }
 
 template<typename Derived>
diff --git a/clang/test/AST/ast-dump-default-init-json.cpp b/clang/test/AST/ast-dump-default-init-json.cpp
index 1058b4e3ea4d93..f4949a9c9eedf4 100644
--- a/clang/test/AST/ast-dump-default-init-json.cpp
+++ b/clang/test/AST/ast-dump-default-init-json.cpp
@@ -789,10 +789,10 @@ void test() {
 // CHECK-NEXT:                  "valueCategory": "lvalue",
 // CHECK-NEXT:                  "extendingDecl": {
 // CHECK-NEXT:                   "id": "0x{{.*}}",
-// CHECK-NEXT:                   "kind": "FieldDecl",
-// CHECK-NEXT:                   "name": "a",
+// CHECK-NEXT:                   "kind": "VarDecl",
+// CHECK-NEXT:                   "name": "b",
 // CHECK-NEXT:                   "type": {
-// CHECK-NEXT:                    "qualType": "const A &"
+// CHECK-NEXT:                    "qualType": "B"
 // CHECK-NEXT:                   }
 // CHECK-NEXT:                  },
 // CHECK-NEXT:                  "storageDuration": "automatic",
diff --git a/clang/test/AST/ast-dump-default-init.cpp b/clang/test/AST/ast-dump-default-init.cpp
index 15b29f04bf21bf..26864fbf15424d 100644
--- a/clang/test/AST/ast-dump-default-init.cpp
+++ b/clang/test/AST/ast-dump-default-init.cpp
@@ -13,7 +13,7 @@ void test() {
 }
 // CHECK: -CXXDefaultInitExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue has rewritten init
 // CHECK-NEXT:  `-ExprWithCleanups 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue
-// CHECK-NEXT:    `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Field 0x{{[^ ]*}} 'a' 'const A &'
+// CHECK-NEXT:    `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Var 0x{{[^ ]*}} 'b' 'B'
 // CHECK-NEXT:      `-ImplicitCastExpr 0x{{[^ ]*}} <{{.*}}> 'const A' <NoOp>
 // CHECK-NEXT:        `-CXXFunctionalCastExpr 0x{{[^ ]*}} <{{.*}}> 'A' functional cast to A <NoOp>
 // CHECK-NEXT:          `-InitListExpr 0x{{[^ ]*}} <{{.*}}> 'A'
diff --git a/clang/test/Analysis/lifetime-extended-regions.cpp b/clang/test/Analysis/lifetime-extended-regions.cpp
index 4e98bd4b0403eb..4458ad294af7cb 100644
--- a/clang/test/Analysis/lifetime-extended-regions.cpp
+++ b/clang/test/Analysis/lifetime-extended-regions.cpp
@@ -120,10 +120,11 @@ void aggregateWithReferences() {
   clang_analyzer_dump(viaReference);    // expected-warning-re {{&lifetime_extended_object{RefAggregate, viaReference, S{{[0-9]+}}} }}
   clang_analyzer_dump(viaReference.rx); // expected-warning-re {{&lifetime_extended_object{int, viaReference, S{{[0-9]+}}} }}
   clang_analyzer_dump(viaReference.ry); // expected-warning-re {{&lifetime_extended_object{Composite, viaReference, S{{[0-9]+}}} }}
-
-  // clang does not currently implement extending lifetime of object bound to reference members of aggregates,
-  // that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`)
-  RefAggregate defaultInitExtended{i}; // clang-bug does not extend `Composite`
+  
+  // FIXME: clang currently support extending lifetime of object bound to reference members of aggregates,
+  // that are created from default member initializer. But CFG and ExprEngine need to be updated to address this change.
+  // The following expect warning: {{&lifetime_extended_object{Composite, defaultInitExtended, S{{[0-9]+}}} }}
+  RefAggregate defaultInitExtended{i};
   clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }}
 }
 
diff --git a/clang/test/CXX/drs/cwg16xx.cpp b/clang/test/CXX/drs/cwg16xx.cpp
index cf6b45ceabf2cc..82ef871939d2c4 100644
--- a/clang/test/CXX/drs/cwg16xx.cpp
+++ b/clang/test/CXX/drs/cwg16xx.cpp
@@ -483,8 +483,6 @@ namespace cwg1696 { // cwg1696: 7
     const A &a = A(); // #cwg1696-D1-a
   };
   D1 d1 = {}; // #cwg1696-d1
-  // since-cxx14-warning at -1 {{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported; lifetime of temporary will end at the end of the full-expression}}
-  //   since-cxx14-note@#cwg1696-D1-a {{initializing field 'a' with default member initializer}}
 
   struct D2 {
     const A &a = A(); // #cwg1696-D2-a
diff --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp
index adfdb738e81c9e..042ad6a17fa758 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -206,19 +206,28 @@ namespace cwg1814 { // cwg1814: yes
 #endif
 }
 
-namespace cwg1815 { // cwg1815: no
+namespace cwg1815 { // cwg1815: 19
 #if __cplusplus >= 201402L
-  // FIXME: needs codegen test
-  struct A { int &&r = 0; }; // #cwg1815-A
+  struct A { int &&r = 0; };
   A a = {};
-  // since-cxx14-warning at -1 {{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported; lifetime of temporary will end at the end of the full-expression}} FIXME
-  //   since-cxx14-note@#cwg1815-A {{initializing field 'r' with default member initializer}}
 
   struct B { int &&r = 0; }; // #cwg1815-B
   // since-cxx14-error at -1 {{reference member 'r' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
   //   since-cxx14-note@#cwg1815-B {{initializing field 'r' with default member initializer}}
   //   since-cxx14-note@#cwg1815-b {{in implicit default constructor for 'cwg1815::B' first required here}}
   B b; // #cwg1815-b
+
+#if __cplusplus >= 201703L
+  struct C { const int &r = 0; };
+  constexpr C c = {}; // OK, since cwg1815
+  static_assert(c.r == 0);
+
+  constexpr int f() {
+    A a = {}; // OK, since cwg1815
+    return a.r;
+  }
+  static_assert(f() == 0);
+#endif
 #endif
 }
 
diff --git a/clang/test/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp
index 5554363cc69abb..a6d2adfd1fd2c5 100644
--- a/clang/test/CXX/special/class.temporary/p6.cpp
+++ b/clang/test/CXX/special/class.temporary/p6.cpp
@@ -269,6 +269,40 @@ void init_capture_init_list() {
   // CHECK: }
 }
 
+void check_dr1815() { // dr1815: yes
+#if __cplusplus >= 201402L
+
+  struct A {
+    int &&r = 0;
+    ~A() {}
+  };
+
+  struct B {
+    A &&a = A{};
+    ~B() {}
+  };
+  B a = {};
+  
+  // CHECK: call {{.*}}block_scope_begin_function
+  extern void block_scope_begin_function();
+  extern void block_scope_end_function();
+  block_scope_begin_function();
+  {
+    // CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
+    // CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
+    B b = {};
+  }
+  // CHECK: call {{.*}}block_scope_end_function
+  block_scope_end_function();
+
+  // CHECK: call {{.*}}some_other_function
+  extern void some_other_function();
+  some_other_function();
+  // CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
+  // CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
+#endif
+}
+
 namespace P2718R0 {
 namespace basic {
 template <typename E> using T2 = std::list<E>;
diff --git a/clang/test/SemaCXX/constexpr-default-arg.cpp b/clang/test/SemaCXX/constexpr-default-arg.cpp
index ec9b2927880bdf..901123bfb359ff 100644
--- a/clang/test/SemaCXX/constexpr-default-arg.cpp
+++ b/clang/test/SemaCXX/constexpr-default-arg.cpp
@@ -32,8 +32,8 @@ void test_default_arg2() {
 }
 
 // Check that multiple CXXDefaultInitExprs don't cause an assertion failure.
-struct A { int &&r = 0; }; // expected-note 2{{default member initializer}}
+struct A { int &&r = 0; };
 struct B { A x, y; };
-B b = {}; // expected-warning 2{{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported}}
+B b = {}; // expected-no-diagnostics
 
 }
diff --git a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp
index dd8e9c6b7fc11f..1ea8b98cd86367 100644
--- a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp
+++ b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp
@@ -27,6 +27,80 @@ class MemInit {
   C m = s;
 };
 
+namespace std {
+typedef decltype(sizeof(int)) size_t;
+
+// libc++'s implementation
+template <class _E> class initializer_list {
+  const _E *__begin_;
+  size_t __size_;
+
+  initializer_list(const _E *__b, size_t __s) : __begin_(__b), __size_(__s) {}
+
+public:
+  typedef _E value_type;
+  typedef const _E &reference;
+  typedef const _E &const_reference;
+  typedef size_t size_type;
+
+  typedef const _E *iterator;
+  typedef const _E *const_iterator;
+
+  initializer_list() : __begin_(nullptr), __size_(0) {}
+
+  size_t size() const { return __size_; }
+  const _E *begin() const { return __begin_; }
+  const _E *end() const { return __begin_ + __size_; }
+};
+} // namespace std
+
+#if __cplusplus >= 201703L
+namespace test_rebuild {
+template <typename T, int> class C {
+public:
+  C(std::initializer_list<T>);
+};
+
+template <typename T> using Ptr = __remove_pointer(T) *;
+template <typename T> C(T) -> C<Ptr<T>, sizeof(T)>;
+
+class A {
+public:
+  template <typename T1, typename T2> T1 *some_func(T2 &&);
+};
+
+struct B : A {
+  // Test CXXDefaultInitExpr rebuild issue in 
+  // https://github.com/llvm/llvm-project/pull/87933
+  int *ar = some_func<int>(C{some_func<int>(0)});
+  B() {}
+};
+
+int TestBody_got;
+template <int> class Vector {
+public:
+  Vector(std::initializer_list<int>);
+};
+template <typename... Ts> Vector(Ts...) -> Vector<sizeof...(Ts)>;
+class ProgramBuilder {
+public:
+  template <typename T, typename ARGS> int *create(ARGS);
+};
+
+struct TypeTest : ProgramBuilder {
+  int *str_f16 = create<int>(Vector{0});
+  TypeTest() {}
+};
+class TypeTest_Element_Test : TypeTest {
+  void TestBody();
+};
+void TypeTest_Element_Test::TestBody() {
+  int *expect = str_f16;
+  &TestBody_got != expect; // expected-warning {{inequality comparison result unused}}
+}
+} //  namespace test_rebuild
+#endif // __cplusplus >= 201703L
+
 #if __cplusplus >= 202002L
 // This test ensures cleanup expressions are correctly produced
 // in the presence of default member initializers.
diff --git a/clang/test/SemaCXX/eval-crashes.cpp b/clang/test/SemaCXX/eval-crashes.cpp
index 0865dafe4bf92a..21e05f19be0caa 100644
--- a/clang/test/SemaCXX/eval-crashes.cpp
+++ b/clang/test/SemaCXX/eval-crashes.cpp
@@ -25,11 +25,9 @@ namespace pr33140_0b {
 }
 
 namespace pr33140_2 {
-  // FIXME: The declaration of 'b' below should lifetime-extend two int
-  // temporaries.
-  struct A { int &&r = 0; }; // expected-note 2{{initializing field 'r' with default member initializer}}
+  struct A { int &&r = 0; };
   struct B { A x, y; };
-  B b = {}; // expected-warning 2{{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported}}
+  B b = {};
 }
 
 namespace pr33140_3 {
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index b8b733dbfa7163..0476aab0b22eee 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -10705,7 +10705,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/1815.html">1815</a></td>
     <td>CD4</td>
     <td>Lifetime extension in aggregate initialization</td>
-    <td class="none" align="center">No</td>
+    <td class="unreleased" align="center">Clang 19</td>
   </tr>
   <tr id="1816">
     <td><a href="https://cplusplus.github.io/CWG/issues/1816.html">1816</a></td>

>From dbe5cd3087de18de28e5406e3364cf2f4ef132aa Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 7 Aug 2024 23:19:14 +0800
Subject: [PATCH 02/12] [Clang] Always rebuild AST nodes when rebuild
 default-arg/init

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/lib/Sema/SemaExpr.cpp                   |  2 ++
 .../cxx11-default-member-initializers.cpp     | 19 +++++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 273bb86478c122..b5134650b01677 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5416,6 +5416,8 @@ struct EnsureImmediateInvocationInDefaultArgs
   EnsureImmediateInvocationInDefaultArgs(Sema &SemaRef)
       : TreeTransform(SemaRef) {}
 
+  bool AlwaysRebuild() { return true; }
+
   // Lambda can only have immediate invocations in the default
   // args of their parameters, which is transformed upon calling the closure.
   // The body is not a subexpression, so we have nothing to do.
diff --git a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp
index 1ea8b98cd86367..b3f3eb508446b6 100644
--- a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp
+++ b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp
@@ -99,6 +99,25 @@ void TypeTest_Element_Test::TestBody() {
   &TestBody_got != expect; // expected-warning {{inequality comparison result unused}}
 }
 } //  namespace test_rebuild
+namespace test_rebuild2 {
+struct F {
+  int g;
+};
+struct H {};
+struct I {
+  I(const F &);
+  I(H);
+};
+struct L {
+  I i = I({.g = 0});
+};
+struct N : L {};
+
+void f() {
+  delete new L; // Ok
+  delete new N; // Ok
+}
+} // namespace test_rebuild2
 #endif // __cplusplus >= 201703L
 
 #if __cplusplus >= 202002L

>From 58c947b0970a4820b60ffaa3f21ff6f9fd850cf9 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 7 Aug 2024 23:22:56 +0800
Subject: [PATCH 03/12] [Clang] Update ReleaseNotes

Signed-off-by: yronglin <yronglin777 at 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 978b4ac8ea2e37..326962b42df80c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -78,6 +78,9 @@ C++ Language Changes
 - Allow single element access of GCC vector/ext_vector_type object to be 
   constant expression. Supports the `V.xyzw` syntax and other tidbits 
   as seen in OpenCL. Selecting multiple elements is left as a future work.
+- Implement `CWG1815 <https://wg21.link/CWG1815>`_. Support lifetime extension 
+  of temporary created by aggregate initialization using a default member
+  initializer.
 
 C++17 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^

>From 0a8214c8f38826d0be8a54d5c4a41448484286f1 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 26 Aug 2024 19:53:07 +0800
Subject: [PATCH 04/12] [Clang] Update clang version in cxx_dr_status

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/www/cxx_dr_status.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 44da3536edbc30..552c21484ddfa8 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -10705,7 +10705,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/1815.html">1815</a></td>
     <td>CD4</td>
     <td>Lifetime extension in aggregate initialization</td>
-    <td class="unreleased" align="center">Clang 19</td>
+    <td class="unreleased" align="center">Clang 20</td>
   </tr>
   <tr id="1816">
     <td><a href="https://cplusplus.github.io/CWG/issues/1816.html">1816</a></td>

>From ad08b40669831904805bb48e9a85b730b7828512 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 28 Aug 2024 00:05:47 +0800
Subject: [PATCH 05/12] [clang] Only rebuild default member initializer in
 lifetime extension context

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/lib/Sema/SemaExpr.cpp | 10 +++++-----
 clang/lib/Sema/SemaInit.cpp | 29 +++++++++++++++++++++++++++--
 2 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 3912a93b737c19..4e87e62b2f5d5c 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5583,14 +5583,16 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   // CWG1815
   // Support lifetime extension of temporary created by aggregate
   // initialization using a default member initializer. We should always rebuild
-  // the initializer if it contains any temporaries (if the initializer
+  // the initializer in a lifetime extension context (if the initializer
   // expression is an ExprWithCleanups). Then make sure the normal lifetime
   // extension code recurses into the default initializer and does lifetime
   // extension when warranted.
   bool ContainsAnyTemporaries =
       isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
-  if (V.HasImmediateCalls || InLifetimeExtendingContext ||
-      ContainsAnyTemporaries) {
+  if (Field->getInClassInitializer() &&
+      !Field->getInClassInitializer()->containsErrors() &&
+      (V.HasImmediateCalls ||
+       (InLifetimeExtendingContext && ContainsAnyTemporaries))) {
     ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
                                                                    CurContext};
     ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
@@ -5601,8 +5603,6 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
     EnsureImmediateInvocationInDefaultArgs Immediate(*this);
     ExprResult Res;
 
-    // Rebuild CXXDefaultInitExpr might cause diagnostics.
-    SFINAETrap Trap(*this);
     runWithSufficientStackSpace(Loc, [&] {
       Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
                                            /*CXXDirectInit=*/false);
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index dcfe3bc80c87ac..9d7db7ebbe231e 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -750,8 +750,33 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
     if (Field->hasInClassInitializer()) {
       if (VerifyOnly)
         return;
-
-      ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
+      ExprResult DIE;
+      {
+        // Enter a lifetime extension context, then we can support lifetime
+        // extension of temporary created by aggregate initialization using a
+        // default member initializer (DR1815 https://wg21.link/CWG1815).
+        //
+        // In a lifetime extension context, BuildCXXDefaultInitExpr will clone
+        // the initializer expression on each use and the temporaries which
+        // actually bound to a reference member should be extended here.
+        //
+        // FIXME: In default member initializer, lifetime extension context will
+        // collect the MaterializedTemporaryExprs which bound to a reference
+        // member, but we don't need that, can we avoid this collection action?
+        EnterExpressionEvaluationContext LifetimeExtensionContext(
+            SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
+            /*LambdaContextDecl=*/nullptr,
+            Sema::ExpressionEvaluationContextRecord::EK_Other, true);
+
+        // Lifetime extension in default-member-init.
+        auto &LastRecord = SemaRef.ExprEvalContexts.back();
+
+        // Just copy previous record, make sure we haven't forget anything.
+        LastRecord =
+            SemaRef.ExprEvalContexts[SemaRef.ExprEvalContexts.size() - 2];
+        LastRecord.InLifetimeExtendingContext = true;
+        DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
+      }
       if (DIE.isInvalid()) {
         hadError = true;
         return;

>From 9ad4dcb7b5b56bda8dddf9a8f7d298134c0204a7 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 2 Sep 2024 20:56:21 +0800
Subject: [PATCH 06/12] [clang] Address review comments, extend
 InLifetimeExtendingContext flag

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/include/clang/Sema/Sema.h | 30 +++++++++++++++++++++++-------
 clang/lib/Parse/ParseDecl.cpp   |  7 +++----
 clang/lib/Sema/SemaExpr.cpp     |  9 +++++----
 clang/lib/Sema/SemaInit.cpp     | 24 +++++++++---------------
 clang/lib/Sema/TreeTransform.h  |  7 +++----
 5 files changed, 43 insertions(+), 34 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1f7e555d1b8717..9bb50d641f3d56 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6298,6 +6298,15 @@ class Sema final : public SemaBase {
 
   using ImmediateInvocationCandidate = llvm::PointerIntPair<ConstantExpr *, 1>;
 
+  enum class LifetimeExtendingContext {
+    None,       // Not in a lifetime extending context.
+    FlagOnly,   // A flag indicating whether we are in lifetime extending
+                // context.
+    CollectTemp // A flag indicating whether we are in lifetime
+                // extending context, additional, collects
+                // temporary variables created in this context.
+  };
+
   /// Data structure used to record current or nested
   /// expression evaluation contexts.
   struct ExpressionEvaluationContextRecord {
@@ -6380,7 +6389,8 @@ class Sema final : public SemaBase {
     /// Whether we are currently in a context in which all temporaries must be
     /// lifetime-extended, even if they're not bound to a reference (for
     /// example, in a for-range initializer).
-    bool InLifetimeExtendingContext = false;
+    LifetimeExtendingContext InLifetimeExtendingContext =
+        LifetimeExtendingContext::None;
 
     // When evaluating immediate functions in the initializer of a default
     // argument or default member initializer, this is the declaration whose
@@ -7804,7 +7814,14 @@ class Sema final : public SemaBase {
   bool isInLifetimeExtendingContext() const {
     assert(!ExprEvalContexts.empty() &&
            "Must be in an expression evaluation context");
-    return ExprEvalContexts.back().InLifetimeExtendingContext;
+    return currentEvaluationContext().InLifetimeExtendingContext !=
+           LifetimeExtendingContext::None;
+  }
+
+  LifetimeExtendingContext getLifetimeExtendingContext() const {
+    assert(!ExprEvalContexts.empty() &&
+           "Must be in an expression evaluation context");
+    return currentEvaluationContext().InLifetimeExtendingContext;
   }
 
   bool isCheckingDefaultArgumentOrInitializer() const {
@@ -7850,11 +7867,10 @@ class Sema final : public SemaBase {
   /// flag from previous context.
   void keepInLifetimeExtendingContext() {
     if (ExprEvalContexts.size() > 2 &&
-        parentEvaluationContext().InLifetimeExtendingContext) {
-      auto &LastRecord = ExprEvalContexts.back();
-      auto &PrevRecord = parentEvaluationContext();
-      LastRecord.InLifetimeExtendingContext =
-          PrevRecord.InLifetimeExtendingContext;
+        parentEvaluationContext().InLifetimeExtendingContext !=
+            LifetimeExtendingContext::None) {
+      currentEvaluationContext().InLifetimeExtendingContext =
+          parentEvaluationContext().InLifetimeExtendingContext;
     }
   }
 
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 78d729c5ef7d8a..80c37a1275be5d 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2483,10 +2483,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
           getLangOpts().CPlusPlus23);
 
       // P2718R0 - Lifetime extension in range-based for loops.
-      if (getLangOpts().CPlusPlus23) {
-        auto &LastRecord = Actions.ExprEvalContexts.back();
-        LastRecord.InLifetimeExtendingContext = true;
-      }
+      if (getLangOpts().CPlusPlus23)
+        Actions.currentEvaluationContext().InLifetimeExtendingContext =
+            Sema::LifetimeExtendingContext::CollectTemp;
 
       if (getLangOpts().OpenMP)
         Actions.OpenMP().startOpenMPCXXRangeFor();
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 4e87e62b2f5d5c..a976a3d71aa4bb 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -17652,11 +17652,12 @@ void Sema::PopExpressionEvaluationContext() {
 
   // Append the collected materialized temporaries into previous context before
   // exit if the previous also is a lifetime extending context.
-  auto &PrevRecord = parentEvaluationContext();
-  if (getLangOpts().CPlusPlus23 && Rec.InLifetimeExtendingContext &&
-      PrevRecord.InLifetimeExtendingContext &&
+  if (getLangOpts().CPlusPlus23 &&
+      Rec.InLifetimeExtendingContext == LifetimeExtendingContext::CollectTemp &&
+      parentEvaluationContext().InLifetimeExtendingContext ==
+          LifetimeExtendingContext::CollectTemp &&
       !Rec.ForRangeLifetimeExtendTemps.empty()) {
-    PrevRecord.ForRangeLifetimeExtendTemps.append(
+    parentEvaluationContext().ForRangeLifetimeExtendTemps.append(
         Rec.ForRangeLifetimeExtendTemps);
   }
 
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 9d7db7ebbe231e..4b0725d655de2b 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -752,29 +752,23 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
         return;
       ExprResult DIE;
       {
-        // Enter a lifetime extension context, then we can support lifetime
+        // Enter a lifetime extending context, then we can support lifetime
         // extension of temporary created by aggregate initialization using a
-        // default member initializer (DR1815 https://wg21.link/CWG1815).
+        // default member initializer (CWG1815 https://wg21.link/CWG1815).
         //
         // In a lifetime extension context, BuildCXXDefaultInitExpr will clone
-        // the initializer expression on each use and the temporaries which
-        // actually bound to a reference member should be extended here.
-        //
-        // FIXME: In default member initializer, lifetime extension context will
-        // collect the MaterializedTemporaryExprs which bound to a reference
-        // member, but we don't need that, can we avoid this collection action?
-        EnterExpressionEvaluationContext LifetimeExtensionContext(
+        // the initializer expression for each use and the temporaries which
+        // binds to a reference member should be extended here.
+        EnterExpressionEvaluationContext LifetimeExtendingContext(
             SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
             /*LambdaContextDecl=*/nullptr,
             Sema::ExpressionEvaluationContextRecord::EK_Other, true);
 
         // Lifetime extension in default-member-init.
-        auto &LastRecord = SemaRef.ExprEvalContexts.back();
-
-        // Just copy previous record, make sure we haven't forget anything.
-        LastRecord =
-            SemaRef.ExprEvalContexts[SemaRef.ExprEvalContexts.size() - 2];
-        LastRecord.InLifetimeExtendingContext = true;
+        // Just copy parent record, make sure we haven't forget anything.
+        SemaRef.currentEvaluationContext() = SemaRef.parentEvaluationContext();
+        SemaRef.currentEvaluationContext().InLifetimeExtendingContext =
+            Sema::LifetimeExtendingContext::FlagOnly;
         DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
       }
       if (DIE.isInvalid()) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e7e77eb4f33108..7ed3934fb1c5e4 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -8892,10 +8892,9 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
       getSema().getLangOpts().CPlusPlus23);
 
   // P2718R0 - Lifetime extension in range-based for loops.
-  if (getSema().getLangOpts().CPlusPlus23) {
-    auto &LastRecord = getSema().ExprEvalContexts.back();
-    LastRecord.InLifetimeExtendingContext = true;
-  }
+  if (getSema().getLangOpts().CPlusPlus23)
+    getSema().currentEvaluationContext().InLifetimeExtendingContext =
+        Sema::LifetimeExtendingContext::CollectTemp;
   StmtResult Init =
       S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult();
   if (Init.isInvalid())

>From 6e0c524073d13e5386bb36a3f48f402a73f03009 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 2 Sep 2024 21:06:47 +0800
Subject: [PATCH 07/12] [clang] amend previous commit

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/lib/Sema/SemaInit.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 4b0725d655de2b..e719d4158a7e06 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7494,10 +7494,8 @@ Sema::CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary,
   // are done in both CreateMaterializeTemporaryExpr and MaybeBindToTemporary,
   // but there may be a chance to merge them.
   Cleanup.setExprNeedsCleanups(false);
-  if (isInLifetimeExtendingContext()) {
-    auto &Record = ExprEvalContexts.back();
-    Record.ForRangeLifetimeExtendTemps.push_back(MTE);
-  }
+  if (getLifetimeExtendingContext() == LifetimeExtendingContext::CollectTemp)
+    currentEvaluationContext().ForRangeLifetimeExtendTemps.push_back(MTE);
   return MTE;
 }
 

>From 077753f368592e0dfc8b8424a4f8bcc398319b80 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Fri, 6 Sep 2024 23:53:02 +0800
Subject: [PATCH 08/12] Introduce RebuildDefaultArgOrDefaultInit and add more
 std examples into test

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/include/clang/Sema/Sema.h               | 39 ++++++++-----------
 clang/lib/Parse/ParseDecl.cpp                 |  8 ++--
 clang/lib/Sema/SemaExpr.cpp                   | 17 ++++----
 clang/lib/Sema/SemaInit.cpp                   | 23 +++++------
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  1 +
 clang/lib/Sema/TreeTransform.h                |  9 +++--
 clang/test/CXX/drs/cwg16xx.cpp                | 22 +++++++++++
 7 files changed, 66 insertions(+), 53 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9bb50d641f3d56..8bafce72e2a36d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6298,15 +6298,6 @@ class Sema final : public SemaBase {
 
   using ImmediateInvocationCandidate = llvm::PointerIntPair<ConstantExpr *, 1>;
 
-  enum class LifetimeExtendingContext {
-    None,       // Not in a lifetime extending context.
-    FlagOnly,   // A flag indicating whether we are in lifetime extending
-                // context.
-    CollectTemp // A flag indicating whether we are in lifetime
-                // extending context, additional, collects
-                // temporary variables created in this context.
-  };
-
   /// Data structure used to record current or nested
   /// expression evaluation contexts.
   struct ExpressionEvaluationContextRecord {
@@ -6389,8 +6380,10 @@ class Sema final : public SemaBase {
     /// Whether we are currently in a context in which all temporaries must be
     /// lifetime-extended, even if they're not bound to a reference (for
     /// example, in a for-range initializer).
-    LifetimeExtendingContext InLifetimeExtendingContext =
-        LifetimeExtendingContext::None;
+    bool InLifetimeExtendingContext = false;
+
+    /// Whether we should rebuild CXXDefaultArgExpr and CXXDefaultInitExpr.
+    bool RebuildDefaultArgOrDefaultInit = false;
 
     // When evaluating immediate functions in the initializer of a default
     // argument or default member initializer, this is the declaration whose
@@ -7812,16 +7805,11 @@ class Sema final : public SemaBase {
   }
 
   bool isInLifetimeExtendingContext() const {
-    assert(!ExprEvalContexts.empty() &&
-           "Must be in an expression evaluation context");
-    return currentEvaluationContext().InLifetimeExtendingContext !=
-           LifetimeExtendingContext::None;
+    return currentEvaluationContext().InLifetimeExtendingContext;
   }
 
-  LifetimeExtendingContext getLifetimeExtendingContext() const {
-    assert(!ExprEvalContexts.empty() &&
-           "Must be in an expression evaluation context");
-    return currentEvaluationContext().InLifetimeExtendingContext;
+  bool needRebuildDefaultArgOrInit() const {
+    return currentEvaluationContext().RebuildDefaultArgOrDefaultInit;
   }
 
   bool isCheckingDefaultArgumentOrInitializer() const {
@@ -7866,12 +7854,17 @@ class Sema final : public SemaBase {
   /// keepInLifetimeExtendingContext - Pull down InLifetimeExtendingContext
   /// flag from previous context.
   void keepInLifetimeExtendingContext() {
-    if (ExprEvalContexts.size() > 2 &&
-        parentEvaluationContext().InLifetimeExtendingContext !=
-            LifetimeExtendingContext::None) {
+    if (ExprEvalContexts.size() > 2)
       currentEvaluationContext().InLifetimeExtendingContext =
           parentEvaluationContext().InLifetimeExtendingContext;
-    }
+  }
+
+  /// keepInRebuildDefaultArgInitContext - Pull down
+  /// RebuildDefaultArgOrDefaultInit flag from previous context.
+  void keepInRebuildDefaultArgOrInitContext() {
+    if (ExprEvalContexts.size() > 2)
+      currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
+          parentEvaluationContext().RebuildDefaultArgOrDefaultInit;
   }
 
   DefaultedComparisonKind getDefaultedComparisonKind(const FunctionDecl *FD) {
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 80c37a1275be5d..1a1652a4157fea 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2483,9 +2483,11 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
           getLangOpts().CPlusPlus23);
 
       // P2718R0 - Lifetime extension in range-based for loops.
-      if (getLangOpts().CPlusPlus23)
-        Actions.currentEvaluationContext().InLifetimeExtendingContext =
-            Sema::LifetimeExtendingContext::CollectTemp;
+      if (getLangOpts().CPlusPlus23) {
+        auto &LastRecord = Actions.currentEvaluationContext();
+        LastRecord.InLifetimeExtendingContext = true;
+        LastRecord.RebuildDefaultArgOrDefaultInit = true;
+      }
 
       if (getLangOpts().OpenMP)
         Actions.OpenMP().startOpenMPCXXRangeFor();
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index a976a3d71aa4bb..00fd56a43005ff 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5457,7 +5457,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
   assert(Param->hasDefaultArg() && "can't build nonexistent default arg");
 
   bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-  bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
+  bool NeedRebuild = needRebuildDefaultArgOrInit();
   std::optional<ExpressionEvaluationContextRecord::InitializationContext>
       InitializationContext =
           OutermostDeclarationWithDelayedImmediateInvocations();
@@ -5493,7 +5493,8 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
 
     // Rewrite the call argument that was created from the corresponding
     // parameter's default argument.
-    if (V.HasImmediateCalls || InLifetimeExtendingContext) {
+    if (V.HasImmediateCalls ||
+        (NeedRebuild && isa_and_present<ExprWithCleanups>(Param->getInit()))) {
       if (V.HasImmediateCalls)
         ExprEvalContexts.back().DelayedDefaultInitializationContext = {
             CallLoc, Param, CurContext};
@@ -5544,10 +5545,9 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   Expr *Init = nullptr;
 
   bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-  bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
+  bool NeedRebuild = needRebuildDefaultArgOrInit();
   EnterExpressionEvaluationContext EvalContext(
       *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
-
   if (!Field->getInClassInitializer()) {
     // Maybe we haven't instantiated the in-class initializer. Go check the
     // pattern FieldDecl to see if it has one.
@@ -5591,8 +5591,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
       isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
   if (Field->getInClassInitializer() &&
       !Field->getInClassInitializer()->containsErrors() &&
-      (V.HasImmediateCalls ||
-       (InLifetimeExtendingContext && ContainsAnyTemporaries))) {
+      (V.HasImmediateCalls || (NeedRebuild && ContainsAnyTemporaries))) {
     ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
                                                                    CurContext};
     ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
@@ -17652,10 +17651,8 @@ void Sema::PopExpressionEvaluationContext() {
 
   // Append the collected materialized temporaries into previous context before
   // exit if the previous also is a lifetime extending context.
-  if (getLangOpts().CPlusPlus23 &&
-      Rec.InLifetimeExtendingContext == LifetimeExtendingContext::CollectTemp &&
-      parentEvaluationContext().InLifetimeExtendingContext ==
-          LifetimeExtendingContext::CollectTemp &&
+  if (getLangOpts().CPlusPlus23 && Rec.InLifetimeExtendingContext &&
+      parentEvaluationContext().InLifetimeExtendingContext &&
       !Rec.ForRangeLifetimeExtendTemps.empty()) {
     parentEvaluationContext().ForRangeLifetimeExtendTemps.append(
         Rec.ForRangeLifetimeExtendTemps);
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index e719d4158a7e06..a8b4b7f935f3fe 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -752,23 +752,18 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
         return;
       ExprResult DIE;
       {
-        // Enter a lifetime extending context, then we can support lifetime
-        // extension of temporary created by aggregate initialization using a
-        // default member initializer (CWG1815 https://wg21.link/CWG1815).
-        //
-        // In a lifetime extension context, BuildCXXDefaultInitExpr will clone
-        // the initializer expression for each use and the temporaries which
-        // binds to a reference member should be extended here.
-        EnterExpressionEvaluationContext LifetimeExtendingContext(
+        // Enter a default initializer rebuild context, then we can support
+        // lifetime extension of temporary created by aggregate initialization
+        // using a default member initializer.
+        // CWG1815 (https://wg21.link/CWG1815).
+        EnterExpressionEvaluationContext RebuildDefaultInit(
             SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
             /*LambdaContextDecl=*/nullptr,
             Sema::ExpressionEvaluationContextRecord::EK_Other, true);
-
-        // Lifetime extension in default-member-init.
-        // Just copy parent record, make sure we haven't forget anything.
+        // Just copy previous record, make sure we haven't forget anything.
         SemaRef.currentEvaluationContext() = SemaRef.parentEvaluationContext();
-        SemaRef.currentEvaluationContext().InLifetimeExtendingContext =
-            Sema::LifetimeExtendingContext::FlagOnly;
+        SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
+            true;
         DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
       }
       if (DIE.isInvalid()) {
@@ -7494,7 +7489,7 @@ Sema::CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary,
   // are done in both CreateMaterializeTemporaryExpr and MaybeBindToTemporary,
   // but there may be a chance to merge them.
   Cleanup.setExprNeedsCleanups(false);
-  if (getLifetimeExtendingContext() == LifetimeExtendingContext::CollectTemp)
+  if (isInLifetimeExtendingContext())
     currentEvaluationContext().ForRangeLifetimeExtendTemps.push_back(MTE);
   return MTE;
 }
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 0e064be2391838..10526d056e20ca 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5479,6 +5479,7 @@ void Sema::InstantiateVariableInitializer(
         *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var);
 
     keepInLifetimeExtendingContext();
+    keepInRebuildDefaultArgOrInitContext();
     // Instantiate the initializer.
     ExprResult Init;
 
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 7ed3934fb1c5e4..da0a73565753c0 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -4255,6 +4255,7 @@ ExprResult TreeTransform<Derived>::TransformInitializer(Expr *Init,
       Construct->isListInitialization());
 
   getSema().keepInLifetimeExtendingContext();
+  getSema().keepInRebuildDefaultArgOrInitContext();
   SmallVector<Expr*, 8> NewArgs;
   bool ArgChanged = false;
   if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(),
@@ -8892,9 +8893,11 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
       getSema().getLangOpts().CPlusPlus23);
 
   // P2718R0 - Lifetime extension in range-based for loops.
-  if (getSema().getLangOpts().CPlusPlus23)
-    getSema().currentEvaluationContext().InLifetimeExtendingContext =
-        Sema::LifetimeExtendingContext::CollectTemp;
+  if (getSema().getLangOpts().CPlusPlus23) {
+    auto &LastRecord = getSema().currentEvaluationContext();
+    LastRecord.InLifetimeExtendingContext = true;
+    LastRecord.RebuildDefaultArgOrDefaultInit = true;
+  }
   StmtResult Init =
       S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult();
   if (Init.isInvalid())
diff --git a/clang/test/CXX/drs/cwg16xx.cpp b/clang/test/CXX/drs/cwg16xx.cpp
index 82ef871939d2c4..b1adfea38c1a7c 100644
--- a/clang/test/CXX/drs/cwg16xx.cpp
+++ b/clang/test/CXX/drs/cwg16xx.cpp
@@ -449,6 +449,28 @@ namespace cwg1696 { // cwg1696: 7
       //   since-cxx14-note at -2 {{default member initializer declared here}}
     };
     A a{a, a};
+
+    struct A1 {
+      A1() : v(42) {}
+      // since-cxx14-error at -1 {{reference member 'v' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+      // since-cxx14-note@#cwg1696-A1 {{reference member declared here}}
+      const int &v; // #cwg1696-A1
+    };
+#if 1
+    struct A2 {
+      A2() = default;
+      // since-cxx14-error at -1 {{reference member 'v' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+      // since-cxx14-note-re@#cwg1696-A2-b {{in defaulted default constructor for {{.*}} first required here}}
+      // since-cxx14-note@#cwg1696-A2-a {{initializing field 'v' with default member initializer}}
+      A2(int v) : v(v) {}
+      // since-cxx14-warning at -1 {{binding reference member 'v' to stack allocated parameter 'v'}}
+      // since-cxx14-note@#cwg1696-A2-a {{reference member declared here}}
+      const int &v = 42;  // #cwg1696-A2-a
+    };
+    A2 a1;    // #cwg1696-A2-b
+    
+    A2 a2(1); // OK, unfortunately
+#endif
 #endif
   }
 

>From d3284e65c2729912b6bcbf0a444681b1179030db Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sat, 7 Sep 2024 00:10:48 +0800
Subject: [PATCH 09/12] [NFC] Revert unnecessary changes

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/lib/Sema/SemaExpr.cpp | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 00fd56a43005ff..3d33c2ff071327 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5507,9 +5507,10 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
         Res = Immediate.TransformInitializer(Param->getInit(),
                                              /*NotCopy=*/false);
       });
-      if (Res.isUsable())
-        Res = ConvertParamDefaultArgument(Param, Res.get(),
-                                          Res.get()->getBeginLoc());
+      if (Res.isInvalid())
+        return ExprError();
+      Res = ConvertParamDefaultArgument(Param, Res.get(),
+                                        Res.get()->getBeginLoc());
       if (Res.isInvalid())
         return ExprError();
       Init = Res.get();
@@ -5548,6 +5549,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   bool NeedRebuild = needRebuildDefaultArgOrInit();
   EnterExpressionEvaluationContext EvalContext(
       *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
+
   if (!Field->getInClassInitializer()) {
     // Maybe we haven't instantiated the in-class initializer. Go check the
     // pattern FieldDecl to see if it has one.
@@ -5582,9 +5584,9 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
 
   // CWG1815
   // Support lifetime extension of temporary created by aggregate
-  // initialization using a default member initializer. We should always rebuild
-  // the initializer in a lifetime extension context (if the initializer
-  // expression is an ExprWithCleanups). Then make sure the normal lifetime
+  // initialization using a default member initializer. We should rebuild
+  // the initializer in a lifetime extension context if the initializer
+  // expression is an ExprWithCleanups. Then make sure the normal lifetime
   // extension code recurses into the default initializer and does lifetime
   // extension when warranted.
   bool ContainsAnyTemporaries =
@@ -5606,7 +5608,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
       Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
                                            /*CXXDirectInit=*/false);
     });
-    if (Res.isUsable())
+    if (!Res.isInvalid())
       Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
     if (Res.isInvalid()) {
       Field->setInvalidDecl();

>From efbf2356660dd63988cc854c5df9af6fb199dc54 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sat, 7 Sep 2024 00:13:38 +0800
Subject: [PATCH 10/12] Revert unnecessary changes

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/lib/Sema/SemaExpr.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 3d33c2ff071327..354417f385fa71 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5603,7 +5603,6 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
     keepInLifetimeExtendingContext();
     EnsureImmediateInvocationInDefaultArgs Immediate(*this);
     ExprResult Res;
-
     runWithSufficientStackSpace(Loc, [&] {
       Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
                                            /*CXXDirectInit=*/false);

>From 3d425bcd88192717026b28d6a0d8abf953a9bd63 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sat, 7 Sep 2024 00:19:55 +0800
Subject: [PATCH 11/12] [NFC] Remove debug code

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/test/CXX/drs/cwg16xx.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/test/CXX/drs/cwg16xx.cpp b/clang/test/CXX/drs/cwg16xx.cpp
index b1adfea38c1a7c..95e241f0d03e9b 100644
--- a/clang/test/CXX/drs/cwg16xx.cpp
+++ b/clang/test/CXX/drs/cwg16xx.cpp
@@ -456,7 +456,7 @@ namespace cwg1696 { // cwg1696: 7
       // since-cxx14-note@#cwg1696-A1 {{reference member declared here}}
       const int &v; // #cwg1696-A1
     };
-#if 1
+
     struct A2 {
       A2() = default;
       // since-cxx14-error at -1 {{reference member 'v' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
@@ -470,7 +470,6 @@ namespace cwg1696 { // cwg1696: 7
     A2 a1;    // #cwg1696-A2-b
     
     A2 a2(1); // OK, unfortunately
-#endif
 #endif
   }
 

>From 8c3c5842c549bcb92fe9fb09ea933d4803a69c85 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sun, 8 Sep 2024 13:51:14 +0800
Subject: [PATCH 12/12] Remove
 keepInLifetimeExtendingContext/keepInRebuildDefaultArgOrInitContext

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/include/clang/Sema/Sema.h                | 16 ----------------
 clang/lib/Sema/SemaExpr.cpp                    |  6 ++++--
 clang/lib/Sema/SemaTemplateInstantiateDecl.cpp |  6 ++++--
 clang/lib/Sema/TreeTransform.h                 |  6 ++++--
 4 files changed, 12 insertions(+), 22 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 8bafce72e2a36d..b6e0123ab1c1d1 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7851,22 +7851,6 @@ class Sema final : public SemaBase {
     return Res;
   }
 
-  /// keepInLifetimeExtendingContext - Pull down InLifetimeExtendingContext
-  /// flag from previous context.
-  void keepInLifetimeExtendingContext() {
-    if (ExprEvalContexts.size() > 2)
-      currentEvaluationContext().InLifetimeExtendingContext =
-          parentEvaluationContext().InLifetimeExtendingContext;
-  }
-
-  /// keepInRebuildDefaultArgInitContext - Pull down
-  /// RebuildDefaultArgOrDefaultInit flag from previous context.
-  void keepInRebuildDefaultArgOrInitContext() {
-    if (ExprEvalContexts.size() > 2)
-      currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
-          parentEvaluationContext().RebuildDefaultArgOrDefaultInit;
-  }
-
   DefaultedComparisonKind getDefaultedComparisonKind(const FunctionDecl *FD) {
     return getDefaultedFunctionKind(FD).asComparison();
   }
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 354417f385fa71..ad5ee44737e30f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5500,7 +5500,8 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
             CallLoc, Param, CurContext};
       // Pass down lifetime extending flag, and collect temporaries in
       // CreateMaterializeTemporaryExpr when we rewrite the call argument.
-      keepInLifetimeExtendingContext();
+      currentEvaluationContext().InLifetimeExtendingContext =
+          parentEvaluationContext().InLifetimeExtendingContext;
       EnsureImmediateInvocationInDefaultArgs Immediate(*this);
       ExprResult Res;
       runWithSufficientStackSpace(CallLoc, [&] {
@@ -5600,7 +5601,8 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
         NestedDefaultChecking;
     // Pass down lifetime extending flag, and collect temporaries in
     // CreateMaterializeTemporaryExpr when we rewrite the call argument.
-    keepInLifetimeExtendingContext();
+    currentEvaluationContext().InLifetimeExtendingContext =
+        parentEvaluationContext().InLifetimeExtendingContext;
     EnsureImmediateInvocationInDefaultArgs Immediate(*this);
     ExprResult Res;
     runWithSufficientStackSpace(Loc, [&] {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 10526d056e20ca..0025d071934d90 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5478,8 +5478,10 @@ void Sema::InstantiateVariableInitializer(
     EnterExpressionEvaluationContext Evaluated(
         *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var);
 
-    keepInLifetimeExtendingContext();
-    keepInRebuildDefaultArgOrInitContext();
+    currentEvaluationContext().InLifetimeExtendingContext =
+        parentEvaluationContext().InLifetimeExtendingContext;
+    currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
+        parentEvaluationContext().RebuildDefaultArgOrDefaultInit;
     // Instantiate the initializer.
     ExprResult Init;
 
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index da0a73565753c0..b346793d9b944a 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -4254,8 +4254,10 @@ ExprResult TreeTransform<Derived>::TransformInitializer(Expr *Init,
       getSema(), EnterExpressionEvaluationContext::InitList,
       Construct->isListInitialization());
 
-  getSema().keepInLifetimeExtendingContext();
-  getSema().keepInRebuildDefaultArgOrInitContext();
+  getSema().currentEvaluationContext().InLifetimeExtendingContext =
+      getSema().parentEvaluationContext().InLifetimeExtendingContext;
+  getSema().currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
+      getSema().parentEvaluationContext().RebuildDefaultArgOrDefaultInit;
   SmallVector<Expr*, 8> NewArgs;
   bool ArgChanged = false;
   if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(),



More information about the cfe-commits mailing list