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

via cfe-commits cfe-commits at lists.llvm.org
Sat Apr 6 09:17:59 PDT 2024


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

>From 55f6fe094d50759784fdf7ba2e80616bc7bf3f4b Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 2 Apr 2024 23:47:33 +0800
Subject: [PATCH] [Clang] Extend lifetime of temporaries in mem-default-init
 for P2718R0

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/include/clang/Sema/Sema.h               |  47 --------
 clang/lib/CodeGen/CGExpr.cpp                  |  62 +++++-----
 clang/lib/CodeGen/CGStmt.cpp                  |  18 +++
 clang/lib/CodeGen/CodeGenFunction.h           |  31 +++++
 clang/lib/Parse/ParseDecl.cpp                 |   4 -
 clang/lib/Sema/SemaExpr.cpp                   |  11 +-
 clang/lib/Sema/SemaExprCXX.cpp                |   2 +-
 clang/lib/Sema/SemaInit.cpp                   |  36 +++---
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   1 -
 clang/lib/Sema/TreeTransform.h                |   4 -
 clang/test/CXX/drs/dr16xx.cpp                 |   3 +-
 clang/test/CXX/drs/dr18xx.cpp                 |   7 +-
 clang/test/CXX/special/class.temporary/p6.cpp | 106 ++++++++++++++++++
 clang/test/SemaCXX/constexpr-default-arg.cpp  |   4 +-
 clang/test/SemaCXX/eval-crashes.cpp           |   6 +-
 15 files changed, 225 insertions(+), 117 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f49bc724c96c89..0a07e850c74e47 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5078,34 +5078,6 @@ class Sema final : public SemaBase {
     /// example, in a for-range initializer).
     bool InLifetimeExtendingContext = false;
 
-    /// Whether we are currently in a context in which all temporaries must be
-    /// materialized.
-    ///
-    /// [class.temporary]/p2:
-    /// The materialization of a temporary object is generally delayed as long
-    /// as possible in order to avoid creating unnecessary temporary objects.
-    ///
-    /// Temporary objects are materialized:
-    ///   (2.1) when binding a reference to a prvalue ([dcl.init.ref],
-    ///   [expr.type.conv], [expr.dynamic.cast], [expr.static.cast],
-    ///   [expr.const.cast], [expr.cast]),
-    ///
-    ///   (2.2) when performing member access on a class prvalue ([expr.ref],
-    ///   [expr.mptr.oper]),
-    ///
-    ///   (2.3) when performing an array-to-pointer conversion or subscripting
-    ///   on an array prvalue ([conv.array], [expr.sub]),
-    ///
-    ///   (2.4) when initializing an object of type
-    ///   std​::​initializer_list<T> from a braced-init-list
-    ///   ([dcl.init.list]),
-    ///
-    ///   (2.5) for certain unevaluated operands ([expr.typeid], [expr.sizeof])
-    ///
-    ///   (2.6) when a prvalue that has type other than cv void appears as a
-    ///   discarded-value expression ([expr.context]).
-    bool InMaterializeTemporaryObjectContext = false;
-
     // When evaluating immediate functions in the initializer of a default
     // argument or default member initializer, this is the declaration whose
     // default initializer is being evaluated and the location of the call
@@ -6386,19 +6358,6 @@ class Sema final : public SemaBase {
     }
   }
 
-  /// keepInMaterializeTemporaryObjectContext - Pull down
-  /// InMaterializeTemporaryObjectContext flag from previous context.
-  void keepInMaterializeTemporaryObjectContext() {
-    if (ExprEvalContexts.size() > 2 &&
-        ExprEvalContexts[ExprEvalContexts.size() - 2]
-            .InMaterializeTemporaryObjectContext) {
-      auto &LastRecord = ExprEvalContexts.back();
-      auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
-      LastRecord.InMaterializeTemporaryObjectContext =
-          PrevRecord.InMaterializeTemporaryObjectContext;
-    }
-  }
-
   DefaultedComparisonKind getDefaultedComparisonKind(const FunctionDecl *FD) {
     return getDefaultedFunctionKind(FD).asComparison();
   }
