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

via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 26 06:29:21 PST 2024


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

>From 89eeeaea909ba8297236889e50936f8948dd82a4 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 11 Jan 2024 23:28:32 +0800
Subject: [PATCH 1/2] [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               |  43 ++-
 clang/lib/Parse/ParseDecl.cpp                 |  19 +
 clang/lib/Parse/ParseStmt.cpp                 |   8 +-
 clang/lib/Sema/SemaExpr.cpp                   |  22 +-
 clang/lib/Sema/SemaExprCXX.cpp                |  47 ++-
 clang/lib/Sema/SemaInit.cpp                   |   4 +
 clang/lib/Sema/SemaStmt.cpp                   |  17 +-
 .../test/AST/ast-dump-for-range-lifetime.cpp  | 355 ++++++++++++++++++
 clang/test/CXX/special/class.temporary/p6.cpp | 234 ++++++++++++
 11 files changed, 713 insertions(+), 41 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 392f694065a242b..01b581b26934ebe 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -195,6 +195,9 @@ C++23 Feature Support
 - Added a separate warning to warn the use of attributes on lambdas as a C++23 extension
   in previous language versions: ``-Wc++23-lambda-attributes``.
 
+- 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 9e2b452cbe05172..7bc2acd5e84d092 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2398,7 +2398,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 0db39333b0ee347..1f2a5a546821f8a 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 expression which need to
+    /// extend lifetime. Add MaterializeTemporaryExpr* if the value of
+    /// IsInLifetimeExtendingContext 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,19 @@ class Sema final {
     // VLAs).
     bool InConditionallyConstantEvaluateContext = false;
 
+    /// Whether we are currently in a context in which temporaries must be
+    /// lifetime-extended (Eg. in a for-range initializer).
+    bool IsInLifetimeExtendingContext = false;
+
+    /// Whether we should materialize temporaries in discarded expressions.
+    ///
+    /// [C++23][class.temporary]/p2.6 when a prvalue that has type other than cv
+    /// void appears as a discarded-value expression ([expr.context]).
+    ///
+    /// We do not materialize temporaries by default in order to avoid creating
+    /// unnecessary temporary objects.
+    bool MaterializePRValueInDiscardedExpression = 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
@@ -5245,13 +5264,11 @@ 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 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,
@@ -9985,6 +10002,18 @@ class Sema final {
     return currentEvaluationContext().isImmediateFunctionContext();
   }
 
+  bool isInLifetimeExtendingContext() const {
+    assert(!ExprEvalContexts.empty() &&
+           "Must be in an expression evaluation context");
+    return ExprEvalContexts.back().IsInLifetimeExtendingContext;
+  }
+
+  bool ShouldMaterializePRValueInDiscardedExpression() const {
+    assert(!ExprEvalContexts.empty() &&
+           "Must be in an expression evaluation context");
+    return ExprEvalContexts.back().MaterializePRValueInDiscardedExpression;
+  }
+
   bool isCheckingDefaultArgumentOrInitializer() const {
     const ExpressionEvaluationContextRecord &Ctx = currentEvaluationContext();
     return (Ctx.Context ==
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 356e7851ec639c1..f68164fef372ad0 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2312,12 +2312,31 @@ 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.IsInLifetimeExtendingContext = true;
+
+        // Materialize non-`cv void` prvalue temporaries in discarded
+        // expressions. These materialized temporaries may be lifetime-extented.
+        LastRecord.MaterializePRValueInDiscardedExpression = true;
+      }
+
       if (getLangOpts().OpenMP)
         Actions.startOpenMPCXXRangeFor();
       if (Tok.is(tok::l_brace))
         FRI->RangeExpr = ParseBraceInitializer();
       else
         FRI->RangeExpr = ParseExpression();
+      if (getLangOpts().CPlusPlus23)
+        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 6413a48f809ac9c..ad2dca6fabf083b 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6256,7 +6256,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
   assert(Param->hasDefaultArg() && "can't build nonexistent default arg");
 
   bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-
+  bool IsInLifetimeExtendingContext = isInLifetimeExtendingContext();
   std::optional<ExpressionEvaluationContextRecord::InitializationContext>
       InitializationContext =
           OutermostDeclarationWithDelayedImmediateInvocations();
@@ -6289,9 +6289,19 @@ 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 || IsInLifetimeExtendingContext) {
+      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.
+      auto &LastRecord = ExprEvalContexts.back();
+      auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
+      LastRecord.IsInLifetimeExtendingContext = IsInLifetimeExtendingContext;
+
       EnsureImmediateInvocationInDefaultArgs Immediate(*this);
       ExprResult Res;
       runWithSufficientStackSpace(CallLoc, [&] {
@@ -6305,6 +6315,10 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
       if (Res.isInvalid())
         return ExprError();
       Init = Res.get();
+
+      // Collect MaterializeTemporaryExprs in the rewritten CXXDefaultArgExpr.
+      PrevRecord.ForRangeLifetimeExtendTemps.append(
+          LastRecord.ForRangeLifetimeExtendTemps);
     }
   }
 
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 8b6a80b45b27b12..259354821ebf306 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8213,21 +8213,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
@@ -8248,11 +8233,33 @@ 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 (ShouldMaterializePRValueInDiscardedExpression() &&
+        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 18440a69e3a3d92..1fa5039849adc75 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..24eab21284e814a 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2489,11 +2489,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();
@@ -2545,6 +2545,13 @@ StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
     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());
+  }
+
   // Claim the type doesn't contain auto: we've already done the checking.
   DeclGroupPtrTy RangeGroup =
       BuildDeclaratorGroup(MutableArrayRef<Decl *>((Decl **)&RangeVar, 1));
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..ff5ba7e563b0645 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
 
 namespace std {
   typedef decltype(sizeof(int)) size_t;
@@ -238,3 +239,236 @@ void init_capture_init_list() {
   // CHECK: call {{.*}}dtor
   // CHECK: }
 }
+
+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-CXX23: void @_ZN7P2718R05test1Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  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-CXX23: void @_ZN7P2718R05test2Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01BD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  for (auto e : f(g(B())))
+    bar(e);
+}
+
+// Test discard statement.
+struct LockGuard {
+    LockGuard() {}
+    ~LockGuard() {}
+};
+
+void test3() {
+  int v[] = {42, 17, 13};
+
+  // CHECK-CXX23: void @_ZN7P2718R05test3Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  for ([[maybe_unused]] int x : static_cast<void>(LockGuard()), v)
+    LockGuard guard;
+  
+  // CHECK-CXX23: for.cond.cleanup11:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end17
+  for ([[maybe_unused]] int x : (void)LockGuard(), v)
+    LockGuard guard;
+  
+  // CHECK-CXX23: for.cond.cleanup27:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end33
+  for ([[maybe_unused]] int x : LockGuard(), v)
+    LockGuard guard;
+}
+
+// Test default arg
+int (&default_arg_fn(const A & = A()))[3];
+void test4() {
+
+  // CHECK-CXX23: void @_ZN7P2718R05test4Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  for (auto e : default_arg_fn()) 
+    bar(e);
+}
+
+struct DefaultA {
+  DefaultA() {}
+  ~DefaultA() {}
+};
+
+A foo(const A&, const DefaultA &Default = DefaultA()) {
+  return A();
+}
+
+void test5() {
+  // CHECK-CXX23: void @_ZN7P2718R05test5Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  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() {
+  // CHECK-CXX23: void @_ZN7P2718R05test6Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
+  // CHECK-CXX23: br label %for.end
+  for (auto e : C(0, C(0, C(0, C()))))
+    bar(e);
+}
+
+// Test member call
+void test7() {
+  // CHECK-CXX23: void @_ZN7P2718R05test7Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  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-CXX23: void @_ZN7P2718R05test8Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  for (auto e : df1(dg<A>()))
+    sum += e;
+}
+
+template <typename T> int (&df2(const T *))[3];
+const A *dg2(const A &);
+
+void test9() {
+  // CHECK-CXX23: void @_ZN7P2718R05test9Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01BD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  for (auto e : df2(dg2(B())))
+    bar(e);
+}
+
+// Test discard statement && dependent context
+void test10() {
+  int v[] = {42, 17, 13};
+
+  // CHECK-CXX23: void @_ZN7P2718R06test10Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  for ([[maybe_unused]] int x : static_cast<void>(df1(LockGuard())), v)
+    LockGuard guard;
+  
+  // CHECK-CXX23: for.cond.cleanup12:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.inc16
+  for ([[maybe_unused]] int x : (void)df1(LockGuard()), v)
+    LockGuard guard;
+  
+  // CHECK-CXX23: for.cond.cleanup31:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end37
+  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() {
+  // CHECK-CXX23: void @_ZN7P2718R06test11Ev()
+  // CHECK-CXX23-NEXT: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  for (auto e : default_arg_fn2<A>()) 
+    bar(e);
+}
+
+template <typename T> A foo2(const T&, const DefaultA &Default = DefaultA());
+
+void test12() {
+  // CHECK-CXX23: void @_ZN7P2718R06test12Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  for (auto e : default_arg_fn2(foo2(foo2(foo2(A())))))
+    bar(e);
+}
+
+// Test member call && dependent context
+void test13() {
+
+  // CHECK-CXX23: void @_ZN7P2718R06test13Ev()
+  // CHECK-CXX23: for.cond.cleanup:
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+  // CHECK-CXX23-NEXT: br label %for.end
+  for (auto e : dg<A>().r().g().r().g().r().g())
+    bar(e);
+}
+} // namespace P2718R0
+

>From 39d21fcc656849f52739f668eacbaad658961824 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Fri, 26 Jan 2024 22:28:49 +0800
Subject: [PATCH 2/2] Address review comments and fix bugs in dependent context

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 clang/include/clang/Sema/Sema.h               |  38 +-
 clang/lib/Parse/ParseDecl.cpp                 |  13 +-
 clang/lib/Sema/SemaExpr.cpp                   |  28 +-
 clang/lib/Sema/SemaExprCXX.cpp                |   2 +-
 clang/lib/Sema/SemaStmt.cpp                   |  20 +-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   2 +
 clang/lib/Sema/TreeTransform.h                |  48 +-
 clang/test/CXX/special/class.temporary/p6.cpp | 493 ++++++++++--------
 8 files changed, 391 insertions(+), 253 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1f2a5a546821f8a..aece94d5e21c415 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1343,7 +1343,7 @@ class Sema final {
     llvm::SmallPtrSet<DeclRefExpr *, 4> ReferenceToConsteval;
 
     /// P2718R0 - Lifetime extension in range-based for loops.
-    /// MaterializeTemporaryExprs in for-range-init expression which need to
+    /// MaterializeTemporaryExprs in for-range-init expressions which need to
     /// extend lifetime. Add MaterializeTemporaryExpr* if the value of
     /// IsInLifetimeExtendingContext is true.
     SmallVector<MaterializeTemporaryExpr *, 8> ForRangeLifetimeExtendTemps;
@@ -1367,8 +1367,9 @@ class Sema final {
     // VLAs).
     bool InConditionallyConstantEvaluateContext = false;
 
-    /// Whether we are currently in a context in which temporaries must be
-    /// lifetime-extended (Eg. in a for-range initializer).
+    /// 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 IsInLifetimeExtendingContext = false;
 
     /// Whether we should materialize temporaries in discarded expressions.
@@ -5277,7 +5278,8 @@ class Sema final {
                                   Expr *Cond, Expr *Inc,
                                   Stmt *LoopVarDecl,
                                   SourceLocation RParenLoc,
-                                  BuildForRangeKind Kind);
+                                  BuildForRangeKind Kind,
+      ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps = {});
   StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body);
 
   StmtResult ActOnGotoStmt(SourceLocation GotoLoc,
@@ -10008,7 +10010,7 @@ class Sema final {
     return ExprEvalContexts.back().IsInLifetimeExtendingContext;
   }
 
-  bool ShouldMaterializePRValueInDiscardedExpression() const {
+  bool shouldMaterializePRValueInDiscardedExpression() const {
     assert(!ExprEvalContexts.empty() &&
            "Must be in an expression evaluation context");
     return ExprEvalContexts.back().MaterializePRValueInDiscardedExpression;
@@ -10053,6 +10055,32 @@ class Sema final {
     return Res;
   }
 
+  /// keepInLifetimeExtendingContext - Pull down IsInLifetimeExtendingContext
+  /// flag from previous context.
+  void keepInLifetimeExtendingContext() {
+    if (ExprEvalContexts.size() > 2 &&
+        ExprEvalContexts[ExprEvalContexts.size() - 2]
+            .IsInLifetimeExtendingContext) {
+      auto &LastRecord = ExprEvalContexts.back();
+      auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
+      LastRecord.IsInLifetimeExtendingContext =
+          PrevRecord.IsInLifetimeExtendingContext;
+    }
+  }
+
+  /// keepInLifetimeExtendingContext - Pull down
+  /// MaterializePRValueInDiscardedExpression flag from previous context.
+  void keepMaterializePRValueInDiscardedExpression() {
+    if (ExprEvalContexts.size() > 2 &&
+        ExprEvalContexts[ExprEvalContexts.size() - 2]
+            .MaterializePRValueInDiscardedExpression) {
+      auto &LastRecord = ExprEvalContexts.back();
+      auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
+      LastRecord.MaterializePRValueInDiscardedExpression =
+          PrevRecord.MaterializePRValueInDiscardedExpression;
+    }
+  }
+
   /// RAII class used to determine whether SFINAE has
   /// trapped any errors that occur during template argument
   /// deduction.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index f68164fef372ad0..1624e27d653f465 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2334,9 +2334,16 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
         FRI->RangeExpr = ParseBraceInitializer();
       else
         FRI->RangeExpr = ParseExpression();
-      if (getLangOpts().CPlusPlus23)
-        FRI->LifetimeExtendTemps = std::move(
-            Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
+
+      // 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/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index ad2dca6fabf083b..db6177754d08ffb 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6298,10 +6298,8 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
             CallLoc, Param, CurContext};
       // Pass down lifetime extending flag, and collect temporaries in
       // CreateMaterializeTemporaryExpr when we rewrite the call argument.
-      auto &LastRecord = ExprEvalContexts.back();
-      auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
-      LastRecord.IsInLifetimeExtendingContext = IsInLifetimeExtendingContext;
-
+      keepInLifetimeExtendingContext();
+      keepMaterializePRValueInDiscardedExpression();
       EnsureImmediateInvocationInDefaultArgs Immediate(*this);
       ExprResult Res;
       runWithSufficientStackSpace(CallLoc, [&] {
@@ -6315,10 +6313,6 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
       if (Res.isInvalid())
         return ExprError();
       Init = Res.get();
-
-      // Collect MaterializeTemporaryExprs in the rewritten CXXDefaultArgExpr.
-      PrevRecord.ForRangeLifetimeExtendTemps.append(
-          LastRecord.ForRangeLifetimeExtendTemps);
     }
   }
 
@@ -6351,7 +6345,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   Expr *Init = nullptr;
 
   bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-
+  bool IsInLifetimeExtendingContext = isInLifetimeExtendingContext();
   EnterExpressionEvaluationContext EvalContext(
       *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
 
@@ -6386,12 +6380,16 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   ImmediateCallVisitor V(getASTContext());
   if (!NestedDefaultChecking)
     V.TraverseDecl(Field);
-  if (V.HasImmediateCalls) {
+  if (V.HasImmediateCalls || IsInLifetimeExtendingContext) {
     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();
+    keepMaterializePRValueInDiscardedExpression();
     EnsureImmediateInvocationInDefaultArgs Immediate(*this);
     ExprResult Res;
     runWithSufficientStackSpace(Loc, [&] {
@@ -18664,6 +18662,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.IsInLifetimeExtendingContext && !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 259354821ebf306..f7131c6d9ecedf7 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8237,7 +8237,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 (ShouldMaterializePRValueInDiscardedExpression() &&
+    if (shouldMaterializePRValueInDiscardedExpression() &&
         getLangOpts().CPlusPlus17 && E->isPRValue() &&
         !E->getType()->isVoidType()) {
       ExprResult Res = TemporaryMaterializationConversion(E);
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 24eab21284e814a..24f37105d5c2ad6 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"
@@ -2545,13 +2546,6 @@ StmtResult Sema::ActOnCXXForRangeStmt(
     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());
-  }
-
   // Claim the type doesn't contain auto: we've already done the checking.
   DeclGroupPtrTy RangeGroup =
       BuildDeclaratorGroup(MutableArrayRef<Decl *>((Decl **)&RangeVar, 1));
@@ -2564,7 +2558,7 @@ StmtResult Sema::ActOnCXXForRangeStmt(
   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();
@@ -2760,7 +2754,7 @@ StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc,
                                       Stmt *Begin, Stmt *End, Expr *Cond,
                                       Expr *Inc, Stmt *LoopVarDecl,
                                       SourceLocation RParenLoc,
-                                      BuildForRangeKind Kind) {
+                                      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.
@@ -2820,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..35071f0d56bb96f 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();
+    keepMaterializePRValueInDiscardedExpression();
     // Instantiate the initializer.
     ExprResult Init;
 
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 4463904b07211bd..72a2ac9b392f846 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -2549,7 +2549,7 @@ class TreeTransform {
                                     SourceLocation ColonLoc, Stmt *Range,
                                     Stmt *Begin, Stmt *End, Expr *Cond,
                                     Expr *Inc, Stmt *LoopVar,
-                                    SourceLocation RParenLoc) {
+                                    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)) {
@@ -2577,7 +2577,7 @@ class TreeTransform {
 
     return getSema().BuildCXXForRangeStmt(ForLoc, CoawaitLoc, Init, ColonLoc,
                                           Range, Begin, End, Cond, Inc, LoopVar,
-                                          RParenLoc, Sema::BFRK_Rebuild);
+                                          RParenLoc, Sema::BFRK_Rebuild, LifetimeExtendTemps);
   }
 
   /// Build a new C++0x range-based for statement.
@@ -4134,6 +4134,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(),
@@ -8537,6 +8539,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.IsInLifetimeExtendingContext = true;
+
+    // Materialize non-`cv void` prvalue temporaries in discarded
+    // expressions. These materialized temporaries may be lifetime-extented.
+    LastRecord.MaterializePRValueInDiscardedExpression = true;
+  }
   StmtResult Init =
       S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult();
   if (Init.isInvalid())
@@ -8546,6 +8563,11 @@ 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();
@@ -8582,13 +8604,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(
@@ -8604,13 +8623,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/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp
index ff5ba7e563b0645..5554363cc69abb5 100644
--- a/clang/test/CXX/special/class.temporary/p6.cpp
+++ b/clang/test/CXX/special/class.temporary/p6.cpp
@@ -1,5 +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
+// 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;
@@ -10,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();
 
@@ -241,234 +270,280 @@ void init_capture_init_list() {
 }
 
 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-CXX23: void @_ZN7P2718R05test1Ev()
-  // CHECK-CXX23: for.cond.cleanup:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  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-CXX23: void @_ZN7P2718R05test2Ev()
+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 @_ZN7P2718R01BD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  for (auto e : f(g(B())))
-    bar(e);
-}
+  // 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(); }
+};
 
-// Test discard statement.
-struct LockGuard {
-    LockGuard() {}
-    ~LockGuard() {}
+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() {}
 };
 
-void test3() {
-  int v[] = {42, 17, 13};
+template <typename T>
+struct C2 : public std::list<T> {
+  C2() {}
+  C2(int, const C2 &, const DefaultArg<T> &Default = DefaultArg<T>{}) {}
+};
 
-  // CHECK-CXX23: void @_ZN7P2718R05test3Ev()
+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 @_ZN7P2718R09LockGuardD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  for ([[maybe_unused]] int x : static_cast<void>(LockGuard()), v)
-    LockGuard guard;
-  
-  // CHECK-CXX23: for.cond.cleanup11:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end17
-  for ([[maybe_unused]] int x : (void)LockGuard(), v)
-    LockGuard guard;
-  
-  // CHECK-CXX23: for.cond.cleanup27:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end33
-  for ([[maybe_unused]] int x : LockGuard(), v)
-    LockGuard guard;
-}
-
-// Test default arg
-int (&default_arg_fn(const A & = A()))[3];
-void test4() {
-
-  // CHECK-CXX23: void @_ZN7P2718R05test4Ev()
-  // CHECK-CXX23: for.cond.cleanup:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  for (auto e : default_arg_fn()) 
-    bar(e);
-}
+  // 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>;
 
-struct DefaultA {
-  DefaultA() {}
-  ~DefaultA() {}
-};
-
-A foo(const A&, const DefaultA &Default = DefaultA()) {
-  return A();
-}
+A g() { return A(); }
+const A &f1(const A &t) { return t; }
 
-void test5() {
-  // CHECK-CXX23: void @_ZN7P2718R05test5Ev()
-  // CHECK-CXX23: for.cond.cleanup:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  for (auto e : default_arg_fn(foo(foo(foo(A())))))
-    bar(e);
+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()) {}
 };
 
-void test6() {
-  // CHECK-CXX23: void @_ZN7P2718R05test6Ev()
-  // CHECK-CXX23: for.cond.cleanup:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
-  // CHECK-CXX23: br label %for.end
-  for (auto e : C(0, C(0, C(0, C()))))
-    bar(e);
-}
-
-// Test member call
-void test7() {
-  // CHECK-CXX23: void @_ZN7P2718R05test7Ev()
-  // CHECK-CXX23: for.cond.cleanup:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  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-CXX23: void @_ZN7P2718R05test8Ev()
-  // CHECK-CXX23: for.cond.cleanup:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  for (auto e : df1(dg<A>()))
-    sum += e;
+A foo(const A&, const DefaultA &Default = DefaultA()) {
+  return A();
 }
 
-template <typename T> int (&df2(const T *))[3];
-const A *dg2(const A &);
+int (&some_func(const A & = A{}))[3];
 
-void test9() {
-  // CHECK-CXX23: void @_ZN7P2718R05test9Ev()
-  // CHECK-CXX23: for.cond.cleanup:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01BD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  for (auto e : df2(dg2(B())))
-    bar(e);
+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()) {}
 }
 
-// Test discard statement && dependent context
-void test10() {
-  int v[] = {42, 17, 13};
+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())))) {}
+}
 
-  // CHECK-CXX23: void @_ZN7P2718R06test10Ev()
-  // CHECK-CXX23: for.cond.cleanup:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  for ([[maybe_unused]] int x : static_cast<void>(df1(LockGuard())), v)
-    LockGuard guard;
-  
-  // CHECK-CXX23: for.cond.cleanup12:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.inc16
-  for ([[maybe_unused]] int x : (void)df1(LockGuard()), v)
-    LockGuard guard;
-  
-  // CHECK-CXX23: for.cond.cleanup31:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end37
-  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() {
-  // CHECK-CXX23: void @_ZN7P2718R06test11Ev()
-  // CHECK-CXX23-NEXT: for.cond.cleanup:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  for (auto e : default_arg_fn2<A>()) 
-    bar(e);
-}
-
-template <typename T> A foo2(const T&, const DefaultA &Default = DefaultA());
-
-void test12() {
-  // CHECK-CXX23: void @_ZN7P2718R06test12Ev()
-  // CHECK-CXX23: for.cond.cleanup:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  for (auto e : default_arg_fn2(foo2(foo2(foo2(A())))))
-    bar(e);
-}
-
-// Test member call && dependent context
-void test13() {
-
-  // CHECK-CXX23: void @_ZN7P2718R06test13Ev()
-  // CHECK-CXX23: for.cond.cleanup:
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
-  // CHECK-CXX23-NEXT: br label %for.end
-  for (auto e : dg<A>().r().g().r().g().r().g())
-    bar(e);
+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
 



More information about the cfe-commits mailing list