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 for (const DeclContext *DC = D->getDeclContext(); DC; DC = DC->getParent()) {
103 if (DC->isStdNamespace())
104 return true;
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 }
113 return false;
114}
115
117 return isGslPointerType(QT) || QT->isPointerType() || QT->isNullPtrType();
118}
119
121 return QT->isReferenceType() || isPointerLikeType(QT);
122}
123
125 bool RunningUnderLifetimeSafety) {
126 if (!Callee)
127 return false;
128 if (auto *Conv = dyn_cast<CXXConversionDecl>(Callee))
129 if (isGslPointerType(Conv->getConversionType()) &&
130 Callee->getParent()->hasAttr<OwnerAttr>())
131 return true;
132 if (!isGslPointerType(Callee->getFunctionObjectParameterType()) &&
133 !isGslOwnerType(Callee->getFunctionObjectParameterType()))
134 return false;
135
136 // Begin and end iterators.
137 static const llvm::StringSet<> IteratorMembers = {
138 "begin", "end", "rbegin", "rend", "cbegin", "cend", "crbegin", "crend"};
139 static const llvm::StringSet<> InnerPointerGetters = {
140 // Inner pointer getters.
141 "c_str", "data", "get"};
142 static const llvm::StringSet<> ContainerFindFns = {
143 // Map and set types.
144 "find", "equal_range", "lower_bound", "upper_bound"};
145 // Track dereference operator and transparent functions like begin(), get(),
146 // etc. for all GSL pointers. Only do so for lifetime safety analysis and not
147 // for Sema's statement-local analysis as it starts to have false-positives.
148 if (RunningUnderLifetimeSafety &&
149 isGslPointerType(Callee->getFunctionObjectParameterType()) &&
150 isReferenceOrPointerLikeType(Callee->getReturnType())) {
151 // Propagate origins through GSL pointer arithmetic and dereference
152 // operators.
153 switch (Callee->getOverloadedOperator()) {
154 case OO_Arrow:
155 case OO_Star:
156 case OO_Plus:
157 case OO_Minus:
158 case OO_PlusPlus:
159 case OO_MinusMinus:
160 return true;
161 default:
162 break;
163 }
164 if (Callee->getIdentifier() &&
165 (IteratorMembers.contains(Callee->getName()) ||
166 InnerPointerGetters.contains(Callee->getName())))
167 return true;
168 }
169
170 if (!isInStlNamespace(Callee->getParent()))
171 return false;
172
173 if (isPointerLikeType(Callee->getReturnType())) {
174 if (!Callee->getIdentifier())
175 // e.g., std::optional<T>::operator->() returns T*.
176 return RunningUnderLifetimeSafety
177 ? Callee->getParent()->hasAttr<OwnerAttr>() &&
178 Callee->getOverloadedOperator() ==
179 OverloadedOperatorKind::OO_Arrow
180 : false;
181 return IteratorMembers.contains(Callee->getName()) ||
182 InnerPointerGetters.contains(Callee->getName()) ||
183 ContainerFindFns.contains(Callee->getName());
184 }
185 if (Callee->getReturnType()->isReferenceType()) {
186 if (!Callee->getIdentifier()) {
187 auto OO = Callee->getOverloadedOperator();
188 if (!Callee->getParent()->hasAttr<OwnerAttr>())
189 return false;
190 return OO == OverloadedOperatorKind::OO_Subscript ||
191 OO == OverloadedOperatorKind::OO_Star;
192 }
193 return llvm::StringSwitch<bool>(Callee->getName())
194 .Cases({"front", "back", "at", "top", "value"}, true)
195 .Default(false);
196 }
197 return false;
198}
199
201 if (!FD->getIdentifier() || FD->getNumParams() < 1)
202 return false;
203 if (!FD->isInStdNamespace())
204 return false;
205 // Track std:: algorithm functions that return an iterator whose lifetime is
206 // bound to the first argument.
207 if (FD->getNumParams() >= 2 && FD->isInStdNamespace() &&
209 if (llvm::StringSwitch<bool>(FD->getName())
210 .Cases(
211 {
212 "find",
213 "find_if",
214 "find_if_not",
215 "find_first_of",
216 "adjacent_find",
217 "search",
218 "find_end",
219 "lower_bound",
220 "upper_bound",
221 "partition_point",
222 },
223 true)
224 .Default(false))
225 return true;
226 }
227 const auto *RD = FD->getParamDecl(0)->getType()->getPointeeCXXRecordDecl();
228 if (!RD || !RD->isInStdNamespace())
229 return false;
230 if (!RD->hasAttr<PointerAttr>() && !RD->hasAttr<OwnerAttr>())
231 return false;
232
233 if (FD->getNumParams() != 1)
234 return false;
235
236 if (FD->getReturnType()->isPointerType() ||
238 return llvm::StringSwitch<bool>(FD->getName())
239 .Cases({"begin", "rbegin", "cbegin", "crbegin"}, true)
240 .Cases({"end", "rend", "cend", "crend"}, true)
241 .Case("data", true)
242 .Default(false);
243 }
244 if (FD->getReturnType()->isReferenceType()) {
245 return llvm::StringSwitch<bool>(FD->getName())
246 .Cases({"get", "any_cast"}, true)
247 .Default(false);
248 }
249 return false;
250}
251
253 if (FD->getNumParams() < 2)
254 return false;
255 const auto *RD = FD->getParamDecl(1)->getType()->getAsCXXRecordDecl();
256 if (!RD)
257 return false;
258 // For free-standing `+`/`-` operators annotated with `gsl::Pointer`, track
259 // the second parameter when its type matches the return type.
260 return RD->hasAttr<PointerAttr>() &&
261 (FD->getOverloadedOperator() == OO_Plus ||
262 FD->getOverloadedOperator() == OO_Minus) &&
264 FD->getReturnType()) &&
266}
267
268template <typename T> static bool isRecordWithAttr(QualType Type) {
269 auto *RD = Type->getAsCXXRecordDecl();
270 if (!RD)
271 return false;
272 // Generally, if a primary template class declaration is annotated with an
273 // attribute, all its specializations generated from template instantiations
274 // should inherit the attribute.
275 //
276 // However, since lifetime analysis occurs during parsing, we may encounter
277 // cases where a full definition of the specialization is not required. In
278 // such cases, the specialization declaration remains incomplete and lacks the
279 // attribute. Therefore, we fall back to checking the primary template class.
280 //
281 // Note: it is possible for a specialization declaration to have an attribute
282 // even if the primary template does not.
283 //
284 // FIXME: What if the primary template and explicit specialization
285 // declarations have conflicting attributes? We should consider diagnosing
286 // this scenario.
287 bool Result = RD->hasAttr<T>();
288
289 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
290 Result |= CTSD->getSpecializedTemplate()->getTemplatedDecl()->hasAttr<T>();
291
292 return Result;
293}
294
297
298static StringRef getName(const CXXRecordDecl &RD) {
299 if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(&RD))
300 return CTSD->getSpecializedTemplate()->getName();
301 if (RD.getIdentifier())
302 return RD.getName();
303 return "";
304}
305
306static StringRef getName(const FunctionDecl &FD) {
307 if (FD.getIdentifier())
308 return FD.getName();
309 return "";
310}
311
312static bool isStdUniquePtr(const CXXRecordDecl &RD) {
313 return RD.isInStdNamespace() && getName(RD) == "unique_ptr";
314}
315
317 return MD.getIdentifier() && MD.getName() == "release" &&
318 MD.getNumParams() == 0 && isStdUniquePtr(*MD.getParent());
319}
320
322 const CXXRecordDecl *RD = MD.getParent();
323 if (!isInStlNamespace(RD))
324 return false;
325
326 // `pop_back` is excluded: it only invalidates references to the removed
327 // element, not to other elements.
328 static const llvm::StringSet<> Vector = {// Insertion
329 "insert", "emplace", "emplace_back",
330 "push_back", "insert_range",
331 "append_range",
332 // Removal
333 "erase", "clear",
334 // Memory management
335 "reserve", "resize", "shrink_to_fit",
336 // Assignment
337 "assign", "assign_range"};
338
339 // `pop_*` methods are excluded: they only invalidate references to the
340 // removed element, not to other elements.
341 static const llvm::StringSet<> Deque = {// Insertion
342 "insert", "emplace", "insert_range",
343 // Removal
344 "erase", "clear",
345 // Memory management
346 "resize", "shrink_to_fit",
347 // Assignment
348 "assign", "assign_range"};
349
350 static const llvm::StringSet<> String = {
351 // Insertion
352 "insert", "push_back", "append", "replace", "replace_with_range",
353 "insert_range", "append_range",
354 // Removal
355 "pop_back", "erase", "clear",
356 // Memory management
357 "reserve", "resize", "resize_and_overwrite", "shrink_to_fit",
358 // Assignment
359 "swap", "assign", "assign_range"};
360
361 // FIXME: Add queue and stack and check for underlying container
362 // (e.g. no invalidation for std::list).
363 static const llvm::StringSet<> PriorityQueue = {// Insertion
364 "push", "emplace",
365 "push_range",
366 // Removal
367 "pop"};
368
369 // `erase` and `extract` are excluded: they only affect the removed element,
370 // not to other elements.
371 static const llvm::StringSet<> NodeBased = {// Removal
372 "clear"};
373
374 // For `flat_*` container adaptors, `try_emplace` and `insert_or_assign`
375 // only exist on `flat_map`. Listing them here is harmless since the methods
376 // won't be found on other types.
377 static const llvm::StringSet<> Flat = {// Insertion
378 "insert", "emplace", "emplace_hint",
379 "try_emplace", "insert_or_assign",
380 "insert_range", "merge",
381 // Removal
382 "extract", "erase", "clear",
383 // Assignment
384 "replace"};
385
386 static const llvm::StringSet<> UniquePtr = {// Reallocation
387 "reset"};
388
389 const StringRef RecordName = getName(*RD);
390 // TODO: Consider caching this lookup by CXXMethodDecl pointer if this
391 // StringSwitch becomes a performance bottleneck.
392 const llvm::StringSet<> *InvalidatingMethods =
393 llvm::StringSwitch<const llvm::StringSet<> *>(RecordName)
394 .Case("vector", &Vector)
395 .Case("basic_string", &String)
396 .Case("deque", &Deque)
397 .Case("priority_queue", &PriorityQueue)
398 .Cases({"set", "multiset", "map", "multimap", "unordered_set",
399 "unordered_multiset", "unordered_map", "unordered_multimap"},
400 &NodeBased)
401 .Cases({"flat_map", "flat_set", "flat_multimap", "flat_multiset"},
402 &Flat)
403 .Case("unique_ptr", &UniquePtr)
404 .Default(nullptr);
405
406 if (!InvalidatingMethods)
407 return false;
408
409 // Handle Operators via OverloadedOperatorKind
411 if (OO != OO_None) {
412 switch (OO) {
413 case OO_Equal: // operator= : Always invalidates (Assignment)
414 case OO_PlusEqual: // operator+= : Append (String/Vector)
415 return true;
416 case OO_Subscript: // operator[] : Invalidation only for
417 // `flat_map` (Insert-or-access).
418 // `map` and `unordered_map` are excluded.
419 return RecordName == "flat_map";
420 default:
421 return false;
422 }
423 }
424
425 if (!MD.getIdentifier())
426 return false;
427
428 return InvalidatingMethods->contains(MD.getName());
429}
430
433 return true;
434 return isInStlNamespace(&FD) && getName(FD) == "destroy_at";
435}
436
438 if (!RD || !isInStlNamespace(RD))
439 return false;
440 StringRef Name = getName(*RD);
441 return Name == "function" || Name == "move_only_function";
442}
443
444} // 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
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2122
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:8416
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:1875
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:8682
bool isReferenceType() const
Definition TypeBase.h:8706
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:8710
bool isNullPtrType() const
Definition TypeBase.h:9085
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