llvm-project/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
Sam McCall bf47c1ed85 [dataflow] Extract arena for Value/StorageLocation out of DataflowAnalysisContext
DataflowAnalysisContext has a few too many responsibilities, this narrows them.

It also allows the Arena to be shared with analysis steps, which need to create
Values, without exposing the whole DACtx API (flow conditions etc).
This means Environment no longer needs to proxy all these methods.
(For now it still does, because there are many callsites to update, and maybe
if we separate bool formulas from values we can avoid churning them twice)

In future, if we untangle the concepts of Values from boolean formulas/atoms,
Arena would also be responsible for creating formulas and managing atom IDs.

Differential Revision: https://reviews.llvm.org/D148554
2023-04-19 14:32:13 +02:00

162 lines
5.7 KiB
C++

//===- unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp ---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <memory>
namespace {
using namespace clang;
using namespace dataflow;
class DataflowAnalysisContextTest : public ::testing::Test {
protected:
DataflowAnalysisContextTest()
: Context(std::make_unique<WatchedLiteralsSolver>()), A(Context.arena()) {
}
DataflowAnalysisContext Context;
Arena &A;
};
TEST_F(DataflowAnalysisContextTest, DistinctTopsNotEquivalent) {
auto &X = A.create<TopBoolValue>();
auto &Y = A.create<TopBoolValue>();
EXPECT_FALSE(Context.equivalentBoolValues(X, Y));
}
TEST_F(DataflowAnalysisContextTest, EmptyFlowCondition) {
auto &FC = A.makeFlowConditionToken();
auto &C = A.create<AtomicBoolValue>();
EXPECT_FALSE(Context.flowConditionImplies(FC, C));
}
TEST_F(DataflowAnalysisContextTest, AddFlowConditionConstraint) {
auto &FC = A.makeFlowConditionToken();
auto &C = A.create<AtomicBoolValue>();
Context.addFlowConditionConstraint(FC, C);
EXPECT_TRUE(Context.flowConditionImplies(FC, C));
}
TEST_F(DataflowAnalysisContextTest, ForkFlowCondition) {
auto &FC1 = A.makeFlowConditionToken();
auto &C1 = A.create<AtomicBoolValue>();
Context.addFlowConditionConstraint(FC1, C1);
// Forked flow condition inherits the constraints of its parent flow
// condition.
auto &FC2 = Context.forkFlowCondition(FC1);
EXPECT_TRUE(Context.flowConditionImplies(FC2, C1));
// Adding a new constraint to the forked flow condition does not affect its
// parent flow condition.
auto &C2 = A.create<AtomicBoolValue>();
Context.addFlowConditionConstraint(FC2, C2);
EXPECT_TRUE(Context.flowConditionImplies(FC2, C2));
EXPECT_FALSE(Context.flowConditionImplies(FC1, C2));
}
TEST_F(DataflowAnalysisContextTest, JoinFlowConditions) {
auto &C1 = A.create<AtomicBoolValue>();
auto &C2 = A.create<AtomicBoolValue>();
auto &C3 = A.create<AtomicBoolValue>();
auto &FC1 = A.makeFlowConditionToken();
Context.addFlowConditionConstraint(FC1, C1);
Context.addFlowConditionConstraint(FC1, C3);
auto &FC2 = A.makeFlowConditionToken();
Context.addFlowConditionConstraint(FC2, C2);
Context.addFlowConditionConstraint(FC2, C3);
auto &FC3 = Context.joinFlowConditions(FC1, FC2);
EXPECT_FALSE(Context.flowConditionImplies(FC3, C1));
EXPECT_FALSE(Context.flowConditionImplies(FC3, C2));
EXPECT_TRUE(Context.flowConditionImplies(FC3, C3));
}
TEST_F(DataflowAnalysisContextTest, FlowConditionTautologies) {
// Fresh flow condition with empty/no constraints is always true.
auto &FC1 = A.makeFlowConditionToken();
EXPECT_TRUE(Context.flowConditionIsTautology(FC1));
// Literal `true` is always true.
auto &FC2 = A.makeFlowConditionToken();
Context.addFlowConditionConstraint(FC2, A.makeLiteral(true));
EXPECT_TRUE(Context.flowConditionIsTautology(FC2));
// Literal `false` is never true.
auto &FC3 = A.makeFlowConditionToken();
Context.addFlowConditionConstraint(FC3, A.makeLiteral(false));
EXPECT_FALSE(Context.flowConditionIsTautology(FC3));
// We can't prove that an arbitrary bool A is always true...
auto &C1 = A.create<AtomicBoolValue>();
auto &FC4 = A.makeFlowConditionToken();
Context.addFlowConditionConstraint(FC4, C1);
EXPECT_FALSE(Context.flowConditionIsTautology(FC4));
// ... but we can prove A || !A is true.
auto &FC5 = A.makeFlowConditionToken();
Context.addFlowConditionConstraint(FC5, A.makeOr(C1, A.makeNot(C1)));
EXPECT_TRUE(Context.flowConditionIsTautology(FC5));
}
TEST_F(DataflowAnalysisContextTest, EquivBoolVals) {
auto &X = A.create<AtomicBoolValue>();
auto &Y = A.create<AtomicBoolValue>();
auto &Z = A.create<AtomicBoolValue>();
auto &True = A.makeLiteral(true);
auto &False = A.makeLiteral(false);
// X == X
EXPECT_TRUE(Context.equivalentBoolValues(X, X));
// X != Y
EXPECT_FALSE(Context.equivalentBoolValues(X, Y));
// !X != X
EXPECT_FALSE(Context.equivalentBoolValues(A.makeNot(X), X));
// !(!X) = X
EXPECT_TRUE(Context.equivalentBoolValues(A.makeNot(A.makeNot(X)), X));
// (X || X) == X
EXPECT_TRUE(Context.equivalentBoolValues(A.makeOr(X, X), X));
// (X || Y) != X
EXPECT_FALSE(Context.equivalentBoolValues(A.makeOr(X, Y), X));
// (X || True) == True
EXPECT_TRUE(Context.equivalentBoolValues(A.makeOr(X, True), True));
// (X || False) == X
EXPECT_TRUE(Context.equivalentBoolValues(A.makeOr(X, False), X));
// (X && X) == X
EXPECT_TRUE(Context.equivalentBoolValues(A.makeAnd(X, X), X));
// (X && Y) != X
EXPECT_FALSE(Context.equivalentBoolValues(A.makeAnd(X, Y), X));
// (X && True) == X
EXPECT_TRUE(Context.equivalentBoolValues(A.makeAnd(X, True), X));
// (X && False) == False
EXPECT_TRUE(Context.equivalentBoolValues(A.makeAnd(X, False), False));
// (X || Y) == (Y || X)
EXPECT_TRUE(Context.equivalentBoolValues(A.makeOr(X, Y), A.makeOr(Y, X)));
// (X && Y) == (Y && X)
EXPECT_TRUE(Context.equivalentBoolValues(A.makeAnd(X, Y), A.makeAnd(Y, X)));
// ((X || Y) || Z) == (X || (Y || Z))
EXPECT_TRUE(Context.equivalentBoolValues(A.makeOr(A.makeOr(X, Y), Z),
A.makeOr(X, A.makeOr(Y, Z))));
// ((X && Y) && Z) == (X && (Y && Z))
EXPECT_TRUE(Context.equivalentBoolValues(A.makeAnd(A.makeAnd(X, Y), Z),
A.makeAnd(X, A.makeAnd(Y, Z))));
}
} // namespace