1//=======- PtrTypesSemantics.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//===----------------------------------------------------------------------===//
8
9#include "PtrTypesSemantics.h"
10#include "ASTUtils.h"
11#include "clang/AST/Attr.h"
12#include "clang/AST/CXXInheritance.h"
13#include "clang/AST/Decl.h"
14#include "clang/AST/DeclCXX.h"
15#include "clang/AST/ExprCXX.h"
16#include "clang/AST/StmtVisitor.h"
17#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
18#include <optional>
19
20using namespace clang;
21
22namespace {
23
24bool hasPublicMethodInBaseClass(const CXXRecordDecl *R, StringRef NameToMatch) {
25 assert(R);
26 assert(R->hasDefinition());
27
28 for (const CXXMethodDecl *MD : R->methods()) {
29 const auto MethodName = safeGetName(ASTNode: MD);
30 if (MethodName == NameToMatch && MD->getAccess() == AS_public)
31 return true;
32 }
33
34 for (const Decl *D : R->decls()) {
35 const auto *Shadow = dyn_cast<UsingShadowDecl>(Val: D);
36 if (!Shadow || Shadow->getAccess() != AS_public)
37 continue;
38 const auto *MD = dyn_cast<CXXMethodDecl>(Val: Shadow->getTargetDecl());
39 if (MD && safeGetName(ASTNode: MD) == NameToMatch)
40 return true;
41 }
42 return false;
43}
44
45} // namespace
46
47namespace clang {
48
49std::optional<const clang::CXXRecordDecl *>
50hasPublicMethodInBase(const CXXBaseSpecifier *Base, StringRef NameToMatch) {
51 assert(Base);
52
53 const Type *T = Base->getType().getTypePtrOrNull();
54 if (!T)
55 return std::nullopt;
56
57 const CXXRecordDecl *R = T->getAsCXXRecordDecl();
58 if (!R) {
59 auto CT = Base->getType().getCanonicalType();
60 if (auto *TST = dyn_cast<TemplateSpecializationType>(Val&: CT)) {
61 auto TmplName = TST->getTemplateName();
62 if (!TmplName.isNull()) {
63 if (auto *TD = TmplName.getAsTemplateDecl())
64 R = dyn_cast_or_null<CXXRecordDecl>(Val: TD->getTemplatedDecl());
65 }
66 }
67 if (!R)
68 return std::nullopt;
69 }
70 if (!R->hasDefinition())
71 return std::nullopt;
72
73 return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr;
74}
75
76static std::optional<bool> hasPublicMethodInHierarchy(const CXXRecordDecl *R,
77 StringRef MethodName) {
78 assert(R);
79
80 R = R->getDefinition();
81 if (!R)
82 return std::nullopt;
83
84 if (hasPublicMethodInBaseClass(R, NameToMatch: MethodName))
85 return true;
86
87 CXXBasePaths Paths;
88 Paths.setOrigin(const_cast<CXXRecordDecl *>(R));
89
90 bool AnyInconclusiveBase = false;
91 const auto hasPublicMethod = [&](const CXXBaseSpecifier *Base,
92 CXXBasePath &) {
93 auto HasMethodInBase = clang::hasPublicMethodInBase(Base, NameToMatch: MethodName);
94 if (!HasMethodInBase) {
95 AnyInconclusiveBase = true;
96 return false;
97 }
98 return (*HasMethodInBase) != nullptr;
99 };
100
101 bool Found = R->lookupInBases(BaseMatches: hasPublicMethod, Paths,
102 /*LookupInDependent =*/true);
103 if (AnyInconclusiveBase)
104 return std::nullopt;
105
106 return Found;
107}
108
109std::optional<bool> isSmartPtrCompatible(const CXXRecordDecl *R,
110 StringRef IncMethodName,
111 StringRef DecMethodName) {
112 assert(R);
113
114 auto HasInc = hasPublicMethodInHierarchy(R, MethodName: IncMethodName);
115 if (!HasInc)
116 return std::nullopt;
117
118 auto HasDec = hasPublicMethodInHierarchy(R, MethodName: DecMethodName);
119 if (!HasDec)
120 return std::nullopt;
121
122 return *HasInc && *HasDec;
123}
124
125std::optional<bool> isRefCountable(const clang::CXXRecordDecl *R) {
126 return isSmartPtrCompatible(R, IncMethodName: "ref", DecMethodName: "deref");
127}
128
129std::optional<bool> isCheckedPtrCapable(const clang::CXXRecordDecl *R) {
130 return isSmartPtrCompatible(R, IncMethodName: "incrementCheckedPtrCount",
131 DecMethodName: "decrementCheckedPtrCount");
132}
133
134bool isRefType(const std::string &Name) {
135 return Name == "Ref" || Name == "RefAllowingPartiallyDestroyed" ||
136 Name == "RefPtr" || Name == "RefPtrAllowingPartiallyDestroyed";
137}
138
139bool isRetainPtrOrOSPtr(const std::string &Name) {
140 return Name == "RetainPtr" || Name == "RetainPtrArc" ||
141 Name == "OSObjectPtr" || Name == "OSObjectPtrArc";
142}
143
144bool isCheckedPtr(const std::string &Name) {
145 return Name == "CheckedPtr" || Name == "CheckedRef";
146}
147
148bool isUniquePtr(const std::string &Name) {
149 return Name == "unique_ptr" || Name == "UniqueRef" || Name == "LazyUniqueRef";
150}
151
152bool isOwnerPtr(const std::string &Name) {
153 return isRefType(Name) || isCheckedPtr(Name) || isRetainPtrOrOSPtr(Name) ||
154 isUniquePtr(Name);
155}
156
157static bool isWeakPtrClass(const std::string &Name) {
158 return Name == "WeakPtr" || Name == "SingleThreadPackedWeakPtr" ||
159 Name == "SingleThreadWeakPtr" || Name == "ThreadSafeWeakPtr" ||
160 Name == "ThreadSafeWeakOrStrongPtr" || Name == "InlineWeakPtr";
161}
162
163bool isSmartPtrClass(const std::string &Name) {
164 return isRefType(Name) || isCheckedPtr(Name) || isRetainPtrOrOSPtr(Name) ||
165 isWeakPtrClass(Name) || Name == "WeakPtrFactory" ||
166 Name == "WeakPtrFactoryWithBitField" || Name == "WeakPtrImplBase" ||
167 Name == "WeakPtrImplBaseSingleThread" ||
168 Name == "ThreadSafeWeakOrStrongPtr" ||
169 Name == "ThreadSafeWeakPtrControlBlock" ||
170 Name == "ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr";
171}
172
173std::string getConstructorName(const clang::FunctionDecl *F) {
174 if (auto *Ctor = dyn_cast_or_null<CXXConstructorDecl>(Val: F))
175 return safeGetName(ASTNode: Ctor->getParent());
176 return safeGetName(ASTNode: F);
177}
178
179bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
180 assert(F);
181 auto FunctionName = getConstructorName(F);
182 return isRefType(Name: FunctionName) || FunctionName == "adoptRef" ||
183 FunctionName == "UniqueRef" || FunctionName == "makeUniqueRef" ||
184 FunctionName == "makeUniqueRefWithoutFastMallocCheck"
185
186 || FunctionName == "String" || FunctionName == "AtomString" ||
187 FunctionName == "UniqueString"
188 // FIXME: Implement as attribute.
189 || FunctionName == "Identifier";
190}
191
192bool isCtorOfCheckedPtr(const clang::FunctionDecl *F) {
193 assert(F);
194 return isCheckedPtr(Name: getConstructorName(F));
195}
196
197bool isCtorOfRetainPtrOrOSPtr(const clang::FunctionDecl *F) {
198 auto FunctionName = getConstructorName(F);
199 return isRetainPtrOrOSPtr(Name: FunctionName) || FunctionName == "adoptNS" ||
200 FunctionName == "adoptNSNullable" || FunctionName == "adoptCF" ||
201 FunctionName == "adoptCFNullable" || FunctionName == "retainPtr" ||
202 FunctionName == "adoptNSArc" || FunctionName == "adoptOSObject" ||
203 FunctionName == "adoptOSObjectArc";
204}
205
206bool isCtorOfSafePtr(const clang::FunctionDecl *F) {
207 return isCtorOfRefCounted(F) || isCtorOfCheckedPtr(F) ||
208 isCtorOfRetainPtrOrOSPtr(F);
209}
210
211bool isStdOrWTFMove(const clang::FunctionDecl *F) {
212 auto FnName = safeGetName(ASTNode: F);
213 auto *Namespace = F->getParent();
214 if (!Namespace)
215 return false;
216 auto *TUDeck = Namespace->getParent();
217 if (!isa_and_nonnull<TranslationUnitDecl>(Val: TUDeck))
218 return false;
219 auto NsName = safeGetName(ASTNode: Namespace);
220 return (NsName == "WTF" || NsName == "std") && FnName == "move";
221}
222
223template <typename Predicate>
224static bool isPtrOfType(const clang::QualType T, Predicate Pred) {
225 QualType type = T;
226 while (!type.isNull()) {
227 if (auto *SpecialT = type->getAs<TemplateSpecializationType>()) {
228 auto *Decl = SpecialT->getTemplateName().getAsTemplateDecl();
229 return Decl && Pred(Decl->getNameAsString());
230 } else if (auto *DTS = type->getAs<DeducedTemplateSpecializationType>()) {
231 auto *Decl = DTS->getTemplateName().getAsTemplateDecl();
232 return Decl && Pred(Decl->getNameAsString());
233 } else if (auto *RD = type->getAs<RecordType>()) {
234 auto *Decl = RD->getDecl();
235 return Decl && Pred(Decl->getNameAsString());
236 } else
237 break;
238 }
239 return false;
240}
241
242bool isRefOrCheckedPtrType(const clang::QualType T) {
243 return isPtrOfType(
244 T, Pred: [](auto Name) { return isRefType(Name) || isCheckedPtr(Name); });
245}
246
247bool isRetainPtrOrOSPtrType(const clang::QualType T) {
248 return isPtrOfType(T, Pred: [](auto Name) { return isRetainPtrOrOSPtr(Name); });
249}
250
251bool isOwnerPtrType(const clang::QualType T) {
252 return isPtrOfType(T, Pred: [](auto Name) { return isOwnerPtr(Name); });
253}
254
255std::optional<bool> isUncounted(const QualType T) {
256 if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(Val: T)) {
257 if (auto *Decl = Subst->getAssociatedDecl()) {
258 if (isRefType(Name: safeGetName(ASTNode: Decl)))
259 return false;
260 }
261 }
262 return isUncounted(Class: T->getAsCXXRecordDecl());
263}
264
265std::optional<bool> isUnchecked(const QualType T) {
266 if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(Val: T)) {
267 if (auto *Decl = Subst->getAssociatedDecl()) {
268 if (isCheckedPtr(Name: safeGetName(ASTNode: Decl)))
269 return false;
270 }
271 }
272 return isUnchecked(Class: T->getAsCXXRecordDecl());
273}
274
275void RetainTypeChecker::visitTranslationUnitDecl(
276 const TranslationUnitDecl *TUD) {
277 IsARCEnabled = TUD->getLangOpts().ObjCAutoRefCount;
278 DefaultSynthProperties = TUD->getLangOpts().ObjCDefaultSynthProperties;
279}
280
281void RetainTypeChecker::visitTypedef(const TypedefDecl *TD) {
282 auto QT = TD->getUnderlyingType();
283 if (!QT->isPointerType())
284 return;
285
286 auto PointeeQT = QT->getPointeeType();
287 const RecordType *RT = PointeeQT->getAsCanonical<RecordType>();
288 if (!RT) {
289 if (TD->hasAttr<ObjCBridgeAttr>() || TD->hasAttr<ObjCBridgeMutableAttr>()) {
290 RecordlessTypes.insert(V: TD->getASTContext()
291 .getTypedefType(Keyword: ElaboratedTypeKeyword::None,
292 /*Qualifier=*/std::nullopt, Decl: TD)
293 .getTypePtr());
294 }
295 return;
296 }
297
298 for (auto *Redecl : RT->getDecl()->getMostRecentDecl()->redecls()) {
299 if (Redecl->getAttr<ObjCBridgeAttr>() ||
300 Redecl->getAttr<ObjCBridgeMutableAttr>()) {
301 CFPointees.insert(KV: {RT, TD});
302 return;
303 }
304 }
305}
306
307bool RetainTypeChecker::isUnretained(const QualType QT, bool ignoreARC) {
308 if (ento::cocoa::isCocoaObjectRef(T: QT) && (!IsARCEnabled || ignoreARC))
309 return true;
310 if (auto *RT = dyn_cast_or_null<RecordType>(
311 Val: QT.getCanonicalType()->getPointeeType().getTypePtrOrNull()))
312 return CFPointees.contains(Val: RT);
313 return RecordlessTypes.contains(V: QT.getTypePtr());
314}
315
316const TypedefDecl *RetainTypeChecker::getCanonicalDecl(QualType QT) {
317 if (auto *TT = dyn_cast_or_null<TypedefType>(Val: QT.getTypePtrOrNull())) {
318 if (auto *TD = dyn_cast<TypedefDecl>(Val: TT->getDecl()))
319 return TD;
320 }
321 QT = QT.getCanonicalType();
322 auto PointeeQT = QT->getPointeeType();
323 auto *PointeeType = PointeeQT.getTypePtrOrNull();
324 if (!PointeeType)
325 return nullptr;
326 auto *RD = dyn_cast<RecordType>(Val: PointeeType);
327 if (!RD)
328 return nullptr;
329 return CFPointees.lookup(Val: RD);
330}
331
332std::optional<bool> isUncounted(const CXXRecordDecl* Class)
333{
334 // Keep isRefCounted first as it's cheaper.
335 if (!Class || isRefCounted(Class))
336 return false;
337
338 std::optional<bool> IsRefCountable = isRefCountable(R: Class);
339 if (!IsRefCountable)
340 return std::nullopt;
341
342 return (*IsRefCountable);
343}
344
345std::optional<bool> isUnchecked(const CXXRecordDecl *Class) {
346 if (!Class || isCheckedPtr(Class))
347 return false; // Cheaper than below
348 return isCheckedPtrCapable(R: Class);
349}
350
351std::optional<bool> isUncountedPtr(const QualType T) {
352 if (T->isPointerType() || T->isReferenceType()) {
353 if (auto *CXXRD = T->getPointeeCXXRecordDecl())
354 return isUncounted(Class: CXXRD);
355 }
356 return false;
357}
358
359std::optional<bool> isUncheckedPtr(const QualType T) {
360 if (T->isPointerType() || T->isReferenceType()) {
361 if (auto *CXXRD = T->getPointeeCXXRecordDecl())
362 return isUnchecked(Class: CXXRD);
363 }
364 return false;
365}
366
367std::optional<bool> isGetterOfSafePtr(const CXXMethodDecl *M) {
368 assert(M);
369
370 const CXXRecordDecl *calleeMethodsClass = M->getParent();
371 std::string className = safeGetName(ASTNode: calleeMethodsClass);
372 std::string method = safeGetName(ASTNode: M);
373
374 auto OpType = M->getOverloadedOperator();
375 if (isCheckedPtr(Name: className) &&
376 (method == "get" || method == "ptr" || OpType == OO_Star))
377 return true;
378
379 if ((isRefType(Name: className) &&
380 (method == "get" || method == "ptr" || OpType == OO_Star)) ||
381 ((className == "String" || className == "AtomString" ||
382 className == "AtomStringImpl" || className == "UniqueString" ||
383 className == "UniqueStringImpl" || className == "Identifier") &&
384 method == "impl"))
385 return true;
386
387 if (isRetainPtrOrOSPtr(Name: className) && method == "get")
388 return true;
389
390 // Ref<T> -> T conversion
391 // FIXME: Currently allowing any Ref<T> -> whatever cast.
392 if (isRefType(Name: className)) {
393 if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(Val: M)) {
394 QualType QT = maybeRefToRawOperator->getConversionType();
395 const Type *T = QT.getTypePtrOrNull();
396 return T && (T->isPointerType() || T->isReferenceType());
397 }
398 }
399
400 if (isCheckedPtr(Name: className)) {
401 if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(Val: M)) {
402 QualType QT = maybeRefToRawOperator->getConversionType();
403 const Type *T = QT.getTypePtrOrNull();
404 return T && (T->isPointerType() || T->isReferenceType());
405 }
406 }
407
408 if (isRetainPtrOrOSPtr(Name: className)) {
409 if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(Val: M)) {
410 QualType QT = maybeRefToRawOperator->getConversionType();
411 const Type *T = QT.getTypePtrOrNull();
412 return T && (T->isPointerType() || T->isReferenceType() ||
413 T->isObjCObjectPointerType());
414 }
415 }
416 return false;
417}
418
419bool isGetterOfUniquePtr(const CXXMethodDecl *M) {
420 assert(M);
421 if (!isUniquePtr(Name: safeGetName(ASTNode: M->getParent())))
422 return false;
423 auto method = safeGetName(ASTNode: M);
424 if (method == "get" || method == "ptr")
425 return true;
426 if (auto *conversion = dyn_cast<CXXConversionDecl>(Val: M)) {
427 const Type *T = conversion->getConversionType().getTypePtrOrNull();
428 return T && (T->isPointerType() || T->isReferenceType());
429 }
430 return false;
431}
432
433bool isRefCounted(const CXXRecordDecl *R) {
434 assert(R);
435 if (auto *TmplR = R->getTemplateInstantiationPattern()) {
436 // FIXME: String/AtomString/UniqueString
437 const auto &ClassName = safeGetName(ASTNode: TmplR);
438 return isRefType(Name: ClassName);
439 }
440 return false;
441}
442
443bool isCheckedPtr(const CXXRecordDecl *R) {
444 assert(R);
445 if (auto *TmplR = R->getTemplateInstantiationPattern()) {
446 const auto &ClassName = safeGetName(ASTNode: TmplR);
447 return isCheckedPtr(Name: ClassName);
448 }
449 return false;
450}
451
452bool isRetainPtrOrOSPtr(const CXXRecordDecl *R) {
453 assert(R);
454 if (auto *TmplR = R->getTemplateInstantiationPattern())
455 return isRetainPtrOrOSPtr(Name: safeGetName(ASTNode: TmplR));
456 return false;
457}
458
459bool isWeakPtr(const CXXRecordDecl *R) {
460 assert(R);
461 if (auto *TmplR = R->getTemplateInstantiationPattern())
462 return isWeakPtrClass(Name: safeGetName(ASTNode: TmplR));
463 return false;
464}
465
466bool isSmartPtr(const CXXRecordDecl *R) {
467 assert(R);
468 if (auto *TmplR = R->getTemplateInstantiationPattern())
469 return isSmartPtrClass(Name: safeGetName(ASTNode: TmplR));
470 return false;
471}
472
473enum class WebKitAnnotation : uint8_t {
474 None,
475 PointerConversion,
476 NoDelete,
477};
478
479static WebKitAnnotation typeAnnotationForReturnType(const FunctionDecl *FD) {
480 auto RetType = FD->getReturnType();
481 auto *Type = RetType.getTypePtrOrNull();
482 if (auto *MacroQualified = dyn_cast_or_null<MacroQualifiedType>(Val: Type))
483 Type = MacroQualified->desugar().getTypePtrOrNull();
484 auto *Attr = dyn_cast_or_null<AttributedType>(Val: Type);
485 if (!Attr)
486 return WebKitAnnotation::None;
487 auto *AnnotateType = dyn_cast_or_null<AnnotateTypeAttr>(Val: Attr->getAttr());
488 if (!AnnotateType)
489 return WebKitAnnotation::None;
490 auto Annotation = AnnotateType->getAnnotation();
491 if (Annotation == "webkit.pointerconversion")
492 return WebKitAnnotation::PointerConversion;
493 if (Annotation == "webkit.nodelete")
494 return WebKitAnnotation::NoDelete;
495 return WebKitAnnotation::None;
496}
497
498bool isPtrConversion(const FunctionDecl *F) {
499 assert(F);
500 if (isCtorOfRefCounted(F))
501 return true;
502
503 // FIXME: check # of params == 1
504 const auto FunctionName = safeGetName(ASTNode: F);
505 if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
506 FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
507 FunctionName == "checkedDowncast" || FunctionName == "bit_cast" ||
508 FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast" ||
509 FunctionName == "bridge_cast" || FunctionName == "bridge_id_cast" ||
510 FunctionName == "dynamic_cf_cast" || FunctionName == "checked_cf_cast" ||
511 FunctionName == "dynamic_objc_cast" ||
512 FunctionName == "checked_objc_cast")
513 return true;
514
515 if (typeAnnotationForReturnType(FD: F) == WebKitAnnotation::PointerConversion)
516 return true;
517
518 return false;
519}
520
521static bool isNoDeleteFunctionDecl(const FunctionDecl *F) {
522 return typeAnnotationForReturnType(FD: F) == WebKitAnnotation::NoDelete;
523}
524
525bool isNoDeleteFunction(const FunctionDecl *F) {
526 if (llvm::any_of(Range: F->redecls(), P: isNoDeleteFunctionDecl))
527 return true;
528
529 const auto *MD = dyn_cast<CXXMethodDecl>(Val: F);
530 if (!MD || !MD->isVirtual())
531 return false;
532
533 auto Overriders = llvm::to_vector(Range: MD->overridden_methods());
534 while (!Overriders.empty()) {
535 const auto *Fn = Overriders.pop_back_val();
536 llvm::append_range(C&: Overriders, R: Fn->overridden_methods());
537 if (isNoDeleteFunctionDecl(F: Fn))
538 return true;
539 }
540
541 return false;
542}
543
544bool isTrivialBuiltinFunction(const FunctionDecl *F) {
545 if (!F || !F->getDeclName().isIdentifier())
546 return false;
547 auto Name = F->getName();
548 return Name.starts_with(Prefix: "__builtin") || Name == "__libcpp_verbose_abort" ||
549 Name.starts_with(Prefix: "os_log") || Name.starts_with(Prefix: "_os_log");
550}
551
552bool isSingleton(const NamedDecl *F) {
553 assert(F);
554 // FIXME: check # of params == 1
555 if (auto *MethodDecl = dyn_cast<CXXMethodDecl>(Val: F)) {
556 if (!MethodDecl->isStatic())
557 return false;
558 }
559 const auto &NameStr = safeGetName(ASTNode: F);
560 StringRef Name = NameStr; // FIXME: Make safeGetName return StringRef.
561 return Name == "singleton" || Name.ends_with(Suffix: "Singleton");
562}
563
564// We only care about statements so let's use the simple
565// (non-recursive) visitor.
566class TrivialFunctionAnalysisVisitor
567 : public ConstStmtVisitor<TrivialFunctionAnalysisVisitor, bool> {
568
569 // Returns false if at least one child is non-trivial.
570 bool VisitChildren(const Stmt *S) {
571 for (const Stmt *Child : S->children()) {
572 if (Child && !Visit(S: Child)) {
573 if (OffendingStmt && !*OffendingStmt)
574 *OffendingStmt = Child;
575 return false;
576 }
577 }
578
579 return true;
580 }
581
582 template <typename StmtOrDecl, typename CheckFunction>
583 bool WithCachedResult(const StmtOrDecl *S, CheckFunction Function) {
584 auto CacheIt = Cache.find(S);
585 if (CacheIt != Cache.end() && !OffendingStmt)
586 return CacheIt->second;
587
588 // Treat a recursive statement to be trivial until proven otherwise.
589 auto [RecursiveIt, IsNew] = RecursiveFn.insert(std::make_pair(S, true));
590 if (!IsNew)
591 return RecursiveIt->second;
592
593 bool Result = Function();
594
595 if (!Result) {
596 for (auto &It : RecursiveFn)
597 It.second = false;
598 }
599 RecursiveIt = RecursiveFn.find(S);
600 assert(RecursiveIt != RecursiveFn.end());
601 Result = RecursiveIt->second;
602 RecursiveFn.erase(RecursiveIt);
603 Cache[S] = Result;
604
605 return Result;
606 }
607
608 bool CanTriviallyDestruct(QualType Ty) {
609 if (Ty.isNull())
610 return false;
611
612 // T*, T& or T&& does not run its destructor.
613 if (Ty->isPointerOrReferenceType())
614 return true;
615
616 // FIXME: Handle a case when there is a local autorelease pool.
617 if (Ty->isObjCObjectPointerType()) {
618 auto Type = Ty.isDestructedType();
619 if (Type == QualType::DK_objc_weak_lifetime || Type == QualType::DK_none)
620 return true;
621 // strong lifetime in ARC could dealloc an object.
622 }
623
624 // Fundamental types (integral, nullptr_t, etc...) don't have destructors.
625 if (Ty->isFundamentalType() || Ty->isIntegralOrEnumerationType())
626 return true;
627
628 if (const auto *R = Ty->getAsCXXRecordDecl()) {
629 // C++ trivially destructible classes are fine.
630 if (R->hasDefinition() && R->hasTrivialDestructor())
631 return true;
632
633 if (HasFieldWithNonTrivialDtor(Cls: R))
634 return false;
635
636 // For Webkit, side-effects are fine as long as we don't delete objects,
637 // so check recursively.
638 if (const auto *Dtor = R->getDestructor())
639 return IsFunctionTrivial(D: Dtor);
640 }
641
642 // Structs in C are trivial.
643 if (Ty->isRecordType())
644 return true;
645
646 // For arrays it depends on the element type.
647 // FIXME: We should really use ASTContext::getAsArrayType instead.
648 if (const auto *AT = Ty->getAsArrayTypeUnsafe())
649 return CanTriviallyDestruct(Ty: AT->getElementType());
650
651 return false; // Otherwise it's likely not trivial.
652 }
653
654 bool HasFieldWithNonTrivialDtor(const CXXRecordDecl *Cls) {
655 auto CacheIt = FieldDtorCache.find(Val: Cls);
656 if (CacheIt != FieldDtorCache.end())
657 return CacheIt->second;
658
659 bool Result = ([&] {
660 auto HasNonTrivialField = [&](const CXXRecordDecl *R) {
661 for (const FieldDecl *F : R->fields()) {
662 if (!CanTriviallyDestruct(Ty: F->getType()))
663 return true;
664 }
665 return false;
666 };
667
668 if (HasNonTrivialField(Cls))
669 return true;
670
671 if (!Cls->hasDefinition())
672 return false;
673
674 CXXBasePaths Paths;
675 Paths.setOrigin(const_cast<CXXRecordDecl *>(Cls));
676 return Cls->lookupInBases(
677 BaseMatches: [&](const CXXBaseSpecifier *B, CXXBasePath &) {
678 auto *T = B->getType().getTypePtrOrNull();
679 if (!T)
680 return false;
681 auto *R = T->getAsCXXRecordDecl();
682 return R && HasNonTrivialField(R);
683 },
684 Paths, /*LookupInDependent =*/true);
685 })();
686
687 FieldDtorCache[Cls] = Result;
688
689 return Result;
690 }
691
692public:
693 using CacheTy = TrivialFunctionAnalysis::CacheTy;
694
695 TrivialFunctionAnalysisVisitor(CacheTy &Cache,
696 const Stmt **OffendingStmt = nullptr)
697 : Cache(Cache), OffendingStmt(OffendingStmt) {}
698
699 bool IsFunctionTrivial(const Decl *D) {
700 const Stmt **SavedOffendingStmt = std::exchange(obj&: OffendingStmt, new_val: nullptr);
701 auto Result = WithCachedResult(S: D, Function: [&]() {
702 auto *FnDecl = dyn_cast<FunctionDecl>(Val: D);
703 auto *MethodDecl = dyn_cast<CXXMethodDecl>(Val: D);
704 auto *CtorDecl = dyn_cast<CXXConstructorDecl>(Val: D);
705 auto *DtorDecl = dyn_cast<CXXDestructorDecl>(Val: D);
706
707 if (FnDecl) {
708 if (isNoDeleteFunction(F: FnDecl))
709 return true;
710 if (MethodDecl && MethodDecl->isVirtual())
711 return false;
712 for (auto *Param : FnDecl->parameters()) {
713 if (!HasTrivialDestructor(VD: Param))
714 return false;
715 }
716 }
717 if (CtorDecl) {
718 for (auto *CtorInit : CtorDecl->inits()) {
719 if (!Visit(S: CtorInit->getInit()))
720 return false;
721 }
722 }
723 // An implicit or =default special member runs no user code when it is
724 // trivial in the C++ standard sense, so it cannot delete. Such a
725 // member's synthesized body is typically absent from the AST until
726 // codegen materialises it, which the generic null-body check below
727 // would otherwise conservatively classify as non-trivial.
728 if (MethodDecl && !MethodDecl->isUserProvided()) {
729 if (CtorDecl) {
730 const CXXRecordDecl *RD = CtorDecl->getParent();
731 if ((CtorDecl->isDefaultConstructor() &&
732 RD->hasTrivialDefaultConstructor()) ||
733 (CtorDecl->isCopyConstructor() &&
734 RD->hasTrivialCopyConstructor()) ||
735 (CtorDecl->isMoveConstructor() &&
736 RD->hasTrivialMoveConstructor()))
737 return true;
738 }
739 if (DtorDecl && DtorDecl->getParent()->hasTrivialDestructor())
740 return true;
741 }
742 const Stmt *Body = D->getBody();
743 if (!Body)
744 return false;
745 return Visit(S: Body);
746 });
747 OffendingStmt = SavedOffendingStmt;
748 return Result;
749 }
750
751 bool HasTrivialDestructor(const VarDecl *VD) {
752 return WithCachedResult(
753 S: VD, Function: [&] { return CanTriviallyDestruct(Ty: VD->getType()); });
754 }
755
756 bool IsStatementTrivial(const Stmt *S) {
757 auto CacheIt = Cache.find(Val: S);
758 if (CacheIt != Cache.end())
759 return CacheIt->second;
760 bool Result = Visit(S);
761 Cache[S] = Result;
762 return Result;
763 }
764
765 bool VisitStmt(const Stmt *S) {
766 // All statements are non-trivial unless overriden later.
767 // Don't even recurse into children by default.
768 return false;
769 }
770
771 bool VisitAttributedStmt(const AttributedStmt *AS) {
772 // Ignore attributes.
773 return Visit(S: AS->getSubStmt());
774 }
775
776 bool VisitCompoundStmt(const CompoundStmt *CS) {
777 // A compound statement is allowed as long each individual sub-statement
778 // is trivial.
779 return WithCachedResult(S: CS, Function: [&]() { return VisitChildren(S: CS); });
780 }
781
782 bool VisitCoroutineBodyStmt(const CoroutineBodyStmt *CBS) {
783 return WithCachedResult(S: CBS, Function: [&]() { return VisitChildren(S: CBS); });
784 }
785
786 bool VisitReturnStmt(const ReturnStmt *RS) {
787 // A return statement is allowed as long as the return value is trivial. A
788 // returned smart-pointer prvalue is special: under guaranteed copy elision
789 // the temporary *is* the function's return slot, so it is destructed by the
790 // caller, not here. Hence we may ignore that temporary's destructor.
791 if (auto *RV = RS->getRetValue())
792 return visitReturnValueElidingTemp(Arg: RV);
793 return true;
794 }
795
796 bool VisitDeclStmt(const DeclStmt *DS) {
797 for (auto &Decl : DS->decls()) {
798 // FIXME: Handle DecompositionDecls.
799 if (auto *VD = dyn_cast<VarDecl>(Val: Decl)) {
800 if (!HasTrivialDestructor(VD))
801 return false;
802 }
803 }
804 return VisitChildren(S: DS);
805 }
806 bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(S: DS); }
807 bool VisitIfStmt(const IfStmt *IS) {
808 return WithCachedResult(S: IS, Function: [&]() { return VisitChildren(S: IS); });
809 }
810 bool VisitForStmt(const ForStmt *FS) {
811 return WithCachedResult(S: FS, Function: [&]() { return VisitChildren(S: FS); });
812 }
813 bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
814 return WithCachedResult(S: FS, Function: [&]() { return VisitChildren(S: FS); });
815 }
816 bool VisitWhileStmt(const WhileStmt *WS) {
817 return WithCachedResult(S: WS, Function: [&]() { return VisitChildren(S: WS); });
818 }
819 bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(S: SS); }
820 bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(S: CS); }
821 bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(S: DS); }
822
823 // break, continue, goto, and label statements are always trivial.
824 bool VisitBreakStmt(const BreakStmt *) { return true; }
825 bool VisitContinueStmt(const ContinueStmt *) { return true; }
826 bool VisitGotoStmt(const GotoStmt *) { return true; }
827 bool VisitLabelStmt(const LabelStmt *) { return true; }
828
829 bool VisitUnaryOperator(const UnaryOperator *UO) {
830 // Unary operators are trivial if its operand is trivial except co_await.
831 return UO->getOpcode() != UO_Coawait && Visit(S: UO->getSubExpr());
832 }
833
834 bool VisitBinaryOperator(const BinaryOperator *BO) {
835 // Binary operators are trivial if their operands are trivial.
836 return Visit(S: BO->getLHS()) && Visit(S: BO->getRHS());
837 }
838
839 bool VisitCompoundAssignOperator(const CompoundAssignOperator *CAO) {
840 // Compound assignment operator such as |= is trivial if its
841 // subexpresssions are trivial.
842 return VisitChildren(S: CAO);
843 }
844
845 bool VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE) {
846 return VisitChildren(S: ASE);
847 }
848
849 bool VisitConditionalOperator(const ConditionalOperator *CO) {
850 // Ternary operators are trivial if their conditions & values are trivial.
851 return VisitChildren(S: CO);
852 }
853
854 bool VisitAtomicExpr(const AtomicExpr *E) { return VisitChildren(S: E); }
855
856 bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) {
857 // Any static_assert is considered trivial.
858 return true;
859 }
860
861 bool VisitCallExpr(const CallExpr *CE) {
862 if (!checkArguments(CE))
863 return false;
864
865 auto *Callee = CE->getDirectCallee();
866 if (!Callee)
867 return false;
868
869 if (isPtrConversion(F: Callee))
870 return true;
871
872 const auto &Name = safeGetName(ASTNode: Callee);
873
874 if (Callee->isInStdNamespace() &&
875 (Name == "addressof" || Name == "forward" || Name == "move"))
876 return true;
877
878 if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" ||
879 Name == "WTFReportBacktrace" ||
880 Name == "WTFCrashWithSecurityImplication" || Name == "WTFCrash" ||
881 Name == "WTFReportAssertionFailure" || Name == "isMainThread" ||
882 Name == "isMainThreadOrGCThread" || Name == "isMainRunLoop" ||
883 Name == "isWebThread" || Name == "isUIThread" ||
884 Name == "mayBeGCThread" || Name == "compilerFenceForCrash" ||
885 isTrivialBuiltinFunction(F: Callee))
886 return true;
887
888 return IsFunctionTrivial(D: Callee);
889 }
890
891 bool VisitGCCAsmStmt(const GCCAsmStmt *AS) {
892 return AS->getAsmString() == "brk #0xc471";
893 }
894
895 bool
896 VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E) {
897 // Non-type template paramter is compile time constant and trivial.
898 return true;
899 }
900
901 bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) {
902 return VisitChildren(S: E);
903 }
904
905 bool VisitPredefinedExpr(const PredefinedExpr *E) {
906 // A predefined identifier such as "func" is considered trivial.
907 return true;
908 }
909
910 bool VisitOffsetOfExpr(const OffsetOfExpr *OE) {
911 // offsetof(T, D) is considered trivial.
912 return true;
913 }
914
915 bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
916 if (!checkArguments(CE: MCE))
917 return false;
918
919 bool TrivialThis = Visit(S: MCE->getImplicitObjectArgument());
920 if (!TrivialThis)
921 return false;
922
923 auto *Callee = MCE->getMethodDecl();
924 if (!Callee)
925 return false;
926
927 if (isa<CXXDestructorDecl>(Val: Callee) &&
928 !CanTriviallyDestruct(Ty: MCE->getObjectType()))
929 return false;
930
931 auto Name = safeGetName(ASTNode: Callee);
932 if (Name == "ref" || Name == "incrementCheckedPtrCount")
933 return true;
934
935 std::optional<bool> IsGetterOfRefCounted = isGetterOfSafePtr(M: Callee);
936 if (IsGetterOfRefCounted && *IsGetterOfRefCounted)
937 return true;
938
939 // Recursively descend into the callee to confirm that it's trivial as well.
940 return IsFunctionTrivial(D: Callee);
941 }
942
943 bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
944 if (!checkArguments(CE: OCE))
945 return false;
946 auto *Callee = OCE->getCalleeDecl();
947 if (!Callee)
948 return false;
949 // Recursively descend into the callee to confirm that it's trivial as well.
950 return IsFunctionTrivial(D: Callee);
951 }
952
953 bool VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *Op) {
954 auto *SemanticExpr = Op->getSemanticForm();
955 return SemanticExpr && Visit(S: SemanticExpr);
956 }
957
958 bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) {
959 if (auto *Expr = E->getExpr()) {
960 if (!Visit(S: Expr))
961 return false;
962 }
963 return true;
964 }
965
966 bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) {
967 return Visit(S: E->getExpr());
968 }
969
970 bool checkArguments(const CallExpr *CE) {
971 for (const Expr *Arg : CE->arguments()) {
972 if (Arg && !Visit(S: Arg))
973 return false;
974 }
975 return true;
976 }
977
978 // Triviality check for a return value that may elide a smart-pointer
979 // temporary's destructor.
980 //
981 // This is only valid for *return values*: a returned class prvalue is
982 // constructed directly into the function's return slot (C++17 guaranteed copy
983 // elision), so the temporary is destructed by the caller rather than here.
984 //
985 // It is deliberately NOT applied to call/constructor arguments. An argument
986 // temporary's lifetime ends at the full-expression *in this function* (the
987 // caller destroys arguments, e.g. per the Itanium C++ ABI), so its destructor
988 // runs here and may invoke delete. Proving otherwise would require
989 // interprocedural ownership analysis, so arguments are checked normally.
990 bool visitReturnValueElidingTemp(const Expr *Arg) {
991 QualType OriginalQT = Arg->getType();
992 auto *Type = OriginalQT.getTypePtrOrNull();
993 if (!Type)
994 return Visit(S: Arg);
995 auto *CXXRD = Type->getAsCXXRecordDecl();
996 if (!CXXRD || !isSmartPtrClass(Name: safeGetName(ASTNode: CXXRD)))
997 return Visit(S: Arg);
998 Arg = Arg->IgnoreParenCasts();
999 if (!Arg->isPRValue())
1000 return Visit(S: Arg);
1001 if (auto *Init = dyn_cast<InitListExpr>(Val: Arg)) {
1002 if (Init->getNumInits() == 1)
1003 Arg = Init->getInit(Init: 0);
1004 }
1005 if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Val: Arg)) {
1006 // Only elide when the temporary *is* the returned object, i.e. it has the
1007 // same smart-pointer type as the return value. Compare canonical,
1008 // unqualified types rather than relying on exact QualType identity, which
1009 // is sensitive to sugar (typedefs/aliases) and cv-qualifiers.
1010 if (OriginalQT.getCanonicalType().getUnqualifiedType() ==
1011 BTE->getType().getCanonicalType().getUnqualifiedType())
1012 return Visit(S: BTE->getSubExpr());
1013 }
1014 return Visit(S: Arg);
1015 }
1016
1017 bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
1018 if (CE->getNumArgs() == 1) {
1019 auto *InnerArg = CE->getArg(Arg: 0);
1020 if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Val: InnerArg)) {
1021 auto *InnerExpr = MTE->getSubExpr();
1022 if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Val: InnerExpr))
1023 InnerExpr = BTE->getSubExpr();
1024 auto InnerQT = InnerExpr->getType();
1025 if (auto *InnerDecl = InnerQT->getAsCXXRecordDecl()) {
1026 auto *OuterCls = CE->getConstructor()->getParent();
1027 if (isRefType(Name: safeGetName(ASTNode: OuterCls)) &&
1028 isRefType(Name: safeGetName(ASTNode: InnerDecl)))
1029 return Visit(S: InnerExpr);
1030 }
1031 }
1032 }
1033
1034 for (const Expr *Arg : CE->arguments()) {
1035 if (Arg && !Visit(S: Arg))
1036 return false;
1037 }
1038
1039 // Recursively descend into the callee to confirm that it's trivial.
1040 return IsFunctionTrivial(D: CE->getConstructor());
1041 }
1042
1043 bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E) {
1044 return IsFunctionTrivial(D: E->getConstructor());
1045 }
1046
1047 bool VisitCXXNewExpr(const CXXNewExpr *NE) { return VisitChildren(S: NE); }
1048
1049 bool VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
1050 return Visit(S: ICE->getSubExpr());
1051 }
1052
1053 bool VisitExplicitCastExpr(const ExplicitCastExpr *ECE) {
1054 return Visit(S: ECE->getSubExpr());
1055 }
1056
1057 bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT) {
1058 return Visit(S: VMT->getSubExpr());
1059 }
1060
1061 bool VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE) {
1062 if (auto *Temp = BTE->getTemporary()) {
1063 if (!IsFunctionTrivial(D: Temp->getDestructor()))
1064 return false;
1065 }
1066 return Visit(S: BTE->getSubExpr());
1067 }
1068
1069 bool VisitArrayInitLoopExpr(const ArrayInitLoopExpr *AILE) {
1070 return Visit(S: AILE->getCommonExpr()) && Visit(S: AILE->getSubExpr());
1071 }
1072
1073 bool VisitArrayInitIndexExpr(const ArrayInitIndexExpr *AIIE) {
1074 return true; // The current array index in VisitArrayInitLoopExpr is always
1075 // trivial.
1076 }
1077
1078 bool VisitOpaqueValueExpr(const OpaqueValueExpr *OVE) {
1079 return Visit(S: OVE->getSourceExpr());
1080 }
1081
1082 bool VisitExprWithCleanups(const ExprWithCleanups *EWC) {
1083 return Visit(S: EWC->getSubExpr());
1084 }
1085
1086 bool VisitParenExpr(const ParenExpr *PE) { return Visit(S: PE->getSubExpr()); }
1087
1088 bool VisitInitListExpr(const InitListExpr *ILE) {
1089 for (const Expr *Child : ILE->inits()) {
1090 if (Child && !Visit(S: Child))
1091 return false;
1092 }
1093 return true;
1094 }
1095
1096 bool VisitMemberExpr(const MemberExpr *ME) {
1097 // Field access is allowed but the base pointer may itself be non-trivial.
1098 return Visit(S: ME->getBase());
1099 }
1100
1101 bool VisitCXXThisExpr(const CXXThisExpr *CTE) {
1102 // The expression 'this' is always trivial, be it explicit or implicit.
1103 return true;
1104 }
1105
1106 bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) {
1107 // nullptr is trivial.
1108 return true;
1109 }
1110
1111 bool VisitDeclRefExpr(const DeclRefExpr *DRE) {
1112 // The use of a variable is trivial.
1113 return true;
1114 }
1115
1116 // Constant literal expressions are always trivial
1117 bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; }
1118 bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; }
1119 bool VisitFixedPointLiteral(const FixedPointLiteral *E) { return true; }
1120 bool VisitCharacterLiteral(const CharacterLiteral *E) { return true; }
1121 bool VisitStringLiteral(const StringLiteral *E) { return true; }
1122 bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E) { return true; }
1123
1124 bool VisitConstantExpr(const ConstantExpr *CE) {
1125 // Constant expressions are trivial.
1126 return true;
1127 }
1128
1129 bool VisitImplicitValueInitExpr(const ImplicitValueInitExpr *IVIE) {
1130 // An implicit value initialization is trvial.
1131 return true;
1132 }
1133
1134private:
1135 CacheTy &Cache;
1136 CacheTy FieldDtorCache;
1137 CacheTy RecursiveFn;
1138 const Stmt **OffendingStmt;
1139};
1140
1141bool TrivialFunctionAnalysis::isTrivialImpl(
1142 const Decl *D, TrivialFunctionAnalysis::CacheTy &Cache,
1143 const Stmt **OffendingStmt) {
1144 TrivialFunctionAnalysisVisitor V(Cache, OffendingStmt);
1145 return V.IsFunctionTrivial(D);
1146}
1147
1148bool TrivialFunctionAnalysis::isTrivialImpl(
1149 const Stmt *S, TrivialFunctionAnalysis::CacheTy &Cache,
1150 const Stmt **OffendingStmt) {
1151 TrivialFunctionAnalysisVisitor V(Cache, OffendingStmt);
1152 return V.IsStatementTrivial(S);
1153}
1154
1155bool TrivialFunctionAnalysis::hasTrivialDtorImpl(const VarDecl *VD,
1156 CacheTy &Cache) {
1157 TrivialFunctionAnalysisVisitor V(Cache);
1158 return V.HasTrivialDestructor(VD);
1159}
1160
1161} // namespace clang
1162