Environment Environment::pushCall(const CallExpr *Call) const {
Environment Env(*this);
- // FIXME: Currently this only works if the callee is never a method and the
- // same callee is never analyzed from multiple separate callsites. To
- // generalize this, we'll need to store a "context" field (probably a stack of
- // `const CallExpr *`s) in the `Environment`, and then change the
- // `DataflowAnalysisContext` class to hold a map from contexts to "frames",
- // where each frame stores its own version of what are currently the
- // `DeclToLoc`, `ExprToLoc`, and `ThisPointeeLoc` fields.
-
const auto *FuncDecl = Call->getDirectCallee();
assert(FuncDecl != nullptr);
assert(FuncDecl->getBody() != nullptr);
for (; ArgIt != ArgEnd; ++ParamIt, ++ArgIt) {
assert(ParamIt != FuncDecl->param_end());
- const VarDecl *Param = *ParamIt;
const Expr *Arg = *ArgIt;
auto *ArgLoc = Env.getStorageLocation(*Arg, SkipPast::Reference);
assert(ArgLoc != nullptr);
- Env.setStorageLocation(*Param, *ArgLoc);
+
+ const VarDecl *Param = *ParamIt;
+ auto &Loc = Env.createStorageLocation(*Param);
+ Env.setStorageLocation(*Param, Loc);
+
+ QualType ParamType = Param->getType();
+ if (ParamType->isReferenceType()) {
+ auto &Val = Env.takeOwnership(std::make_unique<ReferenceValue>(*ArgLoc));
+ Env.setValue(Loc, Val);
+ } else if (auto *ArgVal = Env.getValue(*ArgLoc)) {
+ Env.setValue(Loc, *ArgVal);
+ } else if (Value *Val = Env.createValue(ParamType)) {
+ Env.setValue(Loc, *Val);
+ }
}
return Env;
}
+void Environment::popCall(const Environment &CalleeEnv) {
+ // We ignore `DACtx` because it's already the same in both. We don't bring
+ // back `DeclToLoc` and `ExprToLoc` because we want to be able to later
+ // analyze the same callee in a different context, and `setStorageLocation`
+ // requires there to not already be a storage location assigned. Conceptually,
+ // these maps capture information from the local scope, so when popping that
+ // scope, we do not propagate the maps.
+ this->LocToVal = std::move(CalleeEnv.LocToVal);
+ this->MemberLocToStruct = std::move(CalleeEnv.MemberLocToStruct);
+ this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);
+}
+
bool Environment::equivalentTo(const Environment &Other,
Environment::ValueModel &Model) const {
assert(DACtx == Other.DACtx);
/*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
}
+TEST(TransferTest, ContextSensitiveSetBothTrueAndFalse) {
+ std::string Code = R"(
+ bool GiveBool();
+ void SetBool(bool &Var, bool Val) { Var = Val; }
+
+ void target() {
+ bool Foo = GiveBool();
+ bool Bar = GiveBool();
+ SetBool(Foo, true);
+ SetBool(Bar, false);
+ // [[p]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ auto &FooVal =
+ *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
+ EXPECT_TRUE(Env.flowConditionImplies(FooVal));
+ EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
+
+ auto &BarVal =
+ *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
+ EXPECT_FALSE(Env.flowConditionImplies(BarVal));
+ EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
+ },
+ {/*.ApplyBuiltinTransfer=*/true,
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+}
+
+TEST(TransferTest, ContextSensitiveSetTwoLayers) {
+ std::string Code = R"(
+ bool GiveBool();
+ void SetBool1(bool &Var) { Var = true; }
+ void SetBool2(bool &Var) { SetBool1(Var); }
+
+ void target() {
+ bool Foo = GiveBool();
+ SetBool2(Foo);
+ // [[p]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ auto &FooVal =
+ *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
+ EXPECT_FALSE(Env.flowConditionImplies(FooVal));
+ EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
+ },
+ {/*.ApplyBuiltinTransfer=*/true,
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+}
+
+TEST(TransferTest, ContextSensitiveSetMultipleLines) {
+ std::string Code = R"(
+ void SetBools(bool &Var1, bool &Var2) {
+ Var1 = true;
+ Var2 = false;
+ }
+
+ void target() {
+ bool Foo = false;
+ bool Bar = true;
+ SetBools(Foo, Bar);
+ // [[p]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ auto &FooVal =
+ *cast<BoolValue>(Env.getValue(*FooDecl, SkipPast::None));
+ EXPECT_TRUE(Env.flowConditionImplies(FooVal));
+ EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(FooVal)));
+
+ auto &BarVal =
+ *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
+ EXPECT_FALSE(Env.flowConditionImplies(BarVal));
+ EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
+ },
+ {/*.ApplyBuiltinTransfer=*/true,
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+}
+
+TEST(TransferTest, ContextSensitiveSetMultipleBlocks) {
+ std::string Code = R"(
+ void IfCond(bool Cond, bool &Then, bool &Else) {
+ if (Cond) {
+ Then = true;
+ } else {
+ Else = true;
+ }
+ }
+
+ void target() {
+ bool Foo = false;
+ bool Bar = false;
+ bool Baz = false;
+ IfCond(Foo, Bar, Baz);
+ // [[p]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
+ ASSERT_THAT(BazDecl, NotNull());
+
+ auto &BarVal =
+ *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
+ EXPECT_FALSE(Env.flowConditionImplies(BarVal));
+ EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
+
+ auto &BazVal =
+ *cast<BoolValue>(Env.getValue(*BazDecl, SkipPast::None));
+ EXPECT_TRUE(Env.flowConditionImplies(BazVal));
+ EXPECT_FALSE(Env.flowConditionImplies(Env.makeNot(BazVal)));
+ },
+ {/*.ApplyBuiltinTransfer=*/true,
+ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}});
+}
+
} // namespace