#include <utility>
#include "TestingSupport.h"
#include "clang/Analysis/FlowSensitive/Solver.h"
#include "TestingSupport.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace {
using namespace clang;
using namespace dataflow;
using test::ConstraintContext;
using testing::_;
using testing::AnyOf;
using testing::Optional;
using testing::Pair;
using testing::UnorderedElementsAre;
Solver::Result solve(llvm::DenseSet<BoolValue *> Vals) {
return WatchedLiteralsSolver().solve(std::move(Vals));
}
void expectUnsatisfiable(Solver::Result Result) {
EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Unsatisfiable);
EXPECT_FALSE(Result.getSolution().has_value());
}
template <typename Matcher>
void expectSatisfiable(Solver::Result Result, Matcher Solution) {
EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Satisfiable);
EXPECT_THAT(Result.getSolution(), Optional(Solution));
}
TEST(SolverTest, Var) {
ConstraintContext Ctx;
auto X = Ctx.atom();
expectSatisfiable(
solve({X}),
UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue)));
}
TEST(SolverTest, NegatedVar) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto NotX = Ctx.neg(X);
expectSatisfiable(
solve({NotX}),
UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse)));
}
TEST(SolverTest, UnitConflict) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto NotX = Ctx.neg(X);
expectUnsatisfiable(solve({X, NotX}));
}
TEST(SolverTest, DistinctVars) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto NotY = Ctx.neg(Y);
expectSatisfiable(
solve({X, NotY}),
UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue),
Pair(Y, Solver::Result::Assignment::AssignedFalse)));
}
TEST(SolverTest, DoubleNegation) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto NotX = Ctx.neg(X);
auto NotNotX = Ctx.neg(NotX);
expectUnsatisfiable(solve({NotNotX, NotX}));
}
TEST(SolverTest, NegatedDisjunction) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto XOrY = Ctx.disj(X, Y);
auto NotXOrY = Ctx.neg(XOrY);
expectUnsatisfiable(solve({NotXOrY, XOrY}));
}
TEST(SolverTest, NegatedConjunction) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto XAndY = Ctx.conj(X, Y);
auto NotXAndY = Ctx.neg(XAndY);
expectUnsatisfiable(solve({NotXAndY, XAndY}));
}
TEST(SolverTest, DisjunctionSameVarWithNegation) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto NotX = Ctx.neg(X);
auto XOrNotX = Ctx.disj(X, NotX);
expectSatisfiable(solve({XOrNotX}), _);
}
TEST(SolverTest, DisjunctionSameVar) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto XOrX = Ctx.disj(X, X);
expectSatisfiable(solve({XOrX}), _);
}
TEST(SolverTest, ConjunctionSameVarsConflict) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto NotX = Ctx.neg(X);
auto XAndNotX = Ctx.conj(X, NotX);
expectUnsatisfiable(solve({XAndNotX}));
}
TEST(SolverTest, ConjunctionSameVar) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto XAndX = Ctx.conj(X, X);
expectSatisfiable(solve({XAndX}), _);
}
TEST(SolverTest, PureVar) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto NotX = Ctx.neg(X);
auto NotXOrY = Ctx.disj(NotX, Y);
auto NotY = Ctx.neg(Y);
auto NotXOrNotY = Ctx.disj(NotX, NotY);
expectSatisfiable(
solve({NotXOrY, NotXOrNotY}),
UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
Pair(Y, _)));
}
TEST(SolverTest, MustAssumeVarIsFalse) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto XOrY = Ctx.disj(X, Y);
auto NotX = Ctx.neg(X);
auto NotXOrY = Ctx.disj(NotX, Y);
auto NotY = Ctx.neg(Y);
auto NotXOrNotY = Ctx.disj(NotX, NotY);
expectSatisfiable(
solve({XOrY, NotXOrY, NotXOrNotY}),
UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
Pair(Y, Solver::Result::Assignment::AssignedTrue)));
}
TEST(SolverTest, DeepConflict) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto XOrY = Ctx.disj(X, Y);
auto NotX = Ctx.neg(X);
auto NotXOrY = Ctx.disj(NotX, Y);
auto NotY = Ctx.neg(Y);
auto NotXOrNotY = Ctx.disj(NotX, NotY);
auto XOrNotY = Ctx.disj(X, NotY);
expectUnsatisfiable(solve({XOrY, NotXOrY, NotXOrNotY, XOrNotY}));
}
TEST(SolverTest, IffIsEquivalentToDNF) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto NotX = Ctx.neg(X);
auto NotY = Ctx.neg(Y);
auto XIffY = Ctx.iff(X, Y);
auto XIffYDNF = Ctx.disj(Ctx.conj(X, Y), Ctx.conj(NotX, NotY));
auto NotEquivalent = Ctx.neg(Ctx.iff(XIffY, XIffYDNF));
expectUnsatisfiable(solve({NotEquivalent}));
}
TEST(SolverTest, IffSameVars) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto XEqX = Ctx.iff(X, X);
expectSatisfiable(solve({XEqX}), _);
}
TEST(SolverTest, IffDistinctVars) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto XEqY = Ctx.iff(X, Y);
expectSatisfiable(
solve({XEqY}),
AnyOf(UnorderedElementsAre(
Pair(X, Solver::Result::Assignment::AssignedTrue),
Pair(Y, Solver::Result::Assignment::AssignedTrue)),
UnorderedElementsAre(
Pair(X, Solver::Result::Assignment::AssignedFalse),
Pair(Y, Solver::Result::Assignment::AssignedFalse))));
}
TEST(SolverTest, IffWithUnits) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto XEqY = Ctx.iff(X, Y);
expectSatisfiable(
solve({XEqY, X, Y}),
UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue),
Pair(Y, Solver::Result::Assignment::AssignedTrue)));
}
TEST(SolverTest, IffWithUnitsConflict) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto XEqY = Ctx.iff(X, Y);
auto NotY = Ctx.neg(Y);
expectUnsatisfiable(solve({XEqY, X, NotY}));
}
TEST(SolverTest, IffTransitiveConflict) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto Z = Ctx.atom();
auto XEqY = Ctx.iff(X, Y);
auto YEqZ = Ctx.iff(Y, Z);
auto NotX = Ctx.neg(X);
expectUnsatisfiable(solve({XEqY, YEqZ, Z, NotX}));
}
TEST(SolverTest, DeMorgan) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto Z = Ctx.atom();
auto W = Ctx.atom();
auto A = Ctx.iff(Ctx.neg(Ctx.disj(X, Y)), Ctx.conj(Ctx.neg(X), Ctx.neg(Y)));
auto B = Ctx.iff(Ctx.neg(Ctx.conj(Z, W)), Ctx.disj(Ctx.neg(Z), Ctx.neg(W)));
expectSatisfiable(solve({A, B}), _);
}
TEST(SolverTest, RespectsAdditionalConstraints) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto XEqY = Ctx.iff(X, Y);
auto NotY = Ctx.neg(Y);
expectUnsatisfiable(solve({XEqY, X, NotY}));
}
TEST(SolverTest, ImplicationIsEquivalentToDNF) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto XImpliesY = Ctx.impl(X, Y);
auto XImpliesYDNF = Ctx.disj(Ctx.neg(X), Y);
auto NotEquivalent = Ctx.neg(Ctx.iff(XImpliesY, XImpliesYDNF));
expectUnsatisfiable(solve({NotEquivalent}));
}
TEST(SolverTest, ImplicationConflict) {
ConstraintContext Ctx;
auto X = Ctx.atom();
auto Y = Ctx.atom();
auto *XImplY = Ctx.impl(X, Y);
auto *XAndNotY = Ctx.conj(X, Ctx.neg(Y));
expectUnsatisfiable(solve({XImplY, XAndNotY}));
}
}