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 <mar.vit...@icloud.com> 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<const InitializedEntity *, 3, LifetimeKind>; + } // 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 0000000000000..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 <typename T> struct vector { + T &operator[](size_t I); +}; + +struct string { + const char *begin(); + const char *end(); +}; + +} // namespace std + +std::vector<std::string> 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 <mar.vit...@icloud.com> 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<VarDecl> { 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,6 +1590,20 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { 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<ParmVarDecl>(this) ? false + : NonParmVarDeclBits.IsCXXForRangeImplicitVar; + } + + void setCXXForRangeImplicitVar(bool FRV) { + assert(!isa<ParmVarDecl>(this) && + "Cannot set IsCXXForRangeRangeVar 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 114e4f989ed9f..fc52de1e6bd89 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -1342,9 +1342,12 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity, } if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) { - if (SemaRef.getLangOpts().CPlusPlus23 && - SemaRef.isInLifetimeExtendingContext()) - return false; + + if (SemaRef.getLangOpts().CPlusPlus23) { + if (const VarDecl *VD = cast<VarDecl>(InitEntity->getDecl()); + VD && VD->isCXXForRangeImplicitVar()) + return false; + } SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer) << DiagRange; diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 633f73946b729..ef0aff1b2838f 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2426,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 b/clang/lib/Serialization/ASTReaderDecl.cpp index 0ffd78424be0d..e6a8a0a243401 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1637,6 +1637,7 @@ RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) { VarDeclBits.getNextBits(/*Width*/ 3); VD->NonParmVarDeclBits.ObjCForDecl = VarDeclBits.getNextBit(); + VD->NonParmVarDeclBits.IsCXXForRangeImplicitVar = VarDeclBits.getNextBit(); } // If this variable has a deduced type, defer reading that type until we are diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 7457c4920dd10..8064ccae1a64e 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1319,6 +1319,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { VarDeclBits.addBits(0, /*Width=*/3); VarDeclBits.addBit(D->isObjCForDecl()); + VarDeclBits.addBit(D->isCXXForRangeImplicitVar()); } Record.push_back(VarDeclBits); @@ -2740,6 +2741,7 @@ void ASTWriter::WriteDeclAbbrevs() { // isInline, isInlineSpecified, isConstexpr, // isInitCapture, isPrevDeclInSameScope, hasInitWithSideEffects, // EscapingByref, HasDeducedType, ImplicitParamKind, isObjCForDecl + // isCXXForRangeDecl Abv->Add(BitCodeAbbrevOp(0)); // VarKind (local enum) // Type Source Info Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits