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..e5f4b81437b24 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,19 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { NonParmVarDeclBits.IsCXXCondDecl = true; } + /// Whether this variable is the implicit '__range' variable in C++ + /// range-based for loops. + 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..28c52e0fe27d1 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -1341,6 +1341,14 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity, } if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) { + + if (SemaRef.getLangOpts().CPlusPlus23) { + if (const VarDecl *VD = + dyn_cast_if_present(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..f85826aecadf3 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2423,6 +2423,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 2e390dbe79ec6..1d4c3e4e7b88a 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 + // IsCXXForRangeImplicitVar Abv->Add(BitCodeAbbrevOp(0)); // VarKind (local enum) // Type Source Info Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); 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 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