[nullability] Add a test with a false positive for const methods.
The false positive happens because a suboptimal CFG structure causes a join that
is (I believe) unnecessary, which then loses information about the value
returned from the const method. The exact mechanism that causes the false
positive is explained in the test.
PiperOrigin-RevId: 662933047
Change-Id: I52f7f33af0ffacdcaae5e71a6637103597ff956c
diff --git a/bazel/llvm.bzl b/bazel/llvm.bzl
index 95b6dd6..79a7e2f 100644
--- a/bazel/llvm.bzl
+++ b/bazel/llvm.bzl
@@ -53,7 +53,7 @@
executable = False,
)
-LLVM_COMMIT_SHA = "35f55f53dfbb62902da007f308a618192102dd1c"
+LLVM_COMMIT_SHA = "c8b5d30f707757a4fe4d9d0bb01f762665f6942f"
def llvm_loader_repository_dependencies():
# This *declares* the dependency, but it won't actually be *downloaded* unless it's used.
diff --git a/nullability/test/function_calls.cc b/nullability/test/function_calls.cc
index ee658d7..c89240e 100644
--- a/nullability/test/function_calls.cc
+++ b/nullability/test/function_calls.cc
@@ -894,6 +894,45 @@
)cc"));
}
+TEST(PointerNullabilityTest, ConstMethodJoinLosesInformation) {
+ EXPECT_TRUE(checkDiagnostics(R"cc(
+ struct A {
+ bool cond() const;
+ };
+ struct C {
+ int *_Nullable property() const;
+ A a() const;
+ };
+ void target(const C &c1, const C &c2) {
+ if (c1.property() == nullptr || c2.property() == nullptr || c2.a().cond())
+ return;
+ *c1.property();
+ // TODO(b/359457439): This is a false positive, caused by a suboptimal CFG
+ // structure. All of the possible edges out of the if statement's
+ // condition join in a single CFG block before branching out again to
+ // the `return` block on the one hand and the block that performs the
+ // dereferences on the other.
+ // When we perform the join for the various edges out of the condition,
+ // we discard the return value for `c2.property()` because in the case
+ // where `c1.property()` is null, we never evaluate `c2.property()` and
+ // hence don't have a return value for it. When we call `c2.property()`
+ // again, we therefore create a fresh return value for it, and we hence
+ // cannot infer that this value is nonnull.
+ // The false positive does not occur if `c2.a().cond()` is replaced with
+ // a simpler condition, e.g. `c2.cond()` (assuming that `cond()` is
+ // moved to `C`). In this case, the CFG is structured differently: All of
+ // the edges taken when one of the conditions in the if state is true
+ // lead directly to the `return` block, and the edge taken when all
+ // conditions are false leads diresctly to the block that performs the
+ // dereferences. No join is performed, and we can therefore conclude that
+ // `c2.property()` is nonnull.
+ // I am not sure what causes the different CFG structure in the two cases,
+ // but it may be triggered by the `A` temporary that is returned by `a()`.
+ *c2.property(); // [[unsafe]]
+ }
+ )cc"));
+}
+
TEST(PointerNullabilityTest, ConstMethodNoRecordForCallObject) {
EXPECT_TRUE(checkDiagnostics(R"cc(
struct S {