https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/170005
None >From 363a78cc719fcd2d96ab3805ed58b08de52cbf82 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <[email protected]> Date: Sat, 29 Nov 2025 15:07:40 +0000 Subject: [PATCH] Implicit lifetimebound for std namespace --- .../LifetimeSafety/LifetimeAnnotations.h | 4 + .../LifetimeSafety/FactsGenerator.cpp | 3 +- .../LifetimeSafety/LifetimeAnnotations.cpp | 82 +++++++++++++++++++ clang/lib/Sema/CheckExprLifetime.cpp | 64 +-------------- 4 files changed, 90 insertions(+), 63 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h index 1a16fb82f9a84..65ff03d48094d 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h @@ -38,6 +38,10 @@ bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD); /// method or because it's a normal assignment operator. bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD); +bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee); + +bool shouldTrackFirstArgument(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]]. diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index d2ef4248cb8c0..52a74bf40d8a0 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -395,7 +395,8 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, Method && Method->isInstance()) { if (I == 0) // For the 'this' argument, the attribute is on the method itself. - return implicitObjectParamIsLifetimeBound(Method); + return implicitObjectParamIsLifetimeBound(Method) || + shouldTrackImplicitObjectArg(Method); if ((I - 1) < Method->getNumParams()) // For explicit arguments, find the corresponding parameter // declaration. diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp index 54e343fc2ee5e..860aa5373a32c 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp @@ -71,6 +71,88 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { return isNormalAssignmentOperator(FD); } +// Decl::isInStdNamespace will return false for iterators in some STL +// implementations due to them being defined in a namespace outside of the std +// namespace. +static bool isInStlNamespace(const Decl *D) { + const DeclContext *DC = D->getDeclContext(); + if (!DC) + return false; + if (const auto *ND = dyn_cast<NamespaceDecl>(DC)) + if (const IdentifierInfo *II = ND->getIdentifier()) { + StringRef Name = II->getName(); + if (Name.size() >= 2 && Name.front() == '_' && + (Name[1] == '_' || isUppercase(Name[1]))) + return true; + } + + return DC->isStdNamespace(); +} + +static bool isPointerLikeType(QualType QT) { + return isGslPointerType(QT) || QT->isPointerType() || QT->isNullPtrType(); +} + +bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) { + if (auto *Conv = dyn_cast_or_null<CXXConversionDecl>(Callee)) + if (isGslPointerType(Conv->getConversionType()) && + Callee->getParent()->hasAttr<OwnerAttr>()) + return true; + if (!isInStlNamespace(Callee->getParent())) + return false; + if (!isGslPointerType(Callee->getFunctionObjectParameterType()) && + !isGslOwnerType(Callee->getFunctionObjectParameterType())) + return false; + if (isPointerLikeType(Callee->getReturnType())) { + if (!Callee->getIdentifier()) + return false; + return llvm::StringSwitch<bool>(Callee->getName()) + .Cases({"begin", "rbegin", "cbegin", "crbegin"}, true) + .Cases({"end", "rend", "cend", "crend"}, true) + .Cases({"c_str", "data", "get"}, true) + // Map and set types. + .Cases({"find", "equal_range", "lower_bound", "upper_bound"}, true) + .Default(false); + } + if (Callee->getReturnType()->isReferenceType()) { + if (!Callee->getIdentifier()) { + auto OO = Callee->getOverloadedOperator(); + if (!Callee->getParent()->hasAttr<OwnerAttr>()) + return false; + return OO == OverloadedOperatorKind::OO_Subscript || + OO == OverloadedOperatorKind::OO_Star; + } + return llvm::StringSwitch<bool>(Callee->getName()) + .Cases({"front", "back", "at", "top", "value"}, true) + .Default(false); + } + return false; +} + +bool shouldTrackFirstArgument(const FunctionDecl *FD) { + if (!FD->getIdentifier() || FD->getNumParams() != 1) + return false; + const auto *RD = FD->getParamDecl(0)->getType()->getPointeeCXXRecordDecl(); + if (!FD->isInStdNamespace() || !RD || !RD->isInStdNamespace()) + return false; + if (!RD->hasAttr<PointerAttr>() && !RD->hasAttr<OwnerAttr>()) + return false; + if (FD->getReturnType()->isPointerType() || + isGslPointerType(FD->getReturnType())) { + return llvm::StringSwitch<bool>(FD->getName()) + .Cases({"begin", "rbegin", "cbegin", "crbegin"}, true) + .Cases({"end", "rend", "cend", "crend"}, true) + .Case("data", true) + .Default(false); + } + if (FD->getReturnType()->isReferenceType()) { + return llvm::StringSwitch<bool>(FD->getName()) + .Cases({"get", "any_cast"}, true) + .Default(false); + } + return false; +} + template <typename T> static bool isRecordWithAttr(QualType Type) { auto *RD = Type->getAsCXXRecordDecl(); if (!RD) diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index c91ca751984c9..26e4d75b1fa49 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -320,66 +320,6 @@ static bool isStdInitializerListOfPointer(const RecordDecl *RD) { return false; } -static bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) { - if (auto *Conv = dyn_cast_or_null<CXXConversionDecl>(Callee)) - if (isGslPointerType(Conv->getConversionType()) && - Callee->getParent()->hasAttr<OwnerAttr>()) - return true; - if (!isInStlNamespace(Callee->getParent())) - return false; - if (!isGslPointerType(Callee->getFunctionObjectParameterType()) && - !isGslOwnerType(Callee->getFunctionObjectParameterType())) - return false; - if (isPointerLikeType(Callee->getReturnType())) { - if (!Callee->getIdentifier()) - return false; - return llvm::StringSwitch<bool>(Callee->getName()) - .Cases({"begin", "rbegin", "cbegin", "crbegin"}, true) - .Cases({"end", "rend", "cend", "crend"}, true) - .Cases({"c_str", "data", "get"}, true) - // Map and set types. - .Cases({"find", "equal_range", "lower_bound", "upper_bound"}, true) - .Default(false); - } - if (Callee->getReturnType()->isReferenceType()) { - if (!Callee->getIdentifier()) { - auto OO = Callee->getOverloadedOperator(); - if (!Callee->getParent()->hasAttr<OwnerAttr>()) - return false; - return OO == OverloadedOperatorKind::OO_Subscript || - OO == OverloadedOperatorKind::OO_Star; - } - return llvm::StringSwitch<bool>(Callee->getName()) - .Cases({"front", "back", "at", "top", "value"}, true) - .Default(false); - } - return false; -} - -static bool shouldTrackFirstArgument(const FunctionDecl *FD) { - if (!FD->getIdentifier() || FD->getNumParams() != 1) - return false; - const auto *RD = FD->getParamDecl(0)->getType()->getPointeeCXXRecordDecl(); - if (!FD->isInStdNamespace() || !RD || !RD->isInStdNamespace()) - return false; - if (!RD->hasAttr<PointerAttr>() && !RD->hasAttr<OwnerAttr>()) - return false; - if (FD->getReturnType()->isPointerType() || - isGslPointerType(FD->getReturnType())) { - return llvm::StringSwitch<bool>(FD->getName()) - .Cases({"begin", "rbegin", "cbegin", "crbegin"}, true) - .Cases({"end", "rend", "cend", "crend"}, true) - .Case("data", true) - .Default(false); - } - if (FD->getReturnType()->isReferenceType()) { - return llvm::StringSwitch<bool>(FD->getName()) - .Cases({"get", "any_cast"}, true) - .Default(false); - } - return false; -} - // Returns true if the given constructor is a copy-like constructor, such as // `Ctor(Owner<U>&&)` or `Ctor(const Owner<U>&)`. static bool isCopyLikeConstructor(const CXXConstructorDecl *Ctor) { @@ -564,7 +504,7 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call, VisitLifetimeBoundArg(Callee, ObjectArg); else if (EnableGSLAnalysis) { if (auto *CME = dyn_cast<CXXMethodDecl>(Callee); - CME && shouldTrackImplicitObjectArg(CME)) + CME && lifetimes::shouldTrackImplicitObjectArg(CME)) VisitGSLPointerArg(Callee, ObjectArg); } } @@ -605,7 +545,7 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call, VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg); else if (EnableGSLAnalysis && I == 0) { // Perform GSL analysis for the first argument - if (shouldTrackFirstArgument(CanonCallee)) { + if (lifetimes::shouldTrackFirstArgument(CanonCallee)) { VisitGSLPointerArg(CanonCallee, Arg); } else if (auto *Ctor = dyn_cast<CXXConstructExpr>(Call); Ctor && shouldTrackFirstArgumentForConstructor(Ctor)) { _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
