Enforce Pos_or_decl.t in reasons for decl types
[hiphop-php.git] / hphp / hack / src / typing / typing_array_access.ml
blob37a33669d99c2670328865bfb5bb300446358498
1 (*
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 open Hh_prelude
11 open Common
12 open Aast
13 open Typing_defs
14 module Env = Typing_env
15 module TUtils = Typing_utils
16 module Reason = Typing_reason
17 module Union = Typing_union
18 module Inter = Typing_intersection
19 module MakeType = Typing_make_type
20 module SubType = Typing_subtype
21 module Partial = Partial_provider
22 module GenericRules = Typing_generic_rules
23 open String.Replace_polymorphic_compare
25 let err_witness env p = TUtils.terr env (Reason.Rwitness p)
27 let error_array env p ty =
28 Errors.array_access_read p (get_pos ty) (Typing_print.error env ty);
29 (env, err_witness env p)
31 let error_const_mutation env p ty =
32 Errors.const_mutation p (get_pos ty) (Typing_print.error env ty);
33 (env, err_witness env p)
35 let error_assign_array_append env p ty =
36 Errors.array_append p (get_pos ty) (Typing_print.error env ty);
37 (env, ty)
39 (* Given a type `ty` known to be a lower bound on the type of the array operand
40 * to an array get operation, compute the largest upper bound on that type
41 * that validates the get operation. For example, if `vec<string>` is a lower
42 * bound, then the type must be a subtype of `KeyedContainer<#1,#2>` for some
43 * unresolved types #1 and #2. If `(string,int)` is a lower bound, then
44 * `(#1, #2)` is a suitable upper bound. Shapes are the most complex. If
45 * `shape(known_fields, ...)` is a lower bound (an open shape type), and the
46 * access is through a literal field f, then `shape('f' => #1, ...)` is a
47 * suitable upper bound.
49 let widen_for_array_get ~lhs_of_null_coalesce ~expr_pos index_expr env ty =
50 Typing_log.(
51 log_with_level env "typing" 1 (fun () ->
52 log_types
53 expr_pos
54 env
55 [Log_head ("widen_for_array_get", [Log_type ("ty", ty)])]));
56 match deref ty with
57 (* The null type is valid only with a null-coalescing use of array get *)
58 | (_, Tprim Tnull) when lhs_of_null_coalesce -> (env, Some ty)
59 (* dynamic is valid for array get *)
60 | (_, Tdynamic) -> (env, Some ty)
61 (* All class-based containers, and keyset, vec and dict, are subtypes of
62 * some instantiation of KeyedContainer
64 | (r, Tclass ((_, cn), _, _))
65 when cn = SN.Collections.cVector
66 || cn = SN.Collections.cVec
67 || cn = SN.Collections.cMap
68 || cn = SN.Collections.cDict
69 || cn = SN.Collections.cKeyset
70 || cn = SN.Collections.cConstMap
71 || cn = SN.Collections.cImmMap
72 || cn = SN.Collections.cKeyedContainer
73 || cn = SN.Collections.cAnyArray
74 || cn = SN.Collections.cConstVector
75 || cn = SN.Collections.cImmVector ->
76 let (env, element_ty) = Env.fresh_invariant_type_var env expr_pos in
77 let (env, index_ty) = Env.fresh_invariant_type_var env expr_pos in
78 let ty = MakeType.keyed_container r index_ty element_ty in
79 (env, Some ty)
80 (* The same is true of PHP arrays *)
81 | (r, (Tvarray _ | Tdarray _ | Tvec_or_dict _ | Tvarray_or_darray _)) ->
82 let (env, element_ty) = Env.fresh_invariant_type_var env expr_pos in
83 let (env, index_ty) = Env.fresh_invariant_type_var env expr_pos in
84 let ty = MakeType.keyed_container r index_ty element_ty in
85 (env, Some ty)
86 (* For tuples, we just freshen the element types *)
87 | (r, Ttuple tyl) ->
88 (* requires integer literal *)
89 begin
90 match index_expr with
91 (* Should freshen type variables *)
92 | (_, Int _) ->
93 let (env, params) =
94 List.map_env env tyl (fun env _ty ->
95 Env.fresh_invariant_type_var env expr_pos)
97 (env, Some (MakeType.tuple r params))
98 | _ -> (env, None)
99 end
100 (* Whatever the lower bound, construct an open, singleton shape type. *)
101 | (r, Tshape (_, fdm)) ->
102 begin
103 match TUtils.shape_field_name env index_expr with
104 | None -> (env, None)
105 | Some field ->
106 let field = TShapeField.of_ast (fun p -> p) field in
107 (match TShapeMap.find_opt field fdm with
108 (* If field is in the lower bound but is optional, then no upper bound makes sense
109 * unless this is a null-coalesce access *)
110 | Some { sft_optional = true; _ } when not lhs_of_null_coalesce ->
111 (env, None)
112 | _ ->
113 let (env, element_ty) = Env.fresh_invariant_type_var env expr_pos in
114 let upper_fdm =
115 TShapeMap.add
116 field
117 { sft_optional = lhs_of_null_coalesce; sft_ty = element_ty }
118 TShapeMap.empty
120 let upper_shape_ty = mk (r, Tshape (Open_shape, upper_fdm)) in
121 (env, Some upper_shape_ty))
123 | _ -> (env, None)
125 (* Check that an index to a map-like collection passes the basic test of
126 * being a subtype of arraykey
128 let check_arraykey_index error env pos container_ty index_ty =
129 if TypecheckerOptions.disallow_invalid_arraykey (Env.get_tcopt env) then
130 let (env, container_ty) = Env.expand_type env container_ty in
131 let reason =
132 match get_node container_ty with
133 | Tclass ((_, cn), _, _) -> Reason.index_class cn
134 | _ -> Reason.index_array
136 let info_of_type ty = (get_pos ty, Typing_print.error env ty) in
137 let ty_arraykey = MakeType.arraykey (Reason.Ridx_dict pos) in
138 (* Wrap generic type mismatch error with special error code *)
139 Typing_coercion.coerce_type
141 reason
143 index_ty
144 { et_type = ty_arraykey; et_enforced = true }
145 (fun ?code:_ _ _ ->
146 error pos (info_of_type container_ty) (info_of_type index_ty))
147 else
150 let check_arraykey_index_read =
151 check_arraykey_index Errors.invalid_arraykey_read
153 let check_arraykey_index_write =
154 check_arraykey_index Errors.invalid_arraykey_write
156 let rec array_get
157 ~array_pos
158 ~expr_pos
159 ?(lhs_of_null_coalesce = false)
160 is_lvalue
164 ty2 =
165 let (env, ty1) =
166 Typing_solver.expand_type_and_narrow
168 ~description_of_expected:"an array or collection"
169 (widen_for_array_get ~lhs_of_null_coalesce ~expr_pos e2)
170 array_pos
172 Errors.unify_error
174 GenericRules.apply_rules ~ignore_type_structure:true env ty1 (fun env ty1 ->
175 let (r, ety1_) = deref ty1 in
176 (* This is a little weird -- we enforce the right arity when you use certain
177 * collections, even in partial mode (where normally completely omitting the
178 * type parameter list is admitted). Basically the "omit type parameter"
179 * hole was for compatibility with certain interfaces like Arrayaccess, not
180 * for collections! But it's hard to go back on now, so since we've always
181 * errored (with an inscrutable error message) when you try to actually use
182 * a collection with omitted type parameters, we can continue to error and
183 * give a more useful error message. *)
184 let arity_error (_, name) =
185 Errors.array_get_arity expr_pos name (Reason.to_pos r)
187 let nullable_container_get env ty =
189 lhs_of_null_coalesce
190 (* Normally, we would not allow indexing into a nullable container,
191 however, because the pattern shows up so frequently, we are allowing
192 indexing into a nullable container as long as it is on the lhs of a
193 null coalesce *)
194 then
195 array_get
196 ~array_pos
197 ~expr_pos
198 ~lhs_of_null_coalesce
199 is_lvalue
204 else (
205 Errors.null_container
206 expr_pos
207 (Reason.to_string
208 "This is what makes me believe it can be `null`."
210 (env, err_witness env expr_pos)
213 let type_index env p ty_have ty_expect reason =
214 Typing_log.(
215 log_with_level env "typing" 1 (fun () ->
216 log_types
220 Log_head
221 ( "array_get/type_index",
223 Log_type ("ty_have", ty_have);
224 Log_type ("ty_expect", ty_expect);
225 ] );
226 ]));
228 (* coerce if possible *)
229 match
230 Typing_coercion.try_coerce
232 ty_have
233 { et_type = ty_expect; et_enforced = true }
234 with
235 | Some env -> env
236 | None ->
237 (* if subtype of dynamic, allow it to be used *)
239 Typing_solver.is_sub_type env ty_have (MakeType.dynamic Reason.none)
240 then
242 (* fail with useful error *)
243 else
244 Typing_ops.sub_type
246 reason
248 ty_have
249 ty_expect
250 Errors.index_type_mismatch
252 match ety1_ with
253 | Tvarray ty ->
254 let ty1 = MakeType.int (Reason.Ridx (fst e2, r)) in
255 let env = type_index env expr_pos ty2 ty1 Reason.index_array in
256 (env, ty)
257 | Tclass (((_, cn) as id), _, argl)
258 when String.equal cn SN.Collections.cVector
259 || String.equal cn SN.Collections.cVec ->
260 let ty =
261 match argl with
262 | [ty] -> ty
263 | _ ->
264 arity_error id;
265 err_witness env expr_pos
267 let ty1 = MakeType.int (Reason.Ridx_vector (fst e2)) in
268 let env = type_index env expr_pos ty2 ty1 (Reason.index_class cn) in
269 (env, ty)
270 | Tclass (((_, cn) as id), _, argl)
271 when cn = SN.Collections.cMap
272 || cn = SN.Collections.cDict
273 || cn = SN.Collections.cKeyset ->
274 if cn = SN.Collections.cKeyset && is_lvalue then (
275 Errors.keyset_set expr_pos (Reason.to_pos r);
276 (env, err_witness env expr_pos)
277 ) else
278 let (k, v) =
279 match argl with
280 | [t] when String.equal cn SN.Collections.cKeyset -> (t, t)
281 | [k; v] when String.( <> ) cn SN.Collections.cKeyset -> (k, v)
282 | _ ->
283 arity_error id;
284 let any = err_witness env expr_pos in
285 (any, any)
287 (* dict and keyset are covariant in the key type, so subsumption
288 * lets you upcast the key type beyond ty2 to arraykey.
289 * e.g. consider $d: dict<string,int> and $i:int
290 * and $d[$i] should actually type check because
291 * dict<string,int> <: dict<arraykey,int>
293 let (env, k) = Env.expand_type env k in
294 let env =
296 String.equal cn SN.Collections.cDict
297 || String.equal cn SN.Collections.cKeyset
298 then
299 check_arraykey_index_read env expr_pos ty1 ty2
300 else
301 type_index env expr_pos ty2 k (Reason.index_class cn)
303 (env, v)
304 (* Certain container/collection types are intended to be immutable/const,
305 * thus they should never appear as a lvalue when indexing i.e.
307 * $x[0] = 100; // ERROR
308 * $x[0]; // OK
310 | Tclass (((_, cn) as id), _, argl)
311 when String.equal cn SN.Collections.cConstMap
312 || String.equal cn SN.Collections.cImmMap
313 || String.equal cn SN.Collections.cKeyedContainer
314 || String.equal cn SN.Collections.cAnyArray ->
315 if is_lvalue then
316 error_const_mutation env expr_pos ty1
317 else
318 let (_k, v) =
319 match argl with
320 | [k; v] -> (k, v)
321 | _ ->
322 arity_error id;
323 let any = err_witness env expr_pos in
324 (any, any)
326 let env = check_arraykey_index_read env expr_pos ty1 ty2 in
327 (env, v)
328 | Tclass (((_, cn) as id), _, argl)
329 when (not is_lvalue)
330 && ( String.equal cn SN.Collections.cConstVector
331 || String.equal cn SN.Collections.cImmVector ) ->
332 let ty =
333 match argl with
334 | [ty] -> ty
335 | _ ->
336 arity_error id;
337 err_witness env expr_pos
339 let ty1 = MakeType.int (Reason.Ridx (fst e2, r)) in
340 let env = type_index env expr_pos ty2 ty1 (Reason.index_class cn) in
341 (env, ty)
342 | Tclass ((_, cn), _, _)
343 when is_lvalue
344 && ( String.equal cn SN.Collections.cConstVector
345 || String.equal cn SN.Collections.cImmVector ) ->
346 error_const_mutation env expr_pos ty1
347 | Tdarray (_k, v)
348 | Tvec_or_dict (_k, v)
349 | Tvarray_or_darray (_k, v) ->
350 let env = check_arraykey_index_read env expr_pos ty1 ty2 in
351 (env, v)
352 | Terr -> (env, err_witness env expr_pos)
353 | Tdynamic -> (env, ty1)
354 | Tany _ -> (env, TUtils.mk_tany env expr_pos)
355 | Tprim Tstring ->
356 let ty = MakeType.string (Reason.Rwitness expr_pos) in
357 let ty1 = MakeType.int (Reason.Ridx (fst e2, r)) in
358 let env = type_index env expr_pos ty2 ty1 Reason.index_array in
359 (env, ty)
360 | Ttuple tyl ->
361 (* requires integer literal *)
362 (match e2 with
363 | (p, Int n) ->
364 let idx = int_of_string_opt n in
365 (match Option.bind idx ~f:(List.nth tyl) with
366 | Some nth -> (env, nth)
367 | None ->
368 Errors.typing_error p (Reason.string_of_ureason Reason.index_tuple);
369 (env, err_witness env p))
370 | (p, _) ->
371 Errors.typing_error p (Reason.string_of_ureason Reason.URtuple_access);
372 (env, err_witness env p))
373 | Tclass (((_, cn) as id), _, argl)
374 when String.equal cn SN.Collections.cPair ->
375 let (ty1, ty2) =
376 match argl with
377 | [ty1; ty2] -> (ty1, ty2)
378 | _ ->
379 arity_error id;
380 let any = err_witness env expr_pos in
381 (any, any)
383 (* requires integer literal *)
384 (match e2 with
385 | (p, Int n) ->
386 let idx = int_of_string_opt n in
387 (match Option.bind ~f:(List.nth [ty1; ty2]) idx with
388 | Some nth -> (env, nth)
389 | None ->
390 Errors.typing_error p
391 @@ Reason.string_of_ureason (Reason.index_class cn);
392 (env, err_witness env p))
393 | (p, _) ->
394 Errors.typing_error p (Reason.string_of_ureason Reason.URpair_access);
395 (env, err_witness env p))
396 | Tshape (_, fdm) ->
397 if is_lvalue || lhs_of_null_coalesce then
398 (* The expression $s['x'] ?? $y is semantically equivalent to
399 Shapes::idx ($s, 'x') ?? $y. I.e., if $s['x'] occurs on
400 the left of a coalesce operator, then for type checking it
401 can be treated as if it evaluated to null instead of
402 throwing an exception if the field 'x' doesn't exist in $s.
404 Typing_shapes.idx
408 None
409 ~expr_pos
410 ~fun_pos:Reason.Rnone
411 ~shape_pos:array_pos
412 else
413 let p = fst e2 in
414 begin
415 match TUtils.shape_field_name env e2 with
416 | None ->
417 (* there was already an error in shape_field name,
418 don't report another one for a missing field *)
419 (env, err_witness env p)
420 | Some field ->
421 let field = TShapeField.of_ast (fun p -> p) field in
422 begin
423 match TShapeMap.find_opt field fdm with
424 | None ->
425 Errors.undefined_field
426 ~use_pos:p
427 ~name:(TUtils.get_printable_shape_field_name field)
428 ~shape_type_pos:(Reason.to_pos r);
429 (env, err_witness env p)
430 | Some { sft_optional; sft_ty } ->
431 if sft_optional then (
432 let declared_field =
433 List.find_exn
434 ~f:(fun x -> TShapeField.equal field x)
435 (TShapeMap.keys fdm)
437 Errors.array_get_with_optional_field
439 (Env.get_shape_field_name_pos declared_field)
440 (TUtils.get_printable_shape_field_name field);
441 (env, err_witness env p)
442 ) else
443 (env, sft_ty)
446 | Toption ty -> nullable_container_get env ty
447 | Tprim Tnull ->
448 let ty = MakeType.nothing Reason.Rnone in
449 nullable_container_get env ty
450 | Tobject ->
451 if Partial.should_check_error (Env.get_mode env) 4005 then
452 error_array env expr_pos ty1
453 else
454 (env, TUtils.mk_tany env expr_pos)
455 | Tnewtype (ts, [ty], bound) ->
456 begin
457 match deref bound with
458 | (r, Tshape (shape_kind, fields))
459 when String.equal ts SN.FB.cTypeStructure ->
460 let (env, fields) =
461 Typing_structure.transform_shapemap env array_pos ty fields
463 let ty = mk (r, Tshape (shape_kind, fields)) in
464 array_get
465 ~array_pos
466 ~expr_pos
467 ~lhs_of_null_coalesce
468 is_lvalue
473 | _ -> error_array env expr_pos ty1
475 | Tunapplied_alias _ ->
476 Typing_defs.error_Tunapplied_alias_in_illegal_context ()
477 | Tnonnull
478 | Tprim _
479 | Tfun _
480 | Tclass _
481 | Tgeneric _
482 | Tnewtype _
483 | Tdependent _
484 | Tunion _
485 | Tintersection _
486 | Taccess _ ->
487 error_array env expr_pos ty1
488 (* Type-check array access as though it is the method
489 * array_get<Tk,Tv>(KeyedContainer<Tk,Tv> $array, Tk $key): Tv
490 * (We can already force Tk to be the type of the key argument because
491 * Tk does not appear in the result of the call)
493 | Tvar _ ->
494 let (env, value) = Env.fresh_type env expr_pos in
495 let keyed_container = MakeType.keyed_container r ty2 value in
496 let env =
497 SubType.sub_type env ty1 keyed_container Errors.index_type_mismatch
499 (env, value))
501 (* Given a type `ty` known to be a lower bound on the type of the array operand
502 * to an array append operation, compute the largest upper bound on that type
503 * that validates the get operation. For example, if `vec<string>` is a lower
504 * bound, then `vec<#1>` is a suitable upper bound.`
506 let widen_for_assign_array_append ~expr_pos env ty =
507 match deref ty with
508 (* dynamic is valid for array append *)
509 | (_, Tdynamic) -> (env, Some ty)
510 | (r, Tclass (((_, cn) as id), _, tyl))
511 when String.equal cn SN.Collections.cVec
512 || String.equal cn SN.Collections.cKeyset
513 || String.equal cn SN.Collections.cVector
514 || String.equal cn SN.Collections.cMap ->
515 let (env, params) =
516 List.map_env env tyl (fun env _ty ->
517 Env.fresh_invariant_type_var env expr_pos)
519 let ty = mk (r, Tclass (id, Nonexact, params)) in
520 (env, Some ty)
521 | (r, Tvarray _) ->
522 let (env, element_ty) = Env.fresh_invariant_type_var env expr_pos in
523 (env, Some (mk (r, Tvarray element_ty)))
524 | _ -> (env, None)
526 let assign_array_append ~array_pos ~expr_pos ur env ty1 ty2 =
527 let (env, ty1) =
528 Typing_solver.expand_type_and_narrow
529 ~description_of_expected:"an array or collection"
531 (widen_for_assign_array_append ~expr_pos)
532 array_pos
534 Errors.unify_error
536 GenericRules.apply_rules env ty1 (fun env ty1 ->
537 match deref ty1 with
538 | (_, Tany _) -> (env, ty1)
539 | (_, Terr) -> (env, ty1)
540 | (_, Tclass ((_, n), _, [tv]))
541 when String.equal n SN.Collections.cVector
542 || String.equal n SN.Collections.cSet ->
543 let env =
544 Typing_ops.sub_type expr_pos ur env ty2 tv Errors.unify_error
546 (env, ty1)
547 (* Handle the case where Vector or Set was used as a typehint
548 without type parameters *)
549 | (_, Tclass ((_, n), _, []))
550 when String.equal n SN.Collections.cVector
551 || String.equal n SN.Collections.cSet ->
552 (env, ty1)
553 | (r, Tclass (((_, n) as id), e, [tv]))
554 when String.equal n SN.Collections.cVec
555 || String.equal n SN.Collections.cKeyset ->
556 let (env, tv') = Typing_union.union env tv ty2 in
557 (env, mk (r, Tclass (id, e, [tv'])))
558 | (r, Tvarray tv) ->
559 let (env, tv') = Typing_union.union env tv ty2 in
560 (env, mk (r, Tvarray tv'))
561 | (_, Tdynamic) -> (env, ty1)
562 | (_, Tobject) ->
563 if Partial.should_check_error (Env.get_mode env) 4006 then
564 error_assign_array_append env expr_pos ty1
565 else
566 (env, ty1)
567 | (_, Tunapplied_alias _) ->
568 Typing_defs.error_Tunapplied_alias_in_illegal_context ()
569 | ( _,
570 ( Tnonnull | Tdarray _ | Tvec_or_dict _ | Tvarray_or_darray _
571 | Toption _ | Tprim _ | Tvar _ | Tfun _ | Tclass _ | Ttuple _
572 | Tshape _ | Tunion _ | Tintersection _ | Tgeneric _ | Tnewtype _
573 | Tdependent _ | Taccess _ ) ) ->
574 error_assign_array_append env expr_pos ty1)
576 let widen_for_assign_array_get ~expr_pos index_expr env ty =
577 Typing_log.(
578 log_with_level env "typing" 1 (fun () ->
579 log_types
580 expr_pos
582 [Log_head ("widen_for_assign_array_get", [Log_type ("ty", ty)])]));
583 match deref ty with
584 (* dynamic is valid for assign array get *)
585 | (_, Tdynamic) -> (env, Some ty)
586 | (r, Tclass (((_, cn) as id), _, tyl))
587 when cn = SN.Collections.cVec
588 || cn = SN.Collections.cKeyset
589 || cn = SN.Collections.cVector
590 || cn = SN.Collections.cDict
591 || cn = SN.Collections.cMap ->
592 let (env, params) =
593 List.map_env env tyl (fun env _ty ->
594 Env.fresh_invariant_type_var env expr_pos)
596 let ty = mk (r, Tclass (id, Nonexact, params)) in
597 (env, Some ty)
598 | (r, Tvarray _) ->
599 let (env, tv) = Env.fresh_invariant_type_var env expr_pos in
600 (env, Some (mk (r, Tvarray tv)))
601 | (r, Tvarray_or_darray _) ->
602 let (env, tk) = Env.fresh_invariant_type_var env expr_pos in
603 let (env, tv) = Env.fresh_invariant_type_var env expr_pos in
604 (env, Some (mk (r, Tvarray_or_darray (tk, tv))))
605 | (r, Tdarray _) ->
606 let (env, tk) = Env.fresh_invariant_type_var env expr_pos in
607 let (env, tv) = Env.fresh_invariant_type_var env expr_pos in
608 (env, Some (mk (r, Tdarray (tk, tv))))
609 | (r, Ttuple tyl) ->
610 (* requires integer literal *)
611 begin
612 match index_expr with
613 (* Should freshen type variables *)
614 | (_, Int _) ->
615 let (env, params) =
616 List.map_env env tyl (fun env _ty ->
617 Env.fresh_invariant_type_var env expr_pos)
619 (env, Some (mk (r, Ttuple params)))
620 | _ -> (env, None)
622 | _ -> (env, None)
624 (* Used for typing an assignment e1[key] = e2
625 * where e1 has type ty1, key has type tkey and e2 has type ty2.
626 * Return the new array type
628 let assign_array_get ~array_pos ~expr_pos ur env ty1 key tkey ty2 =
629 let (env, ety1) =
630 Typing_solver.expand_type_and_narrow
631 ~description_of_expected:"an array or collection"
633 (widen_for_assign_array_get ~expr_pos key)
634 array_pos
636 Errors.unify_error
638 GenericRules.apply_rules env ety1 (fun env ety1 ->
639 let (r, ety1_) = deref ety1 in
640 let arity_error (_, name) =
641 Errors.array_get_arity expr_pos name (Reason.to_pos r)
643 let type_index env p ty_have ty_expect reason =
644 (* coerce if possible *)
645 match
646 Typing_coercion.try_coerce
648 ty_have
649 { et_type = ty_expect; et_enforced = true }
650 with
651 | Some e -> e
652 | None ->
653 (* if subtype of dynamic, allow it to be used *)
655 Typing_solver.is_sub_type env ty_have (MakeType.dynamic Reason.none)
656 then
658 (* fail with useful error *)
659 else
660 Typing_ops.sub_type
662 reason
664 ty_have
665 ty_expect
666 Errors.index_type_mismatch
668 let error = (env, ety1) in
669 match ety1_ with
670 | Tvarray tv ->
671 let tk = MakeType.int (Reason.Ridx (fst key, r)) in
672 let env = type_index env expr_pos tkey tk Reason.index_array in
673 let (env, tv') = Typing_union.union env tv ty2 in
674 (env, mk (r, Tvarray tv'))
675 | Tclass (((_, cn) as id), _, argl)
676 when String.equal cn SN.Collections.cVector ->
677 let tv =
678 match argl with
679 | [tv] -> tv
680 | _ ->
681 arity_error id;
682 err_witness env expr_pos
684 let tk = MakeType.int (Reason.Ridx_vector (fst key)) in
685 let env = type_index env expr_pos tkey tk (Reason.index_class cn) in
686 let env =
687 Typing_ops.sub_type expr_pos ur env ty2 tv Errors.unify_error
689 (env, ety1)
690 | Tclass (((_, cn) as id), e, argl)
691 when String.equal cn SN.Collections.cVec ->
692 let tv =
693 match argl with
694 | [tv] -> tv
695 | _ ->
696 arity_error id;
697 err_witness env expr_pos
699 let tk = MakeType.int (Reason.Ridx_vector (fst key)) in
700 let env = type_index env expr_pos tkey tk (Reason.index_class cn) in
701 let (env, tv') = Typing_union.union env tv ty2 in
702 (env, mk (r, Tclass (id, e, [tv'])))
703 | Tclass (((_, cn) as id), _, argl) when cn = SN.Collections.cMap ->
704 let env = check_arraykey_index_write env expr_pos ety1 tkey in
705 let (tk, tv) =
706 match argl with
707 | [tk; tv] -> (tk, tv)
708 | _ ->
709 arity_error id;
710 let any = err_witness env expr_pos in
711 (any, any)
713 let env = type_index env expr_pos tkey tk (Reason.index_class cn) in
714 let env =
715 Typing_ops.sub_type expr_pos ur env ty2 tv Errors.unify_error
717 (env, ety1)
718 | Tclass (((_, cn) as id), e, argl)
719 when String.equal cn SN.Collections.cDict ->
720 let env = check_arraykey_index_write env expr_pos ety1 tkey in
721 let (tk, tv) =
722 match argl with
723 | [tk; tv] -> (tk, tv)
724 | _ ->
725 arity_error id;
726 let any = err_witness env expr_pos in
727 (any, any)
729 let (env, tk') = Typing_union.union env tk tkey in
730 let (env, tv') = Typing_union.union env tv ty2 in
731 (env, mk (r, Tclass (id, e, [tk'; tv'])))
732 | Tclass ((_, cn), _, _) when String.equal cn SN.Collections.cKeyset ->
733 Errors.keyset_set expr_pos (Reason.to_pos r);
734 error
735 | Tclass ((_, cn), _, _)
736 when String.equal cn SN.Collections.cConstMap
737 || String.equal cn SN.Collections.cImmMap
738 || String.equal cn SN.Collections.cKeyedContainer
739 || String.equal cn SN.Collections.cAnyArray
740 || String.equal cn SN.Collections.cConstVector
741 || String.equal cn SN.Collections.cImmVector
742 || String.equal cn SN.Collections.cPair ->
743 Errors.const_mutation
744 expr_pos
745 (Reason.to_pos r)
746 (Typing_print.error env ety1);
747 error
748 | Tdarray (tk, tv) ->
749 let env = check_arraykey_index_write env expr_pos ety1 tkey in
750 let (env, tk') = Typing_union.union env tk tkey in
751 let (env, tv') = Typing_union.union env tv ty2 in
752 (env, mk (r, Tdarray (tk', tv')))
753 | Tvarray_or_darray (tk, tv) ->
754 let env = check_arraykey_index_write env expr_pos ety1 tkey in
755 let (env, tk') = Typing_union.union env tk tkey in
756 let (env, tv') = Typing_union.union env tv ty2 in
757 (env, mk (r, Tvarray_or_darray (tk', tv')))
758 | Tvec_or_dict (tk, tv) ->
759 let env = check_arraykey_index_write env expr_pos ety1 tkey in
760 let (env, tk') = Typing_union.union env tk tkey in
761 let (env, tv') = Typing_union.union env tv ty2 in
762 (env, mk (r, Tvec_or_dict (tk', tv')))
763 | Terr -> error
764 | Tdynamic -> (env, ety1)
765 | Tany _ -> (env, ety1)
766 | Tprim Tstring ->
767 let tk = MakeType.int (Reason.Ridx (fst key, r)) in
768 let tv = MakeType.string (Reason.Rwitness expr_pos) in
769 let env = type_index env expr_pos tkey tk Reason.index_array in
770 let env =
771 Typing_ops.sub_type expr_pos ur env ty2 tv Errors.unify_error
773 (env, ety1)
774 | Ttuple tyl ->
775 let fail reason =
776 Errors.typing_error (fst key) (Reason.string_of_ureason reason);
777 error
779 begin
780 match key with
781 | (_, Int n) ->
782 let idx = int_of_string_opt n in
783 (match Option.map ~f:(List.split_n tyl) idx with
784 | Some (tyl', _ :: tyl'') ->
785 (env, MakeType.tuple r (tyl' @ (ty2 :: tyl'')))
786 | _ -> fail Reason.index_tuple)
787 | _ -> fail Reason.URtuple_access
789 | Tshape (shape_kind, fdm) ->
790 begin
791 match TUtils.shape_field_name env key with
792 | None -> error
793 | Some field ->
794 let field = TShapeField.of_ast (fun p -> p) field in
795 let fdm' =
796 TShapeMap.add field { sft_optional = false; sft_ty = ty2 } fdm
798 (env, mk (r, Tshape (shape_kind, fdm')))
800 | Tobject ->
801 if Partial.should_check_error (Env.get_mode env) 4370 then (
802 Errors.array_access_write
803 expr_pos
804 (Reason.to_pos r)
805 (Typing_print.error env ety1);
806 error
807 ) else
808 (env, ety1)
809 | Tunapplied_alias _ ->
810 Typing_defs.error_Tunapplied_alias_in_illegal_context ()
811 | Toption _
812 | Tnonnull
813 | Tprim _
814 | Tunion _
815 | Tintersection _
816 | Tgeneric _
817 | Tnewtype _
818 | Tdependent _
819 | Tvar _
820 | Tfun _
821 | Tclass _
822 | Taccess _ ->
823 Errors.array_access_write
824 expr_pos
825 (Reason.to_pos r)
826 (Typing_print.error env ety1);
827 error)