From b334647cad18434dc6e2880b522f8dbe3e442d0a Mon Sep 17 00:00:00 2001 From: Michael Tingley Date: Thu, 16 Mar 2017 17:14:57 -0700 Subject: [PATCH] Implement type checker enforcement to prevent array <: array Summary: When the `--safe_vector_array` type checker flag is set, this diff enforces that a vector-like array may not be used where a hashtable-like array is required. Currently, Hack assumes the following: ``` array <: array ``` The flag makes it such that there is no subtyping relationship between `array` and `array`. Reviewed By: eshrews Differential Revision: D4720058 fbshipit-source-id: d492fe5d0c53c7e3cfe5385104c3f4c34d793be9 --- hphp/hack/src/typing/typing_subtype.ml | 3 ++- .../test/typecheck/array/safe_vector_array/HH_FLAGS | 1 + ...f_string_when_array_of_int_to_string_is_required.php | 17 +++++++++++++++++ ...ring_when_array_of_int_to_string_is_required.php.exp | 6 ++++++ ...rray_of_int_when_array_of_int_to_int_is_required.php | 7 +++++++ ..._of_int_when_array_of_int_to_int_is_required.php.exp | 6 ++++++ ..._array_of_int_when_array_of_tk_to_tv_is_required.php | 7 +++++++ ...ay_of_int_when_array_of_tk_to_tv_is_required.php.exp | 6 ++++++ ...adic_of_int_when_array_of_int_to_int_is_required.php | 7 +++++++ ..._of_int_when_array_of_int_to_int_is_required.php.exp | 6 ++++++ 10 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 hphp/hack/test/typecheck/array/safe_vector_array/HH_FLAGS create mode 100644 hphp/hack/test/typecheck/array/safe_vector_array/disallows_a_variable_that_may_be_an_array_of_string_when_array_of_int_to_string_is_required.php create mode 100644 hphp/hack/test/typecheck/array/safe_vector_array/disallows_a_variable_that_may_be_an_array_of_string_when_array_of_int_to_string_is_required.php.exp create mode 100644 hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_int_to_int_is_required.php create mode 100644 hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_int_to_int_is_required.php.exp create mode 100644 hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_tk_to_tv_is_required.php create mode 100644 hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_tk_to_tv_is_required.php.exp create mode 100644 hphp/hack/test/typecheck/array/safe_vector_array/disallows_variadic_of_int_when_array_of_int_to_int_is_required.php create mode 100644 hphp/hack/test/typecheck/array/safe_vector_array/disallows_variadic_of_int_when_array_of_int_to_int_is_required.php.exp diff --git a/hphp/hack/src/typing/typing_subtype.ml b/hphp/hack/src/typing/typing_subtype.ml index c7c246e212e..0733fc24d40 100644 --- a/hphp/hack/src/typing/typing_subtype.ml +++ b/hphp/hack/src/typing/typing_subtype.ml @@ -747,7 +747,8 @@ and sub_type_with_uenv env (uenv_sub, ty_sub) (uenv_super, ty_super) = | (_, Tarraykind AKmap (tk_sub, tv_sub)), (_, (Tarraykind (AKmap (tk_super, tv_super)))) -> let env = sub_type env tk_sub tk_super in sub_type env tv_sub tv_super - | (reason, Tarraykind (AKvec elt_ty)), (_, Tarraykind AKmap _) -> + | (reason, Tarraykind (AKvec elt_ty)), (_, Tarraykind AKmap _) + when not (TypecheckerOptions.safe_vector_array (Env.get_options env)) -> let int_reason = Reason.Ridx (Reason.to_pos reason, Reason.Rnone) in let int_type = int_reason, Tprim Nast.Tint in sub_type env (reason, Tarraykind (AKmap (int_type, elt_ty))) ty_super diff --git a/hphp/hack/test/typecheck/array/safe_vector_array/HH_FLAGS b/hphp/hack/test/typecheck/array/safe_vector_array/HH_FLAGS new file mode 100644 index 00000000000..bda26ba4f15 --- /dev/null +++ b/hphp/hack/test/typecheck/array/safe_vector_array/HH_FLAGS @@ -0,0 +1 @@ +--safe_vector_array diff --git a/hphp/hack/test/typecheck/array/safe_vector_array/disallows_a_variable_that_may_be_an_array_of_string_when_array_of_int_to_string_is_required.php b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_a_variable_that_may_be_an_array_of_string_when_array_of_int_to_string_is_required.php new file mode 100644 index 00000000000..19c1ee51843 --- /dev/null +++ b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_a_variable_that_may_be_an_array_of_string_when_array_of_int_to_string_is_required.php @@ -0,0 +1,17 @@ + $_): void {} + +/** + * Test confilicting AKempty promotions - in one branch it's promoted to + * AKvec, in another to AKmap. Should be unresolved of those two afterwards. + */ +function test(): void { + $array = array(); + if (true) { + $array[] = ''; + } else { + $array[0] = ''; + } + consumeArrayOfIntToString($array); +} diff --git a/hphp/hack/test/typecheck/array/safe_vector_array/disallows_a_variable_that_may_be_an_array_of_string_when_array_of_int_to_string_is_required.php.exp b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_a_variable_that_may_be_an_array_of_string_when_array_of_int_to_string_is_required.php.exp new file mode 100644 index 00000000000..8ff979511ed --- /dev/null +++ b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_a_variable_that_may_be_an_array_of_string_when_array_of_int_to_string_is_required.php.exp @@ -0,0 +1,6 @@ +File "disallows_a_variable_that_may_be_an_array_of_string_when_array_of_int_to_string_is_required.php", line 16, characters 29-34: +Invalid argument (Typing[4110]) +File "disallows_a_variable_that_may_be_an_array_of_string_when_array_of_int_to_string_is_required.php", line 3, characters 36-40: +This is an array (used like a hashtable) +File "disallows_a_variable_that_may_be_an_array_of_string_when_array_of_int_to_string_is_required.php", line 12, characters 5-12: +It is incompatible with an array (used like a vector) because a value is appended to it diff --git a/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_int_to_int_is_required.php b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_int_to_int_is_required.php new file mode 100644 index 00000000000..71babbb255c --- /dev/null +++ b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_int_to_int_is_required.php @@ -0,0 +1,7 @@ + $_): void {} + +function test(array $arrayOfInt): void { + consumeArrayOfIntToInt($arrayOfInt); +} diff --git a/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_int_to_int_is_required.php.exp b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_int_to_int_is_required.php.exp new file mode 100644 index 00000000000..a2b7bd80d86 --- /dev/null +++ b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_int_to_int_is_required.php.exp @@ -0,0 +1,6 @@ +File "disallows_array_of_int_when_array_of_int_to_int_is_required.php", line 6, characters 26-36: +Invalid argument (Typing[4110]) +File "disallows_array_of_int_when_array_of_int_to_int_is_required.php", line 3, characters 33-37: +This is an array (used like a hashtable) +File "disallows_array_of_int_when_array_of_int_to_int_is_required.php", line 5, characters 15-19: +It is incompatible with an array (used like a vector) diff --git a/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_tk_to_tv_is_required.php b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_tk_to_tv_is_required.php new file mode 100644 index 00000000000..9d4022a3f7d --- /dev/null +++ b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_tk_to_tv_is_required.php @@ -0,0 +1,7 @@ +(array $_): void {} + +function test(array $arrayOfInt): void { + consumeArrayOfTkToTv($arrayOfInt); +} diff --git a/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_tk_to_tv_is_required.php.exp b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_tk_to_tv_is_required.php.exp new file mode 100644 index 00000000000..38164c43079 --- /dev/null +++ b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_tk_to_tv_is_required.php.exp @@ -0,0 +1,6 @@ +File "disallows_array_of_int_when_array_of_tk_to_tv_is_required.php", line 6, characters 24-34: +Invalid argument (Typing[4110]) +File "disallows_array_of_int_when_array_of_tk_to_tv_is_required.php", line 3, characters 39-43: +This is an array (used like a hashtable) +File "disallows_array_of_int_when_array_of_tk_to_tv_is_required.php", line 5, characters 15-19: +It is incompatible with an array (used like a vector) diff --git a/hphp/hack/test/typecheck/array/safe_vector_array/disallows_variadic_of_int_when_array_of_int_to_int_is_required.php b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_variadic_of_int_when_array_of_int_to_int_is_required.php new file mode 100644 index 00000000000..c7e2472a2c2 --- /dev/null +++ b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_variadic_of_int_when_array_of_int_to_int_is_required.php @@ -0,0 +1,7 @@ + $_): void {} + +function test(int ...$variadicOfInt): void { + consumeArrayOfIntToInt($variadicOfInt); +} diff --git a/hphp/hack/test/typecheck/array/safe_vector_array/disallows_variadic_of_int_when_array_of_int_to_int_is_required.php.exp b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_variadic_of_int_when_array_of_int_to_int_is_required.php.exp new file mode 100644 index 00000000000..8ccd5e112bd --- /dev/null +++ b/hphp/hack/test/typecheck/array/safe_vector_array/disallows_variadic_of_int_when_array_of_int_to_int_is_required.php.exp @@ -0,0 +1,6 @@ +File "disallows_variadic_of_int_when_array_of_int_to_int_is_required.php", line 6, characters 26-39: +Invalid argument (Typing[4110]) +File "disallows_variadic_of_int_when_array_of_int_to_int_is_required.php", line 3, characters 33-37: +This is an array (used like a hashtable) +File "disallows_variadic_of_int_when_array_of_int_to_int_is_required.php", line 5, characters 22-35: +It is incompatible with an array (used like a vector) (variadic argument) -- 2.11.4.GIT