[clang] [Clang] Implement P2718R0 "Lifetime extension in range-based for loops" (PR #76361)

via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 29 07:56:02 PST 2024


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

>From cff4446c697e4abfb18ea0a17ca2b6596c23015f Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sat, 27 Jan 2024 23:25:57 +0800
Subject: [PATCH 1/3] [Clang] Implement P2718R0 "Lifetime extension in
 range-based for loops"

 Implement P2718R0 "Lifetime extension in range-based for loops" (https://wg21.link/P2718R0)

Differential Revision: https://reviews.llvm.org/D153701
---
 clang/docs/ReleaseNotes.rst                   |   3 +
 clang/include/clang/Parse/Parser.h            |   2 +-
 clang/include/clang/Sema/Sema.h               | 104 ++++-
 clang/lib/Frontend/InitPreprocessor.cpp       |   4 +-
 clang/lib/Parse/ParseDecl.cpp                 |  26 ++
 clang/lib/Parse/ParseStmt.cpp                 |   8 +-
 clang/lib/Sema/SemaExpr.cpp                   |  34 +-
 clang/lib/Sema/SemaExprCXX.cpp                |  46 ++-
 clang/lib/Sema/SemaInit.cpp                   |   4 +
 clang/lib/Sema/SemaStmt.cpp                   |  35 +-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   2 +
 clang/lib/Sema/TreeTransform.h                |  62 +--
 .../test/AST/ast-dump-for-range-lifetime.cpp  | 355 ++++++++++++++++++
 clang/test/CXX/special/class.temporary/p6.cpp | 311 ++++++++++++++-
 clang/test/Lexer/cxx-features.cpp             |   2 +-
 clang/www/cxx_status.html                     |   2 +-
 16 files changed, 913 insertions(+), 87 deletions(-)
 create mode 100644 clang/test/AST/ast-dump-for-range-lifetime.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 254e0a9cb72979d..cb7015612b2fc7b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -65,6 +65,9 @@ C++20 Feature Support
 C++23 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
+- Implemented `P2718R0: Lifetime extension in range-based for loops <https://wg21.link/P2718R0>`_. Also
+  materialize temporary object which is a prvalue in discarded-value expression.
+
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index baad6155b7c6392..4d09d8ba95e32bf 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2402,7 +2402,7 @@ class Parser : public CodeCompletionHandler {
   struct ForRangeInit {
     SourceLocation ColonLoc;
     ExprResult RangeExpr;
-
+    SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
     bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); }
   };
   struct ForRangeInfo : ForRangeInit {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b5946e3f3178ff2..59eab0185ae632b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1342,6 +1342,12 @@ class Sema final {
     /// context not already known to be immediately invoked.
     llvm::SmallPtrSet<DeclRefExpr *, 4> ReferenceToConsteval;
 
+    /// P2718R0 - Lifetime extension in range-based for loops.
+    /// MaterializeTemporaryExprs in for-range-init expressions which need to
+    /// extend lifetime. Add MaterializeTemporaryExpr* if the value of
+    /// InLifetimeExtendingContext is true.
+    SmallVector<MaterializeTemporaryExpr *, 8> ForRangeLifetimeExtendTemps;
+
     /// \brief Describes whether we are in an expression constext which we have
     /// to handle differently.
     enum ExpressionKind {
@@ -1361,6 +1367,39 @@ class Sema final {
     // VLAs).
     bool InConditionallyConstantEvaluateContext = false;
 
+    /// 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;
+
+    /// 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
@@ -5253,22 +5292,17 @@ class Sema final {
     BFRK_Check
   };
 
-  StmtResult ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
-                                  SourceLocation CoawaitLoc,
-                                  Stmt *InitStmt,
-                                  Stmt *LoopVar,
-                                  SourceLocation ColonLoc, Expr *Collection,
-                                  SourceLocation RParenLoc,
-                                  BuildForRangeKind Kind);
-  StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc,
-                                  SourceLocation CoawaitLoc,
-                                  Stmt *InitStmt,
-                                  SourceLocation ColonLoc,
-                                  Stmt *RangeDecl, Stmt *Begin, Stmt *End,
-                                  Expr *Cond, Expr *Inc,
-                                  Stmt *LoopVarDecl,
-                                  SourceLocation RParenLoc,
-                                  BuildForRangeKind Kind);
+  StmtResult ActOnCXXForRangeStmt(
+      Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc,
+      Stmt *InitStmt, Stmt *LoopVar, SourceLocation ColonLoc, Expr *Collection,
+      SourceLocation RParenLoc, BuildForRangeKind Kind,
+      ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps = {});
+  StmtResult BuildCXXForRangeStmt(
+      SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
+      SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *Begin, Stmt *End,
+      Expr *Cond, Expr *Inc, Stmt *LoopVarDecl, SourceLocation RParenLoc,
+      BuildForRangeKind Kind,
+      ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps = {});
   StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body);
 
   StmtResult ActOnGotoStmt(SourceLocation GotoLoc,
@@ -10010,6 +10044,18 @@ class Sema final {
     return currentEvaluationContext().isImmediateFunctionContext();
   }
 
+  bool isInLifetimeExtendingContext() const {
+    assert(!ExprEvalContexts.empty() &&
+           "Must be in an expression evaluation context");
+    return ExprEvalContexts.back().InLifetimeExtendingContext;
+  }
+
+  bool isInMaterializeTemporaryObjectContext() const {
+    assert(!ExprEvalContexts.empty() &&
+           "Must be in an expression evaluation context");
+    return ExprEvalContexts.back().InMaterializeTemporaryObjectContext;
+  }
+
   bool isCheckingDefaultArgumentOrInitializer() const {
     const ExpressionEvaluationContextRecord &Ctx = currentEvaluationContext();
     return (Ctx.Context ==
@@ -10049,6 +10095,32 @@ class Sema final {
     return Res;
   }
 
+  /// keepInLifetimeExtendingContext - Pull down InLifetimeExtendingContext
+  /// flag from previous context.
+  void keepInLifetimeExtendingContext() {
+    if (ExprEvalContexts.size() > 2 &&
+        ExprEvalContexts[ExprEvalContexts.size() - 2]
+            .InLifetimeExtendingContext) {
+      auto &LastRecord = ExprEvalContexts.back();
+      auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
+      LastRecord.InLifetimeExtendingContext =
+          PrevRecord.InLifetimeExtendingContext;
+    }
+  }
+
+  /// 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;
+    }
+  }
+
   /// RAII class used to determine whether SFINAE has
   /// trapped any errors that occur during template argument
   /// deduction.
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 1b91c86f91398cc..877e205e2e9bfab 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -643,7 +643,9 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
                                                                   : "200704");
     Builder.defineMacro("__cpp_constexpr_in_decltype", "201711L");
     Builder.defineMacro("__cpp_range_based_for",
-                        LangOpts.CPlusPlus17 ? "201603L" : "200907");
+                        LangOpts.CPlusPlus23   ? "202211L"
+                        : LangOpts.CPlusPlus17 ? "201603L"
+                                               : "200907");
     Builder.defineMacro("__cpp_static_assert", LangOpts.CPlusPlus26 ? "202306L"
                                                : LangOpts.CPlusPlus17
                                                    ? "201411L"
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7ea0dccd1c724b5..bcbe2d9c635a626 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2312,12 +2312,38 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
     bool IsForRangeLoop = false;
     if (TryConsumeToken(tok::colon, FRI->ColonLoc)) {
       IsForRangeLoop = true;
+      EnterExpressionEvaluationContext ForRangeInitContext(
+          Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
+          /*LambdaContextDecl=*/nullptr,
+          Sema::ExpressionEvaluationContextRecord::EK_Other,
+          getLangOpts().CPlusPlus23);
+
+      // P2718R0 - Lifetime extension in range-based for loops.
+      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)
         Actions.startOpenMPCXXRangeFor();
       if (Tok.is(tok::l_brace))
         FRI->RangeExpr = ParseBraceInitializer();
       else
         FRI->RangeExpr = ParseExpression();
+
+      // Before c++23, ForRangeLifetimeExtendTemps should be empty.
+      assert(
+          getLangOpts().CPlusPlus23 ||
+          Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty());
+
+      // Move the collected materialized temporaries into ForRangeInit before
+      // ForRangeInitContext exit.
+      FRI->LifetimeExtendTemps = std::move(
+          Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
     }
 
     Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index d0ff33bd1379ab7..76a3fa8f2627de2 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2288,11 +2288,11 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
     ForRangeStmt = Actions.ActOnCXXForRangeStmt(
         getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(),
         ForRangeInfo.LoopVar.get(), ForRangeInfo.ColonLoc, CorrectedRange.get(),
-        T.getCloseLocation(), Sema::BFRK_Build);
-
-  // Similarly, we need to do the semantic analysis for a for-range
-  // statement immediately in order to close over temporaries correctly.
+        T.getCloseLocation(), Sema::BFRK_Build,
+        ForRangeInfo.LifetimeExtendTemps);
   } else if (ForEach) {
+    // Similarly, we need to do the semantic analysis for a for-range
+    // statement immediately in order to close over temporaries correctly.
     ForEachStmt = Actions.ActOnObjCForCollectionStmt(ForLoc,
                                                      FirstPart.get(),
                                                      Collection.get(),
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 2f1ddfb215116d0..cbcf5d690939c00 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6259,7 +6259,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
   assert(Param->hasDefaultArg() && "can't build nonexistent default arg");
 
   bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-
+  bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
   std::optional<ExpressionEvaluationContextRecord::InitializationContext>
       InitializationContext =
           OutermostDeclarationWithDelayedImmediateInvocations();
@@ -6292,9 +6292,17 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
     ImmediateCallVisitor V(getASTContext());
     if (!NestedDefaultChecking)
       V.TraverseDecl(Param);
-    if (V.HasImmediateCalls) {
-      ExprEvalContexts.back().DelayedDefaultInitializationContext = {
-          CallLoc, Param, CurContext};
+
+    // Rewrite the call argument that was created from the corresponding
+    // parameter's default argument.
+    if (V.HasImmediateCalls || InLifetimeExtendingContext) {
+      if (V.HasImmediateCalls)
+        ExprEvalContexts.back().DelayedDefaultInitializationContext = {
+            CallLoc, Param, CurContext};
+      // 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, [&] {
@@ -6340,7 +6348,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   Expr *Init = nullptr;
 
   bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-
+  bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
   EnterExpressionEvaluationContext EvalContext(
       *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
 
@@ -6375,12 +6383,16 @@ 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();
+    keepInMaterializeTemporaryObjectContext();
     EnsureImmediateInvocationInDefaultArgs Immediate(*this);
     ExprResult Res;
     runWithSufficientStackSpace(Loc, [&] {
@@ -18655,6 +18667,16 @@ void Sema::PopExpressionEvaluationContext() {
     }
   }
 
+  // Append the collected materialized temporaries into previous context before
+  // exit if the previous also is a lifetime extending context.
+  auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
+  if (getLangOpts().CPlusPlus23 && isInLifetimeExtendingContext() &&
+      PrevRecord.InLifetimeExtendingContext && !ExprEvalContexts.empty()) {
+    auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
+    PrevRecord.ForRangeLifetimeExtendTemps.append(
+        Rec.ForRangeLifetimeExtendTemps);
+  }
+
   WarnOnPendingNoDerefs(Rec);
   HandleImmediateInvocations(*this, Rec);
 
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 51c61886739bb91..2b695fd43eac063 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8222,21 +8222,6 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) {
     E = result.get();
   }
 
-  // C99 6.3.2.1:
-  //   [Except in specific positions,] an lvalue that does not have
-  //   array type is converted to the value stored in the
-  //   designated object (and is no longer an lvalue).
-  if (E->isPRValue()) {
-    // In C, function designators (i.e. expressions of function type)
-    // are r-values, but we still want to do function-to-pointer decay
-    // on them.  This is both technically correct and convenient for
-    // some clients.
-    if (!getLangOpts().CPlusPlus && E->getType()->isFunctionType())
-      return DefaultFunctionArrayConversion(E);
-
-    return E;
-  }
-
   if (getLangOpts().CPlusPlus) {
     // The C++11 standard defines the notion of a discarded-value expression;
     // normally, we don't need to do anything to handle it, but if it is a
@@ -8257,11 +8242,32 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) {
     //   If the expression is a prvalue after this optional conversion, the
     //   temporary materialization conversion is applied.
     //
-    // 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.
-    // FIXME: We don't emit lifetime markers for the temporaries due to this.
-    // FIXME: Do any other AST consumers care about this?
+    // We do not materialize temporaries by default in order to avoid creating
+    // 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 &&
+        E->isPRValue() && !E->getType()->isVoidType()) {
+      ExprResult Res = TemporaryMaterializationConversion(E);
+      if (Res.isInvalid())
+        return E;
+      E = Res.get();
+    }
+    return E;
+  }
+
+  // C99 6.3.2.1:
+  //   [Except in specific positions,] an lvalue that does not have
+  //   array type is converted to the value stored in the
+  //   designated object (and is no longer an lvalue).
+  if (E->isPRValue()) {
+    // In C, function designators (i.e. expressions of function type)
+    // are r-values, but we still want to do function-to-pointer decay
+    // on them.  This is both technically correct and convenient for
+    // some clients.
+    if (!getLangOpts().CPlusPlus && E->getType()->isFunctionType())
+      return DefaultFunctionArrayConversion(E);
+
     return E;
   }
 
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 457fa377355a97c..b6de06464cd6f31 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8491,6 +8491,10 @@ 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);
+  }
   return MTE;
 }
 
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 9e7c8c7e4e8c12c..28f75196163cca4 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -27,6 +27,7 @@
 #include "clang/AST/TypeOrdering.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Ownership.h"
@@ -2489,11 +2490,11 @@ static bool ObjCEnumerationCollection(Expr *Collection) {
 ///
 /// The body of the loop is not available yet, since it cannot be analysed until
 /// we have determined the type of the for-range-declaration.
-StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
-                                      SourceLocation CoawaitLoc, Stmt *InitStmt,
-                                      Stmt *First, SourceLocation ColonLoc,
-                                      Expr *Range, SourceLocation RParenLoc,
-                                      BuildForRangeKind Kind) {
+StmtResult Sema::ActOnCXXForRangeStmt(
+    Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
+    Stmt *First, SourceLocation ColonLoc, Expr *Range, SourceLocation RParenLoc,
+    BuildForRangeKind Kind,
+    ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
   // FIXME: recover in order to allow the body to be parsed.
   if (!First)
     return StmtError();
@@ -2557,7 +2558,8 @@ StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
   StmtResult R = BuildCXXForRangeStmt(
       ForLoc, CoawaitLoc, InitStmt, ColonLoc, RangeDecl.get(),
       /*BeginStmt=*/nullptr, /*EndStmt=*/nullptr,
-      /*Cond=*/nullptr, /*Inc=*/nullptr, DS, RParenLoc, Kind);
+      /*Cond=*/nullptr, /*Inc=*/nullptr, DS, RParenLoc, Kind,
+      LifetimeExtendTemps);
   if (R.isInvalid()) {
     ActOnInitializerError(LoopVar);
     return StmtError();
@@ -2747,13 +2749,12 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
 }
 
 /// BuildCXXForRangeStmt - Build or instantiate a C++11 for-range statement.
-StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc,
-                                      SourceLocation CoawaitLoc, Stmt *InitStmt,
-                                      SourceLocation ColonLoc, Stmt *RangeDecl,
-                                      Stmt *Begin, Stmt *End, Expr *Cond,
-                                      Expr *Inc, Stmt *LoopVarDecl,
-                                      SourceLocation RParenLoc,
-                                      BuildForRangeKind Kind) {
+StmtResult Sema::BuildCXXForRangeStmt(
+    SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
+    SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *Begin, Stmt *End,
+    Expr *Cond, Expr *Inc, Stmt *LoopVarDecl, SourceLocation RParenLoc,
+    BuildForRangeKind Kind,
+    ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
   // FIXME: This should not be used during template instantiation. We should
   // pick up the set of unqualified lookup results for the != and + operators
   // in the initial parse.
@@ -2813,6 +2814,14 @@ StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc,
                             diag::err_for_range_incomplete_type))
       return StmtError();
 
+    // P2718R0 - Lifetime extension in range-based for loops.
+    if (getLangOpts().CPlusPlus23 && !LifetimeExtendTemps.empty()) {
+      InitializedEntity Entity =
+          InitializedEntity::InitializeVariable(RangeVar);
+      for (auto *MTE : LifetimeExtendTemps)
+        MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber());
+    }
+
     // Build auto __begin = begin-expr, __end = end-expr.
     // Divide by 2, since the variables are in the inner scope (loop body).
     const auto DepthStr = std::to_string(S->getDepth() / 2);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index fbc8572ea0e0f0c..fcb27a880290b85 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5440,6 +5440,8 @@ void Sema::InstantiateVariableInitializer(
     EnterExpressionEvaluationContext Evaluated(
         *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 2922924e550d0cf..e252c3e2ffc0e3c 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -2550,12 +2550,11 @@ class TreeTransform {
   ///
   /// By default, performs semantic analysis to build the new statement.
   /// Subclasses may override this routine to provide different behavior.
-  StmtResult RebuildCXXForRangeStmt(SourceLocation ForLoc,
-                                    SourceLocation CoawaitLoc, Stmt *Init,
-                                    SourceLocation ColonLoc, Stmt *Range,
-                                    Stmt *Begin, Stmt *End, Expr *Cond,
-                                    Expr *Inc, Stmt *LoopVar,
-                                    SourceLocation RParenLoc) {
+  StmtResult RebuildCXXForRangeStmt(
+      SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *Init,
+      SourceLocation ColonLoc, Stmt *Range, Stmt *Begin, Stmt *End, Expr *Cond,
+      Expr *Inc, Stmt *LoopVar, SourceLocation RParenLoc,
+      ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
     // If we've just learned that the range is actually an Objective-C
     // collection, treat this as an Objective-C fast enumeration loop.
     if (DeclStmt *RangeStmt = dyn_cast<DeclStmt>(Range)) {
@@ -2581,9 +2580,9 @@ class TreeTransform {
       }
     }
 
-    return getSema().BuildCXXForRangeStmt(ForLoc, CoawaitLoc, Init, ColonLoc,
-                                          Range, Begin, End, Cond, Inc, LoopVar,
-                                          RParenLoc, Sema::BFRK_Rebuild);
+    return getSema().BuildCXXForRangeStmt(
+        ForLoc, CoawaitLoc, Init, ColonLoc, Range, Begin, End, Cond, Inc,
+        LoopVar, RParenLoc, Sema::BFRK_Rebuild, LifetimeExtendTemps);
   }
 
   /// Build a new C++0x range-based for statement.
@@ -4151,6 +4150,8 @@ ExprResult TreeTransform<Derived>::TransformInitializer(Expr *Init,
       getSema(), EnterExpressionEvaluationContext::InitList,
       Construct->isListInitialization());
 
+  getSema().keepInLifetimeExtendingContext();
+  getSema().keepInLifetimeExtendingContext();
   SmallVector<Expr*, 8> NewArgs;
   bool ArgChanged = false;
   if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(),
@@ -8656,6 +8657,21 @@ StmtResult TreeTransform<Derived>::TransformCXXTryStmt(CXXTryStmt *S) {
 template<typename Derived>
 StmtResult
 TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
+  EnterExpressionEvaluationContext ForRangeInitContext(
+      getSema(), Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
+      /*LambdaContextDecl=*/nullptr,
+      Sema::ExpressionEvaluationContextRecord::EK_Other,
+      getSema().getLangOpts().CPlusPlus23);
+
+  // P2718R0 - Lifetime extension in range-based for loops.
+  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();
   if (Init.isInvalid())
@@ -8665,6 +8681,12 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
   if (Range.isInvalid())
     return StmtError();
 
+  // Before c++23, ForRangeLifetimeExtendTemps should be empty.
+  assert(getSema().getLangOpts().CPlusPlus23 ||
+         getSema().ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty());
+  auto ForRangeLifetimeExtendTemps =
+      getSema().ExprEvalContexts.back().ForRangeLifetimeExtendTemps;
+
   StmtResult Begin = getDerived().TransformStmt(S->getBeginStmt());
   if (Begin.isInvalid())
     return StmtError();
@@ -8701,13 +8723,10 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
       Cond.get() != S->getCond() ||
       Inc.get() != S->getInc() ||
       LoopVar.get() != S->getLoopVarStmt()) {
-    NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(),
-                                                  S->getCoawaitLoc(), Init.get(),
-                                                  S->getColonLoc(), Range.get(),
-                                                  Begin.get(), End.get(),
-                                                  Cond.get(),
-                                                  Inc.get(), LoopVar.get(),
-                                                  S->getRParenLoc());
+    NewStmt = getDerived().RebuildCXXForRangeStmt(
+        S->getForLoc(), S->getCoawaitLoc(), Init.get(), S->getColonLoc(),
+        Range.get(), Begin.get(), End.get(), Cond.get(), Inc.get(),
+        LoopVar.get(), S->getRParenLoc(), ForRangeLifetimeExtendTemps);
     if (NewStmt.isInvalid() && LoopVar.get() != S->getLoopVarStmt()) {
       // Might not have attached any initializer to the loop variable.
       getSema().ActOnInitializerError(
@@ -8723,13 +8742,10 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
   // Body has changed but we didn't rebuild the for-range statement. Rebuild
   // it now so we have a new statement to attach the body to.
   if (Body.get() != S->getBody() && NewStmt.get() == S) {
-    NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(),
-                                                  S->getCoawaitLoc(), Init.get(),
-                                                  S->getColonLoc(), Range.get(),
-                                                  Begin.get(), End.get(),
-                                                  Cond.get(),
-                                                  Inc.get(), LoopVar.get(),
-                                                  S->getRParenLoc());
+    NewStmt = getDerived().RebuildCXXForRangeStmt(
+        S->getForLoc(), S->getCoawaitLoc(), Init.get(), S->getColonLoc(),
+        Range.get(), Begin.get(), End.get(), Cond.get(), Inc.get(),
+        LoopVar.get(), S->getRParenLoc(), ForRangeLifetimeExtendTemps);
     if (NewStmt.isInvalid())
       return StmtError();
   }
diff --git a/clang/test/AST/ast-dump-for-range-lifetime.cpp b/clang/test/AST/ast-dump-for-range-lifetime.cpp
new file mode 100644
index 000000000000000..dec2e29184526ee
--- /dev/null
+++ b/clang/test/AST/ast-dump-for-range-lifetime.cpp
@@ -0,0 +1,355 @@
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-linux-gnu -fcxx-exceptions -ast-dump %s \
+// RUN: | FileCheck -strict-whitespace %s
+
+namespace P2718R0 {
+
+// Test basic
+struct A {
+  int a[3] = {1, 2, 3};
+  A() {}
+  ~A() {}
+  const int *begin() const { return a; }
+  const int *end() const { return a + 3; }
+  A& r() { return *this; }
+  A g() { return A(); }
+};
+
+A g() { return A(); }
+const A &f1(const A &t) { return t; }
+
+void test1() {
+  [[maybe_unused]] int sum = 0;
+  // CHECK: FunctionDecl {{.*}} test1 'void ()'
+  // CHECK:      |   `-CXXForRangeStmt {{.*}}
+  // CHECK-NEXT: |     |-<<<NULL>>>
+  // CHECK-NEXT: |     |-DeclStmt {{.*}}
+  // CHECK-NEXT: |     | `-VarDecl {{.*}} implicit used __range1 'const A &' cinit
+  // CHECK-NEXT: |     |   `-ExprWithCleanups {{.*}} 'const A':'const P2718R0::A' lvalue
+  // CHECK-NEXT: |     |     `-CallExpr {{.*}} 'const A':'const P2718R0::A' lvalue
+  // CHECK-NEXT: |     |       |-ImplicitCastExpr {{.*}} 'const A &(*)(const A &)' <FunctionToPointerDecay>
+  // CHECK-NEXT: |     |       | `-DeclRefExpr {{.*}} 'const A &(const A &)' lvalue Function {{.*}} 'f1' 'const A &(const A &)'
+  // CHECK-NEXT: |     |       `-MaterializeTemporaryExpr {{.*}} 'const A':'const P2718R0::A' lvalue extended by Var {{.*}} '__range1' 'const A &'
+  // CHECK-NEXT: |     |         `-ImplicitCastExpr {{.*}} 'const A':'const P2718R0::A' <NoOp>
+  // CHECK-NEXT: |     |           `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+  // CHECK-NEXT: |     |             `-CallExpr {{.*}} 'A':'P2718R0::A'
+  // CHECK-NEXT: |     |               `-ImplicitCastExpr {{.*}} 'A (*)()' <FunctionToPointerDecay>
+  // CHECK-NEXT: |     |                 `-DeclRefExpr {{.*}} 'A ()' lvalue Function {{.*}} 'g' 'A ()'
+  for (auto e : f1(g()))
+    sum += e;
+}
+
+struct B : A {};
+int (&f(const A *))[3];
+const A *g(const A &);
+void bar(int) {}
+
+void test2() {
+  // CHECK: FunctionDecl {{.*}} test2 'void ()'
+  // CHECK:      |   `-CXXForRangeStmt {{.*}}
+  // CHECK-NEXT: |     |-<<<NULL>>>
+  // CHECK-NEXT: |     |-DeclStmt {{.*}}
+  // CHECK-NEXT: |     | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+  // CHECK-NEXT: |     |   `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+  // CHECK-NEXT: |     |     `-CallExpr {{.*}} 'int[3]' lvalue
+  // CHECK-NEXT: |     |       |-ImplicitCastExpr {{.*}} 'int (&(*)(const A *))[3]' <FunctionToPointerDecay>
+  // CHECK-NEXT: |     |       | `-DeclRefExpr {{.*}} 'int (&(const A *))[3]' lvalue Function {{.*}} 'f' 'int (&(const A *))[3]'
+  // CHECK-NEXT: |     |       `-CallExpr {{.*}} 'const A *'
+  // CHECK-NEXT: |     |         |-ImplicitCastExpr {{.*}} 'const A *(*)(const A &)' <FunctionToPointerDecay>
+  // CHECK-NEXT: |     |         | `-DeclRefExpr {{.*}} 'const A *(const A &)' lvalue Function {{.*}} 'g' 'const A *(const A &)'
+  // CHECK-NEXT: |     |         `-ImplicitCastExpr {{.*}} 'const A':'const P2718R0::A' lvalue <DerivedToBase (A)>
+  // CHECK-NEXT: |     |           `-MaterializeTemporaryExpr {{.*}} 'const B':'const P2718R0::B' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+  // CHECK-NEXT: |     |             `-ImplicitCastExpr {{.*}} 'const B':'const P2718R0::B' <NoOp>
+  // CHECK-NEXT: |     |               `-CXXBindTemporaryExpr {{.*}} 'B':'P2718R0::B' (CXXTemporary {{.*}})
+  // CHECK-NEXT: |     |                 `-CXXTemporaryObjectExpr {{.*}} 'B':'P2718R0::B' 'void () noexcept(false)' zeroing
+  for (auto e : f(g(B())))
+    bar(e);
+}
+
+// Test discard statement.
+struct LockGuard {
+    LockGuard() {}
+    ~LockGuard() {}
+};
+
+void test3() {
+  int v[] = {42, 17, 13};
+
+  // CHECK: FunctionDecl {{.*}} test3 'void ()'
+  // CHECK:      -CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:  |-<<<NULL>>>
+  // CHECK-NEXT:  |-DeclStmt {{.*}}
+  // CHECK-NEXT:  | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+  // CHECK-NEXT:  |   `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+  // CHECK-NEXT:  |     `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+  // CHECK-NEXT:  |       |-CXXStaticCastExpr {{.*}} 'void' static_cast<void> <ToVoid>
+  // CHECK-NEXT:  |       | `-MaterializeTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' xvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+  // CHECK-NEXT:  |       |   `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |       |     `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+  // CHECK-NEXT:  |       `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+  for ([[maybe_unused]] int x : static_cast<void>(LockGuard()), v)
+    LockGuard guard;
+
+  // CHECK:      -CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:  |-<<<NULL>>>
+  // CHECK-NEXT:  |-DeclStmt {{.*}}
+  // CHECK-NEXT:  | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+  // CHECK-NEXT:  |   `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+  // CHECK-NEXT:  |     `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+  // CHECK-NEXT:  |       |-CStyleCastExpr {{.*}} 'void' <ToVoid>
+  // CHECK-NEXT:  |       | `-MaterializeTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' xvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+  // CHECK-NEXT:  |       |   `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |       |     `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+  // CHECK-NEXT:  |       `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+  for ([[maybe_unused]] int x : (void)LockGuard(), v)
+    LockGuard guard;
+
+  // CHECK:      -CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:  |-<<<NULL>>>
+  // CHECK-NEXT:  |-DeclStmt {{.*}}
+  // CHECK-NEXT:  | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+  // CHECK-NEXT:  |   `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+  // CHECK-NEXT:  |     `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+  // CHECK-NEXT:  |       |-MaterializeTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' xvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+  // CHECK-NEXT:  |       | `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |       |   `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+  // CHECK-NEXT:  |       `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+  for ([[maybe_unused]] int x : LockGuard(), v)
+    LockGuard guard;
+}
+
+// Test default arg
+int (&default_arg_fn(const A & = A()))[3];
+void test4() {
+
+  // CHECK: FunctionDecl {{.*}} test4 'void ()'
+  // FIXME: Should dump CXXDefaultArgExpr->getExpr() if CXXDefaultArgExpr has been rewrited?
+  for (auto e : default_arg_fn()) 
+    bar(e);
+}
+
+struct DefaultA {
+  DefaultA() {}
+  ~DefaultA() {}
+};
+
+A foo(const A&, const DefaultA &Default = DefaultA()) {
+  return A();
+}
+
+void test5() {
+  for (auto e : default_arg_fn(foo(foo(foo(A())))))
+    bar(e);
+}
+
+struct C : public A {
+  C() {}
+  C(int, const C &, const DefaultA & = DefaultA()) {}
+};
+
+void test6() {
+  for (auto e : C(0, C(0, C(0, C()))))
+    bar(e);
+}
+
+// Test member call
+void test7() {
+  // CHECK: FunctionDecl {{.*}} test7 'void ()'
+  // CHECK:      -CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:  |-<<<NULL>>>
+  // CHECK-NEXT:  |-DeclStmt {{.*}}
+  // CHECK-NEXT:  | `-VarDecl {{.*}} implicit used __range1 'A &&' cinit
+  // CHECK-NEXT:  |   `-ExprWithCleanups {{.*}} 'A':'P2718R0::A' xvalue
+  // CHECK-NEXT:  |     `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+  // CHECK-NEXT:  |       `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |         `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+  // CHECK-NEXT:  |           `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+  // CHECK-NEXT:  |             `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+  // CHECK-NEXT:  |               `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+  // CHECK-NEXT:  |                 `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+  // CHECK-NEXT:  |                   `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |                     `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+  // CHECK-NEXT:  |                       `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+  // CHECK-NEXT:  |                         `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+  // CHECK-NEXT:  |                           `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+  // CHECK-NEXT:  |                             `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+  // CHECK-NEXT:  |                               `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |                                 `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+  // CHECK-NEXT:  |                                   `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+  // CHECK-NEXT:  |                                     `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+  // CHECK-NEXT:  |                                       `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+  // CHECK-NEXT:  |                                         `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+  // CHECK-NEXT:  |                                           `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |                                             `-CallExpr {{.*}} 'A':'P2718R0::A'
+  // CHECK-NEXT:  |                                               `-ImplicitCastExpr {{.*}} 'A (*)()' <FunctionToPointerDecay>
+  // CHECK-NEXT:  |                                                 `-DeclRefExpr {{.*}} 'A ()' lvalue Function {{.*}} 'g' 'A ()'
+  for (auto e : g().r().g().r().g().r().g())
+    bar(e);
+}
+
+// Test basic && dependent context
+template <typename T> T dg() { return T(); }
+template <typename T> const T &df1(const T &t) { return t; }
+
+void test8() {
+  [[maybe_unused]] int sum = 0;
+  // CHECK: FunctionDecl {{.*}} test8 'void ()'
+  // CHECK:      -CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:  |-<<<NULL>>>
+  // CHECK-NEXT:  |-DeclStmt {{.*}}
+  // CHECK-NEXT:  | `-VarDecl {{.*}} implicit used __range1 'const P2718R0::A &' cinit
+  // CHECK-NEXT:  |   `-ExprWithCleanups {{.*}} 'const P2718R0::A' lvalue
+  // CHECK-NEXT:  |     `-CallExpr {{.*}} 'const P2718R0::A' lvalue
+  // CHECK-NEXT:  |       |-ImplicitCastExpr {{.*}} 'const P2718R0::A &(*)(const P2718R0::A &)' <FunctionToPointerDecay>
+  // CHECK-NEXT:  |       | `-DeclRefExpr {{.*}} 'const P2718R0::A &(const P2718R0::A &)' lvalue Function {{.*}} 'df1' 'const P2718R0::A &(const P2718R0::A &)' (FunctionTemplate {{.*}} 'df1')
+  // CHECK-NEXT:  |       `-MaterializeTemporaryExpr {{.*}} 'const P2718R0::A' lvalue extended by Var {{.*}} '__range1' 'const P2718R0::A &'
+  // CHECK-NEXT:  |         `-ImplicitCastExpr {{.*}} 'const P2718R0::A' <NoOp>
+  // CHECK-NEXT:  |           `-CXXBindTemporaryExpr {{.*}} 'P2718R0::A' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |             `-CallExpr {{.*}} 'P2718R0::A'
+  // CHECK-NEXT:  |               `-ImplicitCastExpr {{.*}} 'P2718R0::A (*)()' <FunctionToPointerDecay>
+  // CHECK-NEXT:  |                 `-DeclRefExpr {{.*}} 'P2718R0::A ()' lvalue Function {{.*}} 'dg' 'P2718R0::A ()' (FunctionTemplate {{.*}} 'dg')
+  for (auto e : df1(dg<A>()))
+    sum += e;
+}
+
+template <typename T> int (&df2(const T *))[3];
+const A *dg2(const A &);
+
+void test9() {
+  // CHECK: FunctionDecl {{.*}} test9 'void ()'
+  // CHECK:      -CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:  |-<<<NULL>>>
+  // CHECK-NEXT:  |-DeclStmt {{.*}}
+  // CHECK-NEXT:  | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+  // CHECK-NEXT:  |   `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+  // CHECK-NEXT:  |     `-CallExpr {{.*}} 'int[3]' lvalue
+  // CHECK-NEXT:  |       |-ImplicitCastExpr {{.*}} 'int (&(*)(const P2718R0::A *))[3]' <FunctionToPointerDecay>
+  // CHECK-NEXT:  |       | `-DeclRefExpr {{.*}} 'int (&(const P2718R0::A *))[3]' lvalue Function {{.*}} 'df2' 'int (&(const P2718R0::A *))[3]' (FunctionTemplate {{.*}} 'df2')
+  // CHECK-NEXT:  |       `-CallExpr {{.*}} 'const A *'
+  // CHECK-NEXT:  |         |-ImplicitCastExpr {{.*}} 'const A *(*)(const A &)' <FunctionToPointerDecay>
+  // CHECK-NEXT:  |         | `-DeclRefExpr {{.*}} 'const A *(const A &)' lvalue Function {{.*}} 'dg2' 'const A *(const A &)'
+  // CHECK-NEXT:  |         `-ImplicitCastExpr {{.*}} 'const A':'const P2718R0::A' lvalue <DerivedToBase (A)>
+  // CHECK-NEXT:  |           `-MaterializeTemporaryExpr {{.*}} 'const B':'const P2718R0::B' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+  // CHECK-NEXT:  |             `-ImplicitCastExpr {{.*}} 'const B':'const P2718R0::B' <NoOp>
+  // CHECK-NEXT:  |               `-CXXBindTemporaryExpr {{.*}} 'B':'P2718R0::B' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |                 `-CXXTemporaryObjectExpr {{.*}} 'B':'P2718R0::B' 'void () noexcept(false)' zeroing
+  for (auto e : df2(dg2(B())))
+    bar(e);
+}
+
+// Test discard statement && dependent context
+void test10() {
+  int v[] = {42, 17, 13};
+
+  // CHECK: FunctionDecl {{.*}} test10 'void ()'
+  // CHECK:      -CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:  |-<<<NULL>>>
+  // CHECK-NEXT:  |-DeclStmt {{.*}}
+  // CHECK-NEXT:  | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+  // CHECK-NEXT:  |   `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+  // CHECK-NEXT:  |     `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+  // CHECK-NEXT:  |       |-CXXStaticCastExpr {{.*}} 'void' static_cast<void> <ToVoid>
+  // CHECK-NEXT:  |       | `-CallExpr {{.*}} 'const P2718R0::LockGuard' lvalue
+  // CHECK-NEXT:  |       |   |-ImplicitCastExpr {{.*}} 'const P2718R0::LockGuard &(*)(const P2718R0::LockGuard &)' <FunctionToPointerDecay>
+  // CHECK-NEXT:  |       |   | `-DeclRefExpr {{.*}} 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' lvalue Function {{.*}} 'df1' 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' (FunctionTemplate {{.*}} 'df1')
+  // CHECK-NEXT:  |       |   `-MaterializeTemporaryExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+  // CHECK-NEXT:  |       |     `-ImplicitCastExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' <NoOp>
+  // CHECK-NEXT:  |       |       `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |       |         `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+  // CHECK-NEXT:  |       `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+  for ([[maybe_unused]] int x : static_cast<void>(df1(LockGuard())), v)
+    LockGuard guard;
+
+  // CHECK:      -CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:  |-<<<NULL>>>
+  // CHECK-NEXT:  |-DeclStmt {{.*}}
+  // CHECK-NEXT:  | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+  // CHECK-NEXT:  |   `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+  // CHECK-NEXT:  |     `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+  // CHECK-NEXT:  |       |-CStyleCastExpr {{.*}} 'void' <ToVoid>
+  // CHECK-NEXT:  |       | `-CallExpr {{.*}} 'const P2718R0::LockGuard' lvalue
+  // CHECK-NEXT:  |       |   |-ImplicitCastExpr {{.*}} 'const P2718R0::LockGuard &(*)(const P2718R0::LockGuard &)' <FunctionToPointerDecay>
+  // CHECK-NEXT:  |       |   | `-DeclRefExpr {{.*}} 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' lvalue Function {{.*}} 'df1' 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' (FunctionTemplate {{.*}} 'df1')
+  // CHECK-NEXT:  |       |   `-MaterializeTemporaryExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+  // CHECK-NEXT:  |       |     `-ImplicitCastExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' <NoOp>
+  // CHECK-NEXT:  |       |       `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |       |         `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+  // CHECK-NEXT:  |       `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+  for ([[maybe_unused]] int x : (void)df1(LockGuard()), v)
+    LockGuard guard;
+
+  // CHECK:      -CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:  |-<<<NULL>>>
+  // CHECK-NEXT:  |-DeclStmt {{.*}}
+  // CHECK-NEXT:  | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+  // CHECK-NEXT:  |   `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+  // CHECK-NEXT:  |     `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+  // CHECK-NEXT:  |       |-BinaryOperator {{.*}} 'const P2718R0::LockGuard' lvalue ','
+  // CHECK-NEXT:  |       | |-CallExpr {{.*}} 'const P2718R0::LockGuard' lvalue
+  // CHECK-NEXT:  |       | | |-ImplicitCastExpr {{.*}} 'const P2718R0::LockGuard &(*)(const P2718R0::LockGuard &)' <FunctionToPointerDecay>
+  // CHECK-NEXT:  |       | | | `-DeclRefExpr {{.*}} 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' lvalue Function {{.*}} 'df1' 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' (FunctionTemplate {{.*}} 'df1')
+  // CHECK-NEXT:  |       | | `-MaterializeTemporaryExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+  // CHECK-NEXT:  |       | |   `-ImplicitCastExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' <NoOp>
+  // CHECK-NEXT:  |       | |     `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |       | |       `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+  // CHECK-NEXT:  |       | `-CallExpr {{.*}} 'const P2718R0::LockGuard' lvalue
+  // CHECK-NEXT:  |       |   |-ImplicitCastExpr {{.*}} 'const P2718R0::LockGuard &(*)(const P2718R0::LockGuard &)' <FunctionToPointerDecay>
+  // CHECK-NEXT:  |       |   | `-DeclRefExpr {{.*}} 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' lvalue Function {{.*}} 'df1' 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' (FunctionTemplate {{.*}} 'df1')
+  // CHECK-NEXT:  |       |   `-MaterializeTemporaryExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+  // CHECK-NEXT:  |       |     `-ImplicitCastExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' <NoOp>
+  // CHECK-NEXT:  |       |       `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |       |         `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+  // CHECK-NEXT:  |       `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+  for ([[maybe_unused]] int x : df1(LockGuard()), df1(LockGuard()), v)
+    LockGuard guard;
+}
+
+// Test default argument && dependent context
+template <typename T> int (&default_arg_fn2(const T & = T()))[3];
+void test11() {
+  for (auto e : default_arg_fn2<A>()) 
+    bar(e);
+}
+
+template <typename T> A foo2(const T&, const DefaultA &Default = DefaultA());
+
+void test12() {
+  for (auto e : default_arg_fn2(foo2(foo2(foo2(A())))))
+    bar(e);
+}
+
+// Test member call && dependent context
+void test13() {
+
+  // CHECK: FunctionDecl {{.*}} test13 'void ()'
+  // CHECK:      -CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:  |-<<<NULL>>>
+  // CHECK-NEXT:  |-DeclStmt {{.*}}
+  // CHECK-NEXT:  | `-VarDecl {{.*}} implicit used __range1 'A &&' cinit
+  // CHECK-NEXT:  |   `-ExprWithCleanups {{.*}} 'A':'P2718R0::A' xvalue
+  // CHECK-NEXT:  |     `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+  // CHECK-NEXT:  |       `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |         `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+  // CHECK-NEXT:  |           `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+  // CHECK-NEXT:  |             `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+  // CHECK-NEXT:  |               `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+  // CHECK-NEXT:  |                 `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+  // CHECK-NEXT:  |                   `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |                     `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+  // CHECK-NEXT:  |                       `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+  // CHECK-NEXT:  |                         `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+  // CHECK-NEXT:  |                           `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+  // CHECK-NEXT:  |                             `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+  // CHECK-NEXT:  |                               `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |                                 `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+  // CHECK-NEXT:  |                                   `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+  // CHECK-NEXT:  |                                     `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+  // CHECK-NEXT:  |                                       `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+  // CHECK-NEXT:  |                                         `-MaterializeTemporaryExpr {{.*}} 'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+  // CHECK-NEXT:  |                                           `-CXXBindTemporaryExpr {{.*}} 'P2718R0::A' (CXXTemporary {{.*}})
+  // CHECK-NEXT:  |                                             `-CallExpr {{.*}} 'P2718R0::A'
+  // CHECK-NEXT:  |                                               `-ImplicitCastExpr {{.*}} 'P2718R0::A (*)()' <FunctionToPointerDecay>
+  // CHECK-NEXT:  |                                                 `-DeclRefExpr {{.*}} 'P2718R0::A ()' lvalue Function {{.*}} 'dg' 'P2718R0::A ()' (FunctionTemplate {{.*}} 'dg')
+  for (auto e : dg<A>().r().g().r().g().r().g())
+    bar(e);
+}
+} // namespace P2718R0
diff --git a/clang/test/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp
index 077385fb7aaaf76..5554363cc69abb5 100644
--- a/clang/test/CXX/special/class.temporary/p6.cpp
+++ b/clang/test/CXX/special/class.temporary/p6.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -std=c++17 %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s --implicit-check-not='call{{.*}}dtor'
+// RUN: %clang_cc1 -std=c++23 %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-CXX23,CHECK-CXX23-NEXT,CHECK-CXX23-LABEL
 
 namespace std {
   typedef decltype(sizeof(int)) size_t;
@@ -9,7 +10,36 @@ namespace std {
     size_t   size;
     initializer_list() : begin(nullptr), size(0) {}
   };
-}
+
+  template <typename E>
+  struct list {
+    list() {}
+    ~list() {}
+    E *begin();
+    E *end();
+    const E *begin() const;
+    const E *end() const;
+  };
+
+  template <typename E>
+  struct vector {
+    vector() {}
+    vector(std::initializer_list<E>) {}
+    ~vector() {}
+    E *begin();
+    E *end();
+    const E *begin() const;
+    const E *end() const;
+  };
+
+  template <typename T>
+  struct lock_guard {
+    lock_guard(T) {}
+    ~lock_guard() {}
+  };
+
+  struct mutex {};
+} // namespace std
 
 void then();
 
@@ -238,3 +268,282 @@ void init_capture_init_list() {
   // CHECK: call {{.*}}dtor
   // CHECK: }
 }
+
+namespace P2718R0 {
+namespace basic {
+template <typename E> using T2 = std::list<E>;
+template <typename E> const T2<E> &f1_temp(const T2<E> &t)  { return t; }
+template <typename E> const T2<E> &f2_temp(T2<E> t)         { return t; }
+template <typename E> T2<E> g_temp()                        { return T2<E>{}; }
+
+template <typename E>
+void foo_dependent_context1() {
+  // CHECK-CXX23: void @_ZN7P2718R05basic22foo_dependent_context1IiEEvv()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  for (auto e : f1_temp(g_temp<E>())) {}  // OK, lifetime of return value of g() extended
+}
+
+template <typename E>
+void foo_dependent_context2() {
+  // CHECK-CXX23: void @_ZN7P2718R05basic22foo_dependent_context2IiEEvv()
+  // CHECK-CXX23-NEXT: entry:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R05basic6g_tempIiEESt4listIT_Ev(
+  // CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R05basic7f2_tempIiEERKSt4listIT_ES4_(
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  // CHECK-CXX23: call {{.*}} @_ZNKSt4listIiE5beginEv(
+  // CHECK-CXX23: call {{.*}} @_ZNKSt4listIiE3endEv(
+  for (auto e : f2_temp(g_temp<E>())) {}  // undefined behavior
+}
+
+template void foo_dependent_context1<int>();
+template void foo_dependent_context2<int>();
+} // namespace basic
+
+namespace discard_value_expression {
+template <typename T>
+void f_dependent_context1() {
+  std::vector<T> v = { 42, 17, 13 };
+  std::mutex m;
+  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression20f_dependent_context1IiEEvv()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
+  for (T x : std::lock_guard<std::mutex>(m), v)  // lock released in C++ 2023
+    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
+}
+
+template <typename T>
+void f_dependent_context2() {
+  std::vector<T> v = { 42, 17, 13 };
+  std::mutex m;
+  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression20f_dependent_context2IiEEvv()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
+  for (T x : (void)std::lock_guard<std::mutex>(m), v)  // lock released in C++ 2023
+    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
+}
+
+template <typename T>
+void f_dependent_context3() {
+  std::vector<T> v = { 42, 17, 13 };
+  std::mutex m;
+  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression20f_dependent_context3IiEEvv()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
+  for (T x : static_cast<void>(std::lock_guard<std::mutex>(m)), v)  // lock released in C++ 2023
+    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
+}
+
+template void f_dependent_context1<int>();
+template void f_dependent_context2<int>();
+template void f_dependent_context3<int>();
+} // namespace discard_value_expression
+
+namespace member_call {
+template <typename T>
+struct ListWrapper {
+  std::list<T> list;
+  ListWrapper() {}
+  ~ListWrapper() {}
+  const T *begin() const { return list.begin(); }
+  const T *end() const { return list.end(); }
+  ListWrapper& r() { return *this; }
+  ListWrapper g() { return ListWrapper(); }
+};
+
+template <typename E>
+ListWrapper<E> g_temp() { return ListWrapper<E>{}; }
+
+template <typename T>
+void member_call_dependent_context() {
+  // CHECK-CXX23: void @_ZN7P2718R011member_call29member_call_dependent_contextIiEEvv()
+  // CHECK-CXX23-LABEL: for.cond.cleanup: 
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+  for (auto e : g_temp<T>().r().g().r().g().r().g()) {}
+}
+
+template void member_call_dependent_context<int>();
+} // namespace member_call
+
+namespace default_arg {
+template <typename T>
+struct DefaultArg {
+  DefaultArg() {}
+  DefaultArg(int) {}
+  ~DefaultArg() {}
+};
+
+template <typename T>
+struct C2 : public std::list<T> {
+  C2() {}
+  C2(int, const C2 &, const DefaultArg<T> &Default = DefaultArg<T>{}) {}
+};
+
+template <typename T>
+std::list<T> temp_foo(const std::list<T>&, const DefaultArg<T> &Default = DefaultArg<T>{}) {
+  return std::list<T>{};
+}
+
+template <typename T>
+void default_arg_dependent_context1() {
+  // CHECK-CXX23: void @_ZN7P2718R011default_arg30default_arg_dependent_context1IiEEvv()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  for (auto e : temp_foo(std::list<T>{})) {}
+}
+
+template <typename T>
+void default_arg_dependent_context2() {
+  // CHECK-CXX23: void @_ZN7P2718R011default_arg30default_arg_dependent_context2IiEEvv()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  for (auto e : temp_foo(temp_foo(std::list<T>{}))) {}
+}
+
+template <typename T>
+void default_arg_dependent_context3() {
+  // CHECK-CXX23: void @_ZN7P2718R011default_arg30default_arg_dependent_context3IiEEvv()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(
+
+  for (auto e : C2<T>(0, C2<T>(0, C2<T>(0, C2<T>())))) {}
+}
+
+template void default_arg_dependent_context1<int>();
+template void default_arg_dependent_context2<int>();
+template void default_arg_dependent_context3<int>();
+} // namespace default_arg
+
+namespace basic {
+using T = std::list<int>;
+const T& f1(const T& t) { return t; }
+const T& f2(T t)        { return t; }
+T g()                   { return T{}; }
+
+void foo1() {
+  // CHECK-CXX23: void @_ZN7P2718R05basic4foo1Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  for (auto e : f1(g())) {}  // OK, lifetime of return value of g() extended
+}
+
+void foo2() {
+  // CHECK-CXX23: void @_ZN7P2718R05basic4foo2Ev()
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R05basic1gEv(
+  // CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R05basic2f2ESt4listIiE(
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  for (auto e : f2(g())) {}  // undefined behavior
+}
+} // namespace basic
+
+namespace discard_value_expression {
+void f1() {
+  std::vector<int> v = { 42, 17, 13 };
+  std::mutex m;
+  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression2f1Ev()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev( 
+  for (int x : std::lock_guard<std::mutex>(m), v)  // lock released in C++ 2023
+    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
+}
+
+void f2() {
+  std::vector<int> v = { 42, 17, 13 };
+  std::mutex m;
+  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression2f2Ev()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
+  for (int x : (void)std::lock_guard<std::mutex>(m), v)  // lock released in C++ 2023
+    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
+}
+
+void f3() {
+  std::vector<int> v = { 42, 17, 13 };
+  std::mutex m;
+  // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression2f3Ev()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
+  for (int x : static_cast<void>(std::lock_guard<std::mutex>(m)), v)  // lock released in C++ 2023
+    std::lock_guard<std::mutex> guard(m);  // OK in C++ 2023, now deadlocks
+}
+} // namespace discard_value_expression
+
+namespace member_call {
+using A = ListWrapper<int>;
+
+A g() { return A(); }
+const A &f1(const A &t) { return t; }
+
+void member_call() {
+  // CHECK-CXX23: void @_ZN7P2718R011member_call11member_callEv()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+  for (auto e : g().r().g().r().g().r().g()) {}
+}
+} // namespace member_call
+
+namespace default_arg {
+using A = std::list<int>;
+using DefaultA = DefaultArg<int>;
+struct C : public A {
+  C() {}
+  C(int, const C &, const DefaultA & = DefaultA()) {}
+};
+
+A foo(const A&, const DefaultA &Default = DefaultA()) {
+  return A();
+}
+
+int (&some_func(const A & = A{}))[3];
+
+void default_arg1() {
+  // CHECK-CXX23: void @_ZN7P2718R011default_arg12default_arg1Ev()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  for (auto e : some_func()) {}
+}
+
+void default_arg2() {
+  // CHECK-CXX23: void @_ZN7P2718R011default_arg12default_arg2Ev()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+  for (auto e : some_func(foo(foo(A())))) {}
+}
+
+void default_arg3() {
+  // CHECK-CXX23: void @_ZN7P2718R011default_arg12default_arg3Ev()
+  // CHECK-CXX23-LABEL: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
+  for (auto e : C(0, C(0, C(0, C())))) {}
+}
+} // namespace default_arg
+} // namespace P2718R0
+
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index eb0e615f9470b2d..2650a3a82252ba2 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -309,7 +309,7 @@
 #error "wrong value for __cpp_constexpr"
 #endif
 
-#if check(range_based_for, 0, 200907, 200907, 201603, 201603, 201603, 201603)
+#if check(range_based_for, 0, 200907, 200907, 201603, 201603, 202211, 202211)
 #error "wrong value for __cpp_range_based_for"
 #endif
 
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index a3c176d02129cfe..7602018bf4be753 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -429,7 +429,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="none" align="center">No</td>
+      <td class="none" align="center">Clang 18</td>
     </tr>
     <!--Issaquah 2023 papers-->
     <tr>

>From 7b353d11fa8b902ce236ac8101ddceeb504d99e0 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jan 2024 23:51:20 +0800
Subject: [PATCH 2/3] Revert changes of CXXDefaultInitExpr

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

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index cbcf5d690939c00..b6d14bf48486c2b 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6348,7 +6348,6 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   Expr *Init = nullptr;
 
   bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-  bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
   EnterExpressionEvaluationContext EvalContext(
       *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
 
@@ -6383,16 +6382,12 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   ImmediateCallVisitor V(getASTContext());
   if (!NestedDefaultChecking)
     V.TraverseDecl(Field);
-  if (V.HasImmediateCalls || InLifetimeExtendingContext) {
+  if (V.HasImmediateCalls) {
     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();
-    keepInMaterializeTemporaryObjectContext();
     EnsureImmediateInvocationInDefaultArgs Immediate(*this);
     ExprResult Res;
     runWithSufficientStackSpace(Loc, [&] {
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 7602018bf4be753..173d4b36e89d04d 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -429,7 +429,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="none" align="center">Clang 18</td>
+      <td class="none" align="center">Clang 19</td>
     </tr>
     <!--Issaquah 2023 papers-->
     <tr>

>From 6e58553c7a2d319f98556ce3e7b4331cc4371fad Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jan 2024 23:55:47 +0800
Subject: [PATCH 3/3] Revert whitespace change

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

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b6d14bf48486c2b..abe300ecc5431b7 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6348,6 +6348,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   Expr *Init = nullptr;
 
   bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
+
   EnterExpressionEvaluationContext EvalContext(
       *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
 



More information about the cfe-commits mailing list