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

2025-07-09 Thread via cfe-commits

yronglin wrote:

> LLVM Buildbot has detected a new failure on builder `sanitizer-aarch64-linux` 
> running on `sanitizer-buildbot7` while building `clang` at step 2 "annotate".
> 
> Full details are available at: 
> https://lab.llvm.org/buildbot/#/builders/51/builds/19520
> 
> Here is the relevant piece of the build log for the reference

This failure seem not caused by this PR.


https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-09 Thread LLVM Continuous Integration via cfe-commits

llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `sanitizer-aarch64-linux` 
running on `sanitizer-buildbot7` while building `clang` at step 2 "annotate".

Full details are available at: 
https://lab.llvm.org/buildbot/#/builders/51/builds/19520


Here is the relevant piece of the build log for the reference

```
Step 2 (annotate) failure: 'python 
../sanitizer_buildbot/sanitizers/zorg/buildbot/builders/sanitizers/buildbot_selector.py'
 (failure)
...
llvm-lit: 
/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60:
 warning: Path reported by clang does not exist: 
"/home/b/sanitizer-aarch64-linux/build/compiler_rt_build/lib/aarch64-unknown-linux-gnu".
 This path was found by running 
['/home/b/sanitizer-aarch64-linux/build/build_default/bin/clang', 
'--target=aarch64-unknown-linux-gnu', '-Wthread-safety', 
'-Wthread-safety-reference', '-Wthread-safety-beta', '-nobuiltininc', 
'-I/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/include', 
'-idirafter', 
'/home/b/sanitizer-aarch64-linux/build/build_default/lib/clang/21/include', 
'-resource-dir=/home/b/sanitizer-aarch64-linux/build/compiler_rt_build', 
'-Wl,-rpath,/home/b/sanitizer-aarch64-linux/build/compiler_rt_build/lib/linux', 
'-print-runtime-dir'].
llvm-lit: 
/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60:
 warning: Path reported by clang does not exist: 
"/home/b/sanitizer-aarch64-linux/build/compiler_rt_build/lib/aarch64-unknown-linux-gnu".
 This path was found by running 
['/home/b/sanitizer-aarch64-linux/build/build_default/bin/clang', 
'--target=aarch64-unknown-linux-gnu', '-Wthread-safety', 
'-Wthread-safety-reference', '-Wthread-safety-beta', '-nobuiltininc', 
'-I/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/include', 
'-idirafter', 
'/home/b/sanitizer-aarch64-linux/build/build_default/lib/clang/21/include', 
'-resource-dir=/home/b/sanitizer-aarch64-linux/build/compiler_rt_build', 
'-Wl,-rpath,/home/b/sanitizer-aarch64-linux/build/compiler_rt_build/lib/linux', 
'-print-runtime-dir'].
llvm-lit: 
/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60:
 warning: Path reported by clang does not exist: 
"/home/b/sanitizer-aarch64-linux/build/compiler_rt_build/lib/aarch64-unknown-linux-gnu".
 This path was found by running 
['/home/b/sanitizer-aarch64-linux/build/build_default/bin/clang', 
'--target=aarch64-unknown-linux-gnu', '-Wthread-safety', 
'-Wthread-safety-reference', '-Wthread-safety-beta', '-nobuiltininc', 
'-I/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/include', 
'-idirafter', 
'/home/b/sanitizer-aarch64-linux/build/build_default/lib/clang/21/include', 
'-resource-dir=/home/b/sanitizer-aarch64-linux/build/compiler_rt_build', 
'-Wl,-rpath,/home/b/sanitizer-aarch64-linux/build/compiler_rt_build/lib/linux', 
'-print-runtime-dir'].
llvm-lit: 
/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60:
 warning: Path reported by clang does not exist: 
"/home/b/sanitizer-aarch64-linux/build/compiler_rt_build/lib/aarch64-unknown-linux-gnu".
 This path was found by running 
['/home/b/sanitizer-aarch64-linux/build/build_default/bin/clang', 
'--target=aarch64-unknown-linux-gnu', '-Wthread-safety', 
'-Wthread-safety-reference', '-Wthread-safety-beta', '-nobuiltininc', 
'-I/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/include', 
'-idirafter', 
'/home/b/sanitizer-aarch64-linux/build/build_default/lib/clang/21/include', 
'-resource-dir=/home/b/sanitizer-aarch64-linux/build/compiler_rt_build', 
'-Wl,-rpath,/home/b/sanitizer-aarch64-linux/build/compiler_rt_build/lib/linux', 
'-print-runtime-dir'].
llvm-lit: 
/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60:
 warning: Path reported by clang does not exist: 
"/home/b/sanitizer-aarch64-linux/build/compiler_rt_build/lib/aarch64-unknown-linux-gnu".
 This path was found by running 
['/home/b/sanitizer-aarch64-linux/build/build_default/bin/clang', 
'--target=aarch64-unknown-linux-gnu', '-Wthread-safety', 
'-Wthread-safety-reference', '-Wthread-safety-beta', '-nobuiltininc', 
'-I/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/include', 
'-idirafter', 
'/home/b/sanitizer-aarch64-linux/build/build_default/lib/clang/21/include', 
'-resource-dir=/home/b/sanitizer-aarch64-linux/build/compiler_rt_build', 
'-Wl,-rpath,/home/b/sanitizer-aarch64-linux/build/compiler_rt_build/lib/linux', 
'-print-runtime-dir'].
llvm-lit: 
/home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/lit.common.cfg.py:60:
 warning: Path reported by clang does not exist: 
"/home/b/sanitizer-aarch64-linux/build/compiler_rt_build/lib/aarch64-unknown-linux-gnu".
 This path was found by running 
['/home/b/sanitizer-aarch64-linux/build/build_default/bin/clang', 
'--target=aarch64-unknown-linux-gnu', '-Wthread-safety', 
'-Wthread-safety-reference', '-Wthread-safety-be

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

2025-07-09 Thread via cfe-commits

yronglin wrote:

Congratulations, the first LLVM PR has landed!

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-09 Thread via cfe-commits

github-actions[bot] wrote:



@mrcvtl Congratulations on having your first Pull Request (PR) merged into the 
LLVM Project!

Your changes will be combined with recent changes from other authors, then 
tested by our [build bots](https://lab.llvm.org/buildbot/). If there is a 
problem with a build, you may receive a report in an email or a comment on this 
PR.

Please check whether problems have been caused by your change specifically, as 
the builds can include changes from many authors. It is not uncommon for your 
change to be included in a build that fails due to someone else's changes, or 
infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail 
[here](https://llvm.org/docs/MyFirstTypoFix.html#myfirsttypofix-issues-after-landing-your-pr).

If your change does cause a problem, it may be reverted, or you can revert it 
yourself. This is a normal part of [LLVM 
development](https://llvm.org/docs/DeveloperPolicy.html#patch-reversion-policy).
 You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are 
working as expected, well done!


https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-09 Thread via cfe-commits

https://github.com/yronglin closed 
https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-09 Thread Marco Vitale via cfe-commits

mrcvtl wrote:

> Do you need the help to merge this PR?

Yes please, also not sure if I can merge being my first PR.
Also should we wait on the third review? Not sure about the reviews policies!

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-09 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/4] [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-07-08 Thread via cfe-commits

yronglin wrote:

Do you need the help to merge this PR?

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-07 Thread via cfe-commits

https://github.com/yronglin approved this pull request.

LGTM

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-07 Thread Haojian Wu via cfe-commits

https://github.com/hokein approved this pull request.

This change looks good to me.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-07 Thread Haojian Wu via cfe-commits

hokein wrote:

> I have debug the issue, seems we cannot move the __range variable check to 
> the start of checkExprLifetimeImpl, we may missing extending lifetime of 
> temporaries.

Ah, that makes sense -- I missed the fact that this part of the code is also 
responsible for extending the object’s lifetime. Thanks a lot for looking into 
this and clarifying it.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-06 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/4] [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-07-06 Thread via cfe-commits

yronglin wrote:

> Forgot that the revert wouldn't include the `lifetimebound` attribute. I 
> restructured the switch case like that, in order to catch both:
> 
> ```
> case LK_Extended: {
>   if (!MTE) {
> // The initialized entity has lifetime beyond the full-expression,
> // and the local entity does too, so don't warn.
> //
> // FIXME: We should consider warning if a static / thread storage
> // duration variable retains an automatic storage duration local.
> return false;
>   }
>   
>   switch (shouldLifetimeExtendThroughPath(Path)) {
>   case PathLifetimeKind::Extend:
> assert(!IsGslPtrValueFromGslTempOwner); // Just a test, to be sure 
> that we aren't losing something here.
> // can be removed safely I 
> guess.
>
> // Update the storage duration of the materialized temporary.
> // FIXME: Rebuild the expression instead of mutating it.
> MTE->setExtendingDecl(ExtendingEntity->getDecl(),
>   ExtendingEntity->allocateManglingNumber());
> // Also visit the temporaries lifetime-extended by this initializer.
> return true;
>   
> case PathLifetimeKind::NoExtend:
>   if (SemaRef.getLangOpts().CPlusPlus23 && InitEntity) {
> if (const VarDecl *VD =
> dyn_cast_if_present(InitEntity->getDecl());
> VD && VD->isCXXForRangeImplicitVar())
>   return false;
>   }
>   
>   
>   if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
> SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
> << DiagRange;
> return false;
>   }
> 
> // If the path goes through the initialization of a variable or field,
> // it can't possibly reach a temporary created in this 
> full-expression.
> // We will have already diagnosed any problems with the initializer.
> if (pathContainsInit(Path))
>   return false;
> 
> SemaRef.Diag(DiagLoc, diag::warn_dangling_variable)
> << RK << !InitEntity->getParent()
> << ExtendingEntity->getDecl()->isImplicit()
> << ExtendingEntity->getDecl() << Init->isGLValue() << DiagRange;
> break;
>   }
>   break;
> }
> ```

Could you push the changes to this PR?

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-06 Thread Marco Vitale via cfe-commits

mrcvtl wrote:

Forgot that the revert wouldn't include the `lifetimebound` attribute. I 
restructured the switch case like that, in order to catch both:
```
case LK_Extended: {
  if (!MTE) {
// The initialized entity has lifetime beyond the full-expression,
// and the local entity does too, so don't warn.
//
// FIXME: We should consider warning if a static / thread storage
// duration variable retains an automatic storage duration local.
return false;
  }
  
  switch (shouldLifetimeExtendThroughPath(Path)) {
  case PathLifetimeKind::Extend:
assert(!IsGslPtrValueFromGslTempOwner); // Just a test, to be sure that 
we aren't losing something here.
// can be removed safely I 
guess.
   
// Update the storage duration of the materialized temporary.
// FIXME: Rebuild the expression instead of mutating it.
MTE->setExtendingDecl(ExtendingEntity->getDecl(),
  ExtendingEntity->allocateManglingNumber());
// Also visit the temporaries lifetime-extended by this initializer.
return true;
  
case PathLifetimeKind::NoExtend:
  if (SemaRef.getLangOpts().CPlusPlus23 && InitEntity) {
if (const VarDecl *VD =
dyn_cast_if_present(InitEntity->getDecl());
VD && VD->isCXXForRangeImplicitVar())
  return false;
  }
  
  
  if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
<< DiagRange;
return false;
  }

// If the path goes through the initialization of a variable or field,
// it can't possibly reach a temporary created in this full-expression.
// We will have already diagnosed any problems with the initializer.
if (pathContainsInit(Path))
  return false;

SemaRef.Diag(DiagLoc, diag::warn_dangling_variable)
<< RK << !InitEntity->getParent()
<< ExtendingEntity->getDecl()->isImplicit()
<< ExtendingEntity->getDecl() << Init->isGLValue() << DiagRange;
break;
  }
  break;
}
```

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-05 Thread Marco Vitale via cfe-commits

mrcvtl wrote:

> I think we should revert to the previous approach, what do you think?
> 
> ```c++
>   if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
> if (const auto *VD =
> dyn_cast_if_present(ExtendingEntity->getDecl());
> SemaRef.getLangOpts().CPlusPlus23 && VD &&
> VD->isCXXForRangeImplicitVar())
>   return true;
> SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
> << DiagRange;
> return false;
>   }
> ```

Thanks for debugging that!
I agree btw, didn't think about this side effect before.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-05 Thread Marco Vitale via cfe-commits

mrcvtl wrote:

> I have debug the issue, seems we cannot move the `__range` variable check to 
> the start of `checkExprLifetimeImpl`, we may missing extending lifetime of 
> temporaries. Eg.
> 
> ```c++
> template 
> struct ListWrapper {
>   ListWrapper() {}
>   ~ListWrapper() {}
>   const T *begin() const;
>   const T *end() const;
>   ListWrapper& r();
>   ListWrapper g();
> };
> 
> using A = ListWrapper;
> 
> A g() { return A(); }
> const A &f1(const A &t) { return t; }
> 
> void member_call() {
>   // CHECK-CXX23: void @_ZN7P2718R011member_call11member_callEv()
>   // CHECK-CXX23-LABEL: for.cond.cleanup:
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   for (auto e : g().r().g().r().g().r().g()) {}
> }
> ```
> 
> I think we should revert to the previous approach, what do you think?
> 
> ```c++
>   if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
> if (const auto *VD =
> dyn_cast_if_present(ExtendingEntity->getDecl());
> SemaRef.getLangOpts().CPlusPlus23 && VD &&
> VD->isCXXForRangeImplicitVar())
>   return true;
> SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
> << DiagRange;
> return false;
>   }
> ```

Agree, there are some side effect that I can't fully understand.

> I have debug the issue, seems we cannot move the `__range` variable check to 
> the start of `checkExprLifetimeImpl`, we may missing extending lifetime of 
> temporaries. Eg.
> 
> ```c++
> template 
> struct ListWrapper {
>   ListWrapper() {}
>   ~ListWrapper() {}
>   const T *begin() const;
>   const T *end() const;
>   ListWrapper& r();
>   ListWrapper g();
> };
> 
> using A = ListWrapper;
> 
> A g() { return A(); }
> const A &f1(const A &t) { return t; }
> 
> void member_call() {
>   // CHECK-CXX23: void @_ZN7P2718R011member_call11member_callEv()
>   // CHECK-CXX23-LABEL: for.cond.cleanup:
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   for (auto e : g().r().g().r().g().r().g()) {}
> }
> ```
> 
> I think we should revert to the previous approach, what do you think?
> 
> ```c++
>   if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
> if (const auto *VD =
> dyn_cast_if_present(ExtendingEntity->getDecl());
> SemaRef.getLangOpts().CPlusPlus23 && VD &&
> VD->isCXXForRangeImplicitVar())
>   return true;
> SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
> << DiagRange;
> return false;
>   }
> ```

I agree. There are so

> I have debug the issue, seems we cannot move the `__range` variable check to 
> the start of `checkExprLifetimeImpl`, we may missing extending lifetime of 
> temporaries. Eg.
> 
> ```c++
> template 
> struct ListWrapper {
>   ListWrapper() {}
>   ~ListWrapper() {}
>   const T *begin() const;
>   const T *end() const;
>   ListWrapper& r();
>   ListWrapper g();
> };
> 
> using A = ListWrapper;
> 
> A g() { return A(); }
> const A &f1(const A &t) { return t; }
> 
> void member_call() {
>   // CHECK-CXX23: void @_ZN7P2718R011member_call11member_callEv()
>   // CHECK-CXX23-LABEL: for.cond.cleanup:
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   // CHECK-CXX23-NEXT: call void 
> @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
>   for (auto e : g().r().g().r().g().r().g()) {}
> }
> ```
> 
> I think we should revert to the previous approach, what do you think?
> 
> ```c++
>   if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
> if (const auto *VD =
> dyn_cast_if_present(ExtendingEntity->getDecl());
> SemaRef.getLangOpts().CPlusPlus23 && VD &&
> VD->isCXXForRangeImplicitVar())
>   return true;
> SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
> << DiagRange;
> return false;
>   }
> ```

Thanks for debugging that! 
I agree btw, didn't think about this side effect before.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-04 Thread via cfe-commits

yronglin wrote:

I have debug the issue, seems we cannot move the `__range` variable check to 
the start of `checkExprLifetimeImpl`, we may missing extending lifetime of 
temporaries. 
Eg.
```cpp
template 
struct ListWrapper {
  ListWrapper() {}
  ~ListWrapper() {}
  const T *begin() const;
  const T *end() const;
  ListWrapper& r();
  ListWrapper g();
};