@@ -6542,12 +6501,6 @@ class Sema final : public SemaBase {
   /// used in initializer of the field.
   llvm::MapVector<FieldDecl *, DeleteLocs> DeleteExprs;
 
-  bool isInMaterializeTemporaryObjectContext() const {
-    assert(!ExprEvalContexts.empty() &&
-           "Must be in an expression evaluation context");
-    return ExprEvalContexts.back().InMaterializeTemporaryObjectContext;
-  }
-
   ParsedType getInheritingConstructorName(CXXScopeSpec &SS,
                                           SourceLocation NameLoc,
                                           IdentifierInfo &Name);
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 2480972f1432f7..27f616d497a2c5 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -274,9 +274,9 @@ void CodeGenFunction::EmitAnyExprToMem(const Expr *E,
   llvm_unreachable("bad evaluation kind");
 }
 
-static void
-pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
-                     const Expr *E, Address ReferenceTemporary) {
+void CodeGenFunction::pushTemporaryCleanup(const MaterializeTemporaryExpr *M,
+                                           const Expr *E,
+                                           Address ReferenceTemporary) {
   // Objective-C++ ARC:
   //   If we are binding a reference to a temporary that has ownership, we
   //   need to perform retain/release operations on the temporary.
@@ -311,9 +311,9 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
         CleanupKind CleanupKind;
         if (Lifetime == Qualifiers::OCL_Strong) {
           const ValueDecl *VD = M->getExtendingDecl();
-          bool Precise =
-              VD && isa<VarDecl>(VD) && VD->hasAttr<ObjCPreciseLifetimeAttr>();
-          CleanupKind = CGF.getARCCleanupKind();
+          bool Precise = isa_and_nonnull<VarDecl>(VD) &&
+                         VD->hasAttr<ObjCPreciseLifetimeAttr>();
+          CleanupKind = getARCCleanupKind();
           Destroy = Precise ? &CodeGenFunction::destroyARCStrongPrecise
                             : &CodeGenFunction::destroyARCStrongImprecise;
         } else {
@@ -323,13 +323,12 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
           Destroy = &CodeGenFunction::destroyARCWeak;
         }
         if (Duration == SD_FullExpression)
-          CGF.pushDestroy(CleanupKind, ReferenceTemporary,
-                          M->getType(), *Destroy,
-                          CleanupKind & EHCleanup);
+          pushDestroy(CleanupKind, ReferenceTemporary, M->getType(), *Destroy,
+                      CleanupKind & EHCleanup);
         else
-          CGF.pushLifetimeExtendedDestroy(CleanupKind, ReferenceTemporary,
-                                          M->getType(),
-                                          *Destroy, CleanupKind & EHCleanup);
+          pushLifetimeExtendedDestroy(CleanupKind, ReferenceTemporary,
+                                      M->getType(), *Destroy,
+                                      CleanupKind & EHCleanup);
         return;
 
       case SD_Dynamic:
@@ -358,32 +357,31 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
     llvm::FunctionCallee CleanupFn;
     llvm::Constant *CleanupArg;
     if (E->getType()->isArrayType()) {
-      CleanupFn = CodeGenFunction(CGF.CGM).generateDestroyHelper(
-          ReferenceTemporary, E->getType(),
-          CodeGenFunction::destroyCXXObject, CGF.getLangOpts().Exceptions,
+      CleanupFn = CodeGenFunction(CGM).generateDestroyHelper(
+          ReferenceTemporary, E->getType(), CodeGenFunction::destroyCXXObject,
+          getLangOpts().Exceptions,
           dyn_cast_or_null<VarDecl>(M->getExtendingDecl()));
-      CleanupArg = llvm::Constant::getNullValue(CGF.Int8PtrTy);
+      CleanupArg = llvm::Constant::getNullValue(Int8PtrTy);
     } else {
-      CleanupFn = CGF.CGM.getAddrAndTypeOfCXXStructor(
+      CleanupFn = CGM.getAddrAndTypeOfCXXStructor(
           GlobalDecl(ReferenceTemporaryDtor, Dtor_Complete));
-      CleanupArg = cast<llvm::Constant>(ReferenceTemporary.emitRawPointer(CGF));
+      CleanupArg =
+          cast<llvm::Constant>(ReferenceTemporary.emitRawPointer(*this));
     }
-    CGF.CGM.getCXXABI().registerGlobalDtor(
-        CGF, *cast<VarDecl>(M->getExtendingDecl()), CleanupFn, CleanupArg);
+    CGM.getCXXABI().registerGlobalDtor(
+        *this, *cast<VarDecl>(M->getExtendingDecl()), CleanupFn, CleanupArg);
     break;
   }
 
   case SD_FullExpression:
-    CGF.pushDestroy(NormalAndEHCleanup, ReferenceTemporary, E->getType(),
-                    CodeGenFunction::destroyCXXObject,
-                    CGF.getLangOpts().Exceptions);
+    pushDestroy(NormalAndEHCleanup, ReferenceTemporary, E->getType(),
+                CodeGenFunction::destroyCXXObject, getLangOpts().Exceptions);
     break;
 
   case SD_Automatic:
-    CGF.pushLifetimeExtendedDestroy(NormalAndEHCleanup,
-                                    ReferenceTemporary, E->getType(),
-                                    CodeGenFunction::destroyCXXObject,
-                                    CGF.getLangOpts().Exceptions);
+    pushLifetimeExtendedDestroy(NormalAndEHCleanup, ReferenceTemporary,
+                                E->getType(), CodeGenFunction::destroyCXXObject,
+                                getLangOpts().Exceptions);
     break;
 
   case SD_Dynamic:
@@ -490,7 +488,7 @@ EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
     }
     }
 
-    pushTemporaryCleanup(*this, M, E, Object);
+    pushTemporaryCleanup(M, E, Object);
     return RefTempDst;
   }
 
@@ -579,7 +577,13 @@ EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
     }
     EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);
   }
-  pushTemporaryCleanup(*this, M, E, Object);
+
+  // If this temporary extended by for-range variable, delay to emitting
+  // cleanup.
+  if (CurLexicalScope && CurLexicalScope->isExtendedByForRangeVar(M))
+    CurLexicalScope->addForRangeInitTemp(M, Object);
+  else
+    pushTemporaryCleanup(M, E, Object);
 
   // Perform derived-to-base casts and/or field accesses, to get from the
   // temporary object we created (and, potentially, for which we extended
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 576fe2f7a2d46f..d277d15b17df13 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -1230,11 +1230,29 @@ CodeGenFunction::EmitCXXForRangeStmt(const CXXForRangeStmt &S,
   JumpDest LoopExit = getJumpDestInCurrentScope("for.end");
 
   LexicalScope ForScope(*this, S.getSourceRange());
+  const DeclStmt *RangeDS = cast<DeclStmt>(S.getRangeStmt());
+  const VarDecl *RangeVar = cast<VarDecl>(RangeDS->getSingleDecl());
+  if (getLangOpts().CPlusPlus23)
+    ForScope.setForRangeVar(RangeVar);
 
   // Evaluate the first pieces before the loop.
   if (S.getInit())
     EmitStmt(S.getInit());
   EmitStmt(S.getRangeStmt());
+
+  // Emit cleanup for tempories in for-range-init expression.
+  if (getLangOpts().CPlusPlus23) {
+    RunCleanupsScope Scope(*this);
+    auto LifetimeExtendTemps = ForScope.getForRangeInitTemps();
+    for (const auto &Temp : LifetimeExtendTemps) {
+      // llvm::errs() << "===============Begin===============\n";
+      auto [M, Object] = Temp;
+      // M->dumpColor();
+      pushTemporaryCleanup(M, M->getSubExpr(), Object);
+      // llvm::errs() << "================End================\n";
+    }
+  }
+
   EmitStmt(S.getBeginStmt());
   EmitStmt(S.getEndStmt());
 
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index e2a7e28c8211ea..167ac71fefb722 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -875,6 +875,9 @@ class CodeGenFunction : public CodeGenTypeCache {
       new (Buffer + sizeof(Header) + sizeof(T)) RawAddress(ActiveFlag);
   }
 
+  void pushTemporaryCleanup(const MaterializeTemporaryExpr *M, const Expr *E,
+                            Address ReferenceTemporary);
+
   /// Set up the last cleanup that was pushed as a conditional
   /// full-expression cleanup.
   void initFullExprCleanup() {
@@ -982,11 +985,24 @@ class CodeGenFunction : public CodeGenTypeCache {
   EHScopeStack::stable_iterator CurrentCleanupScopeDepth =
       EHScopeStack::stable_end();
 
+  struct ForRangeInitLifetimeExtendTemporary {
+    const MaterializeTemporaryExpr *M;
+    RawAddress Object;
+  };
+
   class LexicalScope : public RunCleanupsScope {
     SourceRange Range;
     SmallVector<const LabelDecl*, 4> Labels;
+    SmallVector<ForRangeInitLifetimeExtendTemporary, 4> ForRangeInitTemps;
     LexicalScope *ParentScope;
 
+    // This will be set to `__range` variable when we emitting a
+    // CXXForRangeStmt. It was used to check whether we are emitting a
+    // materialized temporary which in for-range-init and lifetime-extended by
+    // __range var. If so, the codegen of cleanup for that temporary object
+    // needs to be delayed.
+    const VarDecl *ForRangeVar = nullptr;
+
     LexicalScope(const LexicalScope &) = delete;
     void operator=(const LexicalScope &) = delete;
 
@@ -1004,6 +1020,21 @@ class CodeGenFunction : public CodeGenTypeCache {
       Labels.push_back(label);
     }
 
+    void addForRangeInitTemp(const MaterializeTemporaryExpr *M,
+                             RawAddress Object) {
+      assert(PerformCleanup && "adding temps to dead scope?");
+      ForRangeInitTemps.push_back({M, Object});
+    }
+
+    ArrayRef<ForRangeInitLifetimeExtendTemporary> getForRangeInitTemps() const {
+      return ForRangeInitTemps;
+    }
+
+    void setForRangeVar(const VarDecl *Var) { ForRangeVar = Var; }
+    bool isExtendedByForRangeVar(const MaterializeTemporaryExpr *M) const {
+      return M && ForRangeVar && M->getExtendingDecl() == ForRangeVar;
+    }
+
     /// Exit this cleanup scope, emitting any accumulated
     /// cleanups.
     ~LexicalScope() {
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 0aa14b0510746b..ae5d2cae06e536 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2379,10 +2379,6 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
       if (getLangOpts().CPlusPlus23) {
         auto &LastRecord = Actions.ExprEvalContexts.back();
         LastRecord.InLifetimeExtendingContext = true;
-
-        // Materialize non-`cv void` prvalue temporaries in discarded
-        // expressions. These materialized temporaries may be lifetime-extented.
-        LastRecord.InMaterializeTemporaryObjectContext = true;
       }
 
       if (getLangOpts().OpenMP)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index ffe7e44e2387f4..2caf8a3a3c8e83 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6331,7 +6331,6 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
       // Pass down lifetime extending flag, and collect temporaries in
       // CreateMaterializeTemporaryExpr when we rewrite the call argument.
       keepInLifetimeExtendingContext();
-      keepInMaterializeTemporaryObjectContext();
       EnsureImmediateInvocationInDefaultArgs Immediate(*this);
       ExprResult Res;
       runWithSufficientStackSpace(CallLoc, [&] {
@@ -6377,7 +6376,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   Expr *Init = nullptr;
 
   bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-
+  bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
   EnterExpressionEvaluationContext EvalContext(
       *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
 
@@ -6412,19 +6411,21 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   ImmediateCallVisitor V(getASTContext());
   if (!NestedDefaultChecking)
     V.TraverseDecl(Field);
-  if (V.HasImmediateCalls) {
+  if (V.HasImmediateCalls || InLifetimeExtendingContext) {
     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;
     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 db84f181012268..9f13e3827c742d 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8361,7 +8361,7 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) {
     // unnecessary temporary objects. If we skip this step, IR generation is
     // able to synthesize the storage for itself in the aggregate case, and
     // adding the extra node to the AST is just clutter.
-    if (isInMaterializeTemporaryObjectContext() && getLangOpts().CPlusPlus17 &&
+    if (isInLifetimeExtendingContext() && getLangOpts().CPlusPlus17 &&
         E->isPRValue() && !E->getType()->isVoidType()) {
       ExprResult Res = TemporaryMaterializationConversion(E);
       if (Res.isInvalid())
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index a75e9925a43146..c2269206988efc 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -710,6 +710,26 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
       if (VerifyOnly)
         return;
 
+      // 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 that would lifetime extend its
+      // temporaries.
+      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;
+
       ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
       if (DIE.isInvalid()) {
         hadError = true;
@@ -7699,6 +7719,8 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
     // Step into CXXDefaultInitExprs so we can diagnose cases where a
     // constructor inherits one as an implicit mem-initializer.
     if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) {
+      assert(DIE->hasRewrittenInit() &&
+             "CXXDefaultInitExpr must has rewritten init");
       Path.push_back(
           {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()});
       Init = DIE->getExpr();
@@ -8194,24 +8216,12 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
 
       switch (shouldLifetimeExtendThroughPath(Path)) {
       case PathLifetimeKind::Extend:
+      case PathLifetimeKind::ShouldExtend:
         // Update the storage duration of the materialized temporary.
-        // FIXME: Rebuild the expression instead of mutating it.
         MTE->setExtendingDecl(ExtendingEntity->getDecl(),
                               ExtendingEntity->allocateManglingNumber());
-        // 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.
-        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/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 1cb071e4eb7d1c..15d0b8a69bcd2c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5477,7 +5477,6 @@ void Sema::InstantiateVariableInitializer(
         *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var);
 
     keepInLifetimeExtendingContext();
-    keepInMaterializeTemporaryObjectContext();
     // Instantiate the initializer.
     ExprResult Init;
 
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 7df352c24e8648..818837064a38b7 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -8749,10 +8749,6 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
   if (getSema().getLangOpts().CPlusPlus23) {
     auto &LastRecord = getSema().ExprEvalContexts.back();
     LastRecord.InLifetimeExtendingContext = true;
-
-    // Materialize non-`cv void` prvalue temporaries in discarded
-    // expressions. These materialized temporaries may be lifetime-extented.
-    LastRecord.InMaterializeTemporaryObjectContext = true;
   }
   StmtResult Init =
       S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult();
diff --git a/clang/test/CXX/drs/dr16xx.cpp b/clang/test/CXX/drs/dr16xx.cpp
index f4d6c04fb8e073..c316555eec47c5 100644
--- a/clang/test/CXX/drs/dr16xx.cpp
+++ b/clang/test/CXX/drs/dr16xx.cpp
@@ -484,8 +484,7 @@ namespace dr1696 { // dr1696: 7
     const A &a = A(); // #dr1696-D1-a
   };
   D1 d1 = {}; // #dr1696-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@#dr1696-D1-a {{initializing field 'a' with default member initializer}}
+
 
   struct D2 {
     const A &a = A(); // #dr1696-D2-a
diff --git a/clang/test/CXX/drs/dr18xx.cpp b/clang/test/CXX/drs/dr18xx.cpp
index e78730e8992cf8..440db2eb6bc93b 100644
--- a/clang/test/CXX/drs/dr18xx.cpp
+++ b/clang/test/CXX/drs/dr18xx.cpp
@@ -206,13 +206,10 @@ namespace dr1814 { // dr1814: yes
 #endif
 }
 
-namespace dr1815 { // dr1815: no
+namespace dr1815 { // dr1815: yes
 #if __cplusplus >= 201402L
-  // FIXME: needs codegen test
-  struct A { int &&r = 0; }; // #dr1815-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@#dr1815-A {{initializing field 'r' with default member initializer}}
 
   struct B { int &&r = 0; }; // #dr1815-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}}
diff --git a/clang/test/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp
index 5554363cc69abb..ab781c34ac357f 100644
--- a/clang/test/CXX/special/class.temporary/p6.cpp
+++ b/clang/test/CXX/special/class.temporary/p6.cpp
@@ -269,6 +269,26 @@ 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() {}
+  };
+
+  // CHECK: void @_Z12check_dr1815v()
+  // CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev(
+  // CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev(
+  B a = {};
+#endif
+}
+
 namespace P2718R0 {
 namespace basic {
 template <typename E> using T2 = std::list<E>;
@@ -429,6 +449,45 @@ 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; }
@@ -545,5 +604,52 @@ void default_arg3() {
   for (auto e : C(0, C(0, C(0, C())))) {}
 }
 } // namespace default_arg
+
+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/test/SemaCXX/constexpr-default-arg.cpp b/clang/test/SemaCXX/constexpr-default-arg.cpp
index 7c883692829548..8510a6ddc80399 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/eval-crashes.cpp b/clang/test/SemaCXX/eval-crashes.cpp
index 017df977b26b7b..a06f60f71e9c7e 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 {



More information about the cfe-commits mailing list