Rip out legacy reactivity from the typechecker and HackC
[hiphop-php.git] / hphp / hack / src / typing / typing_toplevel.ml
blobb3fa9694c8b7335f414c9a5963be744bb8591c94
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 Common
17 open Aast
18 open Typing_defs
19 open Typing_env_types
20 open Typing_helpers
21 open Utils
22 module FunUtils = Decl_fun_utils
23 module Reason = Typing_reason
24 module Env = Typing_env
25 module EnvFromDef = Typing_env_from_def
26 module MakeType = Typing_make_type
28 (* The two following functions enable us to retrieve the function (or class)
29 header from the shared mem. Note that they only return a non None value if
30 global inference is on *)
31 let get_decl_function_header env function_id =
32 let is_global_inference_on = TCO.global_inference (Env.get_tcopt env) in
33 if is_global_inference_on then
34 match Decl_provider.get_fun (Env.get_ctx env) function_id with
35 | Some { fe_type; _ } ->
36 begin
37 match get_node fe_type with
38 | Tfun fun_type -> Some fun_type
39 | _ -> None
40 end
41 | _ -> None
42 else
43 None
45 and get_decl_method_header tcopt cls method_id ~is_static =
46 let is_global_inference_on = TCO.global_inference tcopt in
47 if is_global_inference_on then
48 match Cls.get_any_method ~is_static cls method_id with
49 | Some { ce_type = (lazy ty); _ } ->
50 begin
51 match get_node ty with
52 | Tfun fun_type -> Some fun_type
53 | _ -> None
54 end
55 | _ -> None
56 else
57 None
59 let enforce_param_not_disposable env param ty =
60 if has_accept_disposable_attribute param then
62 else
63 let p = param.param_pos in
64 match Typing_disposable.is_disposable_type env ty with
65 | Some class_name -> Errors.invalid_disposable_hint p (strip_ns class_name)
66 | None -> ()
68 (* In strict mode, we force you to give a type declaration on a parameter *)
69 (* But the type checker is nice: it makes a suggestion :-) *)
70 let check_param_has_hint env param ty is_code_error =
71 let env =
72 if is_code_error 4231 then
73 Typing.attributes_check_def
74 env
75 SN.AttributeKinds.parameter
76 param.param_user_attributes
77 else
78 env
80 match hint_of_type_hint param.param_type_hint with
81 | None when param.param_is_variadic && is_code_error 4033 ->
82 Errors.expecting_type_hint_variadic param.param_pos
83 | None when is_code_error 4032 -> Errors.expecting_type_hint param.param_pos
84 | Some _ when is_code_error 4010 ->
85 (* We do not permit hints to implement IDisposable or IAsyncDisposable *)
86 enforce_param_not_disposable env param ty
87 | _ -> ()
89 (* This function is used to determine the type of an argument.
90 * When we want to type-check the body of a function, we need to
91 * introduce the type of the arguments of the function in the environment
92 * Let's take an example, we want to check the code of foo:
94 * function foo(int $x): int {
95 * // CALL TO make_param_type on (int $x)
96 * // Now we know that the type of $x is int
98 * return $x; // in the environment $x is an int, the code is correct
99 * }
101 * When we localize, we want to resolve to "static" or "$this" depending on
102 * the context. Even though we are passing in CIstatic, resolve_with_class_id
103 * is smart enough to know what to do. Why do this? Consider the following
105 * abstract class C {
106 * abstract const type T;
108 * private this::T $val;
110 * final public function __construct(this::T $x) {
111 * $this->val = $x;
114 * public static function create(this::T $x): this {
115 * return new static($x);
119 * class D extends C { const type T = int; }
121 * In __construct() we want to be able to assign $x to $this->val. The type of
122 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
123 * We can do this soundly because when we construct a new class such as,
124 * 'new D(0)' we can determine the late static bound type (D) and resolve
125 * 'this::T' to 'D::T' which is int.
127 * A similar line of reasoning is applied for the static method create.
129 let make_param_local_ty env decl_hint param =
130 let ety_env = Phase.env_with_self env in
131 let r = Reason.Rwitness param.param_pos in
132 let (env, ty) =
133 match decl_hint with
134 | None -> (env, mk (r, TUtils.tany env))
135 | Some ty ->
136 let { et_type = ty; _ } =
137 Typing_enforceability.compute_enforced_and_pessimize_ty
138 ~explicitly_untrusted:param.param_is_variadic
142 Phase.localize ~ety_env env ty
144 let ty =
145 match get_node ty with
146 | t when param.param_is_variadic ->
147 (* when checking the body of a function with a variadic
148 * argument, "f(C ...$args)", $args is a varray<C> *)
149 let r = Reason.Rvar_param param.param_pos in
150 let arr_values = mk (r, t) in
151 let unification =
152 TypecheckerOptions.array_unification (Env.get_tcopt env)
154 MakeType.varray ~unification r arr_values
155 | _ -> ty
157 (env, ty)
159 let get_callable_variadicity ~partial_callback ~pos env variadicity_decl_ty =
160 function
161 | FVvariadicArg vparam ->
162 let (env, ty) = make_param_local_ty env variadicity_decl_ty vparam in
163 check_param_has_hint env vparam ty partial_callback;
164 let (env, t_variadic) = Typing.bind_param env (ty, vparam) in
165 (env, Aast.FVvariadicArg t_variadic)
166 | FVellipsis p ->
167 if Partial.should_check_error (Env.get_mode env) 4223 then
168 Errors.ellipsis_strict_mode ~require:`Type_and_param_name pos;
169 (env, Aast.FVellipsis p)
170 | FVnonVariadic -> (env, Aast.FVnonVariadic)
172 let merge_hint_with_decl_hint env type_hint decl_ty =
173 let contains_tvar decl_ty =
174 match decl_ty with
175 | None -> false
176 | Some decl_ty -> TUtils.contains_tvar_decl decl_ty
178 if contains_tvar decl_ty then
179 decl_ty
180 else
181 Option.map type_hint ~f:(Decl_hint.hint env.decl_env)
183 (* During the decl phase we can, for global inference, add "improved type hints".
184 That is we can say that some missing type hints are in fact global tyvars.
185 In that case to get the real type hint we must merge the type hint present
186 in the ast with the one we created during the decl phase. This function does
187 exactly this for the return type, the parameters and the variadic parameters.
189 let merge_decl_header_with_hints ~params ~ret ~variadic decl_header env =
190 let ret_decl_ty =
191 merge_hint_with_decl_hint
193 (hint_of_type_hint ret)
194 (Option.map
195 ~f:(fun { ft_ret = { et_type; _ }; _ } -> et_type)
196 decl_header)
198 let params_decl_ty =
199 match decl_header with
200 | None ->
201 List.map
202 ~f:(fun h ->
203 merge_hint_with_decl_hint
205 (hint_of_type_hint h.param_type_hint)
206 None)
207 params
208 | Some { ft_params; _ } ->
209 List.zip_exn params ft_params
210 |> List.map ~f:(fun (h, { fp_type = { et_type; _ }; _ }) ->
211 merge_hint_with_decl_hint
213 (hint_of_type_hint h.param_type_hint)
214 (Some et_type))
216 let variadicity_decl_ty =
217 match (decl_header, variadic) with
218 | ( Some { ft_arity = Fvariadic { fp_type = { et_type; _ }; _ }; _ },
219 FVvariadicArg fp ) ->
220 merge_hint_with_decl_hint
222 (hint_of_type_hint fp.param_type_hint)
223 (Some et_type)
224 | (_, FVvariadicArg fp) ->
225 merge_hint_with_decl_hint env (hint_of_type_hint fp.param_type_hint) None
226 | _ -> None
228 (ret_decl_ty, params_decl_ty, variadicity_decl_ty)
230 (* Checking this with List.exists will be a single op in the vast majority of cases (empty) *)
231 let get_ctx_vars ctxs =
232 Option.value_map
233 ~f:(fun (_, cs) ->
234 List.filter_map cs ~f:(function
235 | (_, Haccess ((_, Hvar n), _)) -> Some n
236 | _ -> None))
237 ~default:[]
238 ctxs
240 let fun_def ctx f :
241 (Tast.fun_def * Typing_inference_env.t_global_with_pos) option =
242 Counters.count Counters.Category.Typecheck @@ fun () ->
243 Errors.run_with_span f.f_span @@ fun () ->
244 let env = EnvFromDef.fun_env ~origin:Decl_counters.TopLevel ctx f in
245 with_timeout env f.f_name ~do_:(fun env ->
246 (* reset the expression dependent display ids for each function body *)
247 Reason.expr_display_id_map := IMap.empty;
248 let pos = fst f.f_name in
249 let decl_header = get_decl_function_header env (snd f.f_name) in
250 let env = Env.open_tyvars env (fst f.f_name) in
251 let env = Env.set_env_function_pos env pos in
252 let env = Env.set_env_pessimize env in
253 let env =
254 Typing.attributes_check_def env SN.AttributeKinds.fn f.f_user_attributes
256 let (env, file_attrs) = Typing.file_attributes env f.f_file_attributes in
257 let (env, cap_ty, unsafe_cap_ty) =
258 Typing.type_capability env f.f_ctxs f.f_unsafe_ctxs (fst f.f_name)
260 let (env, _) =
261 Typing_coeffects.register_capabilities env cap_ty unsafe_cap_ty
263 Typing_check_decls.fun_ env f;
264 let env =
265 Phase.localize_and_add_ast_generic_parameters_and_where_constraints
268 f.f_tparams
269 f.f_where_constraints
271 let env = Env.set_fn_kind env f.f_fun_kind in
272 let (return_decl_ty, params_decl_ty, variadicity_decl_ty) =
273 merge_decl_header_with_hints
274 ~params:f.f_params
275 ~ret:f.f_ret
276 ~variadic:f.f_variadic
277 decl_header
280 let (env, return_ty) =
281 match return_decl_ty with
282 | None ->
283 (env, Typing_return.make_default_return ~is_method:false env f.f_name)
284 | Some ty ->
285 let localize env ty = Phase.localize_with_self env ty in
286 Typing_return.make_return_type localize env ty
288 let return =
289 Typing_return.make_info
290 f.f_fun_kind
291 f.f_user_attributes
293 ~is_explicit:(Option.is_some (hint_of_type_hint f.f_ret))
294 return_ty
295 return_decl_ty
297 let (env, param_tys) =
298 List.zip_exn f.f_params params_decl_ty
299 |> List.map_env env ~f:(fun env (param, hint) ->
300 make_param_local_ty env hint param)
302 let partial_callback = Partial.should_check_error (Env.get_mode env) in
303 let check_has_hint p t = check_param_has_hint env p t partial_callback in
304 List.iter2_exn ~f:check_has_hint f.f_params param_tys;
305 Typing_memoize.check_function env f;
306 let params_need_immutable = get_ctx_vars f.f_ctxs in
307 let (env, typed_params) =
308 let bind_param_and_check env param =
309 let name = (snd param).param_name in
310 let immutable =
311 List.exists ~f:(String.equal name) params_need_immutable
313 let (env, fun_param) = Typing.bind_param ~immutable env param in
314 (env, fun_param)
316 List.map_env
318 (List.zip_exn param_tys f.f_params)
319 bind_param_and_check
321 let (env, t_variadic) =
322 get_callable_variadicity
323 ~pos
324 ~partial_callback
326 variadicity_decl_ty
327 f.f_variadic
329 let env =
330 set_tyvars_variance_in_callable env return_ty param_tys t_variadic
332 let local_tpenv = Env.get_tpenv env in
333 let disable =
334 Naming_attributes.mem
335 SN.UserAttributes.uaDisableTypecheckerInternal
336 f.f_user_attributes
338 let (env, tb) =
339 Typing.fun_ ~disable env return pos f.f_body f.f_fun_kind
341 begin
342 match hint_of_type_hint f.f_ret with
343 | None ->
344 if partial_callback 4030 then Errors.expecting_return_type_hint pos
345 | Some _ -> ()
346 end;
347 let (env, tparams) = List.map_env env f.f_tparams Typing.type_param in
348 let (env, user_attributes) =
349 List.map_env env f.f_user_attributes Typing.user_attribute
351 let env =
352 Typing_solver.close_tyvars_and_solve env Errors.bad_function_typevar
354 let env =
355 Typing_solver.solve_all_unsolved_tyvars env Errors.bad_function_typevar
357 let fundef =
359 Aast.f_annotation = Env.save local_tpenv env;
360 Aast.f_span = f.f_span;
361 Aast.f_mode = f.f_mode;
362 Aast.f_readonly_ret = f.f_readonly_ret;
363 Aast.f_ret = (return_ty, hint_of_type_hint f.f_ret);
364 Aast.f_name = f.f_name;
365 Aast.f_tparams = tparams;
366 Aast.f_where_constraints = f.f_where_constraints;
367 Aast.f_variadic = t_variadic;
368 Aast.f_params = typed_params;
369 Aast.f_ctxs = f.f_ctxs;
370 Aast.f_unsafe_ctxs = f.f_unsafe_ctxs;
371 Aast.f_fun_kind = f.f_fun_kind;
372 Aast.f_file_attributes = file_attrs;
373 Aast.f_user_attributes = user_attributes;
374 Aast.f_body = { Aast.fb_ast = tb; fb_annotation = () };
375 Aast.f_external = f.f_external;
376 Aast.f_namespace = f.f_namespace;
377 Aast.f_doc_comment = f.f_doc_comment;
378 Aast.f_static = f.f_static;
381 let (_env, global_inference_env) = Env.extract_global_inference_env env in
382 (fundef, (pos, global_inference_env)))
384 let method_def env cls m =
385 Errors.run_with_span m.m_span @@ fun () ->
386 with_timeout env m.m_name ~do_:(fun env ->
387 FunUtils.check_params m.m_params;
388 let initial_env = env in
389 (* reset the expression dependent display ids for each method body *)
390 Reason.expr_display_id_map := IMap.empty;
391 let decl_header =
392 get_decl_method_header
393 (Env.get_tcopt env)
395 (snd m.m_name)
396 ~is_static:m.m_static
398 let pos = fst m.m_name in
399 let env = Env.open_tyvars env (fst m.m_name) in
400 let env = Env.reinitialize_locals env in
401 let env = Env.set_env_function_pos env pos in
402 let env =
403 Typing.attributes_check_def
405 SN.AttributeKinds.mthd
406 m.m_user_attributes
408 let (env, cap_ty, unsafe_cap_ty) =
409 Typing.type_capability env m.m_ctxs m.m_unsafe_ctxs (fst m.m_name)
411 let (env, _) =
412 Typing_coeffects.register_capabilities env cap_ty unsafe_cap_ty
414 let is_ctor = String.equal (snd m.m_name) SN.Members.__construct in
415 let env = Env.set_fun_is_constructor env is_ctor in
416 let env =
417 Phase.localize_and_add_ast_generic_parameters_and_where_constraints
420 m.m_tparams
421 m.m_where_constraints
423 let env =
424 match Env.get_self_ty env with
425 | Some ty when not (Env.is_static env) ->
426 Env.set_local env this ty Pos.none
427 | _ -> env
429 let env =
430 match Env.get_self_class env with
431 | None -> env
432 | Some c ->
433 (* Mark $this as a using variable if it has a disposable type *)
434 if Cls.is_disposable c then
435 Env.set_using_var env this
436 else
439 let env = Env.clear_params env in
440 let (ret_decl_ty, params_decl_ty, variadicity_decl_ty) =
441 merge_decl_header_with_hints
442 ~params:m.m_params
443 ~ret:m.m_ret
444 ~variadic:m.m_variadic
445 decl_header
448 let env = Env.set_fn_kind env m.m_fun_kind in
449 let (env, locl_ty) =
450 match ret_decl_ty with
451 | None ->
452 (env, Typing_return.make_default_return ~is_method:true env m.m_name)
453 | Some ret ->
454 (* If a 'this' type appears it needs to be compatible with the
455 * late static type
457 let ety_env = Phase.env_with_self env in
458 Typing_return.make_return_type (Phase.localize ~ety_env) env ret
460 let return =
461 Typing_return.make_info
462 m.m_fun_kind
463 m.m_user_attributes
465 ~is_explicit:(Option.is_some (hint_of_type_hint m.m_ret))
466 locl_ty
467 ret_decl_ty
469 let (env, param_tys) =
470 List.zip_exn m.m_params params_decl_ty
471 |> List.map_env env ~f:(fun env (param, hint) ->
472 make_param_local_ty env hint param)
474 let partial_callback = Partial.should_check_error (Env.get_mode env) in
475 let param_fn p t = check_param_has_hint env p t partial_callback in
476 List.iter2_exn ~f:param_fn m.m_params param_tys;
477 Typing_memoize.check_method env m;
478 let params_need_immutable = get_ctx_vars m.m_ctxs in
479 let (env, typed_params) =
480 let bind_param_and_check env param =
481 let name = (snd param).param_name in
482 let immutable =
483 List.exists ~f:(String.equal name) params_need_immutable
485 let (env, fun_param) = Typing.bind_param ~immutable env param in
486 (env, fun_param)
488 List.map_env
490 (List.zip_exn param_tys m.m_params)
491 bind_param_and_check
493 let (env, t_variadic) =
494 get_callable_variadicity
495 ~partial_callback
496 ~pos
498 variadicity_decl_ty
499 m.m_variadic
501 let env =
502 set_tyvars_variance_in_callable env locl_ty param_tys t_variadic
504 let nb = Nast.assert_named_body m.m_body in
505 let local_tpenv = Env.get_tpenv env in
506 let disable =
507 Naming_attributes.mem
508 SN.UserAttributes.uaDisableTypecheckerInternal
509 m.m_user_attributes
511 let (env, tb) =
512 Typing.fun_
513 ~abstract:m.m_abstract
514 ~disable
516 return
519 m.m_fun_kind
521 let type_hint' =
522 match hint_of_type_hint m.m_ret with
523 | None when String.equal (snd m.m_name) SN.Members.__construct ->
524 Some (pos, Hprim Tvoid)
525 | None ->
526 if partial_callback 4030 then Errors.expecting_return_type_hint pos;
527 None
528 | Some _ -> hint_of_type_hint m.m_ret
530 let m = { m with m_ret = (fst m.m_ret, type_hint') } in
531 let (env, tparams) = List.map_env env m.m_tparams Typing.type_param in
532 let (env, user_attributes) =
533 List.map_env env m.m_user_attributes Typing.user_attribute
535 let env =
536 Typing_solver.close_tyvars_and_solve env Errors.bad_method_typevar
538 let env =
539 Typing_solver.solve_all_unsolved_tyvars env Errors.bad_method_typevar
541 (* if the class implements dynamic, then check that its methods are dynamically callable *)
543 TypecheckerOptions.enable_sound_dynamic
544 (Provider_context.get_tcopt (Env.get_ctx env))
545 && Cls.get_implements_dynamic cls
546 then begin
547 (* 1. check if all the parameters of the method are enforceable *)
548 List.iter params_decl_ty ~f:(fun dtyopt ->
549 match dtyopt with
550 | Some dty ->
551 let te_check = Typing_enforceability.is_enforceable env dty in
552 if not te_check then
553 Errors.method_is_not_dynamically_callable
554 (fst m.m_name)
555 (snd m.m_name)
556 (Cls.name cls)
557 | None -> ());
558 (* 2. check if the return type is coercible *)
561 (Typing_subtype.is_sub_type_for_union
562 ~coerce:(Some Typing_logic.CoerceToDynamic)
564 locl_ty
565 (mk (Reason.Rnone, Tdynamic)))
566 then
567 Errors.method_is_not_dynamically_callable
568 (fst m.m_name)
569 (snd m.m_name)
570 (Cls.name cls)
571 end;
572 let method_def =
574 Aast.m_annotation = Env.save local_tpenv env;
575 Aast.m_span = m.m_span;
576 Aast.m_final = m.m_final;
577 Aast.m_static = m.m_static;
578 Aast.m_abstract = m.m_abstract;
579 Aast.m_visibility = m.m_visibility;
580 Aast.m_readonly_this = m.m_readonly_this;
581 Aast.m_name = m.m_name;
582 Aast.m_tparams = tparams;
583 Aast.m_where_constraints = m.m_where_constraints;
584 Aast.m_variadic = t_variadic;
585 Aast.m_params = typed_params;
586 Aast.m_ctxs = m.m_ctxs;
587 Aast.m_unsafe_ctxs = m.m_unsafe_ctxs;
588 Aast.m_fun_kind = m.m_fun_kind;
589 Aast.m_user_attributes = user_attributes;
590 Aast.m_readonly_ret = m.m_readonly_ret;
591 Aast.m_ret = (locl_ty, hint_of_type_hint m.m_ret);
592 Aast.m_body = { Aast.fb_ast = tb; fb_annotation = () };
593 Aast.m_external = m.m_external;
594 Aast.m_doc_comment = m.m_doc_comment;
597 let (env, global_inference_env) = Env.extract_global_inference_env env in
598 let _env = Env.log_env_change "method_def" initial_env env in
599 (method_def, (pos, global_inference_env)))
601 (** Checks that extending this parent is legal - e.g. it is not final and not const. *)
602 let check_parent env class_def class_type =
603 match Env.get_parent_class env with
604 | Some parent_type ->
605 let position = fst class_def.c_name in
606 if Cls.const class_type && not (Cls.const parent_type) then
607 Errors.self_const_parent_not position;
608 if Cls.final parent_type then
609 Errors.extend_final position (Cls.pos parent_type) (Cls.name parent_type)
610 | None -> ()
612 let check_parent_sealed ~(is_enum_class : bool) child_type parent_type =
613 match Cls.sealed_whitelist parent_type with
614 | None -> ()
615 | Some whitelist ->
616 let parent_pos = Cls.pos parent_type in
617 let parent_name = Cls.name parent_type in
618 let child_pos = Cls.pos child_type in
619 let child_name = Cls.name child_type in
620 let check kind action =
621 if not (SSet.mem child_name whitelist) then
622 Errors.extend_sealed child_pos parent_pos parent_name kind action
624 begin
625 match (Cls.kind parent_type, Cls.kind child_type) with
626 | (Ast_defs.Cinterface, Ast_defs.Cinterface) -> check "interface" "extend"
627 | (Ast_defs.Cinterface, _) -> check "interface" "implement"
628 | (Ast_defs.Ctrait, _) -> check "trait" "use"
629 | (Ast_defs.Cabstract, _)
630 | (Ast_defs.Cnormal, _) ->
631 check "class" "extend"
632 | (Ast_defs.Cenum, _) when is_enum_class -> check "enum class" "extend"
633 | (Ast_defs.Cenum, _) -> check "enum" "use"
636 let check_parents_sealed env child_def child_type =
637 let parents =
638 match child_def.c_enum with
639 | Some enum -> enum.e_includes
640 | None -> child_def.c_extends
642 let parents = parents @ child_def.c_implements @ child_def.c_uses in
643 let is_enum_class = Aast.is_enum_class child_def in
644 List.iter parents (function
645 | (_, Happly ((_, name), _)) ->
646 begin
647 match Env.get_class_dep env name with
648 | Some parent_type ->
649 check_parent_sealed ~is_enum_class child_type parent_type
650 | None -> ()
652 | _ -> ())
654 (* Reject multiple instantiations of the same generic interface
655 * in extends and implements clauses.
656 * e.g. disallow class C implements I<string>, I<int>
658 * O(n^2) but we don't expect number of instantiated interfaces to be large
660 let rec check_implements_or_extends_unique impl =
661 match impl with
662 | [] -> ()
663 | ty :: rest ->
664 (match get_node ty with
665 | Tapply ((pos, name), _ :: _) ->
666 let (pos_list, rest) =
667 List.partition_map rest (fun ty ->
668 match get_node ty with
669 | Tapply ((pos', name'), _) when String.equal name name' ->
670 `Fst pos'
671 | _ -> `Snd ty)
673 if not (List.is_empty pos_list) then
674 Errors.duplicate_interface pos name pos_list;
675 check_implements_or_extends_unique rest
676 | _ -> check_implements_or_extends_unique rest)
678 let check_cstr_dep env deps =
679 List.iter deps (fun dep ->
680 match deref dep with
681 | (_, Tapply ((_, class_name), _)) ->
682 Env.make_depend_on_constructor env class_name
683 | (r, Tgeneric _) ->
684 let p = Typing_reason.to_pos r in
685 Errors.expected_class ~suffix:" or interface but got a generic" p
686 | (r, _) ->
687 let p = Typing_reason.to_pos r in
688 Errors.expected_class ~suffix:" or interface" p)
690 let check_const_trait_members pos env use_list =
691 let (_, trait, _) = Decl_utils.unwrap_class_hint use_list in
692 match Env.get_class env trait with
693 | Some c when Ast_defs.(equal_class_kind (Cls.kind c) Ctrait) ->
694 List.iter (Cls.props c) (fun (x, ce) ->
695 if not (get_ce_const ce) then Errors.trait_prop_const_class pos x)
696 | _ -> ()
698 let check_consistent_enum_inclusion included_cls (dest_cls : Cls.t) =
699 match (Cls.enum_type included_cls, Cls.enum_type dest_cls) with
700 | (Some included_e, Some dest_e) ->
701 (* ensure that the base types are identical *)
702 if not (Typing_defs.equal_decl_ty included_e.te_base dest_e.te_base) then
703 Errors.incompatible_enum_inclusion_base
704 (Cls.pos dest_cls)
705 (Cls.name dest_cls)
706 (Cls.name included_cls);
707 (* ensure that the visibility constraint are compatible *)
708 (match (included_e.te_constraint, dest_e.te_constraint) with
709 | (None, Some _) ->
710 Errors.incompatible_enum_inclusion_constraint
711 (Cls.pos dest_cls)
712 (Cls.name dest_cls)
713 (Cls.name included_cls)
714 | (_, _) -> ());
715 (* ensure normal enums can't include enum classes *)
716 if included_e.te_enum_class && not dest_e.te_enum_class then
717 Errors.wrong_extend_kind
718 ~parent_pos:(Cls.pos included_cls)
719 ~parent_kind:Ast_defs.Cenum
720 ~parent_name:(Cls.name included_cls)
721 ~parent_is_enum_class:true
722 ~child_pos:(Cls.pos dest_cls)
723 ~child_kind:Ast_defs.Cenum
724 ~child_name:(Cls.name dest_cls)
725 ~child_is_enum_class:false
726 | (None, _) ->
727 Errors.enum_inclusion_not_enum
728 (Cls.pos dest_cls)
729 (Cls.name dest_cls)
730 (Cls.name included_cls)
731 | (_, _) -> ()
733 let check_enum_includes env cls =
734 (* checks that there are no duplicated enum-constants when folded-decls are enabled *)
735 if Ast_defs.is_c_enum cls.c_kind then (
736 let (dest_class_pos, dest_class_name) = cls.c_name in
737 let enum_constant_map = ref SMap.empty in
738 (* prepopulate the map with the constants declared in cls *)
739 List.iter cls.c_consts ~f:(fun cc ->
740 enum_constant_map :=
741 SMap.add
742 (snd cc.cc_id)
743 (fst cc.cc_id, dest_class_name)
744 !enum_constant_map);
745 (* for all included enums *)
746 let included_enums =
747 Aast.enum_includes_map cls.c_enum ~f:(fun ce ->
748 List.filter_map ce.e_includes ~f:(fun ie ->
749 match snd ie with
750 | Happly (sid, _) ->
751 (match Env.get_class env (snd sid) with
752 | None -> None
753 | Some ie_cls -> Some (fst ie, ie_cls))
754 | _ -> None))
756 List.iter included_enums ~f:(fun (ie_pos, ie_cls) ->
757 let src_class_name = Cls.name ie_cls in
758 (* 1. Check for consistency *)
759 (match Env.get_class env dest_class_name with
760 | None -> ()
761 | Some cls -> check_consistent_enum_inclusion ie_cls cls);
762 (* 2. Check for duplicates *)
763 List.iter (Cls.consts ie_cls) ~f:(fun (const_name, class_const) ->
764 ( if String.equal const_name "class" then
766 else if SMap.mem const_name !enum_constant_map then
767 (* distinguish between multiple inherit and redeclare *)
768 let (origin_const_pos, origin_class_name) =
769 SMap.find const_name !enum_constant_map
771 if String.equal origin_class_name dest_class_name then
772 (* redeclare *)
773 Errors.redeclaring_classish_const
774 dest_class_pos
775 dest_class_name
776 origin_const_pos
777 src_class_name
778 const_name
779 else
780 (* multiple inherit *)
781 Errors.reinheriting_classish_const
782 dest_class_pos
783 dest_class_name
784 ie_pos
785 src_class_name
786 origin_class_name
787 const_name );
788 enum_constant_map :=
789 SMap.add
790 const_name
791 (dest_class_pos, class_const.cc_origin)
792 !enum_constant_map))
795 let shallow_decl_enabled (ctx : Provider_context.t) : bool =
796 TypecheckerOptions.shallow_class_decl (Provider_context.get_tcopt ctx)
798 let class_type_param env ct =
799 let (env, tparam_list) = List.map_env env ct Typing.type_param in
800 (env, tparam_list)
802 (* This function sets a temporary coeffect context to check constants
803 * with the right one (either pure / write_props).
804 * We need to carefully restore the locals, otherwise the next continuation
805 * is just reset and the call to register_capabilities is a no-op, no
806 * capability is registered.
808 let expr_with_special_coeffects env ?expected e cap_ty unsafe_cap_ty =
809 let init =
810 Option.map (Env.next_cont_opt env) ~f:(fun next_cont ->
811 let initial_locals = next_cont.Typing_per_cont_env.local_types in
812 let tpenv = Env.get_tpenv env in
813 (initial_locals, tpenv))
815 let (env, (te, ty)) =
816 Typing_lenv.stash_and_do env (Env.all_continuations env) (fun env ->
817 let env =
818 match init with
819 | None -> env
820 | Some (initial_locals, tpenv) ->
821 let env = Env.reinitialize_locals env in
822 let env = Env.set_locals env initial_locals in
823 let env = Env.env_with_tpenv env tpenv in
826 let (env, _ty) =
827 Typing_coeffects.register_capabilities env cap_ty unsafe_cap_ty
829 let (env, te, ty) = Typing.expr ?expected env e in
830 (env, (te, ty)))
832 (env, te, ty)
834 (* Some (legacy) special functions are allowed as class constant init.,
835 therefore treat them as pure and insert the matching capabilities. *)
836 let expr_with_pure_coeffects env ?expected e =
837 let pure = MakeType.mixed (Reason.Rwitness (fst e)) in
838 expr_with_special_coeffects env ?expected e pure pure
840 (* Enum class constant initializers are restricted to be `write_props` *)
841 let expr_with_write_props_coeffects env ?expected e =
842 let e_pos = fst e in
843 let make_hint pos s = (pos, Aast.Happly ((pos, s), [])) in
844 let enum_class_ctx =
845 Some (e_pos, [make_hint e_pos SN.Capabilities.writeProperty])
847 let (env, cap_ty, unsafe_cap_ty) =
848 Typing.type_capability env enum_class_ctx enum_class_ctx e_pos
850 expr_with_special_coeffects env ?expected e cap_ty unsafe_cap_ty
852 (** Checks that a dynamic element is also dynamic in the parents. *)
853 let check_dynamic_class_element get_static_elt element_name dyn_pos ~elt_type =
854 (* The non-static properties that we get passed do not start with '$', but the
855 static properties we want to look up do, so add it. *)
856 let id =
857 match elt_type with
858 | `Method -> element_name
859 | `Property -> "$" ^ element_name
861 match get_static_elt id with
862 | None -> ()
863 | Some static_element ->
864 let (lazy ty) = static_element.ce_type in
865 Errors.static_redeclared_as_dynamic
866 dyn_pos
867 (get_pos ty)
868 element_name
869 ~elt_type
871 (** Checks that a static element is also static in the parents. *)
872 let check_static_class_element get_dyn_elt element_name static_pos ~elt_type =
873 (* The static properties that we get passed in start with '$', but the
874 non-static properties we're matching against don't, so we need to detect
875 that and remove it if present. *)
876 let element_name = String_utils.lstrip element_name "$" in
877 match get_dyn_elt element_name with
878 | None -> ()
879 | Some dyn_element ->
880 let (lazy ty) = dyn_element.ce_type in
881 Errors.dynamic_redeclared_as_static
882 static_pos
883 (get_pos ty)
884 element_name
885 ~elt_type
887 (** Error if there are abstract methods that this class is supposed to provide
888 implementation for. *)
889 let check_extend_abstract_meth ~is_final p seq =
890 List.iter seq (fun (x, ce) ->
891 match ce.ce_type with
892 | (lazy ty) when get_ce_abstract ce && is_fun ty ->
893 Errors.implement_abstract ~is_final p (get_pos ty) "method" x
894 | _ -> ())
896 let check_extend_abstract_prop ~is_final p seq =
897 List.iter seq (fun (x, ce) ->
898 if get_ce_abstract ce then
899 let ce_pos = Lazy.force ce.ce_type |> get_pos in
900 Errors.implement_abstract ~is_final p ce_pos "property" x)
902 (* Type constants must be bound to a concrete type for non-abstract classes.
904 let check_extend_abstract_typeconst ~is_final p seq =
905 List.iter seq (fun (x, tc) ->
906 if Option.is_none tc.ttc_type then
907 Errors.implement_abstract
908 ~is_final
910 (fst tc.ttc_name)
911 "type constant"
914 let check_extend_abstract_const ~is_final p seq =
915 List.iter seq (fun (x, cc) ->
916 if cc.cc_abstract && not cc.cc_synthesized then
917 let cc_pos = get_pos cc.cc_type in
918 Errors.implement_abstract ~is_final p cc_pos "constant" x)
920 exception Found of Pos.t
922 let contains_generic : Typing_defs.decl_ty -> Pos.t option =
923 fun ty ->
924 let visitor =
925 object
926 inherit [_] Type_visitor.decl_type_visitor as super
928 method! on_type env ty =
929 match get_node ty with
930 | Tgeneric _ -> raise (Found (get_pos ty))
931 | _ -> super#on_type env ty
935 visitor#on_type () ty;
936 None
937 with Found p -> Some p
939 let check_no_generic_static_property tc =
941 (* Check whether the type of a static property (class variable) contains
942 * any generic type parameters. Outside of traits, this is illegal as static
943 * properties are shared across all generic instantiations.
944 * Although not strictly speaking a variance check, it fits here because
945 * it concerns the presence of generic type parameters in types.
947 Ast_defs.(equal_class_kind (Cls.kind tc) Ctrait)
948 then
950 else
951 Cls.sprops tc
952 |> List.iter ~f:(fun (_prop_name, prop) ->
953 let (lazy ty) = prop.ce_type in
954 let var_type_pos = get_pos ty in
955 let class_pos = Cls.pos tc in
956 match contains_generic ty with
957 | None -> ()
958 | Some generic_pos ->
959 Errors.static_property_type_generic_param
960 ~class_pos
961 ~var_type_pos
962 ~generic_pos)
964 let get_decl_prop_ty env cls ~is_static prop_id =
965 let is_global_inference_on = TCO.global_inference (Env.get_tcopt env) in
966 if is_global_inference_on then
967 let prop_opt =
968 if is_static then
969 (* this is very ad-hoc, but this is how we do it in the decl-heap *)
970 Cls.get_sprop cls ("$" ^ prop_id)
971 else
972 Cls.get_prop cls prop_id
974 match prop_opt with
975 | None -> failwith "error: could not find property in decl heap"
976 | Some { ce_type; _ } -> Some (Lazy.force ce_type)
977 else
978 None
980 let typeconst_def
984 c_tconst_abstract;
985 c_tconst_name = (pos, _) as id;
986 c_tconst_as_constraint;
987 c_tconst_type = hint;
988 c_tconst_user_attributes;
989 c_tconst_span;
990 c_tconst_doc_comment;
991 c_tconst_is_ctx;
993 if Ast_defs.is_c_enum cls.c_kind then
994 Errors.cannot_declare_constant `enum pos cls.c_name;
995 let (env, cstr) =
996 opt Phase.localize_hint_with_self env c_tconst_as_constraint
998 let (env, ty) =
999 match hint with
1000 | None -> (env, None)
1001 | Some hint ->
1002 let ty = Decl_hint.hint env.decl_env hint in
1003 (* We want to report cycles through the definition *)
1004 let name = snd cls.c_name ^ "::" ^ snd id in
1005 let (env, ty) =
1006 Phase.localize_with_self env ~pos ~report_cycle:(pos, name) ty
1008 (env, Some ty)
1010 let check env t c =
1011 Type.sub_type pos Reason.URtypeconst_cstr env t c Errors.unify_error
1013 let env = Option.value ~default:env @@ Option.map2 ty cstr ~f:(check env) in
1014 let env =
1015 match hint with
1016 | Some (pos, Hshape { nsi_field_map; _ }) ->
1017 let get_name sfi = sfi.sfi_name in
1018 Typing.check_shape_keys_validity
1021 (List.map ~f:get_name nsi_field_map)
1022 | _ -> env
1024 let env =
1025 Typing.attributes_check_def
1027 SN.AttributeKinds.typeconst
1028 c_tconst_user_attributes
1030 let (env, user_attributes) =
1031 List.map_env env c_tconst_user_attributes Typing.user_attribute
1033 ( env,
1035 Aast.c_tconst_abstract;
1036 Aast.c_tconst_name = id;
1037 Aast.c_tconst_as_constraint;
1038 Aast.c_tconst_type = hint;
1039 Aast.c_tconst_user_attributes = user_attributes;
1040 Aast.c_tconst_span;
1041 Aast.c_tconst_doc_comment;
1042 Aast.c_tconst_is_ctx;
1045 (* This should agree with the set of expressions whose type can be inferred in
1046 * Decl_utils.infer_const
1048 let is_literal_expr e =
1049 match snd e with
1050 | String _
1051 | True
1052 | False
1053 | Int _
1054 | Float _
1055 | Null ->
1056 true
1057 | Unop ((Ast_defs.Uminus | Ast_defs.Uplus), (_, (Int _ | Float _))) -> true
1058 | _ -> false
1060 let class_const_def ~in_enum_class c env cc =
1061 let { cc_type = h; cc_id = id; cc_expr = e; _ } = cc in
1062 let (env, ty, opt_expected) =
1063 match h with
1064 | None ->
1065 begin
1066 match e with
1067 | None -> ()
1068 | Some e ->
1070 (not (is_literal_expr e))
1071 && Partial.should_check_error c.c_mode 2035
1072 && not Ast_defs.(equal_class_kind c.c_kind Cenum)
1073 then
1074 Errors.missing_typehint (fst id)
1075 end;
1076 let (env, ty) = Env.fresh_type env (fst id) in
1077 (env, MakeType.unenforced ty, None)
1078 | Some h ->
1079 let ty = Decl_hint.hint env.decl_env h in
1080 let ty = Typing_enforceability.compute_enforced_ty env ty in
1081 let (env, ty) = Phase.localize_possibly_enforced_with_self env ty in
1082 (* Removing the HH\MemberOf wrapper in case of enum classes so the
1083 * following call to expr_* has the right expected type
1085 let opt_ty =
1086 if in_enum_class then
1087 match get_node ty.et_type with
1088 | Tnewtype (memberof, [_; et_type], _)
1089 when String.equal memberof SN.Classes.cMemberOf ->
1090 { ty with et_type }
1091 | _ -> ty
1092 else
1095 ( env,
1097 Some (ExpectedTy.make_and_allow_coercion (fst id) Reason.URhint opt_ty)
1100 let (env, eopt, ty) =
1101 match e with
1102 | Some e ->
1103 let (env, te, ty') =
1104 if in_enum_class then
1105 expr_with_write_props_coeffects env ?expected:opt_expected e
1106 else
1107 expr_with_pure_coeffects env ?expected:opt_expected e
1109 (* If we are checking an enum class, wrap ty' into the right
1110 * HH\MemberOf<class name, ty'> alias
1112 let (te, ty') =
1113 if in_enum_class then
1114 match deref ty.et_type with
1115 | (r, Tnewtype (memberof, [enum_name; _], _))
1116 when String.equal memberof SN.Classes.cMemberOf ->
1117 let lift r ty = mk (r, Tnewtype (memberof, [enum_name; ty], ty)) in
1118 let ((p, te_ty), te) = te in
1119 let te = ((p, lift (get_reason te_ty) te_ty), te) in
1120 let ty' = lift r ty' in
1121 (te, ty')
1122 | _ -> (te, ty')
1123 else
1124 (te, ty')
1126 let env =
1127 Typing_coercion.coerce_type
1128 (fst id)
1129 Reason.URhint
1133 Errors.class_constant_value_does_not_match_hint
1135 (env, Some te, ty')
1136 | None -> (env, None, ty.et_type)
1138 ( env,
1140 Aast.cc_type = cc.cc_type;
1141 Aast.cc_id = cc.cc_id;
1142 Aast.cc_expr = eopt;
1143 Aast.cc_doc_comment = cc.cc_doc_comment;
1145 ty ) )
1147 let class_constr_def env cls constructor =
1148 let env = { env with inside_constructor = true } in
1149 Option.bind constructor (method_def env cls)
1151 let class_implements_type env implements c1 ctype2 =
1152 let params =
1153 List.map c1.c_tparams (fun { tp_name = (p, s); _ } ->
1154 mk (Reason.Rwitness p, Tgeneric (s, [])))
1156 let r = Reason.Rwitness (fst c1.c_name) in
1157 let ctype1 = mk (r, Tapply (c1.c_name, params)) in
1158 Typing_extends.check_implements env implements ctype2 ctype1
1160 (** Type-check a property declaration, with optional initializer *)
1161 let class_var_def ~is_static cls env cv =
1162 (* First pick up and localize the hint if it exists *)
1163 let decl_cty =
1164 merge_hint_with_decl_hint
1166 (hint_of_type_hint cv.cv_type)
1167 (get_decl_prop_ty env cls ~is_static (snd cv.cv_id))
1169 let (env, expected) =
1170 match decl_cty with
1171 | None -> (env, None)
1172 | Some decl_cty ->
1173 let decl_cty = Typing_enforceability.compute_enforced_ty env decl_cty in
1174 let (env, cty) =
1175 Phase.localize_possibly_enforced_with_self env decl_cty
1177 let expected =
1178 Some (ExpectedTy.make_and_allow_coercion cv.cv_span Reason.URhint cty)
1180 (env, expected)
1182 (* Next check the expression, passing in expected type if present *)
1183 let (env, typed_cv_expr) =
1184 match cv.cv_expr with
1185 | None -> (env, None)
1186 | Some e ->
1187 let (env, te, ty) = expr_with_pure_coeffects env ?expected e in
1188 (* Check that the inferred type is a subtype of the expected type.
1189 * Eventually this will be the responsibility of `expr`
1191 let env =
1192 match expected with
1193 | None -> env
1194 | Some ExpectedTy.{ pos = p; reason = ur; ty = cty } ->
1195 Typing_coercion.coerce_type
1201 Errors.class_property_initializer_type_does_not_match_hint
1203 (env, Some te)
1206 let env =
1207 if is_static then
1208 Typing.attributes_check_def
1210 SN.AttributeKinds.staticProperty
1211 cv.cv_user_attributes
1212 else
1213 Typing.attributes_check_def
1215 SN.AttributeKinds.instProperty
1216 cv.cv_user_attributes
1218 let (env, user_attributes) =
1219 List.map_env env cv.cv_user_attributes Typing.user_attribute
1222 Option.is_none (hint_of_type_hint cv.cv_type)
1223 && Partial.should_check_error (Env.get_mode env) 2001
1224 then
1225 Errors.prop_without_typehint
1226 (string_of_visibility cv.cv_visibility)
1227 cv.cv_id;
1228 let (env, global_inference_env) = Env.extract_global_inference_env env in
1229 let cv_type =
1230 match expected with
1231 | Some expected ->
1232 (expected.ExpectedTy.ty.et_type, hint_of_type_hint cv.cv_type)
1233 | None -> Tast.dummy_type_hint (hint_of_type_hint cv.cv_type)
1235 (* if the class implements dynamic, then check that the type of the property
1236 * is enforceable (for writing) and coerces to dynamic (for reading) *)
1238 TypecheckerOptions.enable_sound_dynamic
1239 (Provider_context.get_tcopt (Env.get_ctx env))
1240 && Cls.get_implements_dynamic cls
1241 then begin
1242 Option.iter decl_cty (fun ty ->
1243 let te_check = Typing_enforceability.is_enforceable env ty in
1244 if not te_check then
1245 Errors.property_is_not_enforceable
1246 (fst cv.cv_id)
1247 (snd cv.cv_id)
1248 (Cls.name cls));
1251 (Typing_subtype.is_sub_type_for_union
1252 ~coerce:(Some Typing_logic.CoerceToDynamic)
1254 (fst cv_type)
1255 (mk (Reason.Rnone, Tdynamic)))
1256 then
1257 Errors.property_is_not_dynamic
1258 (fst cv.cv_id)
1259 (snd cv.cv_id)
1260 (Cls.name cls)
1261 end;
1262 ( env,
1264 Aast.cv_final = cv.cv_final;
1265 Aast.cv_xhp_attr = cv.cv_xhp_attr;
1266 Aast.cv_abstract = cv.cv_abstract;
1267 Aast.cv_visibility = cv.cv_visibility;
1268 Aast.cv_type;
1269 Aast.cv_id = cv.cv_id;
1270 Aast.cv_expr = typed_cv_expr;
1271 Aast.cv_user_attributes = user_attributes;
1272 Aast.cv_is_promoted_variadic = cv.cv_is_promoted_variadic;
1273 Aast.cv_doc_comment = cv.cv_doc_comment;
1274 (* Can make None to save space *)
1275 Aast.cv_is_static = is_static;
1276 Aast.cv_span = cv.cv_span;
1277 Aast.cv_readonly = cv.cv_readonly;
1279 (cv.cv_span, global_inference_env) ) )
1281 let class_def_ env c tc =
1282 let env =
1283 let kind =
1284 match c.c_kind with
1285 | Ast_defs.Cenum ->
1286 (match c.c_enum with
1287 | Some enum when enum.e_enum_class -> SN.AttributeKinds.enumcls
1288 | _ -> SN.AttributeKinds.enum)
1289 | _ -> SN.AttributeKinds.cls
1291 Typing.attributes_check_def env kind c.c_user_attributes
1293 let (env, file_attrs) = Typing.file_attributes env c.c_file_attributes in
1294 let ctx = Env.get_ctx env in
1296 ( Ast_defs.(equal_class_kind c.c_kind Cnormal)
1297 || Ast_defs.(equal_class_kind c.c_kind Cabstract) )
1298 && not (shallow_decl_enabled ctx)
1299 then (
1300 (* These checks are only for eager mode. The same checks are performed
1301 * for shallow mode in Typing_inheritance *)
1302 let method_pos ~is_static class_id meth_id =
1303 let get_meth =
1304 if is_static then
1305 Decl_heap.StaticMethods.get
1306 else
1307 Decl_heap.Methods.get
1309 match get_meth (class_id, meth_id) with
1310 | Some { fe_pos; _ } -> fe_pos
1311 | None -> Pos.none
1313 let check_override ~is_static (id, ce) =
1314 if get_ce_override ce then
1315 let pos = method_pos ~is_static ce.ce_origin id in
1316 (* Method is actually defined in this class *)
1317 if String.equal ce.ce_origin (snd c.c_name) then
1318 Errors.should_be_override pos (snd c.c_name) id
1319 else
1320 match Env.get_class env ce.ce_origin with
1321 | None -> ()
1322 | Some parent_class ->
1323 (* If it's not defined here, then either it's inherited (so we have emitted an error already)
1324 * or it's in a trait, and so we need to emit the error now *)
1325 if not Ast_defs.(equal_class_kind (Cls.kind parent_class) Ctrait)
1326 then
1328 else
1329 Errors.override_per_trait c.c_name id ce.ce_origin pos
1332 List.iter (Cls.methods tc) (check_override ~is_static:false);
1333 List.iter (Cls.smethods tc) (check_override ~is_static:true)
1335 check_enum_includes env c;
1336 let (pc, c_name) = c.c_name in
1337 let (req_extends, req_implements) = split_reqs c in
1338 let extends = List.map c.c_extends (Decl_hint.hint env.decl_env) in
1339 let implements = List.map c.c_implements (Decl_hint.hint env.decl_env) in
1340 let uses = List.map c.c_uses (Decl_hint.hint env.decl_env) in
1341 let req_extends = List.map req_extends (Decl_hint.hint env.decl_env) in
1342 let req_implements = List.map req_implements (Decl_hint.hint env.decl_env) in
1343 let additional_parents =
1344 (* In an abstract class or a trait, we assume the interfaces
1345 will be implemented in the future, so we take them as
1346 part of the class (as requested by dependency injection implementers) *)
1347 match c.c_kind with
1348 | Ast_defs.Cabstract -> implements
1349 | Ast_defs.Ctrait -> implements @ req_implements
1350 | _ -> []
1352 let check_cstr_dep = check_cstr_dep env in
1353 check_implements_or_extends_unique implements;
1354 check_implements_or_extends_unique extends;
1355 check_cstr_dep extends;
1356 check_cstr_dep uses;
1357 check_cstr_dep req_extends;
1358 check_cstr_dep additional_parents;
1359 begin
1360 match c.c_enum with
1361 | Some e ->
1362 check_cstr_dep (List.map e.e_includes (Decl_hint.hint env.decl_env))
1363 | _ -> ()
1364 end;
1365 let impl = extends @ implements @ uses in
1366 let env =
1367 Phase.localize_and_add_ast_generic_parameters_and_where_constraints
1370 c.c_tparams
1371 c.c_where_constraints
1373 let env =
1374 Phase.check_where_constraints
1375 ~in_class:true
1376 ~use_pos:pc
1377 ~definition_pos:pc
1378 ~ety_env:(Phase.env_with_self env)
1380 (Cls.where_constraints tc)
1382 Typing_variance.class_def env c;
1383 check_no_generic_static_property tc;
1384 let check_where_constraints env ht =
1385 let (_, (p, _), _) = TUtils.unwrap_class_type ht in
1386 let (env, locl_ty) = Phase.localize_with_self env ht in
1387 match get_node (TUtils.get_base_type env locl_ty) with
1388 | Tclass (cls, _, tyl) ->
1389 (match Env.get_class env (snd cls) with
1390 | Some cls when not (List.is_empty (Cls.where_constraints cls)) ->
1391 let tc_tparams = Cls.tparams cls in
1392 let ety_env =
1394 (Phase.env_with_self env) with
1395 substs = Subst.make_locl tc_tparams tyl;
1398 Phase.check_where_constraints
1399 ~in_class:true
1400 ~use_pos:pc
1401 ~definition_pos:p
1402 ~ety_env
1404 (Cls.where_constraints cls)
1405 | _ -> env)
1406 | _ -> env
1408 let env = List.fold impl ~init:env ~f:check_where_constraints in
1409 check_parent env c tc;
1410 check_parents_sealed env c tc;
1412 let is_final = Cls.final tc in
1414 (Ast_defs.(equal_class_kind (Cls.kind tc) Cnormal) || is_final)
1415 && Cls.members_fully_known tc
1416 then (
1417 check_extend_abstract_meth ~is_final pc (Cls.methods tc);
1418 (match fst (Cls.construct tc) with
1419 | Some constr ->
1420 check_extend_abstract_meth ~is_final pc [(SN.Members.__construct, constr)]
1421 | None -> ());
1422 check_extend_abstract_meth ~is_final pc (Cls.smethods tc);
1423 check_extend_abstract_prop ~is_final pc (Cls.sprops tc);
1424 check_extend_abstract_const ~is_final pc (Cls.consts tc);
1425 check_extend_abstract_typeconst ~is_final pc (Cls.typeconsts tc)
1427 if Cls.const tc then List.iter c.c_uses (check_const_trait_members pc env);
1428 let (static_vars, vars) = split_vars c in
1429 List.iter static_vars ~f:(fun { cv_id = (p, id); _ } ->
1430 check_static_class_element (Cls.get_prop tc) ~elt_type:`Property id p);
1431 List.iter vars ~f:(fun { cv_id = (p, id); _ } ->
1432 check_dynamic_class_element (Cls.get_sprop tc) ~elt_type:`Property id p);
1433 let (constructor, static_methods, methods) = split_methods c in
1434 List.iter static_methods ~f:(fun { m_name = (p, id); _ } ->
1435 check_static_class_element (Cls.get_method tc) ~elt_type:`Method id p);
1436 List.iter methods ~f:(fun { m_name = (p, id); _ } ->
1437 check_dynamic_class_element (Cls.get_smethod tc) ~elt_type:`Method id p);
1438 let env =
1439 List.fold ~init:env impl ~f:(fun env ->
1440 class_implements_type env implements c)
1442 if Cls.is_disposable tc then
1443 List.iter
1444 (c.c_extends @ c.c_uses)
1445 (Typing_disposable.enforce_is_disposable env);
1446 let (env, typed_vars_and_global_inference_envs) =
1447 List.map_env env vars (class_var_def ~is_static:false tc)
1449 let (typed_vars, vars_global_inference_envs) =
1450 List.unzip typed_vars_and_global_inference_envs
1452 let (typed_methods, methods_global_inference_envs) =
1453 List.filter_map methods (method_def env tc) |> List.unzip
1455 let (env, typed_typeconsts) =
1456 List.map_env env c.c_typeconsts (typeconst_def c)
1458 let in_enum_class = Env.is_enum_class env c_name in
1459 let (env, consts) =
1460 List.map_env env c.c_consts (class_const_def ~in_enum_class c)
1462 let (typed_consts, const_types) = List.unzip consts in
1463 let env = Typing_enum.enum_class_check env tc c.c_consts const_types in
1464 let typed_constructor = class_constr_def env tc constructor in
1465 let env = Env.set_static env in
1466 let (env, typed_static_vars_and_global_inference_envs) =
1467 List.map_env env static_vars (class_var_def ~is_static:true tc)
1469 let (typed_static_vars, static_vars_global_inference_envs) =
1470 List.unzip typed_static_vars_and_global_inference_envs
1472 let (typed_static_methods, static_methods_global_inference_envs) =
1473 List.filter_map static_methods (method_def env tc) |> List.unzip
1475 let (methods, constr_global_inference_env) =
1476 match typed_constructor with
1477 | None -> (typed_static_methods @ typed_methods, [])
1478 | Some (m, global_inference_env) ->
1479 ((m :: typed_static_methods) @ typed_methods, [global_inference_env])
1481 let (env, tparams) = class_type_param env c.c_tparams in
1482 let (env, user_attributes) =
1483 List.map_env env c.c_user_attributes Typing.user_attribute
1485 let env =
1486 Typing_solver.solve_all_unsolved_tyvars env Errors.bad_class_typevar
1489 ( if TypecheckerOptions.enable_sound_dynamic (Provider_context.get_tcopt ctx)
1490 then
1491 let parent_names =
1492 List.filter_map
1493 (c.c_extends @ c.c_uses @ c.c_implements)
1494 (function
1495 | (_, Happly ((_, name), _)) -> Some name
1496 | _ -> None)
1498 let error_parent_implements_dynamic parent f =
1499 Errors.parent_implements_dynamic
1500 (fst c.c_name)
1501 (snd c.c_name, c.c_kind)
1502 (Cls.name parent, Cls.kind parent)
1505 List.iter parent_names (fun name ->
1506 match Env.get_class_dep env name with
1507 | Some parent_type ->
1508 begin
1509 match Cls.kind parent_type with
1510 | Ast_defs.Cnormal
1511 | Ast_defs.Cabstract ->
1514 (Bool.equal
1515 (Cls.get_implements_dynamic parent_type)
1516 c.c_implements_dynamic)
1517 then
1518 error_parent_implements_dynamic
1519 parent_type
1520 c.c_implements_dynamic
1521 | Ast_defs.Ctrait ->
1523 c.c_implements_dynamic
1524 && not (Cls.get_implements_dynamic parent_type)
1525 then
1526 error_parent_implements_dynamic parent_type true
1527 | Ast_defs.Cinterface ->
1529 (not c.c_implements_dynamic)
1530 && Cls.get_implements_dynamic parent_type
1531 then
1532 error_parent_implements_dynamic parent_type false
1533 else if
1534 Ast_defs.is_c_interface c.c_kind
1535 && not
1536 (Bool.equal
1537 (Cls.get_implements_dynamic parent_type)
1538 c.c_implements_dynamic)
1539 then
1540 error_parent_implements_dynamic
1541 parent_type
1542 c.c_implements_dynamic
1543 | _ -> ()
1545 | None -> ()) );
1548 Aast.c_span = c.c_span;
1549 Aast.c_annotation = Env.save (Env.get_tpenv env) env;
1550 Aast.c_mode = c.c_mode;
1551 Aast.c_final = c.c_final;
1552 Aast.c_is_xhp = c.c_is_xhp;
1553 Aast.c_has_xhp_keyword = c.c_has_xhp_keyword;
1554 Aast.c_kind = c.c_kind;
1555 Aast.c_name = c.c_name;
1556 Aast.c_tparams = tparams;
1557 Aast.c_extends = c.c_extends;
1558 Aast.c_uses = c.c_uses;
1559 (* c_use_as_alias and c_insteadof_alias are PHP features not supported
1560 * in Hack but are required since we have runtime support for it
1562 Aast.c_use_as_alias = [];
1563 Aast.c_insteadof_alias = [];
1564 Aast.c_xhp_attr_uses = c.c_xhp_attr_uses;
1565 Aast.c_xhp_category = c.c_xhp_category;
1566 Aast.c_reqs = c.c_reqs;
1567 Aast.c_implements = c.c_implements;
1568 Aast.c_implements_dynamic = c.c_implements_dynamic;
1569 Aast.c_where_constraints = c.c_where_constraints;
1570 Aast.c_consts = typed_consts;
1571 Aast.c_typeconsts = typed_typeconsts;
1572 Aast.c_vars = typed_static_vars @ typed_vars;
1573 Aast.c_methods = methods;
1574 Aast.c_file_attributes = file_attrs;
1575 Aast.c_user_attributes = user_attributes;
1576 Aast.c_namespace = c.c_namespace;
1577 Aast.c_enum = c.c_enum;
1578 Aast.c_doc_comment = c.c_doc_comment;
1579 Aast.c_attributes = [];
1580 Aast.c_xhp_children = c.c_xhp_children;
1581 Aast.c_xhp_attrs = [];
1582 Aast.c_emit_id = c.c_emit_id;
1584 methods_global_inference_envs
1585 @ static_methods_global_inference_envs
1586 @ constr_global_inference_env
1587 @ static_vars_global_inference_envs
1588 @ vars_global_inference_envs )
1590 let class_def ctx c =
1591 Counters.count Counters.Category.Typecheck @@ fun () ->
1592 Errors.run_with_span c.c_span @@ fun () ->
1593 let env = EnvFromDef.class_env ~origin:Decl_counters.TopLevel ctx c in
1594 let tc = Env.get_class env (snd c.c_name) in
1595 let env = Env.set_env_pessimize env in
1596 Typing_helpers.add_decl_errors (Option.bind tc Cls.decl_errors);
1597 Typing_check_decls.class_ env c;
1598 NastInitCheck.class_ env c;
1599 match tc with
1600 | None ->
1601 (* This can happen if there was an error during the declaration
1602 * of the class. *)
1603 None
1604 | Some tc ->
1605 (* If there are duplicate definitions of the class then we will end up
1606 * checking one AST with respect to the decl corresponding to the other definition.
1607 * Naming has already detected duplicates, so let's just avoid cascading unhelpful
1608 * typing errors, and also avoid triggering the bad position assert
1610 if not (Pos.equal (fst c.c_name) (Cls.pos tc)) then
1611 None
1612 else
1613 let env = Typing_requirements.check_class env tc in
1614 if shallow_decl_enabled ctx then Typing_inheritance.check_class env tc;
1615 Some (class_def_ env c tc)
1617 let gconst_def ctx cst =
1618 Counters.count Counters.Category.Typecheck @@ fun () ->
1619 Errors.run_with_span cst.cst_span @@ fun () ->
1620 let env = EnvFromDef.gconst_env ~origin:Decl_counters.TopLevel ctx cst in
1621 let env = Env.set_env_pessimize env in
1622 let (typed_cst_value, env) =
1623 let value = cst.cst_value in
1624 match cst.cst_type with
1625 | Some hint ->
1626 let ty = Decl_hint.hint env.decl_env hint in
1627 let ty = Typing_enforceability.compute_enforced_ty env ty in
1628 let (env, dty) = Phase.localize_possibly_enforced_with_self env ty in
1629 let (env, te, value_type) =
1630 let expected =
1631 ExpectedTy.make_and_allow_coercion (fst hint) Reason.URhint dty
1633 expr_with_pure_coeffects env ~expected value
1635 let env =
1636 Typing_coercion.coerce_type
1637 (fst hint)
1638 Reason.URhint
1640 value_type
1642 Errors.unify_error
1644 (te, env)
1645 | None ->
1647 (not (is_literal_expr value))
1648 && Partial.should_check_error cst.cst_mode 2035
1649 then
1650 Errors.missing_typehint (fst cst.cst_name);
1651 let (env, te, _value_type) = expr_with_pure_coeffects env value in
1652 (te, env)
1655 Aast.cst_annotation = Env.save (Env.get_tpenv env) env;
1656 Aast.cst_mode = cst.cst_mode;
1657 Aast.cst_name = cst.cst_name;
1658 Aast.cst_type = cst.cst_type;
1659 Aast.cst_value = typed_cst_value;
1660 Aast.cst_namespace = cst.cst_namespace;
1661 Aast.cst_span = cst.cst_span;
1662 Aast.cst_emit_id = cst.cst_emit_id;
1665 let record_field env f =
1666 let (id, hint, e) = f in
1667 let ((p, _) as cty) = hint in
1668 let (env, cty) =
1669 let cty = Decl_hint.hint env.decl_env cty in
1670 Phase.localize_with_self env cty
1672 let expected = ExpectedTy.make p Reason.URhint cty in
1673 match e with
1674 | Some e ->
1675 let (env, te, ty) = expr_with_pure_coeffects env ~expected e in
1676 let env =
1677 Typing_coercion.coerce_type
1679 Reason.URhint
1682 (MakeType.unenforced cty)
1683 Errors.record_init_value_does_not_match_hint
1685 (env, (id, hint, Some te))
1686 | None -> (env, (id, hint, None))
1688 let record_def_parent env rd parent_hint =
1689 match snd parent_hint with
1690 | Aast.Happly ((parent_pos, parent_name), []) ->
1691 (match Decl_provider.get_record_def (Env.get_ctx env) parent_name with
1692 | Some parent_rd ->
1693 (* We can only inherit from abstract records. *)
1694 ( if not parent_rd.rdt_abstract then
1695 let (parent_pos, parent_name) = parent_rd.rdt_name in
1696 Errors.extend_non_abstract_record
1697 parent_name
1698 (fst rd.rd_name)
1699 parent_pos );
1701 (* Ensure we aren't defining fields that overlap with
1702 inherited fields. *)
1703 let inherited_fields = Typing_helpers.all_record_fields env parent_rd in
1704 List.iter rd.rd_fields ~f:(fun ((pos, name), _, _) ->
1705 match SMap.find_opt name inherited_fields with
1706 | Some ((prev_pos, _), _) ->
1707 Errors.repeated_record_field name pos prev_pos
1708 | None -> ())
1709 | None ->
1710 (* Something exists with this name (naming succeeded), but it's
1711 not a record. *)
1712 Errors.unbound_name parent_pos parent_name Errors.RecordContext)
1713 | _ ->
1714 failwith
1715 "Record parent was not an Happly. This should have been a syntax error."
1717 (* Report an error if we have inheritance cycles in record declarations. *)
1718 let check_record_inheritance_cycle env ((rd_pos, rd_name) : Aast.sid) : unit =
1719 let rec worker name trace seen =
1720 match Decl_provider.get_record_def (Env.get_ctx env) name with
1721 | Some rd ->
1722 (match rd.rdt_extends with
1723 | Some (_, parent_name) when String.equal parent_name rd_name ->
1724 (* This record is in an inheritance cycle.*)
1725 Errors.cyclic_record_def trace rd_pos
1726 | Some (_, parent_name) when SSet.mem parent_name seen ->
1727 (* There's an inheritance cycle higher in the chain. *)
1729 | Some (_, parent_name) ->
1730 worker parent_name (parent_name :: trace) (SSet.add parent_name seen)
1731 | None -> ())
1732 | None -> ()
1734 worker rd_name [rd_name] (SSet.singleton rd_name)
1736 let record_def_def ctx rd =
1737 Counters.count Counters.Category.Typecheck @@ fun () ->
1738 let env = EnvFromDef.record_def_env ~origin:Decl_counters.TopLevel ctx rd in
1739 (match rd.rd_extends with
1740 | Some parent -> record_def_parent env rd parent
1741 | None -> ());
1743 check_record_inheritance_cycle env rd.rd_name;
1745 let (env, attributes) =
1746 List.map_env env rd.rd_user_attributes Typing.user_attribute
1748 let (_env, fields) = List.map_env env rd.rd_fields record_field in
1750 Aast.rd_annotation = Env.save (Env.get_tpenv env) env;
1751 Aast.rd_name = rd.rd_name;
1752 Aast.rd_extends = rd.rd_extends;
1753 Aast.rd_abstract = rd.rd_abstract;
1754 Aast.rd_fields = fields;
1755 Aast.rd_user_attributes = attributes;
1756 Aast.rd_namespace = rd.rd_namespace;
1757 Aast.rd_span = rd.rd_span;
1758 Aast.rd_doc_comment = rd.rd_doc_comment;
1759 Aast.rd_emit_id = rd.rd_emit_id;
1762 let nast_to_tast_gienv ~(do_tast_checks : bool) ctx nast :
1763 _ * Typing_inference_env.t_global_with_pos list =
1764 let convert_def = function
1765 (* Sometimes typing will just return `None` but that should only be the case
1766 * if an error had already been registered e.g. in naming
1768 | Fun f ->
1769 begin
1770 match fun_def ctx f with
1771 | Some (f, env) -> Some (Aast.Fun f, [env])
1772 | None -> None
1774 | Constant gc -> Some (Aast.Constant (gconst_def ctx gc), [])
1775 | Typedef td -> Some (Aast.Typedef (Typing.typedef_def ctx td), [])
1776 | Class c ->
1777 begin
1778 match class_def ctx c with
1779 | Some (c, envs) -> Some (Aast.Class c, envs)
1780 | None -> None
1782 | RecordDef rd -> Some (Aast.RecordDef (record_def_def ctx rd), [])
1783 (* We don't typecheck top level statements:
1784 * https://docs.hhvm.com/hack/unsupported/top-level
1785 * so just create the minimal env for us to construct a Stmt.
1787 | Stmt s ->
1788 let env = Env.empty ctx Relative_path.default None in
1789 Some (Aast.Stmt (snd (Typing.stmt env s)), [])
1790 | Namespace _
1791 | NamespaceUse _
1792 | SetNamespaceEnv _
1793 | FileAttributes _ ->
1794 failwith
1795 "Invalid nodes in NAST. These nodes should be removed during naming."
1797 Nast_check.program ctx nast;
1798 let (tast, envs) = List.unzip @@ List.filter_map nast convert_def in
1799 let envs = List.concat envs in
1800 if do_tast_checks then Tast_check.program ctx tast;
1801 (tast, envs)
1803 let nast_to_tast ~do_tast_checks ctx nast =
1804 let (tast, _gienvs) = nast_to_tast_gienv ~do_tast_checks ctx nast in
1805 tast