//===- LifetimeAnnotations.cpp - -*--------------- C++------------------*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h" #include "clang/AST/ASTContext.h" #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" namespace clang::lifetimes { const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD) { return FD != nullptr ? FD->getMostRecentDecl() : nullptr; } const CXXMethodDecl * getDeclWithMergedLifetimeBoundAttrs(const CXXMethodDecl *CMD) { const FunctionDecl *FD = CMD; return cast_if_present( getDeclWithMergedLifetimeBoundAttrs(FD)); } bool isNormalAssignmentOperator(const FunctionDecl *FD) { OverloadedOperatorKind OO = FD->getDeclName().getCXXOverloadedOperator(); bool IsAssignment = OO == OO_Equal || isCompoundAssignmentOperator(OO); if (!IsAssignment) return false; QualType RetT = FD->getReturnType(); if (!RetT->isLValueReferenceType()) return false; ASTContext &Ctx = FD->getASTContext(); QualType LHST; auto *MD = dyn_cast(FD); if (MD && MD->isCXXInstanceMember()) LHST = Ctx.getLValueReferenceType(MD->getFunctionObjectParameterType()); else LHST = FD->getParamDecl(0)->getType(); return Ctx.hasSameType(RetT, LHST); } bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD) { CMD = getDeclWithMergedLifetimeBoundAttrs(CMD); return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 && CMD->getParamDecl(0)->hasAttr(); } bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { FD = getDeclWithMergedLifetimeBoundAttrs(FD); const TypeSourceInfo *TSI = FD->getTypeSourceInfo(); if (!TSI) return false; // Don't declare this variable in the second operand of the for-statement; // GCC miscompiles that by ending its lifetime before evaluating the // third operand. See gcc.gnu.org/PR86769. AttributedTypeLoc ATL; for (TypeLoc TL = TSI->getTypeLoc(); (ATL = TL.getAsAdjusted()); TL = ATL.getModifiedLoc()) { if (ATL.getAttrAs()) return true; } return isNormalAssignmentOperator(FD); } template 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(); if (auto *CTSD = dyn_cast(RD)) Result |= CTSD->getSpecializedTemplate()->getTemplatedDecl()->hasAttr(); return Result; } bool isGslPointerType(QualType QT) { return isRecordWithAttr(QT); } bool isGslOwnerType(QualType QT) { return isRecordWithAttr(QT); } } // namespace clang::lifetimes