Reland "Extend non-flow-sensitive transfer for CastExpr"

This used to crash when casting nullptr to a type alias, due to a violated invariant about the nullability vector size. This was fixed by "Handle aliases (and all sugar) in type => nullability vector computation".
Added a test for this case.

PiperOrigin-RevId: 524833947
diff --git a/nullability_verification/pointer_nullability_analysis.cc b/nullability_verification/pointer_nullability_analysis.cc
index 2d095a5..4bde0a2 100644
--- a/nullability_verification/pointer_nullability_analysis.cc
+++ b/nullability_verification/pointer_nullability_analysis.cc
@@ -355,11 +355,133 @@
 void transferNonFlowSensitiveCastExpr(
     const CastExpr* CE, const MatchFinder::MatchResult& MR,
     TransferState<PointerNullabilityLattice>& State) {
-  // TODO: Handle casts where the input and output types can have different
-  // numbers of pointers, and therefore different nullability. For example, a
-  // reinterpret_cast from `int *` to int.
-  computeNullability(CE, State, [&]() {
-    return getNullabilityForChild(CE->getSubExpr(), State).vec();
+  computeNullability(CE, State, [&]() -> std::vector<NullabilityKind> {
+    // Most casts that can convert ~unrelated types drop nullability in general.
+    // As a special case, preserve nullability of outer pointer types.
+    // For example, int* p; (void*)p; is a BitCast, but preserves nullability.
+    auto PreserveTopLevelPointers = [&](std::vector<NullabilityKind> V) {
+      auto ArgNullability = getNullabilityForChild(CE->getSubExpr(), State);
+      const PointerType* ArgType = dyn_cast<PointerType>(
+          CE->getSubExpr()->getType().getCanonicalType().getTypePtr());
+      const PointerType* CastType =
+          dyn_cast<PointerType>(CE->getType().getCanonicalType().getTypePtr());
+      for (int I = 0; ArgType && CastType; ++I) {
+        V[I] = ArgNullability[I];
+        ArgType = dyn_cast<PointerType>(ArgType->getPointeeType().getTypePtr());
+        CastType =
+            dyn_cast<PointerType>(CastType->getPointeeType().getTypePtr());
+      }
+      return V;
+    };
+
+    switch (CE->getCastKind()) {
+      // Casts between unrelated types: we can't say anything about nullability.
+      case CK_LValueBitCast:
+      case CK_BitCast:
+      case CK_LValueToRValueBitCast:
+        return PreserveTopLevelPointers(unspecifiedNullability(CE));
+
+      // Casts between equivalent types.
+      case CK_LValueToRValue:
+      case CK_NoOp:
+      case CK_AtomicToNonAtomic:
+      case CK_NonAtomicToAtomic:
+      case CK_AddressSpaceConversion:
+        return getNullabilityForChild(CE->getSubExpr(), State).vec();
+
+      // Controlled conversions between types
+      // TODO: these should be doable somehow
+      case CK_BaseToDerived:
+      case CK_DerivedToBase:
+      case CK_UncheckedDerivedToBase:
+        return PreserveTopLevelPointers(unspecifiedNullability(CE));
+      case CK_UserDefinedConversion:
+      case CK_ConstructorConversion:
+        return unspecifiedNullability(CE);
+
+      case CK_Dynamic: {
+        auto Result = unspecifiedNullability(CE);
+        // A dynamic_cast to pointer is null if the runtime check fails.
+        if (isa<PointerType>(CE->getType().getCanonicalType()))
+          Result.front() = NullabilityKind::Nullable;
+        return Result;
+      }
+
+      // Primitive values have no nullability.
+      case CK_ToVoid:
+      case CK_MemberPointerToBoolean:
+      case CK_PointerToBoolean:
+      case CK_PointerToIntegral:
+      case CK_IntegralCast:
+      case CK_IntegralToBoolean:
+      case CK_IntegralToFloating:
+      case CK_FloatingToFixedPoint:
+      case CK_FixedPointToFloating:
+      case CK_FixedPointCast:
+      case CK_FixedPointToIntegral:
+      case CK_IntegralToFixedPoint:
+      case CK_FixedPointToBoolean:
+      case CK_FloatingToIntegral:
+      case CK_FloatingToBoolean:
+      case CK_BooleanToSignedIntegral:
+      case CK_FloatingCast:
+      case CK_FloatingRealToComplex:
+      case CK_FloatingComplexToReal:
+      case CK_FloatingComplexToBoolean:
+      case CK_FloatingComplexCast:
+      case CK_FloatingComplexToIntegralComplex:
+      case CK_IntegralRealToComplex:
+      case CK_IntegralComplexToReal:
+      case CK_IntegralComplexToBoolean:
+      case CK_IntegralComplexCast:
+      case CK_IntegralComplexToFloatingComplex:
+        return {};
+
+      // This can definitely be null!
+      case CK_NullToPointer: {
+        auto Nullability = getNullabilityAnnotationsFromType(CE->getType());
+        Nullability.front() = NullabilityKind::Nullable;
+        return Nullability;
+      }
+
+      // Pointers out of thin air, who knows?
+      case CK_IntegralToPointer:
+        return unspecifiedNullability(CE);
+
+      // Decayed objects are never null.
+      case CK_ArrayToPointerDecay:
+      case CK_FunctionToPointerDecay:
+      case CK_BuiltinFnToFnPtr:
+        return prepend(NullabilityKind::NonNull,
+                       getNullabilityForChild(CE->getSubExpr(), State));
+
+      // TODO: what is our model of member pointers?
+      case CK_BaseToDerivedMemberPointer:
+      case CK_DerivedToBaseMemberPointer:
+      case CK_NullToMemberPointer:
+      case CK_ReinterpretMemberPointer:
+      case CK_ToUnion:  // and unions?
+        return unspecifiedNullability(CE);
+
+      // TODO: Non-C/C++ constructs, do we care about these?
+      case CK_CPointerToObjCPointerCast:
+      case CK_ObjCObjectLValueCast:
+      case CK_MatrixCast:
+      case CK_VectorSplat:
+      case CK_BlockPointerToObjCPointerCast:
+      case CK_AnyPointerToBlockPointerCast:
+      case CK_ARCProduceObject:
+      case CK_ARCConsumeObject:
+      case CK_ARCReclaimReturnedObject:
+      case CK_ARCExtendBlockObject:
+      case CK_CopyAndAutoreleaseBlockObject:
+      case CK_ZeroToOCLOpaqueType:
+      case CK_IntToOCLSampler:
+        return unspecifiedNullability(CE);
+
+      case CK_Dependent:
+        CHECK(false) << "Shouldn't see dependent casts here?";
+    }
   });
 }
 
diff --git a/nullability_verification/test/casts.cc b/nullability_verification/test/casts.cc
index cd2b77f..4358fee 100644
--- a/nullability_verification/test/casts.cc
+++ b/nullability_verification/test/casts.cc
@@ -101,6 +101,138 @@
   )cc"));
 }
 
+// CK_Bitcast: Bitcasts preserve outer nullability
+TEST(PointerNullabilityTest, Bitcast) {
+  EXPECT_TRUE(checkDiagnostics(R"cc(
+    template <class X>
+    struct vector {};
+
+    void target() {
+      // Bitcasts preserve nullability.
+      __assert_nullability<NK_nullable>((void*)value<int* _Nullable>());
+      __assert_nullability<NK_nonnull>((void*)value<int* _Nonnull>());
+      __assert_nullability<NK_unspecified>((void*)value<int*>());
+      // Nullability of further outer pointer types is preserved in bitcasts.
+      __assert_nullability<NK_nullable, NK_nullable>(
+          (void**)value<int* _Nullable* _Nullable>());
+      __assert_nullability<NK_nonnull, NK_nonnull>(
+          (void**)value<int* _Nonnull* _Nonnull>());
+      __assert_nullability<NK_unspecified, NK_unspecified>((void**)value<int**>());
+      // But nullability of other inner types is dropped.
+      __assert_nullability<NK_nullable, NK_unspecified>(
+          (void**)value<vector<int* _Nullable>* _Nullable>());
+      __assert_nullability<NK_nonnull, NK_unspecified>(
+          (void**)value<vector<int* _Nonnull>* _Nonnull>());
+
+      __assert_nullability<NK_nonnull, NK_unspecified>(
+          (void**)value<int* _Nonnull>);
+      __assert_nullability<NK_nonnull>((void*)value<int* _Nonnull* _Nonnull>());
+    }
+  )cc"));
+}
+
+// CK_NoOp: No-op casts preserve deep nullability
+// TODO: fix false-positives from treating untracked values as unsafe.
+TEST(PointerNullabilityTest, NoOp) {
+  EXPECT_TRUE(checkDiagnostics(R"cc(
+    template <class X>
+    struct vector {};
+
+    void target() {
+      // No-op casts preserve deep nullability.
+      __assert_nullability  // [[unsafe]] TODO: fix false positive
+          <NK_nullable, NK_nullable>(const_cast<vector<int>*>(
+              (vector<int>* const)value<vector<int* _Nullable>* _Nullable>()));
+    }
+  )cc"));
+}
+
+// Casts between types with inheritance - only simple cases handled.
+// TODO: fix false-positives from treating untracked values as unsafe.
+TEST(PointerNullabilityTest, Inheritance) {
+  EXPECT_TRUE(checkDiagnostics(R"cc(
+    template <class X>
+    struct base {
+      virtual void ensure_polymorphic();
+    };
+    template <class X>
+    struct derived : base<X> {};
+
+    void target() {
+      // CK_BaseToDerived: preserves outer nullability only.
+      // TODO: determine that derived's type param is actually nullable here.
+      __assert_nullability<NK_nullable, NK_unspecified>(
+          (derived<int *> *)value<base<int *_Nullable> *_Nullable>());
+      // CK_Dynamic: dynamic_cast returns a nullable pointer.
+      auto b = value<base<int *_Nonnull> *_Nonnull>();
+      __assert_nullability  // [[unsafe]] TODO: fix false positive
+          <NK_nullable, NK_unspecified>(dynamic_cast<derived<int> *>(b));
+      // ... only if casting to a pointer!
+      auto c = value<base<int *>>();
+      __assert_nullability<NK_unspecified>(dynamic_cast<derived<int *> &>(c));
+    }
+  )cc"));
+}
+
+// User-defined conversions could do anything, use declared type.
+TEST(PointerNullabilityTest, UserDefinedConversions) {
+  EXPECT_TRUE(checkDiagnostics(R"cc(
+    template <class X>
+    struct BuildFromPointer {
+      BuildFromPointer(int*);
+    };
+
+    void target() {
+      // User-defined conversions could do anything.
+      // CK_ConstructorConversion
+      __assert_nullability<NK_unspecified>(
+          (BuildFromPointer<double*>)value<int* _Nonnull>());
+    }
+  )cc"));
+}
+
+TEST(PointerNullabilityTest, CastToNonPointer) {
+  // Casting to non-pointer types destroyes nullability.
+  EXPECT_TRUE(checkDiagnostics(R"cc(
+    using I = __INTPTR_TYPE__;
+
+    // TODO: fix false-positives from treating untracked values as unsafe.
+    void target() {
+      // Casting away pointerness destroys nullability.
+      // CK_PointerToIntegral
+      __assert_nullability<>((I)value<int* _Nonnull>());
+      // CK_PointerToBoolean
+      __assert_nullability<>((bool)value<int* _Nonnull>());
+      // Casting them back does not recover it.
+      // CK_IntegralToPointer
+      __assert_nullability  // [[unsafe]] TODO: fix false positive
+          <>((int*)(I)value<int* _Nonnull>());
+    }
+  )cc"));
+}
+
+TEST(PointerNullabilityTest, TrivialNullability) {
+  // Casts with trivial nullability
+  EXPECT_TRUE(checkDiagnostics(R"cc(
+    void target() {
+      // Null is nullable!
+      __assert_nullability<NK_nullable>((int*)nullptr);
+
+      // Decayed objects are non-null.
+      int array[2];
+      __assert_nullability<NK_nonnull>((int*)array);
+    }
+  )cc"));
+}
+
+TEST(PointerNullabilityTest, CastNullToAlias) {
+  // This used to crash!
+  EXPECT_TRUE(checkDiagnostics(R"cc(
+    using P = int *;
+    P target() { return nullptr; }
+  )cc"));
+}
+
 TEST(PointerNullabilityTest, CastExpression) {
   // TODO: We currently do not warn on local variables
   // whose annotations conflict with the initializer. Decide whether to do so,
diff --git a/nullability_verification/test/check_diagnostics.cc b/nullability_verification/test/check_diagnostics.cc
index 328e7ec..f387828 100644
--- a/nullability_verification/test/check_diagnostics.cc
+++ b/nullability_verification/test/check_diagnostics.cc
@@ -24,6 +24,9 @@
 
   template <NullabilityKind... NK, typename T>
   void __assert_nullability(const T&);
+
+  template <typename T>
+  T value();
 )cc";
 
 bool checkDiagnostics(llvm::StringRef SourceCode) {