Fix bug with enforcing purity in constant initializers
[hiphop-php.git] / hphp / hack / src / typing / typing_toplevel.ml
blob236a3d7a579bc3322f00a3a8173fa76d8329b0e3
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 let param_has_at_most_rx_as_func p =
29 let module UA = SN.UserAttributes in
30 Naming_attributes.mem UA.uaAtMostRxAsFunc p.param_user_attributes
32 let fun_reactivity env attrs params =
33 let r = FunUtils.fun_reactivity env attrs in
34 let module UA = Naming_special_names.UserAttributes in
35 let r =
36 (* if at least one of parameters has <<__AtMostRxAsFunc>> attribute -
37 treat function reactivity as generic that is determined from the reactivity
38 of arguments annotated with __AtMostRxAsFunc. Declared reactivity is used as a
39 upper boundary of the reactivity function can have. *)
40 if List.exists params ~f:param_has_at_most_rx_as_func then
41 RxVar (Some r)
42 else
45 let r =
46 (* if at least one of arguments have <<__OnlyRxIfImpl>> attribute -
47 treat function reactivity as conditional that is determined at the callsite *)
49 List.exists params ~f:(fun { param_user_attributes = p; _ } ->
50 Naming_attributes.mem UA.uaOnlyRxIfImpl p)
51 then
52 MaybeReactive r
53 else
58 (* The two following functions enable us to retrieve the function (or class)
59 header from the shared mem. Note that they only return a non None value if
60 global inference is on *)
61 let get_decl_function_header env function_id =
62 let is_global_inference_on = TCO.global_inference (Env.get_tcopt env) in
63 if is_global_inference_on then
64 match Decl_provider.get_fun (Env.get_ctx env) function_id with
65 | Some { fe_type; _ } ->
66 begin
67 match get_node fe_type with
68 | Tfun fun_type -> Some fun_type
69 | _ -> None
70 end
71 | _ -> None
72 else
73 None
75 and get_decl_method_header tcopt cls method_id ~is_static =
76 let is_global_inference_on = TCO.global_inference tcopt in
77 if is_global_inference_on then
78 match Cls.get_any_method ~is_static cls method_id with
79 | Some { ce_type = (lazy ty); _ } ->
80 begin
81 match get_node ty with
82 | Tfun fun_type -> Some fun_type
83 | _ -> None
84 end
85 | _ -> None
86 else
87 None
89 let enforce_param_not_disposable env param ty =
90 if has_accept_disposable_attribute param then
92 else
93 let p = param.param_pos in
94 match Typing_disposable.is_disposable_type env ty with
95 | Some class_name -> Errors.invalid_disposable_hint p (strip_ns class_name)
96 | None -> ()
98 (* In strict mode, we force you to give a type declaration on a parameter *)
99 (* But the type checker is nice: it makes a suggestion :-) *)
100 let check_param_has_hint env param ty is_code_error =
101 let env =
102 if is_code_error 4231 then
103 Typing.attributes_check_def
105 SN.AttributeKinds.parameter
106 param.param_user_attributes
107 else
110 match hint_of_type_hint param.param_type_hint with
111 | None when param.param_is_variadic && is_code_error 4033 ->
112 Errors.expecting_type_hint_variadic param.param_pos
113 | None when is_code_error 4032 -> Errors.expecting_type_hint param.param_pos
114 | Some _ when is_code_error 4010 ->
115 (* We do not permit hints to implement IDisposable or IAsyncDisposable *)
116 enforce_param_not_disposable env param ty
117 | _ -> ()
119 (* This function is used to determine the type of an argument.
120 * When we want to type-check the body of a function, we need to
121 * introduce the type of the arguments of the function in the environment
122 * Let's take an example, we want to check the code of foo:
124 * function foo(int $x): int {
125 * // CALL TO make_param_type on (int $x)
126 * // Now we know that the type of $x is int
128 * return $x; // in the environment $x is an int, the code is correct
131 * When we localize, we want to resolve to "static" or "$this" depending on
132 * the context. Even though we are passing in CIstatic, resolve_with_class_id
133 * is smart enough to know what to do. Why do this? Consider the following
135 * abstract class C {
136 * abstract const type T;
138 * private this::T $val;
140 * final public function __construct(this::T $x) {
141 * $this->val = $x;
144 * public static function create(this::T $x): this {
145 * return new static($x);
149 * class D extends C { const type T = int; }
151 * In __construct() we want to be able to assign $x to $this->val. The type of
152 * $this->val will expand to '$this::T', so we need $x to also be '$this::T'.
153 * We can do this soundly because when we construct a new class such as,
154 * 'new D(0)' we can determine the late static bound type (D) and resolve
155 * 'this::T' to 'D::T' which is int.
157 * A similar line of reasoning is applied for the static method create.
159 let make_param_local_ty env decl_hint param =
160 let ety_env = Phase.env_with_self env in
161 let r = Reason.Rwitness param.param_pos in
162 let (env, ty) =
163 match decl_hint with
164 | None -> (env, mk (r, TUtils.tany env))
165 | Some ty ->
166 let { et_type = ty; _ } =
167 Typing_enforceability.compute_enforced_and_pessimize_ty
168 ~explicitly_untrusted:param.param_is_variadic
172 let condition_type =
173 FunUtils.condition_type_from_attributes
174 env.decl_env
175 param.param_user_attributes
177 begin
178 match condition_type with
179 | Some condition_type ->
180 let (env, ty) = Phase.localize ~ety_env env ty in
181 begin
182 match
183 TR.try_substitute_type_with_condition env condition_type ty
184 with
185 | Some r -> r
186 | None -> (env, ty)
189 when Naming_attributes.mem
190 SN.UserAttributes.uaAtMostRxAsFunc
191 param.param_user_attributes ->
192 let (env, ty) = Phase.localize ~ety_env env ty in
193 (* expand type to track aliased function types *)
194 let (env, expanded_ty) = Env.expand_type env ty in
195 let adjusted_ty = make_function_type_rxvar expanded_ty in
196 ( env,
197 if phys_equal adjusted_ty expanded_ty then
199 else
200 adjusted_ty )
201 | _ -> Phase.localize ~ety_env env ty
204 let ty =
205 match get_node ty with
206 | t when param.param_is_variadic ->
207 (* when checking the body of a function with a variadic
208 * argument, "f(C ...$args)", $args is a varray<C> *)
209 let r = Reason.Rvar_param param.param_pos in
210 let arr_values = mk (r, t) in
211 mk (r, Tvarray arr_values)
212 | _ -> ty
214 Typing_reactivity.disallow_atmost_rx_as_rxfunc_on_non_functions env param ty;
215 (env, ty)
217 let get_callable_variadicity
218 ?(is_function = false) ~partial_callback ~pos env variadicity_decl_ty =
219 function
220 | FVvariadicArg vparam ->
221 let (env, ty) = make_param_local_ty env variadicity_decl_ty vparam in
222 check_param_has_hint env vparam ty partial_callback;
223 let (env, t_variadic) = Typing.bind_param env (ty, vparam) in
224 (env, Aast.FVvariadicArg t_variadic)
225 | FVellipsis p ->
226 if is_function && Partial.should_check_error (Env.get_mode env) 4223 then
227 Errors.ellipsis_strict_mode ~require:`Type_and_param_name pos;
228 (env, Aast.FVellipsis p)
229 | FVnonVariadic -> (env, Aast.FVnonVariadic)
231 let merge_hint_with_decl_hint env type_hint decl_ty =
232 let contains_tvar decl_ty =
233 match decl_ty with
234 | None -> false
235 | Some decl_ty -> TUtils.contains_tvar_decl decl_ty
237 if contains_tvar decl_ty then
238 decl_ty
239 else
240 Option.map type_hint ~f:(Decl_hint.hint env.decl_env)
242 (* During the decl phase we can, for global inference, add "improved type hints".
243 That is we can say that some missing type hints are in fact global tyvars.
244 In that case to get the real type hint we must merge the type hint present
245 in the ast with the one we created during the decl phase. This function does
246 exactly this for the return type, the parameters and the variadic parameters.
248 let merge_decl_header_with_hints ~params ~ret ~variadic decl_header env =
249 let ret_decl_ty =
250 merge_hint_with_decl_hint
252 (hint_of_type_hint ret)
253 (Option.map
254 ~f:(fun { ft_ret = { et_type; _ }; _ } -> et_type)
255 decl_header)
257 let params_decl_ty =
258 match decl_header with
259 | None ->
260 List.map
261 ~f:(fun h ->
262 merge_hint_with_decl_hint
264 (hint_of_type_hint h.param_type_hint)
265 None)
266 params
267 | Some { ft_params; _ } ->
268 List.zip_exn params ft_params
269 |> List.map ~f:(fun (h, { fp_type = { et_type; _ }; _ }) ->
270 merge_hint_with_decl_hint
272 (hint_of_type_hint h.param_type_hint)
273 (Some et_type))
275 let variadicity_decl_ty =
276 match (decl_header, variadic) with
277 | ( Some { ft_arity = Fvariadic { fp_type = { et_type; _ }; _ }; _ },
278 FVvariadicArg fp ) ->
279 merge_hint_with_decl_hint
281 (hint_of_type_hint fp.param_type_hint)
282 (Some et_type)
283 | (_, FVvariadicArg fp) ->
284 merge_hint_with_decl_hint env (hint_of_type_hint fp.param_type_hint) None
285 | _ -> None
287 (ret_decl_ty, params_decl_ty, variadicity_decl_ty)
289 (* Checking this with List.exists will be a single op in the vast majority of cases (empty) *)
290 let get_ctx_vars ctxs =
291 Option.value_map
292 ~f:(fun (_, cs) ->
293 List.filter_map cs ~f:(function
294 | (_, Haccess ((_, Hvar n), _)) -> Some n
295 | _ -> None))
296 ~default:[]
297 ctxs
299 let fun_def ctx f :
300 (Tast.fun_def * Typing_inference_env.t_global_with_pos) option =
301 Counters.count Counters.Category.Typecheck @@ fun () ->
302 Errors.run_with_span f.f_span @@ fun () ->
303 let env = EnvFromDef.fun_env ~origin:Decl_counters.TopLevel ctx f in
304 with_timeout env f.f_name ~do_:(fun env ->
305 (* reset the expression dependent display ids for each function body *)
306 Reason.expr_display_id_map := IMap.empty;
307 let pos = fst f.f_name in
308 let decl_header = get_decl_function_header env (snd f.f_name) in
309 let env = Env.open_tyvars env (fst f.f_name) in
310 let env = Env.set_env_function_pos env pos in
311 let env = Env.set_env_pessimize env in
312 let env =
313 Typing.attributes_check_def env SN.AttributeKinds.fn f.f_user_attributes
315 let (env, file_attrs) = Typing.file_attributes env f.f_file_attributes in
316 let (env, cap_ty, unsafe_cap_ty) =
317 Typing.type_capability env f.f_ctxs f.f_unsafe_ctxs (fst f.f_name)
319 let (env, _) =
320 Typing_coeffects.register_capabilities env cap_ty unsafe_cap_ty
322 let reactive =
323 fun_reactivity env.decl_env f.f_user_attributes f.f_params
325 let mut = TUtils.fun_mutable f.f_user_attributes in
326 let env = Env.set_env_reactive env reactive in
327 let env = Env.set_fun_mutable env mut in
328 Typing_check_decls.fun_ env f;
329 let env =
330 Phase.localize_and_add_ast_generic_parameters_and_where_constraints
333 f.f_tparams
334 f.f_where_constraints
336 let env = Env.set_fn_kind env f.f_fun_kind in
337 let (return_decl_ty, params_decl_ty, variadicity_decl_ty) =
338 merge_decl_header_with_hints
339 ~params:f.f_params
340 ~ret:f.f_ret
341 ~variadic:f.f_variadic
342 decl_header
345 let (env, return_ty) =
346 match return_decl_ty with
347 | None ->
348 (env, Typing_return.make_default_return ~is_method:false env f.f_name)
349 | Some ty ->
350 let localize env ty = Phase.localize_with_self env ty in
351 Typing_return.make_return_type localize env ty
353 let return =
354 Typing_return.make_info
355 f.f_fun_kind
356 f.f_user_attributes
358 ~is_explicit:(Option.is_some (hint_of_type_hint f.f_ret))
359 return_ty
360 return_decl_ty
362 let (env, param_tys) =
363 List.zip_exn f.f_params params_decl_ty
364 |> List.map_env env ~f:(fun env (param, hint) ->
365 make_param_local_ty env hint param)
367 let partial_callback = Partial.should_check_error (Env.get_mode env) in
368 let check_has_hint p t = check_param_has_hint env p t partial_callback in
369 List.iter2_exn ~f:check_has_hint f.f_params param_tys;
370 Typing_memoize.check_function env f;
371 let params_need_immutable = get_ctx_vars f.f_ctxs in
372 let (env, typed_params) =
373 let bind_param_and_check env param =
374 let name = (snd param).param_name in
375 let immutable =
376 List.exists ~f:(String.equal name) params_need_immutable
378 let (env, fun_param) = Typing.bind_param ~immutable env param in
379 (env, fun_param)
381 List.map_env
383 (List.zip_exn param_tys f.f_params)
384 bind_param_and_check
386 let (env, t_variadic) =
387 get_callable_variadicity
388 ~is_function:true
389 ~pos
390 ~partial_callback
392 variadicity_decl_ty
393 f.f_variadic
395 let env =
396 set_tyvars_variance_in_callable env return_ty param_tys t_variadic
398 let local_tpenv = Env.get_tpenv env in
399 let disable =
400 Naming_attributes.mem
401 SN.UserAttributes.uaDisableTypecheckerInternal
402 f.f_user_attributes
404 let (env, tb) =
405 Typing.fun_ ~disable env return pos f.f_body f.f_fun_kind
407 (* restore original reactivity *)
408 let env = Env.set_env_reactive env reactive in
409 begin
410 match hint_of_type_hint f.f_ret with
411 | None ->
412 if partial_callback 4030 then Errors.expecting_return_type_hint pos
413 | Some _ -> ()
414 end;
415 let (env, tparams) = List.map_env env f.f_tparams Typing.type_param in
416 let (env, user_attributes) =
417 List.map_env env f.f_user_attributes Typing.user_attribute
419 let env =
420 Typing_solver.close_tyvars_and_solve env Errors.bad_function_typevar
422 let env =
423 Typing_solver.solve_all_unsolved_tyvars env Errors.bad_function_typevar
425 let fundef =
427 Aast.f_annotation = Env.save local_tpenv env;
428 Aast.f_span = f.f_span;
429 Aast.f_mode = f.f_mode;
430 Aast.f_readonly_ret = f.f_readonly_ret;
431 Aast.f_ret = (return_ty, hint_of_type_hint f.f_ret);
432 Aast.f_name = f.f_name;
433 Aast.f_tparams = tparams;
434 Aast.f_where_constraints = f.f_where_constraints;
435 Aast.f_variadic = t_variadic;
436 Aast.f_params = typed_params;
437 Aast.f_ctxs = f.f_ctxs;
438 Aast.f_unsafe_ctxs = f.f_unsafe_ctxs;
439 Aast.f_fun_kind = f.f_fun_kind;
440 Aast.f_file_attributes = file_attrs;
441 Aast.f_user_attributes = user_attributes;
442 Aast.f_body = { Aast.fb_ast = tb; fb_annotation = () };
443 Aast.f_external = f.f_external;
444 Aast.f_namespace = f.f_namespace;
445 Aast.f_doc_comment = f.f_doc_comment;
446 Aast.f_static = f.f_static;
449 let (_env, global_inference_env) = Env.extract_global_inference_env env in
450 (fundef, (pos, global_inference_env)))
452 let method_def env cls m =
453 Errors.run_with_span m.m_span @@ fun () ->
454 with_timeout env m.m_name ~do_:(fun env ->
455 FunUtils.check_params m.m_params;
456 let initial_env = env in
457 (* reset the expression dependent display ids for each method body *)
458 Reason.expr_display_id_map := IMap.empty;
459 let decl_header =
460 get_decl_method_header
461 (Env.get_tcopt env)
463 (snd m.m_name)
464 ~is_static:m.m_static
466 let pos = fst m.m_name in
467 let env = Env.open_tyvars env (fst m.m_name) in
468 let env = Env.reinitialize_locals env in
469 let env = Env.set_env_function_pos env pos in
470 let env =
471 Typing.attributes_check_def
473 SN.AttributeKinds.mthd
474 m.m_user_attributes
476 let reactive =
477 fun_reactivity env.decl_env m.m_user_attributes m.m_params
479 let mut =
480 match TUtils.fun_mutable m.m_user_attributes with
481 | None ->
482 (* <<__Mutable>> is implicit on constructors *)
483 if String.equal (snd m.m_name) SN.Members.__construct then
484 Some Param_borrowed_mutable
485 else
486 None
487 | x -> x
489 let (env, cap_ty, unsafe_cap_ty) =
490 Typing.type_capability env m.m_ctxs m.m_unsafe_ctxs (fst m.m_name)
492 let (env, _) =
493 Typing_coeffects.register_capabilities env cap_ty unsafe_cap_ty
495 let env = Env.set_env_reactive env reactive in
496 let env = Env.set_fun_mutable env mut in
497 let env =
498 Phase.localize_and_add_ast_generic_parameters_and_where_constraints
501 m.m_tparams
502 m.m_where_constraints
504 let env =
505 if Env.is_static env then
507 else
508 Env.set_local env this (Env.get_self env) Pos.none
510 let env =
511 match Env.get_self_class env with
512 | None -> env
513 | Some c ->
514 (* Mark $this as a using variable if it has a disposable type *)
515 if Cls.is_disposable c then
516 Env.set_using_var env this
517 else
520 let env = Env.clear_params env in
521 let (ret_decl_ty, params_decl_ty, variadicity_decl_ty) =
522 merge_decl_header_with_hints
523 ~params:m.m_params
524 ~ret:m.m_ret
525 ~variadic:m.m_variadic
526 decl_header
529 let env = Env.set_fn_kind env m.m_fun_kind in
530 let (env, locl_ty) =
531 match ret_decl_ty with
532 | None ->
533 (env, Typing_return.make_default_return ~is_method:true env m.m_name)
534 | Some ret ->
535 (* If a 'this' type appears it needs to be compatible with the
536 * late static type
538 let ety_env = Phase.env_with_self env in
539 Typing_return.make_return_type (Phase.localize ~ety_env) env ret
541 let return =
542 Typing_return.make_info
543 m.m_fun_kind
544 m.m_user_attributes
546 ~is_explicit:(Option.is_some (hint_of_type_hint m.m_ret))
547 locl_ty
548 ret_decl_ty
550 let (env, param_tys) =
551 List.zip_exn m.m_params params_decl_ty
552 |> List.map_env env ~f:(fun env (param, hint) ->
553 make_param_local_ty env hint param)
555 let partial_callback = Partial.should_check_error (Env.get_mode env) in
556 let param_fn p t = check_param_has_hint env p t partial_callback in
557 List.iter2_exn ~f:param_fn m.m_params param_tys;
558 Typing_memoize.check_method env m;
559 let params_need_immutable = get_ctx_vars m.m_ctxs in
560 let (env, typed_params) =
561 let bind_param_and_check env param =
562 let name = (snd param).param_name in
563 let immutable =
564 List.exists ~f:(String.equal name) params_need_immutable
566 let (env, fun_param) = Typing.bind_param ~immutable env param in
567 (env, fun_param)
569 List.map_env
571 (List.zip_exn param_tys m.m_params)
572 bind_param_and_check
574 let (env, t_variadic) =
575 get_callable_variadicity
576 ~partial_callback
577 ~pos
579 variadicity_decl_ty
580 m.m_variadic
582 let env =
583 set_tyvars_variance_in_callable env locl_ty param_tys t_variadic
585 let nb = Nast.assert_named_body m.m_body in
586 let local_tpenv = Env.get_tpenv env in
587 let disable =
588 Naming_attributes.mem
589 SN.UserAttributes.uaDisableTypecheckerInternal
590 m.m_user_attributes
592 let (env, tb) =
593 Typing.fun_
594 ~abstract:m.m_abstract
595 ~disable
597 return
600 m.m_fun_kind
602 (* restore original method reactivity *)
603 let env = Env.set_env_reactive env reactive in
604 let type_hint' =
605 match hint_of_type_hint m.m_ret with
606 | None when String.equal (snd m.m_name) SN.Members.__construct ->
607 Some (pos, Hprim Tvoid)
608 | None ->
609 if partial_callback 4030 then Errors.expecting_return_type_hint pos;
610 None
611 | Some _ -> hint_of_type_hint m.m_ret
613 let m = { m with m_ret = (fst m.m_ret, type_hint') } in
614 let (env, tparams) = List.map_env env m.m_tparams Typing.type_param in
615 let (env, user_attributes) =
616 List.map_env env m.m_user_attributes Typing.user_attribute
618 let env =
619 Typing_solver.close_tyvars_and_solve env Errors.bad_method_typevar
621 let env =
622 Typing_solver.solve_all_unsolved_tyvars env Errors.bad_method_typevar
624 (* if the class implements dynamic, then check that its methods are dynamically callable *)
626 TypecheckerOptions.enable_sound_dynamic
627 (Provider_context.get_tcopt (Env.get_ctx env))
628 && Cls.get_implements_dynamic cls
629 then begin
630 (* 1. check if all the parameters of the method are enforceable *)
631 List.iter params_decl_ty ~f:(fun dtyopt ->
632 match dtyopt with
633 | Some dty ->
634 let te_check = Typing_enforceability.is_enforceable env dty in
635 if not te_check then
636 Errors.method_is_not_dynamically_callable
637 (fst m.m_name)
638 (snd m.m_name)
639 (Cls.name cls)
640 | None -> ());
641 (* 2. check if the return type is coercible *)
644 (Typing_subtype.is_sub_type_for_union
645 ~coerce:(Some Typing_logic.CoerceToDynamic)
647 locl_ty
648 (mk (Reason.Rnone, Tdynamic)))
649 then
650 Errors.method_is_not_dynamically_callable
651 (fst m.m_name)
652 (snd m.m_name)
653 (Cls.name cls)
654 end;
655 let method_def =
657 Aast.m_annotation = Env.save local_tpenv env;
658 Aast.m_span = m.m_span;
659 Aast.m_final = m.m_final;
660 Aast.m_static = m.m_static;
661 Aast.m_abstract = m.m_abstract;
662 Aast.m_visibility = m.m_visibility;
663 Aast.m_readonly_this = m.m_readonly_this;
664 Aast.m_name = m.m_name;
665 Aast.m_tparams = tparams;
666 Aast.m_where_constraints = m.m_where_constraints;
667 Aast.m_variadic = t_variadic;
668 Aast.m_params = typed_params;
669 Aast.m_ctxs = m.m_ctxs;
670 Aast.m_unsafe_ctxs = m.m_unsafe_ctxs;
671 Aast.m_fun_kind = m.m_fun_kind;
672 Aast.m_user_attributes = user_attributes;
673 Aast.m_readonly_ret = m.m_readonly_ret;
674 Aast.m_ret = (locl_ty, hint_of_type_hint m.m_ret);
675 Aast.m_body = { Aast.fb_ast = tb; fb_annotation = () };
676 Aast.m_external = m.m_external;
677 Aast.m_doc_comment = m.m_doc_comment;
680 let (env, global_inference_env) = Env.extract_global_inference_env env in
681 let _env = Env.log_env_change "method_def" initial_env env in
682 (method_def, (pos, global_inference_env)))
684 (** Checks that extending this parent is legal - e.g. it is not final and not const. *)
685 let check_parent env class_def class_type =
686 match Env.get_parent_class env with
687 | Some parent_type ->
688 let position = fst class_def.c_name in
689 if Cls.const class_type && not (Cls.const parent_type) then
690 Errors.self_const_parent_not position;
691 if Cls.final parent_type then
692 Errors.extend_final position (Cls.pos parent_type) (Cls.name parent_type)
693 | None -> ()
695 let check_parent_sealed ~(is_enum_class : bool) child_type parent_type =
696 match Cls.sealed_whitelist parent_type with
697 | None -> ()
698 | Some whitelist ->
699 let parent_pos = Cls.pos parent_type in
700 let parent_name = Cls.name parent_type in
701 let child_pos = Cls.pos child_type in
702 let child_name = Cls.name child_type in
703 let check kind action =
704 if not (SSet.mem child_name whitelist) then
705 Errors.extend_sealed child_pos parent_pos parent_name kind action
707 begin
708 match (Cls.kind parent_type, Cls.kind child_type) with
709 | (Ast_defs.Cinterface, Ast_defs.Cinterface) -> check "interface" "extend"
710 | (Ast_defs.Cinterface, _) -> check "interface" "implement"
711 | (Ast_defs.Ctrait, _) -> check "trait" "use"
712 | (Ast_defs.Cabstract, _)
713 | (Ast_defs.Cnormal, _) ->
714 check "class" "extend"
715 | (Ast_defs.Cenum, _) when is_enum_class -> check "enum class" "extend"
716 | (Ast_defs.Cenum, _) -> check "enum" "use"
719 let check_parents_sealed env child_def child_type =
720 let parents =
721 match child_def.c_enum with
722 | Some enum -> enum.e_includes
723 | None -> child_def.c_extends
725 let parents = parents @ child_def.c_implements @ child_def.c_uses in
726 let is_enum_class = Aast.is_enum_class child_def in
727 List.iter parents (function
728 | (_, Happly ((_, name), _)) ->
729 begin
730 match Env.get_class_dep env name with
731 | Some parent_type ->
732 check_parent_sealed ~is_enum_class child_type parent_type
733 | None -> ()
735 | _ -> ())
737 (* Reject multiple instantiations of the same generic interface
738 * in extends and implements clauses.
739 * e.g. disallow class C implements I<string>, I<int>
741 * O(n^2) but we don't expect number of instantiated interfaces to be large
743 let rec check_implements_or_extends_unique impl =
744 match impl with
745 | [] -> ()
746 | ty :: rest ->
747 (match get_node ty with
748 | Tapply ((pos, name), _ :: _) ->
749 let (pos_list, rest) =
750 List.partition_map rest (fun ty ->
751 match get_node ty with
752 | Tapply ((pos', name'), _) when String.equal name name' ->
753 `Fst pos'
754 | _ -> `Snd ty)
756 if not (List.is_empty pos_list) then
757 Errors.duplicate_interface pos name pos_list;
758 check_implements_or_extends_unique rest
759 | _ -> check_implements_or_extends_unique rest)
761 let check_cstr_dep env deps =
762 List.iter deps (fun dep ->
763 match deref dep with
764 | (_, Tapply ((_, class_name), _)) ->
765 Env.make_depend_on_constructor env class_name
766 | (r, Tgeneric _) ->
767 let p = Typing_reason.to_pos r in
768 Errors.expected_class ~suffix:" or interface but got a generic" p
769 | (r, _) ->
770 let p = Typing_reason.to_pos r in
771 Errors.expected_class ~suffix:" or interface" p)
773 let check_const_trait_members pos env use_list =
774 let (_, trait, _) = Decl_utils.unwrap_class_hint use_list in
775 match Env.get_class env trait with
776 | Some c when Ast_defs.(equal_class_kind (Cls.kind c) Ctrait) ->
777 List.iter (Cls.props c) (fun (x, ce) ->
778 if not (get_ce_const ce) then Errors.trait_prop_const_class pos x)
779 | _ -> ()
781 let check_consistent_enum_inclusion included_cls (dest_cls : Cls.t) =
782 match (Cls.enum_type included_cls, Cls.enum_type dest_cls) with
783 | (Some included_e, Some dest_e) ->
784 (* Temporary workaround until D26410129 reaches HHVM *)
785 let pos_included = Cls.pos included_cls in
786 let pos_dest = Cls.pos dest_cls in
788 Relative_path.equal (Pos.filename pos_dest) (Pos.filename pos_included)
789 && Pos.line pos_dest < Pos.line pos_included
790 then
791 Errors.enum_inclusion_unsupported_ordering
792 pos_dest
793 (Cls.name dest_cls)
794 (Cls.name included_cls);
795 (* ensure that the base types are identical *)
796 if not (Typing_defs.equal_decl_ty included_e.te_base dest_e.te_base) then
797 Errors.incompatible_enum_inclusion_base
798 (Cls.pos dest_cls)
799 (Cls.name dest_cls)
800 (Cls.name included_cls);
801 (* ensure that the visibility constraint are compatible *)
802 (match (included_e.te_constraint, dest_e.te_constraint) with
803 | (None, Some _) ->
804 Errors.incompatible_enum_inclusion_constraint
805 (Cls.pos dest_cls)
806 (Cls.name dest_cls)
807 (Cls.name included_cls)
808 | (_, _) -> ());
809 (* ensure normal enums can't include enum classes *)
810 if included_e.te_enum_class && not dest_e.te_enum_class then
811 Errors.wrong_extend_kind
812 ~parent_pos:(Cls.pos included_cls)
813 ~parent_kind:Ast_defs.Cenum
814 ~parent_name:(Cls.name included_cls)
815 ~parent_is_enum_class:true
816 ~child_pos:(Cls.pos dest_cls)
817 ~child_kind:Ast_defs.Cenum
818 ~child_name:(Cls.name dest_cls)
819 ~child_is_enum_class:false
820 | (None, _) ->
821 Errors.enum_inclusion_not_enum
822 (Cls.pos dest_cls)
823 (Cls.name dest_cls)
824 (Cls.name included_cls)
825 | (_, _) -> ()
827 let check_enum_includes env cls =
828 (* checks that there are no duplicated enum-constants when folded-decls are enabled *)
829 if Ast_defs.is_c_enum cls.c_kind then (
830 let (dest_class_pos, dest_class_name) = cls.c_name in
831 let enum_constant_map = ref SMap.empty in
832 (* prepopulate the map with the constants declared in cls *)
833 List.iter cls.c_consts ~f:(fun cc ->
834 enum_constant_map :=
835 SMap.add
836 (snd cc.cc_id)
837 (fst cc.cc_id, dest_class_name)
838 !enum_constant_map);
839 (* for all included enums *)
840 let included_enums =
841 Aast.enum_includes_map cls.c_enum ~f:(fun ce ->
842 List.filter_map ce.e_includes ~f:(fun ie ->
843 match snd ie with
844 | Happly (sid, _) ->
845 (match Env.get_class env (snd sid) with
846 | None -> None
847 | Some ie_cls -> Some (fst ie, ie_cls))
848 | _ -> None))
850 List.iter included_enums ~f:(fun (ie_pos, ie_cls) ->
851 let src_class_name = Cls.name ie_cls in
852 (* 1. Check for consistency *)
853 (match Env.get_class env dest_class_name with
854 | None -> ()
855 | Some cls -> check_consistent_enum_inclusion ie_cls cls);
856 (* 2. Check for duplicates *)
857 List.iter (Cls.consts ie_cls) ~f:(fun (const_name, class_const) ->
858 ( if String.equal const_name "class" then
860 else if SMap.mem const_name !enum_constant_map then
861 (* distinguish between multiple inherit and redeclare *)
862 let (origin_const_pos, origin_class_name) =
863 SMap.find const_name !enum_constant_map
865 if String.equal origin_class_name dest_class_name then
866 (* redeclare *)
867 Errors.redeclaring_classish_const
868 dest_class_pos
869 dest_class_name
870 origin_const_pos
871 src_class_name
872 const_name
873 else
874 (* multiple inherit *)
875 Errors.reinheriting_classish_const
876 dest_class_pos
877 dest_class_name
878 ie_pos
879 src_class_name
880 origin_class_name
881 const_name );
882 enum_constant_map :=
883 SMap.add
884 const_name
885 (dest_class_pos, class_const.cc_origin)
886 !enum_constant_map))
889 let shallow_decl_enabled (ctx : Provider_context.t) : bool =
890 TypecheckerOptions.shallow_class_decl (Provider_context.get_tcopt ctx)
892 let class_type_param env ct =
893 let (env, tparam_list) = List.map_env env ct Typing.type_param in
894 (env, tparam_list)
896 (* This function sets a temporary coeffect context to check constants
897 * with the right one (either pure / write_props).
898 * We need to carefully restore the locals, otherwise the next continuation
899 * is just reset and the call to register_capabilities is a no-op, no
900 * capability is registered.
902 let expr_with_special_coeffects env ?expected e cap_ty unsafe_cap_ty =
903 let init =
904 Option.map (Env.next_cont_opt env) ~f:(fun next_cont ->
905 let initial_locals = next_cont.Typing_per_cont_env.local_types in
906 let tpenv = Env.get_tpenv env in
907 (initial_locals, tpenv))
909 let (env, (te, ty)) =
910 Typing_lenv.stash_and_do env (Env.all_continuations env) (fun env ->
911 let env =
912 match init with
913 | None -> env
914 | Some (initial_locals, tpenv) ->
915 let env = Env.reinitialize_locals env in
916 let env = Env.set_locals env initial_locals in
917 let env = Env.env_with_tpenv env tpenv in
920 let (env, _ty) =
921 Typing_coeffects.register_capabilities env cap_ty unsafe_cap_ty
923 let (env, te, ty) = Typing.expr ?expected env e in
924 (env, (te, ty)))
926 (env, te, ty)
928 (* Some (legacy) special functions are allowed as class constant init.,
929 therefore treat them as pure and insert the matching capabilities. *)
930 let expr_with_pure_coeffects env ?expected e =
931 let pure = MakeType.mixed (Reason.Rwitness (fst e)) in
932 expr_with_special_coeffects env ?expected e pure pure
934 (* Enum class constant initializers are restricted to be `write_props` *)
935 let expr_with_write_props_coeffects env ?expected e =
936 let e_pos = fst e in
937 let make_hint pos s = (pos, Aast.Happly ((pos, s), [])) in
938 let enum_class_ctx =
939 Some (e_pos, [make_hint e_pos SN.Capabilities.writeProperty])
941 let (env, cap_ty, unsafe_cap_ty) =
942 Typing.type_capability env enum_class_ctx enum_class_ctx e_pos
944 expr_with_special_coeffects env ?expected e cap_ty unsafe_cap_ty
946 (** Checks that a dynamic element is also dynamic in the parents. *)
947 let check_dynamic_class_element get_static_elt element_name dyn_pos ~elt_type =
948 (* The non-static properties that we get passed do not start with '$', but the
949 static properties we want to look up do, so add it. *)
950 let id =
951 match elt_type with
952 | `Method -> element_name
953 | `Property -> "$" ^ element_name
955 match get_static_elt id with
956 | None -> ()
957 | Some static_element ->
958 let (lazy ty) = static_element.ce_type in
959 Errors.static_redeclared_as_dynamic
960 dyn_pos
961 (get_pos ty)
962 element_name
963 ~elt_type
965 (** Checks that a static element is also static in the parents. *)
966 let check_static_class_element get_dyn_elt element_name static_pos ~elt_type =
967 (* The static properties that we get passed in start with '$', but the
968 non-static properties we're matching against don't, so we need to detect
969 that and remove it if present. *)
970 let element_name = String_utils.lstrip element_name "$" in
971 match get_dyn_elt element_name with
972 | None -> ()
973 | Some dyn_element ->
974 let (lazy ty) = dyn_element.ce_type in
975 Errors.dynamic_redeclared_as_static
976 static_pos
977 (get_pos ty)
978 element_name
979 ~elt_type
981 (** Error if there are abstract methods that this class is supposed to provide
982 implementation for. *)
983 let check_extend_abstract_meth ~is_final p seq =
984 List.iter seq (fun (x, ce) ->
985 match ce.ce_type with
986 | (lazy ty) when get_ce_abstract ce && is_fun ty ->
987 Errors.implement_abstract ~is_final p (get_pos ty) "method" x
988 | _ -> ())
990 let check_extend_abstract_prop ~is_final p seq =
991 List.iter seq (fun (x, ce) ->
992 if get_ce_abstract ce then
993 let ce_pos = Lazy.force ce.ce_type |> get_pos in
994 Errors.implement_abstract ~is_final p ce_pos "property" x)
996 (* Type constants must be bound to a concrete type for non-abstract classes.
998 let check_extend_abstract_typeconst ~is_final p seq =
999 List.iter seq (fun (x, tc) ->
1000 if Option.is_none tc.ttc_type then
1001 Errors.implement_abstract
1002 ~is_final
1004 (fst tc.ttc_name)
1005 "type constant"
1008 let check_extend_abstract_const ~is_final p seq =
1009 List.iter seq (fun (x, cc) ->
1010 if cc.cc_abstract && not cc.cc_synthesized then
1011 let cc_pos = get_pos cc.cc_type in
1012 Errors.implement_abstract ~is_final p cc_pos "constant" x)
1014 exception Found of Pos.t
1016 let contains_generic : Typing_defs.decl_ty -> Pos.t option =
1017 fun ty ->
1018 let visitor =
1019 object
1020 inherit [_] Type_visitor.decl_type_visitor as super
1022 method! on_type env ty =
1023 match get_node ty with
1024 | Tgeneric _ -> raise (Found (get_pos ty))
1025 | _ -> super#on_type env ty
1029 visitor#on_type () ty;
1030 None
1031 with Found p -> Some p
1033 let check_no_generic_static_property tc =
1035 (* Check whether the type of a static property (class variable) contains
1036 * any generic type parameters. Outside of traits, this is illegal as static
1037 * properties are shared across all generic instantiations.
1038 * Although not strictly speaking a variance check, it fits here because
1039 * it concerns the presence of generic type parameters in types.
1041 Ast_defs.(equal_class_kind (Cls.kind tc) Ctrait)
1042 then
1044 else
1045 Cls.sprops tc
1046 |> List.iter ~f:(fun (_prop_name, prop) ->
1047 let (lazy ty) = prop.ce_type in
1048 let var_type_pos = get_pos ty in
1049 let class_pos = Cls.pos tc in
1050 match contains_generic ty with
1051 | None -> ()
1052 | Some generic_pos ->
1053 Errors.static_property_type_generic_param
1054 ~class_pos
1055 ~var_type_pos
1056 ~generic_pos)
1058 let get_decl_prop_ty env cls ~is_static prop_id =
1059 let is_global_inference_on = TCO.global_inference (Env.get_tcopt env) in
1060 if is_global_inference_on then
1061 let prop_opt =
1062 if is_static then
1063 (* this is very ad-hoc, but this is how we do it in the decl-heap *)
1064 Cls.get_sprop cls ("$" ^ prop_id)
1065 else
1066 Cls.get_prop cls prop_id
1068 match prop_opt with
1069 | None -> failwith "error: could not find property in decl heap"
1070 | Some { ce_type; _ } -> Some (Lazy.force ce_type)
1071 else
1072 None
1074 let typeconst_def
1078 c_tconst_abstract;
1079 c_tconst_name = (pos, _) as id;
1080 c_tconst_as_constraint;
1081 c_tconst_type = hint;
1082 c_tconst_user_attributes;
1083 c_tconst_span;
1084 c_tconst_doc_comment;
1085 c_tconst_is_ctx;
1087 if Ast_defs.is_c_enum cls.c_kind then
1088 Errors.cannot_declare_constant `enum pos cls.c_name;
1089 let (env, cstr) =
1090 opt Phase.localize_hint_with_self env c_tconst_as_constraint
1092 let (env, ty) =
1093 match hint with
1094 | None -> (env, None)
1095 | Some hint ->
1096 let ty = Decl_hint.hint env.decl_env hint in
1097 (* We want to report cycles through the definition *)
1098 let name = snd cls.c_name ^ "::" ^ snd id in
1099 let (env, ty) =
1100 Phase.localize_with_self env ~pos ~report_cycle:(pos, name) ty
1102 (env, Some ty)
1104 let check env t c =
1105 Type.sub_type pos Reason.URtypeconst_cstr env t c Errors.unify_error
1107 let env = Option.value ~default:env @@ Option.map2 ty cstr ~f:(check env) in
1108 let env =
1109 match hint with
1110 | Some (pos, Hshape { nsi_field_map; _ }) ->
1111 let get_name sfi = sfi.sfi_name in
1112 Typing.check_shape_keys_validity
1115 (List.map ~f:get_name nsi_field_map)
1116 | _ -> env
1118 let env =
1119 Typing.attributes_check_def
1121 SN.AttributeKinds.typeconst
1122 c_tconst_user_attributes
1124 let (env, user_attributes) =
1125 List.map_env env c_tconst_user_attributes Typing.user_attribute
1127 ( env,
1129 Aast.c_tconst_abstract;
1130 Aast.c_tconst_name = id;
1131 Aast.c_tconst_as_constraint;
1132 Aast.c_tconst_type = hint;
1133 Aast.c_tconst_user_attributes = user_attributes;
1134 Aast.c_tconst_span;
1135 Aast.c_tconst_doc_comment;
1136 Aast.c_tconst_is_ctx;
1139 (* This should agree with the set of expressions whose type can be inferred in
1140 * Decl_utils.infer_const
1142 let is_literal_expr e =
1143 match snd e with
1144 | String _
1145 | True
1146 | False
1147 | Int _
1148 | Float _
1149 | Null ->
1150 true
1151 | Unop ((Ast_defs.Uminus | Ast_defs.Uplus), (_, (Int _ | Float _))) -> true
1152 | _ -> false
1154 let class_const_def ~in_enum_class c env cc =
1155 let { cc_type = h; cc_id = id; cc_expr = e; _ } = cc in
1156 let (env, ty, opt_expected) =
1157 match h with
1158 | None ->
1159 begin
1160 match e with
1161 | None -> ()
1162 | Some e ->
1164 (not (is_literal_expr e))
1165 && Partial.should_check_error c.c_mode 2035
1166 && not Ast_defs.(equal_class_kind c.c_kind Cenum)
1167 then
1168 Errors.missing_typehint (fst id)
1169 end;
1170 let (env, ty) = Env.fresh_type env (fst id) in
1171 (env, MakeType.unenforced ty, None)
1172 | Some h ->
1173 let ty = Decl_hint.hint env.decl_env h in
1174 let ty = Typing_enforceability.compute_enforced_ty env ty in
1175 let (env, ty) = Phase.localize_possibly_enforced_with_self env ty in
1176 (* Removing the HH\MemberOf wrapper in case of enum classes so the
1177 * following call to expr_* has the right expected type
1179 let opt_ty =
1180 if in_enum_class then
1181 match get_node ty.et_type with
1182 | Tnewtype (memberof, [_; et_type], _)
1183 when String.equal memberof SN.Classes.cMemberOf ->
1184 { ty with et_type }
1185 | _ -> ty
1186 else
1189 ( env,
1191 Some (ExpectedTy.make_and_allow_coercion (fst id) Reason.URhint opt_ty)
1194 let (env, eopt, ty) =
1195 match e with
1196 | Some e ->
1197 let (env, te, ty') =
1198 if in_enum_class then
1199 expr_with_write_props_coeffects env ?expected:opt_expected e
1200 else
1201 expr_with_pure_coeffects env ?expected:opt_expected e
1203 (* If we are checking an enum class, wrap ty' into the right
1204 * HH\MemberOf<class name, ty'> alias
1206 let (te, ty') =
1207 if in_enum_class then
1208 match deref ty.et_type with
1209 | (r, Tnewtype (memberof, [enum_name; _], _))
1210 when String.equal memberof SN.Classes.cMemberOf ->
1211 let lift r ty = mk (r, Tnewtype (memberof, [enum_name; ty], ty)) in
1212 let ((p, te_ty), te) = te in
1213 let te = ((p, lift (get_reason te_ty) te_ty), te) in
1214 let ty' = lift r ty' in
1215 (te, ty')
1216 | _ -> (te, ty')
1217 else
1218 (te, ty')
1220 let env =
1221 Typing_coercion.coerce_type
1222 (fst id)
1223 Reason.URhint
1227 Errors.class_constant_value_does_not_match_hint
1229 (env, Some te, ty')
1230 | None -> (env, None, ty.et_type)
1232 ( env,
1234 Aast.cc_type = cc.cc_type;
1235 Aast.cc_id = cc.cc_id;
1236 Aast.cc_expr = eopt;
1237 Aast.cc_doc_comment = cc.cc_doc_comment;
1239 ty ) )
1241 let class_constr_def env cls constructor =
1242 let env = { env with inside_constructor = true } in
1243 Option.bind constructor (method_def env cls)
1245 let class_implements_type env c1 ctype2 =
1246 let params =
1247 List.map c1.c_tparams (fun { tp_name = (p, s); _ } ->
1248 mk (Reason.Rwitness p, Tgeneric (s, [])))
1250 let r = Reason.Rwitness (fst c1.c_name) in
1251 let ctype1 = mk (r, Tapply (c1.c_name, params)) in
1252 Typing_extends.check_implements env ctype2 ctype1
1254 (** Type-check a property declaration, with optional initializer *)
1255 let class_var_def ~is_static cls env cv =
1256 (* First pick up and localize the hint if it exists *)
1257 let decl_cty =
1258 merge_hint_with_decl_hint
1260 (hint_of_type_hint cv.cv_type)
1261 (get_decl_prop_ty env cls ~is_static (snd cv.cv_id))
1263 let (env, expected) =
1264 match decl_cty with
1265 | None -> (env, None)
1266 | Some decl_cty ->
1267 let decl_cty = Typing_enforceability.compute_enforced_ty env decl_cty in
1268 let (env, cty) =
1269 Phase.localize_possibly_enforced_with_self env decl_cty
1271 let expected =
1272 Some (ExpectedTy.make_and_allow_coercion cv.cv_span Reason.URhint cty)
1274 (env, expected)
1276 (* Next check the expression, passing in expected type if present *)
1277 let (env, typed_cv_expr) =
1278 match cv.cv_expr with
1279 | None -> (env, None)
1280 | Some e ->
1281 let (env, te, ty) = expr_with_pure_coeffects env ?expected e in
1282 (* Check that the inferred type is a subtype of the expected type.
1283 * Eventually this will be the responsibility of `expr`
1285 let env =
1286 match expected with
1287 | None -> env
1288 | Some ExpectedTy.{ pos = p; reason = ur; ty = cty } ->
1289 Typing_coercion.coerce_type
1295 Errors.class_property_initializer_type_does_not_match_hint
1297 (env, Some te)
1300 let env =
1301 if is_static then
1302 Typing.attributes_check_def
1304 SN.AttributeKinds.staticProperty
1305 cv.cv_user_attributes
1306 else
1307 Typing.attributes_check_def
1309 SN.AttributeKinds.instProperty
1310 cv.cv_user_attributes
1312 let (env, user_attributes) =
1313 List.map_env env cv.cv_user_attributes Typing.user_attribute
1316 Option.is_none (hint_of_type_hint cv.cv_type)
1317 && Partial.should_check_error (Env.get_mode env) 2001
1318 then
1319 Errors.prop_without_typehint
1320 (string_of_visibility cv.cv_visibility)
1321 cv.cv_id;
1322 let (env, global_inference_env) = Env.extract_global_inference_env env in
1323 let cv_type =
1324 match expected with
1325 | Some expected ->
1326 (expected.ExpectedTy.ty.et_type, hint_of_type_hint cv.cv_type)
1327 | None -> Tast.dummy_type_hint (hint_of_type_hint cv.cv_type)
1329 ( env,
1331 Aast.cv_final = cv.cv_final;
1332 Aast.cv_xhp_attr = cv.cv_xhp_attr;
1333 Aast.cv_abstract = cv.cv_abstract;
1334 Aast.cv_visibility = cv.cv_visibility;
1335 Aast.cv_type;
1336 Aast.cv_id = cv.cv_id;
1337 Aast.cv_expr = typed_cv_expr;
1338 Aast.cv_user_attributes = user_attributes;
1339 Aast.cv_is_promoted_variadic = cv.cv_is_promoted_variadic;
1340 Aast.cv_doc_comment = cv.cv_doc_comment;
1341 (* Can make None to save space *)
1342 Aast.cv_is_static = is_static;
1343 Aast.cv_span = cv.cv_span;
1344 Aast.cv_readonly = cv.cv_readonly;
1346 (cv.cv_span, global_inference_env) ) )
1348 let class_def_ env c tc =
1349 let env =
1350 let kind =
1351 match c.c_kind with
1352 | Ast_defs.Cenum ->
1353 (match c.c_enum with
1354 | Some enum when enum.e_enum_class -> SN.AttributeKinds.enumcls
1355 | _ -> SN.AttributeKinds.enum)
1356 | _ -> SN.AttributeKinds.cls
1358 Typing.attributes_check_def env kind c.c_user_attributes
1360 let (env, file_attrs) = Typing.file_attributes env c.c_file_attributes in
1361 let ctx = Env.get_ctx env in
1363 ( Ast_defs.(equal_class_kind c.c_kind Cnormal)
1364 || Ast_defs.(equal_class_kind c.c_kind Cabstract) )
1365 && not (shallow_decl_enabled ctx)
1366 then (
1367 (* These checks are only for eager mode. The same checks are performed
1368 * for shallow mode in Typing_inheritance *)
1369 let method_pos ~is_static class_id meth_id =
1370 let get_meth =
1371 if is_static then
1372 Decl_heap.StaticMethods.get
1373 else
1374 Decl_heap.Methods.get
1376 match get_meth (class_id, meth_id) with
1377 | Some { fe_pos; _ } -> fe_pos
1378 | None -> Pos.none
1380 let check_override ~is_static (id, ce) =
1381 if get_ce_override ce then
1382 let pos = method_pos ~is_static ce.ce_origin id in
1383 (* Method is actually defined in this class *)
1384 if String.equal ce.ce_origin (snd c.c_name) then
1385 Errors.should_be_override pos (snd c.c_name) id
1386 else
1387 match Env.get_class env ce.ce_origin with
1388 | None -> ()
1389 | Some parent_class ->
1390 (* If it's not defined here, then either it's inherited (so we have emitted an error already)
1391 * or it's in a trait, and so we need to emit the error now *)
1392 if not Ast_defs.(equal_class_kind (Cls.kind parent_class) Ctrait)
1393 then
1395 else
1396 Errors.override_per_trait c.c_name id ce.ce_origin pos
1399 List.iter (Cls.methods tc) (check_override ~is_static:false);
1400 List.iter (Cls.smethods tc) (check_override ~is_static:true)
1402 check_enum_includes env c;
1403 let (pc, c_name) = c.c_name in
1404 let (req_extends, req_implements) = split_reqs c in
1405 let extends = List.map c.c_extends (Decl_hint.hint env.decl_env) in
1406 let implements = List.map c.c_implements (Decl_hint.hint env.decl_env) in
1407 let uses = List.map c.c_uses (Decl_hint.hint env.decl_env) in
1408 let req_extends = List.map req_extends (Decl_hint.hint env.decl_env) in
1409 let req_implements = List.map req_implements (Decl_hint.hint env.decl_env) in
1410 let additional_parents =
1411 (* In an abstract class or a trait, we assume the interfaces
1412 will be implemented in the future, so we take them as
1413 part of the class (as requested by dependency injection implementers) *)
1414 match c.c_kind with
1415 | Ast_defs.Cabstract -> implements
1416 | Ast_defs.Ctrait -> implements @ req_implements
1417 | _ -> []
1419 let check_cstr_dep = check_cstr_dep env in
1420 check_implements_or_extends_unique implements;
1421 check_implements_or_extends_unique extends;
1422 check_cstr_dep extends;
1423 check_cstr_dep uses;
1424 check_cstr_dep req_extends;
1425 check_cstr_dep additional_parents;
1426 begin
1427 match c.c_enum with
1428 | Some e ->
1429 check_cstr_dep (List.map e.e_includes (Decl_hint.hint env.decl_env))
1430 | _ -> ()
1431 end;
1432 let impl = extends @ implements @ uses in
1433 let env =
1434 Phase.localize_and_add_ast_generic_parameters_and_where_constraints
1437 c.c_tparams
1438 c.c_where_constraints
1440 let env =
1441 Phase.check_where_constraints
1442 ~in_class:true
1443 ~use_pos:pc
1444 ~definition_pos:pc
1445 ~ety_env:(Phase.env_with_self env)
1447 (Cls.where_constraints tc)
1449 Typing_variance.class_def env c;
1450 check_no_generic_static_property tc;
1451 let check_where_constraints env ht =
1452 let (_, (p, _), _) = TUtils.unwrap_class_type ht in
1453 let (env, locl_ty) = Phase.localize_with_self env ht in
1454 match get_node (TUtils.get_base_type env locl_ty) with
1455 | Tclass (cls, _, tyl) ->
1456 (match Env.get_class env (snd cls) with
1457 | Some cls when not (List.is_empty (Cls.where_constraints cls)) ->
1458 let tc_tparams = Cls.tparams cls in
1459 let ety_env =
1461 (Phase.env_with_self env) with
1462 substs = Subst.make_locl tc_tparams tyl;
1465 Phase.check_where_constraints
1466 ~in_class:true
1467 ~use_pos:pc
1468 ~definition_pos:p
1469 ~ety_env
1471 (Cls.where_constraints cls)
1472 | _ -> env)
1473 | _ -> env
1475 let env = List.fold impl ~init:env ~f:check_where_constraints in
1476 check_parent env c tc;
1477 check_parents_sealed env c tc;
1479 let is_final = Cls.final tc in
1481 (Ast_defs.(equal_class_kind (Cls.kind tc) Cnormal) || is_final)
1482 && Cls.members_fully_known tc
1483 then (
1484 check_extend_abstract_meth ~is_final pc (Cls.methods tc);
1485 (match fst (Cls.construct tc) with
1486 | Some constr ->
1487 check_extend_abstract_meth ~is_final pc [(SN.Members.__construct, constr)]
1488 | None -> ());
1489 check_extend_abstract_meth ~is_final pc (Cls.smethods tc);
1490 check_extend_abstract_prop ~is_final pc (Cls.sprops tc);
1491 check_extend_abstract_const ~is_final pc (Cls.consts tc);
1492 check_extend_abstract_typeconst ~is_final pc (Cls.typeconsts tc)
1494 if Cls.const tc then List.iter c.c_uses (check_const_trait_members pc env);
1495 let (static_vars, vars) = split_vars c in
1496 List.iter static_vars ~f:(fun { cv_id = (p, id); _ } ->
1497 check_static_class_element (Cls.get_prop tc) ~elt_type:`Property id p);
1498 List.iter vars ~f:(fun { cv_id = (p, id); _ } ->
1499 check_dynamic_class_element (Cls.get_sprop tc) ~elt_type:`Property id p);
1500 let (constructor, static_methods, methods) = split_methods c in
1501 List.iter static_methods ~f:(fun { m_name = (p, id); _ } ->
1502 check_static_class_element (Cls.get_method tc) ~elt_type:`Method id p);
1503 List.iter methods ~f:(fun { m_name = (p, id); _ } ->
1504 check_dynamic_class_element (Cls.get_smethod tc) ~elt_type:`Method id p);
1505 let env =
1506 List.fold ~init:env impl ~f:(fun env -> class_implements_type env c)
1508 if Cls.is_disposable tc then
1509 List.iter
1510 (c.c_extends @ c.c_uses)
1511 (Typing_disposable.enforce_is_disposable env);
1512 let (env, typed_vars_and_global_inference_envs) =
1513 List.map_env env vars (class_var_def ~is_static:false tc)
1515 let (typed_vars, vars_global_inference_envs) =
1516 List.unzip typed_vars_and_global_inference_envs
1518 let (typed_methods, methods_global_inference_envs) =
1519 List.filter_map methods (method_def env tc) |> List.unzip
1521 let (env, typed_typeconsts) =
1522 List.map_env env c.c_typeconsts (typeconst_def c)
1524 let in_enum_class = Env.is_enum_class env c_name in
1525 let (env, consts) =
1526 List.map_env env c.c_consts (class_const_def ~in_enum_class c)
1528 let (typed_consts, const_types) = List.unzip consts in
1529 let env = Typing_enum.enum_class_check env tc c.c_consts const_types in
1530 let typed_constructor = class_constr_def env tc constructor in
1531 let env = Env.set_static env in
1532 let (env, typed_static_vars_and_global_inference_envs) =
1533 List.map_env env static_vars (class_var_def ~is_static:true tc)
1535 let (typed_static_vars, static_vars_global_inference_envs) =
1536 List.unzip typed_static_vars_and_global_inference_envs
1538 let (typed_static_methods, static_methods_global_inference_envs) =
1539 List.filter_map static_methods (method_def env tc) |> List.unzip
1541 let (methods, constr_global_inference_env) =
1542 match typed_constructor with
1543 | None -> (typed_static_methods @ typed_methods, [])
1544 | Some (m, global_inference_env) ->
1545 ((m :: typed_static_methods) @ typed_methods, [global_inference_env])
1547 let (env, tparams) = class_type_param env c.c_tparams in
1548 let (env, user_attributes) =
1549 List.map_env env c.c_user_attributes Typing.user_attribute
1551 let env =
1552 Typing_solver.solve_all_unsolved_tyvars env Errors.bad_class_typevar
1555 ( if TypecheckerOptions.enable_sound_dynamic (Provider_context.get_tcopt ctx)
1556 then
1557 let parent_names =
1558 List.filter_map
1559 (c.c_extends @ c.c_uses @ c.c_implements)
1560 (function
1561 | (_, Happly ((_, name), _)) -> Some name
1562 | _ -> None)
1564 let error_parent_implements_dynamic parent f =
1565 Errors.parent_implements_dynamic
1566 (fst c.c_name)
1567 (snd c.c_name, c.c_kind)
1568 (Cls.name parent, Cls.kind parent)
1571 List.iter parent_names (fun name ->
1572 match Env.get_class_dep env name with
1573 | Some parent_type ->
1574 begin
1575 match Cls.kind parent_type with
1576 | Ast_defs.Cnormal
1577 | Ast_defs.Cabstract ->
1580 (Bool.equal
1581 (Cls.get_implements_dynamic parent_type)
1582 c.c_implements_dynamic)
1583 then
1584 error_parent_implements_dynamic
1585 parent_type
1586 c.c_implements_dynamic
1587 | Ast_defs.Ctrait ->
1589 c.c_implements_dynamic
1590 && not (Cls.get_implements_dynamic parent_type)
1591 then
1592 error_parent_implements_dynamic parent_type true
1593 | Ast_defs.Cinterface ->
1595 (not c.c_implements_dynamic)
1596 && Cls.get_implements_dynamic parent_type
1597 then
1598 error_parent_implements_dynamic parent_type false
1599 else if
1600 Ast_defs.is_c_interface c.c_kind
1601 && not
1602 (Bool.equal
1603 (Cls.get_implements_dynamic parent_type)
1604 c.c_implements_dynamic)
1605 then
1606 error_parent_implements_dynamic
1607 parent_type
1608 c.c_implements_dynamic
1609 | _ -> ()
1611 | None -> ()) );
1614 Aast.c_span = c.c_span;
1615 Aast.c_annotation = Env.save (Env.get_tpenv env) env;
1616 Aast.c_mode = c.c_mode;
1617 Aast.c_final = c.c_final;
1618 Aast.c_is_xhp = c.c_is_xhp;
1619 Aast.c_has_xhp_keyword = c.c_has_xhp_keyword;
1620 Aast.c_kind = c.c_kind;
1621 Aast.c_name = c.c_name;
1622 Aast.c_tparams = tparams;
1623 Aast.c_extends = c.c_extends;
1624 Aast.c_uses = c.c_uses;
1625 (* c_use_as_alias and c_insteadof_alias are PHP features not supported
1626 * in Hack but are required since we have runtime support for it
1628 Aast.c_use_as_alias = [];
1629 Aast.c_insteadof_alias = [];
1630 Aast.c_xhp_attr_uses = c.c_xhp_attr_uses;
1631 Aast.c_xhp_category = c.c_xhp_category;
1632 Aast.c_reqs = c.c_reqs;
1633 Aast.c_implements = c.c_implements;
1634 Aast.c_implements_dynamic = c.c_implements_dynamic;
1635 Aast.c_where_constraints = c.c_where_constraints;
1636 Aast.c_consts = typed_consts;
1637 Aast.c_typeconsts = typed_typeconsts;
1638 Aast.c_vars = typed_static_vars @ typed_vars;
1639 Aast.c_methods = methods;
1640 Aast.c_file_attributes = file_attrs;
1641 Aast.c_user_attributes = user_attributes;
1642 Aast.c_namespace = c.c_namespace;
1643 Aast.c_enum = c.c_enum;
1644 Aast.c_doc_comment = c.c_doc_comment;
1645 Aast.c_attributes = [];
1646 Aast.c_xhp_children = c.c_xhp_children;
1647 Aast.c_xhp_attrs = [];
1648 Aast.c_emit_id = c.c_emit_id;
1650 methods_global_inference_envs
1651 @ static_methods_global_inference_envs
1652 @ constr_global_inference_env
1653 @ static_vars_global_inference_envs
1654 @ vars_global_inference_envs )
1656 let class_def ctx c =
1657 Counters.count Counters.Category.Typecheck @@ fun () ->
1658 Errors.run_with_span c.c_span @@ fun () ->
1659 let env = EnvFromDef.class_env ~origin:Decl_counters.TopLevel ctx c in
1660 let tc = Env.get_class env (snd c.c_name) in
1661 let env = Env.set_env_pessimize env in
1662 Typing_helpers.add_decl_errors (Option.bind tc Cls.decl_errors);
1663 Typing_check_decls.class_ env c;
1664 NastInitCheck.class_ env c;
1665 match tc with
1666 | None ->
1667 (* This can happen if there was an error during the declaration
1668 * of the class. *)
1669 None
1670 | Some tc ->
1671 (* If there are duplicate definitions of the class then we will end up
1672 * checking one AST with respect to the decl corresponding to the other definition.
1673 * Naming has already detected duplicates, so let's just avoid cascading unhelpful
1674 * typing errors, and also avoid triggering the bad position assert
1676 if not (Pos.equal (fst c.c_name) (Cls.pos tc)) then
1677 None
1678 else
1679 let env = Typing_requirements.check_class env tc in
1680 if shallow_decl_enabled ctx then Typing_inheritance.check_class env tc;
1681 Some (class_def_ env c tc)
1683 let gconst_def ctx cst =
1684 Counters.count Counters.Category.Typecheck @@ fun () ->
1685 Errors.run_with_span cst.cst_span @@ fun () ->
1686 let env = EnvFromDef.gconst_env ~origin:Decl_counters.TopLevel ctx cst in
1687 let env = Env.set_env_pessimize env in
1688 let (typed_cst_value, env) =
1689 let value = cst.cst_value in
1690 match cst.cst_type with
1691 | Some hint ->
1692 let ty = Decl_hint.hint env.decl_env hint in
1693 let ty = Typing_enforceability.compute_enforced_ty env ty in
1694 let (env, dty) = Phase.localize_possibly_enforced_with_self env ty in
1695 let (env, te, value_type) =
1696 let expected =
1697 ExpectedTy.make_and_allow_coercion (fst hint) Reason.URhint dty
1699 expr_with_pure_coeffects env ~expected value
1701 let env =
1702 Typing_coercion.coerce_type
1703 (fst hint)
1704 Reason.URhint
1706 value_type
1708 Errors.unify_error
1710 (te, env)
1711 | None ->
1713 (not (is_literal_expr value))
1714 && Partial.should_check_error cst.cst_mode 2035
1715 then
1716 Errors.missing_typehint (fst cst.cst_name);
1717 let (env, te, _value_type) = expr_with_pure_coeffects env value in
1718 (te, env)
1721 Aast.cst_annotation = Env.save (Env.get_tpenv env) env;
1722 Aast.cst_mode = cst.cst_mode;
1723 Aast.cst_name = cst.cst_name;
1724 Aast.cst_type = cst.cst_type;
1725 Aast.cst_value = typed_cst_value;
1726 Aast.cst_namespace = cst.cst_namespace;
1727 Aast.cst_span = cst.cst_span;
1728 Aast.cst_emit_id = cst.cst_emit_id;
1731 let record_field env f =
1732 let (id, hint, e) = f in
1733 let ((p, _) as cty) = hint in
1734 let (env, cty) =
1735 let cty = Decl_hint.hint env.decl_env cty in
1736 Phase.localize_with_self env cty
1738 let expected = ExpectedTy.make p Reason.URhint cty in
1739 match e with
1740 | Some e ->
1741 let (env, te, ty) = expr_with_pure_coeffects env ~expected e in
1742 let env =
1743 Typing_coercion.coerce_type
1745 Reason.URhint
1748 (MakeType.unenforced cty)
1749 Errors.record_init_value_does_not_match_hint
1751 (env, (id, hint, Some te))
1752 | None -> (env, (id, hint, None))
1754 let record_def_parent env rd parent_hint =
1755 match snd parent_hint with
1756 | Aast.Happly ((parent_pos, parent_name), []) ->
1757 (match Decl_provider.get_record_def (Env.get_ctx env) parent_name with
1758 | Some parent_rd ->
1759 (* We can only inherit from abstract records. *)
1760 ( if not parent_rd.rdt_abstract then
1761 let (parent_pos, parent_name) = parent_rd.rdt_name in
1762 Errors.extend_non_abstract_record
1763 parent_name
1764 (fst rd.rd_name)
1765 parent_pos );
1767 (* Ensure we aren't defining fields that overlap with
1768 inherited fields. *)
1769 let inherited_fields = Typing_helpers.all_record_fields env parent_rd in
1770 List.iter rd.rd_fields ~f:(fun ((pos, name), _, _) ->
1771 match SMap.find_opt name inherited_fields with
1772 | Some ((prev_pos, _), _) ->
1773 Errors.repeated_record_field name pos prev_pos
1774 | None -> ())
1775 | None ->
1776 (* Something exists with this name (naming succeeded), but it's
1777 not a record. *)
1778 Errors.unbound_name parent_pos parent_name Errors.RecordContext)
1779 | _ ->
1780 failwith
1781 "Record parent was not an Happly. This should have been a syntax error."
1783 (* Report an error if we have inheritance cycles in record declarations. *)
1784 let check_record_inheritance_cycle env ((rd_pos, rd_name) : Aast.sid) : unit =
1785 let rec worker name trace seen =
1786 match Decl_provider.get_record_def (Env.get_ctx env) name with
1787 | Some rd ->
1788 (match rd.rdt_extends with
1789 | Some (_, parent_name) when String.equal parent_name rd_name ->
1790 (* This record is in an inheritance cycle.*)
1791 Errors.cyclic_record_def trace rd_pos
1792 | Some (_, parent_name) when SSet.mem parent_name seen ->
1793 (* There's an inheritance cycle higher in the chain. *)
1795 | Some (_, parent_name) ->
1796 worker parent_name (parent_name :: trace) (SSet.add parent_name seen)
1797 | None -> ())
1798 | None -> ()
1800 worker rd_name [rd_name] (SSet.singleton rd_name)
1802 let record_def_def ctx rd =
1803 Counters.count Counters.Category.Typecheck @@ fun () ->
1804 let env = EnvFromDef.record_def_env ~origin:Decl_counters.TopLevel ctx rd in
1805 (match rd.rd_extends with
1806 | Some parent -> record_def_parent env rd parent
1807 | None -> ());
1809 check_record_inheritance_cycle env rd.rd_name;
1811 let (env, attributes) =
1812 List.map_env env rd.rd_user_attributes Typing.user_attribute
1814 let (_env, fields) = List.map_env env rd.rd_fields record_field in
1816 Aast.rd_annotation = Env.save (Env.get_tpenv env) env;
1817 Aast.rd_name = rd.rd_name;
1818 Aast.rd_extends = rd.rd_extends;
1819 Aast.rd_abstract = rd.rd_abstract;
1820 Aast.rd_fields = fields;
1821 Aast.rd_user_attributes = attributes;
1822 Aast.rd_namespace = rd.rd_namespace;
1823 Aast.rd_span = rd.rd_span;
1824 Aast.rd_doc_comment = rd.rd_doc_comment;
1825 Aast.rd_emit_id = rd.rd_emit_id;
1828 let nast_to_tast_gienv ~(do_tast_checks : bool) ctx nast :
1829 _ * Typing_inference_env.t_global_with_pos list =
1830 let convert_def = function
1831 (* Sometimes typing will just return `None` but that should only be the case
1832 * if an error had already been registered e.g. in naming
1834 | Fun f ->
1835 begin
1836 match fun_def ctx f with
1837 | Some (f, env) -> Some (Aast.Fun f, [env])
1838 | None -> None
1840 | Constant gc -> Some (Aast.Constant (gconst_def ctx gc), [])
1841 | Typedef td -> Some (Aast.Typedef (Typing.typedef_def ctx td), [])
1842 | Class c ->
1843 begin
1844 match class_def ctx c with
1845 | Some (c, envs) -> Some (Aast.Class c, envs)
1846 | None -> None
1848 | RecordDef rd -> Some (Aast.RecordDef (record_def_def ctx rd), [])
1849 (* We don't typecheck top level statements:
1850 * https://docs.hhvm.com/hack/unsupported/top-level
1851 * so just create the minimal env for us to construct a Stmt.
1853 | Stmt s ->
1854 let env = Env.empty ctx Relative_path.default None in
1855 Some (Aast.Stmt (snd (Typing.stmt env s)), [])
1856 | Namespace _
1857 | NamespaceUse _
1858 | SetNamespaceEnv _
1859 | FileAttributes _ ->
1860 failwith
1861 "Invalid nodes in NAST. These nodes should be removed during naming."
1863 Nast_check.program ctx nast;
1864 let (tast, envs) = List.unzip @@ List.filter_map nast convert_def in
1865 let envs = List.concat envs in
1866 if do_tast_checks then Tast_check.program ctx tast;
1867 (tast, envs)
1869 let nast_to_tast ~do_tast_checks ctx nast =
1870 let (tast, _gienvs) = nast_to_tast_gienv ~do_tast_checks ctx nast in
1871 tast