[nullsafe][TypeOrigin refactor] Split TypeOrigin.Const to nullable and non-nullable constants

Summary:
This diff is a part of work teaching Nullsafe to explain decisions it's
making.

Introducing more specific origins will allow us to automatically infer
nullability based on type origin (in follow up diffs).

Reviewed By: ngorogiannis

Differential Revision: D18450165

fbshipit-source-id: 9dc0d25c0
master
Mitya Lyubarskiy 6 years ago committed by Facebook Github Bot
parent a356ef19d7
commit 4a81a69fb3

@ -73,8 +73,6 @@ let create_nullable origin = {origin; nullability= Nullability.Nullable}
let create_nonnull origin = {origin; nullability= Nullability.Nonnull} let create_nonnull origin = {origin; nullability= Nullability.Nonnull}
let with_origin t o = {t with origin= o}
let of_annotated_nullability annotated_nullability origin = let of_annotated_nullability annotated_nullability origin =
match annotated_nullability with match annotated_nullability with
| AnnotatedNullability.Nullable _ -> | AnnotatedNullability.Nullable _ ->

@ -61,6 +61,3 @@ val join : t -> t -> t
val origin_is_fun_library : t -> bool val origin_is_fun_library : t -> bool
val to_string : t -> string val to_string : t -> string
val with_origin : t -> TypeOrigin.t -> t
(** Leave the same nullability, but change the origin *)

@ -111,11 +111,21 @@ type checks = {eradicate: bool; check_ret_type: check_return_type list}
let rec typecheck_expr ~is_strict_mode find_canonical_duplicate visited checks tenv node instr_ref let rec typecheck_expr ~is_strict_mode find_canonical_duplicate visited checks tenv node instr_ref
(curr_pdesc : Procdesc.t) typestate e tr_default loc : TypeState.range = (curr_pdesc : Procdesc.t) typestate e tr_default loc : TypeState.range =
match e with match e with
(* null literal or 0 *)
| _ when Exp.is_null_literal e -> | _ when Exp.is_null_literal e ->
let typ, inferred_nullability = tr_default in let typ, _ = tr_default in
(* 0 is not the same thing as null. They are encoded as the same thing in SIL.
We distinct them by type.
*)
if PatternMatch.type_is_class typ then if PatternMatch.type_is_class typ then
(typ, InferredNullability.create_nullable (TypeOrigin.Const loc)) (typ, InferredNullability.create_nullable (TypeOrigin.NullConst loc))
else (typ, InferredNullability.with_origin inferred_nullability (TypeOrigin.Const loc)) else
(* 0 const (this is not the same as null) *)
(typ, InferredNullability.create_nonnull (TypeOrigin.NonnullConst loc))
| Exp.Const _ ->
let typ, _ = tr_default in
(* We already considered case of null literal above, so this is a non-null const. *)
(typ, InferredNullability.create_nonnull (TypeOrigin.NonnullConst loc))
| Exp.Lvar pvar -> | Exp.Lvar pvar ->
Option.value (TypeState.lookup_pvar pvar typestate) ~default:tr_default Option.value (TypeState.lookup_pvar pvar typestate) ~default:tr_default
| Exp.Var id -> | Exp.Var id ->
@ -123,9 +133,6 @@ let rec typecheck_expr ~is_strict_mode find_canonical_duplicate visited checks t
| Exp.Exn e1 -> | Exp.Exn e1 ->
typecheck_expr ~is_strict_mode find_canonical_duplicate visited checks tenv node instr_ref typecheck_expr ~is_strict_mode find_canonical_duplicate visited checks tenv node instr_ref
curr_pdesc typestate e1 tr_default loc curr_pdesc typestate e1 tr_default loc
| Exp.Const _ ->
let typ, _ = tr_default in
(typ, InferredNullability.create_nonnull (TypeOrigin.Const loc))
| Exp.Lfield (exp, field_name, typ) -> | Exp.Lfield (exp, field_name, typ) ->
let _, _ = tr_default in let _, _ = tr_default in
let _, inferred_nullability = let _, inferred_nullability =

