Skip to content

Commit c43ac96

Browse files
authored
[LifetimeSafety] Move GSL pointer/owner type detection to LifetimeAnnotations (#169620)
Refactored GSL pointer and owner type detection functions to improve code organization and reusability.
1 parent f481f5b commit c43ac96

File tree

6 files changed

+57
-67
lines changed

6 files changed

+57
-67
lines changed

clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD);
3838
/// method or because it's a normal assignment operator.
3939
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD);
4040

41+
// Tells whether the type is annotated with [[gsl::Pointer]].
42+
bool isGslPointerType(QualType QT);
43+
// Tells whether the type is annotated with [[gsl::Owner]].
44+
bool isGslOwnerType(QualType QT);
45+
4146
} // namespace clang::lifetimes
4247

4348
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H

clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,6 @@
1515
namespace clang::lifetimes::internal {
1616
using llvm::isa_and_present;
1717

18-
static bool isGslPointerType(QualType QT) {
19-
if (const auto *RD = QT->getAsCXXRecordDecl()) {
20-
// We need to check the template definition for specializations.
21-
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
22-
return CTSD->getSpecializedTemplate()
23-
->getTemplatedDecl()
24-
->hasAttr<PointerAttr>();
25-
return RD->hasAttr<PointerAttr>();
26-
}
27-
return false;
28-
}
29-
3018
static bool isPointerType(QualType QT) {
3119
return QT->isPointerOrReferenceType() || isGslPointerType(QT);
3220
}

clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "clang/AST/Attr.h"
1111
#include "clang/AST/Decl.h"
1212
#include "clang/AST/DeclCXX.h"
13+
#include "clang/AST/DeclTemplate.h"
1314
#include "clang/AST/Type.h"
1415
#include "clang/AST/TypeLoc.h"
1516

@@ -70,4 +71,34 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
7071
return isNormalAssignmentOperator(FD);
7172
}
7273

74+
template <typename T> static bool isRecordWithAttr(QualType Type) {
75+
auto *RD = Type->getAsCXXRecordDecl();
76+
if (!RD)
77+
return false;
78+
// Generally, if a primary template class declaration is annotated with an
79+
// attribute, all its specializations generated from template instantiations
80+
// should inherit the attribute.
81+
//
82+
// However, since lifetime analysis occurs during parsing, we may encounter
83+
// cases where a full definition of the specialization is not required. In
84+
// such cases, the specialization declaration remains incomplete and lacks the
85+
// attribute. Therefore, we fall back to checking the primary template class.
86+
//
87+
// Note: it is possible for a specialization declaration to have an attribute
88+
// even if the primary template does not.
89+
//
90+
// FIXME: What if the primary template and explicit specialization
91+
// declarations have conflicting attributes? We should consider diagnosing
92+
// this scenario.
93+
bool Result = RD->hasAttr<T>();
94+
95+
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
96+
Result |= CTSD->getSpecializedTemplate()->getTemplatedDecl()->hasAttr<T>();
97+
98+
return Result;
99+
}
100+
101+
bool isGslPointerType(QualType QT) { return isRecordWithAttr<PointerAttr>(QT); }
102+
bool isGslOwnerType(QualType QT) { return isRecordWithAttr<OwnerAttr>(QT); }
103+
73104
} // namespace clang::lifetimes

clang/lib/Sema/CheckExprLifetime.cpp

Lines changed: 19 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
#include "llvm/ADT/PointerIntPair.h"
1818

