Skip to content

Commit

Permalink
Tuple indexing for tuple splats
Browse files Browse the repository at this point in the history
Summary: Add support for indexing of values with types such as `(int, ...T)`. Simply recurse on the `T` with an adjusted index.

Differential Revision: D66003892

fbshipit-source-id: 1509699cdef09e3ec5e72bd106c636eacec166dd
  • Loading branch information
Andrew Kennedy authored and facebook-github-bot committed Nov 19, 2024
1 parent 4cf06f1 commit bf35c48
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 2 deletions.
58 changes: 56 additions & 2 deletions hphp/hack/src/typing/typing_array_access.ml
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,62 @@ let rec array_get
in
Option.iter ty_err1 ~f:(Typing_error_utils.add_typing_error ~env);
(env, (ty, dflt_arr_res, idx_err_res))
(* TODO splats in tuples *)
| Ttuple { t_required; t_extra = Tsplat t_splat } -> begin
(* requires integer literal *)
match e2 with
| (a, p, Int idx_str) ->
let idx = int_of_string_opt idx_str in
let count_required = List.length t_required in
(match idx with
(* Index is within required elements *)
| Some n when Int.(n >= 0) && Int.(n < count_required) ->
(env, (List.nth_exn t_required n, dflt_arr_res, Ok ty2))
(* Index is within splat elements; delegate to splat tuple *)
| Some n when Int.(n >= count_required) ->
let (env, (ty, err_opt_arr, err_opt_idx)) =
array_get
~array_pos
~expr_pos
~expr_ty
~lhs_of_null_coalesce
is_lvalue
env
t_splat
(* Clunky hack so we can recurse with a new index expression *)
(a, p, Int (Int.to_string (n - count_required)))
ty2
in
let err_res_idx =
Option.value_map
err_opt_idx
~default:(Ok ty2)
~f:(fun (ty_have, ty_expect) -> Error (ty_have, ty_expect))
in
let err_res_arr =
Option.value_map
err_opt_arr
~default:dflt_arr_res
~f:(fun (ty_have, ty_expect) -> Error (ty_have, ty_expect))
in
(env, (ty, err_res_arr, err_res_idx))
(* Index is not an integer literal, or is negative *)
| _ ->
let (env, ty) = err_witness env p in
add_error_tuple env p;
(env, (ty, dflt_arr_res, Ok ty2)))
| (_, p, _) ->
Typing_error_utils.add_typing_error
~env
Typing_error.(
primary
@@ Primary.Generic_unify
{
pos = p;
msg = Reason.string_of_ureason Reason.URtuple_access;
});
let (env, ty) = err_witness env expr_pos in
(env, (ty, dflt_arr_res, Error (ty2, MakeType.int Reason.none)))
end
| Ttuple { t_required; t_extra = Textra { t_optional; t_variadic } } ->
begin
(* requires integer literal *)
Expand Down Expand Up @@ -865,7 +920,6 @@ let rec array_get
| Tintersection _
| Taccess _
| Tlabel _
| Ttuple { t_extra = Tsplat _; _ }
| Tneg _
| Tclass_ptr _ ->
if not ignore_error then error_array env expr_pos expr_ty;
Expand Down
21 changes: 21 additions & 0 deletions hphp/hack/test/typecheck/splat/type_splat_tuple_proj.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?hh

<<file: __EnableUnstableFeatures('open_tuples', 'type_splat')>>

function proj1<T as (arraykey...)>((int, string, ...T) $tup):void {
hh_expect_equivalent<int>($tup[0]);
hh_expect_equivalent<string>($tup[1]);
// This should be an array because the element may not exist
$y = $tup[2];
hh_expect_equivalent<?arraykey>($tup[2] ?? null);
}

function proj2<T as (float, bool, arraykey...)>((int, string, ...T) $tup):void {
hh_expect_equivalent<int>($tup[0]);
hh_expect_equivalent<string>($tup[1]);
hh_expect_equivalent<float>($tup[2]);
hh_expect_equivalent<bool>($tup[3]);
// This should be an array because the element may not exist
$y = $tup[4];
hh_expect_equivalent<?arraykey>($tup[4] ?? null);
}
4 changes: 4 additions & 0 deletions hphp/hack/test/typecheck/splat/type_splat_tuple_proj.php.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ERROR: File "type_splat_tuple_proj.php", line 9, characters 13-13:
Invalid index type for this tuple (Typing[4116])
ERROR: File "type_splat_tuple_proj.php", line 19, characters 13-13:
Invalid index type for this tuple (Typing[4116])

0 comments on commit bf35c48

Please sign in to comment.