Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use subtyping to handle splat destructuring
Summary: This diff changes splat (...) unpacking to use Tdestructure, and extends Tdestructure to have 3 main boxes for unpacking. There are two cases to explore: # Tuple destructuring Now, `Tdestructure` for splat (henceforth as "splat(...)") has three separate boxes for types. For example, the following example ``` function take(bool $b, float $f = 3.14, arraykey ...$aks): void {} function f((bool, float, int, string) $tup): void { take(...$tup); } ``` corresponds to the subtyping assertion ``` (bool, float, int, string) <: splat([#1], [opt#2], ...#3) ``` and results in ``` bool <: #1 float <: #2 arraykey <: #3 ``` First, it is required that the tuple have enough elements to fill the required values -- in this case just `[#1]`. Then, if there are remaining elements, they are first used to fill the optional elements `[#2]`. Finally, the remainder are collected in the variadic element `#3`. If tuple elements remain but there is no variadic element, then we emit an error. # Array destructuring Previously, the typechecker would allow nonsense like ``` function f(int $i = 3): void {} function g(int $i, int ...$j): void {} function passes_typechecker(): void { f(...vec[1,2]); // throws f(...vec[]); // throws } ``` Now, we require that splat operations with arrays have a variadic component to accept the values. We additionally ban unpacking into the required arguments of a function because the array could be empty. Unpacking into optionals is OK. Basically, we require that the function signature can accept the entire array. **Note:** we still allow partial array destructuring with *list destructuring*, so ``` list($a, $b) = vec[1,2,3,4]; ``` is still valid and is represented as ``` vec<int> <: list(#1, #2) ``` # Typing Rules In these rules `r` is a type of a required field, `o` is a type of an optional field, `v` is the type of the variadic field. # destruct_tuple_list ``` forall i in 1...n t_i <: r_i -------------------------------------- (t_1, ..., t_n) <: list(r_1, ..., r_n) ``` **Example** ``` function test_runner((int, string, bool) $tup): void { list($a, $b, $c) = $tup; } ``` # destruct_tuple_splat ``` forall i in 1...m forall j in (if p < n then m+1...p else m+1...n) forall k in p+1...n m <= n t_i <: r_i t_j <: o_j t_k <: v ----------------------------------------------------------- (t_1, ..., t_n) <: splat(r_1, ..., r_m, o_m+1, ..., o_p, v) ``` **Example** ``` function take(int $i, string $j, bool $kopt = false, float $fopt = 3.14, mixed ...$rest): void {} function test_runner((int, string) $tup): void { take(...$tup); } function test_runner2((int, string, bool) $tup): void { take(...$tup); } function test_runner3((int, string, bool, float) $tup): void { take(...$tup); } function test_runner4((int, string, bool, float, null, int, mixed) $tup): void { take(...$tup); } ``` # destruct_array_list This rule already exists, and it allows for operations that can throw. ``` forall i in 1...n t <: r_i ------------------------------------------- vec<t> <: list(r_1, ... r_n) Vector<t> <: ImmVector<t> <: ConstVector<t> <: ``` **Example** ``` list($a, $b, $c) = vec[3]; // legal, but will throw list($a) = vec[2,3,5]; // legal even though incomplete ``` # destructure_array_splat Note the lack of required field in this row, and the presence of the variadic. ``` forall i in 1..n t <: o_i t <: v ---------------------------------------- Traversable<t> <: splat(o_1, ... o_n, v) ``` **Example** ``` function take1(int $i = 3, int ...$is): void {} function take2(int ...$is): void {} function test(vec<int> $v): void { take1(...$v); take2(...$v); } ``` Reviewed By: CatherineGasnier Differential Revision: D18633271 fbshipit-source-id: 2b7a7beebf2ce30c2cb2765f75de2db6bdb3c24e
- Loading branch information