Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD);
/// method or because it's a normal assignment operator.
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD);

// Tells whether the type is annotated with [[gsl::Pointer]].
bool isGslPointerType(QualType QT);
// Tells whether the type is annotated with [[gsl::Owner]].
bool isGslOwnerType(QualType QT);

} // namespace clang::lifetimes

#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H
12 changes: 0 additions & 12 deletions clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,6 @@
namespace clang::lifetimes::internal {
using llvm::isa_and_present;

static bool isGslPointerType(QualType QT) {
if (const auto *RD = QT->getAsCXXRecordDecl()) {
// We need to check the template definition for specializations.
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
return CTSD->getSpecializedTemplate()
->getTemplatedDecl()
->hasAttr<PointerAttr>();
return RD->hasAttr<PointerAttr>();
}
return false;
}

static bool isPointerType(QualType QT) {
return QT->isPointerOrReferenceType() || isGslPointerType(QT);
}
Expand Down
31 changes: 31 additions & 0 deletions clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"

Expand Down Expand Up @@ -70,4 +71,34 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
return isNormalAssignmentOperator(FD);
}

template <typename T> static bool isRecordWithAttr(QualType Type) {
auto *RD = Type->getAsCXXRecordDecl();
if (!RD)
return false;
// Generally, if a primary template class declaration is annotated with an
// attribute, all its specializations generated from template instantiations
// should inherit the attribute.
//
// However, since lifetime analysis occurs during parsing, we may encounter
// cases where a full definition of the specialization is not required. In
// such cases, the specialization declaration remains incomplete and lacks the
// attribute. Therefore, we fall back to checking the primary template class.
//
// Note: it is possible for a specialization declaration to have an attribute
// even if the primary template does not.
//
// FIXME: What if the primary template and explicit specialization
// declarations have conflicting attributes? We should consider diagnosing
// this scenario.
bool Result = RD->hasAttr<T>();

if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
Result |= CTSD->getSpecializedTemplate()->getTemplatedDecl()->hasAttr<T>();

return Result;
}

bool isGslPointerType(QualType QT) { return isRecordWithAttr<PointerAttr>(QT); }
bool isGslOwnerType(QualType QT) { return isRecordWithAttr<OwnerAttr>(QT); }

} // namespace clang::lifetimes
69 changes: 19 additions & 50 deletions clang/lib/Sema/CheckExprLifetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
#include "llvm/ADT/PointerIntPair.h"

namespace clang::sema {
using lifetimes::isGslOwnerType;
using lifetimes::isGslPointerType;

namespace {
enum LifetimeKind {
/// The lifetime of a temporary bound to this entity ends at the end of the
Expand Down Expand Up @@ -257,38 +260,8 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
Expr *Init, ReferenceKind RK,
LocalVisitor Visit);

template <typename T> static bool isRecordWithAttr(QualType Type) {
auto *RD = Type->getAsCXXRecordDecl();
if (!RD)
return false;
// Generally, if a primary template class declaration is annotated with an
// attribute, all its specializations generated from template instantiations
// should inherit the attribute.
//
// However, since lifetime analysis occurs during parsing, we may encounter
// cases where a full definition of the specialization is not required. In
// such cases, the specialization declaration remains incomplete and lacks the
// attribute. Therefore, we fall back to checking the primary template class.
//
// Note: it is possible for a specialization declaration to have an attribute
// even if the primary template does not.
//
// FIXME: What if the primary template and explicit specialization
// declarations have conflicting attributes? We should consider diagnosing
// this scenario.
bool Result = RD->hasAttr<T>();

if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
Result |= CTSD->getSpecializedTemplate()->getTemplatedDecl()->hasAttr<T>();

return Result;
}

// Tells whether the type is annotated with [[gsl::Pointer]].
bool isGLSPointerType(QualType QT) { return isRecordWithAttr<PointerAttr>(QT); }

static bool isPointerLikeType(QualType QT) {
return isGLSPointerType(QT) || QT->isPointerType() || QT->isNullPtrType();
return isGslPointerType(QT) || QT->isPointerType() || QT->isNullPtrType();
}

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

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

static bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) {
if (auto *Conv = dyn_cast_or_null<CXXConversionDecl>(Callee))
if (isRecordWithAttr<PointerAttr>(Conv->getConversionType()) &&
if (isGslPointerType(Conv->getConversionType()) &&
Callee->getParent()->hasAttr<OwnerAttr>())
return true;
if (!isInStlNamespace(Callee->getParent()))
return false;
if (!isRecordWithAttr<PointerAttr>(
Callee->getFunctionObjectParameterType()) &&
!isRecordWithAttr<OwnerAttr>(Callee->getFunctionObjectParameterType()))
if (!isGslPointerType(Callee->getFunctionObjectParameterType()) &&
!isGslOwnerType(Callee->getFunctionObjectParameterType()))
return false;
if (isPointerLikeType(Callee->getReturnType())) {
if (!Callee->getIdentifier())
Expand Down Expand Up @@ -393,7 +365,7 @@ static bool shouldTrackFirstArgument(const FunctionDecl *FD) {
if (!RD->hasAttr<PointerAttr>() && !RD->hasAttr<OwnerAttr>())
return false;
if (FD->getReturnType()->isPointerType() ||
isRecordWithAttr<PointerAttr>(FD->getReturnType())) {
isGslPointerType(FD->getReturnType())) {
return llvm::StringSwitch<bool>(FD->getName())
.Cases({"begin", "rbegin", "cbegin", "crbegin"}, true)
.Cases({"end", "rend", "cend", "crend"}, true)
Expand Down Expand Up @@ -465,7 +437,7 @@ shouldTrackFirstArgumentForConstructor(const CXXConstructExpr *Ctor) {
return true;

// RHS must be an owner.
if (!isRecordWithAttr<OwnerAttr>(RHSArgType))
if (!isGslOwnerType(RHSArgType))
return false;

// Bail out if the RHS is Owner<Pointer>.
Expand Down Expand Up @@ -547,7 +519,7 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
// Once we initialized a value with a non gsl-owner reference, it can no
// longer dangle.
if (ReturnType->isReferenceType() &&
!isRecordWithAttr<OwnerAttr>(ReturnType->getPointeeType())) {
!isGslOwnerType(ReturnType->getPointeeType())) {
for (const IndirectLocalPathEntry &PE : llvm::reverse(Path)) {
if (PE.Kind == IndirectLocalPathEntry::GslReferenceInit ||
PE.Kind == IndirectLocalPathEntry::LifetimeBoundCall)
Expand Down Expand Up @@ -1158,8 +1130,7 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
// auto p2 = Temp().owner; // Here p2 is dangling.
if (const auto *FD = llvm::dyn_cast_or_null<FieldDecl>(E.D);
FD && !FD->getType()->isReferenceType() &&
isRecordWithAttr<OwnerAttr>(FD->getType()) &&
LK != LK_MemInitializer) {
isGslOwnerType(FD->getType()) && LK != LK_MemInitializer) {
return Report;
}
return Abandon;
Expand Down Expand Up @@ -1191,10 +1162,9 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
// const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
// GSLOwner* func(cosnt Foo& foo [[clang::lifetimebound]])
// GSLPointer func(const Foo& foo [[clang::lifetimebound]])
if (FD &&
((FD->getReturnType()->isPointerOrReferenceType() &&
isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType())) ||
isGLSPointerType(FD->getReturnType())))
if (FD && ((FD->getReturnType()->isPointerOrReferenceType() &&
isGslOwnerType(FD->getReturnType()->getPointeeType())) ||
isGslPointerType(FD->getReturnType())))
return Report;

return Abandon;
Expand All @@ -1206,7 +1176,7 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
// int &p = *localUniquePtr;
// someContainer.add(std::move(localUniquePtr));
// return p;
if (!pathContainsInit(Path) && isRecordWithAttr<OwnerAttr>(L->getType()))
if (!pathContainsInit(Path) && isGslOwnerType(L->getType()))
return Report;
return Abandon;
}
Expand All @@ -1215,8 +1185,7 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);

bool IsGslPtrValueFromGslTempOwner =
MTE && !MTE->getExtendingDecl() &&
isRecordWithAttr<OwnerAttr>(MTE->getType());
MTE && !MTE->getExtendingDecl() && isGslOwnerType(MTE->getType());
// Skipping a chain of initializing gsl::Pointer annotated objects.
// We are looking only for the final source to find out if it was
// a local or temporary owner or the address of a local
Expand All @@ -1231,7 +1200,7 @@ static bool shouldRunGSLAssignmentAnalysis(const Sema &SemaRef,
bool EnableGSLAssignmentWarnings = !SemaRef.getDiagnostics().isIgnored(
diag::warn_dangling_lifetime_pointer_assignment, SourceLocation());
return (EnableGSLAssignmentWarnings &&
(isRecordWithAttr<PointerAttr>(Entity.LHS->getType()) ||
(isGslPointerType(Entity.LHS->getType()) ||
lifetimes::isAssignmentOperatorLifetimeBound(
Entity.AssignmentOperator)));
}
Expand Down Expand Up @@ -1400,7 +1369,7 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,
// Suppress false positives for code like the one below:
// Ctor(unique_ptr<T> up) : pointer(up.get()), owner(move(up)) {}
// FIXME: move this logic to analyzePathForGSLPointer.
if (DRE && isRecordWithAttr<OwnerAttr>(DRE->getType()))
if (DRE && isGslOwnerType(DRE->getType()))
return false;

auto *VD = DRE ? dyn_cast<VarDecl>(DRE->getDecl()) : nullptr;
Expand Down
3 changes: 0 additions & 3 deletions clang/lib/Sema/CheckExprLifetime.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@

namespace clang::sema {

// Tells whether the type is annotated with [[gsl::Pointer]].
bool isGLSPointerType(QualType QT);

/// Describes an entity that is being assigned.
struct AssignedEntity {
// The left-hand side expression of the assignment.
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
//
//===----------------------------------------------------------------------===//

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