@ -17,20 +17,23 @@ type proc_origin =
[@@deriving compare] [@@deriving compare]
type t = type t =
| Const of Location.t | NullConst of Location.t (** A null literal in the source *)
| Field of t * Typ.Fieldname.t * Location.t | NonnullConst of Location.t (** A constant (not equal to null) in the source. *)
| Formal of Mangled.t | Field of t * Typ.Fieldname.t * Location.t (** A field access *)
| Proc of proc_origin | Formal of Mangled.t (** A formal parameter *)
| New | Proc of proc_origin (** A procedure call *)
| ONone | New (** A new object creation *)
| Undef | ONone (** No origin is known *)
| Undef (** Undefined value before initialization *)
[@@deriving compare] [@@deriving compare]
let equal = [%compare.equal: t] let equal = [%compare.equal: t]
let rec to_string = function let rec to_string = function
| Const _ -> | NullConst _ ->
"Const" "null"
| NonnullConst _ ->
"Const (nonnull)"
| Field (o, fn, _) -> | Field (o, fn, _) ->
"Field " ^ Typ.Fieldname.to_simplified_string fn ^ " (inner: " ^ to_string o ^ ")" "Field " ^ Typ.Fieldname.to_simplified_string fn ^ " (inner: " ^ to_string o ^ ")"
| Formal s -> | Formal s ->
@ -48,7 +51,7 @@ let rec to_string = function
let get_description origin = let get_description origin =
let atline loc = " at line " ^ string_of_int loc.Location.line in let atline loc = " at line " ^ string_of_int loc.Location.line in
match origin with match origin with
| Const loc -> | NullConst loc ->
Some ("null constant" ^ atline loc, Some loc, None) Some ("null constant" ^ atline loc, Some loc, None)
| Field (_, fn, loc) -> | Field (_, fn, loc) ->
Some ("field " ^ Typ.Fieldname.to_simplified_string fn ^ atline loc, Some loc, None) Some ("field " ^ Typ.Fieldname.to_simplified_string fn ^ atline loc, Some loc, None)
@ -67,7 +70,16 @@ let get_description origin =
modelled_in (atline po.loc) modelled_in (atline po.loc)
in in
Some (description, Some po.loc, Some po.annotated_signature) Some (description, Some po.loc, Some po.annotated_signature)
| New | ONone | Undef -> (* These are origins of non-nullable expressions that are result of evaluating of some rvalue.
Because they are non-nullable and they are rvalues, we won't get normal type violations
With them. All we could get is things like condition redundant or overannotated.
But for these issues we currently don't print origins in the error string.
It is a good idea to change this and start printing origins for these origins as well.
*)
| New | NonnullConst _ ->
None
(* Two special cases - should not really occur in normal code *)
| ONone | Undef ->
None None
@ -76,7 +88,7 @@ let join o1 o2 =
(* left priority *) (* left priority *)
| Undef, _ | _, Undef -> | Undef, _ | _, Undef ->
Undef Undef
| Field _, (Const _ | Formal _ | Proc _ | New) -> | Field _, (NullConst _ | NonnullConst _ | Formal _ | Proc _ | New) ->
(* low priority to Field, to support field initialization patterns *) (* low priority to Field, to support field initialization patterns *)
o2 o2
| _ -> | _ ->

@ -16,7 +16,8 @@ type proc_origin =
[@@deriving compare] [@@deriving compare]
type t = type t =
| Const of Location.t (** A constant in the source *) | NullConst of Location.t (** A null literal in the source *)
| NonnullConst of Location.t (** A constant (not equal to null) in the source. *)
| Field of t * Typ.Fieldname.t * Location.t (** A field access *) | Field of t * Typ.Fieldname.t * Location.t (** A field access *)
| Formal of Mangled.t (** A formal parameter *) | Formal of Mangled.t (** A formal parameter *)
| Proc of proc_origin (** A procedure call *) | Proc of proc_origin (** A procedure call *)

Loading…
Cancel
Save