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