using A = ListWrapper;

A g() { return A(); }
const A &f1(const A &t) { return t; }

void member_call() {
  // CHECK-CXX23: void @_ZN7P2718R011member_call11member_callEv()
  // CHECK-CXX23-LABEL: for.cond.cleanup:
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
  for (auto e : g().r().g().r().g().r().g()) {}
}
```
I think we should revert to the previous approach, what do you think?
```cpp
  if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
if (const auto *VD =
dyn_cast_if_present(ExtendingEntity->getDecl());
SemaRef.getLangOpts().CPlusPlus23 && VD &&
VD->isCXXForRangeImplicitVar())
  return true;
SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)
<< DiagRange;
return false;
  }
```

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-04 Thread Haojian Wu via cfe-commits

hokein wrote:


> Yes, indeed. For example, in `AST/ast-dump-for-range-lifetime.cpp` we move 
> from: `-MaterializeTemporaryExpr {{.*}} 'C':'P2718R0::C' xvalue extended by 
> Var {{.*}} '__range1' 'C &&'` to: `-MaterializeTemporaryExpr {{.*}} 
> 'C':'P2718R0::C' xvalue`. At a first look it seems like we are losing info in 
> the AST, but also a couple of line later we got: `-MaterializeTemporaryExpr 
> {{.*}} 'const C':'const P2718R0::C' lvalue extended by Var {{.*}} '__range1' 
> 'C &&'` So it might be fine...
> 
> In `special/class.temporary/p6.cpp` instead is a bit more weird, because it 
> seems we are losing some IR call in case of for-range loops. You can take a 
> look in the logs of the failing test.
> 
> Given that I don't know enough about it to say if it is okay or not, I wanted 
> to double check with you. What do you think?

Thanks for looking at them.

It seems this patch is causing some unexpected behavior changes. From what I 
can tell, this patch only adds a new bit to Decl, which shouldn’t affect the 
two failing tests. However, I don't spot any obvious cause by reading the 
patch. Maybe @yronglin has some insights here.


https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-03 Thread Marco Vitale via cfe-commits

mrcvtl wrote:

> There are some test failures in the presubmit checks, you probably need to 
> update these tests.

Yes, indeed. For example, in `AST/ast-dump-for-range-lifetime.cpp` we move from:
 `-MaterializeTemporaryExpr {{.*}} 'C':'P2718R0::C' xvalue extended by Var 
{{.*}} '__range1' 'C &&'` 
 to: 
`-MaterializeTemporaryExpr  {{.*}} 'C':'P2718R0::C' xvalue`. 
At a first look it seems like we are losing info in the AST, but also a couple 
of line later we got:
`-MaterializeTemporaryExpr {{.*}} 'const C':'const P2718R0::C' lvalue extended 
by Var {{.*}} '__range1' 'C &&'`
So it might be fine...

In `special/class.temporary/p6.cpp` instead is a bit more weird, because it 
seems we are losing some IR call in case of for-range loops. You can take a 
look in the logs of the failing test.

Given that I don't know enough about it to say if it is okay or not, I wanted 
to double check with you. What do you think? 


https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-03 Thread Marco Vitale via cfe-commits


@@ -1304,6 +1304,13 @@ checkExprLifetimeImpl(Sema &SemaRef, const 
InitializedEntity *InitEntity,
   if (LK == LK_FullExpression)
 return;
 
+  if (LK == LK_Extended && SemaRef.getLangOpts().CPlusPlus23) {

mrcvtl wrote:

Yes, we can. We just need to check also for `InitEntity != nullptr` and it 
works I guess.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-03 Thread Haojian Wu via cfe-commits

https://github.com/hokein commented:

There are some test failures in the presubmit checks, you probably need to 
update these tests.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-03 Thread Haojian Wu via cfe-commits


@@ -1304,6 +1304,13 @@ checkExprLifetimeImpl(Sema &SemaRef, const 
InitializedEntity *InitEntity,
   if (LK == LK_FullExpression)
 return;
 
+  if (LK == LK_Extended && SemaRef.getLangOpts().CPlusPlus23) {

hokein wrote:

Is the `LK == LK_Extended` check necessary here? I think we can simply bail out 
in this case, regardless of the LK value.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-01 Thread Marco Vitale via cfe-commits


@@ -1304,6 +1304,13 @@ checkExprLifetimeImpl(Sema &SemaRef, const 
InitializedEntity *InitEntity,
   if (LK == LK_FullExpression)
 return;
 
+  if (LK == LK_Extended && SemaRef.getLangOpts().CPlusPlus23) {

mrcvtl wrote:

both this and the previous if condition can also be moved in the switch case 
after the temporary visitor. No strong opinion here.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-07-01 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/3] [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-30 Thread via cfe-commits


@@ -1341,6 +1341,14 @@ checkExprLifetimeImpl(Sema &SemaRef, const 
InitializedEntity *InitEntity,
   }
 
   if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
+
+if (SemaRef.getLangOpts().CPlusPlus23) {

yronglin wrote:

Only update this test seems make sense.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-30 Thread Haojian Wu via cfe-commits


@@ -1341,6 +1341,14 @@ checkExprLifetimeImpl(Sema &SemaRef, const 
InitializedEntity *InitEntity,
   }
 
   if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {
+
+if (SemaRef.getLangOpts().CPlusPlus23) {

hokein wrote:

the analysis is done here, and we filter out the result. I think we can filter 
out this case before (in `checkExprLifetimeImpl`) running the analysis to save 
some cost. 

Can you add a testcase (for the `lifetimebound` attr)? Make sure we don't warn 
on it

```
using size_t = decltype(sizeof(void *));

