[AMDGPU] Add True16 register classes.
[llvm-project.git] / clang / unittests / Analysis / FlowSensitive / TypeErasedDataflowAnalysisTest.cpp
blob3fc1bb6692acf0b26b077cebeca1062632ca2535
1 //===- unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp ===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "TestingSupport.h"
10 #include "clang/AST/Decl.h"
11 #include "clang/AST/ExprCXX.h"
12 #include "clang/AST/Type.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/ASTMatchers/ASTMatchers.h"
15 #include "clang/Analysis/CFG.h"
16 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
17 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
18 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
19 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
20 #include "clang/Analysis/FlowSensitive/DebugSupport.h"
21 #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
22 #include "clang/Analysis/FlowSensitive/Value.h"
23 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
24 #include "clang/Tooling/Tooling.h"
25 #include "llvm/ADT/STLExtras.h"
26 #include "llvm/ADT/SmallSet.h"
27 #include "llvm/ADT/StringMap.h"
28 #include "llvm/ADT/StringRef.h"
29 #include "llvm/Support/Error.h"
30 #include "llvm/Testing/ADT/StringMapEntry.h"
31 #include "llvm/Testing/Support/Error.h"
32 #include "gmock/gmock.h"
33 #include "gtest/gtest.h"
34 #include <cassert>
35 #include <memory>
36 #include <optional>
37 #include <ostream>
38 #include <string>
39 #include <utility>
40 #include <vector>
42 namespace {
44 using namespace clang;
45 using namespace dataflow;
46 using namespace test;
47 using namespace ast_matchers;
48 using llvm::IsStringMapEntry;
49 using ::testing::DescribeMatcher;
50 using ::testing::ElementsAre;
51 using ::testing::IsEmpty;
52 using ::testing::NotNull;
53 using ::testing::Test;
54 using ::testing::UnorderedElementsAre;
56 template <typename AnalysisT>
57 llvm::Expected<std::vector<
58 std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
59 runAnalysis(llvm::StringRef Code, AnalysisT (*MakeAnalysis)(ASTContext &)) {
60 std::unique_ptr<ASTUnit> AST =
61 tooling::buildASTFromCodeWithArgs(Code, {"-std=c++11"});
63 auto *Func = selectFirst<FunctionDecl>(
64 "func", match(functionDecl(ast_matchers::hasName("target")).bind("func"),
65 AST->getASTContext()));
66 assert(Func != nullptr);
68 auto CFCtx =
69 llvm::cantFail(ControlFlowContext::build(*Func));
71 AnalysisT Analysis = MakeAnalysis(AST->getASTContext());
72 DataflowAnalysisContext DACtx(std::make_unique<WatchedLiteralsSolver>());
73 Environment Env(DACtx);
75 return runDataflowAnalysis(CFCtx, Analysis, Env);
78 TEST(DataflowAnalysisTest, NoopAnalysis) {
79 auto BlockStates = llvm::cantFail(
80 runAnalysis<NoopAnalysis>("void target() {}", [](ASTContext &C) {
81 return NoopAnalysis(C,
82 // Don't use builtin transfer function.
83 DataflowAnalysisOptions{std::nullopt});
84 }));
85 EXPECT_EQ(BlockStates.size(), 2u);
86 EXPECT_TRUE(BlockStates[0].has_value());
87 EXPECT_TRUE(BlockStates[1].has_value());
90 // Basic test that `diagnoseFunction` calls the Diagnoser function for the
91 // number of elements expected.
92 TEST(DataflowAnalysisTest, DiagnoseFunctionDiagnoserCalledOnEachElement) {
93 std::string Code = R"(void target() { int x = 0; ++x; })";
94 std::unique_ptr<ASTUnit> AST =
95 tooling::buildASTFromCodeWithArgs(Code, {"-std=c++11"});
97 auto *Func =
98 cast<FunctionDecl>(findValueDecl(AST->getASTContext(), "target"));
99 auto Diagnoser = [](const CFGElement &Elt, ASTContext &,
100 const TransferStateForDiagnostics<NoopLattice> &) {
101 llvm::SmallVector<std::string> Diagnostics(1);
102 llvm::raw_string_ostream OS(Diagnostics.front());
103 Elt.dumpToStream(OS);
104 return Diagnostics;
106 auto Result = diagnoseFunction<NoopAnalysis, std::string>(
107 *Func, AST->getASTContext(), Diagnoser);
108 // `diagnoseFunction` provides no guarantees about the order in which elements
109 // are visited, so we use `UnorderedElementsAre`.
110 EXPECT_THAT_EXPECTED(Result, llvm::HasValue(UnorderedElementsAre(
111 "0\n", "int x = 0;\n", "x\n", "++x\n")));
114 struct NonConvergingLattice {
115 int State;
117 bool operator==(const NonConvergingLattice &Other) const {
118 return State == Other.State;
121 LatticeJoinEffect join(const NonConvergingLattice &Other) {
122 if (Other.State == 0)
123 return LatticeJoinEffect::Unchanged;
124 State += Other.State;
125 return LatticeJoinEffect::Changed;
129 class NonConvergingAnalysis
130 : public DataflowAnalysis<NonConvergingAnalysis, NonConvergingLattice> {
131 public:
132 explicit NonConvergingAnalysis(ASTContext &Context)
133 : DataflowAnalysis<NonConvergingAnalysis, NonConvergingLattice>(
134 Context,
135 // Don't apply builtin transfer function.
136 DataflowAnalysisOptions{std::nullopt}) {}
138 static NonConvergingLattice initialElement() { return {0}; }
140 void transfer(const CFGElement &, NonConvergingLattice &E, Environment &) {
141 ++E.State;
145 TEST(DataflowAnalysisTest, NonConvergingAnalysis) {
146 std::string Code = R"(
147 void target() {
148 while(true) {}
151 auto Res = runAnalysis<NonConvergingAnalysis>(
152 Code, [](ASTContext &C) { return NonConvergingAnalysis(C); });
153 EXPECT_EQ(llvm::toString(Res.takeError()),
154 "maximum number of iterations reached");
157 // Regression test for joins of bool-typed lvalue expressions. The first loop
158 // results in two passes through the code that follows. Each pass results in a
159 // different `StorageLocation` for the pointee of `v`. Then, the second loop
160 // causes a join at the loop head where the two environments map expresssion
161 // `*v` to different `StorageLocation`s.
163 // An earlier version crashed for this condition (for boolean-typed lvalues), so
164 // this test only verifies that the analysis runs successfully, without
165 // examining any details of the results.
166 TEST(DataflowAnalysisTest, JoinBoolLValues) {
167 std::string Code = R"(
168 void target() {
169 for (int x = 1; x; x = 0)
170 (void)x;
171 bool *v;
172 if (*v)
173 for (int x = 1; x; x = 0)
174 (void)x;
177 ASSERT_THAT_ERROR(
178 runAnalysis<NoopAnalysis>(Code,
179 [](ASTContext &C) {
180 auto EnableBuiltIns = DataflowAnalysisOptions{
181 DataflowAnalysisContext::Options{}};
182 return NoopAnalysis(C, EnableBuiltIns);
184 .takeError(),
185 llvm::Succeeded());
188 struct FunctionCallLattice {
189 using FunctionSet = llvm::SmallSet<std::string, 8>;
190 FunctionSet CalledFunctions;
192 bool operator==(const FunctionCallLattice &Other) const {
193 return CalledFunctions == Other.CalledFunctions;
196 LatticeJoinEffect join(const FunctionCallLattice &Other) {
197 if (Other.CalledFunctions.empty())
198 return LatticeJoinEffect::Unchanged;
199 const size_t size_before = CalledFunctions.size();
200 CalledFunctions.insert(Other.CalledFunctions.begin(),
201 Other.CalledFunctions.end());
202 return CalledFunctions.size() == size_before ? LatticeJoinEffect::Unchanged
203 : LatticeJoinEffect::Changed;
207 std::ostream &operator<<(std::ostream &OS, const FunctionCallLattice &L) {
208 std::string S;
209 llvm::raw_string_ostream ROS(S);
210 llvm::interleaveComma(L.CalledFunctions, ROS);
211 return OS << "{" << S << "}";
214 class FunctionCallAnalysis
215 : public DataflowAnalysis<FunctionCallAnalysis, FunctionCallLattice> {
216 public:
217 explicit FunctionCallAnalysis(ASTContext &Context)
218 : DataflowAnalysis<FunctionCallAnalysis, FunctionCallLattice>(Context) {}
220 static FunctionCallLattice initialElement() { return {}; }
222 void transfer(const CFGElement &Elt, FunctionCallLattice &E, Environment &) {
223 auto CS = Elt.getAs<CFGStmt>();
224 if (!CS)
225 return;
226 const auto *S = CS->getStmt();
227 if (auto *C = dyn_cast<CallExpr>(S)) {
228 if (auto *F = dyn_cast<FunctionDecl>(C->getCalleeDecl())) {
229 E.CalledFunctions.insert(F->getNameInfo().getAsString());
235 class NoreturnDestructorTest : public Test {
236 protected:
237 template <typename Matcher>
238 void runDataflow(llvm::StringRef Code, Matcher Expectations) {
239 tooling::FileContentMappings FilesContents;
240 FilesContents.push_back(std::make_pair<std::string, std::string>(
241 "noreturn_destructor_test_defs.h", R"(
242 int foo();
244 class Fatal {
245 public:
246 ~Fatal() __attribute__((noreturn));
247 int bar();
248 int baz();
251 class NonFatal {
252 public:
253 ~NonFatal();
254 int bar();
256 )"));
258 ASSERT_THAT_ERROR(
259 test::checkDataflow<FunctionCallAnalysis>(
260 AnalysisInputs<FunctionCallAnalysis>(
261 Code, ast_matchers::hasName("target"),
262 [](ASTContext &C, Environment &) {
263 return FunctionCallAnalysis(C);
265 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"})
266 .withASTBuildVirtualMappedFiles(std::move(FilesContents)),
267 /*VerifyResults=*/
268 [&Expectations](
269 const llvm::StringMap<
270 DataflowAnalysisState<FunctionCallLattice>> &Results,
271 const AnalysisOutputs &) {
272 EXPECT_THAT(Results, Expectations);
274 llvm::Succeeded());
278 MATCHER_P(HoldsFunctionCallLattice, m,
279 ((negation ? "doesn't hold" : "holds") +
280 llvm::StringRef(" a lattice element that ") +
281 DescribeMatcher<FunctionCallLattice>(m))
282 .str()) {
283 return ExplainMatchResult(m, arg.Lattice, result_listener);
286 MATCHER_P(HasCalledFunctions, m,
287 ((negation ? "doesn't hold" : "holds") +
288 llvm::StringRef(" a set of called functions that ") +
289 DescribeMatcher<FunctionCallLattice::FunctionSet>(m))
290 .str()) {
291 return ExplainMatchResult(m, arg.CalledFunctions, result_listener);
294 TEST_F(NoreturnDestructorTest, ConditionalOperatorBothBranchesReturn) {
295 std::string Code = R"(
296 #include "noreturn_destructor_test_defs.h"
298 void target(bool b) {
299 int value = b ? foo() : NonFatal().bar();
300 (void)0;
301 // [[p]]
304 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
305 "p", HoldsFunctionCallLattice(HasCalledFunctions(
306 UnorderedElementsAre("foo", "bar"))))));
309 TEST_F(NoreturnDestructorTest, ConditionalOperatorLeftBranchReturns) {
310 std::string Code = R"(
311 #include "noreturn_destructor_test_defs.h"
313 void target(bool b) {
314 int value = b ? foo() : Fatal().bar();
315 (void)0;
316 // [[p]]
319 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
320 "p", HoldsFunctionCallLattice(HasCalledFunctions(
321 UnorderedElementsAre("foo"))))));
324 TEST_F(NoreturnDestructorTest,
325 ConditionalOperatorConstantCondition_LeftBranchReturns) {
326 std::string Code = R"(
327 #include "noreturn_destructor_test_defs.h"
329 void target() {
330 int value = true ? foo() : Fatal().bar();
331 (void)0;
332 // [[p]]
335 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
336 "p", HoldsFunctionCallLattice(HasCalledFunctions(
337 UnorderedElementsAre("foo"))))));
340 TEST_F(NoreturnDestructorTest, ConditionalOperatorRightBranchReturns) {
341 std::string Code = R"(
342 #include "noreturn_destructor_test_defs.h"
344 void target(bool b) {
345 int value = b ? Fatal().bar() : foo();
346 (void)0;
347 // [[p]]
350 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
351 "p", HoldsFunctionCallLattice(HasCalledFunctions(
352 UnorderedElementsAre("foo"))))));
355 TEST_F(NoreturnDestructorTest,
356 ConditionalOperatorConstantCondition_RightBranchReturns) {
357 std::string Code = R"(
358 #include "noreturn_destructor_test_defs.h"
360 void target() {
361 int value = false ? Fatal().bar() : foo();
362 (void)0;
363 // [[p]]
366 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
367 "p", HoldsFunctionCallLattice(HasCalledFunctions(
368 UnorderedElementsAre("foo"))))));
371 TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchesDoNotReturn) {
372 std::string Code = R"(
373 #include "noreturn_destructor_test_defs.h"
375 void target(bool b1, bool b2) {
376 int value = b1 ? foo() : (b2 ? Fatal().bar() : Fatal().baz());
377 (void)0;
378 // [[p]]
381 runDataflow(Code, IsEmpty());
382 // FIXME: Called functions at point `p` should contain "foo".
385 TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchReturns) {
386 std::string Code = R"(
387 #include "noreturn_destructor_test_defs.h"
389 void target(bool b1, bool b2) {
390 int value = b1 ? Fatal().bar() : (b2 ? Fatal().baz() : foo());
391 (void)0;
392 // [[p]]
395 runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
396 "p", HoldsFunctionCallLattice(HasCalledFunctions(
397 UnorderedElementsAre("baz", "foo"))))));
398 // FIXME: Called functions at point `p` should contain only "foo".
401 // Models an analysis that uses flow conditions.
402 class SpecialBoolAnalysis final
403 : public DataflowAnalysis<SpecialBoolAnalysis, NoopLattice> {
404 public:
405 explicit SpecialBoolAnalysis(ASTContext &Context)
406 : DataflowAnalysis<SpecialBoolAnalysis, NoopLattice>(Context) {}
408 static NoopLattice initialElement() { return {}; }
410 void transfer(const CFGElement &Elt, NoopLattice &, Environment &Env) {
411 auto CS = Elt.getAs<CFGStmt>();
412 if (!CS)
413 return;
414 const auto *S = CS->getStmt();
415 auto SpecialBoolRecordDecl = recordDecl(hasName("SpecialBool"));
416 auto HasSpecialBoolType = hasType(SpecialBoolRecordDecl);
418 if (const auto *E = selectFirst<CXXConstructExpr>(
419 "call", match(cxxConstructExpr(HasSpecialBoolType).bind("call"), *S,
420 getASTContext()))) {
421 cast<RecordValue>(Env.getValue(*E))
422 ->setProperty("is_set", Env.getBoolLiteralValue(false));
423 } else if (const auto *E = selectFirst<CXXMemberCallExpr>(
424 "call", match(cxxMemberCallExpr(callee(cxxMethodDecl(ofClass(
425 SpecialBoolRecordDecl))))
426 .bind("call"),
427 *S, getASTContext()))) {
428 auto &ObjectLoc =
429 *cast<RecordStorageLocation>(getImplicitObjectLocation(*E, Env));
431 refreshRecordValue(ObjectLoc, Env)
432 .setProperty("is_set", Env.getBoolLiteralValue(true));
436 ComparisonResult compare(QualType Type, const Value &Val1,
437 const Environment &Env1, const Value &Val2,
438 const Environment &Env2) override {
439 const auto *Decl = Type->getAsCXXRecordDecl();
440 if (Decl == nullptr || Decl->getIdentifier() == nullptr ||
441 Decl->getName() != "SpecialBool")
442 return ComparisonResult::Unknown;
444 auto *IsSet1 = cast_or_null<BoolValue>(Val1.getProperty("is_set"));
445 auto *IsSet2 = cast_or_null<BoolValue>(Val2.getProperty("is_set"));
446 if (IsSet1 == nullptr)
447 return IsSet2 == nullptr ? ComparisonResult::Same
448 : ComparisonResult::Different;
450 if (IsSet2 == nullptr)
451 return ComparisonResult::Different;
453 return Env1.flowConditionImplies(IsSet1->formula()) ==
454 Env2.flowConditionImplies(IsSet2->formula())
455 ? ComparisonResult::Same
456 : ComparisonResult::Different;
459 // Always returns `true` to accept the `MergedVal`.
460 bool merge(QualType Type, const Value &Val1, const Environment &Env1,
461 const Value &Val2, const Environment &Env2, Value &MergedVal,
462 Environment &MergedEnv) override {
463 const auto *Decl = Type->getAsCXXRecordDecl();
464 if (Decl == nullptr || Decl->getIdentifier() == nullptr ||
465 Decl->getName() != "SpecialBool")
466 return true;
468 auto *IsSet1 = cast_or_null<BoolValue>(Val1.getProperty("is_set"));
469 if (IsSet1 == nullptr)
470 return true;
472 auto *IsSet2 = cast_or_null<BoolValue>(Val2.getProperty("is_set"));
473 if (IsSet2 == nullptr)
474 return true;
476 auto &IsSet = MergedEnv.makeAtomicBoolValue();
477 MergedVal.setProperty("is_set", IsSet);
478 if (Env1.flowConditionImplies(IsSet1->formula()) &&
479 Env2.flowConditionImplies(IsSet2->formula()))
480 MergedEnv.addToFlowCondition(IsSet.formula());
482 return true;
486 class JoinFlowConditionsTest : public Test {
487 protected:
488 template <typename Matcher>
489 void runDataflow(llvm::StringRef Code, Matcher Match) {
490 ASSERT_THAT_ERROR(
491 test::checkDataflow<SpecialBoolAnalysis>(
492 AnalysisInputs<SpecialBoolAnalysis>(
493 Code, ast_matchers::hasName("target"),
494 [](ASTContext &Context, Environment &Env) {
495 return SpecialBoolAnalysis(Context);
497 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
498 /*VerifyResults=*/[&Match](const llvm::StringMap<
499 DataflowAnalysisState<NoopLattice>>
500 &Results,
501 const AnalysisOutputs
502 &AO) { Match(Results, AO.ASTCtx); }),
503 llvm::Succeeded());
507 TEST_F(JoinFlowConditionsTest, JoinDistinctButProvablyEquivalentValues) {
508 std::string Code = R"(
509 struct SpecialBool {
510 SpecialBool() = default;
511 void set();
514 void target(bool Cond) {
515 SpecialBool Foo;
516 /*[[p1]]*/
517 if (Cond) {
518 Foo.set();
519 /*[[p2]]*/
520 } else {
521 Foo.set();
522 /*[[p3]]*/
524 (void)0;
525 /*[[p4]]*/
528 runDataflow(
529 Code,
530 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
531 ASTContext &ASTCtx) {
532 ASSERT_THAT(Results.keys(),
533 UnorderedElementsAre("p1", "p2", "p3", "p4"));
534 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
535 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
536 const Environment &Env3 = getEnvironmentAtAnnotation(Results, "p3");
537 const Environment &Env4 = getEnvironmentAtAnnotation(Results, "p4");
539 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
540 ASSERT_THAT(FooDecl, NotNull());
542 auto GetFoo = [FooDecl](const Environment &Env) -> const Formula & {
543 return cast<BoolValue>(Env.getValue(*FooDecl)->getProperty("is_set"))
544 ->formula();
547 EXPECT_FALSE(Env1.flowConditionImplies(GetFoo(Env1)));
548 EXPECT_TRUE(Env2.flowConditionImplies(GetFoo(Env2)));
549 EXPECT_TRUE(Env3.flowConditionImplies(GetFoo(Env3)));
550 EXPECT_TRUE(Env4.flowConditionImplies(GetFoo(Env4)));
554 class OptionalIntAnalysis final
555 : public DataflowAnalysis<OptionalIntAnalysis, NoopLattice> {
556 public:
557 explicit OptionalIntAnalysis(ASTContext &Context)
558 : DataflowAnalysis<OptionalIntAnalysis, NoopLattice>(Context) {}
560 static NoopLattice initialElement() { return {}; }
562 void transfer(const CFGElement &Elt, NoopLattice &, Environment &Env) {
563 auto CS = Elt.getAs<CFGStmt>();
564 if (!CS)
565 return;
566 const Stmt *S = CS->getStmt();
567 auto OptionalIntRecordDecl = recordDecl(hasName("OptionalInt"));
568 auto HasOptionalIntType = hasType(OptionalIntRecordDecl);
570 SmallVector<BoundNodes, 1> Matches = match(
571 stmt(anyOf(cxxConstructExpr(HasOptionalIntType).bind("construct"),
572 cxxOperatorCallExpr(
573 callee(cxxMethodDecl(ofClass(OptionalIntRecordDecl))))
574 .bind("operator"))),
575 *S, getASTContext());
576 if (const auto *E = selectFirst<CXXConstructExpr>(
577 "construct", Matches)) {
578 cast<RecordValue>(Env.getValue(*E))
579 ->setProperty("has_value", Env.getBoolLiteralValue(false));
580 } else if (const auto *E =
581 selectFirst<CXXOperatorCallExpr>("operator", Matches)) {
582 assert(E->getNumArgs() > 0);
583 auto *Object = E->getArg(0);
584 assert(Object != nullptr);
586 refreshRecordValue(*Object, Env)
587 .setProperty("has_value", Env.getBoolLiteralValue(true));
591 ComparisonResult compare(QualType Type, const Value &Val1,
592 const Environment &Env1, const Value &Val2,
593 const Environment &Env2) override {
594 // Nothing to say about a value that does not model an `OptionalInt`.
595 if (!Type->isRecordType() ||
596 Type->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
597 return ComparisonResult::Unknown;
599 auto *Prop1 = Val1.getProperty("has_value");
600 auto *Prop2 = Val2.getProperty("has_value");
601 assert(Prop1 != nullptr && Prop2 != nullptr);
602 return areEquivalentValues(*Prop1, *Prop2) ? ComparisonResult::Same
603 : ComparisonResult::Different;
606 bool merge(QualType Type, const Value &Val1, const Environment &Env1,
607 const Value &Val2, const Environment &Env2, Value &MergedVal,
608 Environment &MergedEnv) override {
609 // Nothing to say about a value that does not model an `OptionalInt`.
610 if (!Type->isRecordType() ||
611 Type->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
612 return false;
614 auto *HasValue1 = cast_or_null<BoolValue>(Val1.getProperty("has_value"));
615 if (HasValue1 == nullptr)
616 return false;
618 auto *HasValue2 = cast_or_null<BoolValue>(Val2.getProperty("has_value"));
619 if (HasValue2 == nullptr)
620 return false;
622 if (HasValue1 == HasValue2)
623 MergedVal.setProperty("has_value", *HasValue1);
624 else
625 MergedVal.setProperty("has_value", MergedEnv.makeTopBoolValue());
626 return true;
630 class WideningTest : public Test {
631 protected:
632 template <typename Matcher>
633 void runDataflow(llvm::StringRef Code, Matcher Match) {
634 tooling::FileContentMappings FilesContents;
635 FilesContents.push_back(
636 std::make_pair<std::string, std::string>("widening_test_defs.h", R"(
637 struct OptionalInt {
638 OptionalInt() = default;
639 OptionalInt& operator=(int);
641 )"));
642 ASSERT_THAT_ERROR(
643 checkDataflow<OptionalIntAnalysis>(
644 AnalysisInputs<OptionalIntAnalysis>(
645 Code, ast_matchers::hasName("target"),
646 [](ASTContext &Context, Environment &Env) {
647 return OptionalIntAnalysis(Context);
649 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"})
650 .withASTBuildVirtualMappedFiles(std::move(FilesContents)),
651 /*VerifyResults=*/[&Match](const llvm::StringMap<
652 DataflowAnalysisState<NoopLattice>>
653 &Results,
654 const AnalysisOutputs
655 &AO) { Match(Results, AO.ASTCtx); }),
656 llvm::Succeeded());
660 TEST_F(WideningTest, JoinDistinctValuesWithDistinctProperties) {
661 std::string Code = R"(
662 #include "widening_test_defs.h"
664 void target(bool Cond) {
665 OptionalInt Foo;
666 /*[[p1]]*/
667 if (Cond) {
668 Foo = 1;
669 /*[[p2]]*/
671 (void)0;
672 /*[[p3]]*/
675 runDataflow(
676 Code,
677 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
678 ASTContext &ASTCtx) {
679 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2", "p3"));
680 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
681 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
682 const Environment &Env3 = getEnvironmentAtAnnotation(Results, "p3");
684 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
685 ASSERT_THAT(FooDecl, NotNull());
687 auto GetFooValue = [FooDecl](const Environment &Env) {
688 return Env.getValue(*FooDecl);
691 EXPECT_EQ(GetFooValue(Env1)->getProperty("has_value"),
692 &Env1.getBoolLiteralValue(false));
693 EXPECT_EQ(GetFooValue(Env2)->getProperty("has_value"),
694 &Env2.getBoolLiteralValue(true));
695 EXPECT_TRUE(
696 isa<TopBoolValue>(GetFooValue(Env3)->getProperty("has_value")));
700 TEST_F(WideningTest, JoinDistinctValuesWithSameProperties) {
701 std::string Code = R"(
702 #include "widening_test_defs.h"
704 void target(bool Cond) {
705 OptionalInt Foo;
706 /*[[p1]]*/
707 if (Cond) {
708 Foo = 1;
709 /*[[p2]]*/
710 } else {
711 Foo = 2;
712 /*[[p3]]*/
714 (void)0;
715 /*[[p4]]*/
718 runDataflow(
719 Code,
720 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
721 ASTContext &ASTCtx) {
722 ASSERT_THAT(Results.keys(),
723 UnorderedElementsAre("p1", "p2", "p3", "p4"));
724 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
725 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
726 const Environment &Env3 = getEnvironmentAtAnnotation(Results, "p3");
727 const Environment &Env4 = getEnvironmentAtAnnotation(Results, "p4");
729 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
730 ASSERT_THAT(FooDecl, NotNull());
732 auto GetFooValue = [FooDecl](const Environment &Env) {
733 return Env.getValue(*FooDecl);
736 EXPECT_EQ(GetFooValue(Env1)->getProperty("has_value"),
737 &Env1.getBoolLiteralValue(false));
738 EXPECT_EQ(GetFooValue(Env2)->getProperty("has_value"),
739 &Env2.getBoolLiteralValue(true));
740 EXPECT_EQ(GetFooValue(Env3)->getProperty("has_value"),
741 &Env3.getBoolLiteralValue(true));
742 EXPECT_EQ(GetFooValue(Env4)->getProperty("has_value"),
743 &Env4.getBoolLiteralValue(true));
747 TEST_F(WideningTest, DistinctPointersToTheSameLocationAreEquivalent) {
748 std::string Code = R"(
749 void target(int Foo, bool Cond) {
750 int *Bar = &Foo;
751 while (Cond) {
752 Bar = &Foo;
754 (void)0;
755 // [[p]]
758 runDataflow(
759 Code,
760 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
761 ASTContext &ASTCtx) {
762 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
763 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
765 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
766 ASSERT_THAT(FooDecl, NotNull());
768 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
769 ASSERT_THAT(BarDecl, NotNull());
771 const auto *FooLoc =
772 cast<ScalarStorageLocation>(Env.getStorageLocation(*FooDecl));
773 const auto *BarVal = cast<PointerValue>(Env.getValue(*BarDecl));
774 EXPECT_EQ(&BarVal->getPointeeLoc(), FooLoc);
778 TEST_F(WideningTest, DistinctValuesWithSamePropertiesAreEquivalent) {
779 std::string Code = R"(
780 #include "widening_test_defs.h"
782 void target(bool Cond) {
783 OptionalInt Foo;
784 Foo = 1;
785 while (Cond) {
786 Foo = 2;
788 (void)0;
789 /*[[p]]*/
792 runDataflow(
793 Code,
794 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
795 ASTContext &ASTCtx) {
796 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
797 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
799 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
800 ASSERT_THAT(FooDecl, NotNull());
802 const auto *FooVal = Env.getValue(*FooDecl);
803 EXPECT_EQ(FooVal->getProperty("has_value"),
804 &Env.getBoolLiteralValue(true));
808 class FlowConditionTest : public Test {
809 protected:
810 template <typename Matcher>
811 void runDataflow(llvm::StringRef Code, Matcher Match) {
812 ASSERT_THAT_ERROR(
813 checkDataflow<NoopAnalysis>(
814 AnalysisInputs<NoopAnalysis>(
815 Code, ast_matchers::hasName("target"),
816 [](ASTContext &Context, Environment &Env) {
817 return NoopAnalysis(Context);
819 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
820 /*VerifyResults=*/[&Match](const llvm::StringMap<
821 DataflowAnalysisState<NoopLattice>>
822 &Results,
823 const AnalysisOutputs
824 &AO) { Match(Results, AO.ASTCtx); }),
825 llvm::Succeeded());
829 TEST_F(FlowConditionTest, IfStmtSingleVar) {
830 std::string Code = R"(
831 void target(bool Foo) {
832 if (Foo) {
833 (void)0;
834 /*[[p1]]*/
835 } else {
836 (void)1;
837 /*[[p2]]*/
841 runDataflow(
842 Code,
843 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
844 ASTContext &ASTCtx) {
845 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
846 ASSERT_THAT(FooDecl, NotNull());
848 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
850 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
851 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula();
852 EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
854 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
855 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula();
856 EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
860 TEST_F(FlowConditionTest, IfStmtSingleNegatedVar) {
861 std::string Code = R"(
862 void target(bool Foo) {
863 if (!Foo) {
864 (void)0;
865 /*[[p1]]*/
866 } else {
867 (void)1;
868 /*[[p2]]*/
872 runDataflow(
873 Code,
874 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
875 ASTContext &ASTCtx) {
876 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
877 ASSERT_THAT(FooDecl, NotNull());
879 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
881 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
882 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula();
883 EXPECT_FALSE(Env1.flowConditionImplies(FooVal1));
885 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
886 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula();
887 EXPECT_TRUE(Env2.flowConditionImplies(FooVal2));
891 TEST_F(FlowConditionTest, WhileStmt) {
892 std::string Code = R"(
893 void target(bool Foo) {
894 while (Foo) {
895 (void)0;
896 /*[[p]]*/
900 runDataflow(
901 Code,
902 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
903 ASTContext &ASTCtx) {
904 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
905 ASSERT_THAT(FooDecl, NotNull());
907 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
908 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
910 auto &FooVal = cast<BoolValue>(Env.getValue(*FooDecl))->formula();
911 EXPECT_TRUE(Env.flowConditionImplies(FooVal));
915 TEST_F(FlowConditionTest, Conjunction) {
916 std::string Code = R"(
917 void target(bool Foo, bool Bar) {
918 if (Foo && Bar) {
919 (void)0;
920 /*[[p1]]*/
921 } else {
922 (void)1;
923 /*[[p2]]*/
927 runDataflow(Code, [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
928 &Results,
929 ASTContext &ASTCtx) {
930 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
931 ASSERT_THAT(FooDecl, NotNull());
933 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
934 ASSERT_THAT(BarDecl, NotNull());
936 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
938 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
939 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula();
940 auto &BarVal1 = cast<BoolValue>(Env1.getValue(*BarDecl))->formula();
941 EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
942 EXPECT_TRUE(Env1.flowConditionImplies(BarVal1));
944 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
945 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula();
946 auto &BarVal2 = cast<BoolValue>(Env2.getValue(*BarDecl))->formula();
947 EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
948 EXPECT_FALSE(Env2.flowConditionImplies(BarVal2));
952 TEST_F(FlowConditionTest, Disjunction) {
953 std::string Code = R"(
954 void target(bool Foo, bool Bar) {
955 if (Foo || Bar) {
956 (void)0;
957 /*[[p1]]*/
958 } else {
959 (void)1;
960 /*[[p2]]*/
964 runDataflow(Code, [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
965 &Results,
966 ASTContext &ASTCtx) {
967 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
968 ASSERT_THAT(FooDecl, NotNull());
970 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
971 ASSERT_THAT(BarDecl, NotNull());
973 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
975 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
976 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula();
977 auto &BarVal1 = cast<BoolValue>(Env1.getValue(*BarDecl))->formula();
978 EXPECT_FALSE(Env1.flowConditionImplies(FooVal1));
979 EXPECT_FALSE(Env1.flowConditionImplies(BarVal1));
981 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
982 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula();
983 auto &BarVal2 = cast<BoolValue>(Env2.getValue(*BarDecl))->formula();
984 EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
985 EXPECT_FALSE(Env2.flowConditionImplies(BarVal2));
989 TEST_F(FlowConditionTest, NegatedConjunction) {
990 std::string Code = R"(
991 void target(bool Foo, bool Bar) {
992 if (!(Foo && Bar)) {
993 (void)0;
994 /*[[p1]]*/
995 } else {
996 (void)1;
997 /*[[p2]]*/
1001 runDataflow(Code, [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
1002 &Results,
1003 ASTContext &ASTCtx) {
1004 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
1005 ASSERT_THAT(FooDecl, NotNull());
1007 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1008 ASSERT_THAT(BarDecl, NotNull());
1010 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
1012 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1013 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula();
1014 auto &BarVal1 = cast<BoolValue>(Env1.getValue(*BarDecl))->formula();
1015 EXPECT_FALSE(Env1.flowConditionImplies(FooVal1));
1016 EXPECT_FALSE(Env1.flowConditionImplies(BarVal1));
1018 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1019 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula();
1020 auto &BarVal2 = cast<BoolValue>(Env2.getValue(*BarDecl))->formula();
1021 EXPECT_TRUE(Env2.flowConditionImplies(FooVal2));
1022 EXPECT_TRUE(Env2.flowConditionImplies(BarVal2));
1026 TEST_F(FlowConditionTest, DeMorgan) {
1027 std::string Code = R"(
1028 void target(bool Foo, bool Bar) {
1029 if (!(!Foo || !Bar)) {
1030 (void)0;
1031 /*[[p1]]*/
1032 } else {
1033 (void)1;
1034 /*[[p2]]*/
1038 runDataflow(Code, [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
1039 &Results,
1040 ASTContext &ASTCtx) {
1041 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
1042 ASSERT_THAT(FooDecl, NotNull());
1044 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1045 ASSERT_THAT(BarDecl, NotNull());
1047 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
1049 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1050 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula();
1051 auto &BarVal1 = cast<BoolValue>(Env1.getValue(*BarDecl))->formula();
1052 EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
1053 EXPECT_TRUE(Env1.flowConditionImplies(BarVal1));
1055 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1056 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula();
1057 auto &BarVal2 = cast<BoolValue>(Env2.getValue(*BarDecl))->formula();
1058 EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
1059 EXPECT_FALSE(Env2.flowConditionImplies(BarVal2));
1063 TEST_F(FlowConditionTest, Join) {
1064 std::string Code = R"(
1065 void target(bool Foo, bool Bar) {
1066 if (Bar) {
1067 if (!Foo)
1068 return;
1069 } else {
1070 if (!Foo)
1071 return;
1073 (void)0;
1074 /*[[p]]*/
1077 runDataflow(
1078 Code,
1079 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1080 ASTContext &ASTCtx) {
1081 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1083 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
1084 ASSERT_THAT(FooDecl, NotNull());
1086 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
1087 auto &FooVal = cast<BoolValue>(Env.getValue(*FooDecl))->formula();
1088 EXPECT_TRUE(Env.flowConditionImplies(FooVal));
1092 // Verifies that flow conditions are properly constructed even when the
1093 // condition is not meaningfully interpreted.
1095 // Note: currently, arbitrary function calls are uninterpreted, so the test
1096 // exercises this case. If and when we change that, this test will not add to
1097 // coverage (although it may still test a valuable case).
1098 TEST_F(FlowConditionTest, OpaqueFlowConditionMergesToOpaqueBool) {
1099 std::string Code = R"(
1100 bool foo();
1102 void target() {
1103 bool Bar = true;
1104 if (foo())
1105 Bar = false;
1106 (void)0;
1107 /*[[p]]*/
1110 runDataflow(
1111 Code,
1112 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1113 ASTContext &ASTCtx) {
1114 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1115 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
1117 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1118 ASSERT_THAT(BarDecl, NotNull());
1120 auto &BarVal = cast<BoolValue>(Env.getValue(*BarDecl))->formula();
1122 EXPECT_FALSE(Env.flowConditionImplies(BarVal));
1126 // Verifies that flow conditions are properly constructed even when the
1127 // condition is not meaningfully interpreted.
1129 // Note: currently, fields with recursive type calls are uninterpreted (beneath
1130 // the first instance), so the test exercises this case. If and when we change
1131 // that, this test will not add to coverage (although it may still test a
1132 // valuable case).
1133 TEST_F(FlowConditionTest, OpaqueFieldFlowConditionMergesToOpaqueBool) {
1134 std::string Code = R"(
1135 struct Rec {
1136 Rec* Next;
1139 struct Foo {
1140 Rec* X;
1143 void target(Foo F) {
1144 bool Bar = true;
1145 if (F.X->Next)
1146 Bar = false;
1147 (void)0;
1148 /*[[p]]*/
1151 runDataflow(
1152 Code,
1153 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1154 ASTContext &ASTCtx) {
1155 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1156 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
1158 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1159 ASSERT_THAT(BarDecl, NotNull());
1161 auto &BarVal = cast<BoolValue>(Env.getValue(*BarDecl))->formula();
1163 EXPECT_FALSE(Env.flowConditionImplies(BarVal));
1167 // Verifies that flow conditions are properly constructed even when the
1168 // condition is not meaningfully interpreted. Adds to above by nesting the
1169 // interestnig case inside a normal branch. This protects against degenerate
1170 // solutions which only test for empty flow conditions, for example.
1171 TEST_F(FlowConditionTest, OpaqueFlowConditionInsideBranchMergesToOpaqueBool) {
1172 std::string Code = R"(
1173 bool foo();
1175 void target(bool Cond) {
1176 bool Bar = true;
1177 if (Cond) {
1178 if (foo())
1179 Bar = false;
1180 (void)0;
1181 /*[[p]]*/
1185 runDataflow(
1186 Code,
1187 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1188 ASTContext &ASTCtx) {
1189 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1190 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
1192 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
1193 ASSERT_THAT(BarDecl, NotNull());
1195 auto &BarVal = cast<BoolValue>(Env.getValue(*BarDecl))->formula();
1197 EXPECT_FALSE(Env.flowConditionImplies(BarVal));
1201 TEST_F(FlowConditionTest, PointerToBoolImplicitCast) {
1202 std::string Code = R"(
1203 void target(int *Ptr) {
1204 bool Foo = false;
1205 if (Ptr) {
1206 Foo = true;
1207 /*[[p1]]*/
1210 (void)0;
1211 /*[[p2]]*/
1214 runDataflow(
1215 Code,
1216 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1217 ASTContext &ASTCtx) {
1218 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
1220 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
1221 ASSERT_THAT(FooDecl, NotNull());
1223 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1224 auto &FooVal1 = cast<BoolValue>(Env1.getValue(*FooDecl))->formula();
1225 EXPECT_TRUE(Env1.flowConditionImplies(FooVal1));
1227 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1228 auto &FooVal2 = cast<BoolValue>(Env2.getValue(*FooDecl))->formula();
1229 EXPECT_FALSE(Env2.flowConditionImplies(FooVal2));
1233 class TopAnalysis final : public DataflowAnalysis<TopAnalysis, NoopLattice> {
1234 public:
1235 explicit TopAnalysis(ASTContext &Context)
1236 : DataflowAnalysis<TopAnalysis, NoopLattice>(Context) {}
1238 static NoopLattice initialElement() { return {}; }
1240 void transfer(const CFGElement &Elt, NoopLattice &, Environment &Env) {
1241 auto CS = Elt.getAs<CFGStmt>();
1242 if (!CS)
1243 return;
1244 const Stmt *S = CS->getStmt();
1245 SmallVector<BoundNodes, 1> Matches =
1246 match(callExpr(callee(functionDecl(hasName("makeTop")))).bind("top"),
1247 *S, getASTContext());
1248 if (const auto *E = selectFirst<CallExpr>("top", Matches)) {
1249 Env.setValue(*E, Env.makeTopBoolValue());
1253 ComparisonResult compare(QualType Type, const Value &Val1,
1254 const Environment &Env1, const Value &Val2,
1255 const Environment &Env2) override {
1256 // Changes to a sound approximation, which allows us to test whether we can
1257 // (soundly) converge for some loops.
1258 return ComparisonResult::Unknown;
1262 class TopTest : public Test {
1263 protected:
1264 template <typename Matcher>
1265 void runDataflow(llvm::StringRef Code, Matcher VerifyResults) {
1266 ASSERT_THAT_ERROR(
1267 checkDataflow<TopAnalysis>(
1268 AnalysisInputs<TopAnalysis>(
1269 Code, ast_matchers::hasName("target"),
1270 [](ASTContext &Context, Environment &Env) {
1271 return TopAnalysis(Context);
1273 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
1274 VerifyResults),
1275 llvm::Succeeded());
1279 // Tests that when Top is unused it remains Top.
1280 TEST_F(TopTest, UnusedTopInitializer) {
1281 std::string Code = R"(
1282 bool makeTop();
1284 void target() {
1285 bool Foo = makeTop();
1286 /*[[p1]]*/
1287 (void)0;
1288 /*[[p2]]*/
1291 runDataflow(
1292 Code,
1293 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1294 const AnalysisOutputs &AO) {
1295 ASSERT_THAT(Results.keys(),
1296 UnorderedElementsAre("p1", "p2"));
1297 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1298 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1300 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1301 ASSERT_THAT(FooDecl, NotNull());
1303 auto GetFooValue = [FooDecl](const Environment &Env) {
1304 return Env.getValue(*FooDecl);
1307 Value *FooVal1 = GetFooValue(Env1);
1308 ASSERT_THAT(FooVal1, NotNull());
1309 EXPECT_TRUE(isa<TopBoolValue>(FooVal1))
1310 << debugString(FooVal1->getKind());
1312 Value *FooVal2 = GetFooValue(Env2);
1313 ASSERT_THAT(FooVal2, NotNull());
1314 EXPECT_TRUE(isa<TopBoolValue>(FooVal2))
1315 << debugString(FooVal2->getKind());
1317 EXPECT_EQ(FooVal1, FooVal2);
1321 // Tests that when Top is unused it remains Top. Like above, but uses the
1322 // assignment form rather than initialization, which uses Top as an lvalue that
1323 // is *not* in an rvalue position.
1324 TEST_F(TopTest, UnusedTopAssignment) {
1325 std::string Code = R"(
1326 bool makeTop();
1328 void target() {
1329 bool Foo;
1330 Foo = makeTop();
1331 /*[[p1]]*/
1332 (void)0;
1333 /*[[p2]]*/
1336 runDataflow(
1337 Code,
1338 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1339 const AnalysisOutputs &AO) {
1340 ASSERT_THAT(Results.keys(),
1341 UnorderedElementsAre("p1", "p2"));
1342 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1343 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1345 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1346 ASSERT_THAT(FooDecl, NotNull());
1348 auto GetFooValue = [FooDecl](const Environment &Env) {
1349 return Env.getValue(*FooDecl);
1352 Value *FooVal1 = GetFooValue(Env1);
1353 ASSERT_THAT(FooVal1, NotNull());
1354 EXPECT_TRUE(isa<TopBoolValue>(FooVal1))
1355 << debugString(FooVal1->getKind());
1357 Value *FooVal2 = GetFooValue(Env2);
1358 ASSERT_THAT(FooVal2, NotNull());
1359 EXPECT_TRUE(isa<TopBoolValue>(FooVal2))
1360 << debugString(FooVal2->getKind());
1362 EXPECT_EQ(FooVal1, FooVal2);
1366 TEST_F(TopTest, UnusedTopJoinsToTop) {
1367 std::string Code = R"(
1368 bool makeTop();
1370 void target(bool Cond, bool F) {
1371 bool Foo = makeTop();
1372 // Force a new CFG block.
1373 if (F) return;
1374 (void)0;
1375 /*[[p1]]*/
1377 bool Zab1;
1378 bool Zab2;
1379 if (Cond) {
1380 Zab1 = true;
1381 } else {
1382 Zab2 = true;
1384 (void)0;
1385 /*[[p2]]*/
1388 runDataflow(
1389 Code,
1390 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1391 const AnalysisOutputs &AO) {
1392 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
1393 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1394 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1396 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1397 ASSERT_THAT(FooDecl, NotNull());
1399 auto GetFooValue = [FooDecl](const Environment &Env) {
1400 return Env.getValue(*FooDecl);
1403 Value *FooVal1 = GetFooValue(Env1);
1404 ASSERT_THAT(FooVal1, NotNull());
1405 EXPECT_TRUE(isa<TopBoolValue>(FooVal1))
1406 << debugString(FooVal1->getKind());
1408 Value *FooVal2 = GetFooValue(Env2);
1409 ASSERT_THAT(FooVal2, NotNull());
1410 EXPECT_TRUE(isa<TopBoolValue>(FooVal2))
1411 << debugString(FooVal2->getKind());
1415 TEST_F(TopTest, TopUsedBeforeBranchJoinsToSameAtomicBool) {
1416 std::string Code = R"(
1417 bool makeTop();
1419 void target(bool Cond, bool F) {
1420 bool Foo = makeTop();
1421 /*[[p0]]*/
1423 // Use `Top`.
1424 bool Bar = Foo;
1425 // Force a new CFG block.
1426 if (F) return;
1427 (void)0;
1428 /*[[p1]]*/
1430 bool Zab1;
1431 bool Zab2;
1432 if (Cond) {
1433 Zab1 = true;
1434 } else {
1435 Zab2 = true;
1437 (void)0;
1438 /*[[p2]]*/
1441 runDataflow(
1442 Code,
1443 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1444 const AnalysisOutputs &AO) {
1445 ASSERT_THAT(Results.keys(),
1446 UnorderedElementsAre("p0", "p1", "p2"));
1447 const Environment &Env0 = getEnvironmentAtAnnotation(Results, "p0");
1448 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1449 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1451 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1452 ASSERT_THAT(FooDecl, NotNull());
1454 auto GetFooValue = [FooDecl](const Environment &Env) {
1455 return Env.getValue(*FooDecl);
1458 Value *FooVal0 = GetFooValue(Env0);
1459 ASSERT_THAT(FooVal0, NotNull());
1460 EXPECT_TRUE(isa<TopBoolValue>(FooVal0))
1461 << debugString(FooVal0->getKind());
1463 Value *FooVal1 = GetFooValue(Env1);
1464 ASSERT_THAT(FooVal1, NotNull());
1465 EXPECT_TRUE(isa<AtomicBoolValue>(FooVal1))
1466 << debugString(FooVal1->getKind());
1468 Value *FooVal2 = GetFooValue(Env2);
1469 ASSERT_THAT(FooVal2, NotNull());
1470 EXPECT_TRUE(isa<AtomicBoolValue>(FooVal2))
1471 << debugString(FooVal2->getKind());
1473 EXPECT_EQ(FooVal2, FooVal1);
1477 TEST_F(TopTest, TopUsedInBothBranchesJoinsToAtomic) {
1478 std::string Code = R"(
1479 bool makeTop();
1481 void target(bool Cond, bool F) {
1482 bool Foo = makeTop();
1483 // Force a new CFG block.
1484 if (F) return;
1485 (void)0;
1486 /*[[p1]]*/
1488 bool Zab1;
1489 bool Zab2;
1490 if (Cond) {
1491 Zab1 = Foo;
1492 } else {
1493 Zab2 = Foo;
1495 (void)0;
1496 /*[[p2]]*/
1499 runDataflow(
1500 Code,
1501 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1502 const AnalysisOutputs &AO) {
1503 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
1504 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1505 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1507 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1508 ASSERT_THAT(FooDecl, NotNull());
1510 auto GetFooValue = [FooDecl](const Environment &Env) {
1511 return Env.getValue(*FooDecl);
1514 Value *FooVal1 = GetFooValue(Env1);
1515 ASSERT_THAT(FooVal1, NotNull());
1516 EXPECT_TRUE(isa<TopBoolValue>(FooVal1))
1517 << debugString(FooVal1->getKind());
1519 Value *FooVal2 = GetFooValue(Env2);
1520 ASSERT_THAT(FooVal2, NotNull());
1521 EXPECT_TRUE(isa<AtomicBoolValue>(FooVal2))
1522 << debugString(FooVal2->getKind());
1526 TEST_F(TopTest, TopUsedInBothBranchesWithoutPrecisionLoss) {
1527 std::string Code = R"(
1528 bool makeTop();
1530 void target(bool Cond, bool F) {
1531 bool Foo = makeTop();
1532 // Force a new CFG block.
1533 if (F) return;
1534 (void)0;
1536 bool Bar;
1537 if (Cond) {
1538 Bar = Foo;
1539 } else {
1540 Bar = Foo;
1542 (void)0;
1543 /*[[p]]*/
1546 runDataflow(
1547 Code,
1548 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1549 const AnalysisOutputs &AO) {
1550 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1551 const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
1553 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1554 ASSERT_THAT(FooDecl, NotNull());
1556 const ValueDecl *BarDecl = findValueDecl(AO.ASTCtx, "Bar");
1557 ASSERT_THAT(BarDecl, NotNull());
1559 auto *FooVal = dyn_cast_or_null<BoolValue>(Env.getValue(*FooDecl));
1560 ASSERT_THAT(FooVal, NotNull());
1562 auto *BarVal = dyn_cast_or_null<BoolValue>(Env.getValue(*BarDecl));
1563 ASSERT_THAT(BarVal, NotNull());
1565 EXPECT_TRUE(Env.flowConditionImplies(
1566 Env.arena().makeEquals(FooVal->formula(), BarVal->formula())));
1570 TEST_F(TopTest, TopUnusedBeforeLoopHeadJoinsToTop) {
1571 std::string Code = R"(
1572 bool makeTop();
1574 void target(bool Cond, bool F) {
1575 bool Foo = makeTop();
1576 // Force a new CFG block.
1577 if (F) return;
1578 (void)0;
1579 /*[[p1]]*/
1581 while (Cond) {
1582 // Use `Foo`.
1583 bool Zab = Foo;
1584 Zab = false;
1585 Foo = makeTop();
1587 (void)0;
1588 /*[[p2]]*/
1591 runDataflow(
1592 Code,
1593 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1594 const AnalysisOutputs &AO) {
1595 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
1596 const Environment &Env1 = getEnvironmentAtAnnotation(Results, "p1");
1597 const Environment &Env2 = getEnvironmentAtAnnotation(Results, "p2");
1599 const ValueDecl *FooDecl = findValueDecl(AO.ASTCtx, "Foo");
1600 ASSERT_THAT(FooDecl, NotNull());
1602 auto GetFooValue = [FooDecl](const Environment &Env) {
1603 return Env.getValue(*FooDecl);
1606 Value *FooVal1 = GetFooValue(Env1);
1607 ASSERT_THAT(FooVal1, NotNull());
1608 EXPECT_TRUE(isa<TopBoolValue>(FooVal1))
1609 << debugString(FooVal1->getKind());
1611 Value *FooVal2 = GetFooValue(Env2);
1612 ASSERT_THAT(FooVal2, NotNull());
1613 EXPECT_TRUE(isa<TopBoolValue>(FooVal2))
1614 << debugString(FooVal2->getKind());
1619 TEST_F(TopTest, ForRangeStmtConverges) {
1620 std::string Code = R"(
1621 void target(bool Foo) {
1622 int Ints[10];
1623 bool B = false;
1624 for (int I : Ints)
1625 B = true;
1628 runDataflow(Code,
1629 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
1630 const AnalysisOutputs &) {
1631 // No additional expectations. We're only checking that the
1632 // analysis converged.
1635 } // namespace