Clean up extends checks to improve performance
[hiphop-php.git] / hphp / hack / src / typing / typing_toplevel.ml
blob121c92cd8f5a85f5b2106a7c437eedb28bb4b0c6
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 (* This module implements the typing.
12 * Given an Nast.program, it infers the type of all the local
13 * variables, and checks that all the types are correct (aka
14 * consistent) *)
15 open Hh_prelude
16 open Option.Monad_infix
17 open Common
18 open Aast
19 open Typing_defs
20 open Typing_env_types
21 open Typing_helpers
22 open Utils
23 module FunUtils = Decl_fun_utils
24 module Reason = Typing_reason
25 module Env = Typing_env
26 module MakeType = Typing_make_type
27 module Type = Typing_ops
28 module Phase = Typing_phase
29 module Subst = Decl_subst
30 module EnvFromDef = Typing_env_from_def
31 module Partial = Partial_provider
32 module TUtils = Typing_utils
33 module TCO = TypecheckerOptions
34 module Cls = Decl_provider.Class
35 module SN = Naming_special_names
37 (* The two following functions enable us to retrieve the function (or class)
38 header from the shared mem. Note that they only return a non None value if
39 global inference is on *)
40 let get_decl_function_header env function_id =
41 let is_global_inference_on = TCO.global_inference (Env.get_tcopt env) in
42 if is_global_inference_on then
43 match Decl_provider.get_fun (Env.get_ctx env) function_id with
44 | Some { fe_type; _ } ->
45 begin
46 match get_node fe_type with
47 | Tfun fun_type -> Some fun_type
48 | _ -> None
49 end
50 | _ -> None
51 else
52 None
54 and get_decl_method_header tcopt cls method_id ~is_static =
55 let is_global_inference_on = TCO.global_inference tcopt in
56 if is_global_inference_on then
57 match Cls.get_any_method ~is_static cls method_id with
58 | Some { ce_type = (lazy ty); _ } ->
59 begin
60 match get_node ty with
61 | Tfun fun_type -> Some fun_type
62 | _ -> None
63 end
64 | _ -> None
65 else
66 None
68 let enforce_param_not_disposable env param ty =
69 if has_accept_disposable_attribute param then
71 else
72 let p = param.param_pos in
73 match Typing_disposable.is_disposable_type env ty with
74 | Some class_name -> Errors.invalid_disposable_hint p (strip_ns class_name)
75 | None -> ()
77 (* In strict mode, we force you to give a type declaration on a parameter *)
78 (* But the type checker is nice: it makes a suggestion :-) *)
79 let check_param_has_hint env param ty is_code_error =
80 let env =
81 if is_code_error 4231 then
82 Typing.attributes_check_def
83 env
84 SN.AttributeKinds.parameter
85 param.param_user_attributes
86 else
87 env
89 match hint_of_type_hint param.param_type_hint with
90 | None when param.param_is_variadic && is_code_error 4033 ->
91 Errors.expecting_type_hint_variadic param.param_pos
92 | None when is_code_error 4032 -> Errors.expecting_type_hint param.param_pos
93 | Some _ when is_code_error 4010 ->
94 (* We do not permit hints to implement IDisposable or IAsyncDisposable *)
95 enforce_param_not_disposable env param ty
96 | _ -> ()
98 (* This function is used to determine the type of an argument.
99 * When we want to type-check the body of a function, we need to
100 * introduce the type of the arguments of the function in the environment
101 * Let's take an example, we want to check the code of foo:
103 * function foo(int $x): int {
104 * // CALL TO make_param_type on (int $x)
105 * // Now we know that the type of $x is int
107 * return $x; // in the environment $x is an int, the code is correct
110 * When we localize, we want to resolve to "static" or "$this" depending on
111 * the context. Even though we are passing in CIstatic, resolve_with_class_id
112 * is smart enough to know what to do. Why do this? Consider the following
114 * abstract class C {
115 * abstract const type T;
117 * private this::T $val;
119 * final public function __construct(this::T $x) {
120 * $this->val = $x;
123 * public static function create(this::T $x): this {
124 * return new static($x);
128 * class D extends C { const type T = int; }
130 * In __construct() we want to be able to assign $x to $this->val. The type of
131 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
132 * We can do this soundly because when we construct a new class such as,
133 * 'new D(0)' we can determine the late static bound type (D) and resolve
134 * 'this::T' to 'D::T' which is int.
136 * A similar line of reasoning is applied for the static method create.
138 let make_param_local_ty env decl_hint param =
139 let r = Reason.Rwitness param.param_pos in
140 let (env, ty) =
141 match decl_hint with
142 | None -> (env, mk (r, TUtils.tany env))
143 | Some ty ->
144 let { et_type = ty; _ } =
145 Typing_enforceability.compute_enforced_and_pessimize_ty
146 ~explicitly_untrusted:param.param_is_variadic
150 Phase.localize_no_subst env ~ignore_errors:false ty
152 let ty =
153 match get_node ty with
154 | t when param.param_is_variadic ->
155 (* when checking the body of a function with a variadic
156 * argument, "f(C ...$args)", $args is a varray<C> *)
157 let r = Reason.Rvar_param param.param_pos in
158 let arr_values = mk (r, t) in
159 MakeType.varray r arr_values
160 | _ -> ty
162 (env, ty)
164 let get_callable_variadicity ~partial_callback ~pos env variadicity_decl_ty =
165 function
166 | FVvariadicArg vparam ->
167 let (env, ty) = make_param_local_ty env variadicity_decl_ty vparam in
168 check_param_has_hint env vparam ty partial_callback;
169 let (env, t_variadic) = Typing.bind_param env (ty, vparam) in
170 (env, Aast.FVvariadicArg t_variadic)
171 | FVellipsis p ->
172 if Partial.should_check_error (Env.get_mode env) 4223 then
173 Errors.ellipsis_strict_mode ~require:`Type_and_param_name pos;
174 (env, Aast.FVellipsis p)
175 | FVnonVariadic -> (env, Aast.FVnonVariadic)
177 let merge_hint_with_decl_hint env type_hint decl_ty =
178 let contains_tvar decl_ty =
179 match decl_ty with
180 | None -> false
181 | Some decl_ty -> TUtils.contains_tvar_decl decl_ty
183 if contains_tvar decl_ty then
184 decl_ty
185 else
186 Option.map type_hint ~f:(Decl_hint.hint env.decl_env)
188 (* During the decl phase we can, for global inference, add "improved type hints".
189 That is we can say that some missing type hints are in fact global tyvars.
190 In that case to get the real type hint we must merge the type hint present
191 in the ast with the one we created during the decl phase. This function does
192 exactly this for the return type, the parameters and the variadic parameters.
194 let merge_decl_header_with_hints ~params ~ret ~variadic decl_header env =
195 let ret_decl_ty =
196 merge_hint_with_decl_hint
198 (hint_of_type_hint ret)
199 (Option.map
200 ~f:(fun { ft_ret = { et_type; _ }; _ } -> et_type)
201 decl_header)
203 let params_decl_ty =
204 match decl_header with
205 | None ->
206 List.map
207 ~f:(fun h ->
208 merge_hint_with_decl_hint
210 (hint_of_type_hint h.param_type_hint)
211 None)
212 params
213 | Some { ft_params; _ } ->
214 List.zip_exn params ft_params
215 |> List.map ~f:(fun (h, { fp_type = { et_type; _ }; _ }) ->
216 merge_hint_with_decl_hint
218 (hint_of_type_hint h.param_type_hint)
219 (Some et_type))
221 let variadicity_decl_ty =
222 match (decl_header, variadic) with
223 | ( Some { ft_arity = Fvariadic { fp_type = { et_type; _ }; _ }; _ },
224 FVvariadicArg fp ) ->
225 merge_hint_with_decl_hint
227 (hint_of_type_hint fp.param_type_hint)
228 (Some et_type)
229 | (_, FVvariadicArg fp) ->
230 merge_hint_with_decl_hint env (hint_of_type_hint fp.param_type_hint) None
231 | _ -> None
233 (ret_decl_ty, params_decl_ty, variadicity_decl_ty)
235 (* Checking this with List.exists will be a single op in the vast majority of cases (empty) *)
236 let get_ctx_vars ctxs =
237 Option.value_map
238 ~f:(fun (_, cs) ->
239 List.filter_map cs ~f:(function
240 | (_, Haccess ((_, Hvar n), _)) -> Some n
241 | _ -> None))
242 ~default:[]
243 ctxs
245 let function_dynamically_callable
246 env f params_decl_ty variadicity_decl_ty ret_locl_ty =
247 let env = { env with in_support_dynamic_type_method_check = true } in
248 let interface_check =
249 Typing_dynamic.sound_dynamic_interface_check
251 (variadicity_decl_ty :: params_decl_ty)
252 ret_locl_ty
254 let function_body_check () =
255 (* Here the body of the function is typechecked again to ensure it is safe
256 * to call it from a dynamic context (eg. under dyn..dyn->dyn assumptions).
257 * The code below must be kept in sync with with the fun_def checks.
259 let make_dynamic pos =
260 Typing_make_type.dynamic (Reason.Rsupport_dynamic_type pos)
262 let dynamic_return_ty = make_dynamic (get_pos ret_locl_ty) in
263 let dynamic_return_info =
264 Typing_env_return_info.
266 return_type = MakeType.unenforced dynamic_return_ty;
267 return_disposable = false;
268 return_explicit = true;
269 return_dynamically_callable = true;
272 let (env, param_tys) =
273 List.zip_exn f.f_params params_decl_ty
274 |> List.map_env env ~f:(fun env (param, hint) ->
275 let dyn_ty =
276 make_dynamic @@ Pos_or_decl.of_raw_pos param.param_pos
278 let ty =
279 match hint with
280 | Some ty when Typing_enforceability.is_enforceable env ty ->
281 Typing_make_type.intersection
282 (Reason.Rsupport_dynamic_type Pos_or_decl.none)
283 [ty; dyn_ty]
284 | _ -> dyn_ty
286 make_param_local_ty env (Some ty) param)
288 let params_need_immutable = get_ctx_vars f.f_ctxs in
289 let (env, _) =
290 (* In this pass, bind_param_and_check receives a pair where the lhs is
291 * either Tdynamic or TInstersection of the original type and TDynamic,
292 * but the fun_param is still referencing the source hint. We amend
293 * the source hint to keep in in sync before calling bind_param
294 * so the right enforcement is computed.
296 let bind_param_and_check env lty_and_param =
297 let (ty, param) = lty_and_param in
298 let name = param.param_name in
299 let (hi, hopt) = param.param_type_hint in
300 let hopt =
301 Option.map hopt ~f:(fun (p, h) ->
302 if Typing_utils.is_tintersection env ty then
303 (p, Hintersection [(p, h); (p, Hdynamic)])
304 else
305 (p, Hdynamic))
307 let param_type_hint = (hi, hopt) in
308 let param = (ty, { param with param_type_hint }) in
309 let immutable =
310 List.exists ~f:(String.equal name) params_need_immutable
312 let (env, fun_param) = Typing.bind_param ~immutable env param in
313 (env, fun_param)
315 List.map_env
317 (List.zip_exn param_tys f.f_params)
318 ~f:bind_param_and_check
321 let pos = fst f.f_name in
322 let (env, t_variadic) =
323 get_callable_variadicity
324 ~partial_callback:(Partial.should_check_error (Env.get_mode env))
325 ~pos
327 (Some (make_dynamic @@ Pos_or_decl.of_raw_pos pos))
328 f.f_variadic
330 let env =
331 set_tyvars_variance_in_callable env dynamic_return_ty param_tys t_variadic
333 let disable =
334 Naming_attributes.mem
335 SN.UserAttributes.uaDisableTypecheckerInternal
336 f.f_user_attributes
339 Errors.try_
340 (fun () ->
341 let (_ : env * Tast.stmt list) =
342 Typing.fun_ ~disable env dynamic_return_info pos f.f_body f.f_fun_kind
345 (fun error ->
346 Errors.function_is_not_dynamically_callable pos (snd f.f_name) error)
348 if not interface_check then function_body_check ()
350 let fun_def ctx fd :
351 (Tast.fun_def * Typing_inference_env.t_global_with_pos) option =
352 let f = fd.fd_fun in
353 Counters.count Counters.Category.Typecheck @@ fun () ->
354 Errors.run_with_span f.f_span @@ fun () ->
355 let env = EnvFromDef.fun_env ~origin:Decl_counters.TopLevel ctx fd in
356 with_timeout env f.f_name @@ fun env ->
357 (* reset the expression dependent display ids for each function body *)
358 Reason.expr_display_id_map := IMap.empty;
359 let pos = fst f.f_name in
360 let decl_header = get_decl_function_header env (snd f.f_name) in
361 let env = Env.open_tyvars env (fst f.f_name) in
362 let env = Env.set_env_callable_pos env pos in
363 let env = Env.set_env_pessimize env in
364 let env =
365 Typing.attributes_check_def env SN.AttributeKinds.fn f.f_user_attributes
367 let (env, file_attrs) = Typing.file_attributes env fd.fd_file_attributes in
368 let (env, cap_ty, unsafe_cap_ty) =
369 Typing.type_capability env f.f_ctxs f.f_unsafe_ctxs (fst f.f_name)
371 let env =
372 Env.set_module
374 (Naming_attributes_params.get_module_attribute f.f_user_attributes)
376 let env =
377 Env.set_internal
379 (Naming_attributes.mem SN.UserAttributes.uaInternal f.f_user_attributes)
381 Typing_type_wellformedness.fun_ env f;
382 let env =
383 Phase.localize_and_add_ast_generic_parameters_and_where_constraints
385 ~ignore_errors:false
386 f.f_tparams
387 f.f_where_constraints
389 let env = Env.set_fn_kind env f.f_fun_kind in
390 let (return_decl_ty, params_decl_ty, variadicity_decl_ty) =
391 merge_decl_header_with_hints
392 ~params:f.f_params
393 ~ret:f.f_ret
394 ~variadic:f.f_variadic
395 decl_header
398 let (env, return_ty) =
399 match return_decl_ty with
400 | None ->
401 (env, Typing_return.make_default_return ~is_method:false env f.f_name)
402 | Some ty ->
403 let localize env ty =
404 Phase.localize_no_subst env ~ignore_errors:false ty
406 Typing_return.make_return_type localize env ty
408 let ret_pos =
409 match snd f.f_ret with
410 | Some (ret_pos, _) -> ret_pos
411 | None -> fst f.f_name
413 let (env, return_ty) =
414 Typing_return.force_return_kind env ret_pos return_ty
416 let return =
417 Typing_return.make_info
418 ret_pos
419 f.f_fun_kind
420 f.f_user_attributes
422 ~is_explicit:(Option.is_some (hint_of_type_hint f.f_ret))
423 return_ty
424 return_decl_ty
426 let sound_dynamic_check_saved_env = env in
427 let (env, param_tys) =
428 List.zip_exn f.f_params params_decl_ty
429 |> List.map_env env ~f:(fun env (param, hint) ->
430 make_param_local_ty env hint param)
432 let partial_callback = Partial.should_check_error (Env.get_mode env) in
433 let check_has_hint p t = check_param_has_hint env p t partial_callback in
434 List.iter2_exn ~f:check_has_hint f.f_params param_tys;
435 let params_need_immutable = get_ctx_vars f.f_ctxs in
436 let (env, typed_params) =
437 let bind_param_and_check env param =
438 let name = (snd param).param_name in
439 let immutable =
440 List.exists ~f:(String.equal name) params_need_immutable
442 let (env, fun_param) = Typing.bind_param ~immutable env param in
443 (env, fun_param)
445 List.map_env env (List.zip_exn param_tys f.f_params) ~f:bind_param_and_check
447 let (env, t_variadic) =
448 get_callable_variadicity
449 ~pos
450 ~partial_callback
452 variadicity_decl_ty
453 f.f_variadic
455 let env =
456 set_tyvars_variance_in_callable env return_ty param_tys t_variadic
458 let local_tpenv = Env.get_tpenv env in
459 let disable =
460 Naming_attributes.mem
461 SN.UserAttributes.uaDisableTypecheckerInternal
462 f.f_user_attributes
464 let (env, _) =
465 Typing_coeffects.register_capabilities env cap_ty unsafe_cap_ty
467 Typing_memoize.check_function env f;
468 let (env, tb) = Typing.fun_ ~disable env return pos f.f_body f.f_fun_kind in
469 begin
470 match hint_of_type_hint f.f_ret with
471 | None ->
472 if partial_callback 4030 then Errors.expecting_return_type_hint pos
473 | Some _ -> ()
474 end;
475 let (env, tparams) = List.map_env env f.f_tparams ~f:Typing.type_param in
476 let (env, user_attributes) =
477 List.map_env env f.f_user_attributes ~f:Typing.user_attribute
479 let env = Typing_solver.close_tyvars_and_solve env in
480 let env = Typing_solver.solve_all_unsolved_tyvars env in
482 let check_support_dynamic_type =
483 Naming_attributes.mem
484 SN.UserAttributes.uaSupportDynamicType
485 f.f_user_attributes
488 TypecheckerOptions.enable_sound_dynamic
489 (Provider_context.get_tcopt (Env.get_ctx env))
490 && check_support_dynamic_type
491 then
492 function_dynamically_callable
493 sound_dynamic_check_saved_env
495 params_decl_ty
496 variadicity_decl_ty
497 return_ty;
499 let fun_ =
501 Aast.f_annotation = Env.save local_tpenv env;
502 Aast.f_readonly_this = f.f_readonly_this;
503 Aast.f_span = f.f_span;
504 Aast.f_readonly_ret = f.f_readonly_ret;
505 Aast.f_ret = (return_ty, hint_of_type_hint f.f_ret);
506 Aast.f_name = f.f_name;
507 Aast.f_tparams = tparams;
508 Aast.f_where_constraints = f.f_where_constraints;
509 Aast.f_variadic = t_variadic;
510 Aast.f_params = typed_params;
511 Aast.f_ctxs = f.f_ctxs;
512 Aast.f_unsafe_ctxs = f.f_unsafe_ctxs;
513 Aast.f_fun_kind = f.f_fun_kind;
514 Aast.f_user_attributes = user_attributes;
515 Aast.f_body = { Aast.fb_ast = tb };
516 Aast.f_external = f.f_external;
517 Aast.f_doc_comment = f.f_doc_comment;
520 let fundef =
522 Aast.fd_mode = fd.fd_mode;
523 Aast.fd_fun = fun_;
524 Aast.fd_file_attributes = file_attrs;
525 Aast.fd_namespace = fd.fd_namespace;
528 let (_env, global_inference_env) = Env.extract_global_inference_env env in
529 (fundef, (pos, global_inference_env))
531 let method_dynamically_callable
532 env cls m params_decl_ty variadicity_decl_ty ret_locl_ty =
533 let env = { env with in_support_dynamic_type_method_check = true } in
534 (* Add `dynamic` lower and upper bound to any type parameters that are not marked <<__NoRequireDynamic>> *)
535 let env_with_require_dynamic =
536 Typing_dynamic.add_require_dynamic_bounds env cls
538 let interface_check =
539 Typing_dynamic.sound_dynamic_interface_check
540 env_with_require_dynamic
541 (variadicity_decl_ty :: params_decl_ty)
542 ret_locl_ty
544 let method_body_check () =
545 (* Here the body of the method is typechecked again to ensure it is safe
546 * to call it from a dynamic context (eg. under dyn..dyn->dyn assumptions).
547 * The code below must be kept in sync with with the method_def checks.
549 let make_dynamic pos =
550 Typing_make_type.dynamic (Reason.Rsupport_dynamic_type pos)
552 let dynamic_return_ty = make_dynamic (get_pos ret_locl_ty) in
553 let dynamic_return_info =
554 Typing_env_return_info.
556 return_type = MakeType.unenforced dynamic_return_ty;
557 return_disposable = false;
558 return_explicit = true;
559 return_dynamically_callable = true;
562 let (env, param_tys) =
563 List.zip_exn m.m_params params_decl_ty
564 |> List.map_env env ~f:(fun env (param, hint) ->
565 let dyn_ty =
566 make_dynamic @@ Pos_or_decl.of_raw_pos param.param_pos
568 let ty =
569 match hint with
570 | Some ty when Typing_enforceability.is_enforceable env ty ->
571 Typing_make_type.intersection
572 (Reason.Rsupport_dynamic_type Pos_or_decl.none)
573 [ty; dyn_ty]
574 | _ -> dyn_ty
576 make_param_local_ty env (Some ty) param)
578 let params_need_immutable = get_ctx_vars m.m_ctxs in
579 let (env, _) =
580 (* In this pass, bind_param_and_check receives a pair where the lhs is
581 * either Tdynamic or TInstersection of the original type and TDynamic,
582 * but the fun_param is still referencing the source hint. We amend
583 * the source hint to keep in in sync before calling bind_param
584 * so the right enforcement is computed.
586 let bind_param_and_check env lty_and_param =
587 let (ty, param) = lty_and_param in
588 let name = param.param_name in
589 let (hi, hopt) = param.param_type_hint in
590 let hopt =
591 Option.map hopt ~f:(fun (p, h) ->
592 if Typing_utils.is_tintersection env ty then
593 (p, Hintersection [(p, h); (p, Hdynamic)])
594 else
595 (p, Hdynamic))
597 let param_type_hint = (hi, hopt) in
598 let param = (ty, { param with param_type_hint }) in
599 let immutable =
600 List.exists ~f:(String.equal name) params_need_immutable
602 let (env, fun_param) = Typing.bind_param ~immutable env param in
603 (env, fun_param)
605 List.map_env
607 (List.zip_exn param_tys m.m_params)
608 ~f:bind_param_and_check
611 let pos = fst m.m_name in
612 let (env, t_variadic) =
613 get_callable_variadicity
614 ~partial_callback:(Partial.should_check_error (Env.get_mode env))
615 ~pos
617 (Some (make_dynamic @@ Pos_or_decl.of_raw_pos pos))
618 m.m_variadic
620 let env =
621 set_tyvars_variance_in_callable env dynamic_return_ty param_tys t_variadic
624 let env =
625 if Cls.get_support_dynamic_type cls then
626 let this_ty =
627 Typing_make_type.intersection
628 (Reason.Rsupport_dynamic_type Pos_or_decl.none)
629 [Env.get_local env this; make_dynamic Pos_or_decl.none]
631 Env.set_local env this this_ty Pos.none
632 else
636 let disable =
637 Naming_attributes.mem
638 SN.UserAttributes.uaDisableTypecheckerInternal
639 m.m_user_attributes
642 Errors.try_
643 (fun () ->
644 let (_ : env * Tast.stmt list) =
645 Typing.fun_
646 ~abstract:m.m_abstract
647 ~disable
649 dynamic_return_info
651 m.m_body
652 m.m_fun_kind
655 (fun error ->
656 Errors.method_is_not_dynamically_callable
658 (snd m.m_name)
659 (Cls.name cls)
660 (Naming_attributes.mem
661 SN.UserAttributes.uaSupportDynamicType
662 m.m_user_attributes
663 || Cls.get_support_dynamic_type cls)
664 None
665 (Some error))
667 if not interface_check then method_body_check ()
669 let method_def env cls m =
670 Errors.run_with_span m.m_span @@ fun () ->
671 with_timeout env m.m_name @@ fun env ->
672 FunUtils.check_params m.m_params;
673 let initial_env = env in
674 (* reset the expression dependent display ids for each method body *)
675 Reason.expr_display_id_map := IMap.empty;
676 let decl_header =
677 get_decl_method_header
678 (Env.get_tcopt env)
680 (snd m.m_name)
681 ~is_static:m.m_static
683 let pos = fst m.m_name in
684 let env = Env.open_tyvars env (fst m.m_name) in
685 let env = Env.reinitialize_locals env in
686 let env = Env.set_env_callable_pos env pos in
687 let env =
688 Typing.attributes_check_def env SN.AttributeKinds.mthd m.m_user_attributes
690 let (env, cap_ty, unsafe_cap_ty) =
691 Typing.type_capability env m.m_ctxs m.m_unsafe_ctxs (fst m.m_name)
693 let (env, _) =
694 Typing_coeffects.register_capabilities env cap_ty unsafe_cap_ty
696 let is_ctor = String.equal (snd m.m_name) SN.Members.__construct in
697 let env = Env.set_fun_is_constructor env is_ctor in
698 let env =
699 Phase.localize_and_add_ast_generic_parameters_and_where_constraints
701 ~ignore_errors:false
702 m.m_tparams
703 m.m_where_constraints
705 let env =
706 match Env.get_self_ty env with
707 | Some ty when not (Env.is_static env) ->
708 Env.set_local env this (MakeType.this (get_reason ty)) Pos.none
709 | _ -> env
711 let env =
712 match Env.get_self_class env with
713 | None -> env
714 | Some c ->
715 (* Mark $this as a using variable if it has a disposable type *)
716 if Cls.is_disposable c then
717 Env.set_using_var env this
718 else
721 let env = Env.clear_params env in
722 let (ret_decl_ty, params_decl_ty, variadicity_decl_ty) =
723 merge_decl_header_with_hints
724 ~params:m.m_params
725 ~ret:m.m_ret
726 ~variadic:m.m_variadic
727 decl_header
730 let env = Env.set_fn_kind env m.m_fun_kind in
731 let (env, locl_ty) =
732 match ret_decl_ty with
733 | None ->
734 (env, Typing_return.make_default_return ~is_method:true env m.m_name)
735 | Some ret ->
736 (* If a 'this' type appears it needs to be compatible with the
737 * late static type
739 let ety_env =
740 empty_expand_env_with_on_error
741 (Env.invalid_type_hint_assert_primary_pos_in_current_decl env)
743 Typing_return.make_return_type (Phase.localize ~ety_env) env ret
745 let ret_pos =
746 match snd m.m_ret with
747 | Some (ret_pos, _) -> ret_pos
748 | None -> fst m.m_name
750 let (env, locl_ty) = Typing_return.force_return_kind env ret_pos locl_ty in
751 let return =
752 Typing_return.make_info
753 ret_pos
754 m.m_fun_kind
755 m.m_user_attributes
757 ~is_explicit:(Option.is_some (hint_of_type_hint m.m_ret))
758 locl_ty
759 ret_decl_ty
761 let sound_dynamic_check_saved_env = env in
762 let (env, param_tys) =
763 List.zip_exn m.m_params params_decl_ty
764 |> List.map_env env ~f:(fun env (param, hint) ->
765 make_param_local_ty env hint param)
767 let partial_callback = Partial.should_check_error (Env.get_mode env) in
768 let param_fn p t = check_param_has_hint env p t partial_callback in
769 List.iter2_exn ~f:param_fn m.m_params param_tys;
770 Typing_memoize.check_method env m;
771 let params_need_immutable = get_ctx_vars m.m_ctxs in
772 let (env, typed_params) =
773 let bind_param_and_check env param =
774 let name = (snd param).param_name in
775 let immutable =
776 List.exists ~f:(String.equal name) params_need_immutable
778 let (env, fun_param) = Typing.bind_param ~immutable env param in
779 (env, fun_param)
781 List.map_env env (List.zip_exn param_tys m.m_params) ~f:bind_param_and_check
783 let (env, t_variadic) =
784 get_callable_variadicity
785 ~partial_callback
786 ~pos
788 variadicity_decl_ty
789 m.m_variadic
791 let env = set_tyvars_variance_in_callable env locl_ty param_tys t_variadic in
792 let nb = m.m_body in
793 let local_tpenv = Env.get_tpenv env in
794 let disable =
795 Naming_attributes.mem
796 SN.UserAttributes.uaDisableTypecheckerInternal
797 m.m_user_attributes
799 let (env, tb) =
800 Typing.fun_ ~abstract:m.m_abstract ~disable env return pos nb m.m_fun_kind
802 let type_hint' =
803 match hint_of_type_hint m.m_ret with
804 | None when String.equal (snd m.m_name) SN.Members.__construct ->
805 Some (pos, Hprim Tvoid)
806 | None ->
807 if partial_callback 4030 then Errors.expecting_return_type_hint pos;
808 None
809 | Some _ -> hint_of_type_hint m.m_ret
811 let m = { m with m_ret = (fst m.m_ret, type_hint') } in
812 let (env, tparams) = List.map_env env m.m_tparams ~f:Typing.type_param in
813 let (env, user_attributes) =
814 List.map_env env m.m_user_attributes ~f:Typing.user_attribute
816 let env = Typing_solver.close_tyvars_and_solve env in
817 let env = Typing_solver.solve_all_unsolved_tyvars env in
819 (* if the enclosing class implements dynamic, or the method is annotated with
820 * <<__SupportDynamicType>>, check that the method is dynamically callable *)
821 let check_support_dynamic_type =
822 (not env.inside_constructor)
823 && (Cls.get_support_dynamic_type cls
824 && not (Aast.equal_visibility m.m_visibility Private)
825 || Naming_attributes.mem
826 SN.UserAttributes.uaSupportDynamicType
827 m.m_user_attributes)
830 TypecheckerOptions.enable_sound_dynamic
831 (Provider_context.get_tcopt (Env.get_ctx env))
832 && check_support_dynamic_type
833 then
834 method_dynamically_callable
835 sound_dynamic_check_saved_env
838 params_decl_ty
839 variadicity_decl_ty
840 locl_ty;
841 let method_def =
843 Aast.m_annotation = Env.save local_tpenv env;
844 Aast.m_span = m.m_span;
845 Aast.m_final = m.m_final;
846 Aast.m_static = m.m_static;
847 Aast.m_abstract = m.m_abstract;
848 Aast.m_visibility = m.m_visibility;
849 Aast.m_readonly_this = m.m_readonly_this;
850 Aast.m_name = m.m_name;
851 Aast.m_tparams = tparams;
852 Aast.m_where_constraints = m.m_where_constraints;
853 Aast.m_variadic = t_variadic;
854 Aast.m_params = typed_params;
855 Aast.m_ctxs = m.m_ctxs;
856 Aast.m_unsafe_ctxs = m.m_unsafe_ctxs;
857 Aast.m_fun_kind = m.m_fun_kind;
858 Aast.m_user_attributes = user_attributes;
859 Aast.m_readonly_ret = m.m_readonly_ret;
860 Aast.m_ret = (locl_ty, hint_of_type_hint m.m_ret);
861 Aast.m_body = { Aast.fb_ast = tb };
862 Aast.m_external = m.m_external;
863 Aast.m_doc_comment = m.m_doc_comment;
866 let (env, global_inference_env) = Env.extract_global_inference_env env in
867 let _env = Env.log_env_change "method_def" initial_env env in
868 (method_def, (pos, global_inference_env))
870 (** Checks that extending this parent is legal - e.g. it is not final and not const. *)
871 let check_parent env class_def class_type =
872 match Env.get_parent_class env with
873 | Some parent_type ->
874 let position = fst class_def.c_name in
875 if Cls.const class_type && not (Cls.const parent_type) then
876 Errors.self_const_parent_not position;
877 if Cls.final parent_type then
878 Errors.extend_final position (Cls.pos parent_type) (Cls.name parent_type)
879 | None -> ()
881 let sealed_subtype ctx (c : Nast.class_) ~is_enum =
882 let parent_name = snd c.c_name in
883 let is_sealed (attr : Nast.user_attribute) =
884 String.equal (snd attr.ua_name) SN.UserAttributes.uaSealed
886 match List.find c.c_user_attributes ~f:is_sealed with
887 | None -> ()
888 | Some sealed_attr ->
889 let iter_item ((_, pos, expr_) : Nast.expr) =
890 match expr_ with
891 | Class_const ((_, _, cid), _) ->
892 let klass_name = Nast.class_id_to_str cid in
893 let klass = Decl_provider.get_class ctx klass_name in
894 (match klass with
895 | None -> ()
896 | Some decl ->
897 let includes_ancestor =
898 if is_enum then
899 match Cls.enum_type decl with
900 | None -> true
901 | Some enum_ty ->
902 let check x =
903 match get_node x with
904 | Tapply ((_, name), _) -> String.equal name parent_name
905 | _ -> true
907 List.exists enum_ty.te_includes ~f:check
908 else
909 Cls.has_ancestor decl parent_name
911 if not includes_ancestor then
912 let parent_pos = pos in
913 let child_pos = Cls.pos decl in
914 let child_name = Cls.name decl in
915 let (child_kind, verb) =
916 match Cls.kind decl with
917 | Ast_defs.Cclass _ -> ("Class", "extend")
918 | Ast_defs.Cinterface -> ("Interface", "implement")
919 | Ast_defs.Ctrait -> ("Trait", "use")
920 | Ast_defs.Cenum -> ("Enum", "use")
921 | Ast_defs.Cenum_class _ -> ("Enum Class", "extend")
923 Errors.sealed_not_subtype
924 verb
925 parent_pos
926 child_pos
927 parent_name
928 child_name
929 child_kind)
930 (* unit below is fine because error cases are handled as Parsing[1002] *)
931 | _ -> ()
933 List.iter sealed_attr.ua_params ~f:iter_item
935 let check_parent_sealed (child_pos, child_type) parent_type =
936 match Cls.sealed_whitelist parent_type with
937 | None -> ()
938 | Some whitelist ->
939 let parent_pos = Cls.pos parent_type in
940 let parent_name = Cls.name parent_type in
941 let child_name = Cls.name child_type in
942 let check kind action =
943 if not (SSet.mem child_name whitelist) then
944 Errors.extend_sealed child_pos parent_pos parent_name kind action
946 begin
947 match (Cls.kind parent_type, Cls.kind child_type) with
948 | (Ast_defs.Cinterface, Ast_defs.Cinterface) -> check "interface" "extend"
949 | (Ast_defs.Cinterface, _) -> check "interface" "implement"
950 | (Ast_defs.Ctrait, _) -> check "trait" "use"
951 | (Ast_defs.Cclass _, _) -> check "class" "extend"
952 | (Ast_defs.Cenum_class _, _) -> check "enum class" "extend"
953 | (Ast_defs.Cenum, _) -> check "enum" "use"
956 let check_parents_sealed env child_def child_type =
957 let parents =
958 match child_def.c_enum with
959 | Some enum -> enum.e_includes
960 | None -> child_def.c_extends
962 let parents = parents @ child_def.c_implements @ child_def.c_uses in
963 List.iter parents ~f:(function
964 | (_, Happly ((_, name), _)) ->
965 begin
966 match Env.get_class_dep env name with
967 | Some parent_type ->
968 check_parent_sealed (fst child_def.c_name, child_type) parent_type
969 | None -> ()
971 | _ -> ())
973 (* Reject multiple instantiations of the same generic interface
974 * in extends and implements clauses.
975 * e.g. disallow class C implements I<string>, I<int>
977 * O(n^2) but we don't expect number of instantiated interfaces to be large
979 let rec check_implements_or_extends_unique impl =
980 match impl with
981 | [] -> ()
982 | (hint, ty) :: rest ->
983 (match get_node ty with
984 | Tapply ((_, name), _ :: _) ->
985 let (pos_list, rest) =
986 List.partition_map rest ~f:(fun (h, ty) ->
987 match get_node ty with
988 | Tapply ((pos', name'), _) when String.equal name name' ->
989 First pos'
990 | _ -> Second (h, ty))
992 if not (List.is_empty pos_list) then
993 Errors.duplicate_interface (fst hint) name pos_list;
994 check_implements_or_extends_unique rest
995 | _ -> check_implements_or_extends_unique rest)
997 let check_constructor_dep env deps =
998 List.iter deps ~f:(fun ((p, _dep_hint), dep) ->
999 match get_node dep with
1000 | Tapply ((_, class_name), _) ->
1001 Env.make_depend_on_constructor env class_name
1002 | Tgeneric _ ->
1003 Errors.expected_class ~suffix:" or interface but got a generic" p
1004 | _ -> Errors.expected_class ~suffix:" or interface" p)
1006 (** For const classes, check that members from traits are all constant. *)
1007 let check_non_const_trait_members pos env use_list =
1008 let (_, trait, _) = Decl_utils.unwrap_class_hint use_list in
1009 match Env.get_class env trait with
1010 | Some c when Ast_defs.is_c_trait (Cls.kind c) ->
1011 List.iter (Cls.props c) ~f:(fun (x, ce) ->
1012 if not (get_ce_const ce) then Errors.trait_prop_const_class pos x)
1013 | _ -> ()
1015 let check_consistent_enum_inclusion
1016 included_cls ((dest_cls_pos, dest_cls) : Pos.t * Cls.t) =
1017 let included_kind = Cls.kind included_cls in
1018 let dest_kind = Cls.kind dest_cls in
1019 match (Cls.enum_type included_cls, Cls.enum_type dest_cls) with
1020 | (Some included_e, Some dest_e) ->
1021 (* ensure that the base types are identical *)
1022 if not (Typing_defs.equal_decl_ty included_e.te_base dest_e.te_base) then
1023 Errors.incompatible_enum_inclusion_base
1024 dest_cls_pos
1025 (Cls.name dest_cls)
1026 (Cls.name included_cls);
1027 (* ensure that the visibility constraint are compatible *)
1028 (match (included_e.te_constraint, dest_e.te_constraint) with
1029 | (None, Some _) ->
1030 Errors.incompatible_enum_inclusion_constraint
1031 dest_cls_pos
1032 (Cls.name dest_cls)
1033 (Cls.name included_cls)
1034 | (_, _) -> ());
1035 (* ensure normal enums can't include enum classes *)
1037 Ast_defs.is_c_enum_class included_kind
1038 && not (Ast_defs.is_c_enum_class dest_kind)
1039 then
1040 Errors.wrong_extend_kind
1041 ~parent_pos:(Cls.pos included_cls)
1042 ~parent_kind:included_kind
1043 ~parent_name:(Cls.name included_cls)
1044 ~child_pos:dest_cls_pos
1045 ~child_kind:dest_kind
1046 ~child_name:(Cls.name dest_cls)
1047 | (None, _) ->
1048 Errors.enum_inclusion_not_enum
1049 dest_cls_pos
1050 (Cls.name dest_cls)
1051 (Cls.name included_cls)
1052 | (_, _) -> ()
1054 let is_enum_or_enum_class k = Ast_defs.is_c_enum k || Ast_defs.is_c_enum_class k
1056 let check_enum_includes env cls =
1057 let is_abstract = function
1058 | CCAbstract has_default -> not has_default
1059 | CCConcrete -> false
1061 (* checks that there are no duplicated enum-constants when folded-decls are enabled *)
1062 if is_enum_or_enum_class cls.c_kind then (
1063 let (dest_class_pos, dest_class_name) = cls.c_name in
1064 let enum_constant_map = ref SMap.empty in
1065 (* prepopulate the map with the constants declared in cls *)
1066 List.iter cls.c_consts ~f:(fun cc ->
1067 enum_constant_map :=
1068 SMap.add
1069 (snd cc.cc_id)
1070 (fst cc.cc_id, dest_class_name)
1071 !enum_constant_map);
1072 (* for all included enums *)
1073 let included_enums =
1074 Aast.enum_includes_map cls.c_enum ~f:(fun ce ->
1075 List.filter_map ce.e_includes ~f:(fun ie ->
1076 match snd ie with
1077 | Happly (sid, _) ->
1078 (match Env.get_class env (snd sid) with
1079 | None -> None
1080 | Some ie_cls -> Some (fst ie, ie_cls))
1081 | _ -> None))
1083 List.iter included_enums ~f:(fun (ie_pos, ie_cls) ->
1084 let src_class_name = Cls.name ie_cls in
1085 (* 1. Check for consistency *)
1086 (match Env.get_class env dest_class_name with
1087 | None -> ()
1088 | Some cls -> check_consistent_enum_inclusion ie_cls (ie_pos, cls));
1089 (* 2. Check for duplicates *)
1090 List.iter (Cls.consts ie_cls) ~f:(fun (const_name, class_const) ->
1091 (if String.equal const_name "class" then
1093 (* TODO: Check with @fzn *)
1094 else if is_abstract class_const.cc_abstract then
1096 else if SMap.mem const_name !enum_constant_map then
1097 (* distinguish between multiple inherit and redeclare *)
1098 let (origin_const_pos, origin_class_name) =
1099 SMap.find const_name !enum_constant_map
1101 if String.equal origin_class_name dest_class_name then
1102 (* redeclare *)
1103 Errors.redeclaring_classish_const
1104 dest_class_pos
1105 dest_class_name
1106 origin_const_pos
1107 src_class_name
1108 const_name
1109 else if String.( <> ) origin_class_name class_const.cc_origin then
1110 (* check for diamond inclusion, if not raise an error about multiple inherit *)
1111 Errors.reinheriting_classish_const
1112 dest_class_pos
1113 dest_class_name
1114 ie_pos
1115 src_class_name
1116 origin_class_name
1117 const_name);
1118 enum_constant_map :=
1119 SMap.add
1120 const_name
1121 (dest_class_pos, class_const.cc_origin)
1122 !enum_constant_map))
1125 let shallow_decl_enabled (ctx : Provider_context.t) : bool =
1126 TypecheckerOptions.shallow_class_decl (Provider_context.get_tcopt ctx)
1128 let skip_hierarchy_checks (ctx : Provider_context.t) : bool =
1129 TypecheckerOptions.skip_hierarchy_checks (Provider_context.get_tcopt ctx)
1131 let class_type_param env ct =
1132 let (env, tparam_list) = List.map_env env ct ~f:Typing.type_param in
1133 (env, tparam_list)
1135 (** Checks that a dynamic element is also dynamic in the parents. *)
1136 let check_dynamic_class_element get_static_elt element_name dyn_pos ~elt_type =
1137 (* The non-static properties that we get passed do not start with '$', but the
1138 static properties we want to look up do, so add it. *)
1139 let id =
1140 match elt_type with
1141 | `Method -> element_name
1142 | `Property -> "$" ^ element_name
1144 match get_static_elt id with
1145 | None -> ()
1146 | Some static_element ->
1147 let (lazy ty) = static_element.ce_type in
1148 Errors.static_redeclared_as_dynamic
1149 dyn_pos
1150 (get_pos ty)
1151 element_name
1152 ~elt_type
1154 (** Checks that a static element is also static in the parents. *)
1155 let check_static_class_element get_dyn_elt element_name static_pos ~elt_type =
1156 (* The static properties that we get passed in start with '$', but the
1157 non-static properties we're matching against don't, so we need to detect
1158 that and remove it if present. *)
1159 let element_name = String_utils.lstrip element_name "$" in
1160 match get_dyn_elt element_name with
1161 | None -> ()
1162 | Some dyn_element ->
1163 let (lazy ty) = dyn_element.ce_type in
1164 Errors.dynamic_redeclared_as_static
1165 static_pos
1166 (get_pos ty)
1167 element_name
1168 ~elt_type
1170 (** Error if there are abstract methods that this class is supposed to provide
1171 implementation for. *)
1172 let check_extend_abstract_meth ~is_final p seq =
1173 List.iter seq ~f:(fun (x, ce) ->
1174 match ce.ce_type with
1175 | (lazy ty) when get_ce_abstract ce && is_fun ty ->
1176 Errors.implement_abstract ~is_final p (get_pos ty) "method" x
1177 | _ -> ())
1179 let check_extend_abstract_prop ~is_final p seq =
1180 List.iter seq ~f:(fun (x, ce) ->
1181 if get_ce_abstract ce then
1182 let ce_pos = Lazy.force ce.ce_type |> get_pos in
1183 Errors.implement_abstract ~is_final p ce_pos "property" x)
1185 (* Type constants must be bound to a concrete type for non-abstract classes.
1187 let check_extend_abstract_typeconst ~is_final p seq =
1188 List.iter seq ~f:(fun (x, tc) ->
1189 match tc.ttc_kind with
1190 | TCAbstract _ ->
1191 Errors.implement_abstract
1192 ~is_final
1194 (fst tc.ttc_name)
1195 "type constant"
1197 | _ -> ())
1199 let check_extend_abstract_const ~is_final p seq =
1200 List.iter seq ~f:(fun (x, cc) ->
1201 match cc.cc_abstract with
1202 | CCAbstract _ when not cc.cc_synthesized ->
1203 let cc_pos = get_pos cc.cc_type in
1204 Errors.implement_abstract ~is_final p cc_pos "constant" x
1205 | _ -> ())
1207 (** Error if there are abstract members that this class is supposed to provide
1208 implementation for. *)
1209 let check_extend_abstract (pc, tc) =
1210 let is_concrete =
1211 let open Ast_defs in
1212 match Cls.kind tc with
1213 | Cclass k
1214 | Cenum_class k ->
1215 is_concrete k
1216 | Cinterface
1217 | Ctrait
1218 | Cenum ->
1219 false
1221 let is_final = Cls.final tc in
1222 if (is_concrete || is_final) && Cls.members_fully_known tc then (
1223 let constructor_as_list cstr =
1224 fst cstr
1225 >>| (fun cstr -> (SN.Members.__construct, cstr))
1226 |> Option.to_list
1228 check_extend_abstract_meth ~is_final pc (Cls.methods tc);
1229 check_extend_abstract_meth
1230 ~is_final
1232 (Cls.construct tc |> constructor_as_list);
1233 check_extend_abstract_meth ~is_final pc (Cls.smethods tc);
1234 check_extend_abstract_prop ~is_final pc (Cls.sprops tc);
1235 check_extend_abstract_const ~is_final pc (Cls.consts tc);
1236 check_extend_abstract_typeconst ~is_final pc (Cls.typeconsts tc)
1239 exception Found of Pos_or_decl.t
1241 let contains_generic : Typing_defs.decl_ty -> Pos_or_decl.t option =
1242 fun ty ->
1243 let visitor =
1244 object
1245 inherit [_] Type_visitor.decl_type_visitor as super
1247 method! on_type env ty =
1248 match get_node ty with
1249 | Tgeneric _ -> raise (Found (get_pos ty))
1250 | _ -> super#on_type env ty
1254 visitor#on_type () ty;
1255 None
1256 with
1257 | Found p -> Some p
1259 (** Check whether the type of a static property (class variable) contains
1260 any generic type parameters. Outside of traits, this is illegal as static
1261 * properties are shared across all generic instantiations. *)
1262 let check_no_generic_static_property env tc =
1263 if Ast_defs.is_c_trait (Cls.kind tc) then
1265 else
1266 Cls.sprops tc
1267 |> List.iter ~f:(fun (_prop_name, prop) ->
1268 let (lazy ty) = prop.ce_type in
1269 let var_type_pos = get_pos ty in
1270 let class_pos = Cls.pos tc in
1271 match contains_generic ty with
1272 | None -> ()
1273 | Some generic_pos ->
1274 Option.iter
1275 (* If the static property is inherited from another trait, the position may be
1276 * in a different file. *)
1277 (Env.fill_in_pos_filename_if_in_current_decl env generic_pos)
1278 ~f:(fun generic_pos ->
1279 Errors.static_property_type_generic_param
1280 ~class_pos
1281 ~var_type_pos
1282 ~generic_pos))
1284 let get_decl_prop_ty env cls ~is_static prop_id =
1285 let is_global_inference_on = TCO.global_inference (Env.get_tcopt env) in
1286 if is_global_inference_on then
1287 let prop_opt =
1288 if is_static then
1289 (* this is very ad-hoc, but this is how we do it in the decl-heap *)
1290 Cls.get_sprop cls ("$" ^ prop_id)
1291 else
1292 Cls.get_prop cls prop_id
1294 match prop_opt with
1295 | None -> failwith "error: could not find property in decl heap"
1296 | Some { ce_type; _ } -> Some (Lazy.force ce_type)
1297 else
1298 None
1300 let typeconst_def
1304 c_tconst_name = (pos, _) as id;
1305 c_tconst_kind;
1306 c_tconst_user_attributes;
1307 c_tconst_span;
1308 c_tconst_doc_comment;
1309 c_tconst_is_ctx;
1311 if is_enum_or_enum_class cls.c_kind then
1312 Errors.cannot_declare_constant `enum pos cls.c_name;
1314 let name = snd cls.c_name ^ "::" ^ snd id in
1315 (* Check constraints and report cycles through the definition *)
1316 let env =
1317 match c_tconst_kind with
1318 | TCAbstract
1319 { c_atc_as_constraint; c_atc_super_constraint; c_atc_default = Some ty }
1321 let (env, ty) =
1322 Phase.localize_hint_no_subst
1323 ~ignore_errors:false
1324 ~report_cycle:(pos, name)
1328 let env =
1329 match c_atc_as_constraint with
1330 | Some as_ ->
1331 let (env, as_) =
1332 Phase.localize_hint_no_subst ~ignore_errors:false env as_
1334 Type.sub_type
1336 Reason.URtypeconst_cstr
1340 Errors.unify_error
1341 | None -> env
1343 let env =
1344 match c_atc_super_constraint with
1345 | Some super ->
1346 let (env, super) =
1347 Phase.localize_hint_no_subst ~ignore_errors:false env super
1349 Type.sub_type
1351 Reason.URtypeconst_cstr
1353 super
1355 Errors.unify_error
1356 | None -> env
1359 | TCPartiallyAbstract { c_patc_constraint = cstr; c_patc_type = ty } ->
1360 let (env, cstr) =
1361 Phase.localize_hint_no_subst ~ignore_errors:false env cstr
1363 let (env, ty) =
1364 Phase.localize_hint_no_subst
1365 ~ignore_errors:false
1366 ~report_cycle:(pos, name)
1370 Type.sub_type pos Reason.URtypeconst_cstr env ty cstr Errors.unify_error
1371 | TCConcrete { c_tc_type = ty } ->
1372 let (env, _ty) =
1373 Phase.localize_hint_no_subst
1374 ~ignore_errors:false
1375 ~report_cycle:(pos, name)
1380 | _ -> env
1383 (* TODO(T88552052): should this check be happening for defaults
1384 * Does this belong here at all? *)
1385 let env =
1386 match c_tconst_kind with
1387 | TCConcrete { c_tc_type = (pos, Hshape { nsi_field_map; _ }) }
1388 | TCPartiallyAbstract
1389 { c_patc_type = (pos, Hshape { nsi_field_map; _ }); _ }
1390 | TCAbstract { c_atc_default = Some (pos, Hshape { nsi_field_map; _ }); _ }
1392 let get_name sfi = sfi.sfi_name in
1393 Typing.check_shape_keys_validity
1396 (List.map ~f:get_name nsi_field_map)
1397 | _ -> env
1400 let env =
1401 Typing.attributes_check_def
1403 SN.AttributeKinds.typeconst
1404 c_tconst_user_attributes
1406 let (env, user_attributes) =
1407 List.map_env env c_tconst_user_attributes ~f:Typing.user_attribute
1409 ( env,
1411 Aast.c_tconst_name = id;
1412 Aast.c_tconst_kind;
1413 Aast.c_tconst_user_attributes = user_attributes;
1414 Aast.c_tconst_span;
1415 Aast.c_tconst_doc_comment;
1416 Aast.c_tconst_is_ctx;
1419 (* This should agree with the set of expressions whose type can be inferred in
1420 * Decl_utils.infer_const
1422 let is_literal_expr (_, _, e) =
1423 match e with
1424 | String _
1425 | True
1426 | False
1427 | Int _
1428 | Float _
1429 | Null ->
1430 true
1431 | Unop ((Ast_defs.Uminus | Ast_defs.Uplus), (_, _, (Int _ | Float _))) -> true
1432 | _ -> false
1434 let class_const_def ~in_enum_class c env cc =
1435 let { cc_type = h; cc_id = id; cc_kind = k; _ } = cc in
1436 let (env, hint_ty, opt_expected) =
1437 match h with
1438 | None ->
1439 begin
1440 match k with
1441 | CCAbstract None -> ()
1442 | CCAbstract (Some e (* default *))
1443 | CCConcrete e ->
1445 (not (is_literal_expr e))
1446 && Partial.should_check_error c.c_mode 2035
1447 && not (is_enum_or_enum_class c.c_kind)
1448 then
1449 Errors.missing_typehint (fst id)
1450 end;
1451 let (env, ty) = Env.fresh_type env (fst id) in
1452 (env, MakeType.unenforced ty, None)
1453 | Some h ->
1454 let ty = Decl_hint.hint env.decl_env h in
1455 let ty = Typing_enforceability.compute_enforced_ty env ty in
1456 let (env, ty) =
1457 Phase.localize_possibly_enforced_no_subst env ~ignore_errors:false ty
1459 (* Removing the HH\MemberOf wrapper in case of enum classes so the
1460 * following call to expr_* has the right expected type
1462 let opt_ty =
1463 if in_enum_class then
1464 match get_node ty.et_type with
1465 | Tnewtype (memberof, [_; et_type], _)
1466 when String.equal memberof SN.Classes.cMemberOf ->
1467 { ty with et_type }
1468 | _ -> ty
1469 else
1472 ( env,
1474 Some (ExpectedTy.make_and_allow_coercion (fst id) Reason.URhint opt_ty)
1477 let check env ty' =
1478 (* Lifted out to closure to illustrate which variables are captured *)
1479 Typing_coercion.coerce_type
1480 (fst id)
1481 Reason.URhint
1484 hint_ty
1485 Errors.class_constant_value_does_not_match_hint
1487 let type_and_check env e =
1488 let (env, (te, ty')) =
1489 Typing.(
1490 expr_with_pure_coeffects ?expected:opt_expected env e |> triple_to_pair)
1492 let env = check env ty' in
1493 (env, te, ty')
1495 let (env, kind, ty) =
1496 match k with
1497 | CCConcrete ((_, e_pos, _) as e) when in_enum_class ->
1498 let (env, cap, unsafe_cap) =
1499 (* Enum class constant initializers are restricted to be `write_props` *)
1500 let make_hint pos s = (pos, Aast.Happly ((pos, s), [])) in
1501 let enum_class_ctx =
1502 Some (e_pos, [make_hint e_pos SN.Capabilities.writeProperty])
1504 Typing.type_capability env enum_class_ctx enum_class_ctx e_pos
1506 let (env, (te, ty')) =
1507 Typing.(
1508 with_special_coeffects env cap unsafe_cap @@ fun env ->
1509 expr env ?expected:opt_expected e |> triple_to_pair)
1511 let (te, ty') =
1512 match deref hint_ty.et_type with
1513 | (r, Tnewtype (memberof, [enum_name; _], _))
1514 when String.equal memberof SN.Classes.cMemberOf ->
1515 let lift r ty = mk (r, Tnewtype (memberof, [enum_name; ty], ty)) in
1516 let (te_ty, p, te) = te in
1517 let te = (lift (get_reason te_ty) te_ty, p, te) in
1518 let ty' = lift r ty' in
1519 (te, ty')
1520 | _ -> (te, ty')
1522 let env = check env ty' in
1523 (env, Aast.CCConcrete te, ty')
1524 | CCConcrete e ->
1525 let (env, te, ty') = type_and_check env e in
1526 (env, Aast.CCConcrete te, ty')
1527 | CCAbstract (Some default) ->
1528 let (env, tdefault, ty') = type_and_check env default in
1529 (env, CCAbstract (Some tdefault), ty')
1530 | CCAbstract None -> (env, CCAbstract None, hint_ty.et_type)
1532 ( env,
1534 Aast.cc_type = cc.cc_type;
1535 Aast.cc_id = cc.cc_id;
1536 Aast.cc_kind = kind;
1537 Aast.cc_doc_comment = cc.cc_doc_comment;
1539 ty ) )
1541 let class_constr_def env cls constructor =
1542 let env = { env with inside_constructor = true } in
1543 Option.bind constructor ~f:(method_def env cls)
1545 (** Type-check a property declaration, with optional initializer *)
1546 let class_var_def ~is_static cls env cv =
1547 (* First pick up and localize the hint if it exists *)
1548 let decl_cty =
1549 merge_hint_with_decl_hint
1551 (hint_of_type_hint cv.cv_type)
1552 (get_decl_prop_ty env cls ~is_static (snd cv.cv_id))
1554 let (env, expected) =
1555 match decl_cty with
1556 | None -> (env, None)
1557 | Some decl_cty ->
1558 let decl_cty = Typing_enforceability.compute_enforced_ty env decl_cty in
1559 let (env, cty) =
1560 Phase.localize_possibly_enforced_no_subst
1562 ~ignore_errors:false
1563 decl_cty
1565 let expected =
1566 Some (ExpectedTy.make_and_allow_coercion cv.cv_span Reason.URhint cty)
1568 (env, expected)
1570 (* Next check the expression, passing in expected type if present *)
1571 let (env, typed_cv_expr) =
1572 match cv.cv_expr with
1573 | None -> (env, None)
1574 | Some e ->
1575 let (env, te, ty) = Typing.expr_with_pure_coeffects env ?expected e in
1576 (* Check that the inferred type is a subtype of the expected type.
1577 * Eventually this will be the responsibility of `expr`
1579 let env =
1580 match expected with
1581 | None -> env
1582 | Some ExpectedTy.{ pos = p; reason = ur; ty = cty } ->
1583 Typing_coercion.coerce_type
1589 Errors.class_property_initializer_type_does_not_match_hint
1591 (env, Some te)
1594 let env =
1595 if is_static then
1596 Typing.attributes_check_def
1598 SN.AttributeKinds.staticProperty
1599 cv.cv_user_attributes
1600 else
1601 Typing.attributes_check_def
1603 SN.AttributeKinds.instProperty
1604 cv.cv_user_attributes
1606 let (env, user_attributes) =
1607 List.map_env env cv.cv_user_attributes ~f:Typing.user_attribute
1610 Option.is_none (hint_of_type_hint cv.cv_type)
1611 && Partial.should_check_error (Env.get_mode env) 2001
1612 then
1613 Errors.prop_without_typehint
1614 (string_of_visibility cv.cv_visibility)
1615 cv.cv_id;
1616 let (env, global_inference_env) = Env.extract_global_inference_env env in
1617 let ((cv_type_ty, _) as cv_type) =
1618 match expected with
1619 | Some expected ->
1620 (expected.ExpectedTy.ty.et_type, hint_of_type_hint cv.cv_type)
1621 | None -> Tast.dummy_type_hint (hint_of_type_hint cv.cv_type)
1623 (* if the class implements dynamic, then check that the type of the property
1624 * is enforceable (for writing) and coerces to dynamic (for reading) *)
1626 TypecheckerOptions.enable_sound_dynamic
1627 (Provider_context.get_tcopt (Env.get_ctx env))
1628 && Cls.get_support_dynamic_type cls
1629 && not (Aast.equal_visibility cv.cv_visibility Private)
1630 then begin
1631 let env_with_require_dynamic =
1632 Typing_dynamic.add_require_dynamic_bounds env cls
1634 Option.iter decl_cty ~f:(fun ty ->
1635 Typing_dynamic.check_property_sound_for_dynamic_write
1636 ~on_error:Errors.property_is_not_enforceable
1637 env_with_require_dynamic
1638 (Cls.name cls)
1639 cv.cv_id
1640 ty);
1641 Typing_dynamic.check_property_sound_for_dynamic_read
1642 ~on_error:Errors.property_is_not_dynamic
1643 env_with_require_dynamic
1644 (Cls.name cls)
1645 cv.cv_id
1646 cv_type_ty
1647 end;
1648 ( env,
1650 Aast.cv_final = cv.cv_final;
1651 Aast.cv_xhp_attr = cv.cv_xhp_attr;
1652 Aast.cv_abstract = cv.cv_abstract;
1653 Aast.cv_visibility = cv.cv_visibility;
1654 Aast.cv_type;
1655 Aast.cv_id = cv.cv_id;
1656 Aast.cv_expr = typed_cv_expr;
1657 Aast.cv_user_attributes = user_attributes;
1658 Aast.cv_is_promoted_variadic = cv.cv_is_promoted_variadic;
1659 Aast.cv_doc_comment = cv.cv_doc_comment;
1660 (* Can make None to save space *)
1661 Aast.cv_is_static = is_static;
1662 Aast.cv_span = cv.cv_span;
1663 Aast.cv_readonly = cv.cv_readonly;
1665 (cv.cv_span, global_inference_env) ) )
1667 (** Check the where constraints of the parents of a class *)
1668 let check_class_parents_where_constraints env pc impl =
1669 let check_where_constraints env ((p, _hint), decl_ty) =
1670 let (env, locl_ty) =
1671 Phase.localize_no_subst env ~ignore_errors:false decl_ty
1673 match get_node (TUtils.get_base_type env locl_ty) with
1674 | Tclass (cls, _, tyl) ->
1675 (match Env.get_class env (snd cls) with
1676 | Some cls when not (List.is_empty (Cls.where_constraints cls)) ->
1677 let tc_tparams = Cls.tparams cls in
1678 let ety_env =
1680 (empty_expand_env_with_on_error
1681 (Env.unify_error_assert_primary_pos_in_current_decl env))
1682 with
1683 substs = Subst.make_locl tc_tparams tyl;
1686 Phase.check_where_constraints
1687 ~in_class:true
1688 ~use_pos:pc
1689 ~definition_pos:(Pos_or_decl.of_raw_pos p)
1690 ~ety_env
1692 (Cls.where_constraints cls)
1693 | _ -> env)
1694 | _ -> env
1696 List.fold impl ~init:env ~f:check_where_constraints
1698 let check_generic_class_with_SupportDynamicType env c parents =
1699 let (pc, c_name) = c.c_name in
1700 if c.c_support_dynamic_type then
1701 (* Any class that extends a class or implements an interface
1702 * that declares <<__SupportDynamicType>> must itself declare
1703 * <<__SupportDynamicType>>. This is checked elsewhere. But if any generic
1704 * parameters are not marked <<__NoRequireDynamic>> then we must check that the
1705 * conditional support for dynamic is sound.
1706 * We require that
1707 * If t <: dynamic
1708 * and C<T1,..,Tn> extends t
1709 * then C<T1,...,Tn> <: dynamic
1711 let dynamic_ty =
1712 MakeType.dynamic (Reason.Rdynamic_coercion (Reason.Rwitness pc))
1714 let env =
1715 List.fold parents ~init:env ~f:(fun env (_, ty) ->
1716 let (env, lty) = Phase.localize_no_subst env ~ignore_errors:true ty in
1717 match get_node lty with
1718 | Tclass ((_, name), _, _) ->
1719 begin
1720 match Env.get_class env name with
1721 | Some c when Cls.get_support_dynamic_type c ->
1722 let env_with_assumptions =
1723 Typing_subtype.add_constraint
1725 Ast_defs.Constraint_as
1727 dynamic_ty
1728 (Errors.unify_error_at pc)
1730 begin
1731 match Env.get_self_ty env with
1732 | Some self_ty ->
1733 TUtils.sub_type
1734 ~coerce:(Some Typing_logic.CoerceToDynamic)
1735 env_with_assumptions
1736 self_ty
1737 dynamic_ty
1738 (fun ?code:_ ?quickfixes:_ reasons ->
1739 let message =
1740 Typing_print.full_strip_ns_decl env ty
1741 ^ " is subtype of dynamic implies "
1742 ^ Typing_print.full_strip_ns env self_ty
1743 ^ " is subtype of dynamic"
1745 Errors.bad_conditional_support_dynamic
1747 ~child:c_name
1748 ~parent:name
1749 message
1750 reasons)
1751 | _ -> env
1753 | _ -> env
1755 | _ -> env)
1758 else
1761 let check_SupportDynamicType env c =
1763 TypecheckerOptions.enable_sound_dynamic
1764 (Provider_context.get_tcopt (Env.get_ctx env))
1765 then
1766 let parent_names =
1767 List.filter_map
1768 (c.c_extends @ c.c_uses @ c.c_implements)
1769 ~f:(function
1770 | (_, Happly ((_, name), _)) -> Some name
1771 | _ -> None)
1773 let error_parent_support_dynamic_type parent f =
1774 Errors.parent_support_dynamic_type
1775 (fst c.c_name)
1776 (snd c.c_name, c.c_kind)
1777 (Cls.name parent, Cls.kind parent)
1780 List.iter parent_names ~f:(fun name ->
1781 match Env.get_class_dep env name with
1782 | Some parent_type ->
1783 begin
1784 match Cls.kind parent_type with
1785 | Ast_defs.Cclass _
1786 | Ast_defs.Cinterface ->
1787 (* ensure that we implement dynamic if we are a subclass/subinterface of a class/interface
1788 * that implements dynamic. Upward well-formedness checks are performed in Typing_extends *)
1790 Cls.get_support_dynamic_type parent_type
1791 && not c.c_support_dynamic_type
1792 then
1793 error_parent_support_dynamic_type
1794 parent_type
1795 c.c_support_dynamic_type
1796 | Ast_defs.(Cenum | Cenum_class _ | Ctrait) -> ()
1798 | None -> ())
1800 let class_def_ env c tc =
1801 let env =
1802 let kind =
1803 if Ast_defs.is_c_enum c.c_kind then
1804 SN.AttributeKinds.enum
1805 else if Ast_defs.is_c_enum_class c.c_kind then
1806 SN.AttributeKinds.enumcls
1807 else
1808 SN.AttributeKinds.cls
1810 Typing.attributes_check_def env kind c.c_user_attributes
1812 let (env, file_attrs) = Typing.file_attributes env c.c_file_attributes in
1813 let ctx = Env.get_ctx env in
1814 if (not Ast_defs.(is_c_trait c.c_kind)) && not (shallow_decl_enabled ctx) then (
1815 (* These checks are only for eager mode. The same checks are performed
1816 * for shallow mode in Typing_inheritance *)
1817 let method_pos ~is_static class_id meth_id =
1818 let get_meth =
1819 if is_static then
1820 Decl_store.((get ()).get_static_method)
1821 else
1822 Decl_store.((get ()).get_method)
1824 match get_meth (class_id, meth_id) with
1825 | Some { fe_pos; _ } -> fe_pos
1826 | None -> Pos_or_decl.none
1828 let check_override ~is_static (id, ce) =
1829 if get_ce_override ce then
1830 let pos = method_pos ~is_static ce.ce_origin id in
1831 (* Method is actually defined in this class *)
1832 if String.equal ce.ce_origin (snd c.c_name) then
1833 Errors.should_be_override
1835 (snd c.c_name)
1837 ~current_decl_and_file:(Env.get_current_decl_and_file env)
1838 else
1839 match Env.get_class env ce.ce_origin with
1840 | None -> ()
1841 | Some parent_class ->
1842 (* If it's not defined here, then either it's inherited (so we have emitted an error already)
1843 * or it's in a trait, and so we need to emit the error now *)
1844 if not Ast_defs.(is_c_trait (Cls.kind parent_class)) then
1846 else
1847 Errors.override_per_trait c.c_name id ce.ce_origin pos
1850 List.iter (Cls.methods tc) ~f:(check_override ~is_static:false);
1851 List.iter (Cls.smethods tc) ~f:(check_override ~is_static:true)
1853 check_enum_includes env c;
1854 let (pc, c_name) = c.c_name in
1855 let (req_extends, req_implements) = split_reqs c.c_reqs in
1856 let hints_and_decl_tys hints =
1857 List.map hints ~f:(fun hint -> (hint, Decl_hint.hint env.decl_env hint))
1859 let extends = hints_and_decl_tys c.c_extends in
1860 let implements = hints_and_decl_tys c.c_implements in
1861 let uses = hints_and_decl_tys c.c_uses in
1862 let req_extends = hints_and_decl_tys req_extends in
1863 let req_implements = hints_and_decl_tys req_implements in
1864 let additional_parents =
1865 (* In an abstract class or a trait, we assume the interfaces
1866 will be implemented in the future, so we take them as
1867 part of the class (as requested by dependency injection implementers) *)
1868 match c.c_kind with
1869 | Ast_defs.Cclass k when Ast_defs.is_abstract k -> implements
1870 | Ast_defs.Ctrait -> implements @ req_implements
1871 | Ast_defs.(Cclass _ | Cinterface | Cenum | Cenum_class _) -> []
1873 let check_constructor_dep = check_constructor_dep env in
1874 check_implements_or_extends_unique implements;
1875 check_implements_or_extends_unique extends;
1876 check_constructor_dep extends;
1877 check_constructor_dep uses;
1878 check_constructor_dep req_extends;
1879 check_constructor_dep additional_parents;
1880 begin
1881 match c.c_enum with
1882 | Some e -> check_constructor_dep (hints_and_decl_tys e.e_includes)
1883 | _ -> ()
1884 end;
1885 let env =
1886 Phase.localize_and_add_ast_generic_parameters_and_where_constraints
1888 ~ignore_errors:false
1889 c.c_tparams
1890 c.c_where_constraints
1892 let env =
1893 Phase.check_where_constraints
1894 ~in_class:true
1895 ~use_pos:pc
1896 ~definition_pos:(Pos_or_decl.of_raw_pos pc)
1897 ~ety_env:
1898 (empty_expand_env_with_on_error
1899 (Env.unify_error_assert_primary_pos_in_current_decl env))
1901 (Cls.where_constraints tc)
1903 Typing_variance.class_def env c;
1904 check_no_generic_static_property env tc;
1905 let impl = extends @ implements @ uses in
1906 let impl =
1908 TypecheckerOptions.require_extends_implements_ancestors
1909 (Env.get_tcopt env)
1910 then
1911 impl @ req_extends @ req_implements
1912 else
1913 impl
1915 let env = check_class_parents_where_constraints env pc impl in
1916 check_parent env c tc;
1917 check_parents_sealed env c tc;
1918 if TypecheckerOptions.enforce_sealed_subclasses (Env.get_tcopt env) then
1919 if is_enum_or_enum_class c.c_kind then
1920 if TypecheckerOptions.enable_enum_supertyping (Env.get_tcopt env) then
1921 sealed_subtype ctx c ~is_enum:true
1922 else
1924 else
1925 sealed_subtype ctx c ~is_enum:false;
1926 let (_ : env) =
1927 check_generic_class_with_SupportDynamicType env c (extends @ implements)
1929 check_extend_abstract (pc, tc);
1930 if Cls.const tc then
1931 List.iter c.c_uses ~f:(check_non_const_trait_members pc env);
1932 let (static_vars, vars) = split_vars c.c_vars in
1933 List.iter static_vars ~f:(fun { cv_id = (p, id); _ } ->
1934 check_static_class_element (Cls.get_prop tc) ~elt_type:`Property id p);
1935 List.iter vars ~f:(fun { cv_id = (p, id); _ } ->
1936 check_dynamic_class_element (Cls.get_sprop tc) ~elt_type:`Property id p);
1937 let (constructor, static_methods, methods) = split_methods c.c_methods in
1938 List.iter static_methods ~f:(fun { m_name = (p, id); _ } ->
1939 check_static_class_element (Cls.get_method tc) ~elt_type:`Method id p);
1940 List.iter methods ~f:(fun { m_name = (p, id); _ } ->
1941 check_dynamic_class_element (Cls.get_smethod tc) ~elt_type:`Method id p);
1942 let env =
1943 if skip_hierarchy_checks ctx then
1945 else
1946 Typing_extends.check_implements_extends_uses
1948 ~implements:(List.map implements ~f:snd)
1949 ~parents:(List.map impl ~f:snd)
1950 (fst c.c_name, tc)
1952 if Cls.is_disposable tc then
1953 List.iter
1954 (c.c_extends @ c.c_uses)
1955 ~f:(Typing_disposable.enforce_is_disposable env);
1956 let (env, typed_vars_and_global_inference_envs) =
1957 List.map_env env vars ~f:(class_var_def ~is_static:false tc)
1959 let (typed_vars, vars_global_inference_envs) =
1960 List.unzip typed_vars_and_global_inference_envs
1962 let (typed_methods, methods_global_inference_envs) =
1963 List.filter_map methods ~f:(method_def env tc) |> List.unzip
1965 let (env, typed_typeconsts) =
1966 List.map_env env c.c_typeconsts ~f:(typeconst_def c)
1968 let in_enum_class = Env.is_enum_class env c_name in
1969 let (env, consts) =
1970 List.map_env env c.c_consts ~f:(class_const_def ~in_enum_class c)
1972 let (typed_consts, const_types) = List.unzip consts in
1973 let env = Typing_enum.enum_class_check env tc c.c_consts const_types in
1974 let typed_constructor = class_constr_def env tc constructor in
1975 let env = Env.set_static env in
1976 let (env, typed_static_vars_and_global_inference_envs) =
1977 List.map_env env static_vars ~f:(class_var_def ~is_static:true tc)
1979 let (typed_static_vars, static_vars_global_inference_envs) =
1980 List.unzip typed_static_vars_and_global_inference_envs
1982 let (typed_static_methods, static_methods_global_inference_envs) =
1983 List.filter_map static_methods ~f:(method_def env tc) |> List.unzip
1985 let (methods, constr_global_inference_env) =
1986 match typed_constructor with
1987 | None -> (typed_static_methods @ typed_methods, [])
1988 | Some (m, global_inference_env) ->
1989 (m :: typed_static_methods @ typed_methods, [global_inference_env])
1991 let (env, tparams) = class_type_param env c.c_tparams in
1992 let (env, user_attributes) =
1993 List.map_env env c.c_user_attributes ~f:Typing.user_attribute
1995 let env = Typing_solver.solve_all_unsolved_tyvars env in
1996 check_SupportDynamicType env c;
1999 Aast.c_span = c.c_span;
2000 Aast.c_annotation = Env.save (Env.get_tpenv env) env;
2001 Aast.c_mode = c.c_mode;
2002 Aast.c_final = c.c_final;
2003 Aast.c_is_xhp = c.c_is_xhp;
2004 Aast.c_has_xhp_keyword = c.c_has_xhp_keyword;
2005 Aast.c_kind = c.c_kind;
2006 Aast.c_name = c.c_name;
2007 Aast.c_tparams = tparams;
2008 Aast.c_extends = c.c_extends;
2009 Aast.c_uses = c.c_uses;
2010 (* c_use_as_alias and c_insteadof_alias are PHP features not supported
2011 * in Hack but are required since we have runtime support for it
2013 Aast.c_use_as_alias = [];
2014 Aast.c_insteadof_alias = [];
2015 Aast.c_xhp_attr_uses = c.c_xhp_attr_uses;
2016 Aast.c_xhp_category = c.c_xhp_category;
2017 Aast.c_reqs = c.c_reqs;
2018 Aast.c_implements = c.c_implements;
2019 Aast.c_support_dynamic_type = c.c_support_dynamic_type;
2020 Aast.c_where_constraints = c.c_where_constraints;
2021 Aast.c_consts = typed_consts;
2022 Aast.c_typeconsts = typed_typeconsts;
2023 Aast.c_vars = typed_static_vars @ typed_vars;
2024 Aast.c_methods = methods;
2025 Aast.c_file_attributes = file_attrs;
2026 Aast.c_user_attributes = user_attributes;
2027 Aast.c_namespace = c.c_namespace;
2028 Aast.c_enum = c.c_enum;
2029 Aast.c_doc_comment = c.c_doc_comment;
2030 Aast.c_attributes = [];
2031 Aast.c_xhp_children = c.c_xhp_children;
2032 Aast.c_xhp_attrs = [];
2033 Aast.c_emit_id = c.c_emit_id;
2035 methods_global_inference_envs
2036 @ static_methods_global_inference_envs
2037 @ constr_global_inference_env
2038 @ static_vars_global_inference_envs
2039 @ vars_global_inference_envs )
2041 let class_def ctx c =
2042 Counters.count Counters.Category.Typecheck @@ fun () ->
2043 Errors.run_with_span c.c_span @@ fun () ->
2044 let env = EnvFromDef.class_env ~origin:Decl_counters.TopLevel ctx c in
2045 let (name_pos, name) = c.c_name in
2046 let tc = Env.get_class env name in
2047 let env = Env.set_env_pessimize env in
2048 Typing_helpers.add_decl_errors (Option.bind tc ~f:Cls.decl_errors);
2049 let env =
2050 Env.set_module
2052 (Naming_attributes_params.get_module_attribute c.c_user_attributes)
2054 let env =
2055 Env.set_internal
2057 (Naming_attributes.mem SN.UserAttributes.uaInternal c.c_user_attributes)
2059 Typing_type_wellformedness.class_ env c;
2060 NastInitCheck.class_ env c;
2061 match tc with
2062 | None ->
2063 (* This can happen if there was an error during the declaration
2064 * of the class. *)
2065 None
2066 | Some tc ->
2067 (* If there are duplicate definitions of the class then we will end up
2068 * checking one AST with respect to the decl corresponding to the other definition.
2069 * Naming has already detected duplicates, so let's just avoid cascading unhelpful
2070 * typing errors, and also avoid triggering the bad position assert
2072 if not (Pos.equal name_pos (Cls.pos tc |> Pos_or_decl.unsafe_to_raw_pos))
2073 then
2074 None
2075 else
2076 let env =
2077 if skip_hierarchy_checks ctx then
2079 else
2080 let env = Typing_requirements.check_class env name_pos tc in
2081 if shallow_decl_enabled ctx then
2082 Typing_inheritance.check_class env name_pos tc;
2085 Some (class_def_ env c tc)
2087 let gconst_def ctx cst =
2088 Counters.count Counters.Category.Typecheck @@ fun () ->
2089 Errors.run_with_span cst.cst_span @@ fun () ->
2090 let env = EnvFromDef.gconst_env ~origin:Decl_counters.TopLevel ctx cst in
2091 let env = Env.set_env_pessimize env in
2092 Typing_type_wellformedness.global_constant env cst;
2093 let (typed_cst_value, env) =
2094 let value = cst.cst_value in
2095 match cst.cst_type with
2096 | Some hint ->
2097 let ty = Decl_hint.hint env.decl_env hint in
2098 let ty = Typing_enforceability.compute_enforced_ty env ty in
2099 let (env, dty) =
2100 Phase.localize_possibly_enforced_no_subst env ~ignore_errors:false ty
2102 let (env, te, value_type) =
2103 let expected =
2104 ExpectedTy.make_and_allow_coercion (fst hint) Reason.URhint dty
2106 Typing.expr_with_pure_coeffects env ~expected value
2108 let env =
2109 Typing_coercion.coerce_type
2110 (fst hint)
2111 Reason.URhint
2113 value_type
2115 Errors.unify_error
2117 (te, env)
2118 | None ->
2120 (not (is_literal_expr value))
2121 && Partial.should_check_error cst.cst_mode 2035
2122 then
2123 Errors.missing_typehint (fst cst.cst_name);
2124 let (env, te, _value_type) = Typing.expr_with_pure_coeffects env value in
2125 (te, env)
2128 Aast.cst_annotation = Env.save (Env.get_tpenv env) env;
2129 Aast.cst_mode = cst.cst_mode;
2130 Aast.cst_name = cst.cst_name;
2131 Aast.cst_type = cst.cst_type;
2132 Aast.cst_value = typed_cst_value;
2133 Aast.cst_namespace = cst.cst_namespace;
2134 Aast.cst_span = cst.cst_span;
2135 Aast.cst_emit_id = cst.cst_emit_id;
2138 let record_field env f =
2139 let (id, hint, e) = f in
2140 let (p, _) = hint in
2141 let (env, cty) = Phase.localize_hint_no_subst env ~ignore_errors:false hint in
2142 let expected = ExpectedTy.make p Reason.URhint cty in
2143 match e with
2144 | Some e ->
2145 let (env, te, ty) = Typing.expr_with_pure_coeffects env ~expected e in
2146 let env =
2147 Typing_coercion.coerce_type
2149 Reason.URhint
2152 (MakeType.unenforced cty)
2153 Errors.record_init_value_does_not_match_hint
2155 (env, (id, hint, Some te))
2156 | None -> (env, (id, hint, None))
2158 let record_def_parent env rd parent_hint =
2159 match snd parent_hint with
2160 | Aast.Happly ((parent_pos, parent_name), []) ->
2161 (match Decl_provider.get_record_def (Env.get_ctx env) parent_name with
2162 | Some parent_rd ->
2163 (* We can only inherit from abstract records. *)
2164 (if not parent_rd.rdt_abstract then
2165 let (parent_pos, parent_name) = parent_rd.rdt_name in
2166 Errors.extend_non_abstract_record
2167 parent_name
2168 (fst rd.rd_name)
2169 parent_pos);
2171 (* Ensure we aren't defining fields that overlap with
2172 inherited fields. *)
2173 let inherited_fields = Typing_helpers.all_record_fields env parent_rd in
2174 List.iter rd.rd_fields ~f:(fun ((pos, name), _, _) ->
2175 match SMap.find_opt name inherited_fields with
2176 | Some ((prev_pos, _), _) ->
2177 Errors.repeated_record_field name pos prev_pos
2178 | None -> ())
2179 | None ->
2180 (* Something exists with this name (naming succeeded), but it's
2181 not a record. *)
2182 Errors.unbound_name parent_pos parent_name Errors.RecordContext)
2183 | _ ->
2184 failwith
2185 "Record parent was not an Happly. This should have been a syntax error."
2187 (* Report an error if we have inheritance cycles in record declarations. *)
2188 let check_record_inheritance_cycle env ((rd_pos, rd_name) : Aast.sid) : unit =
2189 let rec worker name trace seen =
2190 match Decl_provider.get_record_def (Env.get_ctx env) name with
2191 | Some rd ->
2192 (match rd.rdt_extends with
2193 | Some (_, parent_name) when String.equal parent_name rd_name ->
2194 (* This record is in an inheritance cycle.*)
2195 Errors.cyclic_record_def trace rd_pos
2196 | Some (_, parent_name) when SSet.mem parent_name seen ->
2197 (* There's an inheritance cycle higher in the chain. *)
2199 | Some (_, parent_name) ->
2200 worker parent_name (parent_name :: trace) (SSet.add parent_name seen)
2201 | None -> ())
2202 | None -> ()
2204 worker rd_name [rd_name] (SSet.singleton rd_name)
2206 let record_def_def ctx rd =
2207 Counters.count Counters.Category.Typecheck @@ fun () ->
2208 let env = EnvFromDef.record_def_env ~origin:Decl_counters.TopLevel ctx rd in
2209 Typing_type_wellformedness.record_def env rd;
2210 (match rd.rd_extends with
2211 | Some parent -> record_def_parent env rd parent
2212 | None -> ());
2214 check_record_inheritance_cycle env rd.rd_name;
2216 let (env, attributes) =
2217 List.map_env env rd.rd_user_attributes ~f:Typing.user_attribute
2219 let (_env, fields) = List.map_env env rd.rd_fields ~f:record_field in
2221 Aast.rd_annotation = Env.save (Env.get_tpenv env) env;
2222 Aast.rd_name = rd.rd_name;
2223 Aast.rd_extends = rd.rd_extends;
2224 Aast.rd_abstract = rd.rd_abstract;
2225 Aast.rd_fields = fields;
2226 Aast.rd_user_attributes = attributes;
2227 Aast.rd_namespace = rd.rd_namespace;
2228 Aast.rd_span = rd.rd_span;
2229 Aast.rd_doc_comment = rd.rd_doc_comment;
2230 Aast.rd_emit_id = rd.rd_emit_id;
2233 let nast_to_tast_gienv ~(do_tast_checks : bool) ctx nast :
2234 _ * Typing_inference_env.t_global_with_pos list =
2235 let convert_def = function
2236 (* Sometimes typing will just return `None` but that should only be the case
2237 * if an error had already been registered e.g. in naming
2239 | Fun f ->
2240 begin
2241 match fun_def ctx f with
2242 | Some (f, env) -> Some (Aast.Fun f, [env])
2243 | None -> None
2245 | Constant gc -> Some (Aast.Constant (gconst_def ctx gc), [])
2246 | Typedef td -> Some (Aast.Typedef (Typing.typedef_def ctx td), [])
2247 | Class c ->
2248 begin
2249 match class_def ctx c with
2250 | Some (c, envs) -> Some (Aast.Class c, envs)
2251 | None -> None
2253 | RecordDef rd -> Some (Aast.RecordDef (record_def_def ctx rd), [])
2254 (* We don't typecheck top level statements:
2255 * https://docs.hhvm.com/hack/unsupported/top-level
2256 * so just create the minimal env for us to construct a Stmt.
2258 | Stmt s ->
2259 let env = Env.empty ctx Relative_path.default ~droot:None in
2260 Some (Aast.Stmt (snd (Typing.stmt env s)), [])
2261 | Namespace _
2262 | NamespaceUse _
2263 | SetNamespaceEnv _
2264 | FileAttributes _ ->
2265 failwith
2266 "Invalid nodes in NAST. These nodes should be removed during naming."
2268 Nast_check.program ctx nast;
2269 let (tast, envs) = List.unzip @@ List.filter_map nast ~f:convert_def in
2270 let envs = List.concat envs in
2271 if do_tast_checks then Tast_check.program ctx tast;
2272 (tast, envs)
2274 let nast_to_tast ~do_tast_checks ctx nast =
2275 let (tast, _gienvs) = nast_to_tast_gienv ~do_tast_checks ctx nast in
2276 tast