namespace my_ns {
template  struct vector {
  T &operator[](size_t I) [[clang::lifetimebound]];
};

struct string {
  const char *begin();
  const char *end();
};

} // namespace std

my_ns::vector getData();

void foo() {

  for (auto c : getData()[0]) {
(void)c;
  }
}
```

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-29 Thread via cfe-commits

https://github.com/yronglin edited 
https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-29 Thread via cfe-commits

https://github.com/yronglin approved this pull request.

Thanks for this fix! LGTM

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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

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
[email protected]
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 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 via cfe-commits


@@ -2374,6 +2374,9 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl 
*Decl, Expr *Init,
   SemaRef.ObjC().inferObjCARCLifetime(Decl))
 Decl->setInvalidDecl();
 
+  if (SemaRef.getLangOpts().CPlusPlus23)

yronglin wrote:

I think we don't need this change. We already has a flag in `VarDecl`.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 via cfe-commits


@@ -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());

yronglin wrote:

```suggestion
  if (const VarDecl *VD = 
dyn_cast_if_present(InitEntity->getDecl());
```

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 via cfe-commits


@@ -57,6 +57,7 @@ enum LifetimeKind {
 };
 using LifetimeResult =
 llvm::PointerIntPair;
+

yronglin wrote:

Nit: Let's revert blank modification.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 via cfe-commits


@@ -1584,6 +1589,20 @@ class VarDecl : public DeclaratorDecl, public 
Redeclarable {
 NonParmVarDeclBits.IsCXXCondDecl = true;
   }
 
+  /// Determine whether this variable is the compiler-generated '__range'

yronglin wrote:

```suggestion
  /// Whether this variable is the implicit '__range' variable in C++ 
range-based for loops.
```

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 deleted 
https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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
[email protected]
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
[email protected]
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
[email protected]
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

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
[email protected]
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-27 Thread via cfe-commits

yronglin 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?

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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
[email protected]
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-25 Thread Shafik Yaghmour via cfe-commits

https://github.com/shafik commented:

As a previous comment noted, this needs a release note but I think the summary 
should also mention that this fixes the warning diagnostic from 
`-Wdangling-gsl`. Maybe worth a comment in the test explaining these are 
verifying we don't trigger a diagnostic from `-Wdangling-gsl`

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-25 Thread via cfe-commits

yronglin 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 would be 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!).

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 

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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-25 Thread via cfe-commits


@@ -0,0 +1,79 @@
+// RUN: %clangxx -std=c++23 -fsyntax-only -Xclang -verify %s
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 

yronglin wrote:

I have a more simple reproducer:
```cpp
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

extern "C" int printf(const char *, ...);

std::vector getData();

void foo() {
  for (auto c : getData()[0]) {
printf("%c\n", c);
  }
}

```

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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
[email protected]
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 via cfe-commits

yronglin 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

Thanks for you fix!
Maybe we can use `isInLifetimeExtendingContext()` instead of check the 
`__range` variable name. We usually  get into an `LifetimeExtendingContex` in 
Sema. FYI, the initial PR is https://github.com/llvm/llvm-project/pull/76361.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 via cfe-commits


@@ -0,0 +1,79 @@
+// RUN: %clangxx -std=c++23 -fsyntax-only -Xclang -verify %s
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 

yronglin wrote:

We can construct some simple fake std dependencies.
Eg.
```cpp
namespace std {
  typedef decltype(sizeof(int)) size_t;
  template 
  struct initializer_list {
const E *begin;
size_t   size;
initializer_list() : begin(nullptr), size(0) {}
  };

  template 
  struct list {
list() {}
~list() {}
E *begin();
E *end();
const E *begin() const;
const E *end() const;
  };

  template 
  struct vector {
vector() {}
vector(std::initializer_list) {}
~vector() {}
E *begin();
E *end();
const E *begin() const;
const E *end() const;
  };
} // namespace std
```

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 Yanzuo Liu via cfe-commits


@@ -0,0 +1,79 @@
+// RUN: %clangxx -std=c++23 -fsyntax-only -Xclang -verify %s

zwuis wrote:

Use `%clang_cc1` for frontend tests.

`-Xclang` can be removed.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 Yanzuo Liu via cfe-commits

https://github.com/zwuis edited https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 Yanzuo Liu via cfe-commits


@@ -0,0 +1,79 @@
+// RUN: %clangxx -std=c++23 -fsyntax-only -Xclang -verify %s
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 

zwuis wrote:

Do not include files outside test folders. You can search `namespace std` to 
see how other test files handle this case.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 Yanzuo Liu via cfe-commits


@@ -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,

zwuis wrote:

This parameter type can be `const LangOptions &`.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 Yanzuo Liu via cfe-commits

https://github.com/zwuis commented:

Thank you for the patch!

Please add a release note in `clang/docs/ReleaseNotes.rst` so that users can 
know the improvement.

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 via cfe-commits

llvmbot wrote:




@llvm/pr-subscribers-clang

Author: Marco Vitale (mrcvtl)


Changes

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.

Fixes https://github.com/llvm/llvm-project/issues/109793

---
Full diff: https://github.com/llvm/llvm-project/pull/145164.diff


2 Files Affected:

- (modified) clang/lib/Sema/CheckExprLifetime.cpp (+28) 
- (added) clang/test/SemaCXX/range-for-lifetime-cxx23.cpp (+79) 


``diff
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

``




https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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
[email protected]
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

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
[email protected]
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
[email protected]
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
[email protected]
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 A. Jiang via cfe-commits

frederick-vs-ja wrote:

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

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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 via cfe-commits

github-actions[bot] wrote:



Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this 
page.

If this is not working for you, it is probably because you do not have write 
permissions for the repository. In which case you can instead tag reviewers by 
name in a comment by using `@` followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a 
review by "ping"ing the PR by adding a comment “Ping”. The common courtesy 
"ping" rate is once a week. Please remember that you are asking for valuable 
time from other developers.

If you have further questions, they may be answered by the [LLVM GitHub User 
Guide](https://llvm.org/docs/GitHub.html).

You can also ask questions in a comment on this PR, on the [LLVM 
Discord](https://discord.com/invite/xS7Z362) or on the 
[forums](https://discourse.llvm.org/).

https://github.com/llvm/llvm-project/pull/145164
___
cfe-commits mailing list
[email protected]
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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits