diff --git a/clang/lib/Analysis/FlowSensitive/Arena.cpp b/clang/lib/Analysis/FlowSensitive/Arena.cpp --- a/clang/lib/Analysis/FlowSensitive/Arena.cpp +++ b/clang/lib/Analysis/FlowSensitive/Arena.cpp @@ -10,6 +10,10 @@ namespace clang::dataflow { +// Compute a canonical key for an unordered pair of operands, so that we +// can intern (A & B) and (B & A) to the same formula. +// This does not affect the actual order used in the formula, which is always +// that of the first call to makeAnd(). static std::pair makeCanonicalBoolValuePair(BoolValue &LHS, BoolValue &RHS) { auto Res = std::make_pair(&LHS, &RHS); diff --git a/clang/unittests/Analysis/FlowSensitive/ArenaTest.cpp b/clang/unittests/Analysis/FlowSensitive/ArenaTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/ArenaTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/ArenaTest.cpp @@ -147,5 +147,28 @@ EXPECT_EQ(&Formula1, &Formula2); } +// Tests that the structure of the formula we end up with does not depend on +// details like the pointer address of internal values. +TEST_F(ArenaTest, Canonicalization) { + StringRef Expected = "((V0 | V1) = (V1 & V0))"; + + auto &AX = A.create(); + auto &AY = A.create(); + auto &AOr = A.makeOr(AX, AY); + auto &AAnd = A.makeAnd(AY, AX); + auto &AEqual = A.makeEquals(AOr, AAnd); + EXPECT_EQ(Expected, llvm::to_string(A.getFormula(AEqual))); + + // Same, but create the & clause first. + // The relative order of (&AOr, &AAnd) and (&BOr, &BAnd) likely differs. + Arena B; + auto &BX = B.create(); + auto &BY = B.create(); + auto &BAnd = B.makeAnd(BX, BY); + auto &BOr = B.makeOr(BY, BX); + auto &BEqual = B.makeEquals(BOr, BAnd); + EXPECT_EQ(Expected, llvm::to_string(B.getFormula(BEqual))); +} + } // namespace } // namespace clang::dataflow