yronglin updated this revision to Diff 549449.
yronglin added a comment.

Only create addational metarialized temporary in for-range-init.
FIXME: Need to handle function default argument, and add more test to make sure 
the generated LLVM IR is correct.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153701/new/

https://reviews.llvm.org/D153701

Files:
  clang/include/clang/Sema/Sema.h
  clang/lib/Parse/ParseDecl.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaInit.cpp
  clang/lib/Sema/SemaStmt.cpp
  clang/test/AST/ast-dump-for-range-lifetime.cpp

Index: clang/test/AST/ast-dump-for-range-lifetime.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/ast-dump-for-range-lifetime.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-linux-gnu -fcxx-exceptions -ast-dump %s \
+// RUN: | FileCheck -strict-whitespace %s
+
+namespace p2718r0 {
+struct T {
+  int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  T() {}
+  ~T() {}
+  const int *begin() const { return a; }
+  const int *end() const { return a + 10; }
+};
+
+const T &f1(const T &t) { return t; }
+T g() { return T(); }
+
+void foo() {
+  // CHECK: FunctionDecl {{.*}} foo 'void ()'
+  // CHECK: `-CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:    |-<<<NULL>>>
+  // CHECK-NEXT:    |-DeclStmt {{.*}}
+  // CHECK-NEXT:    | `-VarDecl {{.*}} implicit used __range1 'const T &' cinit
+  // CHECK-NEXT:    |   `-ExprWithCleanups {{.*}} 'const T':'const p2718r0::T' lvalue
+  // CHECK-NEXT:    |     `-CallExpr {{.*}} 'const T':'const p2718r0::T' lvalue
+  // CHECK-NEXT:    |       |-ImplicitCastExpr {{.*}} 'const T &(*)(const T &)' <FunctionToPointerDecay>
+  // CHECK-NEXT:    |       | `-DeclRefExpr {{.*}} 'const T &(const T &)' lvalue Function {{.*}} 'f1' 'const T &(const T &)'
+  // CHECK-NEXT:    |       `-MaterializeTemporaryExpr {{.*}} 'const T':'const p2718r0::T' lvalue extended by Var {{.*}} '__range1' 'const T &'
+  // CHECK-NEXT:    |         `-ImplicitCastExpr {{.*}} 'const T':'const p2718r0::T' <NoOp>
+  // CHECK-NEXT:    |           `-CXXBindTemporaryExpr {{.*}} <col:20, col:22> 'T':'p2718r0::T' (CXXTemporary {{.*}})
+  // CHECK-NEXT:    |             `-CallExpr {{.*}} 'T':'p2718r0::T'
+  // CHECK-NEXT:    |               `-ImplicitCastExpr {{.*}} 'T (*)()' <FunctionToPointerDecay>
+  // CHECK-NEXT:    |                 `-DeclRefExpr {{.*}} 'T ()' lvalue Function {{.*}} 'g' 'T ()'
+  [[maybe_unused]] int sum = 0;
+  for (auto e : f1(g()))
+    sum += e;
+}
+
+struct LockGuard {
+    LockGuard(int) {}
+
+    ~LockGuard() {}
+};
+
+void f() {
+  int v[] = {42, 17, 13};
+  int M = 0;
+
+  // CHECK: FunctionDecl {{.*}} f 'void ()'
+  // CHECK: `-CXXForRangeStmt {{.*}}
+  // CHECK-NEXT:   |-<<<NULL>>>
+  // CHECK-NEXT:   |-DeclStmt {{.*}}
+  // CHECK-NEXT:   | `-VarDecl {{.*}} col:16 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:   |       |   `-CXXFunctionalCastExpr {{.*}} 'LockGuard':'p2718r0::LockGuard' functional cast to LockGuard <ConstructorConversion>
+  // CHECK-NEXT:   |       |     `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'p2718r0::LockGuard' (CXXTemporary {{.*}})
+  // CHECK-NEXT:   |       |       `-CXXConstructExpr {{.*}} 'LockGuard':'p2718r0::LockGuard' 'void (int)'
+  // CHECK-NEXT:   |       |         `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+  // CHECK-NEXT:   |       |           `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'M' 'int'
+  // CHECK-NEXT:   |       `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+  for (int x : static_cast<void>(LockGuard(M)), v) // lock released in C++ 2020
+  {
+    LockGuard guard(M); // OK in C++ 2020, now deadlocks
+  }
+}
+
+} // namespace p2718r0
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ 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"
@@ -2529,10 +2530,20 @@
   VarDecl *RangeVar = BuildForRangeVarDecl(*this, RangeLoc,
                                            Context.getAutoRRefDeductType(),
                                            std::string("__range") + DepthStr);
