Path: blob/main/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
35266 views
//===-- DataflowEnvironment.cpp ---------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines an Environment class that is used by dataflow analyses9// that run over Control-Flow Graphs (CFGs) to keep track of the state of the10// program at given program points.11//12//===----------------------------------------------------------------------===//1314#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"15#include "clang/AST/Decl.h"16#include "clang/AST/DeclCXX.h"17#include "clang/AST/ExprCXX.h"18#include "clang/AST/RecursiveASTVisitor.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/PointerUnion.h"29#include "llvm/ADT/STLExtras.h"30#include "llvm/ADT/ScopeExit.h"31#include "llvm/Support/ErrorHandling.h"32#include <algorithm>33#include <cassert>34#include <memory>35#include <utility>3637#define DEBUG_TYPE "dataflow"3839namespace clang {40namespace dataflow {4142// FIXME: convert these to parameters of the analysis or environment. Current43// settings have been experimentaly validated, but only for a particular44// analysis.45static constexpr int MaxCompositeValueDepth = 3;46static constexpr int MaxCompositeValueSize = 1000;4748/// Returns a map consisting of key-value entries that are present in both maps.49static llvm::DenseMap<const ValueDecl *, StorageLocation *> intersectDeclToLoc(50const llvm::DenseMap<const ValueDecl *, StorageLocation *> &DeclToLoc1,51const llvm::DenseMap<const ValueDecl *, StorageLocation *> &DeclToLoc2) {52llvm::DenseMap<const ValueDecl *, StorageLocation *> Result;53for (auto &Entry : DeclToLoc1) {54auto It = DeclToLoc2.find(Entry.first);55if (It != DeclToLoc2.end() && Entry.second == It->second)56Result.insert({Entry.first, Entry.second});57}58return Result;59}6061// Performs a join on either `ExprToLoc` or `ExprToVal`.62// The maps must be consistent in the sense that any entries for the same63// expression must map to the same location / value. This is the case if we are64// performing a join for control flow within a full-expression (which is the65// only case when this function should be used).66template <typename MapT> MapT joinExprMaps(const MapT &Map1, const MapT &Map2) {67MapT Result = Map1;6869for (const auto &Entry : Map2) {70[[maybe_unused]] auto [It, Inserted] = Result.insert(Entry);71// If there was an existing entry, its value should be the same as for the72// entry we were trying to insert.73assert(It->second == Entry.second);74}7576return Result;77}7879// Whether to consider equivalent two values with an unknown relation.80//81// FIXME: this function is a hack enabling unsoundness to support82// convergence. Once we have widening support for the reference/pointer and83// struct built-in models, this should be unconditionally `false` (and inlined84// as such at its call sites).85static bool equateUnknownValues(Value::Kind K) {86switch (K) {87case Value::Kind::Integer:88case Value::Kind::Pointer:89return true;90default:91return false;92}93}9495static bool compareDistinctValues(QualType Type, Value &Val1,96const Environment &Env1, Value &Val2,97const Environment &Env2,98Environment::ValueModel &Model) {99// Note: Potentially costly, but, for booleans, we could check whether both100// can be proven equivalent in their respective environments.101102// FIXME: move the reference/pointers logic from `areEquivalentValues` to here103// and implement separate, join/widen specific handling for104// reference/pointers.105switch (Model.compare(Type, Val1, Env1, Val2, Env2)) {106case ComparisonResult::Same:107return true;108case ComparisonResult::Different:109return false;110case ComparisonResult::Unknown:111return equateUnknownValues(Val1.getKind());112}113llvm_unreachable("All cases covered in switch");114}115116/// Attempts to join distinct values `Val1` and `Val2` in `Env1` and `Env2`,117/// respectively, of the same type `Type`. Joining generally produces a single118/// value that (soundly) approximates the two inputs, although the actual119/// meaning depends on `Model`.120static Value *joinDistinctValues(QualType Type, Value &Val1,121const Environment &Env1, Value &Val2,122const Environment &Env2,123Environment &JoinedEnv,124Environment::ValueModel &Model) {125// Join distinct boolean values preserving information about the constraints126// in the respective path conditions.127if (isa<BoolValue>(&Val1) && isa<BoolValue>(&Val2)) {128// FIXME: Checking both values should be unnecessary, since they should have129// a consistent shape. However, right now we can end up with BoolValue's in130// integer-typed variables due to our incorrect handling of131// boolean-to-integer casts (we just propagate the BoolValue to the result132// of the cast). So, a join can encounter an integer in one branch but a133// bool in the other.134// For example:135// ```136// std::optional<bool> o;137// int x;138// if (o.has_value())139// x = o.value();140// ```141auto &Expr1 = cast<BoolValue>(Val1).formula();142auto &Expr2 = cast<BoolValue>(Val2).formula();143auto &A = JoinedEnv.arena();144auto &JoinedVal = A.makeAtomRef(A.makeAtom());145JoinedEnv.assume(146A.makeOr(A.makeAnd(A.makeAtomRef(Env1.getFlowConditionToken()),147A.makeEquals(JoinedVal, Expr1)),148A.makeAnd(A.makeAtomRef(Env2.getFlowConditionToken()),149A.makeEquals(JoinedVal, Expr2))));150return &A.makeBoolValue(JoinedVal);151}152153Value *JoinedVal = JoinedEnv.createValue(Type);154if (JoinedVal)155Model.join(Type, Val1, Env1, Val2, Env2, *JoinedVal, JoinedEnv);156157return JoinedVal;158}159160static WidenResult widenDistinctValues(QualType Type, Value &Prev,161const Environment &PrevEnv,162Value &Current, Environment &CurrentEnv,163Environment::ValueModel &Model) {164// Boolean-model widening.165if (isa<BoolValue>(Prev) && isa<BoolValue>(Current)) {166// FIXME: Checking both values should be unnecessary, but we can currently167// end up with `BoolValue`s in integer-typed variables. See comment in168// `joinDistinctValues()` for details.169auto &PrevBool = cast<BoolValue>(Prev);170auto &CurBool = cast<BoolValue>(Current);171172if (isa<TopBoolValue>(Prev))173// Safe to return `Prev` here, because Top is never dependent on the174// environment.175return {&Prev, LatticeEffect::Unchanged};176177// We may need to widen to Top, but before we do so, check whether both178// values are implied to be either true or false in the current environment.179// In that case, we can simply return a literal instead.180bool TruePrev = PrevEnv.proves(PrevBool.formula());181bool TrueCur = CurrentEnv.proves(CurBool.formula());182if (TruePrev && TrueCur)183return {&CurrentEnv.getBoolLiteralValue(true), LatticeEffect::Unchanged};184if (!TruePrev && !TrueCur &&185PrevEnv.proves(PrevEnv.arena().makeNot(PrevBool.formula())) &&186CurrentEnv.proves(CurrentEnv.arena().makeNot(CurBool.formula())))187return {&CurrentEnv.getBoolLiteralValue(false), LatticeEffect::Unchanged};188189return {&CurrentEnv.makeTopBoolValue(), LatticeEffect::Changed};190}191192// FIXME: Add other built-in model widening.193194// Custom-model widening.195if (auto Result = Model.widen(Type, Prev, PrevEnv, Current, CurrentEnv))196return *Result;197198return {&Current, equateUnknownValues(Prev.getKind())199? LatticeEffect::Unchanged200: LatticeEffect::Changed};201}202203// Returns whether the values in `Map1` and `Map2` compare equal for those204// keys that `Map1` and `Map2` have in common.205template <typename Key>206bool compareKeyToValueMaps(const llvm::MapVector<Key, Value *> &Map1,207const llvm::MapVector<Key, Value *> &Map2,208const Environment &Env1, const Environment &Env2,209Environment::ValueModel &Model) {210for (auto &Entry : Map1) {211Key K = Entry.first;212assert(K != nullptr);213214Value *Val = Entry.second;215assert(Val != nullptr);216217auto It = Map2.find(K);218if (It == Map2.end())219continue;220assert(It->second != nullptr);221222if (!areEquivalentValues(*Val, *It->second) &&223!compareDistinctValues(K->getType(), *Val, Env1, *It->second, Env2,224Model))225return false;226}227228return true;229}230231// Perform a join on two `LocToVal` maps.232static llvm::MapVector<const StorageLocation *, Value *>233joinLocToVal(const llvm::MapVector<const StorageLocation *, Value *> &LocToVal,234const llvm::MapVector<const StorageLocation *, Value *> &LocToVal2,235const Environment &Env1, const Environment &Env2,236Environment &JoinedEnv, Environment::ValueModel &Model) {237llvm::MapVector<const StorageLocation *, Value *> Result;238for (auto &Entry : LocToVal) {239const StorageLocation *Loc = Entry.first;240assert(Loc != nullptr);241242Value *Val = Entry.second;243assert(Val != nullptr);244245auto It = LocToVal2.find(Loc);246if (It == LocToVal2.end())247continue;248assert(It->second != nullptr);249250if (Value *JoinedVal = Environment::joinValues(251Loc->getType(), Val, Env1, It->second, Env2, JoinedEnv, Model)) {252Result.insert({Loc, JoinedVal});253}254}255256return Result;257}258259// Perform widening on either `LocToVal` or `ExprToVal`. `Key` must be either260// `const StorageLocation *` or `const Expr *`.261template <typename Key>262llvm::MapVector<Key, Value *>263widenKeyToValueMap(const llvm::MapVector<Key, Value *> &CurMap,264const llvm::MapVector<Key, Value *> &PrevMap,265Environment &CurEnv, const Environment &PrevEnv,266Environment::ValueModel &Model, LatticeEffect &Effect) {267llvm::MapVector<Key, Value *> WidenedMap;268for (auto &Entry : CurMap) {269Key K = Entry.first;270assert(K != nullptr);271272Value *Val = Entry.second;273assert(Val != nullptr);274275auto PrevIt = PrevMap.find(K);276if (PrevIt == PrevMap.end())277continue;278assert(PrevIt->second != nullptr);279280if (areEquivalentValues(*Val, *PrevIt->second)) {281WidenedMap.insert({K, Val});282continue;283}284285auto [WidenedVal, ValEffect] = widenDistinctValues(286K->getType(), *PrevIt->second, PrevEnv, *Val, CurEnv, Model);287WidenedMap.insert({K, WidenedVal});288if (ValEffect == LatticeEffect::Changed)289Effect = LatticeEffect::Changed;290}291292return WidenedMap;293}294295namespace {296297// 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<ResultObjectVisitor> {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.306explicit ResultObjectVisitor(307llvm::DenseMap<const Expr *, RecordStorageLocation *> &ResultObjectMap,308RecordStorageLocation *LocForRecordReturnVal,309DataflowAnalysisContext &DACtx)310: ResultObjectMap(ResultObjectMap),311LocForRecordReturnVal(LocForRecordReturnVal), DACtx(DACtx) {}312313// 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.317void TraverseConstructorInits(const CXXConstructorDecl *Ctor,318RecordStorageLocation *ThisPointeeLoc) {319assert(ThisPointeeLoc != nullptr);320for (const CXXCtorInitializer *Init : Ctor->inits()) {321Expr *InitExpr = Init->getInit();322if (FieldDecl *Field = Init->getMember();323Field != nullptr && Field->getType()->isRecordType()) {324PropagateResultObject(InitExpr, cast<RecordStorageLocation>(325ThisPointeeLoc->getChild(*Field)));326} else if (Init->getBaseClass()) {327PropagateResultObject(InitExpr, ThisPointeeLoc);328}329330// Ensure that any result objects within `InitExpr` (e.g. temporaries)331// are also propagated to the prvalues that initialize them.332TraverseStmt(InitExpr);333334// If this is a `CXXDefaultInitExpr`, also propagate any result objects335// within the default expression.336if (auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(InitExpr))337TraverseStmt(DefaultInit->getExpr());338}339}340341bool VisitVarDecl(VarDecl *VD) {342if (VD->getType()->isRecordType() && VD->hasInit())343PropagateResultObject(344VD->getInit(),345&cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*VD)));346return true;347}348349bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE) {350if (MTE->getType()->isRecordType())351PropagateResultObject(352MTE->getSubExpr(),353&cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*MTE)));354return true;355}356357bool VisitReturnStmt(ReturnStmt *Return) {358Expr *RetValue = Return->getRetValue();359if (RetValue != nullptr && RetValue->getType()->isRecordType() &&360RetValue->isPRValue())361PropagateResultObject(RetValue, LocForRecordReturnVal);362return true;363}364365bool VisitExpr(Expr *E) {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`.371if (E->isPRValue() && E->getType()->isRecordType() &&372!ResultObjectMap.contains(E))373PropagateResultObject(374E, &cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*E)));375return true;376}377378void379PropagateResultObjectToRecordInitList(const RecordInitListHelper &InitList,380RecordStorageLocation *Loc) {381for (auto [Base, Init] : InitList.base_inits()) {382assert(Base->getType().getCanonicalType() ==383Init->getType().getCanonicalType());384385// 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.388PropagateResultObject(Init, Loc);389}390391for (auto [Field, Init] : InitList.field_inits()) {392// Fields of non-record type are handled in393// `TransferVisitor::VisitInitListExpr()`.394if (Field->getType()->isRecordType())395PropagateResultObject(396Init, cast<RecordStorageLocation>(Loc->getChild(*Field)));397}398}399400// 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).403void PropagateResultObject(Expr *E, RecordStorageLocation *Loc) {404if (!E->isPRValue() || !E->getType()->isRecordType()) {405assert(false);406// Ensure we don't propagate the result object if we hit this in a407// release build.408return;409}410411ResultObjectMap[E] = Loc;412413// 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).416if (isa<CXXConstructExpr>(E) || isa<CallExpr>(E) || isa<LambdaExpr>(E) ||417isa<CXXDefaultArgExpr>(E) || isa<CXXStdInitializerListExpr>(E) ||418isa<AtomicExpr>(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.422isa<BuiltinBitCastExpr>(E)) {423return;424}425if (auto *Op = dyn_cast<BinaryOperator>(E);426Op && Op->getOpcode() == BO_Cmp) {427// Builtin `<=>` returns a `std::strong_ordering` object.428return;429}430431if (auto *InitList = dyn_cast<InitListExpr>(E)) {432if (!InitList->isSemanticForm())433return;434if (InitList->isTransparent()) {435PropagateResultObject(InitList->getInit(0), Loc);436return;437}438439PropagateResultObjectToRecordInitList(RecordInitListHelper(InitList),440Loc);441return;442}443444if (auto *ParenInitList = dyn_cast<CXXParenListInitExpr>(E)) {445PropagateResultObjectToRecordInitList(RecordInitListHelper(ParenInitList),446Loc);447return;448}449450if (auto *Op = dyn_cast<BinaryOperator>(E); Op && Op->isCommaOp()) {451PropagateResultObject(Op->getRHS(), Loc);452return;453}454455if (auto *Cond = dyn_cast<AbstractConditionalOperator>(E)) {456PropagateResultObject(Cond->getTrueExpr(), Loc);457PropagateResultObject(Cond->getFalseExpr(), Loc);458return;459}460461if (auto *SE = dyn_cast<StmtExpr>(E)) {462PropagateResultObject(cast<Expr>(SE->getSubStmt()->body_back()), Loc);463return;464}465466if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(E)) {467PropagateResultObject(DIE->getExpr(), Loc);468return;469}470471// All other expression nodes that propagate a record prvalue should have472// exactly one child.473SmallVector<Stmt *, 1> Children(E->child_begin(), E->child_end());474LLVM_DEBUG({475if (Children.size() != 1)476E->dump();477});478assert(Children.size() == 1);479for (Stmt *S : Children)480PropagateResultObject(cast<Expr>(S), Loc);481}482483private:484llvm::DenseMap<const Expr *, RecordStorageLocation *> &ResultObjectMap;485RecordStorageLocation *LocForRecordReturnVal;486DataflowAnalysisContext &DACtx;487};488489} // namespace490491void Environment::initialize() {492if (InitialTargetStmt == nullptr)493return;494495if (InitialTargetFunc == nullptr) {496initFieldsGlobalsAndFuncs(getReferencedDecls(*InitialTargetStmt));497ResultObjectMap =498std::make_shared<PrValueToResultObject>(buildResultObjectMap(499DACtx, InitialTargetStmt, getThisPointeeStorageLocation(),500/*LocForRecordReturnValue=*/nullptr));501return;502}503504initFieldsGlobalsAndFuncs(getReferencedDecls(*InitialTargetFunc));505506for (const auto *ParamDecl : InitialTargetFunc->parameters()) {507assert(ParamDecl != nullptr);508setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr));509}510511if (InitialTargetFunc->getReturnType()->isRecordType())512LocForRecordReturnVal = &cast<RecordStorageLocation>(513createStorageLocation(InitialTargetFunc->getReturnType()));514515if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(InitialTargetFunc)) {516auto *Parent = MethodDecl->getParent();517assert(Parent != nullptr);518519if (Parent->isLambda()) {520for (const auto &Capture : Parent->captures()) {521if (Capture.capturesVariable()) {522const auto *VarDecl = Capture.getCapturedVar();523assert(VarDecl != nullptr);524setStorageLocation(*VarDecl, createObject(*VarDecl, nullptr));525} else if (Capture.capturesThis()) {526if (auto *Ancestor = InitialTargetFunc->getNonClosureAncestor()) {527const auto *SurroundingMethodDecl = cast<CXXMethodDecl>(Ancestor);528QualType ThisPointeeType =529SurroundingMethodDecl->getFunctionObjectParameterType();530setThisPointeeStorageLocation(531cast<RecordStorageLocation>(createObject(ThisPointeeType)));532} else if (auto *FieldBeingInitialized =533dyn_cast<FieldDecl>(Parent->getLambdaContextDecl())) {534// This is in a field initializer, rather than a method.535setThisPointeeStorageLocation(536cast<RecordStorageLocation>(createObject(QualType(537FieldBeingInitialized->getParent()->getTypeForDecl(), 0))));538} else {539assert(false && "Unexpected this-capturing lambda context.");540}541}542}543} else if (MethodDecl->isImplicitObjectMemberFunction()) {544QualType ThisPointeeType = MethodDecl->getFunctionObjectParameterType();545auto &ThisLoc =546cast<RecordStorageLocation>(createStorageLocation(ThisPointeeType));547setThisPointeeStorageLocation(ThisLoc);548// Initialize fields of `*this` with values, but only if we're not549// analyzing a constructor; after all, it's the constructor's job to do550// this (and we want to be able to test that).551if (!isa<CXXConstructorDecl>(MethodDecl))552initializeFieldsWithValues(ThisLoc);553}554}555556// We do this below the handling of `CXXMethodDecl` above so that we can557// be sure that the storage location for `this` has been set.558ResultObjectMap =559std::make_shared<PrValueToResultObject>(buildResultObjectMap(560DACtx, InitialTargetFunc, getThisPointeeStorageLocation(),561LocForRecordReturnVal));562}563564// FIXME: Add support for resetting globals after function calls to enable the565// implementation of sound analyses.566567void Environment::initFieldsGlobalsAndFuncs(const ReferencedDecls &Referenced) {568// These have to be added before the lines that follow to ensure that569// `create*` work correctly for structs.570DACtx->addModeledFields(Referenced.Fields);571572for (const VarDecl *D : Referenced.Globals) {573if (getStorageLocation(*D) != nullptr)574continue;575576// We don't run transfer functions on the initializers of global variables,577// so they won't be associated with a value or storage location. We578// therefore intentionally don't pass an initializer to `createObject()`; in579// particular, this ensures that `createObject()` will initialize the fields580// of record-type variables with values.581setStorageLocation(*D, createObject(*D, nullptr));582}583584for (const FunctionDecl *FD : Referenced.Functions) {585if (getStorageLocation(*FD) != nullptr)586continue;587auto &Loc = createStorageLocation(*FD);588setStorageLocation(*FD, Loc);589}590}591592Environment Environment::fork() const {593Environment Copy(*this);594Copy.FlowConditionToken = DACtx->forkFlowCondition(FlowConditionToken);595return Copy;596}597598bool Environment::canDescend(unsigned MaxDepth,599const FunctionDecl *Callee) const {600return CallStack.size() < MaxDepth && !llvm::is_contained(CallStack, Callee);601}602603Environment Environment::pushCall(const CallExpr *Call) const {604Environment Env(*this);605606if (const auto *MethodCall = dyn_cast<CXXMemberCallExpr>(Call)) {607if (const Expr *Arg = MethodCall->getImplicitObjectArgument()) {608if (!isa<CXXThisExpr>(Arg))609Env.ThisPointeeLoc =610cast<RecordStorageLocation>(getStorageLocation(*Arg));611// Otherwise (when the argument is `this`), retain the current612// environment's `ThisPointeeLoc`.613}614}615616if (Call->getType()->isRecordType() && Call->isPRValue())617Env.LocForRecordReturnVal = &Env.getResultObjectLocation(*Call);618619Env.pushCallInternal(Call->getDirectCallee(),620llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));621622return Env;623}624625Environment Environment::pushCall(const CXXConstructExpr *Call) const {626Environment Env(*this);627628Env.ThisPointeeLoc = &Env.getResultObjectLocation(*Call);629Env.LocForRecordReturnVal = &Env.getResultObjectLocation(*Call);630631Env.pushCallInternal(Call->getConstructor(),632llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));633634return Env;635}636637void Environment::pushCallInternal(const FunctionDecl *FuncDecl,638ArrayRef<const Expr *> Args) {639// Canonicalize to the definition of the function. This ensures that we're640// putting arguments into the same `ParamVarDecl`s` that the callee will later641// be retrieving them from.642assert(FuncDecl->getDefinition() != nullptr);643FuncDecl = FuncDecl->getDefinition();644645CallStack.push_back(FuncDecl);646647initFieldsGlobalsAndFuncs(getReferencedDecls(*FuncDecl));648649const auto *ParamIt = FuncDecl->param_begin();650651// FIXME: Parameters don't always map to arguments 1:1; examples include652// overloaded operators implemented as member functions, and parameter packs.653for (unsigned ArgIndex = 0; ArgIndex < Args.size(); ++ParamIt, ++ArgIndex) {654assert(ParamIt != FuncDecl->param_end());655const VarDecl *Param = *ParamIt;656setStorageLocation(*Param, createObject(*Param, Args[ArgIndex]));657}658659ResultObjectMap = std::make_shared<PrValueToResultObject>(660buildResultObjectMap(DACtx, FuncDecl, getThisPointeeStorageLocation(),661LocForRecordReturnVal));662}663664void Environment::popCall(const CallExpr *Call, const Environment &CalleeEnv) {665// We ignore some entries of `CalleeEnv`:666// - `DACtx` because is already the same in both667// - We don't want the callee's `DeclCtx`, `ReturnVal`, `ReturnLoc` or668// `ThisPointeeLoc` because they don't apply to us.669// - `DeclToLoc`, `ExprToLoc`, and `ExprToVal` capture information from the670// callee's local scope, so when popping that scope, we do not propagate671// the maps.672this->LocToVal = std::move(CalleeEnv.LocToVal);673this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);674675if (Call->isGLValue()) {676if (CalleeEnv.ReturnLoc != nullptr)677setStorageLocation(*Call, *CalleeEnv.ReturnLoc);678} else if (!Call->getType()->isVoidType()) {679if (CalleeEnv.ReturnVal != nullptr)680setValue(*Call, *CalleeEnv.ReturnVal);681}682}683684void Environment::popCall(const CXXConstructExpr *Call,685const Environment &CalleeEnv) {686// See also comment in `popCall(const CallExpr *, const Environment &)` above.687this->LocToVal = std::move(CalleeEnv.LocToVal);688this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);689}690691bool Environment::equivalentTo(const Environment &Other,692Environment::ValueModel &Model) const {693assert(DACtx == Other.DACtx);694695if (ReturnVal != Other.ReturnVal)696return false;697698if (ReturnLoc != Other.ReturnLoc)699return false;700701if (LocForRecordReturnVal != Other.LocForRecordReturnVal)702return false;703704if (ThisPointeeLoc != Other.ThisPointeeLoc)705return false;706707if (DeclToLoc != Other.DeclToLoc)708return false;709710if (ExprToLoc != Other.ExprToLoc)711return false;712713if (!compareKeyToValueMaps(ExprToVal, Other.ExprToVal, *this, Other, Model))714return false;715716if (!compareKeyToValueMaps(LocToVal, Other.LocToVal, *this, Other, Model))717return false;718719return true;720}721722LatticeEffect Environment::widen(const Environment &PrevEnv,723Environment::ValueModel &Model) {724assert(DACtx == PrevEnv.DACtx);725assert(ReturnVal == PrevEnv.ReturnVal);726assert(ReturnLoc == PrevEnv.ReturnLoc);727assert(LocForRecordReturnVal == PrevEnv.LocForRecordReturnVal);728assert(ThisPointeeLoc == PrevEnv.ThisPointeeLoc);729assert(CallStack == PrevEnv.CallStack);730assert(ResultObjectMap == PrevEnv.ResultObjectMap);731assert(InitialTargetFunc == PrevEnv.InitialTargetFunc);732assert(InitialTargetStmt == PrevEnv.InitialTargetStmt);733734auto Effect = LatticeEffect::Unchanged;735736// By the API, `PrevEnv` is a previous version of the environment for the same737// block, so we have some guarantees about its shape. In particular, it will738// be the result of a join or widen operation on previous values for this739// block. For `DeclToLoc`, `ExprToVal`, and `ExprToLoc`, join guarantees that740// these maps are subsets of the maps in `PrevEnv`. So, as long as we maintain741// this property here, we don't need change their current values to widen.742assert(DeclToLoc.size() <= PrevEnv.DeclToLoc.size());743assert(ExprToVal.size() <= PrevEnv.ExprToVal.size());744assert(ExprToLoc.size() <= PrevEnv.ExprToLoc.size());745746ExprToVal = widenKeyToValueMap(ExprToVal, PrevEnv.ExprToVal, *this, PrevEnv,747Model, Effect);748749LocToVal = widenKeyToValueMap(LocToVal, PrevEnv.LocToVal, *this, PrevEnv,750Model, Effect);751if (DeclToLoc.size() != PrevEnv.DeclToLoc.size() ||752ExprToLoc.size() != PrevEnv.ExprToLoc.size() ||753ExprToVal.size() != PrevEnv.ExprToVal.size() ||754LocToVal.size() != PrevEnv.LocToVal.size())755Effect = LatticeEffect::Changed;756757return Effect;758}759760Environment Environment::join(const Environment &EnvA, const Environment &EnvB,761Environment::ValueModel &Model,762ExprJoinBehavior ExprBehavior) {763assert(EnvA.DACtx == EnvB.DACtx);764assert(EnvA.LocForRecordReturnVal == EnvB.LocForRecordReturnVal);765assert(EnvA.ThisPointeeLoc == EnvB.ThisPointeeLoc);766assert(EnvA.CallStack == EnvB.CallStack);767assert(EnvA.ResultObjectMap == EnvB.ResultObjectMap);768assert(EnvA.InitialTargetFunc == EnvB.InitialTargetFunc);769assert(EnvA.InitialTargetStmt == EnvB.InitialTargetStmt);770771Environment JoinedEnv(*EnvA.DACtx);772773JoinedEnv.CallStack = EnvA.CallStack;774JoinedEnv.ResultObjectMap = EnvA.ResultObjectMap;775JoinedEnv.LocForRecordReturnVal = EnvA.LocForRecordReturnVal;776JoinedEnv.ThisPointeeLoc = EnvA.ThisPointeeLoc;777JoinedEnv.InitialTargetFunc = EnvA.InitialTargetFunc;778JoinedEnv.InitialTargetStmt = EnvA.InitialTargetStmt;779780const FunctionDecl *Func = EnvA.getCurrentFunc();781if (!Func) {782JoinedEnv.ReturnVal = nullptr;783} else {784JoinedEnv.ReturnVal =785joinValues(Func->getReturnType(), EnvA.ReturnVal, EnvA, EnvB.ReturnVal,786EnvB, JoinedEnv, Model);787}788789if (EnvA.ReturnLoc == EnvB.ReturnLoc)790JoinedEnv.ReturnLoc = EnvA.ReturnLoc;791else792JoinedEnv.ReturnLoc = nullptr;793794JoinedEnv.DeclToLoc = intersectDeclToLoc(EnvA.DeclToLoc, EnvB.DeclToLoc);795796// FIXME: update join to detect backedges and simplify the flow condition797// accordingly.798JoinedEnv.FlowConditionToken = EnvA.DACtx->joinFlowConditions(799EnvA.FlowConditionToken, EnvB.FlowConditionToken);800801JoinedEnv.LocToVal =802joinLocToVal(EnvA.LocToVal, EnvB.LocToVal, EnvA, EnvB, JoinedEnv, Model);803804if (ExprBehavior == KeepExprState) {805JoinedEnv.ExprToVal = joinExprMaps(EnvA.ExprToVal, EnvB.ExprToVal);806JoinedEnv.ExprToLoc = joinExprMaps(EnvA.ExprToLoc, EnvB.ExprToLoc);807}808809return JoinedEnv;810}811812Value *Environment::joinValues(QualType Ty, Value *Val1,813const Environment &Env1, Value *Val2,814const Environment &Env2, Environment &JoinedEnv,815Environment::ValueModel &Model) {816if (Val1 == nullptr || Val2 == nullptr)817// We can't say anything about the joined value -- even if one of the values818// is non-null, we don't want to simply propagate it, because it would be819// too specific: Because the other value is null, that means we have no820// information at all about the value (i.e. the value is unconstrained).821return nullptr;822823if (areEquivalentValues(*Val1, *Val2))824// Arbitrarily return one of the two values.825return Val1;826827return joinDistinctValues(Ty, *Val1, Env1, *Val2, Env2, JoinedEnv, Model);828}829830StorageLocation &Environment::createStorageLocation(QualType Type) {831return DACtx->createStorageLocation(Type);832}833834StorageLocation &Environment::createStorageLocation(const ValueDecl &D) {835// Evaluated declarations are always assigned the same storage locations to836// ensure that the environment stabilizes across loop iterations. Storage837// locations for evaluated declarations are stored in the analysis context.838return DACtx->getStableStorageLocation(D);839}840841StorageLocation &Environment::createStorageLocation(const Expr &E) {842// Evaluated expressions are always assigned the same storage locations to843// ensure that the environment stabilizes across loop iterations. Storage844// locations for evaluated expressions are stored in the analysis context.845return DACtx->getStableStorageLocation(E);846}847848void Environment::setStorageLocation(const ValueDecl &D, StorageLocation &Loc) {849assert(!DeclToLoc.contains(&D));850// The only kinds of declarations that may have a "variable" storage location851// are declarations of reference type and `BindingDecl`. For all other852// declaration, the storage location should be the stable storage location853// returned by `createStorageLocation()`.854assert(D.getType()->isReferenceType() || isa<BindingDecl>(D) ||855&Loc == &createStorageLocation(D));856DeclToLoc[&D] = &Loc;857}858859StorageLocation *Environment::getStorageLocation(const ValueDecl &D) const {860auto It = DeclToLoc.find(&D);861if (It == DeclToLoc.end())862return nullptr;863864StorageLocation *Loc = It->second;865866return Loc;867}868869void Environment::removeDecl(const ValueDecl &D) { DeclToLoc.erase(&D); }870871void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) {872// `DeclRefExpr`s to builtin function types aren't glvalues, for some reason,873// but we still want to be able to associate a `StorageLocation` with them,874// so allow these as an exception.875assert(E.isGLValue() ||876E.getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn));877const Expr &CanonE = ignoreCFGOmittedNodes(E);878assert(!ExprToLoc.contains(&CanonE));879ExprToLoc[&CanonE] = &Loc;880}881882StorageLocation *Environment::getStorageLocation(const Expr &E) const {883// See comment in `setStorageLocation()`.884assert(E.isGLValue() ||885E.getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn));886auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));887return It == ExprToLoc.end() ? nullptr : &*It->second;888}889890RecordStorageLocation &891Environment::getResultObjectLocation(const Expr &RecordPRValue) const {892assert(RecordPRValue.getType()->isRecordType());893assert(RecordPRValue.isPRValue());894895assert(ResultObjectMap != nullptr);896RecordStorageLocation *Loc = ResultObjectMap->lookup(&RecordPRValue);897assert(Loc != nullptr);898// In release builds, use the "stable" storage location if the map lookup899// failed.900if (Loc == nullptr)901return cast<RecordStorageLocation>(902DACtx->getStableStorageLocation(RecordPRValue));903return *Loc;904}905906PointerValue &Environment::getOrCreateNullPointerValue(QualType PointeeType) {907return DACtx->getOrCreateNullPointerValue(PointeeType);908}909910void Environment::initializeFieldsWithValues(RecordStorageLocation &Loc,911QualType Type) {912llvm::DenseSet<QualType> Visited;913int CreatedValuesCount = 0;914initializeFieldsWithValues(Loc, Type, Visited, 0, CreatedValuesCount);915if (CreatedValuesCount > MaxCompositeValueSize) {916llvm::errs() << "Attempting to initialize a huge value of type: " << Type917<< '\n';918}919}920921void Environment::setValue(const StorageLocation &Loc, Value &Val) {922// Records should not be associated with values.923assert(!isa<RecordStorageLocation>(Loc));924LocToVal[&Loc] = &Val;925}926927void Environment::setValue(const Expr &E, Value &Val) {928const Expr &CanonE = ignoreCFGOmittedNodes(E);929930assert(CanonE.isPRValue());931// Records should not be associated with values.932assert(!CanonE.getType()->isRecordType());933ExprToVal[&CanonE] = &Val;934}935936Value *Environment::getValue(const StorageLocation &Loc) const {937// Records should not be associated with values.938assert(!isa<RecordStorageLocation>(Loc));939return LocToVal.lookup(&Loc);940}941942Value *Environment::getValue(const ValueDecl &D) const {943auto *Loc = getStorageLocation(D);944if (Loc == nullptr)945return nullptr;946return getValue(*Loc);947}948949Value *Environment::getValue(const Expr &E) const {950// Records should not be associated with values.951assert(!E.getType()->isRecordType());952953if (E.isPRValue()) {954auto It = ExprToVal.find(&ignoreCFGOmittedNodes(E));955return It == ExprToVal.end() ? nullptr : It->second;956}957958auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));959if (It == ExprToLoc.end())960return nullptr;961return getValue(*It->second);962}963964Value *Environment::createValue(QualType Type) {965llvm::DenseSet<QualType> Visited;966int CreatedValuesCount = 0;967Value *Val = createValueUnlessSelfReferential(Type, Visited, /*Depth=*/0,968CreatedValuesCount);969if (CreatedValuesCount > MaxCompositeValueSize) {970llvm::errs() << "Attempting to initialize a huge value of type: " << Type971<< '\n';972}973return Val;974}975976Value *Environment::createValueUnlessSelfReferential(977QualType Type, llvm::DenseSet<QualType> &Visited, int Depth,978int &CreatedValuesCount) {979assert(!Type.isNull());980assert(!Type->isReferenceType());981assert(!Type->isRecordType());982983// Allow unlimited fields at depth 1; only cap at deeper nesting levels.984if ((Depth > 1 && CreatedValuesCount > MaxCompositeValueSize) ||985Depth > MaxCompositeValueDepth)986return nullptr;987988if (Type->isBooleanType()) {989CreatedValuesCount++;990return &makeAtomicBoolValue();991}992993if (Type->isIntegerType()) {994// FIXME: consider instead `return nullptr`, given that we do nothing useful995// with integers, and so distinguishing them serves no purpose, but could996// prevent convergence.997CreatedValuesCount++;998return &arena().create<IntegerValue>();999}10001001if (Type->isPointerType()) {1002CreatedValuesCount++;1003QualType PointeeType = Type->getPointeeType();1004StorageLocation &PointeeLoc =1005createLocAndMaybeValue(PointeeType, Visited, Depth, CreatedValuesCount);10061007return &arena().create<PointerValue>(PointeeLoc);1008}10091010return nullptr;1011}10121013StorageLocation &1014Environment::createLocAndMaybeValue(QualType Ty,1015llvm::DenseSet<QualType> &Visited,1016int Depth, int &CreatedValuesCount) {1017if (!Visited.insert(Ty.getCanonicalType()).second)1018return createStorageLocation(Ty.getNonReferenceType());1019auto EraseVisited = llvm::make_scope_exit(1020[&Visited, Ty] { Visited.erase(Ty.getCanonicalType()); });10211022Ty = Ty.getNonReferenceType();10231024if (Ty->isRecordType()) {1025auto &Loc = cast<RecordStorageLocation>(createStorageLocation(Ty));1026initializeFieldsWithValues(Loc, Ty, Visited, Depth, CreatedValuesCount);1027return Loc;1028}10291030StorageLocation &Loc = createStorageLocation(Ty);10311032if (Value *Val = createValueUnlessSelfReferential(Ty, Visited, Depth,1033CreatedValuesCount))1034setValue(Loc, *Val);10351036return Loc;1037}10381039void Environment::initializeFieldsWithValues(RecordStorageLocation &Loc,1040QualType Type,1041llvm::DenseSet<QualType> &Visited,1042int Depth,1043int &CreatedValuesCount) {1044auto initField = [&](QualType FieldType, StorageLocation &FieldLoc) {1045if (FieldType->isRecordType()) {1046auto &FieldRecordLoc = cast<RecordStorageLocation>(FieldLoc);1047initializeFieldsWithValues(FieldRecordLoc, FieldRecordLoc.getType(),1048Visited, Depth + 1, CreatedValuesCount);1049} else {1050if (getValue(FieldLoc) != nullptr)1051return;1052if (!Visited.insert(FieldType.getCanonicalType()).second)1053return;1054if (Value *Val = createValueUnlessSelfReferential(1055FieldType, Visited, Depth + 1, CreatedValuesCount))1056setValue(FieldLoc, *Val);1057Visited.erase(FieldType.getCanonicalType());1058}1059};10601061for (const FieldDecl *Field : DACtx->getModeledFields(Type)) {1062assert(Field != nullptr);1063QualType FieldType = Field->getType();10641065if (FieldType->isReferenceType()) {1066Loc.setChild(*Field,1067&createLocAndMaybeValue(FieldType, Visited, Depth + 1,1068CreatedValuesCount));1069} else {1070StorageLocation *FieldLoc = Loc.getChild(*Field);1071assert(FieldLoc != nullptr);1072initField(FieldType, *FieldLoc);1073}1074}1075for (const auto &[FieldName, FieldType] : DACtx->getSyntheticFields(Type)) {1076// Synthetic fields cannot have reference type, so we don't need to deal1077// with this case.1078assert(!FieldType->isReferenceType());1079initField(FieldType, Loc.getSyntheticField(FieldName));1080}1081}10821083StorageLocation &Environment::createObjectInternal(const ValueDecl *D,1084QualType Ty,1085const Expr *InitExpr) {1086if (Ty->isReferenceType()) {1087// Although variables of reference type always need to be initialized, it1088// can happen that we can't see the initializer, so `InitExpr` may still1089// be null.1090if (InitExpr) {1091if (auto *InitExprLoc = getStorageLocation(*InitExpr))1092return *InitExprLoc;1093}10941095// Even though we have an initializer, we might not get an1096// InitExprLoc, for example if the InitExpr is a CallExpr for which we1097// don't have a function body. In this case, we just invent a storage1098// location and value -- it's the best we can do.1099return createObjectInternal(D, Ty.getNonReferenceType(), nullptr);1100}11011102StorageLocation &Loc =1103D ? createStorageLocation(*D) : createStorageLocation(Ty);11041105if (Ty->isRecordType()) {1106auto &RecordLoc = cast<RecordStorageLocation>(Loc);1107if (!InitExpr)1108initializeFieldsWithValues(RecordLoc);1109} else {1110Value *Val = nullptr;1111if (InitExpr)1112// In the (few) cases where an expression is intentionally1113// "uninterpreted", `InitExpr` is not associated with a value. There are1114// two ways to handle this situation: propagate the status, so that1115// uninterpreted initializers result in uninterpreted variables, or1116// provide a default value. We choose the latter so that later refinements1117// of the variable can be used for reasoning about the surrounding code.1118// For this reason, we let this case be handled by the `createValue()`1119// call below.1120//1121// FIXME. If and when we interpret all language cases, change this to1122// assert that `InitExpr` is interpreted, rather than supplying a1123// default value (assuming we don't update the environment API to return1124// references).1125Val = getValue(*InitExpr);1126if (!Val)1127Val = createValue(Ty);1128if (Val)1129setValue(Loc, *Val);1130}11311132return Loc;1133}11341135void Environment::assume(const Formula &F) {1136DACtx->addFlowConditionConstraint(FlowConditionToken, F);1137}11381139bool Environment::proves(const Formula &F) const {1140return DACtx->flowConditionImplies(FlowConditionToken, F);1141}11421143bool Environment::allows(const Formula &F) const {1144return DACtx->flowConditionAllows(FlowConditionToken, F);1145}11461147void Environment::dump(raw_ostream &OS) const {1148llvm::DenseMap<const StorageLocation *, std::string> LocToName;1149if (LocForRecordReturnVal != nullptr)1150LocToName[LocForRecordReturnVal] = "(returned record)";1151if (ThisPointeeLoc != nullptr)1152LocToName[ThisPointeeLoc] = "this";11531154OS << "DeclToLoc:\n";1155for (auto [D, L] : DeclToLoc) {1156auto Iter = LocToName.insert({L, D->getNameAsString()}).first;1157OS << " [" << Iter->second << ", " << L << "]\n";1158}1159OS << "ExprToLoc:\n";1160for (auto [E, L] : ExprToLoc)1161OS << " [" << E << ", " << L << "]\n";11621163OS << "ExprToVal:\n";1164for (auto [E, V] : ExprToVal)1165OS << " [" << E << ", " << V << ": " << *V << "]\n";11661167OS << "LocToVal:\n";1168for (auto [L, V] : LocToVal) {1169OS << " [" << L;1170if (auto Iter = LocToName.find(L); Iter != LocToName.end())1171OS << " (" << Iter->second << ")";1172OS << ", " << V << ": " << *V << "]\n";1173}11741175if (const FunctionDecl *Func = getCurrentFunc()) {1176if (Func->getReturnType()->isReferenceType()) {1177OS << "ReturnLoc: " << ReturnLoc;1178if (auto Iter = LocToName.find(ReturnLoc); Iter != LocToName.end())1179OS << " (" << Iter->second << ")";1180OS << "\n";1181} else if (Func->getReturnType()->isRecordType() ||1182isa<CXXConstructorDecl>(Func)) {1183OS << "LocForRecordReturnVal: " << LocForRecordReturnVal << "\n";1184} else if (!Func->getReturnType()->isVoidType()) {1185if (ReturnVal == nullptr)1186OS << "ReturnVal: nullptr\n";1187else1188OS << "ReturnVal: " << *ReturnVal << "\n";1189}11901191if (isa<CXXMethodDecl>(Func)) {1192OS << "ThisPointeeLoc: " << ThisPointeeLoc << "\n";1193}1194}11951196OS << "\n";1197DACtx->dumpFlowCondition(FlowConditionToken, OS);1198}11991200void Environment::dump() const { dump(llvm::dbgs()); }12011202Environment::PrValueToResultObject Environment::buildResultObjectMap(1203DataflowAnalysisContext *DACtx, const FunctionDecl *FuncDecl,1204RecordStorageLocation *ThisPointeeLoc,1205RecordStorageLocation *LocForRecordReturnVal) {1206assert(FuncDecl->doesThisDeclarationHaveABody());12071208PrValueToResultObject Map = buildResultObjectMap(1209DACtx, FuncDecl->getBody(), ThisPointeeLoc, LocForRecordReturnVal);12101211ResultObjectVisitor Visitor(Map, LocForRecordReturnVal, *DACtx);1212if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FuncDecl))1213Visitor.TraverseConstructorInits(Ctor, ThisPointeeLoc);12141215return Map;1216}12171218Environment::PrValueToResultObject Environment::buildResultObjectMap(1219DataflowAnalysisContext *DACtx, Stmt *S,1220RecordStorageLocation *ThisPointeeLoc,1221RecordStorageLocation *LocForRecordReturnVal) {1222PrValueToResultObject Map;1223ResultObjectVisitor Visitor(Map, LocForRecordReturnVal, *DACtx);1224Visitor.TraverseStmt(S);1225return Map;1226}12271228RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE,1229const Environment &Env) {1230Expr *ImplicitObject = MCE.getImplicitObjectArgument();1231if (ImplicitObject == nullptr)1232return nullptr;1233if (ImplicitObject->getType()->isPointerType()) {1234if (auto *Val = Env.get<PointerValue>(*ImplicitObject))1235return &cast<RecordStorageLocation>(Val->getPointeeLoc());1236return nullptr;1237}1238return cast_or_null<RecordStorageLocation>(1239Env.getStorageLocation(*ImplicitObject));1240}12411242RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,1243const Environment &Env) {1244Expr *Base = ME.getBase();1245if (Base == nullptr)1246return nullptr;1247if (ME.isArrow()) {1248if (auto *Val = Env.get<PointerValue>(*Base))1249return &cast<RecordStorageLocation>(Val->getPointeeLoc());1250return nullptr;1251}1252return Env.get<RecordStorageLocation>(*Base);1253}12541255} // namespace dataflow1256} // namespace clang125712581259