rZhBoYao created this revision.
rZhBoYao added a reviewer: clang-language-wg.
Herald added a project: All.
rZhBoYao requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Implemented the C++23 paper P2718R0


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D139586

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/AST/Decl.h
  clang/lib/Sema/SemaInit.cpp
  clang/lib/Sema/SemaStmt.cpp
  clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
  clang/www/cxx_status.html

Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -1533,7 +1533,7 @@
     <tr>
       <td>Lifetime extension in range-based for loops</td>
       <td><a href="https://wg21.link/P2718R0";>P2718R0</a></td>
-      <td class="none" align="center">No</td>
+      <td class="unreleased" align="center">Clang 16</td>
     </tr>
 </table>
 </details>
Index: clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
===================================================================
--- clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
+++ clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
@@ -1,6 +1,7 @@
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++11 -verify=expected,pre-cxx2b %s
+// RUN: %clang_cc1 -std=c++14 -verify=expected,pre-cxx2b %s
+// RUN: %clang_cc1 -std=c++17 -verify=expected,pre-cxx2b %s
+// RUN: %clang_cc1 -std=c++2b -verify %s
 
 struct pr12960 {
   int begin;
@@ -342,3 +343,19 @@
     for (auto x : f) {} // expected-error {{invalid range expression of type 'NF::F'; no viable 'end' function available}}
   }
 }
+
+namespace p2718r0 {
+struct T {
+  const int *begin() const;
+  const int *end()   const;
+  T &r() [[clang::lifetimebound]];
+};
+
+const T &f1(const T &t [[clang::lifetimebound]]) { return t; }
+T g();
+
+void foo() {
+  for (auto e : f1(g())) {} // pre-cxx2b-warning {{temporary implicitly bound to local reference will be destroyed at the end of the full-expression}}
+  for (auto e : g().r()) {} // pre-cxx2b-warning {{temporary implicitly bound to local reference will be destroyed at the end of the full-expression}}
+}
+}
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -2529,6 +2529,7 @@
   VarDecl *RangeVar = BuildForRangeVarDecl(*this, RangeLoc,
                                            Context.getAutoRRefDeductType(),
                                            std::string("__range") + DepthStr);
+  RangeVar->setCXXRangeVar(true);
   if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
                             diag::err_for_range_deduction_failure)) {
     ActOnInitializerError(LoopVar);
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -6851,6 +6851,7 @@
 /// the initializer are included.
 struct IndirectLocalPathEntry {
   enum EntryKind {
+    CXX2bForRangeInit,
     DefaultInit,
     AddressOf,
     VarInit,
@@ -7188,6 +7189,23 @@
           {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()});
       Init = DIE->getExpr();
     }
+
+    if (auto *CE = dyn_cast<CallExpr>(Init);
+        CE && !Path.empty() &&
+        Path.front().Kind == IndirectLocalPathEntry::CXX2bForRangeInit) {
+      if (EnableLifetimeWarnings)
+        handleGslAnnotatedTypes(Path, Init, Visit);
+      visitLifetimeBoundArguments(Path, Init, Visit);
+      if (auto *ME = dyn_cast<MemberExpr>(CE->getCallee())) {
+        Init = ME->getBase();
+      } else {
+        for (auto *RSE : CE->getRawSubExprs())
+          if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(RSE))
+            if (Visit(Path, Local(MTE), RK))
+              visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(), Visit,
+                                               true, EnableLifetimeWarnings);
+      }
+    }
   } while (Init != Old);
 
   if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) {
@@ -7356,6 +7374,24 @@
 
       Init = CE->getSubExpr();
     }
+
+    if (auto *CE = dyn_cast<CallExpr>(Init);
+        CE && !Path.empty() &&
+        Path.front().Kind == IndirectLocalPathEntry::CXX2bForRangeInit) {
+      if (EnableLifetimeWarnings)
+        handleGslAnnotatedTypes(Path, Init, Visit);
+      visitLifetimeBoundArguments(Path, Init, Visit);
+      if (auto *ME = dyn_cast<MemberExpr>(CE->getCallee())) {
+        visitLocalsRetainedByReferenceBinding(Path, ME->getBase(),
+                                              RK_ReferenceBinding, Visit,
+                                              EnableLifetimeWarnings);
+      } else {
+        for (auto *RSE : CE->getRawSubExprs())
+          if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(RSE))
+            visitLocalsRetainedByReferenceBinding(
+                Path, MTE, RK_ReferenceBinding, Visit, EnableLifetimeWarnings);
+      }
+    }
   } while (Old != Init);
 
   // C++17 [dcl.init.list]p6:
@@ -7558,7 +7594,8 @@
   for (auto Elem : Path) {
     if (Elem.Kind == IndirectLocalPathEntry::DefaultInit)
       Kind = PathLifetimeKind::ShouldExtend;
-    else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit)
+    else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit &&
+             Path.front().Kind != IndirectLocalPathEntry::CXX2bForRangeInit)
       return PathLifetimeKind::NoExtend;
   }
   return Kind;
@@ -7575,6 +7612,7 @@
     case IndirectLocalPathEntry::TemporaryCopy:
     case IndirectLocalPathEntry::GslReferenceInit:
     case IndirectLocalPathEntry::GslPointerInit:
+    case IndirectLocalPathEntry::CXX2bForRangeInit:
       // These exist primarily to mark the path as not permitting or
       // supporting lifetime extension.
       break;
@@ -7824,6 +7862,7 @@
       switch (Elem.Kind) {
       case IndirectLocalPathEntry::AddressOf:
       case IndirectLocalPathEntry::LValToRVal:
+      case IndirectLocalPathEntry::CXX2bForRangeInit:
         // These exist primarily to mark the path as not permitting or
         // supporting lifetime extension.
         break;
@@ -7873,6 +7912,9 @@
   bool EnableLifetimeWarnings = !getDiagnostics().isIgnored(
       diag::warn_dangling_lifetime_pointer, SourceLocation());
   llvm::SmallVector<IndirectLocalPathEntry, 8> Path;
+  if (auto *VD = dyn_cast_or_null<VarDecl>(Entity.getDecl());
+      getLangOpts().CPlusPlus2b && VD && VD->isCXXRangeVar())
+    Path.push_back({IndirectLocalPathEntry::CXX2bForRangeInit, nullptr});
   if (Init->isGLValue())
     visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding,
                                           TemporaryVisitor,
Index: clang/include/clang/AST/Decl.h
===================================================================
--- clang/include/clang/AST/Decl.h
+++ clang/include/clang/AST/Decl.h
@@ -1033,6 +1033,10 @@
     /// (NRVO).
     unsigned NRVOVariable : 1;
 
+    /// Whether this variable is __range{depth} initialized by the
+    /// for-range-initializer in a C++0x for-range statement.
+    unsigned CXXRangeVar : 1;
+
     /// Whether this variable is the for-range-declaration in a C++0x
     /// for-range statement.
     unsigned CXXForRangeDecl : 1;
@@ -1457,6 +1461,16 @@
     NonParmVarDeclBits.NRVOVariable = NRVO;
   }
 
+  /// Determine whether this variable is __range{depth} initialized by the
+  /// for-range-initializer in a C++0x for-range statement.
+  bool isCXXRangeVar() const {
+    return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.CXXRangeVar;
+  }
+  void setCXXRangeVar(bool RV) {
+    assert(!isa<ParmVarDecl>(this));
+    NonParmVarDeclBits.CXXRangeVar = RV;
+  }
+
   /// Determine whether this variable is the for-range-declaration in
   /// a C++0x for-range statement.
   bool isCXXForRangeDecl() const {
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -688,6 +688,7 @@
 - Implemented "char8_t Compatibility and Portability Fix" (`P2513R3 <https://wg21.link/P2513R3>`_).
   This change was applied to C++20 as a Defect Report.
 - Implemented "Permitting static constexpr variables in constexpr functions" (`P2647R1 <https://wg21.link/P2647R1>_`).
+- Implemented "Lifetime extension in range-based for loops" (`P2718R0 <https://wg21.link/P2718R0>_`).
 
 CUDA/HIP Language Changes in Clang
 ----------------------------------
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to