-  if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
-                            diag::err_for_range_deduction_failure)) {
-    ActOnInitializerError(LoopVar);
-    return StmtError();
+  {
+    EnterExpressionEvaluationContext RangeVarContext(
+        *this, ExpressionEvaluationContext::PotentiallyEvaluated, /*LambdaContextDecl=*/nullptr,
+        ExpressionEvaluationContextRecord::EK_Other, getLangOpts().CPlusPlus23);
+    if (getLangOpts().CPlusPlus23) {
+      auto &LastRecord = ExprEvalContexts.back();
+      LastRecord.ExtendCXXForRangeInitVariableLifetime = true;
+      Cleanup = LastRecord.ParentCleanup;
+    }
+    if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
+                              diag::err_for_range_deduction_failure)) {
+      ActOnInitializerError(LoopVar);
+      return StmtError();
+    }
   }
 
   // Claim the type doesn't contain auto: we've already done the checking.
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -7357,12 +7357,13 @@
   });
 }
 
-static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
+static void visitLocalsRetainedByInitializer(Sema &S, IndirectLocalPath &Path,
                                              Expr *Init, LocalVisitor Visit,
                                              bool RevisitSubinits,
                                              bool EnableLifetimeWarnings);
 
-static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
+static void visitLocalsRetainedByReferenceBinding(Sema &S,
+                                                  IndirectLocalPath &Path,
                                                   Expr *Init, ReferenceKind RK,
                                                   LocalVisitor Visit,
                                                   bool EnableLifetimeWarnings);
@@ -7448,7 +7449,7 @@
   return false;
 }
 
-static void handleGslAnnotatedTypes(IndirectLocalPath &Path, Expr *Call,
+static void handleGslAnnotatedTypes(Sema &S, IndirectLocalPath &Path, Expr *Call,
                                     LocalVisitor Visit) {
   auto VisitPointerArg = [&](const Decl *D, Expr *Arg, bool Value) {
     // We are not interested in the temporary base objects of gsl Pointers:
@@ -7469,11 +7470,11 @@
                           : IndirectLocalPathEntry::GslReferenceInit,
                     Arg, D});
     if (Arg->isGLValue())
-      visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding,
+      visitLocalsRetainedByReferenceBinding(S, Path, Arg, RK_ReferenceBinding,
                                             Visit,
                                             /*EnableLifetimeWarnings=*/true);
     else
-      visitLocalsRetainedByInitializer(Path, Arg, Visit, true,
+      visitLocalsRetainedByInitializer(S, Path, Arg, Visit, true,
                                        /*EnableLifetimeWarnings=*/true);
     Path.pop_back();
   };
@@ -7544,7 +7545,7 @@
   return false;
 }
 
-static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call,
+static void visitLifetimeBoundArguments(Sema &S, IndirectLocalPath &Path, Expr *Call,
                                         LocalVisitor Visit) {
   const FunctionDecl *Callee;
   ArrayRef<Expr*> Args;
@@ -7571,11 +7572,11 @@
   auto VisitLifetimeBoundArg = [&](const Decl *D, Expr *Arg) {
     Path.push_back({IndirectLocalPathEntry::LifetimeBoundCall, Arg, D});
     if (Arg->isGLValue())
-      visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding,
+      visitLocalsRetainedByReferenceBinding(S, Path, Arg, RK_ReferenceBinding,
                                             Visit,
                                             /*EnableLifetimeWarnings=*/false);
     else
-      visitLocalsRetainedByInitializer(Path, Arg, Visit, true,
+      visitLocalsRetainedByInitializer(S, Path, Arg, Visit, true,
                                        /*EnableLifetimeWarnings=*/false);
     Path.pop_back();
   };
@@ -7586,14 +7587,15 @@
   for (unsigned I = 0,
                 N = std::min<unsigned>(Callee->getNumParams(), Args.size());
        I != N; ++I) {
-    if (Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
+    if (Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>() ||
+        S.isExtendXXForRangeInitVariableLifetimeContext())
       VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]);
   }
 }
 
 /// Visit the locals that would be reachable through a reference bound to the
 /// glvalue expression \c Init.
