[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-22 Thread Marco Vitale via cfe-commits

mrcvtl wrote:

A couple of quick notes:

- This is my first LLVM PR, so if there are any issues with code style or 
conventions, please let me know!
- I'm not entirely satisfied with the `VD->getName().starts_with("__range")` 
check, but it was the most reliable approach I found. Walking up the AST from 
the node didn’t seem feasible (likely due to optimizations?) and I noticed that 
pattern here: 
https://github.com/llvm/llvm-project/blob/075cb691a5e810f7114369c67b475dfd9127d4af/clang/lib/Sema/SemaStmt.cpp#L2481-L2485

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-22 Thread Marco Vitale via cfe-commits

https://github.com/mrcvtl ready_for_review 
https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-24 Thread Marco Vitale via cfe-commits

mrcvtl wrote:

I’ve taken some time to better understand the code and think through the 
solution.

I tried using `isInLifetimeExtendingContext()`, but it still returns false, and 
I believe I now understand why. In your PR, the flag is set here:
https://github.com/llvm/llvm-project/blob/b581f9d056babadf55098b9d5d100271621b90db/clang/lib/Parse/ParseDecl.cpp#L2314-L2318

However, the warning is actually triggered starting from this location:
https://github.com/llvm/llvm-project/blob/b581f9d056babadf55098b9d5d100271621b90db/clang/lib/Parse/ParseDecl.cpp#L2266

An alternative approach to apply your logic and set again the flag in here:
https://github.com/llvm/llvm-project/blob/43d042b350af8ee8c7401d6b102df68d6c176b5a/clang/lib/Parse/ParseStmt.cpp#L2174-L2179

Maybe we could act on `ForRangeInfo.LifetimeExtendTemps.back()`, or even 
directly on `Actions.currentEvaluationContext();`. If that works, we might be 
able to rely solely on `isInLifetimeExtendingContext()` and remove the need for 
`isRangeBasedForLoopVariable` altogether.

What do you think? I'm absolutely open to every approach (the cleaner, the 
better!).

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-21 Thread Marco Vitale via cfe-commits

mrcvtl wrote:

> Could you associate this PR with the issue to fix, if any?

Done!

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-21 Thread Marco Vitale via cfe-commits

https://github.com/mrcvtl edited 
https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-21 Thread Marco Vitale via cfe-commits

https://github.com/mrcvtl created 
https://github.com/llvm/llvm-project/pull/145164

C++23 mandates that temporaries used in range-based for loops are 
lifetime-extended
to cover the full loop. This patch adds a check for loop variables and compiler-
generated `__range` bindings to apply the correct extension.

Includes test cases based on examples from CWG900/P2644R1.


>From 4398de927292be66f8f54c93c1064b6230f5470a Mon Sep 17 00:00:00 2001
From: Marco Vitale 
Date: Sat, 21 Jun 2025 14:01:53 +0200
Subject: [PATCH] [Sema] Fix lifetime extension for temporaries in range-based
 for loops in C++23

C++23 mandates that temporaries used in range-based for loops are 
lifetime-extended
to cover the full loop. This patch adds a check for loop variables and compiler-
generated `__range` bindings to apply the correct extension.

Includes test cases based on examples from CWG900/P2644R1.
---
 clang/lib/Sema/CheckExprLifetime.cpp  | 28 +++
 .../test/SemaCXX/range-for-lifetime-cxx23.cpp | 79 +++
 2 files changed, 107 insertions(+)
 create mode 100644 clang/test/SemaCXX/range-for-lifetime-cxx23.cpp

diff --git a/clang/lib/Sema/CheckExprLifetime.cpp 
b/clang/lib/Sema/CheckExprLifetime.cpp
index 060ba31660556..0434aa0c29c26 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -57,6 +57,31 @@ enum LifetimeKind {
 };
 using LifetimeResult =
 llvm::PointerIntPair;
+
+/// Returns true if the given entity is part of a range-based for loop and
+/// should trigger lifetime extension under C++23 rules.
+///
+/// This handles both explicit range loop variables and internal compiler-
+/// generated variables like `__range1`.
+static bool
+isRangeBasedForLoopVariable(const Sema &SemaRef,
+const InitializedEntity *ExtendingEntity) {
+  if (!SemaRef.getLangOpts().CPlusPlus23)
+return false;
+
+  const Decl *EntityDecl = ExtendingEntity->getDecl();
+  if (!EntityDecl)
+return false;
+
+  if (const auto *VD = dyn_cast(EntityDecl)) {
+if (VD->isCXXForRangeDecl() || VD->getName().starts_with("__range")) {
+  return true;
+}
+  }
+
+  return false;
+}
+
 } // namespace
 
 /// Determine the declaration which an initialized entity ultimately refers to,
@@ -1341,6 +1366,9 @@ checkExprLifetimeImpl(Sema &SemaRef, const 
InitializedEntity *InitEntity,
   }
 
   if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
+if (isRangeBasedForLoopVariable(SemaRef, ExtendingEntity))
+  return true;
+
 SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
 << DiagRange;
 return false;
diff --git a/clang/test/SemaCXX/range-for-lifetime-cxx23.cpp 
b/clang/test/SemaCXX/range-for-lifetime-cxx23.cpp
new file mode 100644
index 0..bb6e06ec4517c
--- /dev/null
+++ b/clang/test/SemaCXX/range-for-lifetime-cxx23.cpp
@@ -0,0 +1,79 @@
+// RUN: %clangxx -std=c++23 -fsyntax-only -Xclang -verify %s
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static std::vector getVector() {
+  return {"first", "second", "third"};
+}
+
+static std::map> getMap() {
+  return {{"key", {1, 2, 3}}};
+}
+
+static std::tuple> getTuple() {
+  return std::make_tuple(std::vector{3.14, 2.71});
+}
+
+static std::optional> getOptionalColl() {
+  return std::vector{'x', 'y', 'z'};
+}
+
+static std::variant getVariant() {
+  return std::string("variant");
+}
+
+static const std::array& arrOfConst() {
+  static const std::array arr = {10, 20, 30, 40};
+  return arr;
+}
+
+static void testGetVectorSubscript() {
+  for (auto e : getVector()[0]) {
+(void)e;
+  }
+}
+
+static void testGetMapSubscript() {
+  for (auto valueElem : getMap()["key"]) {
+(void)valueElem;
+  }
+}
+
+static void testGetTuple() {
+  for (auto e : std::get<0>(getTuple())) {
+(void)e;
+  }
+}
+
+static void testOptionalValue() {
+  for (auto e : getOptionalColl().value()) {
+(void)e;
+  }
+}
+
+static void testVariantGetString() {
+  for (char c : std::get(getVariant())) {
+(void)c;
+  }
+}
+
+static void testSpanLastFromConstArray() {
+  for (auto s : std::span{arrOfConst()}.last(2)) {
+(void)s;
+  }
+}
+
+static void testSpanFromVectorPtr() {
+  for (auto e : std::span(getVector().data(), 2)) {
+(void)e;
+  }
+}
+
+// expected-no-diagnostics

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-26 Thread Marco Vitale via cfe-commits

mrcvtl wrote:

