clang 23.0.0git
LifetimeAnnotations.cpp
Go to the documentation of this file.
1//===- LifetimeAnnotations.cpp - -*--------------- C++------------------*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
10#include "clang/AST/Attr.h"
11#include "clang/AST/Decl.h"
12#include "clang/AST/DeclCXX.h"
14#include "clang/AST/Type.h"
15#include "clang/AST/TypeLoc.h"
16#include "llvm/ADT/StringSet.h"
17
18namespace clang::lifetimes {
19
20const FunctionDecl *
22 return FD != nullptr ? FD->getMostRecentDecl() : nullptr;
23}
24
25const CXXMethodDecl *
27 const FunctionDecl *FD = CMD;
28 return cast_if_present<CXXMethodDecl>(
30}
31
34 bool IsAssignment = OO == OO_Equal || isCompoundAssignmentOperator(OO);
35 if (!IsAssignment)
36 return false;
37 QualType RetT = FD->getReturnType();
38 if (!RetT->isLValueReferenceType())
39 return false;
40 ASTContext &Ctx = FD->getASTContext();
41 QualType LHST;
42 auto *MD = dyn_cast<CXXMethodDecl>(FD);
43 if (MD && MD->isCXXInstanceMember())
44 LHST = Ctx.getLValueReferenceType(MD->getFunctionObjectParameterType());
45 else
46 LHST = FD->getParamDecl(0)->getType();
47 return Ctx.hasSameType(RetT, LHST);
48}
49
52 return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 &&
53 CMD->getParamDecl(0)->hasAttr<clang::LifetimeBoundAttr>();
54}
55
56/// Check if a function has a lifetimebound attribute on its function type
57/// (which represents the implicit 'this' parameter for methods).
58/// Returns the attribute if found, nullptr otherwise.
59static const LifetimeBoundAttr *
61 // Walk through the type layers looking for a lifetimebound attribute.
62 TypeLoc TL = TSI.getTypeLoc();
63 while (true) {
64 auto ATL = TL.getAsAdjusted<AttributedTypeLoc>();
65 if (!ATL)
66 break;
67 if (auto *LBAttr = ATL.getAttrAs<LifetimeBoundAttr>())
68 return LBAttr;
69 TL = ATL.getModifiedLoc();
70 }
71 return nullptr;
72}
73
76 // Attribute merging doesn't work well with attributes on function types (like
77 // 'this' param). We need to check all redeclarations.
78 auto CheckRedecls = [](const FunctionDecl *F) {
79 return llvm::any_of(F->redecls(), [](const FunctionDecl *Redecl) {
80 const TypeSourceInfo *TSI = Redecl->getTypeSourceInfo();
81 return TSI && getLifetimeBoundAttrFromFunctionType(*TSI);
82 });
83 };
84
85 if (CheckRedecls(FD))
86 return true;
87 if (const FunctionDecl *Pattern = FD->getTemplateInstantiationPattern();
88 Pattern && CheckRedecls(Pattern))
89 return true;
91}
92
93bool isInStlNamespace(const Decl *D) {
94 const DeclContext *DC = D->getDeclContext();
95 if (!DC)
96 return false;
97 if (const auto *ND = dyn_cast<NamespaceDecl>(DC))
98 if (const IdentifierInfo *II = ND->getIdentifier()) {
99 StringRef Name = II->getName();
100 if (Name.size() >= 2 && Name.front() == '_' &&
101 (Name[1] == '_' || isUppercase(Name[1])))
102 return true;
103 }
104 return DC->isStdNamespace();
105}
106
108 return isGslPointerType(QT) || QT->isPointerType() || QT->isNullPtrType();
109}
110
112 return QT->isReferenceType() || isPointerLikeType(QT);
113}
114
116 bool RunningUnderLifetimeSafety) {
117 if (!Callee)
118 return false;
119 if (auto *Conv = dyn_cast<CXXConversionDecl>(Callee))
120 if (isGslPointerType(Conv->getConversionType()) &&
121 Callee->getParent()->hasAttr<OwnerAttr>())
122 return true;
123 if (!isGslPointerType(Callee->getFunctionObjectParameterType()) &&
124 !isGslOwnerType(Callee->getFunctionObjectParameterType()))
125 return false;
126
127 // Begin and end iterators.
128 static const llvm::StringSet<> IteratorMembers = {
129 "begin", "end", "rbegin", "rend", "cbegin", "cend", "crbegin", "crend"};
130 static const llvm::StringSet<> InnerPointerGetters = {
131 // Inner pointer getters.
132 "c_str", "data", "get"};
133 static const llvm::StringSet<> ContainerFindFns = {
134 // Map and set types.
135 "find", "equal_range", "lower_bound", "upper_bound"};
136 // Track dereference operator and transparent functions like begin(), get(),
137 // etc. for all GSL pointers. Only do so for lifetime safety analysis and not
138 // for Sema's statement-local analysis as it starts to have false-positives.
139 if (RunningUnderLifetimeSafety &&
140 isGslPointerType(Callee->getFunctionObjectParameterType()) &&
141 isReferenceOrPointerLikeType(Callee->getReturnType())) {
142 if (Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Star ||
143 Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Arrow)
144 return true;
145 if (Callee->getIdentifier() &&
146 (IteratorMembers.contains(Callee->getName()) ||
147 InnerPointerGetters.contains(Callee->getName())))
148 return true;
149 }
150
151 if (!isInStlNamespace(Callee->getParent()))
152 return false;
153
154 if (isPointerLikeType(Callee->getReturnType())) {
155 if (!Callee->getIdentifier())
156 return false;
157 return IteratorMembers.contains(Callee->getName()) ||
158 InnerPointerGetters.contains(Callee->getName()) ||
159 ContainerFindFns.contains(Callee->getName());
160 }
161 if (Callee->getReturnType()->isReferenceType()) {
162 if (!Callee->getIdentifier()) {
163 auto OO = Callee->getOverloadedOperator();
164 if (!Callee->getParent()->hasAttr<OwnerAttr>())
165 return false;
166 return OO == OverloadedOperatorKind::OO_Subscript ||
167 OO == OverloadedOperatorKind::OO_Star;
168 }
169 return llvm::StringSwitch<bool>(Callee->getName())
170 .Cases({"front", "back", "at", "top", "value"}, true)
171 .Default(false);
172 }
173 return false;
174}
175
177 if (!FD->getIdentifier() || FD->getNumParams() != 1)
178 return false;
179 const auto *RD = FD->getParamDecl(0)->getType()->getPointeeCXXRecordDecl();
180 if (!FD->isInStdNamespace() || !RD || !RD->isInStdNamespace())
181 return false;
182 if (!RD->hasAttr<PointerAttr>() && !RD->hasAttr<OwnerAttr>())
183 return false;
184 if (FD->getReturnType()->isPointerType() ||
186 return llvm::StringSwitch<bool>(FD->getName())
187 .Cases({"begin", "rbegin", "cbegin", "crbegin"}, true)
188 .Cases({"end", "rend", "cend", "crend"}, true)
189 .Case("data", true)
190 .Default(false);
191 }
192 if (FD->getReturnType()->isReferenceType()) {
193 return llvm::StringSwitch<bool>(FD->getName())
194 .Cases({"get", "any_cast"}, true)
195 .Default(false);
196 }
197 return false;
198}
199
200template <typename T> static bool isRecordWithAttr(QualType Type) {
201 auto *RD = Type->getAsCXXRecordDecl();
202 if (!RD)
203 return false;
204 // Generally, if a primary template class declaration is annotated with an
205 // attribute, all its specializations generated from template instantiations
206 // should inherit the attribute.
207 //
208 // However, since lifetime analysis occurs during parsing, we may encounter
209 // cases where a full definition of the specialization is not required. In
210 // such cases, the specialization declaration remains incomplete and lacks the
211 // attribute. Therefore, we fall back to checking the primary template class.
212 //
213 // Note: it is possible for a specialization declaration to have an attribute
214 // even if the primary template does not.
215 //
216 // FIXME: What if the primary template and explicit specialization
217 // declarations have conflicting attributes? We should consider diagnosing
218 // this scenario.
219 bool Result = RD->hasAttr<T>();
220
221 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
222 Result |= CTSD->getSpecializedTemplate()->getTemplatedDecl()->hasAttr<T>();
223
224 return Result;
225}
226
229
230} // namespace clang::lifetimes
Defines the clang::ASTContext interface.
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the C++ template declaration subclasses.
Defines the clang::TypeLoc interface and its subclasses.
C Language Family Type Representation.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:220
QualType getLValueReferenceType(QualType T, bool SpelledAsLValue=true) const
Return the uniqued reference to the type for an lvalue reference to the specified type.
static bool hasSameType(QualType T1, QualType T2)
Determine whether the given types T1 and T2 are equivalent.
Type source information for an attributed type.
Definition TypeLoc.h:1008
Represents a static or instance method of a struct/union/class.
Definition DeclCXX.h:2129
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition DeclBase.h:1449
bool isStdNamespace() const
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
bool isInStdNamespace() const
Definition DeclBase.cpp:449
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:546
DeclContext * getDeclContext()
Definition DeclBase.h:448
bool hasAttr() const
Definition DeclBase.h:577
OverloadedOperatorKind getCXXOverloadedOperator() const
If this name is the name of an overloadable operator in C++ (e.g., operator+), retrieve the kind of o...
Represents a function declaration or definition.
Definition Decl.h:2000
const ParmVarDecl * getParamDecl(unsigned i) const
Definition Decl.h:2797
QualType getReturnType() const
Definition Decl.h:2845
FunctionDecl * getTemplateInstantiationPattern(bool ForDefinition=true) const
Retrieve the function declaration from which this function could be instantiated, if it is an instant...
Definition Decl.cpp:4264
FunctionDecl * getMostRecentDecl()
Returns the most recent (re)declaration of this declaration.
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition Decl.cpp:3826
size_t param_size() const
Definition Decl.h:2790
One of these records is kept for each identifier that is lexed.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition Decl.h:295
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:301
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition Decl.h:340
A (possibly-)qualified type.
Definition TypeBase.h:937
Base wrapper for a particular "section" of type source info.
Definition TypeLoc.h:59
T getAsAdjusted() const
Convert to the specified TypeLoc type, returning a null TypeLoc if this TypeLoc is not of the desired...
Definition TypeLoc.h:2707
A container of type source information.
Definition TypeBase.h:8273
TypeLoc getTypeLoc() const
Return the TypeLoc wrapper for the type source info.
Definition TypeLoc.h:267
The base class of the type hierarchy.
Definition TypeBase.h:1833
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
bool isPointerType() const
Definition TypeBase.h:8539
bool isReferenceType() const
Definition TypeBase.h:8563
const CXXRecordDecl * getPointeeCXXRecordDecl() const
If this is a pointer or reference to a RecordType, return the CXXRecordDecl that the type refers to.
Definition Type.cpp:1910
bool isLValueReferenceType() const
Definition TypeBase.h:8567
bool isNullPtrType() const
Definition TypeBase.h:8938
QualType getType() const
Definition Decl.h:723
static bool isRecordWithAttr(QualType Type)
bool isGslPointerType(QualType QT)
bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee, bool RunningUnderLifetimeSafety)
bool shouldTrackFirstArgument(const FunctionDecl *FD)
bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD)
static const LifetimeBoundAttr * getLifetimeBoundAttrFromFunctionType(const TypeSourceInfo &TSI)
Check if a function has a lifetimebound attribute on its function type (which represents the implicit...
bool isPointerLikeType(QualType QT)
bool isNormalAssignmentOperator(const FunctionDecl *FD)
static bool isReferenceOrPointerLikeType(QualType QT)
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
bool isGslOwnerType(QualType QT)
bool isInStlNamespace(const Decl *D)
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
bool isCompoundAssignmentOperator(OverloadedOperatorKind Kind)
Determine if this is a compound assignment operator.
LLVM_READONLY bool isUppercase(unsigned char c)
Return true if this character is an uppercase ASCII letter: [A-Z].
Definition CharInfo.h:126
@ Result
The result type of a method or function.
Definition TypeBase.h:905
const FunctionProtoType * T