-static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
+static void visitLocalsRetainedByReferenceBinding(Sema &S, IndirectLocalPath &Path,
                                                   Expr *Init, ReferenceKind RK,
                                                   LocalVisitor Visit,
                                                   bool EnableLifetimeWarnings) {
@@ -7615,8 +7617,16 @@
 
     // Step over any subobject adjustments; we may have a materialized
     // temporary inside them.
-    Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments());
+    SmallVector<const Expr *, 32> CommaLHS;
+    SmallVector<SubobjectAdjustment, 32> Adjustments;
+    Init = const_cast<Expr *>(
+        Init->skipRValueSubobjectAdjustments(CommaLHS, Adjustments));
 
+    llvm::for_each(CommaLHS, [&](const Expr *LHS) {
+      visitLocalsRetainedByReferenceBinding(S, Path, const_cast<Expr *>(LHS),
+                                            RK_ReferenceBinding, Visit,
+                                            EnableLifetimeWarnings);
+    });
     // Per current approach for DR1376, look through casts to reference type
     // when performing lifetime extension.
     if (CastExpr *CE = dyn_cast<CastExpr>(Init))
@@ -7633,7 +7643,7 @@
       else
         // We can't lifetime extend through this but we might still find some
         // retained temporaries.
-        return visitLocalsRetainedByInitializer(Path, Init, Visit, true,
+        return visitLocalsRetainedByInitializer(S, Path, Init, Visit, true,
                                                 EnableLifetimeWarnings);
     }
 
@@ -7648,14 +7658,14 @@
 
   if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) {
     if (Visit(Path, Local(MTE), RK))
-      visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(), Visit, true,
+      visitLocalsRetainedByInitializer(S, Path, MTE->getSubExpr(), Visit, true,
                                        EnableLifetimeWarnings);
   }
 
   if (isa<CallExpr>(Init)) {
     if (EnableLifetimeWarnings)
-      handleGslAnnotatedTypes(Path, Init, Visit);
-    return visitLifetimeBoundArguments(Path, Init, Visit);
+      handleGslAnnotatedTypes(S, Path, Init, Visit);
+    return visitLifetimeBoundArguments(S, Path, Init, Visit);
   }
 
   switch (Init->getStmtClass()) {
@@ -7674,7 +7684,7 @@
         break;
       } else if (VD->getInit() && !isVarOnPath(Path, VD)) {
         Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD});
-        visitLocalsRetainedByReferenceBinding(Path, VD->getInit(),
+        visitLocalsRetainedByReferenceBinding(S, Path, VD->getInit(),
                                               RK_ReferenceBinding, Visit,
                                               EnableLifetimeWarnings);
       }
@@ -7688,13 +7698,13 @@
     // handling all sorts of rvalues passed to a unary operator.
     const UnaryOperator *U = cast<UnaryOperator>(Init);
     if (U->getOpcode() == UO_Deref)