> IMO, we have 2 options:
> 
> 1. Add a flag variable into `ExpressionEvaluationContextRecord`. Represent 
> that we are initializing the for-range __range variable.
>Eg.
> 
> ```diff
> diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
> index 9397546c8fc5..c639a4b4af58 100644
> --- a/clang/include/clang/Sema/Sema.h
> +++ b/clang/include/clang/Sema/Sema.h
> @@ -6786,6 +6786,9 @@ public:
>  /// Whether we should rebuild CXXDefaultArgExpr and CXXDefaultInitExpr.
>  bool RebuildDefaultArgOrDefaultInit = false;
>  
> +/// Whether we are initializing a C++ for-range implicit variable 
> '__range'.
> +bool InitCXXForRangeVar = 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
> diff --git a/clang/lib/Sema/CheckExprLifetime.cpp 
> b/clang/lib/Sema/CheckExprLifetime.cpp
> index 060ba3166055..150e27f8acf7 100644
> --- a/clang/lib/Sema/CheckExprLifetime.cpp
> +++ b/clang/lib/Sema/CheckExprLifetime.cpp
> @@ -1341,6 +1341,8 @@ checkExprLifetimeImpl(Sema &SemaRef, const 
> InitializedEntity *InitEntity,
>}
>  
>if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
> +if (SemaRef.currentEvaluationContext().InitCXXForRangeVar)
> +  return false;
>  SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
>  << DiagRange;
>  return false;
> diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
> index 923a9e81fbd6..da97d34854ac 100644
> --- a/clang/lib/Sema/SemaStmt.cpp
> +++ b/clang/lib/Sema/SemaStmt.cpp
> @@ -2374,6 +2374,13 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, 
> VarDecl *Decl, Expr *Init,
>SemaRef.ObjC().inferObjCARCLifetime(Decl))
>  Decl->setInvalidDecl();
>  
> +  // EnterExpressionEvaluationContext ForRangeInitContext(
> +  // SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
> +  // /*LambdaContextDecl=*/nullptr,
> +  // Sema::ExpressionEvaluationContextRecord::EK_Other,
> +  // SemaRef.getLangOpts().CPlusPlus23);
> +  if (SemaRef.getLangOpts().CPlusPlus23)
> +SemaRef.currentEvaluationContext().InitCXXForRangeVar = true;
>SemaRef.AddInitializerToDecl(Decl, Init, /*DirectInit=*/false);
>SemaRef.FinalizeDeclaration(Decl);
>SemaRef.CurContext->addHiddenDecl(Decl);
> ```
> 
> 2. Introduce a bitfields into `clang::VarDecl::NonParmVarDeclBitfields`, like 
> `CXXForRangeDecl`(Eg. `CXXForRangeImplicitVar`)
> 
> CC @cor3ntin

I like option one! Maybe a stupid question, but why can’t we use 
`InLifetimeExtendingContext` flag here? It feels like it’s meant to capture the 
same kind of situation. Is there a subtle difference I’m missing? 

I updated (locally) the PR to address the other feedbacks and I will push when 
I have the green light to use option 1! 


https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-28 Thread Marco Vitale via cfe-commits

https://github.com/mrcvtl updated 
https://github.com/llvm/llvm-project/pull/145164

>From b5146278ce5059b6bf0312f18f509022de5fd661 Mon Sep 17 00:00:00 2001
From: Marco Vitale 
Date: Sat, 21 Jun 2025 14:01:53 +0200
Subject: [PATCH 1/2] [Sema] Fix lifetime extension for temporaries in
 range-based for loops in C++23

C++23 mandates that temporaries used in range-based for loops are 
lifetime-extended
to cover the full loop. This patch adds a check for loop variables and compiler-
generated `__range` bindings to apply the correct extension.

Includes test cases based on examples from CWG900/P2644R1.
---
 clang/docs/ReleaseNotes.rst   |  4 +++
 clang/lib/Sema/CheckExprLifetime.cpp  |  5 
 clang/lib/Sema/SemaStmt.cpp   |  3 +++
 .../test/SemaCXX/range-for-lifetime-cxx23.cpp | 27 +++
 4 files changed, 39 insertions(+)
 create mode 100644 clang/test/SemaCXX/range-for-lifetime-cxx23.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d9847fadc21e5..d79df8c9184be 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -648,6 +648,10 @@ Improvements to Clang's diagnostics
   #GH142457, #GH139913, #GH138850, #GH137867, #GH137860, #GH107840, #GH93308,
   #GH69470, #GH59391, #GH58172, #GH46215, #GH45915, #GH45891, #GH44490,
   #GH36703, #GH32903, #GH23312, #GH69874.
+  
+- Clang no longer emits a spurious -Wdangling-gsl warning in C++23 when
+  iterating over an element of a temporary container in a range-based
+  for loop.(#GH109793, #GH145164)
 
 - Clang now avoids issuing `-Wreturn-type` warnings in some cases where
   the final statement of a non-void function is a `throw` expression, or
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp 
b/clang/lib/Sema/CheckExprLifetime.cpp
index 060ba31660556..114e4f989ed9f 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -57,6 +57,7 @@ enum LifetimeKind {
 };
 using LifetimeResult =
 llvm::PointerIntPair;
+
 } // namespace
 
 /// Determine the declaration which an initialized entity ultimately refers to,
