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?";
+    }
   });
 }