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"
17#include "llvm/ADT/StringSet.h"
18
19namespace clang::lifetimes {
20
21const FunctionDecl *
23 return FD != nullptr ? FD->getMostRecentDecl() : nullptr;
24}
25
26const CXXMethodDecl *
28 const FunctionDecl *FD = CMD;
29 return cast_if_present<CXXMethodDecl>(
31}
32
35 bool IsAssignment = OO == OO_Equal || isCompoundAssignmentOperator(OO);
36 if (!IsAssignment)
37 return false;
38 QualType RetT = FD->getReturnType();
39 if (!RetT->isLValueReferenceType())
40 return false;
41 ASTContext &Ctx = FD->getASTContext();
42 QualType LHST;
43 auto *MD = dyn_cast<CXXMethodDecl>(FD);
44 if (MD && MD->isCXXInstanceMember())
45 LHST = Ctx.getLValueReferenceType(MD->getFunctionObjectParameterType());
46 else
47 LHST = FD->getParamDecl(0)->getType();
48 return Ctx.hasSameType(RetT, LHST);
49}
50
53 return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 &&
54 CMD->getParamDecl(0)->hasAttr<clang::LifetimeBoundAttr>();
55}
56
57/// Check if a function has a lifetimebound attribute on its function type
58/// (which represents the implicit 'this' parameter for methods).
59/// Returns the attribute if found, nullptr otherwise.
60static const LifetimeBoundAttr *
62 // Walk through the type layers looking for a lifetimebound attribute.
63 TypeLoc TL = TSI.getTypeLoc();
64 while (true) {
65 auto ATL = TL.getAsAdjusted<AttributedTypeLoc>();
66 if (!ATL)
67 break;
68 if (auto *LBAttr = ATL.getAttrAs<LifetimeBoundAttr>())
69 return LBAttr;
70 TL = ATL.getModifiedLoc();
71 }
72 return nullptr;
73}
74
75const LifetimeBoundAttr *
78 // Attribute merging doesn't work well with attributes on function types (like
79 // 'this' param). We need to check all redeclarations.
80 auto CheckRedecls = [](const FunctionDecl *F) -> const LifetimeBoundAttr * {
81 for (const FunctionDecl *Redecl : F->redecls())
82 if (const TypeSourceInfo *TSI = Redecl->getTypeSourceInfo())
83 if (const auto *Attr = getLifetimeBoundAttrFromFunctionType(*TSI))
84 return Attr;
85 return nullptr;
86 };
87
88 if (const auto *Attr = CheckRedecls(FD))
89 return Attr;
90 if (const FunctionDecl *Pattern = FD->getTemplateInstantiationPattern())
91 return CheckRedecls(Pattern);
92 return nullptr;
93}
94
100
101bool isInStlNamespace(const Decl *D) {
102 const DeclContext *DC = D->getDeclContext();
103 if (!DC)
104 return false;
105 if (const auto *ND = dyn_cast<NamespaceDecl>(DC))
106 if (const IdentifierInfo *II = ND->getIdentifier()) {
107 StringRef Name = II->getName();
108 if (Name.size() >= 2 && Name.front() == '_' &&
109 (Name[1] == '_' || isUppercase(Name[1])))
110 return true;
111 }
112 return DC->isStdNamespace();
113}
114
116 return isGslPointerType(QT) || QT->isPointerType() || QT->isNullPtrType();
117}
118
120 return QT->isReferenceType() || isPointerLikeType(QT);
121}
122
124 bool RunningUnderLifetimeSafety) {
125 if (!Callee)
126 return false;
127 if (auto *Conv = dyn_cast<CXXConversionDecl>(Callee))
128 if (isGslPointerType(Conv->getConversionType()) &&
129 Callee->getParent()->hasAttr<OwnerAttr>())
130 return true;
131 if (!isGslPointerType(Callee->getFunctionObjectParameterType()) &&
132 !isGslOwnerType(Callee->getFunctionObjectParameterType()))
133 return false;
134
135 // Begin and end iterators.
136 static const llvm::StringSet<> IteratorMembers = {
137 "begin", "end", "rbegin", "rend", "cbegin", "cend", "crbegin", "crend"};
138 static const llvm::StringSet<> InnerPointerGetters = {
139 // Inner pointer getters.
140 "c_str", "data", "get"};
141 static const llvm::StringSet<> ContainerFindFns = {
142 // Map and set types.
143 "find", "equal_range", "lower_bound", "upper_bound"};
144 // Track dereference operator and transparent functions like begin(), get(),
145 // etc. for all GSL pointers. Only do so for lifetime safety analysis and not
146 // for Sema's statement-local analysis as it starts to have false-positives.
147 if (RunningUnderLifetimeSafety &&
148 isGslPointerType(Callee->getFunctionObjectParameterType()) &&
149 isReferenceOrPointerLikeType(Callee->getReturnType())) {
150 // Propagate origins through GSL pointer arithmetic and dereference
151 // operators.
152 switch (Callee->getOverloadedOperator()) {
153 case OO_Arrow:
154 case OO_Star:
155 case OO_Plus:
156 case OO_Minus:
157 case OO_PlusPlus:
158 case OO_MinusMinus:
159 return true;
160 default:
161 break;
162 }
163 if (Callee->getIdentifier() &&
164 (IteratorMembers.contains(Callee->getName()) ||
165 InnerPointerGetters.contains(Callee->getName())))
166 return true;
167 }
168
169 if (!isInStlNamespace(Callee->getParent()))
170 return false;
171
172 if (isPointerLikeType(Callee->getReturnType())) {
173 if (!Callee->getIdentifier())
174 // e.g., std::optional<T>::operator->() returns T*.
175 return RunningUnderLifetimeSafety
176 ? Callee->getParent()->hasAttr<OwnerAttr>() &&
177 Callee->getOverloadedOperator() ==
178 OverloadedOperatorKind::OO_Arrow
179 : false;
180 return IteratorMembers.contains(Callee->getName()) ||
181 InnerPointerGetters.contains(Callee->getName()) ||
182 ContainerFindFns.contains(Callee->getName());
183 }
184 if (Callee->getReturnType()->isReferenceType()) {
185 if (!Callee->getIdentifier()) {
186 auto OO = Callee->getOverloadedOperator();
187 if (!Callee->getParent()->hasAttr<OwnerAttr>())
188 return false;
189 return OO == OverloadedOperatorKind::OO_Subscript ||
190 OO == OverloadedOperatorKind::OO_Star;
191 }
192 return llvm::StringSwitch<bool>(Callee->getName())
193 .Cases({"front", "back", "at", "top", "value"}, true)
194 .Default(false);
195 }
196 return false;
197}
198
200 if (!FD->getIdentifier() || FD->getNumParams() < 1)
201 return false;
202 if (!FD->isInStdNamespace())
203 return false;
204 // Track std:: algorithm functions that return an iterator whose lifetime is
205 // bound to the first argument.
206 if (FD->getNumParams() >= 2 && FD->isInStdNamespace() &&
208 if (llvm::StringSwitch<bool>(FD->getName())
209 .Cases(
210 {
211 "find",
212 "find_if",
213 "find_if_not",
214 "find_first_of",
215 "adjacent_find",
216 "search",
217 "find_end",
218 "lower_bound",
219 "upper_bound",
220 "partition_point",
221 },
222 true)
223 .Default(false))
224 return true;
225 }
226 const auto *RD = FD->getParamDecl(0)->getType()->getPointeeCXXRecordDecl();
227 if (!RD || !RD->isInStdNamespace())
228 return false;
229 if (!RD->hasAttr<PointerAttr>() && !RD->hasAttr<OwnerAttr>())
230 return false;
231
232 if (FD->getNumParams() != 1)
233 return false;
234
235 if (FD->getReturnType()->isPointerType() ||
237 return llvm::StringSwitch<bool>(FD->getName())
238 .Cases({"begin", "rbegin", "cbegin", "crbegin"}, true)
239 .Cases({"end", "rend", "cend", "crend"}, true)
240 .Case("data", true)
241 .Default(false);
242 }
243 if (FD->getReturnType()->isReferenceType()) {
244 return llvm::StringSwitch<bool>(FD->getName())
245 .Cases({"get", "any_cast"}, true)
246 .Default(false);
247 }
248 return false;
249}
250
252 if (FD->getNumParams() < 2)
253 return false;
254 const auto *RD = FD->getParamDecl(1)->getType()->getAsCXXRecordDecl();
255 if (!RD)
256 return false;
257 // For free-standing `+`/`-` operators annotated with `gsl::Pointer`, track
258 // the second parameter when its type matches the return type.
259 return RD->hasAttr<PointerAttr>() &&
260 (FD->getOverloadedOperator() == OO_Plus ||
261 FD->getOverloadedOperator() == OO_Minus) &&
263 FD->getReturnType()) &&
265}
266
267template <typename T> static bool isRecordWithAttr(QualType Type) {
268 auto *RD = Type->getAsCXXRecordDecl();
269 if (!RD)
270 return false;
271 // Generally, if a primary template class declaration is annotated with an
272 // attribute, all its specializations generated from template instantiations
273 // should inherit the attribute.
274 //
275 // However, since lifetime analysis occurs during parsing, we may encounter
276 // cases where a full definition of the specialization is not required. In
277 // such cases, the specialization declaration remains incomplete and lacks the
278 // attribute. Therefore, we fall back to checking the primary template class.
279 //
280 // Note: it is possible for a specialization declaration to have an attribute
281 // even if the primary template does not.
282 //
283 // FIXME: What if the primary template and explicit specialization
284 // declarations have conflicting attributes? We should consider diagnosing
285 // this scenario.
286 bool Result = RD->hasAttr<T>();
287
288 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
289 Result |= CTSD->getSpecializedTemplate()->getTemplatedDecl()->hasAttr<T>();
290
291 return Result;
292}
293
296
297static StringRef getName(const CXXRecordDecl &RD) {
298 if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(&RD))
299 return CTSD->getSpecializedTemplate()->getName();
300 if (RD.getIdentifier())
301 return RD.getName();
302 return "";
303}
304
305static StringRef getName(const FunctionDecl &FD) {
306 if (FD.getIdentifier())
307 return FD.getName();
308 return "";
309}
310
311static bool isStdUniquePtr(const CXXRecordDecl &RD) {
312 return RD.isInStdNamespace() && getName(RD) == "unique_ptr";
313}
314
316 return MD.getIdentifier() && MD.getName() == "release" &&
317 MD.getNumParams() == 0 && isStdUniquePtr(*MD.getParent());
318}
319
321 const CXXRecordDecl *RD = MD.getParent();
322 if (!isInStlNamespace(RD))
323 return false;
324
325 // `pop_back` is excluded: it only invalidates references to the removed
326 // element, not to other elements.
327 static const llvm::StringSet<> Vector = {// Insertion
328 "insert", "emplace", "emplace_back",
329 "push_back", "insert_range",
330 "append_range",
331 // Removal
332 "erase", "clear",
333 // Memory management
334 "reserve", "resize", "shrink_to_fit",
335 // Assignment
336 "assign", "assign_range"};
337
338 // `pop_*` methods are excluded: they only invalidate references to the
339 // removed element, not to other elements.
340 static const llvm::StringSet<> Deque = {// Insertion
341 "insert", "emplace", "insert_range",
342 // Removal
343 "erase", "clear",
344 // Memory management
345 "resize", "shrink_to_fit",
346 // Assignment
347 "assign", "assign_range"};
348
349 static const llvm::StringSet<> String = {
350 // Insertion
351 "insert", "push_back", "append", "replace", "replace_with_range",
352 "insert_range", "append_range",
353 // Removal
354 "pop_back", "erase", "clear",
355 // Memory management
356 "reserve", "resize", "resize_and_overwrite", "shrink_to_fit",
357 // Assignment
358 "swap", "assign", "assign_range"};
359
360 // FIXME: Add queue and stack and check for underlying container
361 // (e.g. no invalidation for std::list).
362 static const llvm::StringSet<> PriorityQueue = {// Insertion
363 "push", "emplace",
364 "push_range",
365 // Removal
366 "pop"};
367
368 // `erase` and `extract` are excluded: they only affect the removed element,
369 // not to other elements.
370 static const llvm::StringSet<> NodeBased = {// Removal
371 "clear"};
372
373 // For `flat_*` container adaptors, `try_emplace` and `insert_or_assign`
374 // only exist on `flat_map`. Listing them here is harmless since the methods
375 // won't be found on other types.
376 static const llvm::StringSet<> Flat = {// Insertion
377 "insert", "emplace", "emplace_hint",
378 "try_emplace", "insert_or_assign",
379 "insert_range", "merge",
380 // Removal
381 "extract", "erase", "clear",
382 // Assignment
383 "replace"};
384
385 static const llvm::StringSet<> UniquePtr = {// Reallocation
386 "reset"};
387
388 const StringRef RecordName = getName(*RD);
389 // TODO: Consider caching this lookup by CXXMethodDecl pointer if this
390 // StringSwitch becomes a performance bottleneck.
391 const llvm::StringSet<> *InvalidatingMethods =
392 llvm::StringSwitch<const llvm::StringSet<> *>(RecordName)
393 .Case("vector", &Vector)
394 .Case("basic_string", &String)
395 .Case("deque", &Deque)
396 .Case("priority_queue", &PriorityQueue)
397 .Cases({"set", "multiset", "map", "multimap", "unordered_set",
398 "unordered_multiset", "unordered_map", "unordered_multimap"},
399 &NodeBased)
400 .Cases({"flat_map", "flat_set", "flat_multimap", "flat_multiset"},
401 &Flat)
402 .Case("unique_ptr", &UniquePtr)
403 .Default(nullptr);
404
405 if (!InvalidatingMethods)
406 return false;
407
408 // Handle Operators via OverloadedOperatorKind
410 if (OO != OO_None) {
411 switch (OO) {
412 case OO_Equal: // operator= : Always invalidates (Assignment)
413 case OO_PlusEqual: // operator+= : Append (String/Vector)
414 return true;
415 case OO_Subscript: // operator[] : Invalidation only for
416 // `flat_map` (Insert-or-access).
417 // `map` and `unordered_map` are excluded.
418 return RecordName == "flat_map";
419 default:
420 return false;
421 }
422 }
423
424 if (!MD.getIdentifier())
425 return false;
426
427 return InvalidatingMethods->contains(MD.getName());
428}
429
432 return true;
433 return isInStlNamespace(&FD) && getName(FD) == "destroy_at";
434}
435
437 if (!RD || !isInStlNamespace(RD))
438 return false;
439 StringRef Name = getName(*RD);
440 return Name == "function" || Name == "move_only_function";
441}
442
443} // 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 an enumeration for C++ overloaded operators.
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:227
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.
static bool hasSameUnqualifiedType(QualType T1, QualType T2)
Determine whether the given types are equivalent after cvr-qualifiers have been removed.
Attr - This represents one attribute.
Definition Attr.h:46
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:2132
const CXXRecordDecl * getParent() const
Return the parent of this method declaration, which is the class in which this method is defined.
Definition DeclCXX.h:2271
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition DeclBase.h:1462
bool isStdNamespace() const
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
bool isInStdNamespace() const
Definition DeclBase.cpp:450
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:547
DeclContext * getDeclContext()
Definition DeclBase.h:456
bool hasAttr() const
Definition DeclBase.h:585
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:2018
const ParmVarDecl * getParamDecl(unsigned i) const
Definition Decl.h:2815
QualType getReturnType() const
Definition Decl.h:2863
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:4259
FunctionDecl * getMostRecentDecl()
Returns the most recent (re)declaration of this declaration.
redecl_range redecls() const
Returns an iterator range for all the redeclarations of the same decl.
OverloadedOperatorKind getOverloadedOperator() const
getOverloadedOperator - Which C++ overloaded operator this function represents, if any.
Definition Decl.cpp:4125
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition Decl.cpp:3821
size_t param_size() const
Definition Decl.h:2808
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:2735
A container of type source information.
Definition TypeBase.h:8407
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:1871
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:8673
bool isReferenceType() const
Definition TypeBase.h:8697
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:1958
bool isLValueReferenceType() const
Definition TypeBase.h:8701
bool isNullPtrType() const
Definition TypeBase.h:9076
QualType getType() const
Definition Decl.h:723
static bool isRecordWithAttr(QualType Type)
bool isGslPointerType(QualType QT)
bool isStdCallableWrapperType(const CXXRecordDecl *RD)
bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee, bool RunningUnderLifetimeSafety)
bool shouldTrackFirstArgument(const FunctionDecl *FD)
static StringRef getName(const CXXRecordDecl &RD)
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)
bool isUniquePtrRelease(const CXXMethodDecl &MD)
static bool isReferenceOrPointerLikeType(QualType QT)
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const LifetimeBoundAttr * getImplicitObjectParamLifetimeBoundAttr(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
bool isInvalidationMethod(const CXXMethodDecl &MD)
bool destructsFirstArg(const FunctionDecl &FD)
bool isGslOwnerType(QualType QT)
bool isInStlNamespace(const Decl *D)
bool shouldTrackSecondArgument(const FunctionDecl *FD)
static bool isStdUniquePtr(const CXXRecordDecl &RD)
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
@ OO_None
Not an overloaded operator.
bool isa(CodeGen::Address addr)
Definition Address.h:330
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
@ Vector
'vector' clause, allowed on 'loop', Combined, and 'routine' directives.
@ Result
The result type of a method or function.
Definition TypeBase.h:905