1919
namespace clang::sema {
20+
using lifetimes::isGslOwnerType;
21+
using lifetimes::isGslPointerType;
22+
2023
namespace {
2124
enum LifetimeKind {
2225
/// The lifetime of a temporary bound to this entity ends at the end of the
@@ -257,38 +260,8 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
257260
Expr *Init, ReferenceKind RK,
258261
LocalVisitor Visit);
259262

260-
template <typename T> static bool isRecordWithAttr(QualType Type) {
261-
auto *RD = Type->getAsCXXRecordDecl();
262-
if (!RD)
263-
return false;
264-
// Generally, if a primary template class declaration is annotated with an
265-
// attribute, all its specializations generated from template instantiations
266-
// should inherit the attribute.
267-
//
268-
// However, since lifetime analysis occurs during parsing, we may encounter
269-
// cases where a full definition of the specialization is not required. In
270-
// such cases, the specialization declaration remains incomplete and lacks the
271-
// attribute. Therefore, we fall back to checking the primary template class.
272-
//
273-
// Note: it is possible for a specialization declaration to have an attribute
274-
// even if the primary template does not.
275-
//
276-
// FIXME: What if the primary template and explicit specialization
277-
// declarations have conflicting attributes? We should consider diagnosing
278-
// this scenario.
279-
bool Result = RD->hasAttr<T>();
280-
281-
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
282-
Result |= CTSD->getSpecializedTemplate()->getTemplatedDecl()->hasAttr<T>();
283-
284-
return Result;
285-
}
286-
287-
// Tells whether the type is annotated with [[gsl::Pointer]].
288-
bool isGLSPointerType(QualType QT) { return isRecordWithAttr<PointerAttr>(QT); }
289-
290263
static bool isPointerLikeType(QualType QT) {
291-
return isGLSPointerType(QT) || QT->isPointerType() || QT->isNullPtrType();
264+
return isGslPointerType(QT) || QT->isPointerType() || QT->isNullPtrType();
292265
}
293266

294267
// Decl::isInStdNamespace will return false for iterators in some STL
@@ -331,7 +304,7 @@ static bool isContainerOfOwner(const RecordDecl *Container) {
331304
return false;
332305
const auto &TAs = CTSD->getTemplateArgs();
333306
return TAs.size() > 0 && TAs[0].getKind() == TemplateArgument::Type &&
334-
isRecordWithAttr<OwnerAttr>(TAs[0].getAsType());
307+
isGslOwnerType(TAs[0].getAsType());
335308
}
336309

337310
// Returns true if the given Record is `std::initializer_list<pointer>`.
@@ -349,14 +322,13 @@ static bool isStdInitializerListOfPointer(const RecordDecl *RD) {
349322

350323
static bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) {
351324
if (auto *Conv = dyn_cast_or_null<CXXConversionDecl>(Callee))
352-
if (isRecordWithAttr<PointerAttr>(Conv->getConversionType()) &&
325+
if (isGslPointerType(Conv->getConversionType()) &&
353326
Callee->getParent()->hasAttr<OwnerAttr>())
354327
return true;
355328
if (!isInStlNamespace(Callee->getParent()))
356329
return false;
357-
if (!isRecordWithAttr<PointerAttr>(
358-
Callee->getFunctionObjectParameterType()) &&
359-
!isRecordWithAttr<OwnerAttr>(Callee->getFunctionObjectParameterType()))
330+
if (!isGslPointerType(Callee->getFunctionObjectParameterType()) &&
331+
!isGslOwnerType(Callee->getFunctionObjectParameterType()))
360332
return false;
361333
if (isPointerLikeType(Callee->getReturnType())) {
362334
if (!Callee->getIdentifier())
@@ -393,7 +365,7 @@ static bool shouldTrackFirstArgument(const FunctionDecl *FD) {
393365
if (!RD->hasAttr<PointerAttr>() && !RD->hasAttr<OwnerAttr>())
394366
return false;
395367
if (FD->getReturnType()->isPointerType() ||
396-
isRecordWithAttr<PointerAttr>(FD->getReturnType())) {
368+
isGslPointerType(FD->getReturnType())) {
397369
return llvm::StringSwitch<bool>(FD->getName())
398370
.Cases({"begin", "rbegin", "cbegin", "crbegin"}, true)
399371
.Cases({"end", "rend", "cend", "crend"}, true)
@@ -465,7 +437,7 @@ shouldTrackFirstArgumentForConstructor(const CXXConstructExpr *Ctor) {
465437
return true;
466438

467439
// RHS must be an owner.
468-
if (!isRecordWithAttr<OwnerAttr>(RHSArgType))
440+
if (!isGslOwnerType(RHSArgType))
469441
return false;
470442

471443
// Bail out if the RHS is Owner<Pointer>.
@@ -547,7 +519,7 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
547519
// Once we initialized a value with a non gsl-owner reference, it can no
548520
// longer dangle.
549521
if (ReturnType->isReferenceType() &&
550-
!isRecordWithAttr<OwnerAttr>(ReturnType->getPointeeType())) {
522+
!isGslOwnerType(ReturnType->getPointeeType())) {
551523
for (const IndirectLocalPathEntry &PE : llvm::reverse(Path)) {
552524
if (PE.Kind == IndirectLocalPathEntry::GslReferenceInit ||
553525
PE.Kind == IndirectLocalPathEntry::LifetimeBoundCall)
@@ -1158,8 +1130,7 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
11581130
// auto p2 = Temp().owner; // Here p2 is dangling.
11591131
if (const auto *FD = llvm::dyn_cast_or_null<FieldDecl>(E.D);
11601132
FD && !FD->getType()->isReferenceType() &&
1161-
isRecordWithAttr<OwnerAttr>(FD->getType()) &&
1162-
LK != LK_MemInitializer) {
1133+
isGslOwnerType(FD->getType()) && LK != LK_MemInitializer) {
11631134
return Report;
11641135
}
11651136
return Abandon;
@@ -1191,10 +1162,9 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
11911162
// const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
11921163
// GSLOwner* func(cosnt Foo& foo [[clang::lifetimebound]])
11931164
// GSLPointer func(const Foo& foo [[clang::lifetimebound]])
1194-
if (FD &&
1195-
((FD->getReturnType()->isPointerOrReferenceType() &&
1196-
isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType())) ||
1197-
isGLSPointerType(FD->getReturnType())))
1165+
if (FD && ((FD->getReturnType()->isPointerOrReferenceType() &&
1166+
isGslOwnerType(FD->getReturnType()->getPointeeType())) ||
1167+
isGslPointerType(FD->getReturnType())))
11981168
return Report;
11991169

12001170
return Abandon;
@@ -1206,7 +1176,7 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
12061176
// int &p = *localUniquePtr;
12071177
// someContainer.add(std::move(localUniquePtr));
12081178
// return p;
1209-
if (!pathContainsInit(Path) && isRecordWithAttr<OwnerAttr>(L->getType()))
1179+
if (!pathContainsInit(Path) && isGslOwnerType(L->getType()))
12101180
return Report;
12111181
return Abandon;
12121182
}
@@ -1215,8 +1185,7 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
12151185
auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
12161186

12171187
bool IsGslPtrValueFromGslTempOwner =
1218-
MTE && !MTE->getExtendingDecl() &&
1219-
isRecordWithAttr<OwnerAttr>(MTE->getType());
1188+
MTE && !MTE->getExtendingDecl() && isGslOwnerType(MTE->getType());
12201189
// Skipping a chain of initializing gsl::Pointer annotated objects.
12211190
// We are looking only for the final source to find out if it was
12221191
// a local or temporary owner or the address of a local
@@ -1231,7 +1200,7 @@ static bool shouldRunGSLAssignmentAnalysis(const Sema &SemaRef,
12311200
bool EnableGSLAssignmentWarnings = !SemaRef.getDiagnostics().isIgnored(
12321201
diag::warn_dangling_lifetime_pointer_assignment, SourceLocation());
12331202
return (EnableGSLAssignmentWarnings &&
1234-
(isRecordWithAttr<PointerAttr>(Entity.LHS->getType()) ||
1203+
(isGslPointerType(Entity.LHS->getType()) ||
12351204
lifetimes::isAssignmentOperatorLifetimeBound(
12361205
Entity.AssignmentOperator)));
12371206
}
@@ -1400,7 +1369,7 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,
14001369
// Suppress false positives for code like the one below:
14011370
// Ctor(unique_ptr<T> up) : pointer(up.get()), owner(move(up)) {}
14021371
// FIXME: move this logic to analyzePathForGSLPointer.
1403-
if (DRE && isRecordWithAttr<OwnerAttr>(DRE->getType()))
1372+
if (DRE && isGslOwnerType(DRE->getType()))
14041373
return false;
14051374

14061375
auto *VD = DRE ? dyn_cast<VarDecl>(DRE->getDecl()) : nullptr;

clang/lib/Sema/CheckExprLifetime.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@
1818

1919
namespace clang::sema {
2020

21-
// Tells whether the type is annotated with [[gsl::Pointer]].
22-
bool isGLSPointerType(QualType QT);
23-
2421
/// Describes an entity that is being assigned.
2522
struct AssignedEntity {
2623
// The left-hand side expression of the assignment.

clang/lib/Sema/SemaAttr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
//
1212
//===----------------------------------------------------------------------===//
1313

14-
#include "CheckExprLifetime.h"
1514
#include "clang/AST/ASTConsumer.h"
1615
#include "clang/AST/Attr.h"
1716
#include "clang/AST/DeclCXX.h"
1817
#include "clang/AST/Expr.h"
18+
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
1919
#include "clang/Basic/TargetInfo.h"
2020
#include "clang/Lex/Preprocessor.h"
2121
#include "clang/Sema/Lookup.h"
@@ -289,7 +289,7 @@ void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) {
289289
// We only apply the lifetime_capture_by attribute to parameters of
290290
// pointer-like reference types (`const T&`, `T&&`).
291291
if (PVD->getType()->isReferenceType() &&
292-
sema::isGLSPointerType(PVD->getType().getNonReferenceType())) {
292+
lifetimes::isGslPointerType(PVD->getType().getNonReferenceType())) {
293293
int CaptureByThis[] = {LifetimeCaptureByAttr::This};
294294
PVD->addAttr(
295295
LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1));

0 commit comments

Comments
 (0)