@@ -1341,6 +1342,10 @@ checkExprLifetimeImpl(Sema &SemaRef, const 
InitializedEntity *InitEntity,
   }
 
   if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
+if (SemaRef.getLangOpts().CPlusPlus23 &&
+SemaRef.isInLifetimeExtendingContext())
+  return false;
+
 SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
 << DiagRange;
 return false;
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 923a9e81fbd6a..633f73946b729 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2374,6 +2374,9 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl 
*Decl, Expr *Init,
   SemaRef.ObjC().inferObjCARCLifetime(Decl))
 Decl->setInvalidDecl();
 
+  if (SemaRef.getLangOpts().CPlusPlus23)
+SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true;
+
   SemaRef.AddInitializerToDecl(Decl, Init, /*DirectInit=*/false);
   SemaRef.FinalizeDeclaration(Decl);
   SemaRef.CurContext->addHiddenDecl(Decl);
diff --git a/clang/test/SemaCXX/range-for-lifetime-cxx23.cpp 
b/clang/test/SemaCXX/range-for-lifetime-cxx23.cpp
new file mode 100644
index 0..c36fd6c246347
--- /dev/null
+++ b/clang/test/SemaCXX/range-for-lifetime-cxx23.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s
+
+using size_t = decltype(sizeof(void *));
+
+namespace std {
+template  struct vector {
+  T &operator[](size_t I);
+};
+
+struct string {
+  const char *begin();
+  const char *end();
+};
+
+} // namespace std
+
+std::vector getData();
+
+void foo() {
+  // Verifies we don't trigger a diagnostic from -Wdangling-gsl
+  // when iterating over a temporary in C++23.
+  for (auto c : getData()[0]) {
+(void)c;
+  }
+}
+
+// expected-no-diagnostics

>From b2e1333c2b9e61ffb8b85d1613c137f8f96a73e4 Mon Sep 17 00:00:00 2001
From: Marco Vitale 
Date: Sat, 28 Jun 2025 00:25:33 +0200
Subject: [PATCH 2/2] Add as a flag in VarDecl

---
 clang/include/clang/AST/Decl.h| 19 +++
 clang/lib/Sema/CheckExprLifetime.cpp  |  9 ++---
 clang/lib/Sema/SemaStmt.cpp   |  1 +
 clang/lib/Serialization/ASTReaderDecl.cpp |  1 +
 clang/lib/Serialization/ASTWriterDecl.cpp |  2 ++
 5 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index c4202f1f3d07e..eee925b01d9e8 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1086,6 +1086,11 @@ class VarDecl : public DeclaratorDecl, public 
