2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
11 (** Module "naming" a program.
13 * The naming phase consists in several things
14 * 1- get all the global names
15 * 2- transform all the local names into a unique identifier
23 module ShapeMap
= N.ShapeMap
24 module SN
= Naming_special_names
26 module GEnv
= NamingGlobal.GEnv
28 (*****************************************************************************)
30 (*****************************************************************************)
32 (* We want to keep the positions of names that have been
33 * replaced by identifiers.
35 type positioned_ident
= (Pos.t
* Local_id.t
)
37 (* <T as A>, A is a type constraint *)
38 type type_constraint
= (Ast.constraint_kind
* Ast.hint
) list
42 (* strict? decl? partial? *)
43 in_mode
: FileInfo.mode
;
45 (* various options that control the strictness of the typechecker *)
46 tcopt
: TypecheckerOptions.t
;
48 (* are we in the body of a try statement? *)
51 (* In function foo<T1, ..., Tn> or class<T1, ..., Tn>, the field
52 * type_params knows T1 .. Tn. It is able to find out about the
53 * constraint on these parameters. *)
54 type_params
: type_constraint
SMap.t
;
56 (* The current class, None if we are in a function *)
57 current_cls
: (Ast.id
* Ast.class_kind
) option;
59 class_consts
: (string, Pos.t
) Hashtbl.t
;
61 class_props
: (string, Pos.t
) Hashtbl.t
;
63 (* Normally we don't need to add dependencies at this stage, but there
64 * are edge cases when we do. *)
65 droot
: Typing_deps.Dep.variant
;
67 (* Namespace environment, e.g., what namespace we're in and what use
68 * declarations are in play. *)
69 namespace
: Namespace_env.env
;
72 (* How to behave when we see an unbound name. Either we raise an
73 * error, or we call a function first and continue if it can resolve
74 * the name. This is used to nest environments when processing
78 | UBMFunc
of ((Pos.t
* string) -> positioned_ident
)
80 (* The primitives to manipulate the naming environment *)
86 val empty_local
: unbound_mode
-> lenv
88 TypecheckerOptions.t
->
89 type_constraint
SMap.t
->
91 Ast.id
* Ast.class_kind
-> Namespace_env.env
-> genv
93 TypecheckerOptions.t
->
94 type_constraint
SMap.t
-> Ast.class_
-> genv
* lenv
95 val make_typedef_env
:
96 TypecheckerOptions.t
->
97 type_constraint
SMap.t
-> Ast.typedef
-> genv
* lenv
99 TypecheckerOptions.t
->
100 type_constraint
SMap.t
->
101 FileInfo.mode
-> string -> Namespace_env.env
-> genv
102 val make_fun_decl_genv
:
103 TypecheckerOptions.t
->
104 type_constraint
SMap.t
-> Ast.fun_
-> genv
105 val make_const_env
: TypecheckerOptions.t
-> Ast.gconst
-> genv
* lenv
107 val has_unsafe
: genv
* lenv
-> bool
108 val set_unsafe
: genv
* lenv
-> bool -> unit
110 val add_lvar
: genv
* lenv
-> Ast.id
-> positioned_ident
-> unit
111 val add_param
: genv
* lenv
-> N.fun_param
-> genv
* lenv
112 val new_lvar
: genv
* lenv
-> Ast.id
-> positioned_ident
113 val found_dollardollar
: genv
* lenv
-> Pos.t
-> positioned_ident
114 val inside_pipe
: genv
* lenv
-> bool
115 val new_pending_lvar
: genv
* lenv
-> Ast.id
-> unit
116 val promote_pending
: genv
* lenv
-> unit
117 val lvar
: genv
* lenv
-> Ast.id
-> positioned_ident
118 val global_const
: genv
* lenv
-> Ast.id
-> Ast.id
119 val type_name
: genv
* lenv
-> Ast.id
-> allow_typedef
:bool -> Ast.id
120 val fun_id
: genv
* lenv
-> Ast.id
-> Ast.id
121 val bind_class_const
: genv
* lenv
-> Ast.id
-> unit
122 val bind_prop
: genv
* lenv
-> Ast.id
-> unit
124 val scope
: genv
* lenv
-> (genv
* lenv
-> 'a
) -> 'a
125 val scope_all
: genv
* lenv
-> (genv
* lenv
-> 'a
) -> all_locals
* 'a
126 val extend_all_locals
: genv
* lenv
-> all_locals
-> unit
127 val pipe_scope
: genv
* lenv
-> (genv
* lenv
-> N.expr
) -> Local_id.t
* N.expr
131 type map
= positioned_ident
SMap.t
132 type all_locals
= Pos.t
SMap.t
134 (* The local environment *)
137 (* The set of locals *)
140 (* We keep all the locals, even if we are in a different scope
141 * to provide better error messages.
146 * Technically, passed this point, $x is unbound.
147 * But it is much better to keep it somewhere, so that you can
148 * say it is bound, but in a different scope.
150 all_locals
: all_locals
ref;
152 (* Some statements can define new variables afterwards, e.g.,
158 * We need to give $x the same name in both branches, but we don't want
159 * $x to actually be a local until after the if block. So we stash it here,
160 * to indicate a name has been pre-allocated, but that the variable isn't
161 * actually defined yet.
163 pending_locals
: map
ref;
165 (* Tag controlling what we do when we encounter an unbound name.
166 * This is used when processing a lambda expression body that has
167 * an automatic use list.
169 * See expr_lambda for details.
171 unbound_mode
: unbound_mode
;
173 (* The presence of an "UNSAFE" in the function body changes the
174 * verifiability of the function's return type, since the unsafe
175 * block could return. For the sanity of the typechecker, we flatten
176 * this out, but need to track if we've seen an "UNSAFE" in order to
178 has_unsafe
: bool ref;
180 (** Allows us to ban $$ appearances outside of pipe expressions and
181 * equals expressions within pipes. *)
182 inside_pipe
: bool ref;
185 let empty_local unbound_mode
= {
186 locals
= ref SMap.empty
;
187 all_locals
= ref SMap.empty
;
188 pending_locals
= ref SMap.empty
;
190 has_unsafe
= ref false;
191 inside_pipe
= ref false;
194 let make_class_genv tcopt tparams mode
(cid
, ckind
) namespace
= {
196 (if !Autocomplete.auto_complete
then FileInfo.Mpartial
else mode
);
199 type_params
= tparams
;
200 current_cls
= Some
(cid
, ckind
);
201 class_consts
= Hashtbl.create
0;
202 class_props
= Hashtbl.create
0;
203 droot
= Typing_deps.Dep.Class
(snd cid
);
207 let make_class_env tcopt tparams c
=
208 let genv = make_class_genv tcopt tparams c
.c_mode
209 (c
.c_name
, c
.c_kind
) c
.c_namespace
in
210 let lenv = empty_local UBMErr
in
211 let env = genv, lenv in
214 let make_typedef_genv tcopt cstrs tdef
= {
215 in_mode
= FileInfo.(if !Ide.is_ide_mode
then Mpartial
else Mstrict
);
220 class_consts
= Hashtbl.create
0;
221 class_props
= Hashtbl.create
0;
222 droot
= Typing_deps.Dep.Class
(snd tdef
.t_id
);
223 namespace
= tdef
.t_namespace
;
226 let make_typedef_env genv cstrs tdef
=
227 let genv = make_typedef_genv genv cstrs tdef
in
228 let lenv = empty_local UBMErr
in
229 let env = genv, lenv in
232 let make_fun_genv tcopt params f_mode f_name f_namespace
= {
236 type_params
= params
;
238 class_consts
= Hashtbl.create
0;
239 class_props
= Hashtbl.create
0;
240 droot
= Typing_deps.Dep.Fun f_name
;
241 namespace
= f_namespace
;
244 let make_fun_decl_genv nenv params f
=
245 make_fun_genv nenv params f
.f_mode
(snd f
.f_name
) f
.f_namespace
247 let make_const_genv tcopt cst
= {
248 in_mode
= cst
.cst_mode
;
251 type_params
= SMap.empty
;
253 class_consts
= Hashtbl.create
0;
254 class_props
= Hashtbl.create
0;
255 droot
= Typing_deps.Dep.GConst
(snd cst
.cst_name
);
256 namespace
= cst
.cst_namespace
;
259 let make_const_env nenv cst
=
260 let genv = make_const_genv nenv cst
in
261 let lenv = empty_local UBMErr
in
262 let env = genv, lenv in
265 let has_unsafe (_genv
, lenv) = !(lenv.has_unsafe)
266 let set_unsafe (_genv
, lenv) x
=
269 let lookup genv env (p
, x
) =
273 (match genv.in_mode
with
274 | FileInfo.Mstrict
-> Errors.unbound_name p x `const
275 | FileInfo.Mpartial
| FileInfo.Mdecl
when not
276 (TypecheckerOptions.assume_php
genv.tcopt
) ->
277 Errors.unbound_name p x `const
278 | FileInfo.Mphp
| FileInfo.Mdecl
| FileInfo.Mpartial
-> ()
282 (* Check and see if the user might have been trying to use one of the
283 * generics in scope as a runtime value *)
284 let check_no_runtime_generic genv (p
, name
) =
285 let tparaml = SMap.keys
genv.type_params
in
286 if List.mem
tparaml name
then Errors.generic_at_runtime p
;
289 let handle_unbound_name genv get_pos get_canon
(p
, name
) kind
=
290 match get_canon name
with
294 |> Option.iter ~f
:(fun p_canon
->
295 Errors.did_you_mean_naming p name p_canon canonical
);
296 (* Recovering from the capitalization error means
297 * returning the name in its canonical form *)
300 (match genv.in_mode
with
301 | FileInfo.Mpartial
| FileInfo.Mdecl
302 when TypecheckerOptions.assume_php
genv.tcopt
303 || name
= SN.Classes.cUnknown
-> ()
304 | FileInfo.Mphp
-> ()
305 | FileInfo.Mstrict
-> Errors.unbound_name p name kind
306 | FileInfo.Mpartial
| FileInfo.Mdecl
->
307 Errors.unbound_name p name kind
311 let canonicalize genv get_pos get_canon
(p
, name
) kind
=
312 match get_pos name
with
314 | None
-> handle_unbound_name genv get_pos get_canon
(p
, name
) kind
316 let check_variable_scoping env (p
, x
) =
317 match SMap.get x
!(env.all_locals
) with
318 | Some p'
-> Errors.different_scope p x p'
321 (* Adds a local variable, without any check *)
322 let add_lvar (_
, lenv) (_
, name
) (p
, x
) =
323 lenv.locals
:= SMap.add name
(p
, x
) !(lenv.locals
);
324 Naming_hooks.dispatch_lvar_hook x
(p
, name
) !(lenv.locals
);
327 let add_param env param
=
328 let p_name = param
.N.param_name
in
329 let id = Local_id.get
p_name in
330 let p_pos = param
.N.param_pos
in
331 let () = add_lvar env (p_pos, p_name) (p_pos, id) in
334 (* Defines a new local variable.
336 1) if the local is not in the local environment then it is added.
337 2) the naming hook callback is invoked.
338 Return value: the given position and deduced/created identifier. *)
339 let new_lvar (_
, lenv) (p
, x
) =
340 let lcl = SMap.get x
!(lenv.locals
) in
343 | Some
lcl -> snd
lcl
345 let ident = match SMap.get x
!(lenv.pending_locals
) with
346 | Some
(_
, ident) -> ident
347 | None
-> Local_id.make x
in
348 lenv.all_locals
:= SMap.add x p
!(lenv.all_locals
);
349 lenv.locals
:= SMap.add x
(p
, ident) !(lenv.locals
);
352 Naming_hooks.dispatch_lvar_hook
ident (p
, x
) !(lenv.locals
);
355 (* Defines a new local variable for this dollardollar (or reuses
356 * the exiting identifier). *)
357 let found_dollardollar (genv, lenv) p
=
358 if not
!(lenv.inside_pipe
) then
359 Errors.undefined p
SN.SpecialIdents.dollardollar
;
360 new_lvar (genv, lenv) (p
, SN.SpecialIdents.dollardollar
)
362 let inside_pipe (_
, lenv) =
365 let new_pending_lvar (_
, lenv) (p
, x
) =
366 match SMap.get x
!(lenv.locals
), SMap.get x
!(lenv.pending_locals
) with
368 let y = p
, Local_id.make x
in
369 lenv.pending_locals
:= SMap.add x
y !(lenv.pending_locals
)
372 let promote_pending (_
, lenv as env) =
373 SMap.iter
begin fun x
(p
, ident) ->
374 add_lvar env (p
, x
) (p
, ident)
375 end !(lenv.pending_locals
);
376 lenv.pending_locals
:= SMap.empty
378 let handle_undefined_variable (_genv
, env) (p
, x
) =
379 match env.unbound_mode
with
380 | UBMErr
-> Errors.undefined p x
; p
, Local_id.make x
381 | UBMFunc f
-> f
(p
, x
)
383 (* Function used to name a local variable *)
384 let lvar (genv, env) (p
, x
) =
386 if SN.Superglobals.is_superglobal x
&& genv.in_mode
= FileInfo.Mpartial
387 then p, Local_id.make x
389 let lcl = SMap.get x
!(env.locals
) in
391 | Some
lcl -> p, snd
lcl
392 | None
when not
!Autocomplete.auto_complete
->
393 check_variable_scoping env (p, x
);
394 handle_undefined_variable (genv, env) (p, x
)
395 | None
-> p, Local_id.tmp
()
397 Naming_hooks.dispatch_lvar_hook
ident (p, x
) !(env.locals
);
400 let get_name genv namespace x
=
401 lookup genv namespace x
; x
403 (* For dealing with namespace fallback on constants *)
404 let elaborate_and_get_name_with_fallback mk_dep
genv get_pos x
=
405 let get_name x
= get_name genv get_pos x
in
406 let fq_x = Namespaces.elaborate_id
genv.namespace NSConst x
in
408 genv.namespace
.Namespace_env.ns_name
<> None
&&
409 not
(String.contains
(snd x
) '
\\'
) in
410 if need_fallback then begin
411 let global_x = (fst x
, "\\" ^
(snd x
)) in
412 (* Explicitly add dependencies on both of the consts we could be
413 * referring to here. Normally naming doesn't have to deal with
414 * deps at all -- they are added during typechecking just by the
415 * nature of looking up a class or function name. However, we're
416 * flattening namespaces here, and the fallback behavior of
417 * consts means that we might suddenly be referring to a
418 * different const without any change to the callsite at
419 * all. Adding both dependencies explicitly captures this
420 * action-at-a-distance. *)
421 Typing_deps.add_idep
genv.droot
(mk_dep
(snd
fq_x));
422 Typing_deps.add_idep
genv.droot
(mk_dep
(snd
global_x));
423 let mem (_
, s
) = get_pos s
in
424 match mem fq_x, mem global_x with
425 (* Found in the current namespace *)
426 | Some _
, _
-> get_name fq_x
427 (* Found in the global namespace *)
428 | _
, Some _
-> get_name global_x
429 (* Not found. Pick the more specific one to error on. *)
430 | None
, None
-> get_name fq_x
434 (* For dealing with namespace fallback on functions *)
435 let elaborate_and_get_name_with_canonicalized_fallback
436 mk_dep
genv get_pos get_canon x
=
437 let get_name x
= get_name genv get_pos x
in
438 let canonicalize = canonicalize genv get_pos get_canon
in
439 let fq_x = Namespaces.elaborate_id
genv.namespace NSFun x
in
441 genv.namespace
.Namespace_env.ns_name
<> None
&&
442 not
(String.contains
(snd x
) '
\\'
) in
443 if need_fallback then begin
444 let global_x = (fst x
, "\\" ^
(snd x
)) in
445 (* Explicitly add dependencies on both of the functions we could be
446 * referring to here. Normally naming doesn't have to deal with deps at
447 * all -- they are added during typechecking just by the nature of
448 * looking up a class or function name. However, we're flattening
449 * namespaces here, and the fallback behavior of functions means that we
450 * might suddenly be referring to a different function without any
451 * change to the callsite at all. Adding both dependencies explicitly
452 * captures this action-at-a-distance. *)
453 Typing_deps.add_idep
genv.droot
(mk_dep
(snd
fq_x));
454 Typing_deps.add_idep
genv.droot
(mk_dep
(snd
global_x));
455 (* canonicalize the names being searched *)
456 let mem (_
, nm
) = get_canon nm
in
457 match mem fq_x, mem global_x with
458 | Some _
, _
-> (* Found in the current namespace *)
459 let fq_x = canonicalize fq_x `func
in
461 | _
, Some _
-> (* Found in the global namespace *)
462 let global_x = canonicalize global_x `func
in
465 (* Not found. Pick the more specific one to error on. *)
468 let fq_x = canonicalize fq_x `func
in
471 let global_const (genv, _env
) x
=
472 elaborate_and_get_name_with_fallback
473 (* Same idea as Dep.FunName, see below. *)
474 (fun x
-> Typing_deps.Dep.GConstName x
)
476 (GEnv.gconst_pos
genv.tcopt
)
479 let type_name (genv, _
) x ~allow_typedef
=
480 (* Generic names are not allowed to shadow class names *)
481 check_no_runtime_generic genv x
;
482 let (pos
, name
) as x
= Namespaces.elaborate_id
genv.namespace NSClass x
in
483 match GEnv.type_info
genv.tcopt name
with
484 | Some
(_def_pos
, `Class
) ->
485 (* Don't let people use strictly internal classes
486 * (except when they are being declared in .hhi files) *)
487 if name
= SN.Classes.cHH_BuiltinEnum
&&
488 not
(string_ends_with
(Relative_path.suffix
(Pos.filename pos
)) ".hhi")
489 then Errors.using_internal_class pos
(strip_ns name
);
491 | Some
(def_pos
, `Typedef
) when not allow_typedef
->
492 Errors.unexpected_typedef pos def_pos
;
494 | Some
(_def_pos
, `Typedef
) -> pos
, name
496 handle_unbound_name genv
497 (GEnv.type_pos
genv.tcopt
)
498 GEnv.type_canon_name x `cls
500 let fun_id (genv, _
) x
=
501 elaborate_and_get_name_with_canonicalized_fallback
502 (* Not just Dep.Fun, but Dep.FunName. This forces an incremental full
503 * redeclaration of this class if the name changes, not just a
504 * retypecheck -- the name that is referred to here actually changes as
505 * a result of what else is defined, which is stronger than just the need
507 (fun x
-> Typing_deps.Dep.FunName x
)
509 (GEnv.fun_pos
genv.tcopt
)
513 let bind_class_member tbl
(p, x
) =
515 let p'
= Hashtbl.find tbl x
in
516 Errors.error_name_already_bound x x
p p'
518 Hashtbl.replace tbl x
p
520 let bind_class_const (genv, _env
) (p, x
) =
521 if String.lowercase x
= "class" then Errors.illegal_member_variable_class
p;
522 bind_class_member genv.class_consts
(p, x
)
524 let bind_prop (genv, _env
) x
=
525 bind_class_member genv.class_props x
527 (* Scope, keep the locals, go and name the body, and leave the
528 * local environment intact
531 let _genv, lenv = env in
532 let lenv_copy = !(lenv.locals
) in
533 let lenv_pending_copy = !(lenv.pending_locals
) in
535 lenv.locals
:= lenv_copy;
536 lenv.pending_locals
:= lenv_pending_copy;
539 let scope_all env f
=
540 let _genv, lenv = env in
541 let lenv_all_locals_copy = !(lenv.all_locals
) in
542 let res = scope env f
in
543 let lenv_all_locals = !(lenv.all_locals
) in
544 lenv.all_locals
:= lenv_all_locals_copy;
547 let extend_all_locals (_genv, lenv) more_locals
=
548 lenv.all_locals
:= SMap.union more_locals
!(lenv.all_locals
)
550 (** Sets up the environment so that naming can be done on the RHS of a
551 * pipe expression. It returns the identity of the $$ in the RHS and the
552 * named RHS. The steps are as follows:
553 * - Removes the $$ from the local env
554 * - Name the RHS scope
555 * - Restore the binding of $$ in the local env (if it was bound).
557 * This will append an error if $$ was not used in the RHS.
559 * The inside_pipe flag is also set before the naming and restored afterwards.
561 let pipe_scope env name_e2
=
563 let outer_pipe_var_opt =
564 SMap.get
SN.SpecialIdents.dollardollar
!(lenv.locals
) in
565 let inner_locals = SMap.remove
SN.SpecialIdents.dollardollar
567 lenv.locals
:= inner_locals;
568 lenv.inside_pipe := true;
569 (** Name the RHS of the pipe expression. During this naming, if the $$ from
570 * this pipe is used, it will be added to the locals. *)
571 let e2 = name_e2
env in
573 match SMap.get
SN.SpecialIdents.dollardollar
!(lenv.locals
) with
575 Errors.dollardollar_unused
(fst
e2);
576 (** The $$ lvar should be named when it is encountered inside e2,
577 * but we've now discovered it wasn't used at all.
578 * Create an ID here so we can keep going. *)
579 Local_id.make
SN.SpecialIdents.dollardollar
582 let restored_locals = SMap.remove
SN.SpecialIdents.dollardollar
584 (match outer_pipe_var_opt with
586 lenv.locals
:= restored_locals;
587 lenv.inside_pipe := false;
589 | Some outer_pipe_var
-> begin
590 let restored_locals = SMap.add
SN.SpecialIdents.dollardollar
591 outer_pipe_var
restored_locals in
592 lenv.locals
:= restored_locals;
598 (*****************************************************************************)
600 (*****************************************************************************)
602 (* Alok is constantly complaining that in partial mode,
603 * he forgets to bind a type parameter, for example T,
604 * and because partial assumes T is just a class that lives
605 * in PHP land there is no error message.
606 * So to help him, I am adding a rule that if
607 * the class name starts with a T and is only 2 characters
608 * it is considered a type variable. You will not be able to
609 * define a class T in php land in this scheme ... But it is a bad
610 * name for a class anyway.
612 let is_alok_type_name (_, x
) = String.length x
<= 2 && x
.[0] = 'T'
614 let check_constraint (_, (pos
, name
), _) =
615 (* TODO refactor this in a separate module for errors *)
616 if String.lowercase name
= "this"
617 then Errors.this_reserved pos
618 else if name
.[0] <> 'T'
then Errors.start_with_T pos
620 let check_repetition s param
=
621 let x = snd param
.param_id
in
623 then Errors.already_bound
(fst param
.param_id
) x;
624 if x <> SN.SpecialIdents.placeholder
then SSet.add
x s
else s
626 let convert_shape_name env = function
627 | SFlit
(pos
, s
) -> (pos
, SFlit
(pos
, s
))
628 | SFclass_const
(x, (pos
, y)) ->
630 if (snd
x) = SN.Classes.cSelf
then
631 match (fst
env).current_cls
with
632 | Some
(cid
, _) -> cid
633 | None
-> Errors.self_outside_class pos
; (pos
, SN.Classes.cUnknown
)
634 else Env.type_name env x ~allow_typedef
:false in
635 (pos
, SFclass_const
(class_name, (pos
, y)))
637 let arg_unpack_unexpected = function
639 | (pos
, _) :: _ -> Errors.naming_too_few_arguments pos
; ()
641 module type GetLocals
= sig
642 val stmt
: TypecheckerOptions.t
-> Namespace_env.env * Pos.t
SMap.t
->
643 Ast.stmt
-> Namespace_env.env * Pos.t
SMap.t
644 val lvalue
: TypecheckerOptions.t
-> Namespace_env.env * Pos.t
SMap.t
->
645 Ast.expr
-> Namespace_env.env * Pos.t
SMap.t
648 (* This was made a functor due to the awkward nature of how our naming
649 * code is structured.
651 * Naming is called both in the decl phase and type-check phase. In the
652 * decl phase it's mostly used to construct things that do not belong in
653 * function bodies; examples include classes, their member fields, and
654 * global constants. This part of naming is entirely self-contained; it
655 * only uses the data from the AST in the current file, and does not need
656 * to cross-reference decl type data from other files.
658 * In the type-check phase, Naming is invoked again, this time to name the
659 * bodies of functions. Now it requires decl type data in order to know
660 * which function calls are marked as `noreturn`, because this affects
661 * which local variables are considered to be defined at the end of a
664 * So decl depends on naming, but naming also depends on decl, creating
665 * a circular dependency. The obvious solution would be to split it into
666 * two, but this is nontrivial because decl-phase naming also does some
667 * naming of expressions -- for example, constant initializers and default
668 * parameter values have them. Of course, none of these expressions can
669 * actually contain local variables, but our code is not written in a way
670 * that the OCaml type system can understand that. So as a hacky solution,
671 * I'm parameterizing GetLocals so that it is a no-op in the decl phase
672 * but can be properly instantiated with Typing_get_locals in the typing
675 module Make
(GetLocals
: GetLocals
) = struct
677 (************************************************************************)
678 (* Naming of type hints *)
679 (************************************************************************)
681 ?
(is_static_var
=false)
683 ?
(allow_retonly
=false)
684 ?
(allow_typedef
=true)
685 ?
(allow_wildcard
=false)
687 p, hint_ ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
690 and shape_field_to_shape_field_info
env { sf_optional
; sf_name
=_; sf_hint
} =
692 N.sfi_optional
=sf_optional
;
693 sfi_hint
=hint env sf_hint
;
696 and ast_shape_info_to_nast_shape_info
698 { si_allows_unknown_fields
; si_shape_field_list
} =
699 let f fdm shape_field
=
700 let pos, name
= convert_shape_name env shape_field
.sf_name
in
701 if ShapeMap.mem name fdm
702 then Errors.fd_name_already_bound
pos;
704 name
(shape_field_to_shape_field_info
env shape_field
) fdm
in
706 List.fold_left si_shape_field_list ~init
:ShapeMap.empty ~
f in
708 nsi_allows_unknown_fields
=si_allows_unknown_fields
;
712 and hint_ ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
713 is_static_var
env x =
715 hint ~is_static_var ~forbid_this ~allow_typedef ~allow_wildcard
in
718 N.Htuple
(List.map hl
(hint ~allow_retonly
env))
720 (* void/noreturn are permitted for Typing.option_return_only_typehint *)
721 N.Hoption
(hint ~allow_retonly
env h
)
723 let h = hint ~allow_retonly
env h
725 | Hfun
(hl
, opt
, h) ->
726 N.Hfun
(List.map hl
(hint env), opt
,
727 hint ~allow_retonly
:true env h)
728 | Happly
((p, _x
) as id, hl
) ->
730 hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
734 | N.Hprim
_ | N.Hmixed
->
735 if hl
<> [] then Errors.unexpected_type_arguments
p
739 | Haccess
((pos, root_id
) as root
, id, ids
) ->
742 | x when x = SN.Classes.cSelf
->
743 (match (fst
env).current_cls
with
745 Errors.self_outside_class
pos;
750 | x when x = SN.Classes.cStatic
|| x = SN.Classes.cParent
->
751 Errors.invalid_type_access_root root
; N.Hany
754 hint_id ~forbid_this ~allow_retonly
755 ~allow_typedef ~allow_wildcard
:false env is_static_var root
[] in
757 | N.Hthis
| N.Happly
_ as h -> h
758 | _ -> Errors.invalid_type_access_root root
; N.Hany
761 N.Haccess
((pos, root_ty), id :: ids
)
762 | Hshape ast_shape_info
->
763 N.Hshape
(ast_shape_info_to_nast_shape_info
env ast_shape_info
)
765 and hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
766 env is_static_var
(p, x as id) hl
=
767 Naming_hooks.dispatch_hint_hook
id;
768 let params = (fst
env).type_params
in
769 if is_alok_type_name id && not
(SMap.mem x params)
770 then Errors.typeparam_alok
id;
771 if is_static_var
&& SMap.mem x params
772 then Errors.generic_class_var
(fst
id);
773 (* some common Xhp screw ups *)
774 if (x = "Xhp") || (x = ":Xhp") || (x = "XHP")
775 then Errors.disallowed_xhp_type
p x;
776 match try_castable_hint ~forbid_this
env p x hl
with
780 | x when x = "_" && allow_wildcard
->
782 (Errors.tparam_with_tparam
p x;
786 | x when x.[0] = '
\\'
&&
787 ( x = ("\\"^
SN.Typehints.void
)
788 || x = ("\\"^
SN.Typehints.noreturn
)
789 || x = ("\\"^
SN.Typehints.int)
790 || x = ("\\"^
SN.Typehints.bool)
791 || x = ("\\"^
SN.Typehints.float)
792 || x = ("\\"^
SN.Typehints.num
)
793 || x = ("\\"^
SN.Typehints.string)
794 || x = ("\\"^
SN.Typehints.resource
)
795 || x = ("\\"^
SN.Typehints.mixed
)
796 || x = ("\\"^
SN.Typehints.array
)
797 || x = ("\\"^
SN.Typehints.arraykey
)
798 || x = ("\\"^
SN.Typehints.integer
)
799 || x = ("\\"^
SN.Typehints.boolean
)
800 || x = ("\\"^
SN.Typehints.double
)
801 || x = ("\\"^
SN.Typehints.real)
803 Errors.primitive_toplevel
p;
805 | x when x = SN.Typehints.void
&& allow_retonly
-> N.Hprim
N.Tvoid
806 | x when x = SN.Typehints.void
->
807 Errors.return_only_typehint
p `void
;
809 | x when x = SN.Typehints.noreturn
&& allow_retonly
-> N.Hprim
N.Tnoreturn
810 | x when x = SN.Typehints.noreturn
->
811 Errors.return_only_typehint
p `noreturn
;
813 | x when x = SN.Typehints.num
-> N.Hprim
N.Tnum
814 | x when x = SN.Typehints.resource
-> N.Hprim
N.Tresource
815 | x when x = SN.Typehints.arraykey
-> N.Hprim
N.Tarraykey
816 | x when x = SN.Typehints.mixed
-> N.Hmixed
817 | x when x = SN.Typehints.this
&& not forbid_this
->
819 then Errors.this_no_argument
p;
820 (match (fst
env).current_cls
with
822 Errors.this_hint_outside_class
p;
827 | x when x = SN.Typehints.this
->
828 (match (fst
env).current_cls
with
830 Errors.this_hint_outside_class
p
832 Errors.this_type_forbidden
p
835 | x when x = SN.Classes.cClassname
&& (List.length hl
) <> 1 ->
836 Errors.classname_param
p;
838 | _ when String.lowercase
x = SN.Typehints.this
->
839 Errors.lowercase_this
p x;
841 | _ when SMap.mem x params ->
843 Errors.tparam_with_tparam
p x;
846 let name = Env.type_name env id ~allow_typedef
in
847 (* Note that we are intentionally setting allow_typedef to `true` here.
848 * In general, generics arguments can be typedefs -- there is no
849 * runtime restriction. *)
850 N.Happly
(name, hintl ~allow_wildcard ~forbid_this ~allow_typedef
:true
851 ~allow_retonly
:true env hl
)
854 (* Hints that are valid both as casts and type annotations. Neither
855 * casts nor annotations are a strict subset of the other: For
856 * instance, 'object' is not a valid annotation. Thus callers will
857 * have to handle the remaining cases. *)
858 and try_castable_hint ?
(forbid_this
=false) env p x hl
=
859 let hint = hint ~forbid_this ~allow_retonly
:false in
860 let canon = String.lowercase
x in
861 let opt_hint = match canon with
862 | nm
when nm
= SN.Typehints.int -> Some
(N.Hprim
N.Tint
)
863 | nm
when nm
= SN.Typehints.bool -> Some
(N.Hprim
N.Tbool
)
864 | nm
when nm
= SN.Typehints.float -> Some
(N.Hprim
N.Tfloat
)
865 | nm
when nm
= SN.Typehints.string -> Some
(N.Hprim
N.Tstring
)
866 | nm
when nm
= SN.Typehints.array
->
868 | [] -> N.Harray
(None
, None
)
869 | [val_
] -> N.Harray
(Some
(hint env val_
), None
)
871 N.Harray
(Some
(hint env key_
), Some
(hint env val_
))
872 | _ -> Errors.too_many_type_arguments
p; N.Hany
874 | nm
when nm
= SN.Typehints.darray
->
875 let darray_and_varray_allowed =
876 TypecheckerOptions.experimental_feature_enabled
878 TypecheckerOptions.experimental_darray_and_varray
in
879 if not
darray_and_varray_allowed then Errors.darray_not_supported
p;
882 | [_] -> Errors.too_few_type_arguments
p; N.Hany
883 | [key_
; val_
] -> N.Hdarray
(hint env key_
, hint env val_
)
884 | _ -> Errors.too_many_type_arguments
p; N.Hany
)
885 | nm
when nm
= SN.Typehints.varray
->
886 let darray_and_varray_allowed =
887 TypecheckerOptions.experimental_feature_enabled
889 TypecheckerOptions.experimental_darray_and_varray
in
890 if not
darray_and_varray_allowed then Errors.varray_not_supported
p;
892 | [] -> Errors.too_few_type_arguments
p; N.Hany
893 | [val_
] -> N.Hvarray
(hint env val_
)
894 | _ -> Errors.too_many_type_arguments
p; N.Hany
)
895 | nm
when nm
= SN.Typehints.integer
->
896 Errors.primitive_invalid_alias
p nm
SN.Typehints.int;
897 Some
(N.Hprim
N.Tint
)
898 | nm
when nm
= SN.Typehints.boolean
->
899 Errors.primitive_invalid_alias
p nm
SN.Typehints.bool;
900 Some
(N.Hprim
N.Tbool
)
901 | nm
when nm
= SN.Typehints.double
|| nm
= SN.Typehints.real ->
902 Errors.primitive_invalid_alias
p nm
SN.Typehints.float;
903 Some
(N.Hprim
N.Tfloat
)
906 let () = match opt_hint with
907 | Some
_ when canon <> x -> Errors.primitive_invalid_alias
p x canon
911 and constraint_ ?
(forbid_this
=false) env (ck
, h) = ck
, hint ~forbid_this
env h
913 and hintl ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
env l
=
915 (hint ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
env)
917 (**************************************************************************)
918 (* All the methods and static methods of an interface are "implicitly"
919 * declared as abstract
921 (**************************************************************************)
923 let add_abstract m
= {m
with N.m_abstract
= true}
925 let add_abstractl methods
= List.map methods
add_abstract
927 let interface c constructor methods smethods
=
928 if c
.c_kind
<> Cinterface
then constructor
, methods
, smethods
else
929 let constructor = Option.map
constructor add_abstract in
930 let methods = add_abstractl methods in
931 let smethods = add_abstractl smethods in
932 constructor, methods, smethods
934 (**************************************************************************)
935 (* Checking for collision on method names *)
936 (**************************************************************************)
938 let check_method acc
{ N.m_name
= (p, x); _ } =
940 then Errors.method_name_already_bound
p x;
943 let check_name_collision methods =
944 ignore
(List.fold_left
methods ~init
:SSet.empty ~
f:check_method)
946 (**************************************************************************)
947 (* Checking for shadowing of method type parameters *)
948 (**************************************************************************)
950 let check_method_tparams class_tparam_names
{ N.m_tparams
= tparams
; _ } =
951 List.iter tparams
begin fun (_, (p,x),_) ->
952 List.iter class_tparam_names
953 (fun (pc
,xc
) -> if (x = xc
) then Errors.shadowed_type_param
p pc
x)
956 let check_tparams_shadow class_tparam_names
methods =
957 List.iter
methods (check_method_tparams class_tparam_names
)
959 (* Naming of a class *)
960 let rec class_ nenv c
=
961 let constraints = make_constraints c
.c_tparams
in
962 let env = Env.make_class_env nenv
constraints c
in
963 (* Checking for a code smell *)
964 List.iter c
.c_tparams
check_constraint;
965 let name = Env.type_name env c
.c_name ~allow_typedef
:false in
967 List.fold_right c
.c_body ~init
:[] ~
f:(class_static_method
env) in
968 let sprops = List.fold_right c
.c_body ~init
:[] ~
f:(class_prop_static
env) in
969 let props = List.fold_right c
.c_body ~init
:[] ~
f:(class_prop
env) in
972 (hint ~allow_retonly
:false ~allow_typedef
:false env) in
973 let parents = match c
.c_kind
with
974 (* Make enums implicitly extend the BuiltinEnum class in order to provide
975 * utility methods. *)
977 let pos = fst
name in
978 let enum_type = pos, N.Happly
(name, []) in
980 pos, N.Happly
((pos, Naming_special_names.Classes.cHH_BuiltinEnum
),
984 let methods = List.fold_right c
.c_body ~init
:[] ~
f:(class_method
env) in
985 let uses = List.fold_right c
.c_body ~init
:[] ~
f:(class_use
env) in
987 List.fold_right c
.c_body ~init
:[] ~
f:(xhp_attr_use
env) in
989 Option.value ~default
:[] @@
990 List.fold_right c
.c_body ~init
:None ~
f:(xhp_category env) in
991 let req_implements, req_extends
= List.fold_right c
.c_body
992 ~init
:([], []) ~
f:(class_require
env c
.c_kind
) in
993 (* Setting a class type parameters constraint to the 'this' type is weird
994 * so lets forbid it for now.
996 let tparam_l = type_paraml ~forbid_this
:true env c
.c_tparams
in
997 let consts = List.fold_right ~
f:(class_const
env) c
.c_body ~init
:[] in
999 List.fold_right ~
f:(class_typeconst
env) c
.c_body ~init
:[] in
1000 let implements = List.map c
.c_implements
1001 (hint ~allow_retonly
:false ~allow_typedef
:false env) in
1002 let constructor = List.fold_left ~
f:(constructor env) ~init
:None c
.c_body
in
1003 let constructor, methods, smethods =
1004 interface c
constructor methods smethods in
1005 let class_tparam_names = List.map c
.c_tparams
(fun (_, x,_) -> x) in
1006 let enum = Option.map c
.c_enum
(enum_
env) in
1007 check_name_collision methods;
1008 check_tparams_shadow class_tparam_names methods;
1009 check_name_collision smethods;
1010 check_tparams_shadow class_tparam_names smethods;
1012 { N.c_mode
= c
.c_mode
;
1013 N.c_final
= c
.c_final
;
1014 N.c_is_xhp
= c
.c_is_xhp
;
1015 N.c_kind
= c
.c_kind
;
1017 N.c_tparams
= (tparam_l, constraints);
1018 N.c_extends
= parents;
1020 N.c_xhp_attr_uses
= xhp_attr_uses;
1021 N.c_xhp_category
= xhp_category;
1022 N.c_req_extends
= req_extends
;
1023 N.c_req_implements
= req_implements;
1024 N.c_implements
= implements;
1025 N.c_consts
= consts;
1026 N.c_typeconsts
= typeconsts;
1027 N.c_static_vars
= sprops;
1029 N.c_constructor
= constructor;
1030 N.c_static_methods
= smethods;
1031 N.c_methods
= methods;
1032 N.c_user_attributes
= user_attributes
env c
.c_user_attributes
;
1036 Naming_hooks.dispatch_class_named_hook
named_class;
1039 and user_attributes
env attrl
=
1040 let seen = Hashtbl.create
0 in
1041 let tc_options = (fst
env).tcopt
in
1042 let validate_seen = begin fun ua_name
->
1043 let pos, name = ua_name
in
1044 let existing_attr_pos =
1045 try Some
(Hashtbl.find
seen name)
1046 with Not_found
-> None
1047 in (match existing_attr_pos with
1048 | Some
p -> Errors.duplicate_user_attribute ua_name
p; false
1049 | None
-> Hashtbl.add
seen name pos; true
1052 let validate_name = begin fun ua_name
->
1053 (validate_seen ua_name
) && begin
1054 let pos, name = ua_name
in
1055 let valid = if string_starts_with
name "__"
1056 then SSet.mem name SN.UserAttributes.as_set
1057 else (TypecheckerOptions.allowed_attribute
tc_options name)
1058 in if not
valid then Errors.unbound_attribute_name
pos name;
1062 List.fold_left attrl ~init
:[] ~
f:begin fun acc
{ua_name
; ua_params
} ->
1063 if not
(validate_name ua_name
) then acc
1065 N.ua_name
= ua_name
;
1066 N.ua_params
= List.map ua_params
(expr
env)
1072 { N.e_base
= hint env e
.e_base
;
1073 N.e_constraint
= Option.map e
.e_constraint
(hint env);
1076 and type_paraml ?
(forbid_this
= false) env tparams
=
1077 let _, ret
= List.fold_left tparams ~init
:(SMap.empty
, [])
1078 ~
f:(fun (seen, tparaml) ((_, (p, name), _) as tparam
) ->
1079 match SMap.get
name seen with
1081 SMap.add
name p seen, (type_param ~forbid_this
env tparam
)::tparaml
1083 Errors.shadowed_type_param
p pos name;
1089 and type_param ~forbid_this
env (variance
, param_name
, cstr_list
) =
1092 List.map cstr_list
(constraint_ ~forbid_this
env)
1094 and type_where_constraints
env locl_cstrl
=
1095 List.map locl_cstrl
(fun (h1
, ck
, h2
) ->
1096 let ty1 = hint env h1
in
1097 let ty2 = hint env h2
in
1100 and class_use
env x acc
=
1102 | Attributes
_ -> acc
1106 hint ~allow_typedef
:false env h :: acc
1107 | XhpAttrUse
_ -> acc
1108 | ClassTraitRequire
_ -> acc
1109 | ClassVars
_ -> acc
1111 | XhpCategory
_ -> acc
1113 | TypeConst
_ -> acc
1115 and xhp_attr_use
env x acc
=
1117 | Attributes
_ -> acc
1122 hint ~allow_typedef
:false env h :: acc
1123 | ClassTraitRequire
_ -> acc
1124 | ClassVars
_ -> acc
1126 | XhpCategory
_ -> acc
1128 | TypeConst
_ -> acc
1130 and xhp_category _env
x acc
=
1132 | Attributes
_ -> acc
1136 | XhpAttrUse
_ -> acc
1137 | ClassTraitRequire
_ -> acc
1138 | ClassVars
_ -> acc
1142 | Some
_ -> Errors.multiple_xhp_category
(fst
(List.hd_exn cs
)); acc
1145 | TypeConst
_ -> acc
1147 and class_require
env c_kind
x acc
=
1149 | Attributes
_ -> acc
1153 | XhpAttrUse
_ -> acc
1154 | ClassTraitRequire
(MustExtend
, h)
1155 when c_kind
<> Ast.Ctrait
&& c_kind
<> Ast.Cinterface
->
1156 let () = Errors.invalid_req_extends
(fst
h) in
1158 | ClassTraitRequire
(MustExtend
, h) ->
1159 let acc_impls, acc_exts
= acc
in
1160 (acc_impls, hint ~allow_typedef
:false env h :: acc_exts
)
1161 | ClassTraitRequire
(MustImplement
, h) when c_kind
<> Ast.Ctrait
->
1162 let () = Errors.invalid_req_implements
(fst
h) in
1164 | ClassTraitRequire
(MustImplement
, h) ->
1165 let acc_impls, acc_exts
= acc
in
1166 (hint ~allow_typedef
:false env h :: acc_impls, acc_exts
)
1167 | ClassVars
_ -> acc
1169 | XhpCategory
_ -> acc
1171 | TypeConst
_ -> acc
1173 and constructor env acc
= function
1174 | Attributes
_ -> acc
1178 | XhpAttrUse
_ -> acc
1179 | ClassTraitRequire
_ -> acc
1180 | ClassVars
_ -> acc
1182 | XhpCategory
_ -> acc
1183 | Method
({ m_name
= (p, name); _ } as m
)
1184 when name = SN.Members.__construct
->
1186 | None
-> Some
(method_
(fst
env) m
)
1187 | Some
_ -> Errors.method_name_already_bound
p name; acc
)
1189 | TypeConst
_ -> acc
1191 and class_const
env x acc
=
1193 | Attributes
_ -> acc
1194 | Const
(h, l
) -> const_defl
h env l
@ acc
1195 | AbsConst
(h, x) -> abs_const_def
env h x :: acc
1197 | XhpAttrUse
_ -> acc
1198 | ClassTraitRequire
_ -> acc
1199 | ClassVars
_ -> acc
1201 | XhpCategory
_ -> acc
1203 | TypeConst
_ -> acc
1205 and class_prop_static
env x acc
=
1207 | Attributes
_ -> acc
1209 | XhpAttrUse
_ -> acc
1210 | ClassTraitRequire
_ -> acc
1213 | ClassVars
(kl
, h, cvl
) when List.mem kl Static
->
1214 (* Static variables are shared for all classes in the hierarchy.
1215 * This makes the 'this' type completely unsafe as a type for a
1216 * static variable. See test/typecheck/this_tparam_static.php as
1217 * an example of what can occur.
1219 let h = Option.map
h (hint ~forbid_this
:true ~is_static_var
:true env) in
1220 let cvl = List.map
cvl (class_prop_
env) in
1221 let cvl = List.map
cvl (fill_prop kl
h) in
1223 | ClassVars
_ -> acc
1225 | XhpCategory
_ -> acc
1227 | TypeConst
_ -> acc
1229 and class_prop
env x acc
=
1231 | Attributes
_ -> acc
1233 | XhpAttrUse
_ -> acc
1234 | ClassTraitRequire
_ -> acc
1237 | ClassVars
(kl
, h, cvl) when not
(List.mem kl Static
) ->
1238 let h = Option.map
h (hint env) in
1239 let cvl = List.map
cvl (class_prop_
env) in
1240 let cvl = List.map
cvl (fill_prop kl
h) in
1242 | ClassVars
_ -> acc
1243 | XhpAttr
(h, cv
, is_required
, maybe_enum
) ->
1244 let _, _, default
= cv
in
1245 let h = (match maybe_enum
with
1246 | Some
(pos, items
) ->
1247 let contains_int = List.exists items
begin function
1251 let contains_str = List.exists items
begin function
1252 | _, String
_ | _, String2
_ -> true
1255 if contains_int && not
contains_str then
1256 Some
(pos, Happly
((pos, "int"), []))
1257 else if not
contains_int && contains_str then
1258 Some
(pos, Happly
((pos, "string"), []))
1260 (* If the list was empty, or if there was a mix of
1261 ints and strings, then fallback to mixed *)
1262 Some
(pos, Happly
((pos, "mixed"), []))
1264 let h = (match h with
1265 | Some
(p, ((Hoption
_) as x)) -> Some
(p, x)
1266 | Some
(p, ((Happly
((_, "mixed"), [])) as x)) -> Some
(p, x)
1268 (* If a non-nullable attribute is not marked as "@required"
1269 AND it does not have a non-null default value, make the
1270 typehint nullable for now *)
1274 | Some
(_, Null
) -> false
1277 else Some
(p, Hoption
(p, h))
1279 let h = Option.map
h (hint env) in
1280 let cv = class_prop_
env cv in
1281 let cv = fill_prop
[] h cv in
1283 | XhpCategory
_ -> acc
1285 | TypeConst
_ -> acc
1287 and class_static_method
env x acc
=
1289 | Attributes
_ -> acc
1291 | XhpAttrUse
_ -> acc
1292 | ClassTraitRequire
_ -> acc
1295 | ClassVars
_ -> acc
1297 | XhpCategory
_ -> acc
1298 | Method m
when snd m
.m_name
= SN.Members.__construct
-> acc
1299 | Method m
when List.mem m
.m_kind Static
-> method_
(fst
env) m
:: acc
1301 | TypeConst
_ -> acc
1303 and class_method
env x acc
=
1305 | Attributes
_ -> acc
1307 | XhpAttrUse
_ -> acc
1308 | ClassTraitRequire
_ -> acc
1311 | ClassVars
_ -> acc
1313 | XhpCategory
_ -> acc
1314 | Method m
when snd m
.m_name
= SN.Members.__construct
-> acc
1315 | Method m
when not
(List.mem m
.m_kind Static
) ->
1316 let genv = fst
env in
1317 method_
genv m
:: acc
1319 | TypeConst
_ -> acc
1321 and class_typeconst
env x acc
=
1323 | Attributes
_ -> acc
1327 | XhpAttrUse
_ -> acc
1328 | ClassTraitRequire
_ -> acc
1329 | ClassVars
_ -> acc
1331 | XhpCategory
_ -> acc
1333 | TypeConst t
-> typeconst
env t
:: acc
1335 and check_constant_expr
(pos, e
) =
1337 | Unsafeexpr
_ | Id
_ | Null
| True
| False
| Int
_
1338 | Float
_ | String
_ -> ()
1339 | Class_const
((_, cls
), _) when cls
<> "static" -> ()
1341 | Unop
((Uplus
| Uminus
| Utild
| Unot
), e
) -> check_constant_expr e
1342 | Binop
(op
, e1
, e2) ->
1343 (* Only assignment is invalid *)
1345 | Eq
_ -> Errors.illegal_constant
pos
1347 check_constant_expr e1
;
1348 check_constant_expr
e2)
1349 | Eif
(e1
, e2, e3
) ->
1350 check_constant_expr e1
;
1351 Option.iter
e2 check_constant_expr
;
1352 check_constant_expr e3
1353 | _ -> Errors.illegal_constant
pos
1355 and constant_expr
env e
=
1356 Errors.try_with_error
begin fun () ->
1357 check_constant_expr e
;
1359 end (fun () -> fst e
, N.Any
)
1361 and const_defl
h env l
= List.map l
(const_def
h env)
1362 and const_def
h env (x, e
) =
1363 Env.bind_class_const env x;
1364 let h = Option.map
h (hint env) in
1365 h, x, Some
(constant_expr
env e
)
1367 and abs_const_def
env h x =
1368 Env.bind_class_const env x;
1369 let h = Option.map
h (hint env) in
1372 and class_prop_
env (_, x, e
) =
1373 Env.bind_prop env x;
1374 let e = Option.map
e (expr
env) in
1375 (* If the user has not provided a value, we initialize the member variable
1376 * ourselves to a value of type Tany. Classes might inherit from our decl
1377 * mode class that are themselves not in decl, and there's no way to figure
1378 * out what variables are initialized in a decl class without typechecking
1379 * its initalizers and constructor, which we don't want to do, so just
1380 * assume we're covered. *)
1382 if (fst
env).in_mode
= FileInfo.Mdecl
&& e = None
1383 then Some
(fst
x, N.Any
)
1386 N.({ cv_final
= false;
1387 cv_is_xhp
= ((String.sub
(snd
x) 0 1) = ":");
1388 cv_visibility
= N.Public
;
1394 and fill_prop kl ty
x =
1395 let x = { x with N.cv_type
= ty
} in
1396 List.fold_left kl ~init
:x ~
f:begin fun x k
->
1397 (* There is no field Static, they are dissociated earlier.
1398 An abstract class variable doesn't make sense.
1401 | Final
-> { x with N.cv_final
= true }
1404 | Private
-> { x with N.cv_visibility
= N.Private
}
1405 | Public
-> { x with N.cv_visibility
= N.Public
}
1406 | Protected
-> { x with N.cv_visibility
= N.Protected
}
1409 and typeconst
env t
=
1410 (* We use the same namespace as constants within the class so we cannot have
1411 * a const and type const with the same name
1413 Env.bind_class_const env t
.tconst_name
;
1414 let constr = Option.map t
.tconst_constraint
(hint env) in
1416 match t
.tconst_type
with
1417 | None
when not t
.tconst_abstract
->
1418 Errors.not_abstract_without_typeconst t
.tconst_name
;
1420 | Some _h
when t
.tconst_abstract
->
1421 Errors.abstract_with_typeconst t
.tconst_name
;
1425 let type_ = Option.map
hint_ (hint env) in
1426 N.({ c_tconst_name
= t
.tconst_name
;
1427 c_tconst_constraint
= constr;
1428 c_tconst_type
= type_;
1431 and func_body_had_unsafe
env = Env.has_unsafe env
1433 and method_
genv m
=
1434 let genv = extend_params
genv m
.m_tparams
in
1435 let env = genv, Env.empty_local UBMErr
in
1436 (* Cannot use 'this' if it is a public instance method *)
1437 let variadicity, paraml
= fun_paraml
env m
.m_params
in
1438 let acc = false, false, N.Public
in
1439 let final, abs
, vis
= List.fold_left ~
f:kind ~init
:acc m
.m_kind
in
1440 List.iter m
.m_tparams
check_constraint;
1441 let tparam_l = type_paraml
env m
.m_tparams
in
1442 let where_constraints = type_where_constraints
env m
.m_constrs
in
1443 let ret = Option.map m
.m_ret
(hint ~allow_retonly
:true env) in
1444 let f_kind = m
.m_fun_kind
in
1445 let body = (match genv.in_mode
with
1446 | FileInfo.Mdecl
| FileInfo.Mphp
->
1451 | FileInfo.Mstrict
| FileInfo.Mpartial
->
1453 N.fub_ast
= m
.m_body
;
1454 fub_tparams
= m
.m_tparams
;
1455 fub_namespace
= genv.namespace
;
1458 let attrs = user_attributes
env m
.m_user_attributes
in
1459 { N.m_final
= final ;
1460 N.m_visibility
= vis
;
1461 N.m_abstract
= abs
;
1462 N.m_name
= m
.Ast.m_name
;
1463 N.m_tparams
= tparam_l ;
1464 N.m_where_constraints
= where_constraints ;
1465 N.m_params
= paraml
;
1467 N.m_fun_kind
= f_kind ;
1469 N.m_variadic
= variadicity ;
1470 N.m_user_attributes
= attrs;
1473 and kind
(final, abs
, vis
) = function
1474 | Final
-> true, abs
, vis
1475 | Static
-> final, abs
, vis
1476 | Abstract
-> final, true, vis
1477 | Private
-> final, abs
, N.Private
1478 | Public
-> final, abs
, N.Public
1479 | Protected
-> final, abs
, N.Protected
1481 and fun_paraml
env l
=
1482 let _names = List.fold_left ~
f:check_repetition ~init
:SSet.empty l
in
1483 let variadicity, l
= determine_variadicity
env l
in
1484 variadicity, List.map l
(fun_param
env)
1486 and determine_variadicity
env l
=
1488 | [] -> N.FVnonVariadic
, []
1490 match x.param_is_variadic
, x.param_id
with
1491 | false, _ -> N.FVnonVariadic
, [x]
1492 (* NOTE: variadic params are removed from the list *)
1493 | true, (_, "...") -> N.FVellipsis
, []
1494 | true, _ -> N.FVvariadicArg
(fun_param
env x), []
1497 let variadicity, rl
= determine_variadicity
env rl
in
1498 variadicity, x :: rl
1500 and fun_param
env param
=
1501 let p, x = param
.param_id
in
1502 let ident = Local_id.get
x in
1503 Env.add_lvar env param
.param_id
(p, ident);
1504 let ty = Option.map param
.param_hint
(hint env) in
1505 let eopt = Option.map param
.param_expr
(expr
env) in
1506 { N.param_hint
= ty;
1507 param_is_reference
= param
.param_is_reference
;
1508 param_is_variadic
= param
.param_is_variadic
;
1509 param_pos
= fst param
.param_id
;
1510 param_name
= snd param
.param_id
;
1514 and make_constraints paraml
=
1515 List.fold_right paraml ~init
:SMap.empty
1516 ~
f:begin fun (_, (_, x), cstr_list
) acc ->
1517 SMap.add
x cstr_list
acc
1520 and extend_params
genv paraml
=
1521 let params = List.fold_right paraml ~init
:genv.type_params
1522 ~
f:begin fun (_, (_, x), cstr_list
) acc ->
1523 SMap.add
x cstr_list
acc
1525 { genv with type_params
= params }
1528 let tparams = make_constraints
f.f_tparams
in
1529 let genv = Env.make_fun_decl_genv nenv
tparams f in
1530 let lenv = Env.empty_local UBMErr
in
1531 let env = genv, lenv in
1532 let h = Option.map
f.f_ret
(hint ~allow_retonly
:true env) in
1533 let variadicity, paraml
= fun_paraml
env f.f_params
in
1534 let x = Env.fun_id env f.f_name
in
1535 List.iter
f.f_tparams
check_constraint;
1536 let f_tparams = type_paraml
env f.f_tparams in
1537 let f_kind = f.f_fun_kind
in
1538 let body = match genv.in_mode
with
1539 | FileInfo.Mdecl
| FileInfo.Mphp
->
1544 | FileInfo.Mstrict
| FileInfo.Mpartial
->
1546 N.fub_ast
= f.f_body
;
1547 fub_tparams
= f.f_tparams;
1548 fub_namespace
= f.f_namespace
;
1552 N.f_mode
= f.f_mode
;
1555 f_tparams = f_tparams;
1558 f_fun_kind
= f_kind;
1559 f_variadic
= variadicity;
1560 f_user_attributes
= user_attributes
env f.f_user_attributes
;
1562 Naming_hooks.dispatch_fun_named_hook
named_fun;
1565 and cut_and_flatten ?
(replacement
=Noop
) env = function
1567 | Unsafe
:: _ -> Env.set_unsafe env true; [replacement
]
1568 | Block b
:: rest
->
1569 (cut_and_flatten ~replacement
env b
) @
1570 (cut_and_flatten ~replacement
env rest
)
1571 | x :: rest
-> x :: (cut_and_flatten ~replacement
env rest
)
1575 | Block
_ -> assert false
1576 | Unsafe
-> assert false
1577 | Fallthrough
-> N.Fallthrough
1579 | Break
p -> N.Break
p
1580 | Continue
p -> N.Continue
p
1581 | Throw
e -> let terminal = not
(fst
env).in_try
in
1582 N.Throw
(terminal, expr
env e)
1583 | Return
(p, e) -> N.Return
(p, oexpr
env e)
1586 (* TODO(t17085086): Name the goto's label *)
1588 | Static_var el
-> N.Static_var
(static_varl
env el
)
1589 | If
(e, b1
, b2
) -> if_stmt
env st
e b1 b2
1590 | Do
(b
, e) -> do_stmt
env b
e
1591 | While
(e, b
) -> while_stmt
env e b
1592 | For
(st1
, e, st2
, b
) -> for_stmt
env st1
e st2 b
1593 | Switch
(e, cl
) -> switch_stmt
env st
e cl
1594 | Foreach
(e, aw
, ae
, b
)-> foreach_stmt
env e aw ae b
1595 | Try
(b
, cl
, fb
) -> try_stmt
env st b cl fb
1596 | Expr
(cp
, Call
((p, Id
(fp
, fn
)), el
, uel
))
1597 when fn
= SN.SpecialFunctions.invariant
->
1598 (* invariant is subject to a source-code transform in the HHVM
1599 * runtime: the arguments to invariant are lazily evaluated only in
1600 * the case in which the invariant condition does not hold. So:
1602 * invariant_violation(<condition>, <format>, <format_args...>)
1604 * ... is rewritten as:
1606 * if (!<condition>) {
1607 * invariant_violation(<format>, <format_args...>);
1612 Errors.naming_too_few_arguments
p;
1614 | (cond_p
, cond
) :: el
->
1615 let violation = (cp
, Call
1616 ((p, Id
(fp
, "\\"^
SN.SpecialFunctions.invariant_violation
)), el
,
1618 if cond
<> False
then
1619 let b1, b2
= [Expr
violation], [Noop
] in
1620 let cond = cond_p
, Unop
(Unot
, (cond_p
, cond)) in
1621 if_stmt
env st
cond b1 b2
1622 else (* a false <condition> means unconditional invariant_violation *)
1623 N.Expr
(expr
env violation)
1625 | Expr
e -> N.Expr
(expr
env e)
1627 and if_stmt
env st
e b1 b2
=
1628 let e = expr
env e in
1629 let nsenv = (fst
env).namespace
in
1630 let _, vars
= GetLocals.stmt
(fst
env).tcopt
(nsenv, SMap.empty
) st
in
1631 SMap.iter
(fun x p -> Env.new_pending_lvar env (p, x)) vars
;
1632 let result = Env.scope env (
1634 let all1, b1 = branch
env b1 in
1635 let all2, b2
= branch
env b2
in
1636 Env.extend_all_locals env all2;
1637 Env.extend_all_locals env all1;
1640 Env.promote_pending env;
1643 and do_stmt
env b
e =
1644 let new_scope = false in
1645 let b = block ~
new_scope env b in
1646 N.Do
(b, expr
env e)
1648 and while_stmt
env e b =
1649 let e = expr
env e in
1650 N.While
(e, block
env b)
1652 and for_stmt
env e1
e2 e3
b =
1653 let e1 = expr
env e1 in
1654 let e2 = expr
env e2 in
1655 let e3 = expr
env e3 in
1658 N.For
(e1, e2, e3, block
env b)
1661 and switch_stmt
env st
e cl
=
1662 let e = expr
env e in
1663 let nsenv = (fst
env).namespace
in
1664 let _, vars
= GetLocals.stmt
(fst
env).tcopt
(nsenv, SMap.empty
) st
in
1665 SMap.iter
(fun x p -> Env.new_pending_lvar env (p, x)) vars
;
1666 let result = Env.scope env begin fun env ->
1667 let all_locals_l, cl
= casel
env cl
in
1668 List.iter
all_locals_l (Env.extend_all_locals env);
1671 Env.promote_pending env;
1674 and foreach_stmt
env e aw ae
b =
1675 let e = expr
env e in
1676 Env.scope env begin fun env ->
1677 let ae = as_expr
env aw
ae in
1678 let b = block
env b in
1679 N.Foreach
(e, ae, b)
1682 and as_expr
env aw
= function
1684 let nsenv = (fst
env).namespace
in
1686 GetLocals.lvalue
(fst
env).tcopt
(nsenv, SMap.empty
) ev
in
1687 SMap.iter
(fun x p -> ignore
(Env.new_lvar env (p, x))) vars
;
1688 let ev = expr
env ev in
1691 | Some
p -> N.Await_as_v
(p, ev))
1692 | As_kv
((p1
, Lvar k
), ev) ->
1693 let k = p1
, N.Lvar
(Env.new_lvar env k) in
1694 let nsenv = (fst
env).namespace
in
1696 GetLocals.lvalue
(fst
env).tcopt
(nsenv, SMap.empty
) ev in
1697 SMap.iter
(fun x p -> ignore
(Env.new_lvar env (p, x))) vars
;
1698 let ev = expr
env ev in
1700 | None
-> N.As_kv
(k, ev)
1701 | Some
p -> N.Await_as_kv
(p, k, ev))
1702 | As_kv
((p, _), _) ->
1703 Errors.expected_variable
p;
1704 let x1 = p, N.Lvar
(Env.new_lvar env (p, "__internal_placeholder")) in
1705 let x2 = p, N.Lvar
(Env.new_lvar env (p, "__internal_placeholder")) in
1707 | None
-> N.As_kv
(x1, x2)
1708 | Some
p -> N.Await_as_kv
(p, x1, x2))
1710 and try_stmt
env st
b cl fb
=
1711 let nsenv = (fst
env).namespace
in
1713 GetLocals.stmt
(fst
env).tcopt
(nsenv, SMap.empty
) st
in
1714 SMap.iter
(fun x p -> Env.new_pending_lvar env (p, x)) vars
;
1715 let result = Env.scope env (
1717 let genv, lenv = env in
1718 (* isolate finally from the rest of the try-catch: if the first
1719 * statement of the try is an uncaught exception, finally will
1720 * still be executed *)
1721 let _all_finally, fb
= branch
env fb
in
1722 let all_locals_b, b = branch
({genv with in_try
= true}, lenv) b in
1723 let all_locals_cl, cl
= catchl
env cl
in
1724 List.iter
all_locals_cl (Env.extend_all_locals env);
1725 Env.extend_all_locals env all_locals_b;
1728 Env.promote_pending env;
1731 and block ?
(new_scope=true) env stl
=
1732 let stl = cut_and_flatten
env stl in
1736 fun env -> List.map
stl (stmt
env)
1738 else List.map
stl (stmt
env)
1740 and branch
env stmt_l
=
1741 let stmt_l = cut_and_flatten
env stmt_l in
1742 Env.scope_all env begin fun env ->
1743 List.map
stmt_l (stmt
env)
1746 and static_varl
env l
= List.map l
(static_var
env)
1747 and static_var
env = function
1748 | p, Lvar
_ as lv
-> expr
env (p, Binop
(Eq None
, lv
, (p, Null
)))
1751 and expr_obj_get_name
env = function
1752 | p, Id
x -> p, N.Id
x
1754 (match (fst
env).in_mode
with
1755 | FileInfo.Mstrict
->
1756 Errors.dynamic_method_call
p
1757 | FileInfo.Mpartial
| FileInfo.Mdecl
| FileInfo.Mphp
->
1762 and exprl
env l
= List.map l
(expr
env)
1763 and oexpr
env e = Option.map
e (expr
env)
1764 and expr
env (p, e) = p, expr_
env p e
1765 and expr_
env p = function
1766 | Array l
-> N.Array
(List.map l
(afield
env))
1768 N.Darray
(List.map l
(fun (e1, e2) -> expr
env e1, expr
env e2))
1769 | Varray l
-> N.Varray
(List.map l
(expr
env))
1770 | Collection
(id, l
) -> begin
1771 let p, cn
= Namespaces.elaborate_id
((fst
env).namespace
) NSClass
id in
1773 | x when N.is_vc_kind
x ->
1774 N.ValCollection
((N.get_vc_kind cn
),
1775 (List.map l
(afield_value
env cn
)))
1776 | x when N.is_kvc_kind
x ->
1777 N.KeyValCollection
((N.get_kvc_kind cn
),
1778 (List.map l
(afield_kvalue
env cn
)))
1779 | x when x = SN.Collections.cPair
->
1782 Errors.naming_too_few_arguments
p;
1785 let pn = SN.Collections.cPair
in
1786 N.Pair
(afield_value
env pn e1, afield_value
env pn e2)
1788 Errors.naming_too_many_arguments
p;
1792 Errors.expected_collection
p cn
;
1795 | Clone
e -> N.Clone
(expr
env e)
1800 | Float s
-> N.Float s
1801 | String s
-> N.String s
1802 | String2 idl
-> N.String2
(string2
env idl
)
1803 | Id
x -> N.Id
(Env.global_const env x)
1804 | Id_type_arguments
(_x
, _hl
) ->
1805 (* Shouldn't happen: parser only allows this in New *)
1806 failwith
"Unexpected Id with type arguments"
1807 | Lvar
(_, x) when x = SN.SpecialIdents.this
-> N.This
1809 N.Dollardollar
(Env.found_dollardollar env p)
1810 | Lvar
(pos, x) when x = SN.SpecialIdents.placeholder
->
1813 N.Lvar
(Env.lvar env x)
1815 N.Lvarvar
(n
, (Env.lvar env x))
1816 | Obj_get
(e1, e2, nullsafe
) ->
1817 (* If we encounter Obj_get(_,_,true) by itself, then it means "?->"
1818 is being used for instance property access; see the case below for
1819 handling nullsafe instance method calls to see how this works *)
1820 let nullsafe = match nullsafe with
1821 | OG_nullsafe
-> N.OG_nullsafe
1822 | OG_nullthrows
-> N.OG_nullthrows
1824 N.Obj_get
(expr
env e1, expr_obj_get_name
env e2, nullsafe)
1825 | Array_get
((p, Lvar
x), None
) ->
1826 let id = p, N.Lvar
(Env.lvar env x) in
1827 N.Array_get
(id, None
)
1828 | Array_get
(e1, e2) -> N.Array_get
(expr
env e1, oexpr
env e2)
1829 | Class_get
(x1, x2) ->
1830 N.Class_get
(make_class_id
env x1 [], x2)
1831 | Class_const
(x1, x2) ->
1832 let (genv, _) = env in
1833 let (_, name) = Namespaces.elaborate_id
genv.namespace NSClass
x1 in
1834 if GEnv.typedef_pos
(genv.tcopt
) name <> None
&& (snd
x2) = "class" then
1835 N.Typename
(Env.type_name env x1 ~allow_typedef
:true)
1837 N.Class_const
(make_class_id
env x1 [], x2)
1838 | Call
((_, Id
(p, pseudo_func
)), el
, uel
)
1839 when pseudo_func
= SN.SpecialFunctions.echo
->
1840 arg_unpack_unexpected uel
;
1841 N.Call
(N.Cnormal
, (p, N.Id
(p, pseudo_func
)), exprl
env el
, [])
1842 | Call
((p, Id
(_, cn
)), el
, uel
)
1843 when cn
= SN.SpecialFunctions.call_user_func
->
1844 arg_unpack_unexpected uel
;
1846 | [] -> Errors.naming_too_few_arguments
p; N.Any
1847 | f :: el
-> N.Call
(N.Cuser_func
, expr
env f, exprl
env el
, [])
1849 | Call
((p, Id
(_, cn
)), el
, uel
) when cn
= SN.SpecialFunctions.fun_
->
1850 arg_unpack_unexpected uel
;
1852 | [] -> Errors.naming_too_few_arguments
p; N.Any
1853 | [_, String
(_p2
, s
)] when String.contains s '
:'
->
1854 Errors.illegal_meth_fun
p; N.Any
1855 | [_, String
x] -> N.Fun_id
(Env.fun_id env x)
1857 Errors.illegal_fun
p;
1859 | _ -> Errors.naming_too_many_arguments
p; N.Any
1861 | Call
((p, Id
(_, cn
)), el
, uel
)
1862 when cn
= SN.SpecialFunctions.inst_meth
->
1863 arg_unpack_unexpected uel
;
1865 | [] -> Errors.naming_too_few_arguments
p; N.Any
1866 | [_] -> Errors.naming_too_few_arguments
p; N.Any
1867 | instance
::(_, String meth
)::[] ->
1868 N.Method_id
(expr
env instance
, meth
)
1869 | (p, _)::(_)::[] ->
1870 Errors.illegal_inst_meth
p;
1872 | _ -> Errors.naming_too_many_arguments
p; N.Any
1874 | Call
((p, Id
(_, cn
)), el
, uel
)
1875 when cn
= SN.SpecialFunctions.meth_caller
->
1876 arg_unpack_unexpected uel
;
1878 | [] -> Errors.naming_too_few_arguments
p; N.Any
1879 | [_] -> Errors.naming_too_few_arguments
p; N.Any
1881 (match (expr
env e1), (expr
env e2) with
1882 | (_, N.String cl
), (_, N.String meth
) ->
1883 N.Method_caller
(Env.type_name env cl ~allow_typedef
:false, meth
)
1884 | (_, N.Class_const
(N.CI
(cl
, _), (_, mem))), (_, N.String meth
)
1885 when mem = SN.Members.mClass
->
1886 N.Method_caller
(Env.type_name env cl ~allow_typedef
:false, meth
)
1888 Errors.illegal_meth_caller
p;
1891 | _ -> Errors.naming_too_many_arguments
p; N.Any
1893 | Call
((p, Id
(_, cn
)), el
, uel
)
1894 when cn
= SN.SpecialFunctions.class_meth
->
1895 arg_unpack_unexpected uel
;
1897 | [] -> Errors.naming_too_few_arguments
p; N.Any
1898 | [_] -> Errors.naming_too_few_arguments
p; N.Any
1900 (match (expr
env e1), (expr
env e2) with
1901 | (_, N.String cl
), (_, N.String meth
) ->
1902 N.Smethod_id
(Env.type_name env cl ~allow_typedef
:false, meth
)
1903 | (_, N.Id
(_, const
)), (_, N.String meth
)
1904 when const
= SN.PseudoConsts.g__CLASS__
->
1905 (* All of these that use current_cls aren't quite correct
1906 * inside a trait, as the class should be the using class.
1907 * It's sufficient for typechecking purposes (we require
1908 * subclass to be compatible with the trait member/method
1911 (match (fst
env).current_cls
with
1912 | Some
(cid
, _) -> N.Smethod_id
(cid
, meth
)
1913 | None
-> Errors.illegal_class_meth
p; N.Any
)
1914 | (_, N.Class_const
(N.CI
(cl
, _), (_, mem))), (_, N.String meth
)
1915 when mem = SN.Members.mClass
->
1916 N.Smethod_id
(Env.type_name env cl ~allow_typedef
:false, meth
)
1917 | (p, N.Class_const
((N.CIself
|N.CIstatic
), (_, mem))),
1918 (_, N.String meth
) when mem = SN.Members.mClass
->
1919 (match (fst
env).current_cls
with
1920 | Some
(cid
, _) -> N.Smethod_id
(cid
, meth
)
1921 | None
-> Errors.illegal_class_meth
p; N.Any
)
1922 | (p, _), (_) -> Errors.illegal_class_meth
p; N.Any
1924 | _ -> Errors.naming_too_many_arguments
p; N.Any
1926 | Call
((p, Id
(_, cn
)), el
, uel
) when cn
= SN.SpecialFunctions.assert_
->
1927 arg_unpack_unexpected uel
;
1928 if List.length el
<> 1
1929 then Errors.assert_arity
p;
1930 N.Assert
(N.AE_assert
(
1931 Option.value_map
(List.hd el
) ~default
:(p, N.Any
) ~
f:(expr
env)
1933 | Call
((p, Id
(_, cn
)), el
, uel
) when cn
= SN.SpecialFunctions.tuple
->
1934 arg_unpack_unexpected uel
;
1936 | [] -> Errors.naming_too_few_arguments
p; N.Any
1937 | el
-> N.List
(exprl
env el
)
1939 | Call
((p, Id
f), el
, uel
) ->
1940 let qualified = Env.fun_id env f in
1941 let cn = snd
qualified in
1942 (* The above special cases (fun, inst_meth, meth_caller, class_meth,
1943 * and friends) are magical language constructs, which we should
1944 * check before calling fun_id and looking up the function and doing
1945 * namespace normalization. However, gena, genva, etc are actual
1946 * functions that actually exist, we just need to handle them
1947 * specially here, during naming. Note that most of the function
1948 * special cases, such as idx, are actually handled in typing, and
1949 * don't require naming magic. *)
1950 if cn = SN.FB.fgena
then begin
1951 arg_unpack_unexpected uel
;
1953 | [e] -> N.Special_func
(N.Gena
(expr
env e))
1954 | _ -> Errors.gena_arity
p; N.Any
1956 end else if cn = SN.FB.fgenva
|| cn = SN.HH.asio_va
then begin
1957 arg_unpack_unexpected uel
;
1958 if List.length el
< 1
1959 then (Errors.genva_arity
p; N.Any
)
1960 else N.Special_func
(N.Genva
(exprl
env el
))
1961 end else if cn = SN.FB.fgen_array_rec
then begin
1962 arg_unpack_unexpected uel
;
1964 | [e] -> N.Special_func
(N.Gen_array_rec
(expr
env e))
1965 | _ -> Errors.gen_array_rec_arity
p; N.Any
1968 N.Call
(N.Cnormal
, (p, N.Id
qualified),
1969 exprl
env el
, exprl
env uel
)
1970 (* Handle nullsafe instance method calls here. Because Obj_get is used
1971 for both instance property access and instance method calls, we need
1972 to match the entire "Call(Obj_get(..), ..)" pattern here so that we
1973 only match instance method calls *)
1974 | Call
((p, Obj_get
(e1, e2, OG_nullsafe
)), el
, uel
) ->
1977 (p, N.Obj_get
(expr
env e1,
1978 expr_obj_get_name
env e2, N.OG_nullsafe
)),
1979 exprl
env el
, exprl
env uel
)
1980 (* Handle all kinds of calls that weren't handled by any of
1982 | Call
(e, el
, uel
) ->
1983 N.Call
(N.Cnormal
, expr
env e, exprl
env el
, exprl
env uel
)
1984 | Yield_break
-> N.Yield_break
1985 | Yield
e -> N.Yield
(afield
env e)
1986 | Await
e -> N.Await
(expr
env e)
1987 | List el
-> N.List
(exprl
env el
)
1988 | Expr_list el
-> N.Expr_list
(exprl
env el
)
1990 let (p, x), hl
= match ty with
1991 | _, Happly
(id, hl
) -> (id, hl
)
1992 | _ -> assert false in
1993 let ty = match try_castable_hint
env p x hl
with
1997 | x when x = SN.Typehints.object_cast
->
1998 (* (object) is a valid cast but not a valid type annotation *)
1999 (* FIXME we are not modeling the correct runtime behavior here --
2000 * the runtime result type is an stdClass if the original type is
2001 * primitive. But we should probably just disallow object casts
2004 | x when x = SN.Typehints.void
->
2007 | x when x = SN.Typehints.unset_cast
->
2008 Errors.unset_cast
p;
2011 (* Let's just assume that any other invalid cases are attempts to
2012 * cast to specific objects *)
2013 let h = hint ~allow_typedef
:false env ty in
2014 Errors.object_cast
p x;
2017 N.Cast
(ty, expr
env e2)
2018 | Unop
(uop
, e) -> N.Unop
(uop
, expr
env e)
2019 | Binop
(Eq None
as op
, lv
, e2) ->
2020 if Env.inside_pipe env then
2021 Errors.unimplemented_feature
p "Assignment within pipe expressions";
2022 let e2 = expr
env e2 in
2023 let nsenv = (fst
env).namespace
in
2025 GetLocals.lvalue
(fst
env).tcopt
(nsenv, SMap.empty
) lv
in
2026 SMap.iter
(fun x p -> ignore
(Env.new_lvar env (p, x))) vars
;
2027 N.Binop
(op
, expr
env lv
, e2)
2028 | Binop
(Eq
_ as bop
, e1, e2) ->
2029 if Env.inside_pipe env then
2030 Errors.unimplemented_feature
p "Assignment within pipe expressions";
2031 let e1 = expr
env e1 in
2032 N.Binop
(bop
, e1, expr
env e2)
2033 | Binop
(bop
, e1, e2) ->
2034 let e1 = expr
env e1 in
2035 N.Binop
(bop
, e1, expr
env e2)
2037 let e1 = expr
env e1 in
2038 let ident, e2 = Env.pipe_scope env
2043 N.Pipe
((p, ident), e1, e2)
2044 | Eif
(e1, e2opt
, e3) ->
2045 (* The order matters here, of course -- e1 can define vars that need to
2046 * be available in e2 and e3. *)
2047 let e1 = expr
env e1 in
2048 let e2opt = oexpr
env e2opt in
2049 let e3 = expr
env e3 in
2050 N.Eif
(e1, e2opt, e3)
2051 | NullCoalesce
(e1, e2) ->
2052 let e1 = expr
env e1 in
2053 let e2 = expr
env e2 in
2054 N.NullCoalesce
(e1, e2)
2055 | InstanceOf
(e, (p, Id
x)) ->
2056 let id = match x with
2057 | px
, n
when n
= SN.Classes.cParent
->
2058 if (fst
env).current_cls
= None
then
2059 let () = Errors.parent_outside_class
p in
2060 N.CI
((px
, SN.Classes.cUnknown
), [])
2062 | px
, n
when n
= SN.Classes.cSelf
->
2063 if (fst
env).current_cls
= None
then
2064 let () = Errors.self_outside_class
p in
2065 N.CI
((px
, SN.Classes.cUnknown
), [])
2067 | px
, n
when n
= SN.Classes.cStatic
->
2068 if (fst
env).current_cls
= None
then
2069 let () = Errors.static_outside_class
p in
2070 N.CI
((px
, SN.Classes.cUnknown
), [])
2073 N.CI
(Env.type_name env x ~allow_typedef
:false, [])
2075 N.InstanceOf
(expr
env e, id)
2076 | InstanceOf
(e1, (_,
2077 (Lvar
_ | Obj_get
_ | Class_get
_ | Class_const
_
2078 | Array_get
_ | Call
_) as e2)) ->
2079 N.InstanceOf
(expr
env e1, N.CIexpr
(expr
env e2))
2080 | InstanceOf
(_e1
, (p, _)) ->
2081 Errors.invalid_instanceof
p;
2083 | New
((_, Id_type_arguments
(x, hl
)), el
, uel
) ->
2084 N.New
(make_class_id
env x hl
,
2087 | New
((_, Id
x), el
, uel
)
2088 | New
((_, Lvar
x), el
, uel
) ->
2089 N.New
(make_class_id
env x [],
2092 | New
((p, _e
), el
, uel
) ->
2093 if (fst
env).in_mode
= FileInfo.Mstrict
2094 then Errors.dynamic_new_in_strict_mode
p;
2095 N.New
(make_class_id
env (p, SN.Classes.cUnknown
) [],
2099 let idl = List.map
idl fst
in
2100 let idl = List.filter
idl
2101 (function (_, x) -> (x <> SN.SpecialIdents.this
)) in
2102 let idl'
= List.map
idl (Env.lvar env) in
2103 let env = (fst
env, Env.empty_local UBMErr
) in
2104 List.iter2_exn
idl idl'
(Env.add_lvar env);
2105 let f = expr_lambda
env f in
2108 (* We have to build the capture list while we're finding names in
2109 the closure body---accumulate it in to_capture. *)
2110 (* semantic duplication: The logic here is also used in `uselist_lambda`.
2111 The differences are enough that it does not make sense to refactor
2112 this out for now. *)
2113 let to_capture = ref [] in
2114 let handle_unbound (p, x) =
2115 let cap = Env.lvar env (p, x) in
2116 to_capture := cap :: !to_capture;
2119 let lenv = Env.empty_local @@ UBMFunc
handle_unbound in
2120 let env = (fst
env, lenv) in
2121 let f = expr_lambda
env f in
2122 N.Efun
(f, !to_capture)
2123 | Xml
(x, al
, el
) ->
2124 N.Xml
(Env.type_name env x ~allow_typedef
:false, attrl
env al
,
2127 N.Shape
begin List.fold_left fdl ~init
:ShapeMap.empty
2128 ~
f:begin fun fdm
(pname
, value) ->
2129 let pos, name = convert_shape_name env pname
in
2130 if ShapeMap.mem name fdm
2131 then Errors.fd_name_already_bound
pos;
2132 ShapeMap.add
name (expr
env value) fdm
2140 and expr_lambda
env f =
2141 let h = Option.map
f.f_ret
(hint ~allow_retonly
:true env) in
2142 let previous_unsafe = Env.has_unsafe env in
2143 (* save unsafe and yield state *)
2144 Env.set_unsafe env false;
2145 let variadicity, paraml
= fun_paraml
env f.f_params
in
2146 let f_kind = f.f_fun_kind
in
2147 (* The bodies of lambdas go through naming in the containing local
2149 let body_nast = block
env f.f_body
in
2150 let unsafe = func_body_had_unsafe
env in
2151 (* restore unsafe state *)
2152 Env.set_unsafe env previous_unsafe;
2153 let body = N.NamedBody
{
2154 N.fnb_unsafe
= unsafe;
2155 fnb_nast
= body_nast;
2157 N.f_mode
= (fst
env).in_mode
;
2163 f_fun_kind
= f_kind;
2164 f_variadic
= variadicity;
2165 f_user_attributes
= user_attributes
env f.f_user_attributes
;
2168 and make_class_id
env (p, x as cid
) hl
=
2170 | x when x = SN.Classes.cParent
->
2171 if (fst
env).current_cls
= None
then
2172 let () = Errors.parent_outside_class
p in
2173 N.CI
((p, SN.Classes.cUnknown
), [])
2175 | x when x = SN.Classes.cSelf
->
2176 if (fst
env).current_cls
= None
then
2177 let () = Errors.self_outside_class
p in
2178 N.CI
((p, SN.Classes.cUnknown
), [])
2180 | x when x = SN.Classes.cStatic
-> if (fst
env).current_cls
= None
then
2181 let () = Errors.static_outside_class
p in
2182 N.CI
((p, SN.Classes.cUnknown
), [])
2184 | x when x = SN.SpecialIdents.this
-> N.CIexpr
(p, N.This
)
2185 | x when x = SN.SpecialIdents.dollardollar
->
2186 (* We won't reach here for "new $$" because the parser creates a
2187 * proper Ast.Dollardollar node, so make_class_id won't be called with
2188 * that node. In fact, the parser creates an Ast.Dollardollar for all
2189 * "$$" except in positions where a classname is expected, like in
2190 * static member access. So, we only reach here for things
2191 * like "$$::someMethod()". *)
2192 N.CIexpr
(p, N.Lvar
(Env.found_dollardollar env p))
2193 | x when x.[0] = '$'
-> N.CIexpr
(p, N.Lvar
(Env.lvar env cid
))
2194 | _ -> N.CI
(Env.type_name env cid ~allow_typedef
:false,
2195 hintl ~allow_wildcard
:true ~forbid_this
:false
2196 ~allow_typedef
:true ~allow_retonly
:true env hl
2200 List.map_env
[] l
(case
env)
2202 and case
env acc = function
2204 let b = cut_and_flatten ~replacement
:Fallthrough
env b in
2205 let all_locals, b = branch
env b in
2206 all_locals :: acc, N.Default
b
2208 let e = expr
env e in
2209 let b = cut_and_flatten ~replacement
:Fallthrough
env b in
2210 let all_locals, b = branch
env b in
2211 all_locals :: acc, N.Case
(e, b)
2213 and catchl
env l
= List.map_env
[] l
(catch
env)
2214 and catch
env acc (x1, x2, b) =
2217 let x2 = Env.new_lvar env x2 in
2218 let all_locals, b = branch
env b in
2219 all_locals :: acc, (Env.type_name env x1 ~allow_typedef
:true, x2, b)
2222 and afield
env = function
2223 | AFvalue
e -> N.AFvalue
(expr
env e)
2224 | AFkvalue
(e1, e2) -> N.AFkvalue
(expr
env e1, expr
env e2)
2226 and afield_value
env cname
= function
2227 | AFvalue
e -> expr
env e
2228 | AFkvalue
(e1, _e2
) ->
2229 Errors.unexpected_arrow
(fst
e1) cname
;
2232 and afield_kvalue
env cname
= function
2234 Errors.missing_arrow
(fst
e) cname
;
2235 expr
env e, expr
env (fst
e, Lvar
(fst
e, "__internal_placeholder"))
2236 | AFkvalue
(e1, e2) -> expr
env e1, expr
env e2
2238 and attrl
env l
= List.map l
(attr env)
2239 and attr env (x, e) = x, expr
env e
2241 and string2
env idl =
2242 List.map
idl (expr
env)
2244 (**************************************************************************)
2245 (* Function/Method Body Naming: *)
2246 (* Ensure that, given a function / class, any UnnamedBody within is
2247 * transformed into a a named body *)
2248 (**************************************************************************)
2250 let func_body nenv
f =
2251 match f.N.f_body
with
2252 | N.NamedBody
b -> b
2253 | N.UnnamedBody
{ N.fub_ast
; N.fub_tparams
; N.fub_namespace
; _ } ->
2254 let genv = Env.make_fun_genv nenv
2255 SMap.empty
f.N.f_mode
(snd
f.N.f_name
) fub_namespace
in
2256 let genv = extend_params
genv fub_tparams
in
2257 let lenv = Env.empty_local UBMErr
in
2258 let env = genv, lenv in
2260 List.fold_left ~
f:Env.add_param f.N.f_params ~init
:env in
2261 let env = match f.N.f_variadic
with
2262 | N.FVellipsis
| N.FVnonVariadic
-> env
2263 | N.FVvariadicArg param
-> Env.add_param env param
2265 let body = block
env fub_ast
in
2266 let unsafe = func_body_had_unsafe
env in {
2268 fnb_unsafe
= unsafe;
2271 let meth_body genv m
=
2272 let named_body = (match m
.N.m_body
with
2273 | N.NamedBody
_ as b -> b
2274 | N.UnnamedBody
{N.fub_ast
; N.fub_tparams
; N.fub_namespace
; _} ->
2275 let genv = {genv with namespace
= fub_namespace
} in
2276 let genv = extend_params
genv fub_tparams
in
2277 let env = genv, Env.empty_local UBMErr
in
2279 List.fold_left ~
f:Env.add_param m
.N.m_params ~init
:env in
2280 let env = match m
.N.m_variadic
with
2281 | N.FVellipsis
| N.FVnonVariadic
-> env
2282 | N.FVvariadicArg param
-> Env.add_param env param
2284 let body = block
env fub_ast
in
2285 let unsafe = func_body_had_unsafe
env in
2288 fnb_unsafe
= unsafe;
2291 {m
with N.m_body
= named_body}
2293 let class_meth_bodies nenv nc
=
2294 let _n_tparams, cstrs
= nc
.N.c_tparams
in
2295 let genv = Env.make_class_genv nenv cstrs
2296 nc
.N.c_mode
(nc
.N.c_name
, nc
.N.c_kind
)
2297 Namespace_env.empty_with_default_popt
2299 let inst_meths = List.map nc
.N.c_methods
(meth_body genv) in
2300 let opt_constructor = match nc
.N.c_constructor
with
2302 | Some c
-> Some
(meth_body genv c
) in
2303 let static_meths = List.map nc
.N.c_static_methods
(meth_body genv) in
2305 N.c_methods
= inst_meths;
2306 N.c_static_methods
= static_meths ;
2307 N.c_constructor
= opt_constructor ;
2310 (**************************************************************************)
2312 (**************************************************************************)
2314 let typedef genv tdef
=
2315 let ty = match tdef
.t_kind
with Alias t
| NewType t
-> t
in
2316 let cstrs = make_constraints tdef
.t_tparams
in
2317 let env = Env.make_typedef_env genv cstrs tdef
in
2318 let tconstraint = Option.map tdef
.t_constraint
(hint env) in
2319 List.iter tdef
.t_tparams
check_constraint;
2320 let tparaml = type_paraml
env tdef
.t_tparams
in
2321 List.iter
tparaml begin fun (_, _, constrs
) ->
2322 List.iter constrs
(fun (_, (pos, _)) -> Errors.typedef_constraint
pos)
2324 let t_vis = match tdef
.t_kind
with
2325 | Ast.Alias
_ -> N.Transparent
2326 | Ast.NewType
_ -> N.Opaque
2328 let attrs = user_attributes
env tdef
.t_user_attributes
in
2330 N.t_name
= tdef
.t_id
;
2331 t_tparams
= tparaml;
2332 t_constraint
= tconstraint;
2333 t_kind
= hint env ty;
2334 t_user_attributes
= attrs;
2335 t_mode
= tdef
.t_mode
;
2339 (**************************************************************************)
2340 (* Global constants *)
2341 (**************************************************************************)
2343 let check_constant_hint cst
=
2344 match cst
.cst_type
with
2345 | None
when cst
.cst_mode
= FileInfo.Mstrict
->
2346 Errors.add_a_typehint
(fst cst
.cst_name
)
2350 let global_const genv cst
=
2351 let env = Env.make_const_env genv cst
in
2352 let hint = Option.map cst
.cst_type
(hint env) in
2353 let e = match cst
.cst_kind
with
2355 check_constant_hint cst
;
2356 Some
(constant_expr
env cst
.cst_value
)
2357 (* Define allows any expression, so don't call check_constant.
2358 * Furthermore it often appears at toplevel, which we don't track at
2359 * all, so don't type or even name that expression, it may refer to
2360 * "undefined" variables that actually exist, just untracked since
2361 * they're toplevel. *)
2362 | Ast.Cst_define
-> None
in
2363 { N.cst_mode
= cst
.cst_mode
;
2364 cst_name
= cst
.cst_name
;
2369 (* Uses a default empty environment to extract the use list
2370 of a lambda expression. This exists only for the sake of
2371 the dehackificator and is not meant for general use. *)
2372 let uselist_lambda f =
2373 (* semantic duplication: This is copied from the implementation of the
2374 `Lfun` variant of `expr_` defined earlier in this file. *)
2375 let to_capture = ref [] in
2376 let handle_unbound (p, x) =
2377 to_capture := x :: !to_capture;
2380 let tcopt = TypecheckerOptions.make_permissive
TypecheckerOptions.default
in
2381 let genv = Env.make_fun_decl_genv tcopt SMap.empty
f in
2382 let lenv = Env.empty_local @@ UBMFunc
handle_unbound in
2383 let env = genv, lenv in
2384 ignore
(expr_lambda
env f);
2385 List.dedup
!to_capture
2387 (**************************************************************************)
2388 (* The entry point to CHECK the program, and transform the program *)
2389 (**************************************************************************)
2391 let program tcopt ast
= List.filter_map ast
begin function
2392 | Ast.Fun
f -> Some
(N.Fun
(fun_
tcopt f))
2393 | Ast.Class c
-> Some
(N.Class
(class_ tcopt c
))
2394 | Ast.Typedef t
-> Some
(N.Typedef
(typedef tcopt t
))
2395 | Ast.Constant cst
-> Some
(N.Constant
(global_const tcopt cst
))
2396 | Ast.Stmt
_ -> None
2398 | Ast.NamespaceUse
_ -> assert false
2404 let stmt _ acc _ = acc
2405 let lvalue _ acc _ = acc