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