-      visitLocalsRetainedByInitializer(Path, U->getSubExpr(), Visit, true,
+      visitLocalsRetainedByInitializer(S, Path, U->getSubExpr(), Visit, true,
                                        EnableLifetimeWarnings);
     break;
   }
 
   case Stmt::OMPArraySectionExprClass: {
-    visitLocalsRetainedByInitializer(Path,
+    visitLocalsRetainedByInitializer(S, Path,
                                      cast<OMPArraySectionExpr>(Init)->getBase(),
                                      Visit, true, EnableLifetimeWarnings);
     break;
@@ -7704,10 +7714,10 @@
   case Stmt::BinaryConditionalOperatorClass: {
     auto *C = cast<AbstractConditionalOperator>(Init);
     if (!C->getTrueExpr()->getType()->isVoidType())
-      visitLocalsRetainedByReferenceBinding(Path, C->getTrueExpr(), RK, Visit,
+      visitLocalsRetainedByReferenceBinding(S, Path, C->getTrueExpr(), RK, Visit,
                                             EnableLifetimeWarnings);
     if (!C->getFalseExpr()->getType()->isVoidType())
-      visitLocalsRetainedByReferenceBinding(Path, C->getFalseExpr(), RK, Visit,
+      visitLocalsRetainedByReferenceBinding(S, Path, C->getFalseExpr(), RK, Visit,
                                             EnableLifetimeWarnings);
     break;
   }
@@ -7721,7 +7731,7 @@
 
 /// Visit the locals that would be reachable through an object initialized by
 /// the prvalue expression \c Init.
-static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
+static void visitLocalsRetainedByInitializer(Sema &S, IndirectLocalPath &Path,
                                              Expr *Init, LocalVisitor Visit,
                                              bool RevisitSubinits,
                                              bool EnableLifetimeWarnings) {
@@ -7757,19 +7767,19 @@
         // initializer.
         Path.push_back({IndirectLocalPathEntry::LValToRVal, CE});
         return visitLocalsRetainedByReferenceBinding(
-            Path, Init, RK_ReferenceBinding,
+            S, Path, Init, RK_ReferenceBinding,
             [&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool {
           if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {
             auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
             if (VD && VD->getType().isConstQualified() && VD->getInit() &&
                 !isVarOnPath(Path, VD)) {
               Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD});
-              visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit, true,
+              visitLocalsRetainedByInitializer(S, Path, VD->getInit(), Visit, true,
                                                EnableLifetimeWarnings);
             }
           } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) {
             if (MTE->getType().isConstQualified())
-              visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(), Visit,
+              visitLocalsRetainedByInitializer(S, Path, MTE->getSubExpr(), Visit,
                                                true, EnableLifetimeWarnings);
           }
           return false;
@@ -7802,7 +7812,7 @@
         // Model array-to-pointer decay as taking the address of the array
         // lvalue.
         Path.push_back({IndirectLocalPathEntry::AddressOf, CE});
-        return visitLocalsRetainedByReferenceBinding(Path, CE->getSubExpr(),
+        return visitLocalsRetainedByReferenceBinding(S, Path, CE->getSubExpr(),
                                                      RK_ReferenceBinding, Visit,
                                                      EnableLifetimeWarnings);
 
@@ -7818,7 +7828,7 @@
   //   initializing an initializer_list object from the array extends the
   //   lifetime of the array exactly like binding a reference to a temporary.
   if (auto *ILE = dyn_cast<CXXStdInitializerListExpr>(Init))
-    return visitLocalsRetainedByReferenceBinding(Path, ILE->getSubExpr(),
+    return visitLocalsRetainedByReferenceBinding(S, Path, ILE->getSubExpr(),
                                                  RK_StdInitializerList, Visit,
                                                  EnableLifetimeWarnings);
 
@@ -7830,13 +7840,13 @@
       return;
 
     if (ILE->isTransparent())
-      return visitLocalsRetainedByInitializer(Path, ILE->getInit(0), Visit,
+      return visitLocalsRetainedByInitializer(S, Path, ILE->getInit(0), Visit,
                                               RevisitSubinits,
                                               EnableLifetimeWarnings);
 
     if (ILE->getType()->isArrayType()) {
       for (unsigned I = 0, N = ILE->getNumInits(); I != N; ++I)
-        visitLocalsRetainedByInitializer(Path, ILE->getInit(I), Visit,
+        visitLocalsRetainedByInitializer(S, Path, ILE->getInit(I), Visit,
                                          RevisitSubinits,
                                          EnableLifetimeWarnings);
       return;
@@ -7850,13 +7860,13 @@
       // bound to temporaries, those temporaries are also lifetime-extended.
       if (RD->isUnion() && ILE->getInitializedFieldInUnion() &&
           ILE->getInitializedFieldInUnion()->getType()->isReferenceType())
-        visitLocalsRetainedByReferenceBinding(Path, ILE->getInit(0),
+        visitLocalsRetainedByReferenceBinding(S, Path, ILE->getInit(0),
                                               RK_ReferenceBinding, Visit,
                                               EnableLifetimeWarnings);
       else {
         unsigned Index = 0;
         for (; Index < RD->getNumBases() && Index < ILE->getNumInits(); ++Index)
-          visitLocalsRetainedByInitializer(Path, ILE->getInit(Index), Visit,
+          visitLocalsRetainedByInitializer(S, Path, ILE->getInit(Index), Visit,
                                            RevisitSubinits,
                                            EnableLifetimeWarnings);
         for (const auto *I : RD->fields()) {
@@ -7866,14 +7876,14 @@
             continue;
           Expr *SubInit = ILE->getInit(Index);
           if (I->getType()->isReferenceType())
-            visitLocalsRetainedByReferenceBinding(Path, SubInit,
+            visitLocalsRetainedByReferenceBinding(S, Path, SubInit,
                                                   RK_ReferenceBinding, Visit,
                                                   EnableLifetimeWarnings);
           else
             // This might be either aggregate-initialization of a member or
             // initialization of a std::initializer_list object. Regardless,
             // we should recursively lifetime-extend that initializer.
-            visitLocalsRetainedByInitializer(Path, SubInit, Visit,
+            visitLocalsRetainedByInitializer(S, Path, SubInit, Visit,
                                              RevisitSubinits,
                                              EnableLifetimeWarnings);
           ++Index;
@@ -7895,10 +7905,10 @@
       if (Cap.capturesVariable())
         Path.push_back({IndirectLocalPathEntry::LambdaCaptureInit, E, &Cap});
       if (E->isGLValue())
-        visitLocalsRetainedByReferenceBinding(Path, E, RK_ReferenceBinding,
+        visitLocalsRetainedByReferenceBinding(S, Path, E, RK_ReferenceBinding,
                                               Visit, EnableLifetimeWarnings);
       else
-        visitLocalsRetainedByInitializer(Path, E, Visit, true,
+        visitLocalsRetainedByInitializer(S, Path, E, Visit, true,
                                          EnableLifetimeWarnings);
       if (Cap.capturesVariable())
         Path.pop_back();
@@ -7913,7 +7923,7 @@
         Expr *Arg = MTE->getSubExpr();
         Path.push_back({IndirectLocalPathEntry::TemporaryCopy, Arg,
                         CCE->getConstructor()});
-        visitLocalsRetainedByInitializer(Path, Arg, Visit, true,
+        visitLocalsRetainedByInitializer(S, Path, Arg, Visit, true,
                                          /*EnableLifetimeWarnings*/false);
         Path.pop_back();
       }
@@ -7922,8 +7932,8 @@
 
   if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init)) {
     if (EnableLifetimeWarnings)
-      handleGslAnnotatedTypes(Path, Init, Visit);
-    return visitLifetimeBoundArguments(Path, Init, Visit);
+      handleGslAnnotatedTypes(S, Path, Init, Visit);
+    return visitLifetimeBoundArguments(S, Path, Init, Visit);
   }
 
   switch (Init->getStmtClass()) {
@@ -7939,7 +7949,7 @@
         return;
 
       Path.push_back({IndirectLocalPathEntry::AddressOf, UO});
-      visitLocalsRetainedByReferenceBinding(Path, UO->getSubExpr(),
+      visitLocalsRetainedByReferenceBinding(S, Path, UO->getSubExpr(),
                                             RK_ReferenceBinding, Visit,
                                             EnableLifetimeWarnings);
     }
@@ -7954,10 +7964,10 @@
       break;
 
     if (BO->getLHS()->getType()->isPointerType())
-      visitLocalsRetainedByInitializer(Path, BO->getLHS(), Visit, true,
+      visitLocalsRetainedByInitializer(S, Path, BO->getLHS(), Visit, true,
                                        EnableLifetimeWarnings);
     else if (BO->getRHS()->getType()->isPointerType())
-      visitLocalsRetainedByInitializer(Path, BO->getRHS(), Visit, true,
+      visitLocalsRetainedByInitializer(S, Path, BO->getRHS(), Visit, true,
                                        EnableLifetimeWarnings);
     break;
   }
@@ -7968,10 +7978,10 @@
     // In C++, we can have a throw-expression operand, which has 'void' type
     // and isn't interesting from a lifetime perspective.
     if (!C->getTrueExpr()->getType()->isVoidType())
-      visitLocalsRetainedByInitializer(Path, C->getTrueExpr(), Visit, true,
+      visitLocalsRetainedByInitializer(S, Path, C->getTrueExpr(), Visit, true,
                                        EnableLifetimeWarnings);
     if (!C->getFalseExpr()->getType()->isVoidType())
-      visitLocalsRetainedByInitializer(Path, C->getFalseExpr(), Visit, true,
+      visitLocalsRetainedByInitializer(S, Path, C->getFalseExpr(), Visit, true,
                                        EnableLifetimeWarnings);
     break;
   }
@@ -8083,6 +8093,12 @@
 
     auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
 
+    if (MTE && isExtendXXForRangeInitVariableLifetimeContext()) {
+      MTE->setExtendingDecl(ExtendingEntity->getDecl(),
+                            ExtendingEntity->allocateManglingNumber());
+      return true;
+    }
+
     bool IsGslPtrInitWithGslTempOwner = false;
     bool IsLocalGslOwner = false;
     if (pathOnlyInitializesGslPointer(Path)) {
@@ -8330,11 +8346,11 @@
       diag::warn_dangling_lifetime_pointer, SourceLocation());
   llvm::SmallVector<IndirectLocalPathEntry, 8> Path;
   if (Init->isGLValue())
-    visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding,
-                                          TemporaryVisitor,
+    visitLocalsRetainedByReferenceBinding(*this, Path, Init,
+                                          RK_ReferenceBinding, TemporaryVisitor,
                                           EnableLifetimeWarnings);
   else
-    visitLocalsRetainedByInitializer(Path, Init, TemporaryVisitor, false,
+    visitLocalsRetainedByInitializer(*this, Path, Init, TemporaryVisitor, false,
                                      EnableLifetimeWarnings);
 }
 
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -8185,21 +8185,6 @@
     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
@@ -8225,6 +8210,29 @@
     // just clutter.
     // FIXME: We don't emit lifetime markers for the temporaries due to this.
     // FIXME: Do any other AST consumers care about this?
+    if (isMaterializePRValueTemporaryInDiscardStatement() &&
+        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;
   }
 
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ clang/lib/Parse/ParseDecl.cpp
@@ -2239,6 +2239,16 @@
     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);
+      if (getLangOpts().CPlusPlus23) {
+        auto &LastRecord = Actions.ExprEvalContexts.back();
+        LastRecord.MaterializePRValueTemporaryInDiscardStatement = true;
+      }
+
       if (getLangOpts().OpenMP)
         Actions.startOpenMPCXXRangeFor();
       if (Tok.is(tok::l_brace))
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -1353,6 +1353,10 @@
 
     bool IsCurrentlyCheckingDefaultArgumentOrInitializer = false;
 
+    bool ExtendCXXForRangeInitVariableLifetime = false;
+
+    bool MaterializePRValueTemporaryInDiscardStatement = 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
@@ -9835,6 +9839,18 @@
     return ExprEvalContexts.back().isImmediateFunctionContext();
   }
 
+  bool isExtendXXForRangeInitVariableLifetimeContext() const {
+    assert(!ExprEvalContexts.empty() &&
+           "Must be in an expression evaluation context");
+    return ExprEvalContexts.back().ExtendCXXForRangeInitVariableLifetime;
+  }
+
+  bool isMaterializePRValueTemporaryInDiscardStatement() const {
+    assert(!ExprEvalContexts.empty() &&
+           "Must be in an expression evaluation context");
+    return ExprEvalContexts.back().MaterializePRValueTemporaryInDiscardStatement;
+  }
+
   bool isCheckingDefaultArgumentOrInitializer() const {
     assert(!ExprEvalContexts.empty() &&
            "Must be in an expression evaluation context");
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to