Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,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 {
Expand Down Expand Up @@ -1584,6 +1589,19 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
NonParmVarDeclBits.IsCXXCondDecl = true;
}

/// Whether this variable is the implicit '__range' variable in C++
/// range-based for loops.
bool isCXXForRangeImplicitVar() const {
return isa<ParmVarDecl>(this) ? false
: NonParmVarDeclBits.IsCXXForRangeImplicitVar;
}

void setCXXForRangeImplicitVar(bool FRV) {
assert(!isa<ParmVarDecl>(this) &&
"Cannot set IsCXXForRangeImplicitVar on ParmVarDecl");
NonParmVarDeclBits.IsCXXForRangeImplicitVar = FRV;
}

/// Determines if this variable's alignment is dependent.
bool hasDependentAlignment() const;

Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Sema/CheckExprLifetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1304,6 +1304,13 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,
if (LK == LK_FullExpression)
return;

if (LK == LK_Extended && SemaRef.getLangOpts().CPlusPlus23) {
if (const auto *VD = dyn_cast_if_present<VarDecl>(InitEntity->getDecl())) {
if (VD->isCXXForRangeImplicitVar())
return;
}
}

// FIXME: consider moving the TemporaryVisitor and visitLocalsRetained*
// functions to a dedicated class.
auto TemporaryVisitor = [&](const IndirectLocalPath &Path, Local L,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand Down
5 changes: 3 additions & 2 deletions clang/test/SemaCXX/attr-lifetimebound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,9 @@ namespace p0936r0_examples {

std::vector make_vector();
void use_reversed_range() {
// FIXME: Don't expose the name of the internal range variable.
for (auto x : reversed(make_vector())) {} // expected-warning {{temporary implicitly bound to local reference will be destroyed at the end of the full-expression}}
// No warning here because C++23 extends the lifetime of the temporary
// in a range-based for loop.
for (auto x : reversed(make_vector())) {}
}

template <typename K, typename V>
Expand Down
27 changes: 27 additions & 0 deletions clang/test/SemaCXX/range-for-lifetime-cxx23.cpp
Original file line number Diff line number Diff line change
@@ -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
Loading