Module Nullsafe.Nullability

module F = Stdlib.Format
type t =
| Null

The only possible value for that type is null

| Nullable

No guarantees on the nullability

| ThirdPartyNonnull

Values coming from third-party methods and fields not explictly annotated neither as @Nullable, nor as @Nonnull. We still consider those as non-nullable but with the least level of confidence.

| UncheckedNonnull

The type comes from a signature that is annotated (explicitly or implicitly according to conventions) as non-nullable. Hovewer, the class is not checked under @Nullsafe, so its actual nullability is not truthworhy, as the class might contain arbitrary number of nullability issues

| LocallyTrustedNonnull

The same as UncheckedNonnull, but the value comes from a class explicitly listed as trusted in the class under analysis. It is less truthworthy than LocallyCheckedNonnull as no actual verification were performed.

| LocallyCheckedNonnull

Non-nullable value that comes from a class checked under @NullsafeLocal mode. Local mode type-checks files against its dependencies but does not require the dependencies to be transitively checked. Therefore this type of non-nullable value is differentiated from StrictNonnull.

| StrictNonnull

Non-nullable value with the highest degree of certainty, because it is either:

  • a non-null literal,
  • an expression that semantically cannot be null,
  • comes from code checked under @NullsafeStrict mode,
  • comes from a third-party method with an appropriate built-in model, user-defined nullability signature, or explicit @NonNull annotation.

The latter two are potential sources of unsoundness issues for nullsafe, but we need to strike the balance between the strictness of analysis, convenience, and real-world risk.

val compare : t -> t -> int
val equal : t -> t -> bool
type pair = t * t
val compare_pair : pair -> pair -> int
val equal_pair : pair -> pair -> bool
val top : t

The most generic type.

val is_subtype : subtype:t -> supertype:t -> bool

A is a subtype of B, if all values of A can be represented in B. Subtype relation is reflexive: everything is a subtype of itself.

val join : t -> t -> t

Unique upper bound over two types: the most precise type that is a supertype of both. Practically, joins occur e.g. when two branches of execution flow are getting merged.

val is_considered_nonnull : nullsafe_mode:NullsafeMode.t -> t -> bool

Check whether a given nullability is considered non-nullable within a given nullsafe_mode.

val is_nonnullish : t -> bool

Check whether a given nullability is one of the non-nullable types with no regards to the mode.

val pp : F.formatter -> t -> unit