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/CXXInheritance.h" 12 #include "clang/AST/Decl.h" 13 #include "clang/AST/DeclCXX.h" 14 #include "clang/AST/ExprCXX.h" 15 #include "clang/AST/StmtVisitor.h" 16 #include <optional> 17 18 using namespace clang; 19 20 namespace { 21 22 bool hasPublicMethodInBaseClass(const CXXRecordDecl *R, StringRef NameToMatch) { 23 assert(R); 24 assert(R->hasDefinition()); 25 26 for (const CXXMethodDecl *MD : R->methods()) { 27 const auto MethodName = safeGetName(MD); 28 if (MethodName == NameToMatch && MD->getAccess() == AS_public) 29 return true; 30 } 31 return false; 32 } 33 34 } // namespace 35 36 namespace clang { 37 38 std::optional<const clang::CXXRecordDecl *> 39 hasPublicMethodInBase(const CXXBaseSpecifier *Base, StringRef NameToMatch) { 40 assert(Base); 41 42 const Type *T = Base->getType().getTypePtrOrNull(); 43 if (!T) 44 return std::nullopt; 45 46 const CXXRecordDecl *R = T->getAsCXXRecordDecl(); 47 if (!R) 48 return std::nullopt; 49 if (!R->hasDefinition()) 50 return std::nullopt; 51 52 return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr; 53 } 54 55 std::optional<bool> isSmartPtrCompatible(const CXXRecordDecl *R, 56 StringRef IncMethodName, 57 StringRef DecMethodName) { 58 assert(R); 59 60 R = R->getDefinition(); 61 if (!R) 62 return std::nullopt; 63 64 bool hasRef = hasPublicMethodInBaseClass(R, IncMethodName); 65 bool hasDeref = hasPublicMethodInBaseClass(R, DecMethodName); 66 if (hasRef && hasDeref) 67 return true; 68 69 CXXBasePaths Paths; 70 Paths.setOrigin(const_cast<CXXRecordDecl *>(R)); 71 72 bool AnyInconclusiveBase = false; 73 const auto hasPublicRefInBase = [&](const CXXBaseSpecifier *Base, 74 CXXBasePath &) { 75 auto hasRefInBase = clang::hasPublicMethodInBase(Base, IncMethodName); 76 if (!hasRefInBase) { 77 AnyInconclusiveBase = true; 78 return false; 79 } 80 return (*hasRefInBase) != nullptr; 81 }; 82 83 hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths, 84 /*LookupInDependent =*/true); 85 if (AnyInconclusiveBase) 86 return std::nullopt; 87 88 Paths.clear(); 89 const auto hasPublicDerefInBase = [&](const CXXBaseSpecifier *Base, 90 CXXBasePath &) { 91 auto hasDerefInBase = clang::hasPublicMethodInBase(Base, DecMethodName); 92 if (!hasDerefInBase) { 93 AnyInconclusiveBase = true; 94 return false; 95 } 96 return (*hasDerefInBase) != nullptr; 97 }; 98 hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths, 99 /*LookupInDependent =*/true); 100 if (AnyInconclusiveBase) 101 return std::nullopt; 102 103 return hasRef && hasDeref; 104 } 105 106 std::optional<bool> isRefCountable(const clang::CXXRecordDecl *R) { 107 return isSmartPtrCompatible(R, "ref", "deref"); 108 } 109 110 std::optional<bool> isCheckedPtrCapable(const clang::CXXRecordDecl *R) { 111 return isSmartPtrCompatible(R, "incrementCheckedPtrCount", 112 "decrementCheckedPtrCount"); 113 } 114 115 bool isRefType(const std::string &Name) { 116 return Name == "Ref" || Name == "RefAllowingPartiallyDestroyed" || 117 Name == "RefPtr" || Name == "RefPtrAllowingPartiallyDestroyed"; 118 } 119 120 bool isCheckedPtr(const std::string &Name) { 121 return Name == "CheckedPtr" || Name == "CheckedRef"; 122 } 123 124 bool isCtorOfRefCounted(const clang::FunctionDecl *F) { 125 assert(F); 126 const std::string &FunctionName = safeGetName(F); 127 128 return isRefType(FunctionName) || FunctionName == "adoptRef" || 129 FunctionName == "UniqueRef" || FunctionName == "makeUniqueRef" || 130 FunctionName == "makeUniqueRefWithoutFastMallocCheck" 131 132 || FunctionName == "String" || FunctionName == "AtomString" || 133 FunctionName == "UniqueString" 134 // FIXME: Implement as attribute. 135 || FunctionName == "Identifier"; 136 } 137 138 bool isCtorOfCheckedPtr(const clang::FunctionDecl *F) { 139 assert(F); 140 return isCheckedPtr(safeGetName(F)); 141 } 142 143 bool isCtorOfSafePtr(const clang::FunctionDecl *F) { 144 return isCtorOfRefCounted(F) || isCtorOfCheckedPtr(F); 145 } 146 147 template <typename Predicate> 148 static bool isPtrOfType(const clang::QualType T, Predicate Pred) { 149 QualType type = T; 150 while (!type.isNull()) { 151 if (auto *elaboratedT = type->getAs<ElaboratedType>()) { 152 type = elaboratedT->desugar(); 153 continue; 154 } 155 auto *SpecialT = type->getAs<TemplateSpecializationType>(); 156 if (!SpecialT) 157 return false; 158 auto *Decl = SpecialT->getTemplateName().getAsTemplateDecl(); 159 if (!Decl) 160 return false; 161 return Pred(Decl->getNameAsString()); 162 } 163 return false; 164 } 165 166 bool isSafePtrType(const clang::QualType T) { 167 return isPtrOfType( 168 T, [](auto Name) { return isRefType(Name) || isCheckedPtr(Name); }); 169 } 170 171 bool isOwnerPtrType(const clang::QualType T) { 172 return isPtrOfType(T, [](auto Name) { 173 return isRefType(Name) || isCheckedPtr(Name) || Name == "unique_ptr" || 174 Name == "UniqueRef" || Name == "LazyUniqueRef"; 175 }); 176 } 177 178 std::optional<bool> isUncounted(const QualType T) { 179 if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(T)) { 180 if (auto *Decl = Subst->getAssociatedDecl()) { 181 if (isRefType(safeGetName(Decl))) 182 return false; 183 } 184 } 185 return isUncounted(T->getAsCXXRecordDecl()); 186 } 187 188 std::optional<bool> isUnchecked(const QualType T) { 189 if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(T)) { 190 if (auto *Decl = Subst->getAssociatedDecl()) { 191 if (isCheckedPtr(safeGetName(Decl))) 192 return false; 193 } 194 } 195 return isUnchecked(T->getAsCXXRecordDecl()); 196 } 197 198 std::optional<bool> isUncounted(const CXXRecordDecl* Class) 199 { 200 // Keep isRefCounted first as it's cheaper. 201 if (!Class || isRefCounted(Class)) 202 return false; 203 204 std::optional<bool> IsRefCountable = isRefCountable(Class); 205 if (!IsRefCountable) 206 return std::nullopt; 207 208 return (*IsRefCountable); 209 } 210 211 std::optional<bool> isUnchecked(const CXXRecordDecl *Class) { 212 if (!Class || isCheckedPtr(Class)) 213 return false; // Cheaper than below 214 return isCheckedPtrCapable(Class); 215 } 216 217 std::optional<bool> isUncountedPtr(const QualType T) { 218 if (T->isPointerType() || T->isReferenceType()) { 219 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) 220 return isUncounted(CXXRD); 221 } 222 return false; 223 } 224 225 std::optional<bool> isUncheckedPtr(const QualType T) { 226 if (T->isPointerType() || T->isReferenceType()) { 227 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) 228 return isUnchecked(CXXRD); 229 } 230 return false; 231 } 232 233 std::optional<bool> isUnsafePtr(const QualType T) { 234 if (T->isPointerType() || T->isReferenceType()) { 235 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) { 236 auto isUncountedPtr = isUncounted(CXXRD); 237 auto isUncheckedPtr = isUnchecked(CXXRD); 238 if (isUncountedPtr && isUncheckedPtr) 239 return *isUncountedPtr || *isUncheckedPtr; 240 if (isUncountedPtr) 241 return *isUncountedPtr; 242 return isUncheckedPtr; 243 } 244 } 245 return false; 246 } 247 248 std::optional<bool> isGetterOfSafePtr(const CXXMethodDecl *M) { 249 assert(M); 250 251 if (isa<CXXMethodDecl>(M)) { 252 const CXXRecordDecl *calleeMethodsClass = M->getParent(); 253 auto className = safeGetName(calleeMethodsClass); 254 auto method = safeGetName(M); 255 256 if (isCheckedPtr(className) && (method == "get" || method == "ptr")) 257 return true; 258 259 if ((isRefType(className) && (method == "get" || method == "ptr")) || 260 ((className == "String" || className == "AtomString" || 261 className == "AtomStringImpl" || className == "UniqueString" || 262 className == "UniqueStringImpl" || className == "Identifier") && 263 method == "impl")) 264 return true; 265 266 // Ref<T> -> T conversion 267 // FIXME: Currently allowing any Ref<T> -> whatever cast. 268 if (isRefType(className)) { 269 if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) 270 return isUnsafePtr(maybeRefToRawOperator->getConversionType()); 271 } 272 273 if (isCheckedPtr(className)) { 274 if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) 275 return isUnsafePtr(maybeRefToRawOperator->getConversionType()); 276 } 277 } 278 return false; 279 } 280 281 bool isRefCounted(const CXXRecordDecl *R) { 282 assert(R); 283 if (auto *TmplR = R->getTemplateInstantiationPattern()) { 284 // FIXME: String/AtomString/UniqueString 285 const auto &ClassName = safeGetName(TmplR); 286 return isRefType(ClassName); 287 } 288 return false; 289 } 290 291 bool isCheckedPtr(const CXXRecordDecl *R) { 292 assert(R); 293 if (auto *TmplR = R->getTemplateInstantiationPattern()) { 294 const auto &ClassName = safeGetName(TmplR); 295 return isCheckedPtr(ClassName); 296 } 297 return false; 298 } 299 300 bool isPtrConversion(const FunctionDecl *F) { 301 assert(F); 302 if (isCtorOfRefCounted(F)) 303 return true; 304 305 // FIXME: check # of params == 1 306 const auto FunctionName = safeGetName(F); 307 if (FunctionName == "getPtr" || FunctionName == "WeakPtr" || 308 FunctionName == "dynamicDowncast" || FunctionName == "downcast" || 309 FunctionName == "checkedDowncast" || 310 FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast") 311 return true; 312 313 return false; 314 } 315 316 bool isSingleton(const FunctionDecl *F) { 317 assert(F); 318 // FIXME: check # of params == 1 319 if (auto *MethodDecl = dyn_cast<CXXMethodDecl>(F)) { 320 if (!MethodDecl->isStatic()) 321 return false; 322 } 323 const auto &NameStr = safeGetName(F); 324 StringRef Name = NameStr; // FIXME: Make safeGetName return StringRef. 325 return Name == "singleton" || Name.ends_with("Singleton"); 326 } 327 328 // We only care about statements so let's use the simple 329 // (non-recursive) visitor. 330 class TrivialFunctionAnalysisVisitor 331 : public ConstStmtVisitor<TrivialFunctionAnalysisVisitor, bool> { 332 333 // Returns false if at least one child is non-trivial. 334 bool VisitChildren(const Stmt *S) { 335 for (const Stmt *Child : S->children()) { 336 if (Child && !Visit(Child)) 337 return false; 338 } 339 340 return true; 341 } 342 343 template <typename StmtOrDecl, typename CheckFunction> 344 bool WithCachedResult(const StmtOrDecl *S, CheckFunction Function) { 345 auto CacheIt = Cache.find(S); 346 if (CacheIt != Cache.end()) 347 return CacheIt->second; 348 349 // Treat a recursive statement to be trivial until proven otherwise. 350 auto [RecursiveIt, IsNew] = RecursiveFn.insert(std::make_pair(S, true)); 351 if (!IsNew) 352 return RecursiveIt->second; 353 354 bool Result = Function(); 355 356 if (!Result) { 357 for (auto &It : RecursiveFn) 358 It.second = false; 359 } 360 RecursiveIt = RecursiveFn.find(S); 361 assert(RecursiveIt != RecursiveFn.end()); 362 Result = RecursiveIt->second; 363 RecursiveFn.erase(RecursiveIt); 364 Cache[S] = Result; 365 366 return Result; 367 } 368 369 public: 370 using CacheTy = TrivialFunctionAnalysis::CacheTy; 371 372 TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {} 373 374 bool IsFunctionTrivial(const Decl *D) { 375 return WithCachedResult(D, [&]() { 376 if (auto *CtorDecl = dyn_cast<CXXConstructorDecl>(D)) { 377 for (auto *CtorInit : CtorDecl->inits()) { 378 if (!Visit(CtorInit->getInit())) 379 return false; 380 } 381 } 382 const Stmt *Body = D->getBody(); 383 if (!Body) 384 return false; 385 return Visit(Body); 386 }); 387 } 388 389 bool VisitStmt(const Stmt *S) { 390 // All statements are non-trivial unless overriden later. 391 // Don't even recurse into children by default. 392 return false; 393 } 394 395 bool VisitCompoundStmt(const CompoundStmt *CS) { 396 // A compound statement is allowed as long each individual sub-statement 397 // is trivial. 398 return WithCachedResult(CS, [&]() { return VisitChildren(CS); }); 399 } 400 401 bool VisitReturnStmt(const ReturnStmt *RS) { 402 // A return statement is allowed as long as the return value is trivial. 403 if (auto *RV = RS->getRetValue()) 404 return Visit(RV); 405 return true; 406 } 407 408 bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); } 409 bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); } 410 bool VisitIfStmt(const IfStmt *IS) { 411 return WithCachedResult(IS, [&]() { return VisitChildren(IS); }); 412 } 413 bool VisitForStmt(const ForStmt *FS) { 414 return WithCachedResult(FS, [&]() { return VisitChildren(FS); }); 415 } 416 bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) { 417 return WithCachedResult(FS, [&]() { return VisitChildren(FS); }); 418 } 419 bool VisitWhileStmt(const WhileStmt *WS) { 420 return WithCachedResult(WS, [&]() { return VisitChildren(WS); }); 421 } 422 bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); } 423 bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); } 424 bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); } 425 426 // break, continue, goto, and label statements are always trivial. 427 bool VisitBreakStmt(const BreakStmt *) { return true; } 428 bool VisitContinueStmt(const ContinueStmt *) { return true; } 429 bool VisitGotoStmt(const GotoStmt *) { return true; } 430 bool VisitLabelStmt(const LabelStmt *) { return true; } 431 432 bool VisitUnaryOperator(const UnaryOperator *UO) { 433 // Unary operators are trivial if its operand is trivial except co_await. 434 return UO->getOpcode() != UO_Coawait && Visit(UO->getSubExpr()); 435 } 436 437 bool VisitBinaryOperator(const BinaryOperator *BO) { 438 // Binary operators are trivial if their operands are trivial. 439 return Visit(BO->getLHS()) && Visit(BO->getRHS()); 440 } 441 442 bool VisitCompoundAssignOperator(const CompoundAssignOperator *CAO) { 443 // Compound assignment operator such as |= is trivial if its 444 // subexpresssions are trivial. 445 return VisitChildren(CAO); 446 } 447 448 bool VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE) { 449 return VisitChildren(ASE); 450 } 451 452 bool VisitConditionalOperator(const ConditionalOperator *CO) { 453 // Ternary operators are trivial if their conditions & values are trivial. 454 return VisitChildren(CO); 455 } 456 457 bool VisitAtomicExpr(const AtomicExpr *E) { return VisitChildren(E); } 458 459 bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) { 460 // Any static_assert is considered trivial. 461 return true; 462 } 463 464 bool VisitCallExpr(const CallExpr *CE) { 465 if (!checkArguments(CE)) 466 return false; 467 468 auto *Callee = CE->getDirectCallee(); 469 if (!Callee) 470 return false; 471 const auto &Name = safeGetName(Callee); 472 473 if (Callee->isInStdNamespace() && 474 (Name == "addressof" || Name == "forward" || Name == "move")) 475 return true; 476 477 if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" || 478 Name == "WTFReportBacktrace" || 479 Name == "WTFCrashWithSecurityImplication" || Name == "WTFCrash" || 480 Name == "WTFReportAssertionFailure" || Name == "isMainThread" || 481 Name == "isMainThreadOrGCThread" || Name == "isMainRunLoop" || 482 Name == "isWebThread" || Name == "isUIThread" || 483 Name == "mayBeGCThread" || Name == "compilerFenceForCrash" || 484 Name == "bitwise_cast" || Name.find("__builtin") == 0 || 485 Name == "__libcpp_verbose_abort") 486 return true; 487 488 return IsFunctionTrivial(Callee); 489 } 490 491 bool 492 VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E) { 493 // Non-type template paramter is compile time constant and trivial. 494 return true; 495 } 496 497 bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) { 498 return VisitChildren(E); 499 } 500 501 bool VisitPredefinedExpr(const PredefinedExpr *E) { 502 // A predefined identifier such as "func" is considered trivial. 503 return true; 504 } 505 506 bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) { 507 if (!checkArguments(MCE)) 508 return false; 509 510 bool TrivialThis = Visit(MCE->getImplicitObjectArgument()); 511 if (!TrivialThis) 512 return false; 513 514 auto *Callee = MCE->getMethodDecl(); 515 if (!Callee) 516 return false; 517 518 auto Name = safeGetName(Callee); 519 if (Name == "ref" || Name == "incrementCheckedPtrCount") 520 return true; 521 522 std::optional<bool> IsGetterOfRefCounted = isGetterOfSafePtr(Callee); 523 if (IsGetterOfRefCounted && *IsGetterOfRefCounted) 524 return true; 525 526 // Recursively descend into the callee to confirm that it's trivial as well. 527 return IsFunctionTrivial(Callee); 528 } 529 530 bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) { 531 if (!checkArguments(OCE)) 532 return false; 533 auto *Callee = OCE->getCalleeDecl(); 534 if (!Callee) 535 return false; 536 // Recursively descend into the callee to confirm that it's trivial as well. 537 return IsFunctionTrivial(Callee); 538 } 539 540 bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { 541 if (auto *Expr = E->getExpr()) { 542 if (!Visit(Expr)) 543 return false; 544 } 545 return true; 546 } 547 548 bool checkArguments(const CallExpr *CE) { 549 for (const Expr *Arg : CE->arguments()) { 550 if (Arg && !Visit(Arg)) 551 return false; 552 } 553 return true; 554 } 555 556 bool VisitCXXConstructExpr(const CXXConstructExpr *CE) { 557 for (const Expr *Arg : CE->arguments()) { 558 if (Arg && !Visit(Arg)) 559 return false; 560 } 561 562 // Recursively descend into the callee to confirm that it's trivial. 563 return IsFunctionTrivial(CE->getConstructor()); 564 } 565 566 bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E) { 567 return IsFunctionTrivial(E->getConstructor()); 568 } 569 570 bool VisitCXXNewExpr(const CXXNewExpr *NE) { return VisitChildren(NE); } 571 572 bool VisitImplicitCastExpr(const ImplicitCastExpr *ICE) { 573 return Visit(ICE->getSubExpr()); 574 } 575 576 bool VisitExplicitCastExpr(const ExplicitCastExpr *ECE) { 577 return Visit(ECE->getSubExpr()); 578 } 579 580 bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT) { 581 return Visit(VMT->getSubExpr()); 582 } 583 584 bool VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE) { 585 if (auto *Temp = BTE->getTemporary()) { 586 if (!TrivialFunctionAnalysis::isTrivialImpl(Temp->getDestructor(), Cache)) 587 return false; 588 } 589 return Visit(BTE->getSubExpr()); 590 } 591 592 bool VisitExprWithCleanups(const ExprWithCleanups *EWC) { 593 return Visit(EWC->getSubExpr()); 594 } 595 596 bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); } 597 598 bool VisitInitListExpr(const InitListExpr *ILE) { 599 for (const Expr *Child : ILE->inits()) { 600 if (Child && !Visit(Child)) 601 return false; 602 } 603 return true; 604 } 605 606 bool VisitMemberExpr(const MemberExpr *ME) { 607 // Field access is allowed but the base pointer may itself be non-trivial. 608 return Visit(ME->getBase()); 609 } 610 611 bool VisitCXXThisExpr(const CXXThisExpr *CTE) { 612 // The expression 'this' is always trivial, be it explicit or implicit. 613 return true; 614 } 615 616 bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) { 617 // nullptr is trivial. 618 return true; 619 } 620 621 bool VisitDeclRefExpr(const DeclRefExpr *DRE) { 622 // The use of a variable is trivial. 623 return true; 624 } 625 626 // Constant literal expressions are always trivial 627 bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; } 628 bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; } 629 bool VisitFixedPointLiteral(const FixedPointLiteral *E) { return true; } 630 bool VisitCharacterLiteral(const CharacterLiteral *E) { return true; } 631 bool VisitStringLiteral(const StringLiteral *E) { return true; } 632 bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E) { return true; } 633 634 bool VisitConstantExpr(const ConstantExpr *CE) { 635 // Constant expressions are trivial. 636 return true; 637 } 638 639 private: 640 CacheTy &Cache; 641 CacheTy RecursiveFn; 642 }; 643 644 bool TrivialFunctionAnalysis::isTrivialImpl( 645 const Decl *D, TrivialFunctionAnalysis::CacheTy &Cache) { 646 TrivialFunctionAnalysisVisitor V(Cache); 647 return V.IsFunctionTrivial(D); 648 } 649 650 bool TrivialFunctionAnalysis::isTrivialImpl( 651 const Stmt *S, TrivialFunctionAnalysis::CacheTy &Cache) { 652 TrivialFunctionAnalysisVisitor V(Cache); 653 bool Result = V.Visit(S); 654 assert(Cache.contains(S) && "Top-level statement not properly cached!"); 655 return Result; 656 } 657 658 } // namespace clang 659