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