Permalink
Browse files

Gate all unification by flag

Summary:
Rename flag from `unresolved-as-union` to `new-inference`.

Use flag to control use of unification vs constraint solving for type inference. For now, just accumulate constraints in environment, and disable unification when flag is set.

To see the effect, try something like
```
function blah<T>(T $x):T {
  return $x;
}
function foo():arraykey {
  $x = blah(3);
  $y = blah($x);
  return $y;
  hh_show_env();
}
```
The environment that is dumped will include the (unsolved) constraints
```
(int <: #7 && #7 <: #10 && #10 <: arraykey)
```

Reviewed By: jamesjwu

Differential Revision: D12869043

fbshipit-source-id: 007a3f8cdcd2df0eb6e9cec3a4d3e1d465f9cb53
  • Loading branch information...
andrewjkennedy authored and hhvm-bot committed Nov 1, 2018
1 parent 54e9167 commit 6ee3860fcb53a8351a133feee45f5227da659dae
No changes.
@@ -185,7 +185,7 @@ let parse_options () =
let unsafe_rx = ref false in
let enable_concurrent = ref false in
let disallow_stringish_magic = ref false in
let unresolved_as_union = ref false in
let new_inference = ref false in
let all_errors = ref false in
let batch_mode = ref false in
let options = [
@@ -361,9 +361,9 @@ let parse_options () =
"--disallow-stringish-magic",
Arg.Set disallow_stringish_magic,
" Disallow using objects in contexts where strings are required.";
"--unresolved-as-union",
Arg.Set unresolved_as_union,
" Interpret unresolved in type inference only as union.";
"--new-inference",
Arg.Set new_inference,
" Type inference by constraint generation.";
"--batch-files",
Arg.Set batch_mode,
" Typecheck each file passed in independently";
@@ -393,7 +393,7 @@ let parse_options () =
GlobalOptions.tco_disallow_anon_use_capture_by_ref = not !allow_anon_use_capture_by_ref;
GlobalOptions.tco_disallow_unset_on_varray = !disallow_unset_on_varray;
GlobalOptions.tco_disallow_stringish_magic = !disallow_stringish_magic;
GlobalOptions.tco_unresolved_as_union = !unresolved_as_union;
GlobalOptions.tco_new_inference = !new_inference;
GlobalOptions.po_auto_namespace_map = !auto_namespace_map;
GlobalOptions.po_enable_concurrent = !enable_concurrent;
} in
@@ -38,7 +38,7 @@ type t = {
tco_disallow_scrutinee_case_value_type_mismatch : bool;
tco_disallow_stringish_magic : bool;
tco_disallow_anon_use_capture_by_ref : bool;
tco_unresolved_as_union : bool;
tco_new_inference : bool;
ignored_fixme_codes : ISet.t;
forward_compatibility_level : ForwardCompatibilityLevel.t;
} [@@deriving show]
@@ -207,7 +207,7 @@ let default = {
tco_disallow_scrutinee_case_value_type_mismatch = false;
tco_disallow_stringish_magic = false;
tco_disallow_anon_use_capture_by_ref = false;
tco_unresolved_as_union = false;
tco_new_inference = false;
ignored_fixme_codes = Errors.default_ignored_fixme_codes;
forward_compatibility_level = ForwardCompatibilityLevel.default;
}
@@ -255,7 +255,7 @@ let make ~tco_assume_php
~tco_disallow_scrutinee_case_value_type_mismatch
~tco_disallow_stringish_magic
~tco_disallow_anon_use_capture_by_ref
~tco_unresolved_as_union
~tco_new_inference
~ignored_fixme_codes
~forward_compatibility_level = {
tco_assume_php;
@@ -289,7 +289,7 @@ let make ~tco_assume_php
tco_disallow_scrutinee_case_value_type_mismatch;
tco_disallow_stringish_magic;
tco_disallow_anon_use_capture_by_ref;
tco_unresolved_as_union;
tco_new_inference;
forward_compatibility_level;
}
let tco_assume_php t = t.tco_assume_php
@@ -332,6 +332,6 @@ let tco_disallow_scrutinee_case_value_type_mismatch t =
t.tco_disallow_scrutinee_case_value_type_mismatch
let tco_disallow_stringish_magic t = t.tco_disallow_stringish_magic
let tco_disallow_anon_use_capture_by_ref t = t.tco_disallow_anon_use_capture_by_ref
let tco_unresolved_as_union t = t.tco_unresolved_as_union
let tco_new_inference t = t.tco_new_inference
let ignored_fixme_codes t = t.ignored_fixme_codes
let forward_compatibility_level t = t.forward_compatibility_level
@@ -170,7 +170,7 @@ type t = {
(*
* Flag to enable interpretation of unresolved only as union
*)
tco_unresolved_as_union : bool;
tco_new_inference : bool;
(* Error codes for which we do not allow HH_FIXMEs *)
ignored_fixme_codes : ISet.t;
@@ -208,7 +208,7 @@ val make :
tco_disallow_scrutinee_case_value_type_mismatch: bool ->
tco_disallow_stringish_magic: bool ->
tco_disallow_anon_use_capture_by_ref: bool ->
tco_unresolved_as_union: bool ->
tco_new_inference: bool ->
ignored_fixme_codes: ISet.t ->
forward_compatibility_level: ForwardCompatibilityLevel.t ->
t
@@ -243,7 +243,7 @@ val tco_disallow_unset_on_varray : t -> bool
val tco_disallow_scrutinee_case_value_type_mismatch : t -> bool
val tco_disallow_stringish_magic : t -> bool
val tco_disallow_anon_use_capture_by_ref : t -> bool
val tco_unresolved_as_union : t -> bool
val tco_new_inference : t -> bool
val default : t
val make_permissive : t -> t
val tco_experimental_instanceof : string
@@ -72,5 +72,5 @@ let disallow_scrutinee_case_value_type_mismatch =
GlobalOptions.tco_disallow_scrutinee_case_value_type_mismatch
let disallow_stringish_magic = GlobalOptions.tco_disallow_stringish_magic
let disallow_anon_use_capture_by_ref = GlobalOptions.tco_disallow_anon_use_capture_by_ref
let unresolved_as_union = GlobalOptions.tco_unresolved_as_union
let new_inference = GlobalOptions.tco_new_inference
let forward_compatibility_level = GlobalOptions.forward_compatibility_level
@@ -295,7 +295,7 @@ let load config_filename options =
~tco_disallow_stringish_magic:(bool_ "disallow_stringish_magic" ~default:false config)
~tco_disallow_anon_use_capture_by_ref:(bool_
"disallow_anon_use_capture_by_ref" ~default:false config)
~tco_unresolved_as_union:(bool_ "unresolved_as_union" ~default:false config)
~tco_new_inference:(bool_ "new_inference" ~default:false config)
~ignored_fixme_codes:(prepare_ignored_fixme_codes config)
~forward_compatibility_level:forward_compat_level
in
@@ -126,4 +126,7 @@ let rec truthiness env ty =
| Ttuple [] -> Always_falsy
| Tobject | Tfun _ | Ttuple _ | Tanon _ -> Always_truthy
| Tvar _ -> failwith "expand_type failed"
| Tvar _ ->
if TypecheckerOptions.new_inference (Env.get_tcopt env)
then Unknown
else failwith "expand_type failed"
@@ -70,7 +70,7 @@ let add env x ty =
let fresh_unresolved_type env =
let v = Ident.tmp () in
let env =
if TypecheckerOptions.unresolved_as_union env.genv.tcopt
if TypecheckerOptions.new_inference env.genv.tcopt
then env
else add env v (Reason.Rnone, Tunresolved []) in
env, (Reason.Rnone, Tvar v)
@@ -842,7 +842,7 @@ end
*)
let rec unbind seen env ty =
if TypecheckerOptions.unresolved_as_union (get_tcopt env)
if TypecheckerOptions.new_inference (get_tcopt env)
then env, ty
else
let env, ty = expand_type env ty in
@@ -353,6 +353,9 @@ module Full = struct
| Toption x -> Concat [text "?"; k x]
| Tprim x -> text @@ prim x
| Tvar n ->
if TypecheckerOptions.new_inference (Env.get_tcopt env)
then text ("#" ^ (string_of_int n))
else
let _, n' = Env.get_var env n in
let prepend =
if ISet.mem n' st then text "[rec]"
@@ -187,14 +187,16 @@ let log_subtype ~this_ty function_name env ty_sub ty_super =
[Typing_log.Log_sub ("Typing_subtype." ^ function_name, types)]
end
(* Process the constraint proposition *)
let rec process_simplify_subtype_result ~this_ty ~fail env prop =
Typing_log.log_prop 2 env.Env.pos "process_simplify_subtype_result" env prop;
match prop with
| TL.Unsat f ->
f ();
env
| TL.Sub (ty1, ty2) -> sub_type_inner_helper env ~this_ty ty1 ty2
| TL.Eq (ty1, ty2) -> fst (Unify.unify env ty2 ty1)
| TL.Eq (ty1, ty2) ->
(* These come only from invariant generics, with new_inference=false *)
fst (Unify.unify env ty2 ty1)
| TL.Conj props ->
(* Evaluates list from left-to-right so preserves order of conjuncts *)
List.fold_left
@@ -333,7 +335,10 @@ and simplify_subtype
end in
match snd ety_sub, snd ety_super with
| Tvar _, _ | _, Tvar _ -> assert false
| Tvar _, _ | _, Tvar _ ->
if TypecheckerOptions.new_inference (Env.get_tcopt env)
then default ()
else assert false
(* Internally, newtypes are always equipped with an upper bound.
* In the case when no upper bound is specified in source code,
@@ -1495,16 +1500,19 @@ and sub_type_inner
(ty_sub: locl ty)
(ty_super: locl ty) : Env.env =
log_subtype ~this_ty "sub_type_inner" env ty_sub ty_super;
let deep = TypecheckerOptions.unresolved_as_union (Env.get_tcopt env) in
let new_inference = TypecheckerOptions.new_inference (Env.get_tcopt env) in
let env, prop = simplify_subtype
~seen_generic_params:empty_seen ~deep ~this_ty ty_sub ty_super env in
~seen_generic_params:empty_seen ~deep:new_inference ~this_ty ty_sub ty_super env in
let env =
if TypecheckerOptions.experimental_feature_enabled
if new_inference || TypecheckerOptions.experimental_feature_enabled
(Env.get_tcopt env)
TypecheckerOptions.experimental_track_subtype_prop
then Env.add_subtype_prop env prop
then begin
log_prop env prop;
Typing_log.log_prop 2 env.Env.pos "sub_type_inner: add_subtype_prop" env prop;
Env.add_subtype_prop env prop
end
else env in
log_prop env prop;
process_simplify_subtype_result ~this_ty
~fail:(fun () -> TUtils.uerror (fst ty_super) (snd ty_super) (fst ty_sub) (snd ty_sub))
env
@@ -1560,7 +1568,8 @@ and sub_type_inner_helper env ~this_ty
sub_type_inner env ~this_ty ty_sub ty_super
(* This case is for when Tany comes from expanding an empty Tvar - it will
* result in binding the type variable to the other type. *)
| _, (_, Tany) -> fst (Unify.unify env ty_super ty_sub)
| _, (_, Tany) ->
fst (Unify.unify env ty_super ty_sub)
(* If the subtype is a type variable bound to an empty unresolved, record
* this in the todo list to be checked later. But make sure that we wrap
* any error using the position and reason information that was supplied
@@ -1737,7 +1746,7 @@ and sub_type_inner_helper env ~this_ty
| _, _ ->
(* TODO: replace this by fail() once we support all subtyping rules that
* are implemented in unification *)
fst (Unify.unify env ty_super ty_sub)
fst (Unify.unify env ty_super ty_sub)
(* BEWARE: hack upon hack here.
* To implement a predicate that tests whether `ty_sub` is a subtype of
@@ -20,6 +20,9 @@ module TURecursive = Typing_unify_recursive
*)
let rec unify ?(opts=TUtils.default_unify_opt) env ty1 ty2 =
if phys_equal ty1 ty2 then env, ty1 else
if TypecheckerOptions.new_inference (Env.get_tcopt env)
then env, ty1
else
let types =
[Typing_log.Log_type ("ty1", ty1);
Typing_log.Log_type ("ty2", ty2)] in
@@ -615,7 +615,7 @@ let in_var env ty =
let unresolved_tparam env (_, (pos, _), _, _) =
let reason = Reason.Rwitness pos in
if TypecheckerOptions.unresolved_as_union (Typing_env.get_tcopt env)
if TypecheckerOptions.new_inference (Typing_env.get_tcopt env)
then env, (reason, Tvar (Env.fresh ()))
else in_var env (reason, Tunresolved [])
@@ -650,7 +650,7 @@ let string_of_visibility = function
| Vprotected _ -> "protected"
let unresolved env ty =
if TypecheckerOptions.unresolved_as_union (Typing_env.get_tcopt env)
if TypecheckerOptions.new_inference (Typing_env.get_tcopt env)
then env, ty
else
let env, ety = Env.expand_type env ty in
@@ -123,7 +123,7 @@ let () =
~tco_disallow_scrutinee_case_value_type_mismatch:false
~tco_disallow_stringish_magic:false
~tco_disallow_anon_use_capture_by_ref:false
~tco_unresolved_as_union:false
~tco_new_inference:false
~ignored_fixme_codes: ISet.empty
~forward_compatibility_level: ForwardCompatibilityLevel.default
in
@@ -51,7 +51,7 @@ let global_opts = GlobalOptions.make
~tco_disallow_scrutinee_case_value_type_mismatch:false
~tco_disallow_stringish_magic:false
~tco_disallow_anon_use_capture_by_ref:false
~tco_unresolved_as_union:false
~tco_new_inference:false
~ignored_fixme_codes: ISet.empty
~forward_compatibility_level: ForwardCompatibilityLevel.default
@@ -4,7 +4,7 @@
function testit(Super $tf, Sub $s):void {
// This works
// bar<Super>(async (C<Super> $step) ==> new Super());
// This does not, with unresolved_as_union, because Hack infers `Sub` for Tv
// This does not, with --new-inference, because Hack infers `Sub` for Tv
// Then C<Sub> does not unify with C<Sub>
bar(async (C<Super> $step) ==> $s);
}
@@ -36,7 +36,7 @@ let global_options =
~tco_disallow_scrutinee_case_value_type_mismatch:false
~tco_disallow_stringish_magic:false
~tco_disallow_anon_use_capture_by_ref:false
~tco_unresolved_as_union:false
~tco_new_inference:false
~ignored_fixme_codes:ISet.empty
~forward_compatibility_level:ForwardCompatibilityLevel.default

0 comments on commit 6ee3860

Please sign in to comment.