1259 lines · cpp
1//===-- DataflowEnvironment.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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines an Environment class that is used by dataflow analyses10// that run over Control-Flow Graphs (CFGs) to keep track of the state of the11// program at given program points.12//13//===----------------------------------------------------------------------===//14 15#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"16#include "clang/AST/Decl.h"17#include "clang/AST/DeclCXX.h"18#include "clang/AST/ExprCXX.h"19#include "clang/AST/Stmt.h"20#include "clang/AST/Type.h"21#include "clang/Analysis/FlowSensitive/ASTOps.h"22#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"23#include "clang/Analysis/FlowSensitive/DataflowLattice.h"24#include "clang/Analysis/FlowSensitive/Value.h"25#include "llvm/ADT/DenseMap.h"26#include "llvm/ADT/DenseSet.h"27#include "llvm/ADT/MapVector.h"28#include "llvm/ADT/STLExtras.h"29#include "llvm/ADT/ScopeExit.h"30#include "llvm/Support/ErrorHandling.h"31#include <cassert>32#include <memory>33#include <utility>34 35#define DEBUG_TYPE "dataflow"36 37namespace clang {38namespace dataflow {39 40// FIXME: convert these to parameters of the analysis or environment. Current41// settings have been experimentaly validated, but only for a particular42// analysis.43static constexpr int MaxCompositeValueDepth = 3;44static constexpr int MaxCompositeValueSize = 1000;45 46/// Returns a map consisting of key-value entries that are present in both maps.47static llvm::DenseMap<const ValueDecl *, StorageLocation *> intersectDeclToLoc(48 const llvm::DenseMap<const ValueDecl *, StorageLocation *> &DeclToLoc1,49 const llvm::DenseMap<const ValueDecl *, StorageLocation *> &DeclToLoc2) {50 llvm::DenseMap<const ValueDecl *, StorageLocation *> Result;51 for (auto &Entry : DeclToLoc1) {52 auto It = DeclToLoc2.find(Entry.first);53 if (It != DeclToLoc2.end() && Entry.second == It->second)54 Result.insert({Entry.first, Entry.second});55 }56 return Result;57}58 59// Performs a join on either `ExprToLoc` or `ExprToVal`.60// The maps must be consistent in the sense that any entries for the same61// expression must map to the same location / value. This is the case if we are62// performing a join for control flow within a full-expression (which is the63// only case when this function should be used).64template <typename MapT>65static MapT joinExprMaps(const MapT &Map1, const MapT &Map2) {66 MapT Result = Map1;67 68 for (const auto &Entry : Map2) {69 [[maybe_unused]] auto [It, Inserted] = Result.insert(Entry);70 // If there was an existing entry, its value should be the same as for the71 // entry we were trying to insert.72 assert(It->second == Entry.second);73 }74 75 return Result;76}77 78// Whether to consider equivalent two values with an unknown relation.79//80// FIXME: this function is a hack enabling unsoundness to support81// convergence. Once we have widening support for the reference/pointer and82// struct built-in models, this should be unconditionally `false` (and inlined83// as such at its call sites).84static bool equateUnknownValues(Value::Kind K) {85 switch (K) {86 case Value::Kind::Integer:87 case Value::Kind::Pointer:88 return true;89 default:90 return false;91 }92}93 94static bool compareDistinctValues(QualType Type, Value &Val1,95 const Environment &Env1, Value &Val2,96 const Environment &Env2,97 Environment::ValueModel &Model) {98 // Note: Potentially costly, but, for booleans, we could check whether both99 // can be proven equivalent in their respective environments.100 101 // FIXME: move the reference/pointers logic from `areEquivalentValues` to here102 // and implement separate, join/widen specific handling for103 // reference/pointers.104 switch (Model.compare(Type, Val1, Env1, Val2, Env2)) {105 case ComparisonResult::Same:106 return true;107 case ComparisonResult::Different:108 return false;109 case ComparisonResult::Unknown:110 return equateUnknownValues(Val1.getKind());111 }112 llvm_unreachable("All cases covered in switch");113}114 115/// Attempts to join distinct values `Val1` and `Val2` in `Env1` and `Env2`,116/// respectively, of the same type `Type`. Joining generally produces a single117/// value that (soundly) approximates the two inputs, although the actual118/// meaning depends on `Model`.119static Value *joinDistinctValues(QualType Type, Value &Val1,120 const Environment &Env1, Value &Val2,121 const Environment &Env2,122 Environment &JoinedEnv,123 Environment::ValueModel &Model) {124 // Join distinct boolean values preserving information about the constraints125 // in the respective path conditions.126 if (isa<BoolValue>(&Val1) && isa<BoolValue>(&Val2)) {127 // FIXME: Checking both values should be unnecessary, since they should have128 // a consistent shape. However, right now we can end up with BoolValue's in129 // integer-typed variables due to our incorrect handling of130 // boolean-to-integer casts (we just propagate the BoolValue to the result131 // of the cast). So, a join can encounter an integer in one branch but a132 // bool in the other.133 // For example:134 // ```135 // std::optional<bool> o;136 // int x;137 // if (o.has_value())138 // x = o.value();139 // ```140 auto &Expr1 = cast<BoolValue>(Val1).formula();141 auto &Expr2 = cast<BoolValue>(Val2).formula();142 auto &A = JoinedEnv.arena();143 auto &JoinedVal = A.makeAtomRef(A.makeAtom());144 JoinedEnv.assume(145 A.makeOr(A.makeAnd(A.makeAtomRef(Env1.getFlowConditionToken()),146 A.makeEquals(JoinedVal, Expr1)),147 A.makeAnd(A.makeAtomRef(Env2.getFlowConditionToken()),148 A.makeEquals(JoinedVal, Expr2))));149 return &A.makeBoolValue(JoinedVal);150 }151 152 Value *JoinedVal = JoinedEnv.createValue(Type);153 if (JoinedVal)154 Model.join(Type, Val1, Env1, Val2, Env2, *JoinedVal, JoinedEnv);155 156 return JoinedVal;157}158 159static WidenResult widenDistinctValues(QualType Type, Value &Prev,160 const Environment &PrevEnv,161 Value &Current, Environment &CurrentEnv,162 Environment::ValueModel &Model) {163 // Boolean-model widening.164 if (isa<BoolValue>(Prev) && isa<BoolValue>(Current)) {165 // FIXME: Checking both values should be unnecessary, but we can currently166 // end up with `BoolValue`s in integer-typed variables. See comment in167 // `joinDistinctValues()` for details.168 auto &PrevBool = cast<BoolValue>(Prev);169 auto &CurBool = cast<BoolValue>(Current);170 171 if (isa<TopBoolValue>(Prev))172 // Safe to return `Prev` here, because Top is never dependent on the173 // environment.174 return {&Prev, LatticeEffect::Unchanged};175 176 // We may need to widen to Top, but before we do so, check whether both177 // values are implied to be either true or false in the current environment.178 // In that case, we can simply return a literal instead.179 bool TruePrev = PrevEnv.proves(PrevBool.formula());180 bool TrueCur = CurrentEnv.proves(CurBool.formula());181 if (TruePrev && TrueCur)182 return {&CurrentEnv.getBoolLiteralValue(true), LatticeEffect::Unchanged};183 if (!TruePrev && !TrueCur &&184 PrevEnv.proves(PrevEnv.arena().makeNot(PrevBool.formula())) &&185 CurrentEnv.proves(CurrentEnv.arena().makeNot(CurBool.formula())))186 return {&CurrentEnv.getBoolLiteralValue(false), LatticeEffect::Unchanged};187 188 return {&CurrentEnv.makeTopBoolValue(), LatticeEffect::Changed};189 }190 191 // FIXME: Add other built-in model widening.192 193 // Custom-model widening.194 if (auto Result = Model.widen(Type, Prev, PrevEnv, Current, CurrentEnv))195 return *Result;196 197 return {&Current, equateUnknownValues(Prev.getKind())198 ? LatticeEffect::Unchanged199 : LatticeEffect::Changed};200}201 202// Returns whether the values in `Map1` and `Map2` compare equal for those203// keys that `Map1` and `Map2` have in common.204template <typename Key>205static bool compareKeyToValueMaps(const llvm::MapVector<Key, Value *> &Map1,206 const llvm::MapVector<Key, Value *> &Map2,207 const Environment &Env1,208 const Environment &Env2,209 Environment::ValueModel &Model) {210 for (auto &Entry : Map1) {211 Key K = Entry.first;212 assert(K != nullptr);213 214 Value *Val = Entry.second;215 assert(Val != nullptr);216 217 auto It = Map2.find(K);218 if (It == Map2.end())219 continue;220 assert(It->second != nullptr);221 222 if (!areEquivalentValues(*Val, *It->second) &&223 !compareDistinctValues(K->getType(), *Val, Env1, *It->second, Env2,224 Model))225 return false;226 }227 228 return true;229}230 231// Perform a join on two `LocToVal` maps.232static llvm::MapVector<const StorageLocation *, Value *>233joinLocToVal(const llvm::MapVector<const StorageLocation *, Value *> &LocToVal,234 const llvm::MapVector<const StorageLocation *, Value *> &LocToVal2,235 const Environment &Env1, const Environment &Env2,236 Environment &JoinedEnv, Environment::ValueModel &Model) {237 llvm::MapVector<const StorageLocation *, Value *> Result;238 for (auto &Entry : LocToVal) {239 const StorageLocation *Loc = Entry.first;240 assert(Loc != nullptr);241 242 Value *Val = Entry.second;243 assert(Val != nullptr);244 245 auto It = LocToVal2.find(Loc);246 if (It == LocToVal2.end())247 continue;248 assert(It->second != nullptr);249 250 if (Value *JoinedVal = Environment::joinValues(251 Loc->getType(), Val, Env1, It->second, Env2, JoinedEnv, Model)) {252 Result.insert({Loc, JoinedVal});253 }254 }255 256 return Result;257}258 259// Perform widening on either `LocToVal` or `ExprToVal`. `Key` must be either260// `const StorageLocation *` or `const Expr *`.261template <typename Key>262static llvm::MapVector<Key, Value *>263widenKeyToValueMap(const llvm::MapVector<Key, Value *> &CurMap,264 const llvm::MapVector<Key, Value *> &PrevMap,265 Environment &CurEnv, const Environment &PrevEnv,266 Environment::ValueModel &Model, LatticeEffect &Effect) {267 llvm::MapVector<Key, Value *> WidenedMap;268 for (auto &Entry : CurMap) {269 Key K = Entry.first;270 assert(K != nullptr);271 272 Value *Val = Entry.second;273 assert(Val != nullptr);274 275 auto PrevIt = PrevMap.find(K);276 if (PrevIt == PrevMap.end())277 continue;278 assert(PrevIt->second != nullptr);279 280 if (areEquivalentValues(*Val, *PrevIt->second)) {281 WidenedMap.insert({K, Val});282 continue;283 }284 285 auto [WidenedVal, ValEffect] = widenDistinctValues(286 K->getType(), *PrevIt->second, PrevEnv, *Val, CurEnv, Model);287 WidenedMap.insert({K, WidenedVal});288 if (ValEffect == LatticeEffect::Changed)289 Effect = LatticeEffect::Changed;290 }291 292 return WidenedMap;293}294 295namespace {296 297// Visitor that builds a map from record prvalues to result objects.298// For each result object that it encounters, it propagates the storage location299// of the result object to all record prvalues that can initialize it.300class ResultObjectVisitor : public AnalysisASTVisitor {301public:302 // `ResultObjectMap` will be filled with a map from record prvalues to result303 // object. If this visitor will traverse a function that returns a record by304 // value, `LocForRecordReturnVal` is the location to which this record should305 // be written; otherwise, it is null.306 explicit ResultObjectVisitor(307 llvm::DenseMap<const Expr *, RecordStorageLocation *> &ResultObjectMap,308 RecordStorageLocation *LocForRecordReturnVal,309 DataflowAnalysisContext &DACtx)310 : ResultObjectMap(ResultObjectMap),311 LocForRecordReturnVal(LocForRecordReturnVal), DACtx(DACtx) {}312 313 // Traverse all member and base initializers of `Ctor`. This function is not314 // called by `RecursiveASTVisitor`; it should be called manually if we are315 // analyzing a constructor. `ThisPointeeLoc` is the storage location that316 // `this` points to.317 void traverseConstructorInits(const CXXConstructorDecl *Ctor,318 RecordStorageLocation *ThisPointeeLoc) {319 assert(ThisPointeeLoc != nullptr);320 for (const CXXCtorInitializer *Init : Ctor->inits()) {321 Expr *InitExpr = Init->getInit();322 if (FieldDecl *Field = Init->getMember();323 Field != nullptr && Field->getType()->isRecordType()) {324 PropagateResultObject(InitExpr, cast<RecordStorageLocation>(325 ThisPointeeLoc->getChild(*Field)));326 } else if (Init->getBaseClass()) {327 PropagateResultObject(InitExpr, ThisPointeeLoc);328 }329 330 // Ensure that any result objects within `InitExpr` (e.g. temporaries)331 // are also propagated to the prvalues that initialize them.332 TraverseStmt(InitExpr);333 334 // If this is a `CXXDefaultInitExpr`, also propagate any result objects335 // within the default expression.336 if (auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(InitExpr))337 TraverseStmt(DefaultInit->getExpr());338 }339 }340 341 bool VisitVarDecl(VarDecl *VD) override {342 if (VD->getType()->isRecordType() && VD->hasInit())343 PropagateResultObject(344 VD->getInit(),345 &cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*VD)));346 return true;347 }348 349 bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE) override {350 if (MTE->getType()->isRecordType())351 PropagateResultObject(352 MTE->getSubExpr(),353 &cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*MTE)));354 return true;355 }356 357 bool VisitReturnStmt(ReturnStmt *Return) override {358 Expr *RetValue = Return->getRetValue();359 if (RetValue != nullptr && RetValue->getType()->isRecordType() &&360 RetValue->isPRValue())361 PropagateResultObject(RetValue, LocForRecordReturnVal);362 return true;363 }364 365 bool VisitExpr(Expr *E) override {366 // Clang's AST can have record-type prvalues without a result object -- for367 // example as full-expressions contained in a compound statement or as368 // arguments of call expressions. We notice this if we get here and a369 // storage location has not yet been associated with `E`. In this case,370 // treat this as if it was a `MaterializeTemporaryExpr`.371 if (E->isPRValue() && E->getType()->isRecordType() &&372 !ResultObjectMap.contains(E))373 PropagateResultObject(374 E, &cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*E)));375 return true;376 }377 378 void379 PropagateResultObjectToRecordInitList(const RecordInitListHelper &InitList,380 RecordStorageLocation *Loc) {381 for (auto [Base, Init] : InitList.base_inits()) {382 assert(Base->getType().getCanonicalType() ==383 Init->getType().getCanonicalType());384 385 // Storage location for the base class is the same as that of the386 // derived class because we "flatten" the object hierarchy and put all387 // fields in `RecordStorageLocation` of the derived class.388 PropagateResultObject(Init, Loc);389 }390 391 for (auto [Field, Init] : InitList.field_inits()) {392 // Fields of non-record type are handled in393 // `TransferVisitor::VisitInitListExpr()`.394 if (Field->getType()->isRecordType())395 PropagateResultObject(396 Init, cast<RecordStorageLocation>(Loc->getChild(*Field)));397 }398 }399 400 // Assigns `Loc` as the result object location of `E`, then propagates the401 // location to all lower-level prvalues that initialize the same object as402 // `E` (or one of its base classes or member variables).403 void PropagateResultObject(Expr *E, RecordStorageLocation *Loc) {404 if (!E->isPRValue() || !E->getType()->isRecordType()) {405 assert(false);406 // Ensure we don't propagate the result object if we hit this in a407 // release build.408 return;409 }410 411 ResultObjectMap[E] = Loc;412 413 // The following AST node kinds are "original initializers": They are the414 // lowest-level AST node that initializes a given object, and nothing415 // below them can initialize the same object (or part of it).416 if (isa<CXXConstructExpr>(E) || isa<CallExpr>(E) || isa<LambdaExpr>(E) ||417 isa<CXXDefaultArgExpr>(E) || isa<CXXStdInitializerListExpr>(E) ||418 isa<AtomicExpr>(E) || isa<CXXInheritedCtorInitExpr>(E) ||419 // We treat `BuiltinBitCastExpr` as an "original initializer" too as420 // it may not even be casting from a record type -- and even if it is,421 // the two objects are in general of unrelated type.422 isa<BuiltinBitCastExpr>(E)) {423 return;424 }425 if (auto *Op = dyn_cast<BinaryOperator>(E);426 Op && Op->getOpcode() == BO_Cmp) {427 // Builtin `<=>` returns a `std::strong_ordering` object.428 return;429 }430 431 if (auto *InitList = dyn_cast<InitListExpr>(E)) {432 if (!InitList->isSemanticForm())433 return;434 if (InitList->isTransparent()) {435 PropagateResultObject(InitList->getInit(0), Loc);436 return;437 }438 439 PropagateResultObjectToRecordInitList(RecordInitListHelper(InitList),440 Loc);441 return;442 }443 444 if (auto *ParenInitList = dyn_cast<CXXParenListInitExpr>(E)) {445 PropagateResultObjectToRecordInitList(RecordInitListHelper(ParenInitList),446 Loc);447 return;448 }449 450 if (auto *Op = dyn_cast<BinaryOperator>(E); Op && Op->isCommaOp()) {451 PropagateResultObject(Op->getRHS(), Loc);452 return;453 }454 455 if (auto *Cond = dyn_cast<AbstractConditionalOperator>(E)) {456 PropagateResultObject(Cond->getTrueExpr(), Loc);457 PropagateResultObject(Cond->getFalseExpr(), Loc);458 return;459 }460 461 if (auto *SE = dyn_cast<StmtExpr>(E)) {462 PropagateResultObject(cast<Expr>(SE->getSubStmt()->body_back()), Loc);463 return;464 }465 466 if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(E)) {467 PropagateResultObject(DIE->getExpr(), Loc);468 return;469 }470 471 // All other expression nodes that propagate a record prvalue should have472 // exactly one child.473 SmallVector<Stmt *, 1> Children(E->child_begin(), E->child_end());474 LLVM_DEBUG({475 if (Children.size() != 1)476 E->dump();477 });478 assert(Children.size() == 1);479 for (Stmt *S : Children)480 PropagateResultObject(cast<Expr>(S), Loc);481 }482 483private:484 llvm::DenseMap<const Expr *, RecordStorageLocation *> &ResultObjectMap;485 RecordStorageLocation *LocForRecordReturnVal;486 DataflowAnalysisContext &DACtx;487};488 489} // namespace490 491void Environment::initialize() {492 if (InitialTargetStmt == nullptr)493 return;494 495 if (InitialTargetFunc == nullptr) {496 initFieldsGlobalsAndFuncs(getReferencedDecls(*InitialTargetStmt));497 ResultObjectMap =498 std::make_shared<PrValueToResultObject>(buildResultObjectMap(499 DACtx, InitialTargetStmt, getThisPointeeStorageLocation(),500 /*LocForRecordReturnValue=*/nullptr));501 return;502 }503 504 initFieldsGlobalsAndFuncs(getReferencedDecls(*InitialTargetFunc));505 506 for (const auto *ParamDecl : InitialTargetFunc->parameters()) {507 assert(ParamDecl != nullptr);508 setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr));509 }510 511 if (InitialTargetFunc->getReturnType()->isRecordType())512 LocForRecordReturnVal = &cast<RecordStorageLocation>(513 createStorageLocation(InitialTargetFunc->getReturnType()));514 515 if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(InitialTargetFunc)) {516 auto *Parent = MethodDecl->getParent();517 assert(Parent != nullptr);518 519 if (Parent->isLambda()) {520 for (const auto &Capture : Parent->captures()) {521 if (Capture.capturesVariable()) {522 const auto *VarDecl = Capture.getCapturedVar();523 assert(VarDecl != nullptr);524 setStorageLocation(*VarDecl, createObject(*VarDecl, nullptr));525 } else if (Capture.capturesThis()) {526 if (auto *Ancestor = InitialTargetFunc->getNonClosureAncestor()) {527 const auto *SurroundingMethodDecl = cast<CXXMethodDecl>(Ancestor);528 QualType ThisPointeeType =529 SurroundingMethodDecl->getFunctionObjectParameterType();530 setThisPointeeStorageLocation(531 cast<RecordStorageLocation>(createObject(ThisPointeeType)));532 } else if (auto *FieldBeingInitialized =533 dyn_cast<FieldDecl>(Parent->getLambdaContextDecl())) {534 // This is in a field initializer, rather than a method.535 const RecordDecl *RD = FieldBeingInitialized->getParent();536 const ASTContext &Ctx = RD->getASTContext();537 CanQualType T = Ctx.getCanonicalTagType(RD);538 setThisPointeeStorageLocation(539 cast<RecordStorageLocation>(createObject(T)));540 } else {541 assert(false && "Unexpected this-capturing lambda context.");542 }543 }544 }545 } else if (MethodDecl->isImplicitObjectMemberFunction()) {546 QualType ThisPointeeType = MethodDecl->getFunctionObjectParameterType();547 auto &ThisLoc =548 cast<RecordStorageLocation>(createStorageLocation(ThisPointeeType));549 setThisPointeeStorageLocation(ThisLoc);550 // Initialize fields of `*this` with values, but only if we're not551 // analyzing a constructor; after all, it's the constructor's job to do552 // this (and we want to be able to test that).553 if (!isa<CXXConstructorDecl>(MethodDecl))554 initializeFieldsWithValues(ThisLoc);555 }556 }557 558 // We do this below the handling of `CXXMethodDecl` above so that we can559 // be sure that the storage location for `this` has been set.560 ResultObjectMap =561 std::make_shared<PrValueToResultObject>(buildResultObjectMap(562 DACtx, InitialTargetFunc, getThisPointeeStorageLocation(),563 LocForRecordReturnVal));564}565 566// FIXME: Add support for resetting globals after function calls to enable the567// implementation of sound analyses.568 569void Environment::initFieldsGlobalsAndFuncs(const ReferencedDecls &Referenced) {570 // These have to be added before the lines that follow to ensure that571 // `create*` work correctly for structs.572 DACtx->addModeledFields(Referenced.Fields);573 574 for (const VarDecl *D : Referenced.Globals) {575 if (getStorageLocation(*D) != nullptr)576 continue;577 578 // We don't run transfer functions on the initializers of global variables,579 // so they won't be associated with a value or storage location. We580 // therefore intentionally don't pass an initializer to `createObject()`; in581 // particular, this ensures that `createObject()` will initialize the fields582 // of record-type variables with values.583 setStorageLocation(*D, createObject(*D, nullptr));584 }585 586 for (const FunctionDecl *FD : Referenced.Functions) {587 if (getStorageLocation(*FD) != nullptr)588 continue;589 auto &Loc = createStorageLocation(*FD);590 setStorageLocation(*FD, Loc);591 }592}593 594Environment Environment::fork() const {595 Environment Copy(*this);596 Copy.FlowConditionToken = DACtx->forkFlowCondition(FlowConditionToken);597 return Copy;598}599 600bool Environment::canDescend(unsigned MaxDepth,601 const FunctionDecl *Callee) const {602 return CallStack.size() < MaxDepth && !llvm::is_contained(CallStack, Callee);603}604 605Environment Environment::pushCall(const CallExpr *Call) const {606 Environment Env(*this);607 608 if (const auto *MethodCall = dyn_cast<CXXMemberCallExpr>(Call)) {609 if (const Expr *Arg = MethodCall->getImplicitObjectArgument()) {610 if (!isa<CXXThisExpr>(Arg))611 Env.ThisPointeeLoc =612 cast<RecordStorageLocation>(getStorageLocation(*Arg));613 // Otherwise (when the argument is `this`), retain the current614 // environment's `ThisPointeeLoc`.615 }616 }617 618 if (Call->getType()->isRecordType() && Call->isPRValue())619 Env.LocForRecordReturnVal = &Env.getResultObjectLocation(*Call);620 621 Env.pushCallInternal(Call->getDirectCallee(),622 llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));623 624 return Env;625}626 627Environment Environment::pushCall(const CXXConstructExpr *Call) const {628 Environment Env(*this);629 630 Env.ThisPointeeLoc = &Env.getResultObjectLocation(*Call);631 Env.LocForRecordReturnVal = &Env.getResultObjectLocation(*Call);632 633 Env.pushCallInternal(Call->getConstructor(),634 llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));635 636 return Env;637}638 639void Environment::pushCallInternal(const FunctionDecl *FuncDecl,640 ArrayRef<const Expr *> Args) {641 // Canonicalize to the definition of the function. This ensures that we're642 // putting arguments into the same `ParamVarDecl`s` that the callee will later643 // be retrieving them from.644 assert(FuncDecl->getDefinition() != nullptr);645 FuncDecl = FuncDecl->getDefinition();646 647 CallStack.push_back(FuncDecl);648 649 initFieldsGlobalsAndFuncs(getReferencedDecls(*FuncDecl));650 651 const auto *ParamIt = FuncDecl->param_begin();652 653 // FIXME: Parameters don't always map to arguments 1:1; examples include654 // overloaded operators implemented as member functions, and parameter packs.655 for (unsigned ArgIndex = 0; ArgIndex < Args.size(); ++ParamIt, ++ArgIndex) {656 assert(ParamIt != FuncDecl->param_end());657 const VarDecl *Param = *ParamIt;658 setStorageLocation(*Param, createObject(*Param, Args[ArgIndex]));659 }660 661 ResultObjectMap = std::make_shared<PrValueToResultObject>(662 buildResultObjectMap(DACtx, FuncDecl, getThisPointeeStorageLocation(),663 LocForRecordReturnVal));664}665 666void Environment::popCall(const CallExpr *Call, const Environment &CalleeEnv) {667 // We ignore some entries of `CalleeEnv`:668 // - `DACtx` because is already the same in both669 // - We don't want the callee's `DeclCtx`, `ReturnVal`, `ReturnLoc` or670 // `ThisPointeeLoc` because they don't apply to us.671 // - `DeclToLoc`, `ExprToLoc`, and `ExprToVal` capture information from the672 // callee's local scope, so when popping that scope, we do not propagate673 // the maps.674 this->LocToVal = std::move(CalleeEnv.LocToVal);675 this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);676 677 if (Call->isGLValue()) {678 if (CalleeEnv.ReturnLoc != nullptr)679 setStorageLocation(*Call, *CalleeEnv.ReturnLoc);680 } else if (!Call->getType()->isVoidType()) {681 if (CalleeEnv.ReturnVal != nullptr)682 setValue(*Call, *CalleeEnv.ReturnVal);683 }684}685 686void Environment::popCall(const CXXConstructExpr *Call,687 const Environment &CalleeEnv) {688 // See also comment in `popCall(const CallExpr *, const Environment &)` above.689 this->LocToVal = std::move(CalleeEnv.LocToVal);690 this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);691}692 693bool Environment::equivalentTo(const Environment &Other,694 Environment::ValueModel &Model) const {695 assert(DACtx == Other.DACtx);696 697 if (ReturnVal != Other.ReturnVal)698 return false;699 700 if (ReturnLoc != Other.ReturnLoc)701 return false;702 703 if (LocForRecordReturnVal != Other.LocForRecordReturnVal)704 return false;705 706 if (ThisPointeeLoc != Other.ThisPointeeLoc)707 return false;708 709 if (DeclToLoc != Other.DeclToLoc)710 return false;711 712 if (ExprToLoc != Other.ExprToLoc)713 return false;714 715 if (!compareKeyToValueMaps(ExprToVal, Other.ExprToVal, *this, Other, Model))716 return false;717 718 if (!compareKeyToValueMaps(LocToVal, Other.LocToVal, *this, Other, Model))719 return false;720 721 return true;722}723 724LatticeEffect Environment::widen(const Environment &PrevEnv,725 Environment::ValueModel &Model) {726 assert(DACtx == PrevEnv.DACtx);727 assert(ReturnVal == PrevEnv.ReturnVal);728 assert(ReturnLoc == PrevEnv.ReturnLoc);729 assert(LocForRecordReturnVal == PrevEnv.LocForRecordReturnVal);730 assert(ThisPointeeLoc == PrevEnv.ThisPointeeLoc);731 assert(CallStack == PrevEnv.CallStack);732 assert(ResultObjectMap == PrevEnv.ResultObjectMap);733 assert(InitialTargetFunc == PrevEnv.InitialTargetFunc);734 assert(InitialTargetStmt == PrevEnv.InitialTargetStmt);735 736 auto Effect = LatticeEffect::Unchanged;737 738 // By the API, `PrevEnv` is a previous version of the environment for the same739 // block, so we have some guarantees about its shape. In particular, it will740 // be the result of a join or widen operation on previous values for this741 // block. For `DeclToLoc`, `ExprToVal`, and `ExprToLoc`, join guarantees that742 // these maps are subsets of the maps in `PrevEnv`. So, as long as we maintain743 // this property here, we don't need change their current values to widen.744 assert(DeclToLoc.size() <= PrevEnv.DeclToLoc.size());745 assert(ExprToVal.size() <= PrevEnv.ExprToVal.size());746 assert(ExprToLoc.size() <= PrevEnv.ExprToLoc.size());747 748 ExprToVal = widenKeyToValueMap(ExprToVal, PrevEnv.ExprToVal, *this, PrevEnv,749 Model, Effect);750 751 LocToVal = widenKeyToValueMap(LocToVal, PrevEnv.LocToVal, *this, PrevEnv,752 Model, Effect);753 if (DeclToLoc.size() != PrevEnv.DeclToLoc.size() ||754 ExprToLoc.size() != PrevEnv.ExprToLoc.size() ||755 ExprToVal.size() != PrevEnv.ExprToVal.size() ||756 LocToVal.size() != PrevEnv.LocToVal.size())757 Effect = LatticeEffect::Changed;758 759 return Effect;760}761 762Environment Environment::join(const Environment &EnvA, const Environment &EnvB,763 Environment::ValueModel &Model,764 ExprJoinBehavior ExprBehavior) {765 assert(EnvA.DACtx == EnvB.DACtx);766 assert(EnvA.LocForRecordReturnVal == EnvB.LocForRecordReturnVal);767 assert(EnvA.ThisPointeeLoc == EnvB.ThisPointeeLoc);768 assert(EnvA.CallStack == EnvB.CallStack);769 assert(EnvA.ResultObjectMap == EnvB.ResultObjectMap);770 assert(EnvA.InitialTargetFunc == EnvB.InitialTargetFunc);771 assert(EnvA.InitialTargetStmt == EnvB.InitialTargetStmt);772 773 Environment JoinedEnv(*EnvA.DACtx);774 775 JoinedEnv.CallStack = EnvA.CallStack;776 JoinedEnv.ResultObjectMap = EnvA.ResultObjectMap;777 JoinedEnv.LocForRecordReturnVal = EnvA.LocForRecordReturnVal;778 JoinedEnv.ThisPointeeLoc = EnvA.ThisPointeeLoc;779 JoinedEnv.InitialTargetFunc = EnvA.InitialTargetFunc;780 JoinedEnv.InitialTargetStmt = EnvA.InitialTargetStmt;781 782 const FunctionDecl *Func = EnvA.getCurrentFunc();783 if (!Func) {784 JoinedEnv.ReturnVal = nullptr;785 } else {786 JoinedEnv.ReturnVal =787 joinValues(Func->getReturnType(), EnvA.ReturnVal, EnvA, EnvB.ReturnVal,788 EnvB, JoinedEnv, Model);789 }790 791 if (EnvA.ReturnLoc == EnvB.ReturnLoc)792 JoinedEnv.ReturnLoc = EnvA.ReturnLoc;793 else794 JoinedEnv.ReturnLoc = nullptr;795 796 JoinedEnv.DeclToLoc = intersectDeclToLoc(EnvA.DeclToLoc, EnvB.DeclToLoc);797 798 // FIXME: update join to detect backedges and simplify the flow condition799 // accordingly.800 JoinedEnv.FlowConditionToken = EnvA.DACtx->joinFlowConditions(801 EnvA.FlowConditionToken, EnvB.FlowConditionToken);802 803 JoinedEnv.LocToVal =804 joinLocToVal(EnvA.LocToVal, EnvB.LocToVal, EnvA, EnvB, JoinedEnv, Model);805 806 if (ExprBehavior == KeepExprState) {807 JoinedEnv.ExprToVal = joinExprMaps(EnvA.ExprToVal, EnvB.ExprToVal);808 JoinedEnv.ExprToLoc = joinExprMaps(EnvA.ExprToLoc, EnvB.ExprToLoc);809 }810 811 return JoinedEnv;812}813 814Value *Environment::joinValues(QualType Ty, Value *Val1,815 const Environment &Env1, Value *Val2,816 const Environment &Env2, Environment &JoinedEnv,817 Environment::ValueModel &Model) {818 if (Val1 == nullptr || Val2 == nullptr)819 // We can't say anything about the joined value -- even if one of the values820 // is non-null, we don't want to simply propagate it, because it would be821 // too specific: Because the other value is null, that means we have no822 // information at all about the value (i.e. the value is unconstrained).823 return nullptr;824 825 if (areEquivalentValues(*Val1, *Val2))826 // Arbitrarily return one of the two values.827 return Val1;828 829 return joinDistinctValues(Ty, *Val1, Env1, *Val2, Env2, JoinedEnv, Model);830}831 832StorageLocation &Environment::createStorageLocation(QualType Type) {833 return DACtx->createStorageLocation(Type);834}835 836StorageLocation &Environment::createStorageLocation(const ValueDecl &D) {837 // Evaluated declarations are always assigned the same storage locations to838 // ensure that the environment stabilizes across loop iterations. Storage839 // locations for evaluated declarations are stored in the analysis context.840 return DACtx->getStableStorageLocation(D);841}842 843StorageLocation &Environment::createStorageLocation(const Expr &E) {844 // Evaluated expressions are always assigned the same storage locations to845 // ensure that the environment stabilizes across loop iterations. Storage846 // locations for evaluated expressions are stored in the analysis context.847 return DACtx->getStableStorageLocation(E);848}849 850void Environment::setStorageLocation(const ValueDecl &D, StorageLocation &Loc) {851 assert(!DeclToLoc.contains(&D));852 // The only kinds of declarations that may have a "variable" storage location853 // are declarations of reference type and `BindingDecl`. For all other854 // declaration, the storage location should be the stable storage location855 // returned by `createStorageLocation()`.856 assert(D.getType()->isReferenceType() || isa<BindingDecl>(D) ||857 &Loc == &createStorageLocation(D));858 DeclToLoc[&D] = &Loc;859}860 861StorageLocation *Environment::getStorageLocation(const ValueDecl &D) const {862 auto It = DeclToLoc.find(&D);863 if (It == DeclToLoc.end())864 return nullptr;865 866 StorageLocation *Loc = It->second;867 868 return Loc;869}870 871void Environment::removeDecl(const ValueDecl &D) { DeclToLoc.erase(&D); }872 873void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) {874 // `DeclRefExpr`s to builtin function types aren't glvalues, for some reason,875 // but we still want to be able to associate a `StorageLocation` with them,876 // so allow these as an exception.877 assert(E.isGLValue() ||878 E.getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn));879 const Expr &CanonE = ignoreCFGOmittedNodes(E);880 assert(!ExprToLoc.contains(&CanonE));881 ExprToLoc[&CanonE] = &Loc;882}883 884StorageLocation *Environment::getStorageLocation(const Expr &E) const {885 // See comment in `setStorageLocation()`.886 assert(E.isGLValue() ||887 E.getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn));888 auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));889 return It == ExprToLoc.end() ? nullptr : &*It->second;890}891 892RecordStorageLocation &893Environment::getResultObjectLocation(const Expr &RecordPRValue) const {894 assert(RecordPRValue.getType()->isRecordType());895 assert(RecordPRValue.isPRValue());896 897 assert(ResultObjectMap != nullptr);898 RecordStorageLocation *Loc = ResultObjectMap->lookup(&RecordPRValue);899 assert(Loc != nullptr);900 // In release builds, use the "stable" storage location if the map lookup901 // failed.902 if (Loc == nullptr)903 return cast<RecordStorageLocation>(904 DACtx->getStableStorageLocation(RecordPRValue));905 return *Loc;906}907 908PointerValue &Environment::getOrCreateNullPointerValue(QualType PointeeType) {909 return DACtx->getOrCreateNullPointerValue(PointeeType);910}911 912void Environment::initializeFieldsWithValues(RecordStorageLocation &Loc,913 QualType Type) {914 llvm::DenseSet<QualType> Visited;915 int CreatedValuesCount = 0;916 initializeFieldsWithValues(Loc, Type, Visited, 0, CreatedValuesCount);917 if (CreatedValuesCount > MaxCompositeValueSize) {918 llvm::errs() << "Attempting to initialize a huge value of type: " << Type919 << '\n';920 }921}922 923void Environment::setValue(const StorageLocation &Loc, Value &Val) {924 // Records should not be associated with values.925 assert(!isa<RecordStorageLocation>(Loc));926 LocToVal[&Loc] = &Val;927}928 929void Environment::setValue(const Expr &E, Value &Val) {930 const Expr &CanonE = ignoreCFGOmittedNodes(E);931 932 assert(CanonE.isPRValue());933 // Records should not be associated with values.934 assert(!CanonE.getType()->isRecordType());935 ExprToVal[&CanonE] = &Val;936}937 938Value *Environment::getValue(const StorageLocation &Loc) const {939 // Records should not be associated with values.940 assert(!isa<RecordStorageLocation>(Loc));941 return LocToVal.lookup(&Loc);942}943 944Value *Environment::getValue(const ValueDecl &D) const {945 auto *Loc = getStorageLocation(D);946 if (Loc == nullptr)947 return nullptr;948 return getValue(*Loc);949}950 951Value *Environment::getValue(const Expr &E) const {952 // Records should not be associated with values.953 assert(!E.getType()->isRecordType());954 955 if (E.isPRValue()) {956 auto It = ExprToVal.find(&ignoreCFGOmittedNodes(E));957 return It == ExprToVal.end() ? nullptr : It->second;958 }959 960 auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));961 if (It == ExprToLoc.end())962 return nullptr;963 return getValue(*It->second);964}965 966Value *Environment::createValue(QualType Type) {967 llvm::DenseSet<QualType> Visited;968 int CreatedValuesCount = 0;969 Value *Val = createValueUnlessSelfReferential(Type, Visited, /*Depth=*/0,970 CreatedValuesCount);971 if (CreatedValuesCount > MaxCompositeValueSize) {972 llvm::errs() << "Attempting to initialize a huge value of type: " << Type973 << '\n';974 }975 return Val;976}977 978Value *Environment::createValueUnlessSelfReferential(979 QualType Type, llvm::DenseSet<QualType> &Visited, int Depth,980 int &CreatedValuesCount) {981 assert(!Type.isNull());982 assert(!Type->isReferenceType());983 assert(!Type->isRecordType());984 985 // Allow unlimited fields at depth 1; only cap at deeper nesting levels.986 if ((Depth > 1 && CreatedValuesCount > MaxCompositeValueSize) ||987 Depth > MaxCompositeValueDepth)988 return nullptr;989 990 if (Type->isBooleanType()) {991 CreatedValuesCount++;992 return &makeAtomicBoolValue();993 }994 995 if (Type->isIntegerType()) {996 // FIXME: consider instead `return nullptr`, given that we do nothing useful997 // with integers, and so distinguishing them serves no purpose, but could998 // prevent convergence.999 CreatedValuesCount++;1000 return &arena().create<IntegerValue>();1001 }1002 1003 if (Type->isPointerType()) {1004 CreatedValuesCount++;1005 QualType PointeeType = Type->getPointeeType();1006 StorageLocation &PointeeLoc =1007 createLocAndMaybeValue(PointeeType, Visited, Depth, CreatedValuesCount);1008 1009 return &arena().create<PointerValue>(PointeeLoc);1010 }1011 1012 return nullptr;1013}1014 1015StorageLocation &1016Environment::createLocAndMaybeValue(QualType Ty,1017 llvm::DenseSet<QualType> &Visited,1018 int Depth, int &CreatedValuesCount) {1019 if (!Visited.insert(Ty.getCanonicalType()).second)1020 return createStorageLocation(Ty.getNonReferenceType());1021 auto EraseVisited = llvm::make_scope_exit(1022 [&Visited, Ty] { Visited.erase(Ty.getCanonicalType()); });1023 1024 Ty = Ty.getNonReferenceType();1025 1026 if (Ty->isRecordType()) {1027 auto &Loc = cast<RecordStorageLocation>(createStorageLocation(Ty));1028 initializeFieldsWithValues(Loc, Ty, Visited, Depth, CreatedValuesCount);1029 return Loc;1030 }1031 1032 StorageLocation &Loc = createStorageLocation(Ty);1033 1034 if (Value *Val = createValueUnlessSelfReferential(Ty, Visited, Depth,1035 CreatedValuesCount))1036 setValue(Loc, *Val);1037 1038 return Loc;1039}1040 1041void Environment::initializeFieldsWithValues(RecordStorageLocation &Loc,1042 QualType Type,1043 llvm::DenseSet<QualType> &Visited,1044 int Depth,1045 int &CreatedValuesCount) {1046 auto initField = [&](QualType FieldType, StorageLocation &FieldLoc) {1047 if (FieldType->isRecordType()) {1048 auto &FieldRecordLoc = cast<RecordStorageLocation>(FieldLoc);1049 initializeFieldsWithValues(FieldRecordLoc, FieldRecordLoc.getType(),1050 Visited, Depth + 1, CreatedValuesCount);1051 } else {1052 if (getValue(FieldLoc) != nullptr)1053 return;1054 if (!Visited.insert(FieldType.getCanonicalType()).second)1055 return;1056 if (Value *Val = createValueUnlessSelfReferential(1057 FieldType, Visited, Depth + 1, CreatedValuesCount))1058 setValue(FieldLoc, *Val);1059 Visited.erase(FieldType.getCanonicalType());1060 }1061 };1062 1063 for (const FieldDecl *Field : DACtx->getModeledFields(Type)) {1064 assert(Field != nullptr);1065 QualType FieldType = Field->getType();1066 1067 if (FieldType->isReferenceType()) {1068 Loc.setChild(*Field,1069 &createLocAndMaybeValue(FieldType, Visited, Depth + 1,1070 CreatedValuesCount));1071 } else {1072 StorageLocation *FieldLoc = Loc.getChild(*Field);1073 assert(FieldLoc != nullptr);1074 initField(FieldType, *FieldLoc);1075 }1076 }1077 for (const auto &[FieldName, FieldType] : DACtx->getSyntheticFields(Type)) {1078 // Synthetic fields cannot have reference type, so we don't need to deal1079 // with this case.1080 assert(!FieldType->isReferenceType());1081 initField(FieldType, Loc.getSyntheticField(FieldName));1082 }1083}1084 1085StorageLocation &Environment::createObjectInternal(const ValueDecl *D,1086 QualType Ty,1087 const Expr *InitExpr) {1088 if (Ty->isReferenceType()) {1089 // Although variables of reference type always need to be initialized, it1090 // can happen that we can't see the initializer, so `InitExpr` may still1091 // be null.1092 if (InitExpr) {1093 if (auto *InitExprLoc = getStorageLocation(*InitExpr))1094 return *InitExprLoc;1095 }1096 1097 // Even though we have an initializer, we might not get an1098 // InitExprLoc, for example if the InitExpr is a CallExpr for which we1099 // don't have a function body. In this case, we just invent a storage1100 // location and value -- it's the best we can do.1101 return createObjectInternal(D, Ty.getNonReferenceType(), nullptr);1102 }1103 1104 StorageLocation &Loc =1105 D ? createStorageLocation(*D) : createStorageLocation(Ty);1106 1107 if (Ty->isRecordType()) {1108 auto &RecordLoc = cast<RecordStorageLocation>(Loc);1109 if (!InitExpr)1110 initializeFieldsWithValues(RecordLoc);1111 } else {1112 Value *Val = nullptr;1113 if (InitExpr)1114 // In the (few) cases where an expression is intentionally1115 // "uninterpreted", `InitExpr` is not associated with a value. There are1116 // two ways to handle this situation: propagate the status, so that1117 // uninterpreted initializers result in uninterpreted variables, or1118 // provide a default value. We choose the latter so that later refinements1119 // of the variable can be used for reasoning about the surrounding code.1120 // For this reason, we let this case be handled by the `createValue()`1121 // call below.1122 //1123 // FIXME. If and when we interpret all language cases, change this to1124 // assert that `InitExpr` is interpreted, rather than supplying a1125 // default value (assuming we don't update the environment API to return1126 // references).1127 Val = getValue(*InitExpr);1128 if (!Val)1129 Val = createValue(Ty);1130 if (Val)1131 setValue(Loc, *Val);1132 }1133 1134 return Loc;1135}1136 1137void Environment::assume(const Formula &F) {1138 DACtx->addFlowConditionConstraint(FlowConditionToken, F);1139}1140 1141bool Environment::proves(const Formula &F) const {1142 return DACtx->flowConditionImplies(FlowConditionToken, F);1143}1144 1145bool Environment::allows(const Formula &F) const {1146 return DACtx->flowConditionAllows(FlowConditionToken, F);1147}1148 1149void Environment::dump(raw_ostream &OS) const {1150 llvm::DenseMap<const StorageLocation *, std::string> LocToName;1151 if (LocForRecordReturnVal != nullptr)1152 LocToName[LocForRecordReturnVal] = "(returned record)";1153 if (ThisPointeeLoc != nullptr)1154 LocToName[ThisPointeeLoc] = "this";1155 1156 OS << "DeclToLoc:\n";1157 for (auto [D, L] : DeclToLoc) {1158 auto Iter = LocToName.insert({L, D->getNameAsString()}).first;1159 OS << " [" << Iter->second << ", " << L << "]\n";1160 }1161 OS << "ExprToLoc:\n";1162 for (auto [E, L] : ExprToLoc)1163 OS << " [" << E << ", " << L << "]\n";1164 1165 OS << "ExprToVal:\n";1166 for (auto [E, V] : ExprToVal)1167 OS << " [" << E << ", " << V << ": " << *V << "]\n";1168 1169 OS << "LocToVal:\n";1170 for (auto [L, V] : LocToVal) {1171 OS << " [" << L;1172 if (auto Iter = LocToName.find(L); Iter != LocToName.end())1173 OS << " (" << Iter->second << ")";1174 OS << ", " << V << ": " << *V << "]\n";1175 }1176 1177 if (const FunctionDecl *Func = getCurrentFunc()) {1178 if (Func->getReturnType()->isReferenceType()) {1179 OS << "ReturnLoc: " << ReturnLoc;1180 if (auto Iter = LocToName.find(ReturnLoc); Iter != LocToName.end())1181 OS << " (" << Iter->second << ")";1182 OS << "\n";1183 } else if (Func->getReturnType()->isRecordType() ||1184 isa<CXXConstructorDecl>(Func)) {1185 OS << "LocForRecordReturnVal: " << LocForRecordReturnVal << "\n";1186 } else if (!Func->getReturnType()->isVoidType()) {1187 if (ReturnVal == nullptr)1188 OS << "ReturnVal: nullptr\n";1189 else1190 OS << "ReturnVal: " << *ReturnVal << "\n";1191 }1192 1193 if (isa<CXXMethodDecl>(Func)) {1194 OS << "ThisPointeeLoc: " << ThisPointeeLoc << "\n";1195 }1196 }1197 1198 OS << "\n";1199 DACtx->dumpFlowCondition(FlowConditionToken, OS);1200}1201 1202void Environment::dump() const { dump(llvm::dbgs()); }1203 1204Environment::PrValueToResultObject Environment::buildResultObjectMap(1205 DataflowAnalysisContext *DACtx, const FunctionDecl *FuncDecl,1206 RecordStorageLocation *ThisPointeeLoc,1207 RecordStorageLocation *LocForRecordReturnVal) {1208 assert(FuncDecl->doesThisDeclarationHaveABody());1209 1210 PrValueToResultObject Map = buildResultObjectMap(1211 DACtx, FuncDecl->getBody(), ThisPointeeLoc, LocForRecordReturnVal);1212 1213 ResultObjectVisitor Visitor(Map, LocForRecordReturnVal, *DACtx);1214 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FuncDecl))1215 Visitor.traverseConstructorInits(Ctor, ThisPointeeLoc);1216 1217 return Map;1218}1219 1220Environment::PrValueToResultObject Environment::buildResultObjectMap(1221 DataflowAnalysisContext *DACtx, Stmt *S,1222 RecordStorageLocation *ThisPointeeLoc,1223 RecordStorageLocation *LocForRecordReturnVal) {1224 PrValueToResultObject Map;1225 ResultObjectVisitor Visitor(Map, LocForRecordReturnVal, *DACtx);1226 Visitor.TraverseStmt(S);1227 return Map;1228}1229 1230RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE,1231 const Environment &Env) {1232 Expr *ImplicitObject = MCE.getImplicitObjectArgument();1233 if (ImplicitObject == nullptr)1234 return nullptr;1235 if (ImplicitObject->getType()->isPointerType()) {1236 if (auto *Val = Env.get<PointerValue>(*ImplicitObject))1237 return &cast<RecordStorageLocation>(Val->getPointeeLoc());1238 return nullptr;1239 }1240 return cast_or_null<RecordStorageLocation>(1241 Env.getStorageLocation(*ImplicitObject));1242}1243 1244RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,1245 const Environment &Env) {1246 Expr *Base = ME.getBase();1247 if (Base == nullptr)1248 return nullptr;1249 if (ME.isArrow()) {1250 if (auto *Val = Env.get<PointerValue>(*Base))1251 return &cast<RecordStorageLocation>(Val->getPointeeLoc());1252 return nullptr;1253 }1254 return Env.get<RecordStorageLocation>(*Base);1255}1256 1257} // namespace dataflow1258} // namespace clang1259