2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
10 (** Module "naming" a program.
12 * The naming phase consists in several things
13 * 1- get all the global names
14 * 2- transform all the local names into a unique identifier
22 module ShapeMap
= N.ShapeMap
23 module SN
= Naming_special_names
24 module NS
= Namespaces
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 (* are we in the body of a finally statement? *)
54 (* In function foo<T1, ..., Tn> or class<T1, ..., Tn>, the field
55 * type_params knows T1 .. Tn. It is able to find out about the
56 * constraint on these parameters. *)
57 type_params
: type_constraint
SMap.t
;
59 (* The current class, None if we are in a function *)
60 current_cls
: (Ast.id
* Ast.class_kind
) option;
62 class_consts
: (string, Pos.t
) Hashtbl.t
;
64 class_props
: (string, Pos.t
) Hashtbl.t
;
66 (* Normally we don't need to add dependencies at this stage, but there
67 * are edge cases when we do. *)
68 droot
: Typing_deps.Dep.variant
;
70 (* Namespace environment, e.g., what namespace we're in and what use
71 * declarations are in play. *)
72 namespace
: Namespace_env.env
;
75 (* How to behave when we see an unbound name. Either we raise an
76 * error, or we call a function first and continue if it can resolve
77 * the name. This is used to nest environments when processing
81 | UBMFunc
of ((Pos.t
* string) -> positioned_ident
)
83 (* The primitives to manipulate the naming environment *)
89 val empty_local
: unbound_mode
-> lenv
91 TypecheckerOptions.t
->
92 type_constraint
SMap.t
->
94 Ast.id
* Ast.class_kind
-> Namespace_env.env
-> genv
96 TypecheckerOptions.t
->
97 type_constraint
SMap.t
-> Ast.class_
-> genv
* lenv
98 val make_typedef_env
:
99 TypecheckerOptions.t
->
100 type_constraint
SMap.t
-> Ast.typedef
-> genv
* lenv
102 TypecheckerOptions.t
->
103 type_constraint
SMap.t
->
104 FileInfo.mode
-> string -> Namespace_env.env
-> genv
105 val make_fun_decl_genv
:
106 TypecheckerOptions.t
->
107 type_constraint
SMap.t
-> Ast.fun_
-> genv
108 val make_const_env
: TypecheckerOptions.t
-> Ast.gconst
-> genv
* lenv
110 val has_unsafe
: genv
* lenv
-> bool
111 val set_unsafe
: genv
* lenv
-> bool -> unit
113 val add_lvar
: genv
* lenv
-> Ast.id
-> positioned_ident
-> unit
114 val add_param
: genv
* lenv
-> N.fun_param
-> genv
* lenv
115 val new_lvar
: genv
* lenv
-> Ast.id
-> positioned_ident
116 val new_let_local
: genv
* lenv
-> Ast.id
-> positioned_ident
117 val found_dollardollar
: genv
* lenv
-> Pos.t
-> positioned_ident
118 val has_dollardollar
: all_locals
-> Pos.t
option
119 val inside_pipe
: genv
* lenv
-> bool
120 val new_pending_lvar
: genv
* lenv
-> Ast.id
-> unit
121 val promote_pending_lvar
: genv
* lenv
-> string -> unit
122 val lvar
: genv
* lenv
-> Ast.id
-> positioned_ident
123 val let_local
: genv
* lenv
-> Ast.id
-> positioned_ident
option
124 val global_const
: genv
* lenv
-> Ast.id
-> Ast.id
125 val type_name
: genv
* lenv
-> Ast.id
-> allow_typedef
:bool -> Ast.id
126 val fun_id
: genv
* lenv
-> Ast.id
-> Ast.id
127 val bind_class_const
: genv
* lenv
-> Ast.id
-> unit
128 val bind_prop
: genv
* lenv
-> Ast.id
-> unit
129 val goto_label
: genv
* lenv
-> string -> Pos.t
option
130 val new_goto_label
: genv
* lenv
-> pstring
-> unit
131 val new_goto_target
: genv
* lenv
-> pstring
-> unit
132 val check_goto_references
: genv
* lenv
-> unit
133 val copy_let_locals
: genv
* lenv
-> genv
* lenv
-> unit
135 val scope
: genv
* lenv
-> (genv
* lenv
-> 'a
) -> 'a
136 val scope_all
: genv
* lenv
-> (genv
* lenv
-> 'a
) -> all_locals
* 'a
137 val scope_lexical
: genv
* lenv
-> (genv
* lenv
-> 'a
) -> 'a
138 val extend_all_locals
: genv
* lenv
-> all_locals
-> unit
139 val remove_locals
: genv
* lenv
-> Ast.id list
-> unit
140 val pipe_scope
: genv
* lenv
-> (genv
* lenv
-> N.expr
) -> Local_id.t
* N.expr
144 type map
= positioned_ident
SMap.t
145 type all_locals
= Pos.t
SMap.t
147 (* The local environment *)
150 (* The set of locals *)
153 (* We keep all the locals, even if we are in a different scope
154 * to provide better error messages.
159 * Technically, passed this point, $x is unbound.
160 * But it is much better to keep it somewhere, so that you can
161 * say it is bound, but in a different scope.
163 all_locals
: all_locals
ref;
165 (* Some statements can define new variables afterwards, e.g.,
171 * We need to give $x the same name in both branches, but we don't want
172 * $x to actually be a local until after the if block. So we stash it here,
173 * to indicate a name has been pre-allocated, but that the variable isn't
174 * actually defined yet.
176 pending_locals
: map
ref;
178 (* The set of lexically-scoped local `let` variables *)
179 (* TODO: Currently these locals live in a separate namespace, it is
180 * worthwhile considering unified namespace for all local variables T28712009 *)
183 (* Tag controlling what we do when we encounter an unbound name.
184 * This is used when processing a lambda expression body that has
185 * an automatic use list.
187 * See expr_lambda for details.
189 unbound_mode
: unbound_mode
;
191 (* The presence of an "UNSAFE" in the function body changes the
192 * verifiability of the function's return type, since the unsafe
193 * block could return. For the sanity of the typechecker, we flatten
194 * this out, but need to track if we've seen an "UNSAFE" in order to
196 has_unsafe
: bool ref;
198 (** Allows us to ban $$ appearances outside of pipe expressions and
199 * equals expressions within pipes. *)
200 inside_pipe
: bool ref;
203 * A map from goto label strings to named labels.
205 goto_labels
: Pos.t
SMap.t
ref;
208 * A map from goto label used in a goto statement to the position of that
211 goto_targets
: Pos.t
SMap.t
ref;
214 let empty_local unbound_mode
= {
215 locals
= ref SMap.empty
;
216 all_locals
= ref SMap.empty
;
217 pending_locals
= ref SMap.empty
;
218 let_locals
= ref SMap.empty
;
220 has_unsafe
= ref false;
221 inside_pipe
= ref false;
222 goto_labels
= ref SMap.empty
;
223 goto_targets
= ref SMap.empty
;
226 let make_class_genv tcopt tparams mode
(cid
, ckind
) namespace
= {
228 (if !Autocomplete.auto_complete
then FileInfo.Mpartial
else mode
);
232 type_params
= tparams
;
233 current_cls
= Some
(cid
, ckind
);
234 class_consts
= Hashtbl.create
0;
235 class_props
= Hashtbl.create
0;
236 droot
= Typing_deps.Dep.Class
(snd cid
);
240 let unbound_name_error genv pos name kind
=
241 (* Naming pretends to be local and not dependent on other files, so it
242 * doesn't bother with adding dependencies (even though it does look up
243 * things in global state). This is mostly brushed aside because "they
244 * will be added during typing". Unfortunately, there are multiple scenarios
245 * when typechecker will name an expression, but gives up on typechecking
246 * it. We are then left with a unrecorded dependency. This should be fixed
247 * on some more basic level, but so far the only incorrectness that anyone
248 * has observed due to this is that we fail to remove "unbound name" errors
249 * sometimes. I add this dependency here for now to fix the annoyance it
250 * causes developers. *)
251 begin match kind
with
252 | `func
-> Typing_deps.Dep.Fun name
253 | `cls
-> Typing_deps.Dep.Class name
254 | `const
-> Typing_deps.Dep.GConst name
255 end |> Typing_deps.add_idep genv
.droot
;
256 Errors.unbound_name pos name kind
258 let make_class_env tcopt tparams c
=
259 let genv = make_class_genv tcopt tparams c
.c_mode
260 (c
.c_name
, c
.c_kind
) c
.c_namespace
in
261 let lenv = empty_local UBMErr
in
262 let env = genv, lenv in
265 let make_typedef_genv tcopt cstrs tdef
= {
266 in_mode
= FileInfo.(if !Ide.is_ide_mode
then Mpartial
else Mstrict
);
272 class_consts
= Hashtbl.create
0;
273 class_props
= Hashtbl.create
0;
274 droot
= Typing_deps.Dep.Class
(snd tdef
.t_id
);
275 namespace
= tdef
.t_namespace
;
278 let make_typedef_env genv cstrs tdef
=
279 let genv = make_typedef_genv genv cstrs tdef
in
280 let lenv = empty_local UBMErr
in
281 let env = genv, lenv in
284 let make_fun_genv tcopt params f_mode f_name f_namespace
= {
289 type_params
= params
;
291 class_consts
= Hashtbl.create
0;
292 class_props
= Hashtbl.create
0;
293 droot
= Typing_deps.Dep.Fun f_name
;
294 namespace
= f_namespace
;
297 let make_fun_decl_genv nenv params f
=
298 make_fun_genv nenv params f
.f_mode
(snd f
.f_name
) f
.f_namespace
300 let make_const_genv tcopt cst
= {
301 in_mode
= cst
.cst_mode
;
305 type_params
= SMap.empty
;
307 class_consts
= Hashtbl.create
0;
308 class_props
= Hashtbl.create
0;
309 droot
= Typing_deps.Dep.GConst
(snd cst
.cst_name
);
310 namespace
= cst
.cst_namespace
;
313 let make_const_env nenv cst
=
314 let genv = make_const_genv nenv cst
in
315 let lenv = empty_local UBMErr
in
316 let env = genv, lenv in
319 let has_unsafe (_genv
, lenv) = !(lenv.has_unsafe)
320 let set_unsafe (_genv
, lenv) x
=
323 let lookup genv (env : string -> FileInfo.pos
option) (p
, x
) =
327 (match genv.in_mode
with
328 | FileInfo.Mstrict
-> unbound_name_error genv p x `const
329 | FileInfo.Mpartial
| FileInfo.Mdecl
when not
330 (TypecheckerOptions.assume_php
genv.tcopt
) ->
331 unbound_name_error genv p x `const
332 | FileInfo.Mphp
| FileInfo.Mdecl
| FileInfo.Mpartial
-> ()
336 (* Check and see if the user might have been trying to use one of the
337 * generics in scope as a runtime value *)
338 let check_no_runtime_generic genv (p
, name
) =
339 let tparaml = SMap.keys
genv.type_params
in
340 if List.mem
tparaml name
then Errors.generic_at_runtime p
;
343 let handle_unbound_name genv get_full_pos get_canon
(p
, name
) kind
=
344 match get_canon name
with
348 |> Option.iter ~f
:(fun p_canon
->
349 Errors.did_you_mean_naming p name p_canon canonical
);
350 (* Recovering from the capitalization error means
351 * returning the name in its canonical form *)
354 (match genv.in_mode
with
355 | FileInfo.Mpartial
| FileInfo.Mdecl
356 when TypecheckerOptions.assume_php
genv.tcopt
357 || name
= SN.Classes.cUnknown
-> ()
358 | FileInfo.Mphp
-> ()
359 | FileInfo.Mstrict
-> unbound_name_error genv p name kind
360 | FileInfo.Mpartial
| FileInfo.Mdecl
->
361 unbound_name_error genv p name kind
365 let canonicalize genv get_pos get_full_pos get_canon
(p
, name
) kind
=
366 (* Get the canonical name to check if the name exists in the heap *)
367 match get_pos name
with
369 | None
-> handle_unbound_name genv get_full_pos get_canon
(p
, name
) kind
371 let check_variable_scoping env (p
, x
) =
372 match SMap.get x
!(env.all_locals
) with
373 | Some p'
-> Errors.different_scope p x p'
376 (* Adds a local variable, without any check *)
377 let add_lvar (_
, lenv) (_
, name
) (p
, x
) =
378 lenv.locals
:= SMap.add name
(p
, x
) !(lenv.locals
);
381 let add_param env param
=
382 let p_name = param
.N.param_name
in
383 let id = Local_id.get
p_name in
384 let p_pos = param
.N.param_pos
in
385 let () = add_lvar env (p_pos, p_name) (p_pos, id) in
388 (* Defines a new local variable.
390 1) if the local is not in the local environment then it is added.
391 Return value: the given position and deduced/created identifier. *)
392 let new_lvar (_
, lenv) (p
, x
) =
393 let lcl = SMap.get x
!(lenv.locals
) in
396 | Some
lcl -> snd
lcl
398 let ident = match SMap.get x
!(lenv.pending_locals
) with
399 | Some
(_
, ident) -> ident
400 | None
-> Local_id.make x
in
401 lenv.all_locals
:= SMap.add x p
!(lenv.all_locals
);
402 lenv.locals
:= SMap.add x
(p
, ident) !(lenv.locals
);
407 (* Defines a new scoped local variable
409 * Always add a new variable in the local environment.
410 * If the variable has been defined already, shadow the previously-defined
412 (* TODO: Emit warning if names are getting shadowed T28436131 *)
413 let new_let_local (_
, lenv) (p
, x
) =
414 let ident = Local_id.make x
in
415 lenv.all_locals
:= SMap.add x p
!(lenv.all_locals
);
416 lenv.let_locals
:= SMap.add x
(p
, ident) !(lenv.let_locals
);
419 (* Defines a new local variable for this dollardollar (or reuses
420 * the exiting identifier). *)
421 let found_dollardollar (genv, lenv) p
=
422 if not
!(lenv.inside_pipe
) then
423 Errors.undefined p
SN.SpecialIdents.dollardollar
;
424 new_lvar (genv, lenv) (p
, SN.SpecialIdents.dollardollar
)
426 (* Check if dollardollar is defined in the current environment *)
427 let has_dollardollar locals
=
428 SMap.get
SN.SpecialIdents.dollardollar locals
430 let inside_pipe (_
, lenv) =
433 let new_pending_lvar (_
, lenv) (p
, x
) =
434 match SMap.get x
!(lenv.locals
), SMap.get x
!(lenv.pending_locals
) with
436 let y = p
, Local_id.make x
in
437 lenv.pending_locals
:= SMap.add x
y !(lenv.pending_locals
)
440 let promote_pending_lvar (_
, lenv) x
=
441 match SMap.get x
!(lenv.pending_locals
) with
443 lenv.locals
:= SMap.add x
(p
, ident) !(lenv.locals
);
444 lenv.pending_locals
:= SMap.remove x
!(lenv.pending_locals
)
447 let handle_undefined_variable (_genv
, env) (p
, x
) =
448 match env.unbound_mode
with
449 | UBMErr
-> Errors.undefined p x
; p
, Local_id.make x
450 | UBMFunc f
-> f
(p
, x
)
452 (* Function used to name a local variable *)
453 let lvar (genv, env) (p
, x
) =
455 if SN.Superglobals.is_superglobal x
&& genv.in_mode
= FileInfo.Mpartial
456 then p, Local_id.make x
458 let lcl = SMap.get x
!(env.locals
) in
460 | Some
lcl -> p, snd
lcl
461 | None
when not
!Autocomplete.auto_complete
->
462 check_variable_scoping env (p, x
);
463 handle_undefined_variable (genv, env) (p, x
)
464 | None
-> p, Local_id.tmp
()
468 let let_local (_genv
, env) (p, x
) =
469 let lcl = SMap.get x
!(env.let_locals
) in
471 | Some
lcl -> Some
(p, snd
lcl)
474 let get_name genv get_pos x
=
475 lookup genv get_pos x
; x
477 (* For dealing with namespace fallback on constants *)
478 let elaborate_and_get_name_with_fallback
481 (get_pos
: string -> FileInfo.pos
option) x
=
482 let get_name x
= get_name genv get_pos x
in
483 let fq_x = NS.elaborate_id
genv.namespace
NS.ElaborateConst x
in
485 genv.namespace
.Namespace_env.ns_name
<> None
&&
486 not
(String.contains
(snd x
) '
\\'
) in
489 (* __FILE__, __LINE__ etc *)
490 (string_starts_with
(snd x
) "__") && (string_ends_with
(snd x
) "__") in
491 if use_fallback then begin
492 let global_x = (fst x
, "\\" ^
(snd x
)) in
493 (* Explicitly add dependencies on both of the consts we could be
494 * referring to here. Normally naming doesn't have to deal with
495 * deps at all -- they are added during typechecking just by the
496 * nature of looking up a class or function name. However, we're
497 * flattening namespaces here, and the fallback behavior of
498 * consts means that we might suddenly be referring to a
499 * different const without any change to the callsite at
500 * all. Adding both dependencies explicitly captures this
501 * action-at-a-distance. *)
502 Typing_deps.add_idep
genv.droot
(mk_dep
(snd
fq_x));
503 Typing_deps.add_idep
genv.droot
(mk_dep
(snd
global_x));
504 let mem (_
, s
) = get_pos s
in
505 match mem fq_x, mem global_x with
506 (* Found in the current namespace *)
507 | Some _
, _
-> get_name fq_x
508 (* Found in the global namespace *)
509 | _
, Some _
-> get_name global_x
510 (* Not found. Pick the more specific one to error on. *)
511 | None
, None
-> get_name fq_x
515 (* For dealing with namespace resolution on functions *)
516 let elaborate_and_get_name_with_canonicalized_fallback
518 (get_pos
: string -> FileInfo.pos
option)
519 (get_full_pos
: string -> Pos.t
option)
521 let get_name x
= get_name genv get_pos x
in
522 let canonicalize = canonicalize genv get_pos get_full_pos get_canon
in
523 let fq_x = NS.elaborate_id
genv.namespace
NS.ElaborateFun x
in
524 let fq_x = canonicalize fq_x `func
in
527 let global_const (genv, _env
) x
=
528 elaborate_and_get_name_with_fallback
529 (* Same idea as Dep.FunName, see below. *)
530 (fun x
-> Typing_deps.Dep.GConstName x
)
532 (Naming_heap.ConstPosHeap.get
)
535 let type_name (genv, _
) x ~allow_typedef
=
536 (* Generic names are not allowed to shadow class names *)
537 check_no_runtime_generic genv x
;
538 let (pos
, name
) as x
= NS.elaborate_id
genv.namespace
NS.ElaborateClass x
in
539 match Naming_heap.TypeIdHeap.get name
with
540 | Some
(_def_pos
, `Class
) ->
541 (* Don't let people use strictly internal classes
542 * (except when they are being declared in .hhi files) *)
543 if name
= SN.Classes.cHH_BuiltinEnum
&&
544 not
(string_ends_with
(Relative_path.suffix
(Pos.filename pos
)) ".hhi")
545 then Errors.using_internal_class pos
(strip_ns name
);
547 | Some
(def_pos
, `Typedef
) when not allow_typedef
->
548 let full_pos, _
= GEnv.get_full_pos
genv.tcopt
(def_pos
, name
) in
549 Errors.unexpected_typedef pos
full_pos;
551 | Some
(_def_pos
, `Typedef
) -> pos
, name
553 handle_unbound_name genv
554 (GEnv.type_pos
genv.tcopt
)
555 GEnv.type_canon_name x `cls
557 let fun_id (genv, _
) x
=
558 elaborate_and_get_name_with_canonicalized_fallback
560 (Naming_heap.FunPosHeap.get
)
561 (GEnv.fun_pos
genv.tcopt
)
565 let bind_class_member tbl
(p, x
) =
567 let p'
= Hashtbl.find tbl x
in
568 Errors.error_name_already_bound x x
p p'
570 Hashtbl.replace tbl x
p
572 let bind_class_const (genv, _env
) (p, x
) =
573 if String.lowercase_ascii x
= "class" then Errors.illegal_member_variable_class
p;
574 bind_class_member genv.class_consts
(p, x
)
576 let bind_prop (genv, _env
) x
=
577 bind_class_member genv.class_props x
580 * Returns the position of the goto label declaration, if it exists.
582 let goto_label (_
, { goto_labels
; _
}) label
=
583 SMap.get label
!goto_labels
586 * Adds a goto label and the position of its declaration to the known labels.
588 let new_goto_label (_
, { goto_labels
; _
}) (pos
, label
) =
589 goto_labels
:= SMap.add label pos
!goto_labels
592 * Adds a goto target and its reference position to the known targets.
594 let new_goto_target (_
, { goto_targets
; _
}) (pos
, label
) =
595 goto_targets
:= SMap.add label pos
!goto_targets
598 * Ensures that goto statements do not reference goto labels that are not
599 * known within the current lenv.
601 let check_goto_references (_
, { goto_labels
; goto_targets
; _
}) =
602 let check_label referenced_label referenced_label_pos
=
603 if not
(SMap.mem referenced_label
!goto_labels
) then
604 Errors.goto_label_undefined referenced_label_pos referenced_label
in
605 SMap.iter
check_label !goto_targets
607 (* Scope, keep the locals, go and name the body, and leave the
608 * local environment intact
611 let _genv, lenv = env in
612 let lenv_copy = !(lenv.locals
) in
613 let lenv_pending_copy = !(lenv.pending_locals
) in
614 let lenv_scoped_copy = !(lenv.let_locals
) in
616 lenv.locals
:= lenv_copy;
617 lenv.pending_locals
:= lenv_pending_copy;
618 lenv.let_locals
:= lenv_scoped_copy;
621 let remove_locals env vars
=
622 let _genv, lenv = env in
624 List.fold_left vars ~f
:(fun l
id -> SMap.remove
(snd
id) l
) ~init
:!(lenv.locals
)
626 let scope_all env f
=
627 let _genv, lenv = env in
628 let lenv_all_locals_copy = !(lenv.all_locals
) in
629 let res = scope env f
in
630 let lenv_all_locals = !(lenv.all_locals
) in
631 lenv.all_locals
:= lenv_all_locals_copy;
634 (* Add a new lexical scope for block-scoped `let` variables.
635 No other changes in the local environment *)
636 let scope_lexical env f
=
637 let _genv, lenv = env in
638 let lenv_scoped_copy = !(lenv.let_locals
) in
640 lenv.let_locals
:= lenv_scoped_copy;
643 (* Copy the let locals from lenv1 to lenv2 *)
644 let copy_let_locals (_genv1
, lenv1
) (_genv2
, lenv2
) =
645 let let_locals_1 = !(lenv1
.let_locals
) in
646 lenv2
.let_locals
:= let_locals_1
648 let extend_all_locals (_genv, lenv) more_locals
=
649 lenv.all_locals
:= SMap.union more_locals
!(lenv.all_locals
)
651 (** Sets up the environment so that naming can be done on the RHS of a
652 * pipe expression. It returns the identity of the $$ in the RHS and the
653 * named RHS. The steps are as follows:
654 * - Removes the $$ from the local env
655 * - Name the RHS scope
656 * - Restore the binding of $$ in the local env (if it was bound).
658 * This will append an error if $$ was not used in the RHS.
660 * The inside_pipe flag is also set before the naming and restored afterwards.
662 let pipe_scope env name_e2
=
664 let outer_pipe_var_opt =
665 SMap.get
SN.SpecialIdents.dollardollar
!(lenv.locals) in
666 let inner_locals = SMap.remove
SN.SpecialIdents.dollardollar
668 lenv.locals := inner_locals;
669 lenv.inside_pipe := true;
670 (** Name the RHS of the pipe expression. During this naming, if the $$ from
671 * this pipe is used, it will be added to the locals. *)
672 let e2 = name_e2
env in
674 match SMap.get
SN.SpecialIdents.dollardollar
!(lenv.locals) with
676 Errors.dollardollar_unused
(fst
e2);
677 (** The $$ lvar should be named when it is encountered inside e2,
678 * but we've now discovered it wasn't used at all.
679 * Create an ID here so we can keep going. *)
680 Local_id.make
SN.SpecialIdents.dollardollar
683 let restored_locals = SMap.remove
SN.SpecialIdents.dollardollar
685 (match outer_pipe_var_opt with
687 lenv.locals := restored_locals;
688 lenv.inside_pipe := false;
690 | Some outer_pipe_var
-> begin
691 let restored_locals = SMap.add
SN.SpecialIdents.dollardollar
692 outer_pipe_var
restored_locals in
693 lenv.locals := restored_locals;
699 (*****************************************************************************)
701 (*****************************************************************************)
703 (* Alok is constantly complaining that in partial mode,
704 * he forgets to bind a type parameter, for example T,
705 * and because partial assumes T is just a class that lives
706 * in PHP land there is no error message.
707 * So to help him, I am adding a rule that if
708 * the class name starts with a T and is only 2 characters
709 * it is considered a type variable. You will not be able to
710 * define a class T in php land in this scheme ... But it is a bad
711 * name for a class anyway.
713 let is_alok_type_name (_, x
) = String.length x
<= 2 && x
.[0] = 'T'
715 let check_constraint (_, (pos
, name
), _) =
716 (* TODO refactor this in a separate module for errors *)
717 if String.lowercase_ascii name
= "this"
718 then Errors.this_reserved pos
719 else if name
.[0] <> 'T'
then Errors.start_with_T pos
721 let check_repetition s param
=
722 let x = snd param
.param_id
in
724 then Errors.already_bound
(fst param
.param_id
) x;
725 if x <> SN.SpecialIdents.placeholder
then SSet.add
x s
else s
727 let convert_shape_name env = function
728 | SFlit
(pos
, s
) -> (pos
, SFlit
(pos
, s
))
729 | SFclass_const
(x, (pos
, y)) ->
731 if (snd
x) = SN.Classes.cSelf
then
732 match (fst
env).current_cls
with
733 | Some
(cid
, _) -> cid
734 | None
-> Errors.self_outside_class pos
; (pos
, SN.Classes.cUnknown
)
735 else Env.type_name env x ~allow_typedef
:false in
736 (pos
, SFclass_const
(class_name, (pos
, y)))
738 let arg_unpack_unexpected = function
740 | (pos
, _) :: _ -> Errors.naming_too_few_arguments pos
; ()
742 module type GetLocals
= sig
743 val stmt
: TypecheckerOptions.t
-> Namespace_env.env * Pos.t
SMap.t
->
744 Ast.stmt
-> Namespace_env.env * Pos.t
SMap.t
745 val lvalue
: TypecheckerOptions.t
-> Namespace_env.env * Pos.t
SMap.t
->
746 Ast.expr
-> Namespace_env.env * Pos.t
SMap.t
749 (* This was made a functor due to the awkward nature of how our naming
750 * code is structured.
752 * Naming is called both in the decl phase and type-check phase. In the
753 * decl phase it's mostly used to construct things that do not belong in
754 * function bodies; examples include classes, their member fields, and
755 * global constants. This part of naming is entirely self-contained; it
756 * only uses the data from the AST in the current file, and does not need
757 * to cross-reference decl type data from other files.
759 * In the type-check phase, Naming is invoked again, this time to name the
760 * bodies of functions. Now it requires decl type data in order to know
761 * which function calls are marked as `noreturn`, because this affects
762 * which local variables are considered to be defined at the end of a
765 * So decl depends on naming, but naming also depends on decl, creating
766 * a circular dependency. The obvious solution would be to split it into
767 * two, but this is nontrivial because decl-phase naming also does some
768 * naming of expressions -- for example, constant initializers and default
769 * parameter values have them. Of course, none of these expressions can
770 * actually contain local variables, but our code is not written in a way
771 * that the OCaml type system can understand that. So as a hacky solution,
772 * I'm parameterizing GetLocals so that it is a no-op in the decl phase
773 * but can be properly instantiated with Typing_get_locals in the typing
776 module Make
(GetLocals
: GetLocals
) = struct
777 let hacksperimental (genv, _) =
778 let tcopt = genv.tcopt in
779 TypecheckerOptions.experimental_feature_enabled
tcopt
780 GlobalOptions.tco_hacksperimental
782 (************************************************************************)
783 (* Naming of type hints *)
784 (************************************************************************)
786 ?
(is_static_var
=false)
788 ?
(allow_retonly
=false)
789 ?
(allow_typedef
=true)
790 ?
(allow_wildcard
=false)
791 ?
(in_where_clause
=false)
803 and shape_field_to_shape_field_info
env { sf_optional
; sf_name
=_; sf_hint
} =
805 N.sfi_optional
= sf_optional
;
806 sfi_hint
= hint env sf_hint
;
809 and ast_shape_info_to_nast_shape_info
811 { si_allows_unknown_fields
; si_shape_field_list
} =
812 let f fdm shape_field
=
813 let pos, name
= convert_shape_name env shape_field
.sf_name
in
814 if ShapeMap.mem name fdm
815 then Errors.fd_name_already_bound
pos;
817 name
(shape_field_to_shape_field_info
env shape_field
) fdm
in
819 List.fold_left si_shape_field_list ~init
:ShapeMap.empty ~
f in
821 nsi_allows_unknown_fields
=si_allows_unknown_fields
;
825 and hint_ ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
826 ~in_where_clause ?
(tp_depth
=0)
827 is_static_var
env x =
829 hint ~is_static_var ~forbid_this ~allow_typedef ~allow_wildcard
in
832 N.Htuple
(List.map hl
(hint ~allow_retonly
env))
834 (* void/noreturn are permitted for Typing.option_return_only_typehint *)
835 N.Hoption
(hint ~allow_retonly
env h
)
837 let h = hint ~allow_retonly
env h
839 | Hfun
(is_coroutine
, hl
, kl
, variadic_hint
, h) ->
840 let is_reactive = false in
841 let variadic_hint = match variadic_hint with
842 | Hvariadic Some
(h) -> N.Hvariadic
(Some
(hint env h))
843 | Hvariadic None
-> N.Hvariadic
(None
)
844 | Hnon_variadic
-> N.Hnon_variadic
in
845 N.Hfun
(is_reactive, is_coroutine
, List.map hl
(hint env), kl
, variadic_hint,
846 hint ~allow_retonly
:true env h)
847 (* Special case for Rx<function> *)
848 | Happly
((_, "Rx"), [(_, Hfun
(is_coroutine
, hl
, kl
, variadic_hint, h))]) ->
849 let is_reactive = true in
850 let variadic_hint = match variadic_hint with
851 | Hvariadic Some
(h) -> N.Hvariadic
(Some
(hint env h))
852 | Hvariadic None
-> N.Hvariadic
(None
)
853 | Hnon_variadic
-> N.Hnon_variadic
in
854 N.Hfun
(is_reactive, is_coroutine
, List.map hl
(hint env), kl
, variadic_hint,
855 hint ~allow_retonly
:true env h)
856 | Happly
((p, _x
) as id, hl
) ->
858 hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth
862 | N.Hprim
_ | N.Hmixed
| N.Hnonnull
->
863 if hl
<> [] then Errors.unexpected_type_arguments
p
867 | Haccess
((pos, root_id
) as root
, id, ids
) ->
870 | x when x = SN.Classes.cSelf
->
871 (match (fst
env).current_cls
with
873 Errors.self_outside_class
pos;
878 | x when x = SN.Classes.cStatic
|| x = SN.Classes.cParent
->
879 Errors.invalid_type_access_root root
; N.Hany
881 let tconst_on_generics_enabled =
882 TypecheckerOptions.experimental_feature_enabled
884 TypecheckerOptions.experimental_tconst_on_generics
in
886 hint_id ~forbid_this ~allow_retonly
887 ~allow_typedef ~allow_wildcard
:false ~tp_depth
env is_static_var root
[] in
889 | N.Hthis
| N.Happly
_ as h -> h
890 | N.Habstr
_ when in_where_clause
&& tconst_on_generics_enabled ->
892 | _ -> Errors.invalid_type_access_root root
; N.Hany
895 N.Haccess
((pos, root_ty), id :: ids
)
896 | Hshape ast_shape_info
->
897 N.Hshape
(ast_shape_info_to_nast_shape_info
env ast_shape_info
)
899 and hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth
900 env is_static_var
(p, x as id) hl
=
901 let params = (fst
env).type_params
in
902 if is_alok_type_name id && not
(SMap.mem x params)
903 then Errors.typeparam_alok
id;
904 if is_static_var
&& SMap.mem x params
905 then Errors.generic_class_var
(fst
id);
906 (* some common Xhp screw ups *)
907 if (x = "Xhp") || (x = ":Xhp") || (x = "XHP")
908 then Errors.disallowed_xhp_type
p x;
909 match try_castable_hint ~forbid_this ~allow_wildcard ~tp_depth
env p x hl
with
913 | x when x = SN.Typehints.wildcard
&& allow_wildcard
&& tp_depth
= 1 ->
915 (Errors.tparam_with_tparam
p x;
919 | x when x = SN.Typehints.wildcard
->
920 Errors.wildcard_disallowed
p;
922 | x when x.[0] = '
\\'
&&
923 ( x = ("\\"^
SN.Typehints.void
)
924 || x = ("\\"^
SN.Typehints.noreturn
)
925 || x = ("\\"^
SN.Typehints.int)
926 || x = ("\\"^
SN.Typehints.bool)
927 || x = ("\\"^
SN.Typehints.float)
928 || x = ("\\"^
SN.Typehints.num
)
929 || x = ("\\"^
SN.Typehints.string)
930 || x = ("\\"^
SN.Typehints.resource
)
931 || x = ("\\"^
SN.Typehints.mixed
)
932 || x = ("\\"^
SN.Typehints.nonnull
)
933 || x = ("\\"^
SN.Typehints.array
)
934 || x = ("\\"^
SN.Typehints.arraykey
)
935 || x = ("\\"^
SN.Typehints.integer
)
936 || x = ("\\"^
SN.Typehints.boolean
)
937 || x = ("\\"^
SN.Typehints.double
)
938 || x = ("\\"^
SN.Typehints.real)
940 Errors.primitive_toplevel
p;
942 | x when x = SN.Typehints.void
&& allow_retonly
-> N.Hprim
N.Tvoid
943 | x when x = SN.Typehints.void
->
944 if TypecheckerOptions.experimental_feature_enabled
946 TypecheckerOptions.experimental_void_is_type_of_null
948 else (Errors.return_only_typehint
p `void
; N.Hany
)
949 | x when x = SN.Typehints.noreturn
&& allow_retonly
-> N.Hprim
N.Tnoreturn
950 | x when x = SN.Typehints.noreturn
->
951 Errors.return_only_typehint
p `noreturn
;
953 | x when x = SN.Typehints.num
-> N.Hprim
N.Tnum
954 | x when x = SN.Typehints.resource
-> N.Hprim
N.Tresource
955 | x when x = SN.Typehints.arraykey
-> N.Hprim
N.Tarraykey
956 | x when x = SN.Typehints.mixed
-> N.Hmixed
957 | x when x = SN.Typehints.nonnull
-> N.Hnonnull
958 | x when x = SN.Typehints.dynamic
-> N.Hdynamic
959 | x when x = SN.Typehints.this
&& not forbid_this
->
961 then Errors.this_no_argument
p;
962 (match (fst
env).current_cls
with
964 Errors.this_hint_outside_class
p;
969 | x when x = SN.Typehints.this
->
970 (match (fst
env).current_cls
with
972 Errors.this_hint_outside_class
p
974 Errors.this_type_forbidden
p
977 | x when x = SN.Classes.cClassname
&& (List.length hl
) <> 1 ->
978 Errors.classname_param
p;
980 | _ when String.lowercase_ascii
x = SN.Typehints.this
->
981 Errors.lowercase_this
p x;
983 | _ when SMap.mem x params ->
985 Errors.tparam_with_tparam
p x;
988 let name = Env.type_name env id ~allow_typedef
in
989 (* Note that we are intentionally setting allow_typedef to `true` here.
990 * In general, generics arguments can be typedefs -- there is no
991 * runtime restriction. *)
992 N.Happly
(name, hintl ~allow_wildcard ~forbid_this ~allow_typedef
:true
993 ~allow_retonly
:true ~tp_depth
:(tp_depth
+1) env hl
)
996 (* Hints that are valid both as casts and type annotations. Neither
997 * casts nor annotations are a strict subset of the other: For
998 * instance, 'object' is not a valid annotation. Thus callers will
999 * have to handle the remaining cases. *)
1000 and try_castable_hint ?
(forbid_this
=false) ?
(allow_wildcard
=false) ~tp_depth
env p x hl
=
1001 let hint = hint ~forbid_this ~tp_depth
:(tp_depth
+1) ~allow_wildcard ~allow_retonly
:false in
1002 let canon = String.lowercase_ascii
x in
1003 let opt_hint = match canon with
1004 | nm
when nm
= SN.Typehints.int -> Some
(N.Hprim
N.Tint
)
1005 | nm
when nm
= SN.Typehints.bool -> Some
(N.Hprim
N.Tbool
)
1006 | nm
when nm
= SN.Typehints.float -> Some
(N.Hprim
N.Tfloat
)
1007 | nm
when nm
= SN.Typehints.string -> Some
(N.Hprim
N.Tstring
)
1008 | nm
when nm
= SN.Typehints.array
->
1009 let tcopt = (fst
env).tcopt in
1010 let array_typehints_disallowed =
1011 TypecheckerOptions.disallow_array_typehint
tcopt in
1012 if array_typehints_disallowed
1013 then Errors.array_typehints_disallowed p;
1015 | [] -> N.Harray
(None
, None
)
1016 | [val_
] -> N.Harray
(Some
(hint env val_
), None
)
1018 N.Harray
(Some
(hint env key_
), Some
(hint env val_
))
1019 | _ -> Errors.too_many_type_arguments
p; N.Hany
1021 | nm
when nm
= SN.Typehints.darray
->
1024 if (fst
env).in_mode
= FileInfo.Mstrict
then
1025 Errors.too_few_type_arguments
p;
1026 N.Hdarray
((p, N.Hany
), (p, N.Hany
))
1027 | [_] -> Errors.too_few_type_arguments
p; N.Hany
1028 | [key_
; val_
] -> N.Hdarray
(hint env key_
, hint env val_
)
1029 | _ -> Errors.too_many_type_arguments
p; N.Hany
)
1030 | nm
when nm
= SN.Typehints.varray
->
1033 if (fst
env).in_mode
= FileInfo.Mstrict
then
1034 Errors.too_few_type_arguments
p;
1035 N.Hvarray
(p, N.Hany
)
1036 | [val_
] -> N.Hvarray
(hint env val_
)
1037 | _ -> Errors.too_many_type_arguments
p; N.Hany
)
1038 | nm
when nm
= SN.Typehints.varray_or_darray
->
1041 if (fst
env).in_mode
= FileInfo.Mstrict
then
1042 Errors.too_few_type_arguments
p;
1043 N.Hvarray_or_darray
(p, N.Hany
)
1044 | [val_
] -> N.Hvarray_or_darray
(hint env val_
)
1045 | _ -> Errors.too_many_type_arguments
p; N.Hany
)
1046 | nm
when nm
= SN.Typehints.integer
->
1047 Errors.primitive_invalid_alias
p nm
SN.Typehints.int;
1048 Some
(N.Hprim
N.Tint
)
1049 | nm
when nm
= SN.Typehints.boolean
->
1050 Errors.primitive_invalid_alias
p nm
SN.Typehints.bool;
1051 Some
(N.Hprim
N.Tbool
)
1052 | nm
when nm
= SN.Typehints.double
|| nm
= SN.Typehints.real ->
1053 Errors.primitive_invalid_alias
p nm
SN.Typehints.float;
1054 Some
(N.Hprim
N.Tfloat
)
1057 let () = match opt_hint with
1058 | Some
_ when canon <> x -> Errors.primitive_invalid_alias
p x canon
1062 and constraint_ ?
(forbid_this
=false) env (ck
, h) = ck
, hint ~forbid_this
env h
1064 and hintl ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth
env l
=
1066 (hint ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth
env)
1067 and hintl_funcall
env l
=
1069 ~allow_wildcard
:true
1077 (**************************************************************************)
1078 (* All the methods and static methods of an interface are "implicitly"
1079 * declared as abstract
1081 (**************************************************************************)
1083 let add_abstract m
= {m
with N.m_abstract
= true}
1085 let add_abstractl methods
= List.map methods
add_abstract
1087 let interface c constructor methods smethods
=
1088 if c
.c_kind
<> Cinterface
then constructor
, methods
, smethods
else
1089 let constructor = Option.map
constructor add_abstract in
1090 let methods = add_abstractl methods in
1091 let smethods = add_abstractl smethods in
1092 constructor, methods, smethods
1094 (**************************************************************************)
1095 (* Checking for collision on method names *)
1096 (**************************************************************************)
1098 let check_method acc
{ N.m_name
= (p, x); _ } =
1100 then Errors.method_name_already_bound
p x;
1103 let check_name_collision methods =
1104 ignore
(List.fold_left
methods ~init
:SSet.empty ~
f:check_method)
1106 (**************************************************************************)
1107 (* Checking for shadowing of method type parameters *)
1108 (**************************************************************************)
1110 let check_method_tparams class_tparam_names
{ N.m_tparams
= tparams
; _ } =
1111 List.iter tparams
begin fun (_, (p,x),_) ->
1112 List.iter class_tparam_names
1113 (fun (pc
,xc
) -> if (x = xc
) then Errors.shadowed_type_param
p pc
x)
1116 let check_tparams_constructor class_tparam_names
constructor =
1117 match constructor with
1119 | Some constr
-> check_method_tparams class_tparam_names constr
1121 let check_tparams_shadow class_tparam_names
methods =
1122 List.iter
methods (check_method_tparams class_tparam_names
)
1124 let check_break_continue_level p level_opt
=
1125 if Option.is_some level_opt
1126 then Errors.break_continue_n_not_supported
p
1128 let ensure_name_not_dynamic env e err
=
1130 | (_, (Id
_ | Lvar
_)) -> ()
1132 if (fst
env).in_mode
= FileInfo.Mstrict
1135 (* Naming of a class *)
1136 let rec class_ nenv c
=
1137 let constraints = make_constraints c
.c_tparams
in
1138 let env = Env.make_class_env nenv
constraints c
in
1139 (* Checking for a code smell *)
1140 List.iter c
.c_tparams
check_constraint;
1141 let name = Env.type_name env c
.c_name ~allow_typedef
:false in
1143 List.fold_right c
.c_body ~init
:[] ~
f:(class_static_method
env) in
1144 let sprops = List.fold_right c
.c_body ~init
:[] ~
f:(class_prop_static
env) in
1145 let attrs = user_attributes
env c
.c_user_attributes
in
1146 let const = (Attributes.find
SN.UserAttributes.uaConst
attrs) in
1147 let props = List.fold_right c
.c_body ~init
:[] ~
f:(class_prop ~
const env) in
1149 List.map c
.c_extends
1150 (hint ~allow_retonly
:false ~allow_typedef
:false env) in
1151 let parents = match c
.c_kind
with
1152 (* Make enums implicitly extend the BuiltinEnum class in order to provide
1153 * utility methods. *)
1155 let pos = fst
name in
1156 let enum_type = pos, N.Happly
(name, []) in
1158 pos, N.Happly
((pos, Naming_special_names.Classes.cHH_BuiltinEnum
),
1162 let methods = List.fold_right c
.c_body ~init
:[] ~
f:(class_method
env) in
1163 let uses = List.fold_right c
.c_body ~init
:[] ~
f:(class_use
env) in
1165 List.fold_right c
.c_body ~init
:[] ~
f:(xhp_attr_use
env) in
1167 Option.value ~default
:[] @@
1168 List.fold_right c
.c_body ~init
:None ~
f:(xhp_category env) in
1169 let req_implements, req_extends
= List.fold_right c
.c_body
1170 ~init
:([], []) ~
f:(class_require
env c
.c_kind
) in
1171 (* Setting a class type parameters constraint to the 'this' type is weird
1172 * so lets forbid it for now.
1174 let tparam_l = type_paraml ~forbid_this
:true env c
.c_tparams
in
1175 let consts = List.fold_right ~
f:(class_const
env) c
.c_body ~init
:[] in
1177 List.fold_right ~
f:(class_typeconst
env) c
.c_body ~init
:[] in
1178 let implements = List.map c
.c_implements
1179 (hint ~allow_retonly
:false ~allow_typedef
:false env) in
1180 let constructor = List.fold_left ~
f:(constructor env) ~init
:None c
.c_body
in
1181 let constructor, methods, smethods =
1182 interface c
constructor methods smethods in
1183 let class_tparam_names = List.map c
.c_tparams
(fun (_, x,_) -> x) in
1184 let enum = Option.map c
.c_enum
(enum_
env) in
1185 check_tparams_constructor class_tparam_names constructor;
1186 check_name_collision methods;
1187 check_tparams_shadow class_tparam_names methods;
1188 check_name_collision smethods;
1189 check_tparams_shadow class_tparam_names smethods;
1191 { N.c_annotation
= ();
1192 N.c_mode
= c
.c_mode
;
1193 N.c_final
= c
.c_final
;
1194 N.c_is_xhp
= c
.c_is_xhp
;
1195 N.c_kind
= c
.c_kind
;
1197 N.c_tparams
= (tparam_l, constraints);
1198 N.c_extends
= parents;
1200 N.c_xhp_attr_uses
= xhp_attr_uses;
1201 N.c_xhp_category
= xhp_category;
1202 N.c_req_extends
= req_extends
;
1203 N.c_req_implements
= req_implements;
1204 N.c_implements
= implements;
1205 N.c_consts
= consts;
1206 N.c_typeconsts
= typeconsts;
1207 N.c_static_vars
= sprops;
1209 N.c_constructor
= constructor;
1210 N.c_static_methods
= smethods;
1211 N.c_methods
= methods;
1212 N.c_user_attributes
= attrs;
1218 and user_attributes
env attrl
=
1219 let seen = Hashtbl.create
0 in
1220 let validate_seen = begin fun ua_name
->
1221 let pos, name = ua_name
in
1222 let existing_attr_pos =
1223 try Some
(Hashtbl.find
seen name)
1224 with Not_found
-> None
1225 in (match existing_attr_pos with
1226 | Some
p -> Errors.duplicate_user_attribute ua_name
p; false
1227 | None
-> Hashtbl.add
seen name pos; true
1230 List.fold_left attrl ~init
:[] ~
f:begin fun acc
{ua_name
; ua_params
} ->
1231 if not
(validate_seen ua_name
) then acc
1233 N.ua_name
= ua_name
;
1234 N.ua_params
= List.map ua_params
(expr
env)
1239 and xhp_attribute_decl
env h cv is_required maybe_enum
=
1240 let p, (_, id), default
= cv
in
1241 if is_required
&& Option.is_some default
then
1242 Errors.xhp_required_with_default
p id;
1243 let h = (match maybe_enum
with
1244 | Some
(pos, _optional
, items
) ->
1245 let contains_int = List.exists items
begin function
1249 let contains_str = List.exists items
begin function
1250 | _, String
_ | _, String2
_ -> true
1253 if contains_int && not
contains_str then
1254 Some
(pos, Happly
((pos, "int"), []))
1255 else if not
contains_int && contains_str then
1256 Some
(pos, Happly
((pos, "string"), []))
1258 (* If the list was empty, or if there was a mix of
1259 ints and strings, then fallback to mixed *)
1260 Some
(pos, Happly
((pos, "mixed"), []))
1262 let h = (match h with
1263 | Some
(p, ((Hoption
_) as x)) -> begin
1264 (* `null` has special meaning in XHP and a required attribute cannot
1265 * actually be nullable *)
1266 if is_required
then Errors.xhp_optional_required_attr
p id;
1269 | Some
(p, ((Happly
((_, "mixed"), [])) as x)) -> Some
(p, x)
1271 (* If a non-nullable attribute is not marked as "@required"
1272 AND it does not have a non-null default value, make the
1273 typehint nullable for now *)
1277 | Some
(_, Null
) -> false
1280 else Some
(p, Hoption
(p, h))
1282 let h = Option.map
h (hint env) in
1283 let cv = class_prop_
env cv in
1287 { N.e_base
= hint env e
.e_base
;
1288 N.e_constraint
= Option.map e
.e_constraint
(hint env);
1291 and type_paraml ?
(forbid_this
= false) env tparams
=
1292 let _, ret
= List.fold_left tparams ~init
:(SMap.empty
, [])
1293 ~
f:(fun (seen, tparaml) ((_, (p, name), _) as tparam
) ->
1294 match SMap.get
name seen with
1296 SMap.add
name p seen, (type_param ~forbid_this
env tparam
)::tparaml
1298 Errors.shadowed_type_param
p pos name;
1304 and type_param ~forbid_this
env (variance
, param_name
, cstr_list
) =
1307 List.map cstr_list
(constraint_ ~forbid_this
env)
1309 and type_where_constraints
env locl_cstrl
=
1310 List.map locl_cstrl
(fun (h1
, ck
, h2
) ->
1311 let ty1 = hint ~in_where_clause
:true env h1
in
1312 let ty2 = hint ~in_where_clause
:true env h2
in
1315 and class_use
env x acc
=
1317 | Attributes
_ -> acc
1321 hint ~allow_typedef
:false env h :: acc
1322 | ClassUseAlias
_ -> acc
1323 | ClassUsePrecedence
_ -> acc
1324 | XhpAttrUse
_ -> acc
1325 | ClassTraitRequire
_ -> acc
1326 | ClassVars
_ -> acc
1328 | XhpCategory
_ -> acc
1331 | TypeConst
_ -> acc
1333 and xhp_attr_use
env x acc
=
1335 | Attributes
_ -> acc
1339 | ClassUseAlias
_ -> acc
1340 | ClassUsePrecedence
_ -> acc
1342 hint ~allow_typedef
:false env h :: acc
1343 | ClassTraitRequire
_ -> acc
1344 | ClassVars
_ -> acc
1346 | XhpCategory
_ -> acc
1349 | TypeConst
_ -> acc
1351 and xhp_category _env
x acc
=
1353 | Attributes
_ -> acc
1357 | ClassUseAlias
_ -> acc
1358 | ClassUsePrecedence
_ -> acc
1359 | XhpAttrUse
_ -> acc
1360 | ClassTraitRequire
_ -> acc
1361 | ClassVars
_ -> acc
1363 | XhpCategory
(_, cs
) ->
1365 | Some
_ -> Errors.multiple_xhp_category
(fst
(List.hd_exn cs
)); acc
1369 | TypeConst
_ -> acc
1371 and class_require
env c_kind
x acc
=
1373 | Attributes
_ -> acc
1377 | ClassUseAlias
_ -> acc
1378 | ClassUsePrecedence
_ -> acc
1379 | XhpAttrUse
_ -> acc
1380 | ClassTraitRequire
(MustExtend
, h)
1381 when c_kind
<> Ast.Ctrait
&& c_kind
<> Ast.Cinterface
->
1382 let () = Errors.invalid_req_extends
(fst
h) in
1384 | ClassTraitRequire
(MustExtend
, h) ->
1385 let acc_impls, acc_exts
= acc
in
1386 (acc_impls, hint ~allow_typedef
:false env h :: acc_exts
)
1387 | ClassTraitRequire
(MustImplement
, h) when c_kind
<> Ast.Ctrait
->
1388 let () = Errors.invalid_req_implements
(fst
h) in
1390 | ClassTraitRequire
(MustImplement
, h) ->
1391 let acc_impls, acc_exts
= acc
in
1392 (hint ~allow_typedef
:false env h :: acc_impls, acc_exts
)
1393 | ClassVars
_ -> acc
1395 | XhpCategory
_ -> acc
1398 | TypeConst
_ -> acc
1400 and constructor env acc
= function
1401 | Attributes
_ -> acc
1405 | ClassUseAlias
_ -> acc
1406 | ClassUsePrecedence
_ -> acc
1407 | XhpAttrUse
_ -> acc
1408 | ClassTraitRequire
_ -> acc
1409 | ClassVars
_ -> acc
1411 | XhpCategory
_ -> acc
1413 | Method
({ m_name
= (p, name); _ } as m
)
1414 when name = SN.Members.__construct
->
1416 | None
-> Some
(method_
(fst
env) m
)
1417 | Some
_ -> Errors.method_name_already_bound
p name; acc
)
1419 | TypeConst
_ -> acc
1421 and class_const
env x acc
=
1423 | Attributes
_ -> acc
1424 | Const
(h, l
) -> const_defl
h env l
@ acc
1425 | AbsConst
(h, x) -> abs_const_def
env h x :: acc
1427 | ClassUseAlias
_ -> acc
1428 | ClassUsePrecedence
_ -> acc
1429 | XhpAttrUse
_ -> acc
1430 | ClassTraitRequire
_ -> acc
1431 | ClassVars
_ -> acc
1433 | XhpCategory
_ -> acc
1436 | TypeConst
_ -> acc
1438 and class_prop_static
env x acc
=
1440 | Attributes
_ -> acc
1442 | ClassUseAlias
_ -> acc
1443 | ClassUsePrecedence
_ -> acc
1444 | XhpAttrUse
_ -> acc
1445 | ClassTraitRequire
_ -> acc
1448 | ClassVars
{ cv_kinds
= kl
; cv_hint
= h; cv_names
= cvl
; _ }
1449 when List.mem kl Static
->
1450 (* Static variables are shared for all classes in the hierarchy.
1451 * This makes the 'this' type completely unsafe as a type for a
1452 * static variable. See test/typecheck/this_tparam_static.php as
1453 * an example of what can occur.
1455 let h = Option.map
h (hint ~forbid_this
:true ~is_static_var
:true env) in
1456 let cvl = List.map
cvl (class_prop_
env) in
1457 let cvl = List.map
cvl (fill_prop kl
h) in
1459 | ClassVars
_ -> acc
1461 | XhpCategory
_ -> acc
1464 | TypeConst
_ -> acc
1466 and class_prop
env ?
(const = None
) x acc
=
1468 | Attributes
_ -> acc
1470 | ClassUseAlias
_ -> acc
1471 | ClassUsePrecedence
_ -> acc
1472 | XhpAttrUse
_ -> acc
1473 | ClassTraitRequire
_ -> acc
1476 | ClassVars
{ cv_kinds
; cv_hint
; cv_names
; cv_user_attributes
; _ }
1477 when not
(List.mem cv_kinds Static
) ->
1478 let h = Option.map cv_hint
(hint env) in
1479 let cvl = List.map cv_names
(class_prop_
env) in
1480 let cvl = List.map
cvl (fill_prop cv_kinds
h) in
1481 let attrs = user_attributes
env cv_user_attributes
in
1482 (* if class is __Const, make all member fields __Const *)
1483 let attrs = match const with
1484 | Some c
-> if not
(Attributes.mem SN.UserAttributes.uaConst
attrs)
1485 then c
:: attrs else attrs
1487 let cvl = List.map
cvl (fun cv -> { cv with N.cv_user_attributes
= attrs}) in
1489 | ClassVars
_ -> acc
1490 | XhpAttr
(h, cv, is_required
, maybe_enum
) ->
1491 (xhp_attribute_decl
env h cv is_required maybe_enum
) :: acc
1492 | XhpCategory
_ -> acc
1495 | TypeConst
_ -> acc
1497 and class_static_method
env x acc
=
1499 | Attributes
_ -> acc
1501 | ClassUseAlias
_ -> acc
1502 | ClassUsePrecedence
_ -> acc
1503 | XhpAttrUse
_ -> acc
1504 | ClassTraitRequire
_ -> acc
1507 | ClassVars
_ -> acc
1509 | XhpCategory
_ -> acc
1511 | Method m
when snd m
.m_name
= SN.Members.__construct
-> acc
1512 | Method m
when List.mem m
.m_kind Static
-> method_
(fst
env) m
:: acc
1514 | TypeConst
_ -> acc
1516 and class_method
env x acc
=
1518 | Attributes
_ -> acc
1520 | ClassUseAlias
_ -> acc
1521 | ClassUsePrecedence
_ -> acc
1522 | XhpAttrUse
_ -> acc
1523 | ClassTraitRequire
_ -> acc
1526 | ClassVars
_ -> acc
1528 | XhpCategory
_ -> acc
1530 | Method m
when snd m
.m_name
= SN.Members.__construct
-> acc
1531 | Method m
when not
(List.mem m
.m_kind Static
) ->(
1532 match (m
.m_name
, m
.m_params
) with
1533 | ( (m_pos
, m_name
), _::_) when m_name
= SN.Members.__clone
->
1534 Errors.clone_too_many_arguments m_pos
; acc
1535 | _ -> let genv = fst
env in method_
genv m
:: acc
1538 | TypeConst
_ -> acc
1540 and class_typeconst
env x acc
=
1542 | Attributes
_ -> acc
1546 | ClassUseAlias
_ -> acc
1547 | ClassUsePrecedence
_ -> acc
1548 | XhpAttrUse
_ -> acc
1549 | ClassTraitRequire
_ -> acc
1550 | ClassVars
_ -> acc
1552 | XhpCategory
_ -> acc
1555 | TypeConst t
-> typeconst
env t
:: acc
1557 and check_constant_expr
env (pos, e
) =
1559 | Unsafeexpr
_ | Id
_ | Null
| True
| False
| Int
_
1560 | Float
_ | String
_ -> ()
1561 | Class_const
((_, cls
), _)
1562 when (match cls
with Id
(_, "static") -> false | _ -> true) -> ()
1563 | Unop
((Uplus
| Uminus
| Utild
| Unot
), e
) -> check_constant_expr
env e
1564 | Binop
(op
, e1
, e2) ->
1565 (* Only assignment is invalid *)
1567 | Eq
_ -> Errors.illegal_constant
pos
1569 check_constant_expr
env e1
;
1570 check_constant_expr
env e2)
1571 | Eif
(e1
, e2, e3
) ->
1572 check_constant_expr
env e1
;
1573 Option.iter
e2 (check_constant_expr
env);
1574 check_constant_expr
env e3
1575 | Array l
-> List.iter l
(check_afield_constant_expr
env)
1576 | Darray l
-> List.iter l
(fun (e1
, e2) ->
1577 check_constant_expr
env e1
;
1578 check_constant_expr
env e2)
1579 | Varray l
-> List.iter l
(check_constant_expr
env)
1581 (* Only check the values because shape field names are always legal *)
1582 List.iter fdl
(fun (_, e
) -> check_constant_expr
env e
)
1583 | Call
((_, Id
(_, cn
)), _, el
, uel
) when cn
= SN.SpecialFunctions.tuple
->
1584 (* Tuples are not really function calls, they are just parsed that way*)
1585 arg_unpack_unexpected uel
;
1586 List.iter el
(check_constant_expr
env)
1587 | Collection
(id, l
) ->
1588 let p, cn
= NS.elaborate_id
((fst
env).namespace
) NS.ElaborateClass
id in
1589 (* Only vec/keyset/dict are allowed because they are value types *)
1592 cn
= SN.Collections.cVec
1593 || cn
= SN.Collections.cKeyset
1594 || cn
= SN.Collections.cDict
->
1595 List.iter l
(check_afield_constant_expr
env)
1596 | _ -> Errors.illegal_constant
p)
1597 | _ -> Errors.illegal_constant
pos
1599 and check_afield_constant_expr
env = function
1600 | AFvalue e
-> check_constant_expr
env e
1601 | AFkvalue
(e1
, e2) ->
1602 check_constant_expr
env e1
;
1603 check_constant_expr
env e2
1605 and constant_expr
env e
=
1606 let valid_constant_expression = Errors.try_with_error
begin fun () ->
1607 check_constant_expr
env e
;
1609 end (fun () -> false) in
1610 if valid_constant_expression then expr
env e
else fst e
, N.Any
1612 and const_defl
h env l
= List.map l
(const_def
h env)
1613 and const_def
h env (x, e
) =
1614 Env.bind_class_const env x;
1615 let h = Option.map
h (hint env) in
1616 h, x, Some
(constant_expr
env e
)
1618 and abs_const_def
env h x =
1619 Env.bind_class_const env x;
1620 let h = Option.map
h (hint env) in
1623 and class_prop_
env (_, x, e
) =
1624 Env.bind_prop env x;
1625 let e = Option.map
e (expr
env) in
1626 (* If the user has not provided a value, we initialize the member variable
1627 * ourselves to a value of type Tany. Classes might inherit from our decl
1628 * mode class that are themselves not in decl, and there's no way to figure
1629 * out what variables are initialized in a decl class without typechecking
1630 * its initalizers and constructor, which we don't want to do, so just
1631 * assume we're covered. *)
1633 if (fst
env).in_mode
= FileInfo.Mdecl
&& e = None
1634 then Some
(fst
x, N.Any
)
1637 let is_xhp = try ((String.sub
(snd
x) 0 1) = ":") with Invalid_argument
_ -> false
1639 { N.cv_final
= false;
1640 N.cv_is_xhp
= is_xhp;
1641 N.cv_visibility
= N.Public
;
1645 N.cv_user_attributes
= [];
1648 and fill_prop kl ty
x =
1649 let x = { x with N.cv_type
= ty
} in
1650 List.fold_left kl ~init
:x ~
f:begin fun x k
->
1651 (* There is no field Static, they are dissociated earlier.
1652 An abstract class variable doesn't make sense.
1655 | Final
-> { x with N.cv_final
= true }
1658 | Private
-> { x with N.cv_visibility
= N.Private
}
1659 | Public
-> { x with N.cv_visibility
= N.Public
}
1660 | Protected
-> { x with N.cv_visibility
= N.Protected
}
1663 and typeconst
env t
=
1664 (* We use the same namespace as constants within the class so we cannot have
1665 * a const and type const with the same name
1667 Env.bind_class_const env t
.tconst_name
;
1668 let constr = Option.map t
.tconst_constraint
(hint env) in
1670 match t
.tconst_type
with
1671 | None
when not t
.tconst_abstract
->
1672 Errors.not_abstract_without_typeconst t
.tconst_name
;
1674 | Some _h
when t
.tconst_abstract
->
1675 Errors.abstract_with_typeconst t
.tconst_name
;
1679 let type_ = Option.map
hint_ (hint env) in
1680 N.({ c_tconst_name
= t
.tconst_name
;
1681 c_tconst_constraint
= constr;
1682 c_tconst_type
= type_;
1685 and func_body_had_unsafe
env = Env.has_unsafe env
1687 and method_
genv m
=
1688 let genv = extend_params
genv m
.m_tparams
in
1689 let env = genv, Env.empty_local UBMErr
in
1690 (* Cannot use 'this' if it is a public instance method *)
1691 let variadicity, paraml
= fun_paraml
env m
.m_params
in
1692 let contains_visibility = List.exists m
.m_kind ~
f:(
1699 if not
contains_visibility then
1700 Errors.method_needs_visibility
(fst m
.m_name
);
1701 let acc = false, false, N.Public
in
1702 let final, abs
, vis
= List.fold_left ~
f:kind ~init
:acc m
.m_kind
in
1703 List.iter m
.m_tparams
check_constraint;
1704 let tparam_l = type_paraml
env m
.m_tparams
in
1705 let where_constraints = type_where_constraints
env m
.m_constrs
in
1706 let ret = Option.map m
.m_ret
(hint ~allow_retonly
:true env) in
1707 let f_kind = m
.m_fun_kind
in
1708 let body = (match genv.in_mode
with
1709 | FileInfo.Mdecl
| FileInfo.Mphp
->
1714 | FileInfo.Mstrict
| FileInfo.Mpartial
->
1716 N.fub_ast
= m
.m_body
;
1717 fub_tparams
= m
.m_tparams
;
1718 fub_namespace
= genv.namespace
;
1721 let attrs = user_attributes
env m
.m_user_attributes
in
1722 { N.m_annotation
= () ;
1724 N.m_visibility
= vis
;
1725 N.m_abstract
= abs
;
1726 N.m_name
= m
.Ast.m_name
;
1727 N.m_tparams
= tparam_l ;
1728 N.m_where_constraints
= where_constraints ;
1729 N.m_params
= paraml
;
1731 N.m_fun_kind
= f_kind ;
1733 N.m_variadic
= variadicity ;
1734 N.m_user_attributes
= attrs;
1735 N.m_ret_by_ref
= m
.m_ret_by_ref
;
1738 and kind
(final, abs
, vis
) = function
1739 | Final
-> true, abs
, vis
1740 | Static
-> final, abs
, vis
1741 | Abstract
-> final, true, vis
1742 | Private
-> final, abs
, N.Private
1743 | Public
-> final, abs
, N.Public
1744 | Protected
-> final, abs
, N.Protected
1746 and fun_paraml
env l
=
1747 let _names = List.fold_left ~
f:check_repetition ~init
:SSet.empty l
in
1748 let variadicity, l
= determine_variadicity
env l
in
1749 variadicity, List.map l
(fun_param
env)
1751 and determine_variadicity
env l
=
1753 | [] -> N.FVnonVariadic
, []
1755 match x.param_is_variadic
, x.param_id
with
1756 | false, _ -> N.FVnonVariadic
, [x]
1757 (* NOTE: variadic params are removed from the list *)
1758 | true, (p, "...") -> N.FVellipsis
p, []
1759 | true, _ -> N.FVvariadicArg
(fun_param
env x), []
1762 let variadicity, rl
= determine_variadicity
env rl
in
1763 variadicity, x :: rl
1765 and fun_param
env param
=
1766 let p, name = param
.param_id
in
1767 let ident = Local_id.get
name in
1768 Env.add_lvar env param
.param_id
(p, ident);
1769 let ty = Option.map param
.param_hint
(hint env) in
1770 let eopt = Option.map param
.param_expr
(expr
env) in
1771 let _ = match (fst
env).in_mode
with
1772 | FileInfo.Mstrict
when param
.param_is_reference
->
1773 Errors.reference_in_strict_mode
p;
1775 { N.param_annotation
= p;
1777 param_is_reference
= param
.param_is_reference
;
1778 param_is_variadic
= param
.param_is_variadic
;
1782 param_callconv
= param
.param_callconv
;
1783 param_user_attributes
= user_attributes
env param
.param_user_attributes
;
1786 and make_constraints paraml
=
1787 List.fold_right paraml ~init
:SMap.empty
1788 ~
f:begin fun (_, (_, x), cstr_list
) acc ->
1789 SMap.add
x cstr_list
acc
1792 and extend_params
genv paraml
=
1793 let params = List.fold_right paraml ~init
:genv.type_params
1794 ~
f:begin fun (_, (_, x), cstr_list
) acc ->
1795 SMap.add
x cstr_list
acc
1797 { genv with type_params
= params }
1800 let tparams = make_constraints
f.f_tparams
in
1801 let genv = Env.make_fun_decl_genv nenv
tparams f in
1802 let lenv = Env.empty_local UBMErr
in
1803 let env = genv, lenv in
1804 let where_constraints = type_where_constraints
env f.f_constrs
in
1805 let h = Option.map
f.f_ret
(hint ~allow_retonly
:true env) in
1806 let variadicity, paraml
= fun_paraml
env f.f_params
in
1807 let x = Env.fun_id env f.f_name
in
1808 List.iter
f.f_tparams
check_constraint;
1809 let f_tparams = type_paraml
env f.f_tparams in
1810 let f_kind = f.f_fun_kind
in
1811 let body = match genv.in_mode
with
1812 | FileInfo.Mdecl
| FileInfo.Mphp
->
1817 | FileInfo.Mstrict
| FileInfo.Mpartial
->
1819 N.fub_ast
= f.f_body
;
1820 fub_tparams
= f.f_tparams;
1821 fub_namespace
= f.f_namespace
;
1825 N.f_annotation
= ();
1829 f_tparams = f_tparams;
1830 f_where_constraints
= where_constraints;
1833 f_fun_kind
= f_kind;
1834 f_variadic
= variadicity;
1835 f_user_attributes
= user_attributes
env f.f_user_attributes
;
1836 f_ret_by_ref
= f.f_ret_by_ref
;
1840 and cut_unsafe ?
(replacement
=Noop
) env = function
1842 | (p, Unsafe
) :: _ -> Env.set_unsafe env true; [p, replacement
]
1843 | (p, Block b
) :: rest
->
1844 (p, Block
(cut_unsafe ~replacement
env b
)) ::
1845 (cut_unsafe ~replacement
env rest
)
1846 | x :: rest
-> x :: (cut_unsafe ~replacement
env rest
)
1848 and get_using_vars
e =
1850 | Expr_list using_clauses
->
1851 List.concat_map using_clauses get_using_vars
1852 (* Simple assignment to local of form `$lvar = e` *)
1853 | Binop
(Ast.Eq None
, (_, Lvar
lvar), _) ->
1855 (* Arbitrary expression. This will be assigned to a temporary *)
1859 and stmt
env (p, st_
as st
) =
1861 | Let
(x, h, e) -> let_stmt
env x h e
1862 | Block
_ -> assert false
1863 | Unsafe
-> assert false
1864 | Fallthrough
-> N.Fallthrough
1866 | Markup
(_, None
) -> N.Noop
(* ignore markup *)
1867 | Markup
(_, Some
e) -> N.Expr
(expr
env e)
1868 | Break level_opt
->
1869 check_break_continue_level p level_opt
;
1871 | Continue level_opt
->
1872 check_break_continue_level p level_opt
;
1874 | Throw
e -> let terminal = not
(fst
env).in_try
in
1875 N.Throw
(terminal, expr
env e)
1876 | Return
e -> N.Return
(p, oexpr
env e)
1877 | GotoLabel label
-> name_goto_label
env label
1878 | Goto label
-> name_goto
env label
1879 | Static_var el
-> N.Static_var
(static_varl
env el
)
1880 | Global_var el
-> N.Global_var
(global_varl
env el
)
1881 | If
(e, b1
, b2
) -> if_stmt
env st
e b1 b2
1882 | Do
(b
, e) -> do_stmt
env b
e
1883 | While
(e, b
) -> while_stmt
env e b
1884 | Declare
(is_block
, e, b
) -> declare_stmt
env is_block
e b
1885 | Using s
-> using_stmt
env s
.us_has_await s
.us_expr s
.us_block
1886 | For
(st1
, e, st2
, b
) -> for_stmt
env st1
e st2 b
1887 | Switch
(e, cl
) -> switch_stmt
env st
e cl
1888 | Foreach
(e, aw
, ae
, b
)-> foreach_stmt
env e aw ae b
1889 | Try
(b
, cl
, fb
) -> try_stmt
env st b cl fb
1891 Errors.experimental_feature
p "inlined definitions"; N.Expr
(p, N.Any
)
1892 | Expr
(cp
, Call
((p, Id
(fp
, fn
)), hl
, el
, uel
))
1893 when fn
= SN.SpecialFunctions.invariant
->
1894 (* invariant is subject to a source-code transform in the HHVM
1895 * runtime: the arguments to invariant are lazily evaluated only in
1896 * the case in which the invariant condition does not hold. So:
1898 * invariant_violation(<condition>, <format>, <format_args...>)
1900 * ... is rewritten as:
1902 * if (!<condition>) {
1903 * invariant_violation(<format>, <format_args...>);
1908 Errors.naming_too_few_arguments
p;
1910 | (cond_p
, cond
) :: el
->
1911 let violation = (cp
, Call
1912 ((p, Id
(fp
, "\\"^
SN.SpecialFunctions.invariant_violation
)), hl
, el
,
1914 if cond
<> False
then
1915 let b1, b2
= [p, Expr
violation], [p, Noop
] in
1916 let cond = cond_p
, Unop
(Unot
, (cond_p
, cond)) in
1917 if_stmt
env st
cond b1 b2
1918 else (* a false <condition> means unconditional invariant_violation *)
1919 N.Expr
(expr
env violation)
1921 | Expr
e -> N.Expr
(expr
env e)
1923 and let_stmt
env x h e =
1924 let e = expr
env e in
1925 let h = Option.map
h (hint env) in
1926 let x = Env.new_let_local env x in
1929 and if_stmt
env st
e b1 b2
=
1930 let e = expr
env e in
1931 let nsenv = (fst
env).namespace
in
1932 let _, vars
= GetLocals.stmt
(fst
env).tcopt (nsenv, SMap.empty
) st
in
1933 SMap.iter
(fun x p -> Env.new_pending_lvar env (p, x)) vars
;
1934 let result = Env.scope env (
1936 let all1, b1 = branch
env b1 in
1937 let all2, b2
= branch
env b2
in
1938 Env.extend_all_locals env all2;
1939 Env.extend_all_locals env all1;
1942 SMap.iter
(fun x _ -> Env.promote_pending_lvar env x) vars
;
1945 and do_stmt
env b
e =
1946 (* lexical block of `do` is extended to the expr of loop termination *)
1947 Env.scope_lexical env (fun env ->
1948 let b = block ~new_scope
:false env b in
1949 let e = expr
env e in
1953 and while_stmt
env e b =
1954 let e = expr
env e in
1955 N.While
(e, block
env b)
1957 and declare_stmt _env _is_block
e _b
=
1958 N.Expr
(fst
e, N.Any
)
1960 (* Scoping is essentially that of do: block is always executed *)
1961 and using_stmt
env has_await
e b =
1962 let vars = get_using_vars
e in
1963 let e = expr
env e in
1964 let b = block ~new_scope
:false env b in
1965 Env.remove_locals env vars;
1966 N.Using
(has_await
, e, b)
1968 and for_stmt
env e1
e2 e3
b =
1969 (* The initialization and condition expression should be in the outer scope,
1970 * as they are always executed. *)
1971 let e1 = expr
env e1 in
1972 let e2 = expr
env e2 in
1975 (* The third expression (iteration step) should have the same scope as the
1976 * block, as it is not always executed. *)
1977 let b = block ~new_scope
:false env b in
1978 let e3 = expr
env e3 in
1979 N.For
(e1, e2, e3, b)
1982 and switch_stmt
env st
e cl
=
1983 let e = expr
env e in
1984 let nsenv = (fst
env).namespace
in
1985 let _, vars = GetLocals.stmt
(fst
env).tcopt (nsenv, SMap.empty
) st
in
1986 SMap.iter
(fun x p -> Env.new_pending_lvar env (p, x)) vars;
1987 let result = Env.scope env begin fun env ->
1988 let all_locals_l, cl
= casel
env cl
in
1989 List.iter
all_locals_l (Env.extend_all_locals env);
1992 SMap.iter
(fun x _ -> Env.promote_pending_lvar env x) vars;
1995 and foreach_stmt
env e aw ae
b =
1996 let e = expr
env e in
1997 Env.scope env begin fun env ->
1998 let ae = as_expr
env aw
ae in
1999 let b = block
env b in
2000 N.Foreach
(e, ae, b)
2003 and as_expr
env aw
=
2004 let handle_v ev
= match ev
with
2005 | p, Id
x when hacksperimental env ->
2006 let x = Env.new_let_local env x in
2007 let ev = (p, N.ImmutableVar
x) in
2010 Errors.expected_variable
p;
2011 p, N.Lvar
(Env.new_lvar env (p, "__internal_placeholder"))
2013 let nsenv = (fst
env).namespace
in
2015 GetLocals.lvalue
(fst
env).tcopt (nsenv, SMap.empty
) ev in
2016 SMap.iter
(fun x p -> ignore
(Env.new_lvar env (p, x))) vars;
2017 let ev = expr
env ev in
2020 let handle_k ek
= match ek
with
2022 p, N.Lvar
(Env.new_lvar env x)
2023 | p, Id
x when hacksperimental env ->
2024 p, N.ImmutableVar
(Env.new_let_local env x)
2026 Errors.expected_variable
p;
2027 p, N.Lvar
(Env.new_lvar env (p, "__internal_placeholder"))
2031 let ev = handle_v ev in
2034 | Some
p -> N.Await_as_v
(p, ev)
2037 let k = handle_k k in
2038 let ev = handle_v ev in
2040 | None
-> N.As_kv
(k, ev)
2041 | Some
p -> N.Await_as_kv
(p, k, ev)
2045 and try_stmt
env st
b cl fb
=
2046 let nsenv = (fst
env).namespace
in
2048 GetLocals.stmt
(fst
env).tcopt (nsenv, SMap.empty
) st
in
2049 SMap.iter
(fun x p -> Env.new_pending_lvar env (p, x)) vars;
2050 let result = Env.scope env (
2052 let genv, lenv = env in
2053 (* isolate finally from the rest of the try-catch: if the first
2054 * statement of the try is an uncaught exception, finally will
2055 * still be executed *)
2056 let _all_finally, fb
= branch
({genv with in_finally
= true}, lenv) fb
in
2057 let all_locals_b, b = branch
({genv with in_try
= true}, lenv) b in
2058 let all_locals_cl, cl
= catchl
env cl
in
2059 List.iter
all_locals_cl (Env.extend_all_locals env);
2060 Env.extend_all_locals env all_locals_b;
2063 SMap.iter
(fun x _ -> Env.promote_pending_lvar env x) vars;
2066 and stmt_with_block
env = fun astmt
-> match astmt
with
2067 | (_, Block
b) -> always_block
env b
2068 | astmt
-> [stmt
env astmt
]
2070 and always_block
env stl
=
2071 (* Add lexical scope for block scoped let variables *)
2072 Env.scope_lexical env (fun env -> List.concat_map stl
(stmt_with_block
env))
2074 and block ?
(new_scope
=true) env stl
=
2075 let stl = cut_unsafe
env stl in
2079 fun env -> List.concat_map
stl (stmt_with_block
env)
2081 else List.concat_map
stl (stmt_with_block
env)
2083 and branch
env stmt_l
=
2084 let stmt_l = cut_unsafe
env stmt_l in
2085 Env.scope_all env begin fun env ->
2086 List.concat_map
stmt_l (stmt_with_block
env)
2090 * Names a goto label.
2092 * The goto label is added to the local labels if it is not already there.
2093 * Otherwise, an error is produced.
2095 * An error is produced if this is called within a finally block.
2098 ({ in_finally
; _ }, _ as env) (label_pos
, label_name
as label
) =
2099 (match Env.goto_label env label_name
with
2100 | Some original_declaration_pos
->
2101 Errors.goto_label_already_defined
2104 original_declaration_pos
2105 | None
-> Env.new_goto_label env label
);
2107 Errors.goto_label_defined_in_finally label_pos
;
2111 * Names a goto target.
2113 * The goto statement's target label is added to the local goto targets.
2115 * An error is produced if this is called within a finally block.
2118 ({ in_finally
; _ }, _ as env) (label_pos
, _ as label
) =
2119 Env.new_goto_target env label
;
2120 if in_finally
then Errors.goto_invoked_in_finally label_pos
;
2123 and static_varl
env l
= List.map l
(static_var
env)
2124 and static_var
env = function
2125 | p, Lvar
_ as lv
-> expr
env (p, Binop
(Eq None
, lv
, (p, Null
)))
2128 and global_varl
env l
= List.map l
(global_var
env)
2129 and global_var
env = function
2130 | p, Lvar
_ as lv
-> expr
env (p, Binop
(Eq None
, lv
, (p, Null
)))
2133 and expr_obj_get_name
env = function
2134 | p, Id
x -> p, N.Id
x
2138 and exprl
env l
= List.map l
(expr
env)
2139 and oexpr
env e = Option.map
e (expr
env)
2140 and expr
env (p, e) = p, expr_
env p e
2141 and expr_
env p = function
2143 let tcopt = (fst
env).tcopt in
2144 let array_literals_disallowed =
2145 TypecheckerOptions.disallow_array_literal
tcopt in
2146 if array_literals_disallowed
2147 then Errors.array_literals_disallowed p;
2148 N.Array
(List.map l
(afield
env))
2149 | ParenthesizedExpr
(p, e) -> expr_
env p e
2151 N.Darray
(List.map l
(fun (e1, e2) -> expr
env e1, expr
env e2))
2152 | Varray l
-> N.Varray
(List.map l
(expr
env))
2153 | Collection
(id, l
) -> begin
2154 let p, cn
= NS.elaborate_id
((fst
env).namespace
) NS.ElaborateClass
id in
2156 | x when N.is_vc_kind
x ->
2157 N.ValCollection
((N.get_vc_kind cn
),
2158 (List.map l
(afield_value
env cn
)))
2159 | x when N.is_kvc_kind
x ->
2160 N.KeyValCollection
((N.get_kvc_kind cn
),
2161 (List.map l
(afield_kvalue
env cn
)))
2162 | x when x = SN.Collections.cPair
->
2165 Errors.naming_too_few_arguments
p;
2168 let pn = SN.Collections.cPair
in
2169 N.Pair
(afield_value
env pn e1, afield_value
env pn e2)
2171 Errors.naming_too_many_arguments
p;
2175 Errors.expected_collection
p cn
;
2178 | Clone
e -> N.Clone
(expr
env e)
2183 | Float s
-> N.Float s
2184 | String s
-> N.String s
2186 (* treat execution operator similar to interpolated strings *)
2187 | Execution_operator idl
-> N.String2
(string2
env idl
)
2189 (** TODO: Emit proper error messages T28473207. Currently the error message
2190 * emitted has reason Naming[2049] unbound name for global constant *)
2191 begin match Env.let_local env x with
2192 | Some
x -> N.ImmutableVar
x
2193 | None
-> N.Id
(Env.global_const env x)
2195 | Id_type_arguments
(_x
, _hl
) ->
2196 (* Shouldn't happen: parser only allows this in New *)
2197 failwith
"Unexpected Id with type arguments"
2198 | Lvar
(_, x) when x = SN.SpecialIdents.this
-> N.This
2199 | Lvar
(_, x) when x = SN.SpecialIdents.dollardollar
->
2200 N.Dollardollar
(Env.found_dollardollar env p)
2201 | Lvar
(pos, x) when x = SN.SpecialIdents.placeholder
->
2204 N.Lvar
(Env.lvar env x)
2205 | Obj_get
(e1, e2, nullsafe
) ->
2206 (* If we encounter Obj_get(_,_,true) by itself, then it means "?->"
2207 is being used for instance property access; see the case below for
2208 handling nullsafe instance method calls to see how this works *)
2209 let nullsafe = match nullsafe with
2210 | OG_nullsafe
-> N.OG_nullsafe
2211 | OG_nullthrows
-> N.OG_nullthrows
2213 N.Obj_get
(expr
env e1, expr_obj_get_name
env e2, nullsafe)
2214 | Array_get
((p, Lvar
x), None
) ->
2215 let id = p, N.Lvar
(Env.lvar env x) in
2216 N.Array_get
(id, None
)
2217 | Array_get
(e1, e2) -> N.Array_get
(expr
env e1, oexpr
env e2)
2218 | Class_get
((_, (Id x1
| Lvar x1
)), (_, (Id x2
| Lvar x2
))) ->
2219 N.Class_get
(make_class_id
env x1
[], x2
)
2220 | Class_get
(x1
, x2
) ->
2221 ensure_name_not_dynamic env x1
2222 Errors.dynamic_class_name_in_strict_mode
;
2223 ensure_name_not_dynamic env x2
2224 Errors.dynamic_class_property_name_in_strict_mode
;
2226 | Class_const
((_, Id x1
), x2
)
2227 | Class_const
((_, Lvar x1
), x2
) ->
2228 let (genv, _) = env in
2229 let (_, name) = NS.elaborate_id
genv.namespace
NS.ElaborateClass x1
in
2230 begin match Naming_heap.TypeIdHeap.get
name with
2231 | Some
(_, `Typedef
) when (snd x2
) = "class" ->
2232 N.Typename
(Env.type_name env x1 ~allow_typedef
:true)
2234 N.Class_const
(make_class_id
env x1
[], x2
)
2237 (* TODO: report error in strict mode *)
2239 | Call
((_, Id
(p, pseudo_func
)), hl
, el
, uel
)
2240 when pseudo_func
= SN.SpecialFunctions.echo
->
2241 arg_unpack_unexpected uel
;
2242 N.Call
(N.Cnormal
, (p, N.Id
(p, pseudo_func
)), hintl_funcall
env hl
, exprl
env el
, [])
2243 | Call
((p, Id
(_, cn
)), hl
, el
, uel
)
2244 when cn
= SN.SpecialFunctions.call_user_func
->
2245 arg_unpack_unexpected uel
;
2247 | [] -> Errors.naming_too_few_arguments
p; N.Any
2248 | f :: el
-> N.Call
(N.Cuser_func
, expr
env f, hintl_funcall
env hl
, exprl
env el
, [])
2250 | Call
((p, Id
(_, cn
)), _, el
, uel
) when cn
= SN.SpecialFunctions.fun_
->
2251 arg_unpack_unexpected uel
;
2253 | [] -> Errors.naming_too_few_arguments
p; N.Any
2254 | [_, String s
] when String.contains s '
:'
->
2255 Errors.illegal_meth_fun
p; N.Any
2256 | [p, String
x] -> N.Fun_id
(Env.fun_id env (p, x))
2258 Errors.illegal_fun
p;
2260 | _ -> Errors.naming_too_many_arguments
p; N.Any
2262 | Call
((p, Id
(_, cn
)), _, el
, uel
)
2263 when cn
= SN.SpecialFunctions.inst_meth
->
2264 arg_unpack_unexpected uel
;
2266 | [] -> Errors.naming_too_few_arguments
p; N.Any
2267 | [_] -> Errors.naming_too_few_arguments
p; N.Any
2268 | instance
::(p, String meth
)::[] ->
2269 N.Method_id
(expr
env instance
, (p, meth
))
2270 | (p, _)::(_)::[] ->
2271 Errors.illegal_inst_meth
p;
2273 | _ -> Errors.naming_too_many_arguments
p; N.Any
2275 | Call
((p, Id
(_, cn
)), _, el
, uel
)
2276 when cn
= SN.SpecialFunctions.meth_caller
->
2277 arg_unpack_unexpected uel
;
2279 | [] -> Errors.naming_too_few_arguments
p; N.Any
2280 | [_] -> Errors.naming_too_few_arguments
p; N.Any
2282 (match (expr
env e1), (expr
env e2) with
2283 | (pc
, N.String cl
), (pm
, N.String meth
) ->
2284 N.Method_caller
(Env.type_name env (pc
, cl
) ~allow_typedef
:false, (pm
, meth
))
2285 | (_, N.Class_const
(((), N.CI
(cl
, _)), (_, mem))), (pm
, N.String meth
)
2286 when mem = SN.Members.mClass
->
2287 N.Method_caller
(Env.type_name env cl ~allow_typedef
:false, (pm
, meth
))
2289 Errors.illegal_meth_caller
p;
2292 | _ -> Errors.naming_too_many_arguments
p; N.Any
2294 | Call
((p, Id
(_, cn
)), _, el
, uel
)
2295 when cn
= SN.SpecialFunctions.class_meth
->
2296 arg_unpack_unexpected uel
;
2298 | [] -> Errors.naming_too_few_arguments
p; N.Any
2299 | [_] -> Errors.naming_too_few_arguments
p; N.Any
2301 (match (expr
env e1), (expr
env e2) with
2302 | (pc
, N.String cl
), (pm
, N.String meth
) ->
2303 N.Smethod_id
(Env.type_name env (pc
, cl
) ~allow_typedef
:false, (pm
, meth
))
2304 | (_, N.Id
(_, const)), (pm
, N.String meth
)
2305 when const = SN.PseudoConsts.g__CLASS__
->
2306 (* All of these that use current_cls aren't quite correct
2307 * inside a trait, as the class should be the using class.
2308 * It's sufficient for typechecking purposes (we require
2309 * subclass to be compatible with the trait member/method
2312 (match (fst
env).current_cls
with
2313 | Some
(cid
, _) -> N.Smethod_id
(cid
, (pm
, meth
))
2314 | None
-> Errors.illegal_class_meth
p; N.Any
)
2315 | (_, N.Class_const
(((), N.CI
(cl
, _)), (_, mem))), (pm
, N.String meth
)
2316 when mem = SN.Members.mClass
->
2317 N.Smethod_id
(Env.type_name env cl ~allow_typedef
:false, (pm
, meth
))
2318 | (p, N.Class_const
(((), (N.CIself
|N.CIstatic
)), (_, mem))),
2319 (pm
, N.String meth
) when mem = SN.Members.mClass
->
2320 (match (fst
env).current_cls
with
2321 | Some
(cid
, _) -> N.Smethod_id
(cid
, (pm
, meth
))
2322 | None
-> Errors.illegal_class_meth
p; N.Any
)
2323 | (p, _), (_) -> Errors.illegal_class_meth
p; N.Any
2325 | _ -> Errors.naming_too_many_arguments
p; N.Any
2327 | Call
((p, Id
(_, cn
)), _, el
, uel
) when cn
= SN.SpecialFunctions.assert_
->
2328 arg_unpack_unexpected uel
;
2329 if List.length el
<> 1
2330 then Errors.assert_arity
p;
2331 N.Assert
(N.AE_assert
(
2332 Option.value_map
(List.hd el
) ~default
:(p, N.Any
) ~
f:(expr
env)
2334 | Call
((p, Id
(_, cn
)), _, el
, uel
) when cn
= SN.SpecialFunctions.tuple
->
2335 arg_unpack_unexpected uel
;
2337 | [] -> Errors.naming_too_few_arguments
p; N.Any
2338 | el
-> N.List
(exprl
env el
)
2340 | Call
((p, Id
f), hl
, el
, uel
) ->
2341 begin match Env.let_local env f with
2343 (* Translate into local id *)
2344 let f = (p, N.ImmutableVar
x) in
2345 N.Call
(N.Cnormal
, f, hintl_funcall
env hl
, exprl
env el
, exprl
env uel
)
2347 (* The name is not a local `let` binding *)
2348 let qualified = Env.fun_id env f in
2349 let cn = snd
qualified in
2350 (* The above special cases (fun, inst_meth, meth_caller, class_meth,
2351 * and friends) are magical language constructs, which we should
2352 * check before calling fun_id and looking up the function and doing
2353 * namespace normalization. However, gena, genva, etc are actual
2354 * functions that actually exist, we just need to handle them
2355 * specially here, during naming. Note that most of the function
2356 * special cases, such as idx, are actually handled in typing, and
2357 * don't require naming magic. *)
2358 if cn = SN.FB.fgena
then begin
2359 arg_unpack_unexpected uel
;
2361 | [e] -> N.Special_func
(N.Gena
(expr
env e))
2362 | _ -> Errors.gena_arity
p; N.Any
2364 end else if (cn = SN.FB.fgenva
)
2365 || (cn = SN.HH.asio_va
)
2366 || (cn = SN.HH.lib_tuple_gen
)
2367 || (cn = SN.HH.lib_tuple_from_async
) then begin
2368 arg_unpack_unexpected uel
;
2369 if List.length el
< 1
2370 then (Errors.genva_arity
p; N.Any
)
2371 else N.Special_func
(N.Genva
(exprl
env el
))
2372 end else if cn = SN.FB.fgen_array_rec
then begin
2373 arg_unpack_unexpected uel
;
2375 | [e] -> N.Special_func
(N.Gen_array_rec
(expr
env e))
2376 | _ -> Errors.gen_array_rec_arity
p; N.Any
2379 N.Call
(N.Cnormal
, (p, N.Id
qualified), hintl_funcall
env hl
,
2380 exprl
env el
, exprl
env uel
)
2382 (* Handle nullsafe instance method calls here. Because Obj_get is used
2383 for both instance property access and instance method calls, we need
2384 to match the entire "Call(Obj_get(..), ..)" pattern here so that we
2385 only match instance method calls *)
2386 | Call
((p, Obj_get
(e1, e2, OG_nullsafe
)), hl
, el
, uel
) ->
2389 (p, N.Obj_get
(expr
env e1,
2390 expr_obj_get_name
env e2, N.OG_nullsafe
)),
2391 hintl_funcall
env hl
,
2392 exprl
env el
, exprl
env uel
)
2393 (* Handle all kinds of calls that weren't handled by any of
2395 | Call
(e, hl
, el
, uel
) ->
2396 N.Call
(N.Cnormal
, expr
env e,
2397 hintl_funcall
env hl
, exprl
env el
, exprl
env uel
)
2398 | Yield_break
-> N.Yield_break
2399 | Yield
e -> N.Yield
(afield
env e)
2400 | Await
e -> N.Await
(expr
env e)
2401 | Suspend
e -> N.Suspend
(expr
env e)
2402 | List el
-> N.List
(exprl
env el
)
2403 | Expr_list el
-> N.Expr_list
(exprl
env el
)
2405 let (p, x), hl
= match ty with
2406 | _, Happly
(id, hl
) -> (id, hl
)
2407 | _ -> assert false in
2408 let ty = match try_castable_hint ~tp_depth
:1 env p x hl
with
2412 | x when x = SN.Typehints.object_cast
->
2413 (* (object) is a valid cast but not a valid type annotation *)
2414 (* FIXME we are not modeling the correct runtime behavior here --
2415 * the runtime result type is an stdClass if the original type is
2416 * primitive. But we should probably just disallow object casts
2419 | x when x = SN.Typehints.void
->
2422 | x when x = SN.Typehints.unset_cast
->
2423 Errors.unset_cast
p;
2426 (* Let's just assume that any other invalid cases are attempts to
2427 * cast to specific objects *)
2428 let h = hint ~allow_typedef
:false env ty in
2429 Errors.object_cast
p x;
2432 N.Cast
(ty, expr
env e2)
2433 | Unop
(uop
, e) -> N.Unop
(uop
, expr
env e)
2434 | Binop
(Eq None
as op
, lv
, e2) ->
2435 if Env.inside_pipe env then
2436 Errors.unimplemented_feature
p "Assignment within pipe expressions";
2437 let e2 = expr
env e2 in
2438 let nsenv = (fst
env).namespace
in
2440 GetLocals.lvalue
(fst
env).tcopt (nsenv, SMap.empty
) lv
in
2441 SMap.iter
(fun x p -> ignore
(Env.new_lvar env (p, x))) vars;
2442 N.Binop
(op
, expr
env lv
, e2)
2443 | Binop
(Eq
_ as bop
, e1, e2) ->
2444 if Env.inside_pipe env then
2445 Errors.unimplemented_feature
p "Assignment within pipe expressions";
2446 let e1 = expr
env e1 in
2447 N.Binop
(bop
, e1, expr
env e2)
2448 | Binop
(bop
, e1, e2) ->
2449 let e1 = expr
env e1 in
2450 N.Binop
(bop
, e1, expr
env e2)
2452 let e1 = expr
env e1 in
2453 let ident, e2 = Env.pipe_scope env
2458 N.Pipe
((p, ident), e1, e2)
2459 | Eif
(e1, e2opt
, e3) ->
2460 (* The order matters here, of course -- e1 can define vars that need to
2461 * be available in e2 and e3. *)
2462 let e1 = expr
env e1 in
2463 let nsenv = (fst
env).namespace
in
2464 let get_lvalues = function e ->
2465 snd
@@ GetLocals.stmt
(fst
env).tcopt (nsenv, SMap.empty
) (p, Expr
e) in
2467 Option.value (Option.map e2opt
get_lvalues) ~default
:SMap.empty
2469 let e3_lvalues = get_lvalues e3 in
2470 let lvalues = smap_inter
e2_lvalues e3_lvalues in
2471 SMap.iter
(fun x p -> Env.new_pending_lvar env (p, x)) lvalues;
2472 let e2opt, e3, dollar_dollar
= Env.scope env (fun env ->
2473 let all2, e2opt = Env.scope_all env (fun env -> oexpr
env e2opt) in
2474 let dollardollar_e2 = Env.has_dollardollar all2 in
2475 let all3, e3 = Env.scope_all env (fun env -> expr
env e3) in
2476 let dollardollar_e3 = Env.has_dollardollar all3 in
2477 Env.extend_all_locals env all2;
2478 Env.extend_all_locals env all3;
2479 e2opt, e3, Option.first_some
dollardollar_e2 dollardollar_e3
2481 SMap.iter
(fun x _ -> Env.promote_pending_lvar env x) lvalues;
2482 if Env.inside_pipe env then
2483 ignore
(Option.map dollar_dollar
(Env.found_dollardollar env));
2484 N.Eif
(e1, e2opt, e3)
2485 | InstanceOf
(e, (p, Id
x)) ->
2486 let id = match x with
2487 | px
, n
when n
= SN.Classes.cParent
->
2488 if (fst
env).current_cls
= None
then
2489 let () = Errors.parent_outside_class
p in
2490 N.CI
((px
, SN.Classes.cUnknown
), [])
2492 | px
, n
when n
= SN.Classes.cSelf
->
2493 if (fst
env).current_cls
= None
then
2494 let () = Errors.self_outside_class
p in
2495 N.CI
((px
, SN.Classes.cUnknown
), [])
2497 | px
, n
when n
= SN.Classes.cStatic
->
2498 if (fst
env).current_cls
= None
then
2499 let () = Errors.static_outside_class
p in
2500 N.CI
((px
, SN.Classes.cUnknown
), [])
2503 N.CI
(Env.type_name env x ~allow_typedef
:false, [])
2505 N.InstanceOf
(expr
env e, ((), id))
2506 | InstanceOf
(e1, (_,
2507 (Lvar
_ | Obj_get
_ | Class_get
_ | Class_const
_
2508 | Array_get
_ | Call
_) as e2)) ->
2509 N.InstanceOf
(expr
env e1, ((), N.CIexpr
(expr
env e2)))
2510 | InstanceOf
(_e1
, (p, _)) ->
2511 Errors.invalid_instanceof
p;
2514 let e1 = expr
env e in
2515 let h1 = hint ~allow_wildcard
:true env h in
2518 let e1 = expr
env e in
2519 let h1 = hint ~allow_wildcard
:true env h in
2521 | New
((_, Id_type_arguments
(x, hl
)), el
, uel
) ->
2522 N.New
(make_class_id
env x hl
,
2525 | New
((_, Id
x), el
, uel
)
2526 | New
((_, Lvar
x), el
, uel
) ->
2527 N.New
(make_class_id
env x [],
2530 | New
((p, _e
), el
, uel
) ->
2531 if (fst
env).in_mode
= FileInfo.Mstrict
2532 then Errors.dynamic_new_in_strict_mode
p;
2533 N.New
(make_class_id
env (p, SN.Classes.cUnknown
) [],
2539 let _ = match (fst
env).in_mode
with
2540 | FileInfo.Mstrict
-> List.iter idl ~
f:(function
2541 | (p, _), is_ref
when is_ref
-> Errors.reference_in_strict_mode
p;
2547 ~
f:(fun ((p, x) as id, _) acc ->
2548 if x = SN.SpecialIdents.this
2549 then (Errors.this_as_lexical_variable
p; acc)
2552 let idl'
= List.map
idl (Env.lvar env) in
2553 let env = (fst
env, Env.empty_local UBMErr
) in
2554 List.iter2_exn
idl idl'
(Env.add_lvar env);
2555 let f = expr_lambda
env f in
2558 (* We have to build the capture list while we're finding names in
2559 the closure body---accumulate it in to_capture. *)
2560 (* semantic duplication: The logic here is also used in `uselist_lambda`.
2561 The differences are enough that it does not make sense to refactor
2562 this out for now. *)
2563 let to_capture = ref [] in
2564 let handle_unbound (p, x) =
2565 let cap = Env.lvar env (p, x) in
2566 to_capture := cap :: !to_capture;
2569 let lenv = Env.empty_local @@ UBMFunc
handle_unbound in
2570 (* Extend the current let binding into the scope of lambda *)
2571 Env.copy_let_locals env (fst
env, lenv);
2572 let env = (fst
env, lenv) in
2573 let f = expr_lambda
env f in
2574 (* TODO T28711692: Compute the correct capture list for let variables,
2575 * it does not seem to affect typechecking... *)
2576 N.Efun
(f, !to_capture)
2577 | Xml
(x, al
, el
) ->
2578 N.Xml
(Env.type_name env x ~allow_typedef
:false, attrl
env al
,
2581 N.Shape
begin List.fold_left fdl ~init
:ShapeMap.empty
2582 ~
f:begin fun fdm
(pname
, value) ->
2583 let pos, name = convert_shape_name env pname
in
2584 if ShapeMap.mem name fdm
2585 then Errors.fd_name_already_bound
pos;
2586 ShapeMap.add
name (expr
env value) fdm
2594 Errors.variable_variables_disallowed
p;
2597 N.Yield_from
(expr
env e)
2602 | Callconv
(kind
, e) ->
2603 N.Callconv
(kind
, expr
env e)
2605 and expr_lambda
env f =
2606 let h = Option.map
f.f_ret
(hint ~allow_retonly
:true env) in
2607 let previous_unsafe = Env.has_unsafe env in
2608 (* save unsafe and yield state *)
2609 Env.set_unsafe env false;
2610 let variadicity, paraml
= fun_paraml
env f.f_params
in
2611 let f_kind = f.f_fun_kind
in
2612 (* The bodies of lambdas go through naming in the containing local
2614 let body_nast = block
env f.f_body
in
2615 let unsafe = func_body_had_unsafe
env in
2616 (* restore unsafe state *)
2617 Env.set_unsafe env previous_unsafe;
2618 let body = N.NamedBody
{
2619 N.fnb_unsafe
= unsafe;
2620 fnb_nast
= body_nast;
2622 N.f_annotation
= ();
2623 f_mode
= (fst
env).in_mode
;
2628 f_where_constraints
= [];
2630 f_fun_kind
= f_kind;
2631 f_variadic
= variadicity;
2632 f_user_attributes
= user_attributes
env f.f_user_attributes
;
2633 f_ret_by_ref
= f.f_ret_by_ref
;
2636 and make_class_id
env (p, x as cid
) hl
=
2639 | x when x = SN.Classes.cParent
->
2640 if (fst
env).current_cls
= None
then
2641 let () = Errors.parent_outside_class
p in
2642 N.CI
((p, SN.Classes.cUnknown
), [])
2644 | x when x = SN.Classes.cSelf
->
2645 if (fst
env).current_cls
= None
then
2646 let () = Errors.self_outside_class
p in
2647 N.CI
((p, SN.Classes.cUnknown
), [])
2649 | x when x = SN.Classes.cStatic
-> if (fst
env).current_cls
= None
then
2650 let () = Errors.static_outside_class
p in
2651 N.CI
((p, SN.Classes.cUnknown
), [])
2653 | x when x = SN.SpecialIdents.this
-> N.CIexpr
(p, N.This
)
2654 | x when x = SN.SpecialIdents.dollardollar
->
2655 (* We won't reach here for "new $$" because the parser creates a
2656 * proper Ast.Dollardollar node, so make_class_id won't be called with
2657 * that node. In fact, the parser creates an Ast.Dollardollar for all
2658 * "$$" except in positions where a classname is expected, like in
2659 * static member access. So, we only reach here for things
2660 * like "$$::someMethod()". *)
2661 N.CIexpr
(p, N.Lvar
(Env.found_dollardollar env p))
2662 | x when x.[0] = '$'
-> N.CIexpr
(p, N.Lvar
(Env.lvar env cid
))
2663 | _ -> N.CI
(Env.type_name env cid ~allow_typedef
:false,
2664 hintl ~allow_wildcard
:true ~forbid_this
:false
2665 ~allow_typedef
:true ~allow_retonly
:true ~tp_depth
:1 env hl
2669 List.map_env
[] l
(case
env)
2671 and case
env acc = function
2673 let b = cut_unsafe ~replacement
:Fallthrough
env b in
2674 let all_locals, b = branch
env b in
2675 all_locals :: acc, N.Default
b
2677 let e = expr
env e in
2678 let b = cut_unsafe ~replacement
:Fallthrough
env b in
2679 let all_locals, b = branch
env b in
2680 all_locals :: acc, N.Case
(e, b)
2682 and catchl
env l
= List.map_env
[] l
(catch
env)
2683 and catch
env acc (x1
, x2
, b) =
2686 (* If the variable does not begin with $, it is an immutable binding *)
2687 let x2 = if (snd
x2).[0] = '$'
(* This is always true if hacksperimental is off *)
2688 then Env.new_lvar env x2
2689 else Env.new_let_local env x2
2691 let all_locals, b = branch
env b in
2692 all_locals :: acc, (Env.type_name env x1 ~allow_typedef
:true, x2, b)
2695 and afield
env = function
2696 | AFvalue
e -> N.AFvalue
(expr
env e)
2697 | AFkvalue
(e1, e2) -> N.AFkvalue
(expr
env e1, expr
env e2)
2699 and afield_value
env cname
= function
2700 | AFvalue
e -> expr
env e
2701 | AFkvalue
(e1, _e2
) ->
2702 Errors.unexpected_arrow
(fst
e1) cname
;
2705 and afield_kvalue
env cname
= function
2707 Errors.missing_arrow
(fst
e) cname
;
2708 expr
env e, expr
env (fst
e, Lvar
(fst
e, "__internal_placeholder"))
2709 | AFkvalue
(e1, e2) -> expr
env e1, expr
env e2
2711 and attrl
env l
= List.map l
(attr env)
2712 and attr env = function
2713 | Xhp_simple
(x, e) -> N.Xhp_simple
(x, expr
env e)
2714 | Xhp_spread
e -> N.Xhp_spread
(expr
env e)
2716 and string2
env idl =
2717 List.map
idl (expr
env)
2719 (**************************************************************************)
2720 (* Function/Method Body Naming: *)
2721 (* Ensure that, given a function / class, any UnnamedBody within is
2722 * transformed into a a named body *)
2723 (**************************************************************************)
2725 let func_body nenv
f =
2726 match f.N.f_body
with
2727 | N.NamedBody
b -> b
2728 | N.UnnamedBody
{ N.fub_ast
; N.fub_tparams
; N.fub_namespace
; _ } ->
2729 let genv = Env.make_fun_genv nenv
2730 SMap.empty
f.N.f_mode
(snd
f.N.f_name
) fub_namespace
in
2731 let genv = extend_params
genv fub_tparams
in
2732 let lenv = Env.empty_local UBMErr
in
2733 let env = genv, lenv in
2735 List.fold_left ~
f:Env.add_param f.N.f_params ~init
:env in
2736 let env = match f.N.f_variadic
with
2737 | N.FVellipsis
_ | N.FVnonVariadic
-> env
2738 | N.FVvariadicArg param
-> Env.add_param env param
2740 let body = block
env fub_ast
in
2741 let unsafe = func_body_had_unsafe
env in
2742 Env.check_goto_references env;
2745 fnb_unsafe
= unsafe;
2748 let meth_body genv m
=
2749 let named_body = (match m
.N.m_body
with
2750 | N.NamedBody
_ as b -> b
2751 | N.UnnamedBody
{N.fub_ast
; N.fub_tparams
; N.fub_namespace
; _} ->
2752 let genv = {genv with namespace
= fub_namespace
} in
2753 let genv = extend_params
genv fub_tparams
in
2754 let env = genv, Env.empty_local UBMErr
in
2756 List.fold_left ~
f:Env.add_param m
.N.m_params ~init
:env in
2757 let env = match m
.N.m_variadic
with
2758 | N.FVellipsis
_ | N.FVnonVariadic
-> env
2759 | N.FVvariadicArg param
-> Env.add_param env param
2761 let body = block
env fub_ast
in
2762 let unsafe = func_body_had_unsafe
env in
2763 Env.check_goto_references env;
2766 fnb_unsafe
= unsafe;
2769 {m
with N.m_body
= named_body}
2771 let class_meth_bodies nenv nc
=
2772 let _n_tparams, cstrs
= nc
.N.c_tparams
in
2773 let genv = Env.make_class_genv nenv cstrs
2774 nc
.N.c_mode
(nc
.N.c_name
, nc
.N.c_kind
)
2775 Namespace_env.empty_with_default_popt
2777 let inst_meths = List.map nc
.N.c_methods
(meth_body genv) in
2778 let opt_constructor = match nc
.N.c_constructor
with
2780 | Some c
-> Some
(meth_body genv c
) in
2781 let static_meths = List.map nc
.N.c_static_methods
(meth_body genv) in
2783 N.c_methods
= inst_meths;
2784 N.c_static_methods
= static_meths ;
2785 N.c_constructor
= opt_constructor ;
2788 (**************************************************************************)
2790 (**************************************************************************)
2792 let typedef genv tdef
=
2793 let ty = match tdef
.t_kind
with Alias t
| NewType t
-> t
in
2794 let cstrs = make_constraints tdef
.t_tparams
in
2795 let env = Env.make_typedef_env genv cstrs tdef
in
2796 let tconstraint = Option.map tdef
.t_constraint
(hint env) in
2797 List.iter tdef
.t_tparams
check_constraint;
2798 let tparaml = type_paraml
env tdef
.t_tparams
in
2799 List.iter
tparaml begin fun (_, _, constrs
) ->
2800 List.iter constrs
(fun (_, (pos, _)) -> Errors.typedef_constraint
pos)
2802 let t_vis = match tdef
.t_kind
with
2803 | Ast.Alias
_ -> N.Transparent
2804 | Ast.NewType
_ -> N.Opaque
2806 let attrs = user_attributes
env tdef
.t_user_attributes
in
2808 N.t_annotation
= ();
2810 t_tparams
= tparaml;
2811 t_constraint
= tconstraint;
2812 t_kind
= hint env ty;
2813 t_user_attributes
= attrs;
2814 t_mode
= tdef
.t_mode
;
2818 (**************************************************************************)
2819 (* Global constants *)
2820 (**************************************************************************)
2822 let check_constant_hint cst
=
2823 match cst
.cst_type
with
2824 | None
when cst
.cst_mode
= FileInfo.Mstrict
->
2825 Errors.add_a_typehint
(fst cst
.cst_name
)
2829 let global_const genv cst
=
2830 let env = Env.make_const_env genv cst
in
2831 let hint = Option.map cst
.cst_type
(hint env) in
2832 let e = match cst
.cst_kind
with
2834 check_constant_hint cst
;
2835 Some
(constant_expr
env cst
.cst_value
)
2836 (* Define allows any expression, so don't call check_constant.
2837 * Furthermore it often appears at toplevel, which we don't track at
2838 * all, so don't type or even name that expression, it may refer to
2839 * "undefined" variables that actually exist, just untracked since
2840 * they're toplevel. *)
2841 | Ast.Cst_define
-> None
in
2842 { N.cst_annotation
= ();
2843 cst_mode
= cst
.cst_mode
;
2844 cst_name
= cst
.cst_name
;
2847 cst_is_define
= (cst
.cst_kind
= Ast.Cst_define
);
2850 (* Uses a default empty environment to extract the use list
2851 of a lambda expression. This exists only for the sake of
2852 the dehackificator and is not meant for general use. *)
2853 let uselist_lambda f =
2854 (* semantic duplication: This is copied from the implementation of the
2855 `Lfun` variant of `expr_` defined earlier in this file. *)
2856 let to_capture = ref [] in
2857 let handle_unbound (p, x) =
2858 to_capture := x :: !to_capture;
2861 let tcopt = TypecheckerOptions.make_permissive
TypecheckerOptions.default
in
2862 let genv = Env.make_fun_decl_genv tcopt SMap.empty
f in
2863 let lenv = Env.empty_local @@ UBMFunc
handle_unbound in
2864 let env = genv, lenv in
2865 ignore
(expr_lambda
env f);
2866 List.dedup
!to_capture
2868 (**************************************************************************)
2869 (* The entry point to CHECK the program, and transform the program *)
2870 (**************************************************************************)
2872 let program tcopt ast
=
2873 let rec program ast
=
2874 List.concat
@@ List.map ast
begin function
2875 | Ast.Fun
f -> [N.Fun
(fun_
tcopt f)]
2876 | Ast.Class c
-> [N.Class
(class_ tcopt c
)]
2877 | Ast.Typedef t
-> [N.Typedef
(typedef tcopt t
)]
2878 | Ast.Constant cst
-> [N.Constant
(global_const tcopt cst
)]
2880 | Ast.Namespace
(_ns
, ast
) -> program ast
2881 | Ast.NamespaceUse
_ -> []
2882 | Ast.SetNamespaceEnv
_ -> []
2887 let stmt _ acc _ = acc
2888 let lvalue _ acc _ = acc