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
23 module ShapeMap
= N.ShapeMap
24 module SN
= Naming_special_names
25 module NS
= Namespaces
27 module GEnv
= NamingGlobal.GEnv
29 (*****************************************************************************)
31 (*****************************************************************************)
33 (* We want to keep the positions of names that have been
34 * replaced by identifiers.
36 type positioned_ident
= (Pos.t
* Local_id.t
)
38 (* <T as A>, A is a type constraint *)
39 type type_constraint
= (Ast.constraint_kind
* Ast.hint
) list
43 (* strict? decl? partial? *)
44 in_mode
: FileInfo.mode
;
46 (* various options that control the strictness of the typechecker *)
47 tcopt
: TypecheckerOptions.t
;
49 (* are we in the body of a try statement? *)
52 (* are we in the body of a finally statement? *)
55 (* are we in a __PPL attributed class *)
58 (* In function foo<T1, ..., Tn> or class<T1, ..., Tn>, the field
59 * type_params knows T1 .. Tn. It is able to find out about the
60 * constraint on these parameters. *)
61 type_params
: type_constraint
SMap.t
;
63 (* The current class, None if we are in a function *)
64 current_cls
: (Ast.id
* Ast.class_kind
) option;
66 class_consts
: (string, Pos.t
) Caml.Hashtbl.t
;
68 class_props
: (string, Pos.t
) Caml.Hashtbl.t
;
70 (* Normally we don't need to add dependencies at this stage, but there
71 * are edge cases when we do. *)
72 droot
: Typing_deps.Dep.variant
;
74 (* Namespace environment, e.g., what namespace we're in and what use
75 * declarations are in play. *)
76 namespace
: Namespace_env.env
;
79 (* How to behave when we see an unbound name. Either we raise an
80 * error, or we call a function first and continue if it can resolve
81 * the name. This is used to nest environments when processing
85 | UBMFunc
of ((Pos.t
* string) -> positioned_ident
)
87 (* The primitives to manipulate the naming environment *)
93 val empty_local
: unbound_mode
-> lenv
95 TypecheckerOptions.t
->
96 type_constraint
SMap.t
->
98 Ast.id
* Ast.class_kind
-> Namespace_env.env
-> bool -> genv
100 TypecheckerOptions.t
->
101 type_constraint
SMap.t
-> Ast.class_
-> genv
* lenv
102 val make_typedef_env
:
103 TypecheckerOptions.t
->
104 type_constraint
SMap.t
-> Ast.typedef
-> genv
* lenv
106 TypecheckerOptions.t
->
107 type_constraint
SMap.t
->
108 FileInfo.mode
-> string -> Namespace_env.env
-> genv
109 val make_fun_decl_genv
:
110 TypecheckerOptions.t
->
111 type_constraint
SMap.t
-> Ast.fun_
-> genv
112 val make_const_env
: TypecheckerOptions.t
-> Ast.gconst
-> genv
* lenv
114 val has_unsafe
: genv
* lenv
-> bool
115 val set_unsafe
: genv
* lenv
-> bool -> unit
117 val in_ppl
: genv
* lenv
-> bool
118 val set_ppl
: genv
* lenv
-> bool -> genv
* lenv
120 val add_lvar
: genv
* lenv
-> Ast.id
-> positioned_ident
-> unit
121 val add_param
: genv
* lenv
-> N.fun_param
-> genv
* lenv
122 val new_lvar
: genv
* lenv
-> Ast.id
-> positioned_ident
123 val new_let_local
: genv
* lenv
-> Ast.id
-> positioned_ident
124 val found_dollardollar
: genv
* lenv
-> Pos.t
-> positioned_ident
125 val get_dollardollar
: genv
* lenv
-> positioned_ident
option
126 val inside_pipe
: genv
* lenv
-> bool
127 val new_pending_lvar
: genv
* lenv
-> Ast.id
-> unit
128 val promote_pending_lvar
: genv
* lenv
-> string -> unit
129 val lvar
: genv
* lenv
-> Ast.id
-> positioned_ident
130 val let_local
: genv
* lenv
-> Ast.id
-> positioned_ident
option
131 val global_const
: genv
* lenv
-> Ast.id
-> Ast.id
132 val type_name
: genv
* lenv
-> Ast.id
-> allow_typedef
:bool -> Ast.id
133 val fun_id
: genv
* lenv
-> Ast.id
-> Ast.id
134 val bind_class_const
: genv
* lenv
-> Ast.id
-> unit
135 val bind_prop
: genv
* lenv
-> Ast.id
-> unit
136 val goto_label
: genv
* lenv
-> string -> Pos.t
option
137 val new_goto_label
: genv
* lenv
-> pstring
-> unit
138 val new_goto_target
: genv
* lenv
-> pstring
-> unit
139 val check_goto_references
: genv
* lenv
-> unit
140 val copy_let_locals
: genv
* lenv
-> genv
* lenv
-> unit
142 val scope
: genv
* lenv
-> (genv
* lenv
-> 'a
) -> 'a
143 val scope_all
: genv
* lenv
-> (genv
* lenv
-> 'a
) -> all_locals
* 'a
144 val scope_lexical
: genv
* lenv
-> (genv
* lenv
-> 'a
) -> 'a
145 val extend_all_locals
: genv
* lenv
-> all_locals
-> unit
146 val remove_locals
: genv
* lenv
-> Ast.id list
-> unit
147 val pipe_scope
: genv
* lenv
-> (genv
* lenv
-> N.expr
) -> Local_id.t
* N.expr
150 type map
= positioned_ident
SMap.t
151 type all_locals
= Pos.t
SMap.t
153 (* The local environment *)
156 (* The set of locals *)
159 (* We keep all the locals, even if we are in a different scope
160 * to provide better error messages.
165 * Technically, passed this point, $x is unbound.
166 * But it is much better to keep it somewhere, so that you can
167 * say it is bound, but in a different scope.
169 all_locals
: all_locals
ref;
171 (* Some statements can define new variables afterwards, e.g.,
177 * We need to give $x the same name in both branches, but we don't want
178 * $x to actually be a local until after the if block. So we stash it here,
179 * to indicate a name has been pre-allocated, but that the variable isn't
180 * actually defined yet.
182 pending_locals
: map
ref;
184 (* The set of lexically-scoped local `let` variables *)
185 (* TODO: Currently these locals live in a separate namespace, it is
186 * worthwhile considering unified namespace for all local variables T28712009 *)
189 (* Tag controlling what we do when we encounter an unbound name.
190 * This is used when processing a lambda expression body that has
191 * an automatic use list.
193 * See expr_lambda for details.
195 unbound_mode
: unbound_mode
;
197 (* The presence of an "UNSAFE" in the function body changes the
198 * verifiability of the function's return type, since the unsafe
199 * block could return. For the sanity of the typechecker, we flatten
200 * this out, but need to track if we've seen an "UNSAFE" in order to
202 has_unsafe
: bool ref;
204 (** Allows us to ban $$ appearances outside of pipe expressions and
205 * equals expressions within pipes. *)
206 inside_pipe
: bool ref;
209 * A map from goto label strings to named labels.
211 goto_labels
: Pos.t
SMap.t
ref;
214 * A map from goto label used in a goto statement to the position of that
217 goto_targets
: Pos.t
SMap.t
ref;
220 let empty_local unbound_mode
= {
221 locals
= ref SMap.empty
;
222 all_locals
= ref SMap.empty
;
223 pending_locals
= ref SMap.empty
;
224 let_locals
= ref SMap.empty
;
226 has_unsafe
= ref false;
227 inside_pipe
= ref false;
228 goto_labels
= ref SMap.empty
;
229 goto_targets
= ref SMap.empty
;
232 let make_class_genv tcopt tparams mode
(cid
, ckind
) namespace is_ppl
=
234 (if !Autocomplete.auto_complete
then FileInfo.Mpartial
else mode
);
239 type_params
= tparams
;
240 current_cls
= Some
(cid
, ckind
);
241 class_consts
= Caml.Hashtbl.create
0;
242 class_props
= Caml.Hashtbl.create
0;
243 droot
= Typing_deps.Dep.Class
(snd cid
);
247 let unbound_name_error genv pos name kind
=
248 (* Naming pretends to be local and not dependent on other files, so it
249 * doesn't bother with adding dependencies (even though it does look up
250 * things in global state). This is mostly brushed aside because "they
251 * will be added during typing". Unfortunately, there are multiple scenarios
252 * when typechecker will name an expression, but gives up on typechecking
253 * it. We are then left with a unrecorded dependency. This should be fixed
254 * on some more basic level, but so far the only incorrectness that anyone
255 * has observed due to this is that we fail to remove "unbound name" errors
256 * sometimes. I add this dependency here for now to fix the annoyance it
257 * causes developers. *)
258 begin match kind
with
259 | `func
-> Typing_deps.Dep.Fun name
260 | `cls
-> Typing_deps.Dep.Class name
261 | `const
-> Typing_deps.Dep.GConst name
262 end |> Typing_deps.add_idep genv
.droot
;
263 Errors.unbound_name pos name kind
265 let make_class_env tcopt tparams c
=
266 let is_ppl = List.exists
268 (fun { ua_name
; _
} -> snd ua_name
= SN.UserAttributes.uaProbabilisticModel
) in
269 let genv = make_class_genv tcopt tparams c
.c_mode
270 (c
.c_name
, c
.c_kind
) c
.c_namespace
is_ppl in
271 let lenv = empty_local UBMErr
in
272 let env = genv, lenv in
275 let make_typedef_genv tcopt cstrs tdef
= {
276 in_mode
= FileInfo.(if !Ide.is_ide_mode
then Mpartial
else Mstrict
);
283 class_consts
= Caml.Hashtbl.create
0;
284 class_props
= Caml.Hashtbl.create
0;
285 droot
= Typing_deps.Dep.Class
(snd tdef
.t_id
);
286 namespace
= tdef
.t_namespace
;
289 let make_typedef_env genv cstrs tdef
=
290 let genv = make_typedef_genv genv cstrs tdef
in
291 let lenv = empty_local UBMErr
in
292 let env = genv, lenv in
295 let make_fun_genv tcopt params f_mode f_name f_namespace
= {
301 type_params
= params
;
303 class_consts
= Caml.Hashtbl.create
0;
304 class_props
= Caml.Hashtbl.create
0;
305 droot
= Typing_deps.Dep.Fun f_name
;
306 namespace
= f_namespace
;
309 let make_fun_decl_genv nenv params f
=
310 make_fun_genv nenv params f
.f_mode
(snd f
.f_name
) f
.f_namespace
312 let make_const_genv tcopt cst
= {
313 in_mode
= cst
.cst_mode
;
318 type_params
= SMap.empty
;
320 class_consts
= Caml.Hashtbl.create
0;
321 class_props
= Caml.Hashtbl.create
0;
322 droot
= Typing_deps.Dep.GConst
(snd cst
.cst_name
);
323 namespace
= cst
.cst_namespace
;
326 let make_const_env nenv cst
=
327 let genv = make_const_genv nenv cst
in
328 let lenv = empty_local UBMErr
in
329 let env = genv, lenv in
332 let has_unsafe (_genv
, lenv) = !(lenv.has_unsafe)
333 let set_unsafe (_genv
, lenv) x
=
336 let in_ppl (genv, _lenv
) = genv.in_ppl
338 let set_ppl (genv, lenv) in_ppl =
339 let genv = { genv with in_ppl } in
342 let lookup genv (env : string -> FileInfo.pos
option) (p
, x
) =
346 (match genv.in_mode
with
347 | FileInfo.Mstrict
| FileInfo.Mexperimental
-> unbound_name_error genv p x `const
348 | FileInfo.Mpartial
| FileInfo.Mdecl
when not
349 (TypecheckerOptions.assume_php
genv.tcopt
) ->
350 unbound_name_error genv p x `const
351 | FileInfo.Mphp
| FileInfo.Mdecl
| FileInfo.Mpartial
-> ()
355 (* Check and see if the user might have been trying to use one of the
356 * generics in scope as a runtime value *)
357 let check_no_runtime_generic genv (p
, name
) =
358 let tparaml = SMap.keys
genv.type_params
in
359 if List.mem
tparaml name ~equal
:(=) then Errors.generic_at_runtime p
;
362 let handle_unbound_name genv get_full_pos get_canon
(p
, name
) kind
=
363 match get_canon name
with
367 |> Option.iter ~f
:(fun p_canon
->
368 Errors.did_you_mean_naming p name p_canon canonical
);
369 (* Recovering from the capitalization error means
370 * returning the name in its canonical form *)
373 (match genv.in_mode
with
374 | FileInfo.Mpartial
| FileInfo.Mdecl
375 when TypecheckerOptions.assume_php
genv.tcopt
376 || name
= SN.Classes.cUnknown
-> ()
377 | FileInfo.Mphp
-> ()
378 | FileInfo.Mstrict
| FileInfo.Mexperimental
-> unbound_name_error genv p name kind
379 | FileInfo.Mpartial
| FileInfo.Mdecl
->
380 unbound_name_error genv p name kind
384 let canonicalize genv get_pos get_full_pos get_canon
(p
, name
) kind
=
385 (* Get the canonical name to check if the name exists in the heap *)
386 match get_pos name
with
388 | None
-> handle_unbound_name genv get_full_pos get_canon
(p
, name
) kind
390 let check_variable_scoping env (p
, x
) =
391 match SMap.get x
!(env.all_locals
) with
392 | Some p'
-> Errors.different_scope p x p'
395 (* Adds a local variable, without any check *)
396 let add_lvar (_
, lenv) (_
, name
) (p
, x
) =
397 lenv.locals
:= SMap.add name
(p
, x
) !(lenv.locals
);
400 let add_param env param
=
401 let p_name = param
.N.param_name
in
402 let id = Local_id.get
p_name in
403 let p_pos = param
.N.param_pos
in
404 let () = add_lvar env (p_pos, p_name) (p_pos, id) in
407 (* Defines a new local variable.
409 1) if the local is not in the local environment then it is added.
410 Return value: the given position and deduced/created identifier. *)
411 let new_lvar (_
, lenv) (p
, x
) =
412 let lcl = SMap.get x
!(lenv.locals
) in
415 | Some
lcl -> snd
lcl
417 let ident = match SMap.get x
!(lenv.pending_locals
) with
418 | Some
(_
, ident) -> ident
419 | None
-> Local_id.make x
in
420 lenv.all_locals
:= SMap.add x p
!(lenv.all_locals
);
421 lenv.locals
:= SMap.add x
(p
, ident) !(lenv.locals
);
426 (* Defines a new scoped local variable
428 * Always add a new variable in the local environment.
429 * If the variable has been defined already, shadow the previously-defined
431 (* TODO: Emit warning if names are getting shadowed T28436131 *)
432 let new_let_local (_
, lenv) (p
, x
) =
433 let ident = Local_id.make x
in
434 lenv.all_locals
:= SMap.add x p
!(lenv.all_locals
);
435 lenv.let_locals
:= SMap.add x
(p
, ident) !(lenv.let_locals
);
438 (* Defines a new local variable for this dollardollar (or reuses
439 * the exiting identifier). *)
440 let found_dollardollar (genv, lenv) p
=
441 if not
!(lenv.inside_pipe
) then
442 Errors.undefined p
SN.SpecialIdents.dollardollar
;
443 new_lvar (genv, lenv) (p
, SN.SpecialIdents.dollardollar
)
445 (* Check if dollardollar is defined in the current environment *)
446 let get_dollardollar (_genv
, lenv) =
447 SMap.get
SN.SpecialIdents.dollardollar
!(lenv.locals
)
449 let inside_pipe (_
, lenv) =
452 let new_pending_lvar (_
, lenv) (p
, x
) =
453 match SMap.get x
!(lenv.locals
), SMap.get x
!(lenv.pending_locals
) with
455 let y = p
, Local_id.make x
in
456 lenv.pending_locals
:= SMap.add x
y !(lenv.pending_locals
)
459 let promote_pending_lvar (_
, lenv) x
=
460 match SMap.get x
!(lenv.pending_locals
) with
462 lenv.locals
:= SMap.add x
(p
, ident) !(lenv.locals
);
463 lenv.pending_locals
:= SMap.remove x
!(lenv.pending_locals
)
466 let handle_undefined_variable (_genv
, env) (p
, x
) =
467 match env.unbound_mode
with
468 | UBMErr
-> Errors.undefined p x
; p
, Local_id.make x
469 | UBMFunc f
-> f
(p
, x
)
471 (* Function used to name a local variable *)
472 let lvar (genv, env) (p
, x
) =
474 if SN.Superglobals.is_superglobal x
&& genv.in_mode
= FileInfo.Mpartial
475 then p, Local_id.make x
477 let lcl = SMap.get x
!(env.locals
) in
479 | Some
lcl -> p, snd
lcl
480 | None
when not
!Autocomplete.auto_complete
->
481 check_variable_scoping env (p, x
);
482 handle_undefined_variable (genv, env) (p, x
)
483 | None
-> p, Local_id.tmp
()
487 let let_local (_genv
, env) (p, x
) =
488 let lcl = SMap.get x
!(env.let_locals
) in
490 | Some
lcl -> Some
(p, snd
lcl)
493 let get_name genv get_pos x
=
494 lookup genv get_pos x
; x
496 (* For dealing with namespace fallback on constants *)
497 let elaborate_and_get_name_with_fallback
500 (get_pos
: string -> FileInfo.pos
option) x
=
501 let get_name x
= get_name genv get_pos x
in
502 let fq_x = NS.elaborate_id
genv.namespace
NS.ElaborateConst x
in
504 genv.namespace
.Namespace_env.ns_name
<> None
&&
505 not
(String.contains
(snd x
) '
\\'
) in
508 (* __FILE__, __LINE__ etc *)
509 (string_starts_with
(snd x
) "__") && (string_ends_with
(snd x
) "__") in
510 if use_fallback then begin
511 let global_x = (fst x
, "\\" ^
(snd x
)) in
512 (* Explicitly add dependencies on both of the consts we could be
513 * referring to here. Normally naming doesn't have to deal with
514 * deps at all -- they are added during typechecking just by the
515 * nature of looking up a class or function name. However, we're
516 * flattening namespaces here, and the fallback behavior of
517 * consts means that we might suddenly be referring to a
518 * different const without any change to the callsite at
519 * all. Adding both dependencies explicitly captures this
520 * action-at-a-distance. *)
521 Typing_deps.add_idep
genv.droot
(mk_dep
(snd
fq_x));
522 Typing_deps.add_idep
genv.droot
(mk_dep
(snd
global_x));
523 let mem (_
, s
) = get_pos s
in
524 match mem fq_x, mem global_x with
525 (* Found in the current namespace *)
526 | Some _
, _
-> get_name fq_x
527 (* Found in the global namespace *)
528 | _
, Some _
-> get_name global_x
529 (* Not found. Pick the more specific one to error on. *)
530 | None
, None
-> get_name fq_x
534 (* For dealing with namespace resolution on functions *)
535 let elaborate_and_get_name_with_canonicalized_fallback
537 (get_pos
: string -> FileInfo.pos
option)
538 (get_full_pos
: string -> Pos.t
option)
540 let get_name x
= get_name genv get_pos x
in
541 let canonicalize = canonicalize genv get_pos get_full_pos get_canon
in
542 let fq_x = NS.elaborate_id
genv.namespace
NS.ElaborateFun x
in
543 let fq_x = canonicalize fq_x `func
in
546 let global_const (genv, _env
) x
=
547 elaborate_and_get_name_with_fallback
548 (* Same idea as Dep.FunName, see below. *)
549 (fun x
-> Typing_deps.Dep.GConstName x
)
551 (Naming_heap.ConstPosHeap.get
)
554 let type_name (genv, _
) x ~allow_typedef
=
555 (* Generic names are not allowed to shadow class names *)
556 check_no_runtime_generic genv x
;
557 let (pos
, name
) as x
= NS.elaborate_id
genv.namespace
NS.ElaborateClass x
in
558 match Naming_heap.TypeIdHeap.get name
with
559 | Some
(_def_pos
, `Class
) ->
560 (* Don't let people use strictly internal classes
561 * (except when they are being declared in .hhi files) *)
562 if name
= SN.Classes.cHH_BuiltinEnum
&&
563 not
(string_ends_with
(Relative_path.suffix
(Pos.filename pos
)) ".hhi")
564 then Errors.using_internal_class pos
(strip_ns name
);
566 | Some
(def_pos
, `Typedef
) when not allow_typedef
->
567 let full_pos, _
= GEnv.get_full_pos
genv.tcopt
(def_pos
, name
) in
568 Errors.unexpected_typedef pos
full_pos;
570 | Some
(_def_pos
, `Typedef
) -> pos
, name
572 handle_unbound_name genv
573 (GEnv.type_pos
genv.tcopt
)
574 GEnv.type_canon_name x `cls
576 let fun_id (genv, _
) x
=
577 elaborate_and_get_name_with_canonicalized_fallback
579 (Naming_heap.FunPosHeap.get
)
580 (GEnv.fun_pos
genv.tcopt
)
584 let bind_class_member tbl
(p, x
) =
586 let p'
= Caml.Hashtbl.find tbl x
in
587 Errors.error_name_already_bound x x
p p'
588 with Caml.Not_found
->
589 Caml.Hashtbl.replace tbl x
p
591 let bind_class_const (genv, _env
) (p, x
) =
592 if String.lowercase x
= "class" then Errors.illegal_member_variable_class
p;
593 bind_class_member genv.class_consts
(p, x
)
595 let bind_prop (genv, _env
) x
=
596 bind_class_member genv.class_props x
599 * Returns the position of the goto label declaration, if it exists.
601 let goto_label (_
, { goto_labels
; _
}) label
=
602 SMap.get label
!goto_labels
605 * Adds a goto label and the position of its declaration to the known labels.
607 let new_goto_label (_
, { goto_labels
; _
}) (pos
, label
) =
608 goto_labels
:= SMap.add label pos
!goto_labels
611 * Adds a goto target and its reference position to the known targets.
613 let new_goto_target (_
, { goto_targets
; _
}) (pos
, label
) =
614 goto_targets
:= SMap.add label pos
!goto_targets
617 * Ensures that goto statements do not reference goto labels that are not
618 * known within the current lenv.
620 let check_goto_references (_
, { goto_labels
; goto_targets
; _
}) =
621 let check_label referenced_label referenced_label_pos
=
622 if not
(SMap.mem referenced_label
!goto_labels
) then
623 Errors.goto_label_undefined referenced_label_pos referenced_label
in
624 SMap.iter
check_label !goto_targets
626 (* Scope, keep the locals, go and name the body, and leave the
627 * local environment intact
630 let _genv, lenv = env in
631 let lenv_copy = !(lenv.locals
) in
632 let lenv_pending_copy = !(lenv.pending_locals
) in
633 let lenv_scoped_copy = !(lenv.let_locals
) in
635 lenv.locals
:= lenv_copy;
636 lenv.pending_locals
:= lenv_pending_copy;
637 lenv.let_locals
:= lenv_scoped_copy;
640 let remove_locals env vars
=
641 let _genv, lenv = env in
643 List.fold_left vars ~f
:(fun l
id -> SMap.remove
(snd
id) l
) ~init
:!(lenv.locals
)
645 let scope_all env f
=
646 let _genv, lenv = env in
647 let lenv_all_locals_copy = !(lenv.all_locals
) in
648 let res = scope env f
in
649 let lenv_all_locals = !(lenv.all_locals
) in
650 lenv.all_locals
:= lenv_all_locals_copy;
653 (* Add a new lexical scope for block-scoped `let` variables.
654 No other changes in the local environment *)
655 let scope_lexical env f
=
656 let _genv, lenv = env in
657 let lenv_scoped_copy = !(lenv.let_locals
) in
659 lenv.let_locals
:= lenv_scoped_copy;
662 (* Copy the let locals from lenv1 to lenv2 *)
663 let copy_let_locals (_genv1
, lenv1
) (_genv2
, lenv2
) =
664 let let_locals_1 = !(lenv1
.let_locals
) in
665 lenv2
.let_locals
:= let_locals_1
667 let extend_all_locals (_genv, lenv) more_locals
=
668 lenv.all_locals
:= SMap.union more_locals
!(lenv.all_locals
)
670 (** Sets up the environment so that naming can be done on the RHS of a
671 * pipe expression. It returns the identity of the $$ in the RHS and the
672 * named RHS. The steps are as follows:
673 * - Removes the $$ from the local env
674 * - Name the RHS scope
675 * - Restore the binding of $$ in the local env (if it was bound).
677 * This will append an error if $$ was not used in the RHS.
679 * The inside_pipe flag is also set before the naming and restored afterwards.
681 let pipe_scope env name_e2
=
683 let outer_pipe_var_opt =
684 SMap.get
SN.SpecialIdents.dollardollar
!(lenv.locals) in
685 let inner_locals = SMap.remove
SN.SpecialIdents.dollardollar
687 lenv.locals := inner_locals;
688 lenv.inside_pipe := true;
689 (** Name the RHS of the pipe expression. During this naming, if the $$ from
690 * this pipe is used, it will be added to the locals. *)
691 let e2 = name_e2
env in
693 match SMap.get
SN.SpecialIdents.dollardollar
!(lenv.locals) with
695 Errors.dollardollar_unused
(fst
e2);
696 (** The $$ lvar should be named when it is encountered inside e2,
697 * but we've now discovered it wasn't used at all.
698 * Create an ID here so we can keep going. *)
699 Local_id.make
SN.SpecialIdents.dollardollar
702 let restored_locals = SMap.remove
SN.SpecialIdents.dollardollar
704 (match outer_pipe_var_opt with
706 lenv.locals := restored_locals;
707 lenv.inside_pipe := false;
709 | Some outer_pipe_var
-> begin
710 let restored_locals = SMap.add
SN.SpecialIdents.dollardollar
711 outer_pipe_var
restored_locals in
712 lenv.locals := restored_locals;
717 (*****************************************************************************)
719 (*****************************************************************************)
721 (* Alok is constantly complaining that in partial mode,
722 * he forgets to bind a type parameter, for example T,
723 * and because partial assumes T is just a class that lives
724 * in PHP land there is no error message.
725 * So to help him, I am adding a rule that if
726 * the class name starts with a T and is only 2 characters
727 * it is considered a type variable. You will not be able to
728 * define a class T in php land in this scheme ... But it is a bad
729 * name for a class anyway.
731 let is_alok_type_name (_, x
) = String.length x
<= 2 && x
.[0] = 'T'
733 let check_constraint (_, (pos
, name
), _, _) =
734 (* TODO refactor this in a separate module for errors *)
735 if String.lowercase name
= "this"
736 then Errors.this_reserved pos
737 else if name
.[0] <> 'T'
then Errors.start_with_T pos
739 let check_repetition s param
=
740 let x = snd param
.param_id
in
742 then Errors.already_bound
(fst param
.param_id
) x;
743 if x <> SN.SpecialIdents.placeholder
then SSet.add
x s
else s
745 let convert_shape_name env = function
746 | SFlit_int
(pos
, s
) -> (pos
, SFlit_int
(pos
, s
))
747 | SFlit_str
(pos
, s
) -> (pos
, SFlit_str
(pos
, s
))
748 | SFclass_const
(x, (pos
, y)) ->
750 if (snd
x) = SN.Classes.cSelf
then
751 match (fst
env).current_cls
with
752 | Some
(cid
, _) -> cid
753 | None
-> Errors.self_outside_class pos
; (pos
, SN.Classes.cUnknown
)
754 else Env.type_name env x ~allow_typedef
:false in
755 (pos
, SFclass_const
(class_name, (pos
, y)))
757 let arg_unpack_unexpected = function
759 | (pos
, _) :: _ -> Errors.naming_too_few_arguments pos
; ()
761 module type GetLocals
= sig
762 val stmt
: TypecheckerOptions.t
-> Namespace_env.env * Pos.t
SMap.t
->
763 Ast.stmt
-> Namespace_env.env * Pos.t
SMap.t
764 val lvalue
: TypecheckerOptions.t
-> Namespace_env.env * Pos.t
SMap.t
->
765 Ast.expr
-> Namespace_env.env * Pos.t
SMap.t
768 (* This was made a functor due to the awkward nature of how our naming
769 * code is structured.
771 * Naming is called both in the decl phase and type-check phase. In the
772 * decl phase it's mostly used to construct things that do not belong in
773 * function bodies; examples include classes, their member fields, and
774 * global constants. This part of naming is entirely self-contained; it
775 * only uses the data from the AST in the current file, and does not need
776 * to cross-reference decl type data from other files.
778 * In the type-check phase, Naming is invoked again, this time to name the
779 * bodies of functions. Now it requires decl type data in order to know
780 * which function calls are marked as `noreturn`, because this affects
781 * which local variables are considered to be defined at the end of a
784 * So decl depends on naming, but naming also depends on decl, creating
785 * a circular dependency. The obvious solution would be to split it into
786 * two, but this is nontrivial because decl-phase naming also does some
787 * naming of expressions -- for example, constant initializers and default
788 * parameter values have them. Of course, none of these expressions can
789 * actually contain local variables, but our code is not written in a way
790 * that the OCaml type system can understand that. So as a hacky solution,
791 * I'm parameterizing GetLocals so that it is a no-op in the decl phase
792 * but can be properly instantiated with Typing_get_locals in the typing
795 module Make
(GetLocals
: GetLocals
) = struct
796 (************************************************************************)
797 (* Naming of type hints *)
798 (************************************************************************)
801 ?
(allow_retonly
=false)
802 ?
(allow_typedef
=true)
803 ?
(allow_wildcard
=false)
804 ?
(in_where_clause
=false)
816 and shape_field_to_shape_field_info
env { sf_optional
; sf_name
=_; sf_hint
} =
818 N.sfi_optional
= sf_optional
;
819 sfi_hint
= hint env sf_hint
;
822 and ast_shape_info_to_nast_shape_info
824 { si_allows_unknown_fields
; si_shape_field_list
} =
825 let f fdm shape_field
=
826 let pos, name
= convert_shape_name env shape_field
.sf_name
in
827 if ShapeMap.mem name fdm
828 then Errors.fd_name_already_bound
pos;
830 name
(shape_field_to_shape_field_info
env shape_field
) fdm
in
832 List.fold_left si_shape_field_list ~init
:ShapeMap.empty ~
f in
834 nsi_allows_unknown_fields
=si_allows_unknown_fields
;
838 and hfun
env reactivity is_coroutine hl kl variadic_hint h
=
839 let variadic_hint = match variadic_hint with
840 | Hvariadic Some
(h
) -> N.Hvariadic
(Some
(hint env h
))
841 | Hvariadic None
-> N.Hvariadic
(None
)
842 | Hnon_variadic
-> N.Hnon_variadic
in
843 N.Hfun
(reactivity
, is_coroutine
, List.map hl
(hint env), kl
, variadic_hint,
844 hint ~allow_retonly
:true env h
)
846 and hint_ ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
847 ~in_where_clause ?
(tp_depth
=0)
850 hint ~forbid_this ~allow_typedef ~allow_wildcard
in
853 N.Htuple
(List.map hl
(hint ~allow_retonly
env))
855 (* void/noreturn are permitted for Typing.option_return_only_typehint *)
856 N.Hoption
(hint ~allow_retonly
env h
)
858 let h = hint ~allow_retonly
env h
860 | Hfun
(is_coroutine
, hl
, kl
, variadic_hint, h) ->
861 hfun
env N.FNonreactive is_coroutine hl kl
variadic_hint h
862 (* Special case for Rx<function> *)
863 | Happly
((_, "Rx"), [(_, Hfun
(is_coroutine
, hl
, kl
, variadic_hint, h))]) ->
864 hfun
env N.FReactive is_coroutine hl kl
variadic_hint h
865 (* Special case for RxShallow<function> *)
866 | Happly
((_, "RxShallow"), [(_, Hfun
(is_coroutine
, hl
, kl
, variadic_hint, h))]) ->
867 hfun
env N.FShallow is_coroutine hl kl
variadic_hint h
868 (* Special case for RxLocal<function> *)
869 | Happly
((_, "RxLocal"), [(_, Hfun
(is_coroutine
, hl
, kl
, variadic_hint, h))]) ->
870 hfun
env N.FLocal is_coroutine hl kl
variadic_hint h
871 | Happly
((p, _x
) as id, hl
) ->
873 hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth
877 | N.Hprim
_ | N.Hmixed
| N.Hnonnull
->
878 if hl
<> [] then Errors.unexpected_type_arguments
p
882 | Haccess
((pos, root_id
) as root
, id, ids
) ->
885 | x when x = SN.Classes.cSelf
->
886 (match (fst
env).current_cls
with
888 Errors.self_outside_class
pos;
893 | x when x = SN.Classes.cStatic
|| x = SN.Classes.cParent
->
894 Errors.invalid_type_access_root root
; N.Hany
896 let tconst_on_generics_enabled =
897 TypecheckerOptions.experimental_feature_enabled
899 TypecheckerOptions.experimental_tconst_on_generics
in
901 hint_id ~forbid_this ~allow_retonly
902 ~allow_typedef ~allow_wildcard
:false ~tp_depth
env root
[] in
904 | N.Hthis
| N.Happly
_ as h -> h
905 | N.Habstr
_ when in_where_clause
&& tconst_on_generics_enabled ->
907 | _ -> Errors.invalid_type_access_root root
; N.Hany
910 N.Haccess
((pos, root_ty), id :: ids
)
911 | Hshape ast_shape_info
->
912 N.Hshape
(ast_shape_info_to_nast_shape_info
env ast_shape_info
)
914 and hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth
915 env (p, x as id) hl
=
916 let params = (fst
env).type_params
in
917 if is_alok_type_name id && not
(SMap.mem x params)
918 then Errors.typeparam_alok
id;
919 (* some common Xhp screw ups *)
920 if (x = "Xhp") || (x = ":Xhp") || (x = "XHP")
921 then Errors.disallowed_xhp_type
p x;
922 match try_castable_hint ~forbid_this ~allow_wildcard ~tp_depth
env p x hl
with
926 | x when x = SN.Typehints.wildcard
&& allow_wildcard
&& tp_depth
= 1 ->
928 (Errors.tparam_with_tparam
p x;
932 | x when x = SN.Typehints.wildcard
->
933 Errors.wildcard_disallowed
p;
935 | x when x.[0] = '
\\'
&&
936 ( x = ("\\"^
SN.Typehints.void
)
937 || x = ("\\"^
SN.Typehints.noreturn
)
938 || x = ("\\"^
SN.Typehints.int)
939 || x = ("\\"^
SN.Typehints.bool)
940 || x = ("\\"^
SN.Typehints.float)
941 || x = ("\\"^
SN.Typehints.num
)
942 || x = ("\\"^
SN.Typehints.string)
943 || x = ("\\"^
SN.Typehints.resource
)
944 || x = ("\\"^
SN.Typehints.mixed
)
945 || x = ("\\"^
SN.Typehints.nonnull
)
946 || x = ("\\"^
SN.Typehints.array
)
947 || x = ("\\"^
SN.Typehints.arraykey
)
948 || x = ("\\"^
SN.Typehints.integer
)
949 || x = ("\\"^
SN.Typehints.boolean
)
950 || x = ("\\"^
SN.Typehints.double
)
951 || x = ("\\"^
SN.Typehints.real)
953 Errors.primitive_toplevel
p;
955 | x when x = SN.Typehints.void
&& allow_retonly
-> N.Hprim
N.Tvoid
956 | x when x = SN.Typehints.void
->
957 if TypecheckerOptions.experimental_feature_enabled
959 TypecheckerOptions.experimental_void_is_type_of_null
961 else (Errors.return_only_typehint
p `void
; N.Hany
)
962 | x when x = SN.Typehints.noreturn
&& allow_retonly
-> N.Hprim
N.Tnoreturn
963 | x when x = SN.Typehints.noreturn
->
964 Errors.return_only_typehint
p `noreturn
;
966 | x when x = SN.Typehints.num
-> N.Hprim
N.Tnum
967 | x when x = SN.Typehints.resource
-> N.Hprim
N.Tresource
968 | x when x = SN.Typehints.arraykey
-> N.Hprim
N.Tarraykey
969 | x when x = SN.Typehints.mixed
-> N.Hmixed
970 | x when x = SN.Typehints.nonnull
-> N.Hnonnull
971 | x when x = SN.Typehints.dynamic
-> N.Hdynamic
972 | x when x = SN.Typehints.this
&& not forbid_this
->
973 if not
(phys_equal hl
[])
974 then Errors.this_no_argument
p;
975 (match (fst
env).current_cls
with
977 Errors.this_hint_outside_class
p;
982 | x when x = SN.Typehints.this
->
983 (match (fst
env).current_cls
with
985 Errors.this_hint_outside_class
p
987 Errors.this_type_forbidden
p
990 | x when x = SN.Classes.cClassname
&& (List.length hl
) <> 1 ->
991 Errors.classname_param
p;
993 | _ when String.lowercase
x = SN.Typehints.this
->
994 Errors.lowercase_this
p x;
996 | _ when SMap.mem x params ->
998 Errors.tparam_with_tparam
p x;
1001 let name = Env.type_name env id ~allow_typedef
in
1002 (* Note that we are intentionally setting allow_typedef to `true` here.
1003 * In general, generics arguments can be typedefs -- there is no
1004 * runtime restriction. *)
1005 N.Happly
(name, hintl ~allow_wildcard ~forbid_this ~allow_typedef
:true
1006 ~allow_retonly
:true ~tp_depth
:(tp_depth
+1) env hl
)
1009 (* Hints that are valid both as casts and type annotations. Neither
1010 * casts nor annotations are a strict subset of the other: For
1011 * instance, 'object' is not a valid annotation. Thus callers will
1012 * have to handle the remaining cases. *)
1013 and try_castable_hint ?
(forbid_this
=false) ?
(allow_wildcard
=false) ~tp_depth
env p x hl
=
1014 let hint = hint ~forbid_this ~tp_depth
:(tp_depth
+1) ~allow_wildcard ~allow_retonly
:false in
1015 let canon = String.lowercase
x in
1016 let opt_hint = match canon with
1017 | nm
when nm
= SN.Typehints.int -> Some
(N.Hprim
N.Tint
)
1018 | nm
when nm
= SN.Typehints.bool -> Some
(N.Hprim
N.Tbool
)
1019 | nm
when nm
= SN.Typehints.float -> Some
(N.Hprim
N.Tfloat
)
1020 | nm
when nm
= SN.Typehints.string -> Some
(N.Hprim
N.Tstring
)
1021 | nm
when nm
= SN.Typehints.array
->
1022 let tcopt = (fst
env).tcopt in
1023 let array_typehints_disallowed =
1024 TypecheckerOptions.disallow_array_typehint
tcopt in
1025 if array_typehints_disallowed
1026 then Errors.array_typehints_disallowed p;
1028 | [] -> N.Harray
(None
, None
)
1029 | [val_
] -> N.Harray
(Some
(hint env val_
), None
)
1031 N.Harray
(Some
(hint env key_
), Some
(hint env val_
))
1032 | _ -> Errors.too_many_type_arguments
p; N.Hany
1034 | nm
when nm
= SN.Typehints.darray
->
1037 if (fst
env).in_mode
= FileInfo.Mstrict
then
1038 Errors.too_few_type_arguments
p;
1039 N.Hdarray
((p, N.Hany
), (p, N.Hany
))
1040 | [_] -> Errors.too_few_type_arguments
p; N.Hany
1041 | [key_
; val_
] -> N.Hdarray
(hint env key_
, hint env val_
)
1042 | _ -> Errors.too_many_type_arguments
p; N.Hany
)
1043 | nm
when nm
= SN.Typehints.varray
->
1046 if (fst
env).in_mode
= FileInfo.Mstrict
then
1047 Errors.too_few_type_arguments
p;
1048 N.Hvarray
(p, N.Hany
)
1049 | [val_
] -> N.Hvarray
(hint env val_
)
1050 | _ -> Errors.too_many_type_arguments
p; N.Hany
)
1051 | nm
when nm
= SN.Typehints.varray_or_darray
->
1054 if (fst
env).in_mode
= FileInfo.Mstrict
then
1055 Errors.too_few_type_arguments
p;
1056 N.Hvarray_or_darray
(p, N.Hany
)
1057 | [val_
] -> N.Hvarray_or_darray
(hint env val_
)
1058 | _ -> Errors.too_many_type_arguments
p; N.Hany
)
1059 | nm
when nm
= SN.Typehints.integer
->
1060 Errors.primitive_invalid_alias
p nm
SN.Typehints.int;
1061 Some
(N.Hprim
N.Tint
)
1062 | nm
when nm
= SN.Typehints.boolean
->
1063 Errors.primitive_invalid_alias
p nm
SN.Typehints.bool;
1064 Some
(N.Hprim
N.Tbool
)
1065 | nm
when nm
= SN.Typehints.double
|| nm
= SN.Typehints.real ->
1066 Errors.primitive_invalid_alias
p nm
SN.Typehints.float;
1067 Some
(N.Hprim
N.Tfloat
)
1070 let () = match opt_hint with
1071 | Some
_ when canon <> x -> Errors.primitive_invalid_alias
p x canon
1075 and constraint_ ?
(forbid_this
=false) env (ck
, h) = ck
, hint ~forbid_this
env h
1077 and hintl ~forbid_this ~allow_retonly
1078 ~allow_typedef ~allow_wildcard ~tp_depth
env l
=
1080 (hint ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth
env)
1081 and hintl_funcall
env p l
=
1083 ~allow_wildcard
:true
1088 env (extract_hintl_from_type_args
env p l
)
1090 and extract_hintl_from_type_args
env p hl
=
1091 let hl, reifiedl
= List.unzip
hl in
1092 if not
(TypecheckerOptions.experimental_feature_enabled
1094 TypecheckerOptions.experimental_reified_generics
)
1095 && List.exists reifiedl
(fun i
-> i
)
1097 Errors.experimental_feature
p "reified generics";
1100 (**************************************************************************)
1101 (* All the methods and static methods of an interface are "implicitly"
1102 * declared as abstract
1104 (**************************************************************************)
1106 let add_abstract m
= {m
with N.m_abstract
= true}
1108 let add_abstractl methods
= List.map methods
add_abstract
1110 let interface c constructor methods smethods
=
1111 if c
.c_kind
<> Cinterface
then constructor
, methods
, smethods
else
1112 let constructor = Option.map
constructor add_abstract in
1113 let methods = add_abstractl methods in
1114 let smethods = add_abstractl smethods in
1115 constructor, methods, smethods
1117 (**************************************************************************)
1118 (* Checking for collision on method names *)
1119 (**************************************************************************)
1121 let check_method acc
{ N.m_name
= (p, x); _ } =
1123 then Errors.method_name_already_bound
p x;
1126 let check_name_collision methods =
1127 ignore
(List.fold_left
methods ~init
:SSet.empty ~
f:check_method)
1129 (**************************************************************************)
1130 (* Checking for shadowing of method type parameters *)
1131 (**************************************************************************)
1133 let check_method_tparams class_tparam_names
{ N.m_tparams
= tparams
; _ } =
1134 List.iter tparams
begin fun (_, (p,x), _, _) ->
1135 List.iter class_tparam_names
1136 (fun (pc
,xc
) -> if (x = xc
) then Errors.shadowed_type_param
p pc
x)
1139 let check_tparams_constructor class_tparam_names
constructor =
1140 match constructor with
1142 | Some constr
-> check_method_tparams class_tparam_names constr
1144 let check_tparams_shadow class_tparam_names
methods =
1145 List.iter
methods (check_method_tparams class_tparam_names
)
1147 let check_break_continue_level p level_opt
=
1148 if Option.is_some level_opt
1149 then Errors.break_continue_n_not_supported
p
1151 let ensure_name_not_dynamic env e err
=
1153 | (_, (Id
_ | Lvar
_)) -> ()
1155 if (fst
env).in_mode
= FileInfo.Mstrict
1158 (* Naming of a class *)
1159 let rec class_ nenv c
=
1160 let constraints = make_constraints c
.c_tparams
in
1161 let env = Env.make_class_env nenv
constraints c
in
1162 (* Checking for a code smell *)
1163 List.iter c
.c_tparams
check_constraint;
1164 let name = Env.type_name env c
.c_name ~allow_typedef
:false in
1166 List.fold_right c
.c_body ~init
:[] ~
f:(class_static_method
env) in
1167 let sprops = List.fold_right c
.c_body ~init
:[] ~
f:(class_prop_static
env) in
1168 let attrs = user_attributes
env c
.c_user_attributes
in
1169 let const = (Attributes.find
SN.UserAttributes.uaConst
attrs) in
1170 let props = List.fold_right c
.c_body ~init
:[] ~
f:(class_prop ~
const env) in
1172 List.map c
.c_extends
1173 (hint ~allow_retonly
:false ~allow_typedef
:false env) in
1174 let parents = match c
.c_kind
with
1175 (* Make enums implicitly extend the BuiltinEnum class in order to provide
1176 * utility methods. *)
1178 let pos = fst
name in
1179 let enum_type = pos, N.Happly
(name, []) in
1181 pos, N.Happly
((pos, Naming_special_names.Classes.cHH_BuiltinEnum
),
1185 let methods = List.fold_right c
.c_body ~init
:[] ~
f:(class_method
env) in
1186 let uses = List.fold_right c
.c_body ~init
:[] ~
f:(class_use
env) in
1188 List.fold_right c
.c_body ~init
:[] ~
f:(xhp_attr_use
env) in
1190 Option.value ~default
:[] @@
1191 List.fold_right c
.c_body ~init
:None ~
f:(xhp_category env) in
1192 let req_implements, req_extends
= List.fold_right c
.c_body
1193 ~init
:([], []) ~
f:(class_require
env c
.c_kind
) in
1194 (* Setting a class type parameters constraint to the 'this' type is weird
1195 * so lets forbid it for now.
1197 let tparam_l = type_paraml ~forbid_this
:true env c
.c_tparams
in
1198 let consts = List.fold_right ~
f:(class_const
env) c
.c_body ~init
:[] in
1200 List.fold_right ~
f:(class_typeconst
env) c
.c_body ~init
:[] in
1201 let implements = List.map c
.c_implements
1202 (hint ~allow_retonly
:false ~allow_typedef
:false env) in
1203 let constructor = List.fold_left ~
f:(constructor env) ~init
:None c
.c_body
in
1204 let constructor, methods, smethods =
1205 interface c
constructor methods smethods in
1206 let class_tparam_names = List.map c
.c_tparams
(fun (_, x, _, _) -> x) in
1207 let enum = Option.map c
.c_enum
(enum_
env) in
1208 check_tparams_constructor class_tparam_names constructor;
1209 check_name_collision methods;
1210 check_tparams_shadow class_tparam_names methods;
1211 check_name_collision smethods;
1212 check_tparams_shadow class_tparam_names smethods;
1214 { N.c_annotation
= ();
1215 N.c_mode
= c
.c_mode
;
1216 N.c_final
= c
.c_final
;
1217 N.c_is_xhp
= c
.c_is_xhp
;
1218 N.c_kind
= c
.c_kind
;
1220 N.c_tparams
= (tparam_l, constraints);
1221 N.c_extends
= parents;
1223 N.c_xhp_attr_uses
= xhp_attr_uses;
1224 N.c_xhp_category
= xhp_category;
1225 N.c_req_extends
= req_extends
;
1226 N.c_req_implements
= req_implements;
1227 N.c_implements
= implements;
1228 N.c_consts
= consts;
1229 N.c_typeconsts
= typeconsts;
1230 N.c_static_vars
= sprops;
1232 N.c_constructor
= constructor;
1233 N.c_static_methods
= smethods;
1234 N.c_methods
= methods;
1235 N.c_user_attributes
= attrs;
1241 and user_attributes
env attrl
=
1242 let seen = Caml.Hashtbl.create
0 in
1243 let validate_seen = begin fun ua_name
->
1244 let pos, name = ua_name
in
1245 let existing_attr_pos =
1246 try Some
(Caml.Hashtbl.find
seen name)
1247 with Caml.Not_found
-> None
1248 in (match existing_attr_pos with
1249 | Some
p -> Errors.duplicate_user_attribute ua_name
p; false
1250 | None
-> Caml.Hashtbl.add
seen name pos; true
1253 List.fold_left attrl ~init
:[] ~
f:begin fun acc
{ua_name
; ua_params
} ->
1255 if String.is_prefix
(snd
ua_name) ~prefix
:"__" ||
1256 TypecheckerOptions.allowed_attribute
(fst
env).tcopt (snd
ua_name)
1258 else Env.type_name env ua_name ~allow_typedef
:false in
1259 if not
(validate_seen ua_name) then acc
1261 N.ua_name = ua_name;
1262 N.ua_params
= List.map ua_params
(expr
env)
1267 and xhp_attribute_decl
env h cv is_required maybe_enum
=
1268 let p, (_, id), default
= cv
in
1269 if is_required
&& Option.is_some default
then
1270 Errors.xhp_required_with_default
p id;
1271 let h = (match maybe_enum
with
1272 | Some
(pos, _optional
, items
) ->
1273 let contains_int = List.exists items
begin function
1277 let contains_str = List.exists items
begin function
1278 | _, String
_ | _, String2
_ -> true
1281 if contains_int && not
contains_str then
1282 Some
(pos, Happly
((pos, "int"), []))
1283 else if not
contains_int && contains_str then
1284 Some
(pos, Happly
((pos, "string"), []))
1286 (* If the list was empty, or if there was a mix of
1287 ints and strings, then fallback to mixed *)
1288 Some
(pos, Happly
((pos, "mixed"), []))
1290 let h = (match h with
1291 | Some
(p, ((Hoption
_) as x)) -> begin
1292 (* `null` has special meaning in XHP and a required attribute cannot
1293 * actually be nullable *)
1294 if is_required
then Errors.xhp_optional_required_attr
p id;
1297 | Some
(p, ((Happly
((_, "mixed"), [])) as x)) -> Some
(p, x)
1299 (* If a non-nullable attribute is not marked as "@required"
1300 AND it does not have a non-null default value, make the
1301 typehint nullable for now *)
1305 | Some
(_, Null
) -> false
1308 else Some
(p, Hoption
(p, h))
1310 let h = Option.map
h (hint env) in
1311 let cv = class_prop_
env cv in
1315 { N.e_base
= hint env e
.e_base
;
1316 N.e_constraint
= Option.map e
.e_constraint
(hint env);
1319 and type_paraml ?
(forbid_this
= false) env tparams
=
1320 let _, ret
= List.fold_left tparams ~init
:(SMap.empty
, [])
1321 ~
f:(fun (seen, tparaml) ((_, (p, name), _, _) as tparam
) ->
1322 match SMap.get
name seen with
1324 SMap.add
name p seen, (type_param ~forbid_this
env tparam
)::tparaml
1326 Errors.shadowed_type_param
p pos name;
1332 and type_param ~forbid_this
env (variance
, param_name
, cstr_list
, reified
) =
1333 if reified
&& not
(TypecheckerOptions.experimental_feature_enabled
1335 TypecheckerOptions.experimental_reified_generics
)
1337 Errors.experimental_feature
(fst param_name
) "reified generics";
1340 List.map cstr_list
(constraint_ ~forbid_this
env),
1343 and type_where_constraints
env locl_cstrl
=
1344 List.map locl_cstrl
(fun (h1
, ck
, h2
) ->
1345 let ty1 = hint ~in_where_clause
:true env h1
in
1346 let ty2 = hint ~in_where_clause
:true env h2
in
1349 and class_use
env x acc
=
1351 | Attributes
_ -> acc
1355 hint ~allow_typedef
:false env h :: acc
1356 | ClassUseAlias
(_, (p, _), _, _) ->
1357 Errors.unsupported_feature
p "Trait use aliasing";
1359 | ClassUsePrecedence
(_, (p, _), _) ->
1360 Errors.unsupported_feature
p "The insteadof keyword";
1362 | XhpAttrUse
_ -> acc
1363 | ClassTraitRequire
_ -> acc
1364 | ClassVars
_ -> acc
1366 | XhpCategory
_ -> acc
1369 | TypeConst
_ -> acc
1371 and xhp_attr_use
env x acc
=
1373 | Attributes
_ -> acc
1377 | ClassUseAlias
_ -> acc
1378 | ClassUsePrecedence
_ -> acc
1380 hint ~allow_typedef
:false env h :: acc
1381 | ClassTraitRequire
_ -> acc
1382 | ClassVars
_ -> acc
1384 | XhpCategory
_ -> acc
1387 | TypeConst
_ -> acc
1389 and xhp_category _env
x acc
=
1391 | Attributes
_ -> acc
1395 | ClassUseAlias
_ -> acc
1396 | ClassUsePrecedence
_ -> acc
1397 | XhpAttrUse
_ -> acc
1398 | ClassTraitRequire
_ -> acc
1399 | ClassVars
_ -> acc
1401 | XhpCategory
(_, cs
) ->
1403 | Some
_ -> Errors.multiple_xhp_category
(fst
(List.hd_exn cs
)); acc
1407 | TypeConst
_ -> acc
1409 and class_require
env c_kind
x acc
=
1411 | Attributes
_ -> acc
1415 | ClassUseAlias
_ -> acc
1416 | ClassUsePrecedence
_ -> acc
1417 | XhpAttrUse
_ -> acc
1418 | ClassTraitRequire
(MustExtend
, h)
1419 when c_kind
<> Ast.Ctrait
&& c_kind
<> Ast.Cinterface
->
1420 let () = Errors.invalid_req_extends
(fst
h) in
1422 | ClassTraitRequire
(MustExtend
, h) ->
1423 let acc_impls, acc_exts
= acc
in
1424 (acc_impls, hint ~allow_typedef
:false env h :: acc_exts
)
1425 | ClassTraitRequire
(MustImplement
, h) when c_kind
<> Ast.Ctrait
->
1426 let () = Errors.invalid_req_implements
(fst
h) in
1428 | ClassTraitRequire
(MustImplement
, h) ->
1429 let acc_impls, acc_exts
= acc
in
1430 (hint ~allow_typedef
:false env h :: acc_impls, acc_exts
)
1431 | ClassVars
_ -> acc
1433 | XhpCategory
_ -> acc
1436 | TypeConst
_ -> acc
1438 and constructor env acc
= function
1439 | Attributes
_ -> acc
1443 | ClassUseAlias
_ -> acc
1444 | ClassUsePrecedence
_ -> acc
1445 | XhpAttrUse
_ -> acc
1446 | ClassTraitRequire
_ -> acc
1447 | ClassVars
_ -> acc
1449 | XhpCategory
_ -> acc
1451 | Method
({ m_name
= (p, name); _ } as m
)
1452 when name = SN.Members.__construct
->
1455 let curr_class_kind =
1456 match (fst
env).current_cls
with
1457 | Some
(_, kind
) -> kind
1458 | None
-> failwith
"current class must be set for methods" in
1459 let params_have_visibility = List.exists m
.m_params
1460 ~
f:(fun p -> p.param_modifier
<> None
) in
1461 begin match curr_class_kind with
1463 | Ctrait
when params_have_visibility ->
1464 Errors.trait_interface_constructor_promo
p
1467 Some
(method_
(fst
env) m
)
1468 | Some
_ -> Errors.method_name_already_bound
p name; acc
)
1470 | TypeConst
_ -> acc
1472 and class_const
env x acc
=
1474 | Attributes
_ -> acc
1475 | Const
(h, l
) -> const_defl
h env l
@ acc
1476 | AbsConst
(h, x) -> abs_const_def
env h x :: acc
1478 | ClassUseAlias
_ -> acc
1479 | ClassUsePrecedence
_ -> acc
1480 | XhpAttrUse
_ -> acc
1481 | ClassTraitRequire
_ -> acc
1482 | ClassVars
_ -> acc
1484 | XhpCategory
_ -> acc
1487 | TypeConst
_ -> acc
1489 and class_prop_static
env x acc
=
1491 | Attributes
_ -> acc
1493 | ClassUseAlias
_ -> acc
1494 | ClassUsePrecedence
_ -> acc
1495 | XhpAttrUse
_ -> acc
1496 | ClassTraitRequire
_ -> acc
1500 { cv_kinds
= kl
; cv_hint
= h; cv_names
= cvl
; cv_user_attributes
= ua
; _ }
1501 when List.mem kl Static ~equal
:(=) ->
1502 (* Static variables are shared for all classes in the hierarchy.
1503 * This makes the 'this' type completely unsafe as a type for a
1504 * static variable. See test/typecheck/this_tparam_static.php as
1505 * an example of what can occur.
1507 let h = Option.map
h (hint ~forbid_this
:true env) in
1508 let attrs = user_attributes
env ua
in
1509 let cvl = List.map
cvl (fun cv ->
1510 let cv = class_prop_
env cv in
1511 let cv = fill_prop kl
h cv in
1512 { cv with N.cv_user_attributes
= attrs }
1515 | ClassVars
_ -> acc
1517 | XhpCategory
_ -> acc
1520 | TypeConst
_ -> acc
1522 and class_prop
env ?
(const = None
) x acc
=
1524 | Attributes
_ -> acc
1526 | ClassUseAlias
_ -> acc
1527 | ClassUsePrecedence
_ -> acc
1528 | XhpAttrUse
_ -> acc
1529 | ClassTraitRequire
_ -> acc
1532 | ClassVars
{ cv_kinds
; cv_hint
; cv_names
; cv_user_attributes
; _ }
1533 when not
(List.mem cv_kinds Static ~equal
:(=)) ->
1534 let h = Option.map cv_hint
(hint env) in
1535 let cvl = List.map cv_names
(class_prop_
env) in
1536 let cvl = List.map
cvl (fill_prop cv_kinds
h) in
1537 let attrs = user_attributes
env cv_user_attributes
in
1538 (* if class is __Const, make all member fields __Const *)
1539 let attrs = match const with
1540 | Some c
-> if not
(Attributes.mem SN.UserAttributes.uaConst
attrs)
1541 then c
:: attrs else attrs
1543 let cvl = List.map
cvl (fun cv -> { cv with N.cv_user_attributes
= attrs}) in
1545 | ClassVars
_ -> acc
1546 | XhpAttr
(h, cv, is_required
, maybe_enum
) ->
1547 (xhp_attribute_decl
env h cv is_required maybe_enum
) :: acc
1548 | XhpCategory
_ -> acc
1551 | TypeConst
_ -> acc
1553 and class_static_method
env x acc
=
1555 | Attributes
_ -> acc
1557 | ClassUseAlias
_ -> acc
1558 | ClassUsePrecedence
_ -> acc
1559 | XhpAttrUse
_ -> acc
1560 | ClassTraitRequire
_ -> acc
1563 | ClassVars
_ -> acc
1565 | XhpCategory
_ -> acc
1567 | Method m
when snd m
.m_name
= SN.Members.__construct
-> acc
1568 | Method m
when List.mem m
.m_kind Static ~equal
:(=) -> method_
(fst
env) m
:: acc
1570 | TypeConst
_ -> acc
1572 and class_method
env x acc
=
1574 | Attributes
_ -> acc
1576 | ClassUseAlias
_ -> acc
1577 | ClassUsePrecedence
_ -> acc
1578 | XhpAttrUse
_ -> acc
1579 | ClassTraitRequire
_ -> acc
1582 | ClassVars
_ -> acc
1584 | XhpCategory
_ -> acc
1586 | Method m
when snd m
.m_name
= SN.Members.__construct
-> acc
1587 | Method m
when not
(List.mem m
.m_kind Static ~equal
:(=)) ->(
1588 match (m
.m_name
, m
.m_params
) with
1589 | ( (m_pos
, m_name
), _::_) when m_name
= SN.Members.__clone
->
1590 Errors.clone_too_many_arguments m_pos
; acc
1591 | _ -> let genv = fst
env in method_
genv m
:: acc
1594 | TypeConst
_ -> acc
1596 and class_typeconst
env x acc
=
1598 | Attributes
_ -> acc
1602 | ClassUseAlias
_ -> acc
1603 | ClassUsePrecedence
_ -> acc
1604 | XhpAttrUse
_ -> acc
1605 | ClassTraitRequire
_ -> acc
1606 | ClassVars
_ -> acc
1608 | XhpCategory
_ -> acc
1611 | TypeConst t
-> typeconst
env t
:: acc
1613 and check_constant_expr
env (pos, e
) =
1615 | Unsafeexpr
_ | Id
_ | Null
| True
| False
| Int
_
1616 | Float
_ | String
_ -> ()
1617 | Class_const
((_, cls
), _)
1618 when (match cls
with Id
(_, "static") -> false | _ -> true) -> ()
1619 | Unop
((Uplus
| Uminus
| Utild
| Unot
), e
) -> check_constant_expr
env e
1620 | Binop
(op
, e1
, e2) ->
1621 (* Only assignment is invalid *)
1623 | Eq
_ -> Errors.illegal_constant
pos
1625 check_constant_expr
env e1
;
1626 check_constant_expr
env e2)
1627 | Eif
(e1
, e2, e3
) ->
1628 check_constant_expr
env e1
;
1629 Option.iter
e2 (check_constant_expr
env);
1630 check_constant_expr
env e3
1631 | Array l
-> List.iter l
(check_afield_constant_expr
env)
1632 | Darray l
-> List.iter l
(fun (e1
, e2) ->
1633 check_constant_expr
env e1
;
1634 check_constant_expr
env e2)
1635 | Varray l
-> List.iter l
(check_constant_expr
env)
1637 (* Only check the values because shape field names are always legal *)
1638 List.iter fdl
(fun (_, e
) -> check_constant_expr
env e
)
1639 | Call
((_, Id
(_, cn
)), _, el
, uel
) when cn
= SN.SpecialFunctions.tuple
->
1640 (* Tuples are not really function calls, they are just parsed that way*)
1641 arg_unpack_unexpected uel
;
1642 List.iter el
(check_constant_expr
env)
1643 | Collection
(id, l
) ->
1644 let p, cn
= NS.elaborate_id
((fst
env).namespace
) NS.ElaborateClass
id in
1645 (* Only vec/keyset/dict are allowed because they are value types *)
1648 cn
= SN.Collections.cVec
1649 || cn
= SN.Collections.cKeyset
1650 || cn
= SN.Collections.cDict
->
1651 List.iter l
(check_afield_constant_expr
env)
1652 | _ -> Errors.illegal_constant
p)
1653 | _ -> Errors.illegal_constant
pos
1655 and check_afield_constant_expr
env = function
1656 | AFvalue e
-> check_constant_expr
env e
1657 | AFkvalue
(e1
, e2) ->
1658 check_constant_expr
env e1
;
1659 check_constant_expr
env e2
1661 and constant_expr
env e
=
1662 let valid_constant_expression = Errors.try_with_error
begin fun () ->
1663 check_constant_expr
env e
;
1665 end (fun () -> false) in
1666 if valid_constant_expression then expr
env e
else fst e
, N.Any
1668 and const_defl
h env l
= List.map l
(const_def
h env)
1669 and const_def
h env (x, e
) =
1670 Env.bind_class_const env x;
1671 let h = Option.map
h (hint env) in
1672 h, x, Some
(constant_expr
env e
)
1674 and abs_const_def
env h x =
1675 Env.bind_class_const env x;
1676 let h = Option.map
h (hint env) in
1679 and class_prop_
env (_, x, e
) =
1680 Env.bind_prop env x;
1681 let e = Option.map
e (expr
env) in
1682 (* If the user has not provided a value, we initialize the member variable
1683 * ourselves to a value of type Tany. Classes might inherit from our decl
1684 * mode class that are themselves not in decl, and there's no way to figure
1685 * out what variables are initialized in a decl class without typechecking
1686 * its initalizers and constructor, which we don't want to do, so just
1687 * assume we're covered. *)
1689 if (fst
env).in_mode
= FileInfo.Mdecl
&& e = None
1690 then Some
(fst
x, N.Any
)
1693 let is_xhp = try ((String.sub
(snd
x) 0 1) = ":") with Invalid_argument
_ -> false
1695 { N.cv_final
= false;
1696 N.cv_is_xhp
= is_xhp;
1697 N.cv_visibility
= N.Public
;
1701 N.cv_user_attributes
= [];
1704 and fill_prop kl ty
x =
1705 let x = { x with N.cv_type
= ty
} in
1706 List.fold_left kl ~init
:x ~
f:begin fun x k
->
1707 (* There is no field Static, they are dissociated earlier.
1708 An abstract class variable doesn't make sense.
1711 | Final
-> { x with N.cv_final
= true }
1714 | Private
-> { x with N.cv_visibility
= N.Private
}
1715 | Public
-> { x with N.cv_visibility
= N.Public
}
1716 | Protected
-> { x with N.cv_visibility
= N.Protected
}
1719 and typeconst
env t
=
1720 (* We use the same namespace as constants within the class so we cannot have
1721 * a const and type const with the same name
1723 Env.bind_class_const env t
.tconst_name
;
1724 let constr = Option.map t
.tconst_constraint
(hint env) in
1726 match t
.tconst_type
with
1727 | None
when not t
.tconst_abstract
->
1728 Errors.not_abstract_without_typeconst t
.tconst_name
;
1730 | Some _h
when t
.tconst_abstract
->
1731 Errors.abstract_with_typeconst t
.tconst_name
;
1735 let type_ = Option.map
hint_ (hint env) in
1736 N.({ c_tconst_name
= t
.tconst_name
;
1737 c_tconst_constraint
= constr;
1738 c_tconst_type
= type_;
1741 and func_body_had_unsafe
env = Env.has_unsafe env
1743 and method_
genv m
=
1744 let genv = extend_params
genv m
.m_tparams
in
1745 let env = genv, Env.empty_local UBMErr
in
1746 (* Cannot use 'this' if it is a public instance method *)
1747 let variadicity, paraml
= fun_paraml
env m
.m_params
in
1748 let contains_visibility = List.exists m
.m_kind ~
f:(
1755 if not
contains_visibility then
1756 Errors.method_needs_visibility
(fst m
.m_name
);
1757 let acc = false, false, N.Public
in
1758 let final, abs
, vis
= List.fold_left ~
f:kind ~init
:acc m
.m_kind
in
1759 List.iter m
.m_tparams
check_constraint;
1760 let tparam_l = type_paraml
env m
.m_tparams
in
1761 let where_constraints = type_where_constraints
env m
.m_constrs
in
1762 let ret = Option.map m
.m_ret
(hint ~allow_retonly
:true env) in
1763 let f_kind = m
.m_fun_kind
in
1764 let body = (match genv.in_mode
with
1765 | FileInfo.Mdecl
| FileInfo.Mphp
->
1770 | FileInfo.Mstrict
| FileInfo.Mpartial
| FileInfo.Mexperimental
->
1772 N.fub_ast
= m
.m_body
;
1773 fub_tparams
= m
.m_tparams
;
1774 fub_namespace
= genv.namespace
;
1777 let attrs = user_attributes
env m
.m_user_attributes
in
1778 { N.m_annotation
= () ;
1780 N.m_visibility
= vis
;
1781 N.m_abstract
= abs
;
1782 N.m_name
= m
.Ast.m_name
;
1783 N.m_tparams
= tparam_l ;
1784 N.m_where_constraints
= where_constraints ;
1785 N.m_params
= paraml
;
1787 N.m_fun_kind
= f_kind ;
1789 N.m_variadic
= variadicity ;
1790 N.m_user_attributes
= attrs;
1791 N.m_ret_by_ref
= m
.m_ret_by_ref
;
1792 N.m_external
= m
.m_external
;
1795 and kind
(final, abs
, vis
) = function
1796 | Final
-> true, abs
, vis
1797 | Static
-> final, abs
, vis
1798 | Abstract
-> final, true, vis
1799 | Private
-> final, abs
, N.Private
1800 | Public
-> final, abs
, N.Public
1801 | Protected
-> final, abs
, N.Protected
1803 and fun_paraml
env l
=
1804 let _names = List.fold_left ~
f:check_repetition ~init
:SSet.empty l
in
1805 let variadicity, l
= determine_variadicity
env l
in
1806 variadicity, List.map l
(fun_param
env)
1808 and determine_variadicity
env l
=
1810 | [] -> N.FVnonVariadic
, []
1812 match x.param_is_variadic
, x.param_id
with
1813 | false, _ -> N.FVnonVariadic
, [x]
1814 (* NOTE: variadic params are removed from the list *)
1815 | true, (p, "...") -> N.FVellipsis
p, []
1816 | true, _ -> N.FVvariadicArg
(fun_param
env x), []
1819 let variadicity, rl
= determine_variadicity
env rl
in
1820 variadicity, x :: rl
1822 and fun_param
env param
=
1823 let p, name = param
.param_id
in
1824 let ident = Local_id.get
name in
1825 Env.add_lvar env param
.param_id
(p, ident);
1826 let ty = Option.map param
.param_hint
(hint env) in
1827 let eopt = Option.map param
.param_expr
(expr
env) in
1828 let _ = match (fst
env).in_mode
with
1829 | FileInfo.Mstrict
when param
.param_is_reference
->
1830 Errors.reference_in_strict_mode
p;
1832 { N.param_annotation
= p;
1834 param_is_reference
= param
.param_is_reference
;
1835 param_is_variadic
= param
.param_is_variadic
;
1839 param_callconv
= param
.param_callconv
;
1840 param_user_attributes
= user_attributes
env param
.param_user_attributes
;
1843 and make_constraints paraml
=
1844 List.fold_right paraml ~init
:SMap.empty
1845 ~
f:begin fun (_, (_, x), cstr_list
, _) acc ->
1846 SMap.add
x cstr_list
acc
1849 and extend_params
genv paraml
=
1850 let params = List.fold_right paraml ~init
:genv.type_params
1851 ~
f:begin fun (_, (_, x), cstr_list
, _) acc ->
1852 SMap.add
x cstr_list
acc
1854 { genv with type_params
= params }
1857 let tparams = make_constraints
f.f_tparams
in
1858 let genv = Env.make_fun_decl_genv nenv
tparams f in
1859 let lenv = Env.empty_local UBMErr
in
1860 let env = genv, lenv in
1861 let where_constraints = type_where_constraints
env f.f_constrs
in
1862 let h = Option.map
f.f_ret
(hint ~allow_retonly
:true env) in
1863 let variadicity, paraml
= fun_paraml
env f.f_params
in
1864 let x = Env.fun_id env f.f_name
in
1865 List.iter
f.f_tparams
check_constraint;
1866 let f_tparams = type_paraml
env f.f_tparams in
1867 let f_kind = f.f_fun_kind
in
1868 let body = match genv.in_mode
with
1869 | FileInfo.Mdecl
| FileInfo.Mphp
->
1874 | FileInfo.Mstrict
| FileInfo.Mpartial
| FileInfo.Mexperimental
->
1876 N.fub_ast
= f.f_body
;
1877 fub_tparams
= f.f_tparams;
1878 fub_namespace
= f.f_namespace
;
1882 N.f_annotation
= ();
1886 f_tparams = f_tparams;
1887 f_where_constraints
= where_constraints;
1890 f_fun_kind
= f_kind;
1891 f_variadic
= variadicity;
1892 f_user_attributes
= user_attributes
env f.f_user_attributes
;
1893 f_ret_by_ref
= f.f_ret_by_ref
;
1894 f_external
= f.f_external
;
1898 and get_using_vars
e =
1900 | Expr_list using_clauses
->
1901 List.concat_map using_clauses get_using_vars
1902 (* Simple assignment to local of form `$lvar = e` *)
1903 | Binop
(Ast.Eq None
, (_, Lvar
lvar), _) ->
1905 (* Arbitrary expression. This will be assigned to a temporary *)
1909 and stmt
env (p, st_
as st
) =
1911 | Let
(x, h, e) -> let_stmt
env x h e
1912 | Block
_ -> assert false
1913 | Unsafe
-> assert false
1914 | Fallthrough
-> N.Fallthrough
1916 | Markup
(_, None
) -> N.Noop
(* ignore markup *)
1917 | Markup
(_, Some
e) -> N.Expr
(expr
env e)
1918 | Break level_opt
->
1919 check_break_continue_level p level_opt
;
1921 | Continue level_opt
->
1922 check_break_continue_level p level_opt
;
1924 | Throw
e -> let terminal = not
(fst
env).in_try
in
1925 N.Throw
(terminal, expr
env e)
1926 | Return
e -> N.Return
(p, oexpr
env e)
1927 | GotoLabel label
-> name_goto_label
env label
1928 | Goto label
-> name_goto
env label
1929 | Static_var el
-> N.Static_var
(static_varl
env el
)
1930 | Global_var el
-> N.Global_var
(global_varl
env el
)
1931 | If
(e, b1
, b2
) -> if_stmt
env st
e b1 b2
1932 | Do
(b
, e) -> do_stmt
env b
e
1933 | While
(e, b
) -> while_stmt
env e b
1934 | Declare
(is_block
, e, b
) -> declare_stmt
env is_block
e b
1935 | Using s
-> using_stmt
env s
.us_has_await s
.us_expr s
.us_block
1936 | For
(st1
, e, st2
, b
) -> for_stmt
env st1
e st2 b
1937 | Switch
(e, cl
) -> switch_stmt
env st
e cl
1938 | Foreach
(e, aw
, ae
, b
)-> foreach_stmt
env e aw ae b
1939 | Try
(b
, cl
, fb
) -> try_stmt
env st b cl fb
1941 Errors.experimental_feature
p "inlined definitions"; N.Expr
(p, N.Any
)
1942 | Expr
(cp
, Call
((p, Id
(fp
, fn
)), hl, el
, uel
))
1943 when fn
= SN.SpecialFunctions.invariant
->
1944 (* invariant is subject to a source-code transform in the HHVM
1945 * runtime: the arguments to invariant are lazily evaluated only in
1946 * the case in which the invariant condition does not hold. So:
1948 * invariant_violation(<condition>, <format>, <format_args...>)
1950 * ... is rewritten as:
1952 * if (!<condition>) {
1953 * invariant_violation(<format>, <format_args...>);
1958 Errors.naming_too_few_arguments
p;
1960 | (cond_p
, cond
) :: el
->
1961 let violation = (cp
, Call
1962 ((p, Id
(fp
, "\\"^
SN.SpecialFunctions.invariant_violation
)), hl, el
,
1964 if cond
<> False
then
1965 let b1, b2
= [p, Expr
violation], [p, Noop
] in
1966 let cond = cond_p
, Unop
(Unot
, (cond_p
, cond)) in
1967 if_stmt
env st
cond b1 b2
1968 else (* a false <condition> means unconditional invariant_violation *)
1969 N.Expr
(expr
env violation)
1971 | Expr
e -> N.Expr
(expr
env e)
1973 and let_stmt
env x h e =
1974 let e = expr
env e in
1975 let h = Option.map
h (hint env) in
1976 let x = Env.new_let_local env x in
1979 and if_stmt
env st
e b1 b2
=
1980 let e = expr
env e in
1981 let nsenv = (fst
env).namespace
in
1982 let _, vars
= GetLocals.stmt
(fst
env).tcopt (nsenv, SMap.empty
) st
in
1983 SMap.iter
(fun x p -> Env.new_pending_lvar env (p, x)) vars
;
1984 let result = Env.scope env (
1986 let all1, b1 = branch
env b1 in
1987 let all2, b2
= branch
env b2
in
1988 Env.extend_all_locals env all2;
1989 Env.extend_all_locals env all1;
1992 SMap.iter
(fun x _ -> Env.promote_pending_lvar env x) vars
;
1995 and do_stmt
env b
e =
1996 (* lexical block of `do` is extended to the expr of loop termination *)
1997 Env.scope_lexical env (fun env ->
1998 let b = block ~new_scope
:false env b in
1999 let e = expr
env e in
2003 and while_stmt
env e b =
2004 let e = expr
env e in
2005 N.While
(e, block
env b)
2007 and declare_stmt _env _is_block
e _b
=
2008 Errors.declare_statement_in_hack
(fst
e);
2009 N.Expr
(fst
e, N.Any
)
2011 (* Scoping is essentially that of do: block is always executed *)
2012 and using_stmt
env has_await
e b =
2013 let vars = get_using_vars
e in
2014 let e = expr
env e in
2015 let b = block ~new_scope
:false env b in
2016 Env.remove_locals env vars;
2017 N.Using
(has_await
, e, b)
2019 and for_stmt
env e1
e2 e3
b =
2020 (* The initialization and condition expression should be in the outer scope,
2021 * as they are always executed. *)
2022 let e1 = expr
env e1 in
2023 let e2 = expr
env e2 in
2026 (* The third expression (iteration step) should have the same scope as the
2027 * block, as it is not always executed. *)
2028 let b = block ~new_scope
:false env b in
2029 let e3 = expr
env e3 in
2030 N.For
(e1, e2, e3, b)
2033 and switch_stmt
env st
e cl
=
2034 let e = expr
env e in
2035 let nsenv = (fst
env).namespace
in
2036 let _, vars = GetLocals.stmt
(fst
env).tcopt (nsenv, SMap.empty
) st
in
2037 SMap.iter
(fun x p -> Env.new_pending_lvar env (p, x)) vars;
2038 let result = Env.scope env begin fun env ->
2039 let all_locals_l, cl
= casel
env cl
in
2040 List.iter
all_locals_l (Env.extend_all_locals env);
2043 SMap.iter
(fun x _ -> Env.promote_pending_lvar env x) vars;
2046 and foreach_stmt
env e aw ae
b =
2047 let e = expr
env e in
2048 Env.scope env begin fun env ->
2049 let ae = as_expr
env aw
ae in
2050 let b = block
env b in
2051 N.Foreach
(e, ae, b)
2054 and as_expr
env aw
=
2055 let handle_v ev
= match ev
with
2056 | p, Id
x when (fst
env).in_mode
= FileInfo.Mexperimental
->
2057 let x = Env.new_let_local env x in
2058 let ev = (p, N.ImmutableVar
x) in
2061 Errors.expected_variable
p;
2062 p, N.Lvar
(Env.new_lvar env (p, "__internal_placeholder"))
2064 let nsenv = (fst
env).namespace
in
2066 GetLocals.lvalue
(fst
env).tcopt (nsenv, SMap.empty
) ev in
2067 SMap.iter
(fun x p -> ignore
(Env.new_lvar env (p, x))) vars;
2068 let ev = expr
env ev in
2071 let handle_k ek
= match ek
with
2073 p, N.Lvar
(Env.new_lvar env x)
2074 | p, Id
x when (fst
env).in_mode
= FileInfo.Mexperimental
->
2075 p, N.ImmutableVar
(Env.new_let_local env x)
2077 Errors.expected_variable
p;
2078 p, N.Lvar
(Env.new_lvar env (p, "__internal_placeholder"))
2082 let ev = handle_v ev in
2085 | Some
p -> N.Await_as_v
(p, ev)
2088 let k = handle_k k in
2089 let ev = handle_v ev in
2091 | None
-> N.As_kv
(k, ev)
2092 | Some
p -> N.Await_as_kv
(p, k, ev)
2096 and try_stmt
env st
b cl fb
=
2097 let nsenv = (fst
env).namespace
in
2099 GetLocals.stmt
(fst
env).tcopt (nsenv, SMap.empty
) st
in
2100 SMap.iter
(fun x p -> Env.new_pending_lvar env (p, x)) vars;
2101 let result = Env.scope env (
2103 let genv, lenv = env in
2104 (* isolate finally from the rest of the try-catch: if the first
2105 * statement of the try is an uncaught exception, finally will
2106 * still be executed *)
2107 let _all_finally, fb
= branch
({genv with in_finally
= true}, lenv) fb
in
2108 let all_locals_b, b = branch
({genv with in_try
= true}, lenv) b in
2109 let all_locals_cl, cl
= catchl
env cl
in
2110 List.iter
all_locals_cl (Env.extend_all_locals env);
2111 Env.extend_all_locals env all_locals_b;
2114 SMap.iter
(fun x _ -> Env.promote_pending_lvar env x) vars;
2117 and stmt_list ?after_unsafe stl
env =
2118 let stmt_list = stmt_list ?after_unsafe
in
2121 | (_, Unsafe
) :: rest
->
2122 Env.set_unsafe env true;
2123 let st = Errors.ignore_
(fun () -> N.Unsafe_block
(stmt_list rest
env)) in
2124 st :: Option.to_list after_unsafe
2125 | (_, Block
b) :: rest
->
2126 (* Add lexical scope for block scoped let variables *)
2127 let b = Env.scope_lexical env (stmt_list b) in
2128 let rest = stmt_list rest env in
2131 let x = stmt
env x in
2132 let rest = stmt_list rest env in
2135 and block ?
(new_scope
=true) env stl
=
2137 then Env.scope env (stmt_list stl
)
2138 else stmt_list stl
env
2140 and branch ?after_unsafe
env stmt_l
=
2141 Env.scope_all env (stmt_list ?after_unsafe stmt_l
)
2144 * Names a goto label.
2146 * The goto label is added to the local labels if it is not already there.
2147 * Otherwise, an error is produced.
2149 * An error is produced if this is called within a finally block.
2152 ({ in_finally
; _ }, _ as env) (label_pos
, label_name
as label
) =
2153 (match Env.goto_label env label_name
with
2154 | Some original_declaration_pos
->
2155 Errors.goto_label_already_defined
2158 original_declaration_pos
2159 | None
-> Env.new_goto_label env label
);
2161 Errors.goto_label_defined_in_finally label_pos
;
2165 * Names a goto target.
2167 * The goto statement's target label is added to the local goto targets.
2169 * An error is produced if this is called within a finally block.
2172 ({ in_finally
; _ }, _ as env) (label_pos
, _ as label
) =
2173 Env.new_goto_target env label
;
2174 if in_finally
then Errors.goto_invoked_in_finally label_pos
;
2177 and static_varl
env l
= List.map l
(static_var
env)
2178 and static_var
env = function
2179 | p, Lvar
_ as lv
-> expr
env (p, Binop
(Eq None
, lv
, (p, Null
)))
2182 and global_varl
env l
= List.map l
(global_var
env)
2183 and global_var
env = function
2184 | p, Lvar
_ as lv
-> expr
env (p, Binop
(Eq None
, lv
, (p, Null
)))
2187 and expr_obj_get_name
env = function
2188 | p, Id
x -> p, N.Id
x
2192 and exprl
env l
= List.map l
(expr
env)
2193 and oexpr
env e = Option.map
e (expr
env)
2194 and expr
env (p, e) = p, expr_
env p e
2195 and expr_
env p = function
2197 let tcopt = (fst
env).tcopt in
2198 let array_literals_disallowed =
2199 TypecheckerOptions.disallow_array_literal
tcopt in
2200 if array_literals_disallowed
2201 then Errors.array_literals_disallowed p;
2202 N.Array
(List.map l
(afield
env))
2203 | ParenthesizedExpr
(p, e) -> expr_
env p e
2205 N.Darray
(List.map l
(fun (e1, e2) -> expr
env e1, expr
env e2))
2206 | Varray l
-> N.Varray
(List.map l
(expr
env))
2207 | Collection
(id, l
) -> begin
2208 let p, cn
= NS.elaborate_id
((fst
env).namespace
) NS.ElaborateClass
id in
2210 | x when N.is_vc_kind
x ->
2211 N.ValCollection
((N.get_vc_kind cn
),
2212 (List.map l
(afield_value
env cn
)))
2213 | x when N.is_kvc_kind
x ->
2214 N.KeyValCollection
((N.get_kvc_kind cn
),
2215 (List.map l
(afield_kvalue
env cn
)))
2216 | x when x = SN.Collections.cPair
->
2219 Errors.naming_too_few_arguments
p;
2222 let pn = SN.Collections.cPair
in
2223 N.Pair
(afield_value
env pn e1, afield_value
env pn e2)
2225 Errors.naming_too_many_arguments
p;
2229 Errors.expected_collection
p cn
;
2232 | Clone
e -> N.Clone
(expr
env e)
2237 | Float s
-> N.Float s
2238 | String s
-> N.String s
2240 (* treat execution operator similar to interpolated strings *)
2241 | Execution_operator idl
-> N.String2
(string2
env idl
)
2242 | PrefixedString
(n
, e) -> N.PrefixedString
(n
, (expr
env e))
2244 (** TODO: Emit proper error messages T28473207. Currently the error message
2245 * emitted has reason Naming[2049] unbound name for global constant *)
2246 begin match Env.let_local env x with
2247 | Some
x -> N.ImmutableVar
x
2248 | None
-> N.Id
(Env.global_const env x)
2250 | Lvar
(_, x) when x = SN.SpecialIdents.this
-> N.This
2251 | Lvar
(_, x) when x = SN.SpecialIdents.dollardollar
->
2252 N.Dollardollar
(Env.found_dollardollar env p)
2253 | Lvar
(pos, x) when x = SN.SpecialIdents.placeholder
->
2256 N.Lvar
(Env.lvar env x)
2257 | Obj_get
(e1, e2, nullsafe
) ->
2258 (* If we encounter Obj_get(_,_,true) by itself, then it means "?->"
2259 is being used for instance property access; see the case below for
2260 handling nullsafe instance method calls to see how this works *)
2261 let nullsafe = match nullsafe with
2262 | OG_nullsafe
-> N.OG_nullsafe
2263 | OG_nullthrows
-> N.OG_nullthrows
2265 N.Obj_get
(expr
env e1, expr_obj_get_name
env e2, nullsafe)
2266 | Array_get
((p, Lvar
x), None
) ->
2267 let id = p, N.Lvar
(Env.lvar env x) in
2268 N.Array_get
(id, None
)
2269 | Array_get
(e1, e2) -> N.Array_get
(expr
env e1, oexpr
env e2)
2270 | Class_get
((_, (Id x1
| Lvar x1
)), (_, (Id x2
| Lvar x2
))) ->
2271 N.Class_get
(make_class_id
env x1
[], x2
)
2272 | Class_get
(x1
, x2
) ->
2273 ensure_name_not_dynamic env x1
2274 Errors.dynamic_class_name_in_strict_mode
;
2275 ensure_name_not_dynamic env x2
2276 Errors.dynamic_class_property_name_in_strict_mode
;
2278 | Class_const
((_, Id x1
), x2
)
2279 | Class_const
((_, Lvar x1
), x2
) ->
2280 let (genv, _) = env in
2281 let (_, name) = NS.elaborate_id
genv.namespace
NS.ElaborateClass x1
in
2282 begin match Naming_heap.TypeIdHeap.get
name with
2283 | Some
(_, `Typedef
) when (snd x2
) = "class" ->
2284 N.Typename
(Env.type_name env x1 ~allow_typedef
:true)
2286 N.Class_const
(make_class_id
env x1
[], x2
)
2289 (* TODO: report error in strict mode *)
2291 | Call
((_, Id
(p, pseudo_func
)), hl, el
, uel
)
2292 when pseudo_func
= SN.SpecialFunctions.echo
->
2293 arg_unpack_unexpected uel
;
2294 N.Call
(N.Cnormal
, (p, N.Id
(p, pseudo_func
)), hintl_funcall
env p hl, exprl
env el
, [])
2295 | Call
((p, Id
(_, cn
)), hl, el
, uel
)
2296 when cn
= SN.SpecialFunctions.call_user_func
->
2297 arg_unpack_unexpected uel
;
2299 | [] -> Errors.naming_too_few_arguments
p; N.Any
2300 | f :: el
-> N.Call
(N.Cuser_func
, expr
env f, hintl_funcall
env p hl, exprl
env el
, [])
2302 | Call
((p, Id
(_, cn
)), _, el
, uel
) when cn
= SN.SpecialFunctions.fun_
->
2303 arg_unpack_unexpected uel
;
2304 let (genv, _) = env in
2306 | [] -> Errors.naming_too_few_arguments
p; N.Any
2307 | [_, String s
] when String.contains s '
:'
->
2308 Errors.illegal_meth_fun
p; N.Any
2309 | [_, String s
] when genv.in_ppl && SN.PPLFunctions.is_reserved s
->
2310 Errors.ppl_meth_pointer
p ("fun("^s^
")"); N.Any
2312 (* Functions referenced by fun() are always fully-qualified. *)
2313 let x = if x <> "" && x.[0] <> '
\\'
then "\\" ^
x else x in
2314 N.Fun_id
(Env.fun_id env (p, x))
2316 Errors.illegal_fun
p;
2318 | _ -> Errors.naming_too_many_arguments
p; N.Any
2320 | Call
((p, Id
(_, cn
)), _, el
, uel
)
2321 when cn
= SN.SpecialFunctions.inst_meth
->
2322 arg_unpack_unexpected uel
;
2324 | [] -> Errors.naming_too_few_arguments
p; N.Any
2325 | [_] -> Errors.naming_too_few_arguments
p; N.Any
2326 | instance
::(p, String meth
)::[] ->
2327 N.Method_id
(expr
env instance
, (p, meth
))
2328 | (p, _)::(_)::[] ->
2329 Errors.illegal_inst_meth
p;
2331 | _ -> Errors.naming_too_many_arguments
p; N.Any
2333 | Call
((p, Id
(_, cn
)), _, el
, uel
)
2334 when cn
= SN.SpecialFunctions.meth_caller
->
2335 arg_unpack_unexpected uel
;
2337 | [] -> Errors.naming_too_few_arguments
p; N.Any
2338 | [_] -> Errors.naming_too_few_arguments
p; N.Any
2340 (match (expr
env e1), (expr
env e2) with
2341 | (pc
, N.String cl
), (pm
, N.String meth
) ->
2342 N.Method_caller
(Env.type_name env (pc
, cl
) ~allow_typedef
:false, (pm
, meth
))
2343 | (_, N.Class_const
((_, N.CI
(cl
, _)), (_, mem))), (pm
, N.String meth
)
2344 when mem = SN.Members.mClass
->
2345 N.Method_caller
(Env.type_name env cl ~allow_typedef
:false, (pm
, meth
))
2347 Errors.illegal_meth_caller
p;
2350 | _ -> Errors.naming_too_many_arguments
p; N.Any
2352 | Call
((p, Id
(_, cn
)), _, el
, uel
)
2353 when cn
= SN.SpecialFunctions.class_meth
->
2354 arg_unpack_unexpected uel
;
2356 | [] -> Errors.naming_too_few_arguments
p; N.Any
2357 | [_] -> Errors.naming_too_few_arguments
p; N.Any
2359 (match (expr
env e1), (expr
env e2) with
2360 | (pc
, N.String cl
), (pm
, N.String meth
) ->
2361 N.Smethod_id
(Env.type_name env (pc
, cl
) ~allow_typedef
:false, (pm
, meth
))
2362 | (_, N.Id
(_, const)), (pm
, N.String meth
)
2363 when const = SN.PseudoConsts.g__CLASS__
->
2364 (* All of these that use current_cls aren't quite correct
2365 * inside a trait, as the class should be the using class.
2366 * It's sufficient for typechecking purposes (we require
2367 * subclass to be compatible with the trait member/method
2370 (match (fst
env).current_cls
with
2371 | Some
(cid
, _) -> N.Smethod_id
(cid
, (pm
, meth
))
2372 | None
-> Errors.illegal_class_meth
p; N.Any
)
2373 | (_, N.Class_const
((_, N.CI
(cl
, _)), (_, mem))), (pm
, N.String meth
)
2374 when mem = SN.Members.mClass
->
2375 N.Smethod_id
(Env.type_name env cl ~allow_typedef
:false, (pm
, meth
))
2376 | (p, N.Class_const
((_, (N.CIself
|N.CIstatic
)), (_, mem))),
2377 (pm
, N.String meth
) when mem = SN.Members.mClass
->
2378 (match (fst
env).current_cls
with
2379 | Some
(cid
, _) -> N.Smethod_id
(cid
, (pm
, meth
))
2380 | None
-> Errors.illegal_class_meth
p; N.Any
)
2381 | (p, _), (_) -> Errors.illegal_class_meth
p; N.Any
2383 | _ -> Errors.naming_too_many_arguments
p; N.Any
2385 | Call
((p, Id
(_, cn
)), _, el
, uel
) when cn
= SN.SpecialFunctions.assert_
->
2386 arg_unpack_unexpected uel
;
2387 if List.length el
<> 1
2388 then Errors.assert_arity
p;
2389 N.Assert
(N.AE_assert
(
2390 Option.value_map
(List.hd el
) ~default
:(p, N.Any
) ~
f:(expr
env)
2392 | Call
((p, Id
(_, cn
)), _, el
, uel
) when cn
= SN.SpecialFunctions.tuple
->
2393 arg_unpack_unexpected uel
;
2395 | [] -> Errors.naming_too_few_arguments
p; N.Any
2396 | el
-> N.List
(exprl
env el
)
2398 (* sample, factor, observe, condition *)
2399 | Call
((p1
, Id
(p2
, cn
)), hl, el
, uel
)
2400 when Env.in_ppl env && SN.PPLFunctions.is_reserved cn
->
2401 let n_expr = N.Id
(p2
, cn
) in
2402 N.Call
(N.Cnormal
, (p1
, n_expr),
2403 hintl_funcall
env p hl, exprl
env el
, exprl
env uel
)
2404 | Call
((p, Id
f), hl, el
, uel
) ->
2405 begin match Env.let_local env f with
2407 (* Translate into local id *)
2408 let f = (p, N.ImmutableVar
x) in
2409 N.Call
(N.Cnormal
, f, hintl_funcall
env p hl, exprl
env el
, exprl
env uel
)
2411 (* The name is not a local `let` binding *)
2412 let qualified = Env.fun_id env f in
2413 let cn = snd
qualified in
2414 (* The above special cases (fun, inst_meth, meth_caller, class_meth,
2415 * and friends) are magical language constructs, which we should
2416 * check before calling fun_id and looking up the function and doing
2417 * namespace normalization. However, gena, genva, etc are actual
2418 * functions that actually exist, we just need to handle them
2419 * specially here, during naming. Note that most of the function
2420 * special cases, such as idx, are actually handled in typing, and
2421 * don't require naming magic. *)
2422 if cn = SN.FB.fgena
then begin
2423 arg_unpack_unexpected uel
;
2425 | [e] -> N.Special_func
(N.Gena
(expr
env e))
2426 | _ -> Errors.gena_arity
p; N.Any
2428 end else if (cn = SN.FB.fgenva
)
2429 || (cn = SN.HH.asio_va
)
2430 || (cn = SN.HH.lib_tuple_gen
)
2431 || (cn = SN.HH.lib_tuple_from_async
) then begin
2432 arg_unpack_unexpected uel
;
2433 if List.length el
< 1
2434 then (Errors.genva_arity
p; N.Any
)
2435 else N.Special_func
(N.Genva
(exprl
env el
))
2436 end else if cn = SN.FB.fgen_array_rec
then begin
2437 arg_unpack_unexpected uel
;
2439 | [e] -> N.Special_func
(N.Gen_array_rec
(expr
env e))
2440 | _ -> Errors.gen_array_rec_arity
p; N.Any
2443 N.Call
(N.Cnormal
, (p, N.Id
qualified), hintl_funcall
env p hl,
2444 exprl
env el
, exprl
env uel
)
2446 (* Handle nullsafe instance method calls here. Because Obj_get is used
2447 for both instance property access and instance method calls, we need
2448 to match the entire "Call(Obj_get(..), ..)" pattern here so that we
2449 only match instance method calls *)
2450 | Call
((p, Obj_get
(e1, e2, OG_nullsafe
)), hl, el
, uel
) ->
2453 (p, N.Obj_get
(expr
env e1,
2454 expr_obj_get_name
env e2, N.OG_nullsafe
)),
2455 hintl_funcall
env p hl,
2456 exprl
env el
, exprl
env uel
)
2457 (* Handle all kinds of calls that weren't handled by any of
2459 | Call
(e, hl, el
, uel
) ->
2460 N.Call
(N.Cnormal
, expr
env e,
2461 hintl_funcall
env p hl, exprl
env el
, exprl
env uel
)
2462 | Yield_break
-> N.Yield_break
2463 | Yield
e -> N.Yield
(afield
env e)
2464 | Await
e -> N.Await
(expr
env e)
2465 | Suspend
e -> N.Suspend
(expr
env e)
2466 | List el
-> N.List
(exprl
env el
)
2467 | Expr_list el
-> N.Expr_list
(exprl
env el
)
2469 let (p, x), hl = match ty with
2470 | _, Happly
(id, hl) -> (id, hl)
2471 | _ -> assert false in
2472 let ty = match try_castable_hint ~tp_depth
:1 env p x hl with
2476 | x when x = SN.Typehints.object_cast
->
2477 (* (object) is a valid cast but not a valid type annotation *)
2478 (* FIXME we are not modeling the correct runtime behavior here --
2479 * the runtime result type is an stdClass if the original type is
2480 * primitive. But we should probably just disallow object casts
2483 | x when x = SN.Typehints.void
->
2486 | x when x = SN.Typehints.unset_cast
->
2487 Errors.unset_cast
p;
2490 (* Let's just assume that any other invalid cases are attempts to
2491 * cast to specific objects *)
2492 let h = hint ~allow_typedef
:false env ty in
2493 Errors.object_cast
p x;
2496 N.Cast
(ty, expr
env e2)
2497 | Unop
(uop
, e) -> N.Unop
(uop
, expr
env e)
2498 | Binop
(Eq None
as op
, lv
, e2) ->
2499 if Env.inside_pipe env then
2500 Errors.unimplemented_feature
p "Assignment within pipe expressions";
2501 let e2 = expr
env e2 in
2502 let nsenv = (fst
env).namespace
in
2504 GetLocals.lvalue
(fst
env).tcopt (nsenv, SMap.empty
) lv
in
2505 SMap.iter
(fun x p -> ignore
(Env.new_lvar env (p, x))) vars;
2506 N.Binop
(op
, expr
env lv
, e2)
2507 | Binop
(Eq
_ as bop
, e1, e2) ->
2508 if Env.inside_pipe env then
2509 Errors.unimplemented_feature
p "Assignment within pipe expressions";
2510 let e1 = expr
env e1 in
2511 N.Binop
(bop
, e1, expr
env e2)
2512 | Binop
(bop
, e1, e2) ->
2513 let e1 = expr
env e1 in
2514 N.Binop
(bop
, e1, expr
env e2)
2516 let e1 = expr
env e1 in
2517 let ident, e2 = Env.pipe_scope env
2522 N.Pipe
((p, ident), e1, e2)
2523 | Eif
(e1, e2opt
, e3) ->
2524 (* The order matters here, of course -- e1 can define vars that need to
2525 * be available in e2 and e3. *)
2526 let inject_dollardollar env (p, dd
) =
2527 Env.add_lvar env (p, SN.SpecialIdents.dollardollar
) (p, dd
)
2529 let e1 = expr
env e1 in
2530 let nsenv = (fst
env).namespace
in
2531 let get_lvalues = function e ->
2532 snd
@@ GetLocals.stmt
(fst
env).tcopt (nsenv, SMap.empty
) (p, Expr
e) in
2534 Option.value (Option.map e2opt
get_lvalues) ~default
:SMap.empty
2536 let e3_lvalues = get_lvalues e3 in
2537 let lvalues = smap_inter
e2_lvalues e3_lvalues in
2538 SMap.iter
(fun x p -> Env.new_pending_lvar env (p, x)) lvalues;
2539 let e2opt, e3, dollar_dollar
= Env.scope env (fun env ->
2540 let all2, (e2opt, dollardollar_e2
) = Env.scope_all env (fun env ->
2541 let e2opt = oexpr
env e2opt in
2542 e2opt, Env.get_dollardollar env) in
2543 (* If $$ is found in e2, we inject the $$ in e3 to prevent multiple
2544 * local_id for the same $$ variable *)
2545 let all3, (e3, dollardollar_e3
) = Env.scope_all env (fun env ->
2546 begin match dollardollar_e2
with
2547 | Some dollardollar
-> inject_dollardollar env dollardollar
2550 let e3 = expr
env e3 in
2551 e3, Env.get_dollardollar env) in
2552 Env.extend_all_locals env all2;
2553 Env.extend_all_locals env all3;
2554 e2opt, e3, Option.first_some dollardollar_e2 dollardollar_e3
2556 SMap.iter
(fun x _ -> Env.promote_pending_lvar env x) lvalues;
2557 if Env.inside_pipe env then
2558 (* Similarly, inject the $$ variable to the current environment *)
2559 Option.iter dollar_dollar
(inject_dollardollar env);
2560 N.Eif
(e1, e2opt, e3)
2561 | InstanceOf
(e, (p, Id
x)) ->
2562 let id = match x with
2563 | px
, n
when n
= SN.Classes.cParent
->
2564 if (fst
env).current_cls
= None
then
2565 let () = Errors.parent_outside_class
p in
2566 N.CI
((px
, SN.Classes.cUnknown
), [])
2568 | px
, n
when n
= SN.Classes.cSelf
->
2569 if (fst
env).current_cls
= None
then
2570 let () = Errors.self_outside_class
p in
2571 N.CI
((px
, SN.Classes.cUnknown
), [])
2573 | px
, n
when n
= SN.Classes.cStatic
->
2574 if (fst
env).current_cls
= None
then
2575 let () = Errors.static_outside_class
p in
2576 N.CI
((px
, SN.Classes.cUnknown
), [])
2579 N.CI
(Env.type_name env x ~allow_typedef
:false, [])
2581 N.InstanceOf
(expr
env e, (p, id))
2582 | InstanceOf
(e1, (_,
2583 (Lvar
_ | Obj_get
_ | Class_get
_ | Class_const
_
2584 | Array_get
_ | Call
_) as e2)) ->
2585 N.InstanceOf
(expr
env e1, (fst
e2, N.CIexpr
(expr
env e2)))
2586 | InstanceOf
(_e1
, (p, _)) ->
2587 Errors.invalid_instanceof
p;
2590 let e1 = expr
env e in
2591 let h1 = hint ~allow_wildcard
:true env h in
2594 let e1 = expr
env e in
2595 let h1 = hint ~allow_wildcard
:true env h in
2597 | New
((_, Id
x), hl, el
, uel
)
2598 | New
((_, Lvar
x), hl, el
, uel
) ->
2599 let hl = extract_hintl_from_type_args
env p hl in
2600 N.New
(make_class_id
env x hl,
2603 | New
((p, _e
), hl, el
, uel
) ->
2604 let hl = extract_hintl_from_type_args
env p hl in
2605 if (fst
env).in_mode
= FileInfo.Mstrict
2606 then Errors.dynamic_new_in_strict_mode
p;
2607 N.New
(make_class_id
env (p, SN.Classes.cUnknown
) hl,
2613 let _ = match (fst
env).in_mode
with
2614 | FileInfo.Mstrict
-> List.iter idl ~
f:(function
2615 | (p, _), is_ref
when is_ref
-> Errors.reference_in_strict_mode
p;
2621 ~
f:(fun ((p, x) as id, _) acc ->
2622 if x = SN.SpecialIdents.this
2623 then (Errors.this_as_lexical_variable
p; acc)
2626 let idl'
= List.map
idl (Env.lvar env) in
2627 let env = (fst
env, Env.empty_local UBMErr
) in
2628 List.iter2_exn
idl idl'
(Env.add_lvar env);
2629 let f = expr_lambda
env f in
2632 (* We have to build the capture list while we're finding names in
2633 the closure body---accumulate it in to_capture. *)
2634 (* semantic duplication: The logic here is also used in `uselist_lambda`.
2635 The differences are enough that it does not make sense to refactor
2636 this out for now. *)
2637 let to_capture = ref [] in
2638 let handle_unbound (p, x) =
2639 let cap = Env.lvar env (p, x) in
2640 to_capture := cap :: !to_capture;
2643 let lenv = Env.empty_local @@ UBMFunc
handle_unbound in
2644 (* Extend the current let binding into the scope of lambda *)
2645 Env.copy_let_locals env (fst
env, lenv);
2646 let env = (fst
env, lenv) in
2647 let f = expr_lambda
env f in
2648 (* TODO T28711692: Compute the correct capture list for let variables,
2649 * it does not seem to affect typechecking... *)
2650 N.Efun
(f, !to_capture)
2651 | Xml
(x, al
, el
) ->
2652 N.Xml
(Env.type_name env x ~allow_typedef
:false, attrl
env al
,
2655 N.Shape
begin List.fold_left fdl ~init
:ShapeMap.empty
2656 ~
f:begin fun fdm
(pname
, value) ->
2657 let pos, name = convert_shape_name env pname
in
2658 if ShapeMap.mem name fdm
2659 then Errors.fd_name_already_bound
pos;
2660 ShapeMap.add
name (expr
env value) fdm
2664 N.Unsafe_expr
(Errors.ignore_
(fun () -> expr
env e))
2668 Errors.variable_variables_disallowed
p;
2671 N.Yield_from
(expr
env e)
2676 | Callconv
(kind
, e) ->
2677 N.Callconv
(kind
, expr
env e)
2679 and expr_lambda
env f =
2680 let env = Env.set_ppl env false in
2681 let h = Option.map
f.f_ret
(hint ~allow_retonly
:true env) in
2682 let previous_unsafe = Env.has_unsafe env in
2683 (* save unsafe and yield state *)
2684 Env.set_unsafe env false;
2685 let variadicity, paraml
= fun_paraml
env f.f_params
in
2686 let f_kind = f.f_fun_kind
in
2687 (* The bodies of lambdas go through naming in the containing local
2689 let body_nast = block
env f.f_body
in
2690 let unsafe = func_body_had_unsafe
env in
2691 (* restore unsafe state *)
2692 Env.set_unsafe env previous_unsafe;
2693 let body = N.NamedBody
{
2694 N.fnb_unsafe
= unsafe;
2695 fnb_nast
= body_nast;
2697 N.f_annotation
= ();
2698 f_mode
= (fst
env).in_mode
;
2703 f_where_constraints
= [];
2705 f_fun_kind
= f_kind;
2706 f_variadic
= variadicity;
2707 f_user_attributes
= user_attributes
env f.f_user_attributes
;
2708 f_ret_by_ref
= f.f_ret_by_ref
;
2709 f_external
= f.f_external
;
2712 and make_class_id
env (p, x as cid
) hl =
2715 | x when x = SN.Classes.cParent
->
2716 if (fst
env).current_cls
= None
then
2717 let () = Errors.parent_outside_class
p in
2718 N.CI
((p, SN.Classes.cUnknown
), [])
2720 | x when x = SN.Classes.cSelf
->
2721 if (fst
env).current_cls
= None
then
2722 let () = Errors.self_outside_class
p in
2723 N.CI
((p, SN.Classes.cUnknown
), [])
2725 | x when x = SN.Classes.cStatic
-> if (fst
env).current_cls
= None
then
2726 let () = Errors.static_outside_class
p in
2727 N.CI
((p, SN.Classes.cUnknown
), [])
2729 | x when x = SN.SpecialIdents.this
-> N.CIexpr
(p, N.This
)
2730 | x when x = SN.SpecialIdents.dollardollar
->
2731 (* We won't reach here for "new $$" because the parser creates a
2732 * proper Ast.Dollardollar node, so make_class_id won't be called with
2733 * that node. In fact, the parser creates an Ast.Dollardollar for all
2734 * "$$" except in positions where a classname is expected, like in
2735 * static member access. So, we only reach here for things
2736 * like "$$::someMethod()". *)
2737 N.CIexpr
(p, N.Lvar
(Env.found_dollardollar env p))
2738 | x when x.[0] = '$'
-> N.CIexpr
(p, N.Lvar
(Env.lvar env cid
))
2739 | _ -> N.CI
(Env.type_name env cid ~allow_typedef
:false,
2740 hintl ~allow_wildcard
:true ~forbid_this
:false
2741 ~allow_typedef
:true ~allow_retonly
:true ~tp_depth
:1 env hl
2745 List.map_env
[] l
(case
env)
2747 and case
env acc = function
2749 let all_locals, b = branch ~after_unsafe
:N.Fallthrough
env b in
2750 all_locals :: acc, N.Default
b
2752 let e = expr
env e in
2753 let all_locals, b = branch ~after_unsafe
:N.Fallthrough
env b in
2754 all_locals :: acc, N.Case
(e, b)
2756 and catchl
env l
= List.map_env
[] l
(catch
env)
2757 and catch
env acc (x1
, x2
, b) =
2760 (* If the variable does not begin with $, it is an immutable binding *)
2761 let x2 = if (snd
x2) <> ""
2762 && (snd
x2).[0] = '$'
(* This is always true if not in experimental mode *)
2763 then Env.new_lvar env x2
2764 else Env.new_let_local env x2
2766 let all_locals, b = branch
env b in
2767 all_locals :: acc, (Env.type_name env x1 ~allow_typedef
:true, x2, b)
2770 and afield
env = function
2771 | AFvalue
e -> N.AFvalue
(expr
env e)
2772 | AFkvalue
(e1, e2) -> N.AFkvalue
(expr
env e1, expr
env e2)
2774 and afield_value
env cname
= function
2775 | AFvalue
e -> expr
env e
2776 | AFkvalue
(e1, _e2
) ->
2777 Errors.unexpected_arrow
(fst
e1) cname
;
2780 and afield_kvalue
env cname
= function
2782 Errors.missing_arrow
(fst
e) cname
;
2783 expr
env e, expr
env (fst
e, Lvar
(fst
e, "__internal_placeholder"))
2784 | AFkvalue
(e1, e2) -> expr
env e1, expr
env e2
2786 and attrl
env l
= List.map l
(attr env)
2787 and attr env = function
2788 | Xhp_simple
(x, e) -> N.Xhp_simple
(x, expr
env e)
2789 | Xhp_spread
e -> N.Xhp_spread
(expr
env e)
2791 and string2
env idl =
2792 List.map
idl (expr
env)
2794 (**************************************************************************)
2795 (* Function/Method Body Naming: *)
2796 (* Ensure that, given a function / class, any UnnamedBody within is
2797 * transformed into a a named body *)
2798 (**************************************************************************)
2800 let func_body nenv
f =
2801 match f.N.f_body
with
2802 | N.NamedBody
b -> b
2803 | N.UnnamedBody
{ N.fub_ast
; N.fub_tparams
; N.fub_namespace
; _ } ->
2804 let genv = Env.make_fun_genv nenv
2805 SMap.empty
f.N.f_mode
(snd
f.N.f_name
) fub_namespace
in
2806 let genv = extend_params
genv fub_tparams
in
2807 let lenv = Env.empty_local UBMErr
in
2808 let env = genv, lenv in
2810 List.fold_left ~
f:Env.add_param f.N.f_params ~init
:env in
2811 let env = match f.N.f_variadic
with
2812 | N.FVellipsis
_ | N.FVnonVariadic
-> env
2813 | N.FVvariadicArg param
-> Env.add_param env param
2815 let body = block
env fub_ast
in
2816 let unsafe = func_body_had_unsafe
env in
2817 Env.check_goto_references env;
2820 fnb_unsafe
= unsafe;
2823 let meth_body genv m
=
2824 let named_body = (match m
.N.m_body
with
2825 | N.NamedBody
_ as b -> b
2826 | N.UnnamedBody
{N.fub_ast
; N.fub_tparams
; N.fub_namespace
; _} ->
2827 let genv = {genv with namespace
= fub_namespace
} in
2828 let genv = extend_params
genv fub_tparams
in
2829 let env = genv, Env.empty_local UBMErr
in
2831 List.fold_left ~
f:Env.add_param m
.N.m_params ~init
:env in
2832 let env = match m
.N.m_variadic
with
2833 | N.FVellipsis
_ | N.FVnonVariadic
-> env
2834 | N.FVvariadicArg param
-> Env.add_param env param
2836 let body = block
env fub_ast
in
2837 let unsafe = func_body_had_unsafe
env in
2838 Env.check_goto_references env;
2841 fnb_unsafe
= unsafe;
2844 {m
with N.m_body
= named_body}
2846 let class_meth_bodies nenv nc
=
2847 let _n_tparams, cstrs
= nc
.N.c_tparams
in
2848 let genv = Env.make_class_genv nenv cstrs
2849 nc
.N.c_mode
(nc
.N.c_name
, nc
.N.c_kind
)
2850 Namespace_env.empty_with_default_popt
2851 (Attributes.mem SN.UserAttributes.uaProbabilisticModel nc
.N.c_user_attributes
)
2853 let inst_meths = List.map nc
.N.c_methods
(meth_body genv) in
2854 let opt_constructor = match nc
.N.c_constructor
with
2856 | Some c
-> Some
(meth_body genv c
) in
2857 let static_meths = List.map nc
.N.c_static_methods
(meth_body genv) in
2859 N.c_methods
= inst_meths;
2860 N.c_static_methods
= static_meths ;
2861 N.c_constructor
= opt_constructor ;
2864 (**************************************************************************)
2866 (**************************************************************************)
2868 let typedef genv tdef
=
2869 let ty = match tdef
.t_kind
with Alias t
| NewType t
-> t
in
2870 let cstrs = make_constraints tdef
.t_tparams
in
2871 let env = Env.make_typedef_env genv cstrs tdef
in
2872 let tconstraint = Option.map tdef
.t_constraint
(hint env) in
2873 List.iter tdef
.t_tparams
check_constraint;
2874 let tparaml = type_paraml
env tdef
.t_tparams
in
2875 let t_vis = match tdef
.t_kind
with
2876 | Ast.Alias
_ -> N.Transparent
2877 | Ast.NewType
_ -> N.Opaque
2879 let attrs = user_attributes
env tdef
.t_user_attributes
in
2881 N.t_annotation
= ();
2883 t_tparams
= tparaml;
2884 t_constraint
= tconstraint;
2885 t_kind
= hint env ty;
2886 t_user_attributes
= attrs;
2887 t_mode
= tdef
.t_mode
;
2891 (**************************************************************************)
2892 (* Global constants *)
2893 (**************************************************************************)
2895 let check_constant_hint cst
=
2896 match cst
.cst_type
with
2897 | None
when cst
.cst_mode
= FileInfo.Mstrict
->
2898 Errors.add_a_typehint
(fst cst
.cst_name
)
2902 let global_const genv cst
=
2903 let env = Env.make_const_env genv cst
in
2904 let hint = Option.map cst
.cst_type
(hint env) in
2905 let e = match cst
.cst_kind
with
2907 check_constant_hint cst
;
2908 Some
(constant_expr
env cst
.cst_value
)
2909 (* Define allows any expression, so don't call check_constant.
2910 * Furthermore it often appears at toplevel, which we don't track at
2911 * all, so don't type or even name that expression, it may refer to
2912 * "undefined" variables that actually exist, just untracked since
2913 * they're toplevel. *)
2914 | Ast.Cst_define
-> None
in
2915 { N.cst_annotation
= ();
2916 cst_mode
= cst
.cst_mode
;
2917 cst_name
= cst
.cst_name
;
2920 cst_is_define
= (cst
.cst_kind
= Ast.Cst_define
);
2923 (* Uses a default empty environment to extract the use list
2924 of a lambda expression. This exists only for the sake of
2925 the dehackificator and is not meant for general use. *)
2926 let uselist_lambda f =
2927 (* semantic duplication: This is copied from the implementation of the
2928 `Lfun` variant of `expr_` defined earlier in this file. *)
2929 let to_capture = ref [] in
2930 let handle_unbound (p, x) =
2931 to_capture := x :: !to_capture;
2934 let tcopt = TypecheckerOptions.make_permissive
TypecheckerOptions.default
in
2935 let genv = Env.make_fun_decl_genv tcopt SMap.empty
f in
2936 let lenv = Env.empty_local @@ UBMFunc
handle_unbound in
2937 let env = genv, lenv in
2938 ignore
(expr_lambda
env f);
2939 List.dedup_and_sort
!to_capture ~compare
:String.compare
2941 (**************************************************************************)
2942 (* The entry point to CHECK the program, and transform the program *)
2943 (**************************************************************************)
2945 let program tcopt ast
=
2946 let rec program ast
=
2947 List.concat
@@ List.map ast
begin function
2948 | Ast.Fun
f -> [N.Fun
(fun_
tcopt f)]
2949 | Ast.Class c
-> [N.Class
(class_ tcopt c
)]
2950 | Ast.Typedef t
-> [N.Typedef
(typedef tcopt t
)]
2951 | Ast.Constant cst
-> [N.Constant
(global_const tcopt cst
)]
2953 | Ast.Namespace
(_ns
, ast
) -> program ast
2954 | Ast.NamespaceUse
_ -> []
2955 | Ast.SetNamespaceEnv
_ -> []
2960 let stmt _ acc _ = acc
2961 let lvalue _ acc _ = acc