const Environment &Env2) {
// FIXME: Consider adding QualType to StructValue and removing the Type
// argument here.
- return false;
+ //
+ // FIXME: default to a sound comparison and/or expand the comparison logic
+ // built into the framework to support broader forms of equivalence than
+ // strict pointer equality.
+ return true;
}
/// Modifies `MergedVal` to approximate both `Val1` and `Val2`. This could
const Environment &Env1, const Value &Val2,
const Environment &Env2, Value &MergedVal,
Environment &MergedEnv) {
- return false;
+ return true;
}
};
if (auto *IndVal1 = dyn_cast<IndirectionValue>(Val1)) {
auto *IndVal2 = cast<IndirectionValue>(Val2);
assert(IndVal1->getKind() == IndVal2->getKind());
- return &IndVal1->getPointeeLoc() == &IndVal2->getPointeeLoc();
+ if (&IndVal1->getPointeeLoc() == &IndVal2->getPointeeLoc())
+ return true;
}
return Model.compareEquivalent(Type, *Val1, Env1, *Val2, Env2);
// depends on `FC1` and `FC2`) and modify `flowConditionImplies` to construct
// a formula that includes the bi-conditionals for all flow condition atoms in
// the transitive set, before invoking the solver.
+ //
+ // FIXME: Does not work for backedges, since the two (or more) paths will not
+ // have mutually exclusive conditions.
if (auto *Expr1 = dyn_cast<BoolValue>(Val1)) {
for (BoolValue *Constraint : Env1.getFlowConditionConstraints()) {
Expr1 = &Env1.makeAnd(*Expr1, *Constraint);
if (MemberLocToStruct != Other.MemberLocToStruct)
return false;
- if (LocToVal.size() != Other.LocToVal.size())
- return false;
-
+ // Compare the contents for the intersection of their domains.
for (auto &Entry : LocToVal) {
const StorageLocation *Loc = Entry.first;
assert(Loc != nullptr);
auto It = Other.LocToVal.find(Loc);
if (It == Other.LocToVal.end())
- return false;
+ continue;
assert(It->second != nullptr);
if (!equivalentValues(Loc->getType(), Val, *this, It->second, Other, Model))
continue;
assert(It->second != nullptr);
- if (equivalentValues(Loc->getType(), Val, *this, It->second, Other,
- Model)) {
+ if (Val == It->second) {
LocToVal.insert({Loc, Val});
continue;
}
});
}
+TEST_F(TransferTest, LoopWithAssignmentConverges) {
+ std::string Code = R"(
+
+ bool &foo();
+
+ void target() {
+ do {
+ bool Bar = foo();
+ if (Bar) break;
+ (void)Bar;
+ /*[[p]]*/
+ } while (true);
+ }
+ )";
+ // The key property that we are verifying is implicit in `runDataflow` --
+ // namely, that the analysis succeeds, rather than hitting the maximum number
+ // of iterations.
+ 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());
+
+ auto &BarVal = *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::None));
+ EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
+ });
+}
+
+TEST_F(TransferTest, LoopWithReferenceAssignmentConverges) {
+ std::string Code = R"(
+
+ bool &foo();
+
+ void target() {
+ do {
+ bool& Bar = foo();
+ if (Bar) break;
+ (void)Bar;
+ /*[[p]]*/
+ } while (true);
+ }
+ )";
+ // The key property that we are verifying is implicit in `runDataflow` --
+ // namely, that the analysis succeeds, rather than hitting the maximum number
+ // of iterations.
+ 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());
+
+ auto &BarVal =
+ *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::Reference));
+ EXPECT_TRUE(Env.flowConditionImplies(Env.makeNot(BarVal)));
+ });
+}
+
} // namespace
if (HasValue2 == nullptr)
return false;
- assert(HasValue1 != HasValue2);
- cast<StructValue>(&MergedVal)->setProperty("has_value", HasValueTop);
+ if (HasValue1 == HasValue2)
+ cast<StructValue>(&MergedVal)->setProperty("has_value", *HasValue1);
+ else
+ cast<StructValue>(&MergedVal)->setProperty("has_value", HasValueTop);
return true;
}