Redeclarable {
 
 LLVM_PREFERRED_TYPE(bool)
 unsigned IsCXXCondDecl : 1;
+
+/// Whether this variable is the implicit __range variable in a for-range
+/// loop.
+LLVM_PREFERRED_TYPE(bool)
+unsigned IsCXXForRangeImplicitVar : 1;
   };
 
   union {
@@ -1585

[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-28 Thread Marco Vitale via cfe-commits

mrcvtl wrote:

> > but why can’t we use InLifetimeExtendingContext flag here?
> > Is there a subtle difference I’m missing?
> 
> This flag will tiger subroutines to collect `MaterializedTemporaryExpr` and 
> rebuild default init/arg。 All we need to know here is that `ExtendingDecl` is 
> a C++ `__range` var in for-range-loop, so I think we should introduce a new 
> flag in VarDecl. We may need to also modify handling of VarDecl in ASTWriter 
> and ASTReader to ensure that the AST serialization is correct. WDYT?

Agreed.

My last commit should implement this. Let me know if something is wrong! 

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-28 Thread Marco Vitale via cfe-commits


@@ -2740,6 +2741,7 @@ void ASTWriter::WriteDeclAbbrevs() {
 // isInline, isInlineSpecified, isConstexpr,
 // isInitCapture, isPrevDeclInSameScope, hasInitWithSideEffects,
 // EscapingByref, HasDeducedType, ImplicitParamKind, isObjCForDecl
+// isCXXForRangeDecl

mrcvtl wrote:

This should be `IsCXXForRangeImplicitVar`

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-28 Thread Marco Vitale via cfe-commits


@@ -1585,6 +1590,20 @@ class VarDecl : public DeclaratorDecl, public 
Redeclarable {
 NonParmVarDeclBits.IsCXXCondDecl = true;
   }
 
+  /// Determine whether this variable is the compiler-generated '__range'
+  /// variable used to hold the range expression in a C++11 and later for-range
+  /// statement.
+  bool isCXXForRangeImplicitVar() const {
+return isa(this) ? false
+  : 
NonParmVarDeclBits.IsCXXForRangeImplicitVar;
+  }
+
+  void setCXXForRangeImplicitVar(bool FRV) {
+assert(!isa(this) &&
+   "Cannot set IsCXXForRangeRangeVar on ParmVarDecl");

mrcvtl wrote:

Also here should be `Cannot set IsCXXForRangeRangeVar...`. 

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-28 Thread Marco Vitale via cfe-commits

https://github.com/mrcvtl deleted 
https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-28 Thread Marco Vitale via cfe-commits

https://github.com/mrcvtl deleted 
https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-28 Thread Marco Vitale via cfe-commits

https://github.com/mrcvtl updated 
https://github.com/llvm/llvm-project/pull/145164

>From 37c57131c397d4aeaef7fe5b860d1983f1e4bdc2 Mon Sep 17 00:00:00 2001
From: Marco Vitale 
Date: Sat, 21 Jun 2025 14:01:53 +0200
Subject: [PATCH] [Sema] Fix lifetime extension for temporaries in range-based
 for loops in C++23

C++23 mandates that temporaries used in range-based for loops are 
lifetime-extended
to cover the full loop. This patch adds a check for loop variables and compiler-
generated `__range` bindings to apply the correct extension.

Includes test cases based on examples from CWG900/P2644R1.
---
 clang/docs/ReleaseNotes.rst   |  4 +++
 clang/include/clang/AST/Decl.h| 19 +
 clang/lib/Sema/CheckExprLifetime.cpp  |  8 ++
 clang/lib/Sema/SemaStmt.cpp   |  4 +++
 clang/lib/Serialization/ASTReaderDecl.cpp |  1 +
 clang/lib/Serialization/ASTWriterDecl.cpp |  2 ++
 .../test/SemaCXX/range-for-lifetime-cxx23.cpp | 27 +++
 7 files changed, 65 insertions(+)
 create mode 100644 clang/test/SemaCXX/range-for-lifetime-cxx23.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 89d86c3371247..184d1f0b188be 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -643,6 +643,10 @@ Improvements to Clang's diagnostics
   #GH142457, #GH139913, #GH138850, #GH137867, #GH137860, #GH107840, #GH93308,
   #GH69470, #GH59391, #GH58172, #GH46215, #GH45915, #GH45891, #GH44490,
   #GH36703, #GH32903, #GH23312, #GH69874.
+  
+- Clang no longer emits a spurious -Wdangling-gsl warning in C++23 when
+  iterating over an element of a temporary container in a range-based
+  for loop.(#GH109793, #GH145164)
 
 
 Improvements to Clang's time-trace
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 0da940883b6f5..ab23346cc2fad 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1085,6 +1085,11 @@ class VarDecl : public DeclaratorDecl, public 
Redeclarable {
 
 LLVM_PREFERRED_TYPE(bool)
 unsigned IsCXXCondDecl : 1;
+
+/// Whether this variable is the implicit __range variable in a for-range
+/// loop.
+LLVM_PREFERRED_TYPE(bool)
+unsigned IsCXXForRangeImplicitVar : 1;
   };
 
   union {
@@ -1584,6 +1589,20 @@ class VarDecl : public DeclaratorDecl, public 
Redeclarable {
 NonParmVarDeclBits.IsCXXCondDecl = true;
   }
 
+  /// Determine whether this variable is the compiler-generated '__range'
+  /// variable used to hold the range expression in a C++11 and later for-range
+  /// statement.
+  bool isCXXForRangeImplicitVar() const {
+return isa(this) ? false
+  : 
NonParmVarDeclBits.IsCXXForRangeImplicitVar;
+  }
+
+  void setCXXForRangeImplicitVar(bool FRV) {
+assert(!isa(this) &&
+   "Cannot set IsCXXForRangeImplicitVar on ParmVarDecl");
+NonParmVarDeclBits.IsCXXForRangeImplicitVar = FRV;
+  }
+
   /// Determines if this variable's alignment is dependent.
   bool hasDependentAlignment() const;
 
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp 
b/clang/lib/Sema/CheckExprLifetime.cpp
index 060ba31660556..fc52de1e6bd89 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -57,6 +57,7 @@ enum LifetimeKind {
 };
 using LifetimeResult =
 llvm::PointerIntPair;
+
 } // namespace
 
 /// Determine the declaration which an initialized entity ultimately refers to,
@@ -1341,6 +1342,13 @@ checkExprLifetimeImpl(Sema &SemaRef, const 
InitializedEntity *InitEntity,
   }
 
   if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
+
+if (SemaRef.getLangOpts().CPlusPlus23) {
+  if (const VarDecl *VD = cast(InitEntity->getDecl());
+  VD && VD->isCXXForRangeImplicitVar())
+return false;
+}
+
 SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
 << DiagRange;
 return false;
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 923a9e81fbd6a..ef0aff1b2838f 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2374,6 +2374,9 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl 
*Decl, Expr *Init,
   SemaRef.ObjC().inferObjCARCLifetime(Decl))
 Decl->setInvalidDecl();
 
+  if (SemaRef.getLangOpts().CPlusPlus23)
+SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true;
+
   SemaRef.AddInitializerToDecl(Decl, Init, /*DirectInit=*/false);
   SemaRef.FinalizeDeclaration(Decl);
   SemaRef.CurContext->addHiddenDecl(Decl);
@@ -2423,6 +2426,7 @@ VarDecl *BuildForRangeVarDecl(Sema &SemaRef, 
SourceLocation Loc,
   VarDecl *Decl = VarDecl::Create(SemaRef.Context, DC, Loc, Loc, II, Type,
   TInfo, SC_None);
   Decl->setImplicit();
+  Decl->setCXXForRangeImplicitVar(true);
   return Decl;
 }
 
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp 

[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-28 Thread Marco Vitale via cfe-commits

https://github.com/mrcvtl updated 
https://github.com/llvm/llvm-project/pull/145164

>From 37c57131c397d4aeaef7fe5b860d1983f1e4bdc2 Mon Sep 17 00:00:00 2001
From: Marco Vitale 
Date: Sat, 21 Jun 2025 14:01:53 +0200
Subject: [PATCH 1/2] [Sema] Fix lifetime extension for temporaries in
 range-based for loops in C++23

C++23 mandates that temporaries used in range-based for loops are 
lifetime-extended
to cover the full loop. This patch adds a check for loop variables and compiler-
generated `__range` bindings to apply the correct extension.

Includes test cases based on examples from CWG900/P2644R1.
---
 clang/docs/ReleaseNotes.rst   |  4 +++
 clang/include/clang/AST/Decl.h| 19 +
 clang/lib/Sema/CheckExprLifetime.cpp  |  8 ++
 clang/lib/Sema/SemaStmt.cpp   |  4 +++
 clang/lib/Serialization/ASTReaderDecl.cpp |  1 +
 clang/lib/Serialization/ASTWriterDecl.cpp |  2 ++
 .../test/SemaCXX/range-for-lifetime-cxx23.cpp | 27 +++
 7 files changed, 65 insertions(+)
 create mode 100644 clang/test/SemaCXX/range-for-lifetime-cxx23.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 89d86c3371247..184d1f0b188be 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -643,6 +643,10 @@ Improvements to Clang's diagnostics
   #GH142457, #GH139913, #GH138850, #GH137867, #GH137860, #GH107840, #GH93308,
   #GH69470, #GH59391, #GH58172, #GH46215, #GH45915, #GH45891, #GH44490,
   #GH36703, #GH32903, #GH23312, #GH69874.
+  
+- Clang no longer emits a spurious -Wdangling-gsl warning in C++23 when
+  iterating over an element of a temporary container in a range-based
+  for loop.(#GH109793, #GH145164)
 
 
 Improvements to Clang's time-trace
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 0da940883b6f5..ab23346cc2fad 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1085,6 +1085,11 @@ class VarDecl : public DeclaratorDecl, public 
Redeclarable {
 
 LLVM_PREFERRED_TYPE(bool)
 unsigned IsCXXCondDecl : 1;
+
+/// Whether this variable is the implicit __range variable in a for-range
+/// loop.
+LLVM_PREFERRED_TYPE(bool)
+unsigned IsCXXForRangeImplicitVar : 1;
   };
 
   union {
@@ -1584,6 +1589,20 @@ class VarDecl : public DeclaratorDecl, public 
Redeclarable {
 NonParmVarDeclBits.IsCXXCondDecl = true;
   }
 
+  /// Determine whether this variable is the compiler-generated '__range'
+  /// variable used to hold the range expression in a C++11 and later for-range
+  /// statement.
+  bool isCXXForRangeImplicitVar() const {
+return isa(this) ? false
+  : 
NonParmVarDeclBits.IsCXXForRangeImplicitVar;
+  }
+
+  void setCXXForRangeImplicitVar(bool FRV) {
+assert(!isa(this) &&
+   "Cannot set IsCXXForRangeImplicitVar on ParmVarDecl");
+NonParmVarDeclBits.IsCXXForRangeImplicitVar = FRV;
+  }
+
   /// Determines if this variable's alignment is dependent.
   bool hasDependentAlignment() const;
 
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp 
b/clang/lib/Sema/CheckExprLifetime.cpp
index 060ba31660556..fc52de1e6bd89 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -57,6 +57,7 @@ enum LifetimeKind {
 };
 using LifetimeResult =
 llvm::PointerIntPair;
+
 } // namespace
 
 /// Determine the declaration which an initialized entity ultimately refers to,
@@ -1341,6 +1342,13 @@ checkExprLifetimeImpl(Sema &SemaRef, const 
InitializedEntity *InitEntity,
   }
 
   if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
+
+if (SemaRef.getLangOpts().CPlusPlus23) {
+  if (const VarDecl *VD = cast(InitEntity->getDecl());
+  VD && VD->isCXXForRangeImplicitVar())
+return false;
+}
+
 SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
 << DiagRange;
 return false;
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 923a9e81fbd6a..ef0aff1b2838f 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2374,6 +2374,9 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl 
*Decl, Expr *Init,
   SemaRef.ObjC().inferObjCARCLifetime(Decl))
 Decl->setInvalidDecl();
 
+  if (SemaRef.getLangOpts().CPlusPlus23)
+SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true;
+
   SemaRef.AddInitializerToDecl(Decl, Init, /*DirectInit=*/false);
   SemaRef.FinalizeDeclaration(Decl);
   SemaRef.CurContext->addHiddenDecl(Decl);
@@ -2423,6 +2426,7 @@ VarDecl *BuildForRangeVarDecl(Sema &SemaRef, 
SourceLocation Loc,
   VarDecl *Decl = VarDecl::Create(SemaRef.Context, DC, Loc, Loc, II, Type,
   TInfo, SC_None);
   Decl->setImplicit();
+  Decl->setCXXForRangeImplicitVar(true);
   return Decl;
 }
 
diff --git a/clang/lib/Serialization/ASTReaderDecl.c

[clang] [Sema] Fix lifetime extension for temporaries in range-based for loops in C++23 (PR #145164)

2025-06-28 Thread Marco Vitale via cfe-commits

mrcvtl wrote:

Old clang tidy test failing seems flaky, locally builds correctly. If it 
continue to fail I can rebase on upstream.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits