Add SetModule and fd_module defs
[hiphop-php.git] / hphp / hack / src / hackc / compile / closure_convert.rs
blob0bf160e1dcff7797796d42b1c5d0648849959768
1 // Copyright (c) Facebook, Inc. and its affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 use env::emitter::Emitter;
7 use error::{Error, Result};
8 use global_state::{ClosureEnclosingClassInfo, GlobalState};
9 use hack_macro::hack_expr;
10 use hash::IndexSet;
11 use hhbc::hhas_coeffects::HhasCoeffects;
12 use hhbc_string_utils as string_utils;
13 use itertools::Itertools;
14 use naming_special_names_rust::{
15     fb, pseudo_consts, pseudo_functions, special_idents, superglobals,
17 use ocamlrep::rc::RcOc;
18 use options::{HhvmFlags, Options};
19 use oxidized::{
20     aast_visitor::{self, visit_mut, AstParams, NodeMut, VisitorMut},
21     ast::{
22         Abstraction, ClassGetExpr, ClassHint, ClassId, ClassId_, ClassName, ClassVar, Class_,
23         ClassishKind, Contexts, Def, EmitId, Expr, Expr_, FunDef, FunKind, FunParam, Fun_,
24         FuncBody, Hint, Hint_, Id, Lid, LocalId, Method_, Pos, ReifyKind, Sid, Stmt, Stmt_, Targ,
25         Tparam, TypeHint, UserAttribute, Visibility,
26     },
27     ast_defs::ParamKind,
28     file_info::Mode,
29     local_id, namespace_env,
30     s_map::SMap,
32 use std::borrow::Cow;
33 use unique_id_builder::{
34     get_unique_id_for_function, get_unique_id_for_main, get_unique_id_for_method,
37 #[derive(Default)]
38 struct Variables {
39     /// all variables declared/used in the scope
40     all_vars: IndexSet<String>,
41     /// names of parameters if scope correspond to a function
42     parameter_names: IndexSet<String>,
43     /// If this is a long lambda then the list of explicitly captured vars (if
44     /// any).
45     explicit_capture: IndexSet<String>,
48 struct ClassSummary<'b> {
49     extends: &'b [ClassHint],
50     kind: ClassishKind,
51     mode: Mode,
52     name: &'b ClassName,
53     span: &'b Pos,
54     tparams: &'b [Tparam],
57 struct FunctionSummary<'b, 'arena> {
58     // Unfortunately HhasCoeffects has to be owned for now.
59     coeffects: HhasCoeffects<'arena>,
60     fun_kind: FunKind,
61     mode: Mode,
62     name: &'b Sid,
63     span: &'b Pos,
64     tparams: &'b [Tparam],
67 struct LambdaSummary<'b, 'arena> {
68     // Unfortunately HhasCoeffects has to be owned for now.
69     coeffects: HhasCoeffects<'arena>,
70     explicit_capture: Option<&'b [Lid]>,
71     fun_kind: FunKind,
72     span: &'b Pos,
75 struct MethodSummary<'b, 'arena> {
76     // Unfortunately HhasCoeffects has to be owned for now.
77     coeffects: HhasCoeffects<'arena>,
78     fun_kind: FunKind,
79     name: &'b Sid,
80     span: &'b Pos,
81     static_: bool,
82     tparams: &'b [Tparam],
85 enum ScopeSummary<'b, 'arena> {
86     TopLevel,
87     Class(ClassSummary<'b>),
88     Function(FunctionSummary<'b, 'arena>),
89     Method(MethodSummary<'b, 'arena>),
90     Lambda(LambdaSummary<'b, 'arena>),
93 impl<'b, 'arena> ScopeSummary<'b, 'arena> {
94     fn span(&self) -> Option<&Pos> {
95         match self {
96             ScopeSummary::TopLevel => None,
97             ScopeSummary::Class(cd) => Some(cd.span),
98             ScopeSummary::Function(fd) => Some(fd.span),
99             ScopeSummary::Method(md) => Some(md.span),
100             ScopeSummary::Lambda(ld) => Some(ld.span),
101         }
102     }
105 /// The environment for a scope. This tracks the summary information and
106 /// variables used within a scope.
107 struct Scope<'b, 'arena> {
108     parent: Option<&'b Scope<'b, 'arena>>,
110     /// Number of closures created in the current function
111     closure_cnt_per_fun: u32,
112     /// Are we immediately in a using statement?
113     in_using: bool,
114     /// What is the current context?
115     summary: ScopeSummary<'b, 'arena>,
116     /// What variables are defined in this scope?
117     variables: Variables,
120 impl<'b, 'arena> Scope<'b, 'arena> {
121     fn toplevel(defs: &[Def]) -> Result<Self> {
122         let all_vars = compute_vars(&[], &defs)?;
124         Ok(Self {
125             in_using: false,
126             closure_cnt_per_fun: 0,
127             parent: None,
128             summary: ScopeSummary::TopLevel,
129             variables: Variables {
130                 all_vars,
131                 ..Variables::default()
132             },
133         })
134     }
136     fn as_class_summary(&self) -> Option<&ClassSummary<'_>> {
137         self.walk_scope().find_map(|scope| match &scope.summary {
138             ScopeSummary::Class(cd) => Some(cd),
139             _ => None,
140         })
141     }
143     fn check_if_in_async_context(&self) -> Result<()> {
144         let check_valid_fun_kind = |name, kind: FunKind| {
145             if !kind.is_async() {
146                 Err(Error::fatal_parse(
147                     &self.span_or_none(),
148                     format!(
149                         "Function '{}' contains 'await' but is not declared as async.",
150                         string_utils::strip_global_ns(name)
151                     ),
152                 ))
153             } else {
154                 Ok(())
155             }
156         };
157         let check_lambda = |is_async: bool| {
158             if !is_async {
159                 Err(Error::fatal_parse(
160                     &self.span_or_none(),
161                     "Await may only appear in an async function",
162                 ))
163             } else {
164                 Ok(())
165             }
166         };
167         match &self.summary {
168             ScopeSummary::TopLevel => Err(Error::fatal_parse(
169                 &self.span_or_none(),
170                 "'await' can only be used inside a function",
171             )),
172             ScopeSummary::Lambda(ld) => check_lambda(ld.fun_kind.is_async()),
173             ScopeSummary::Class(_) => Ok(()), /* Syntax error, wont get here */
174             ScopeSummary::Function(fd) => check_valid_fun_kind(&fd.name.1, fd.fun_kind),
175             ScopeSummary::Method(md) => check_valid_fun_kind(&md.name.1, md.fun_kind),
176         }
177     }
179     fn class_tparams(&self) -> &[Tparam] {
180         self.as_class_summary().map_or(&[], |cd| cd.tparams)
181     }
183     fn coeffects_of_scope<'c>(&'c self) -> Option<&'c HhasCoeffects<'arena>> {
184         for scope in self.walk_scope() {
185             match &scope.summary {
186                 ScopeSummary::Class(_) => break,
187                 ScopeSummary::Method(md) => return Some(&md.coeffects),
188                 ScopeSummary::Function(fd) => return Some(&fd.coeffects),
189                 ScopeSummary::Lambda(ld) => {
190                     if !ld.coeffects.get_static_coeffects().is_empty() {
191                         return Some(&ld.coeffects);
192                     }
193                 }
194                 ScopeSummary::TopLevel => {}
195             }
196         }
197         None
198     }
200     fn fun_tparams(&self) -> &[Tparam] {
201         for scope in self.walk_scope() {
202             match &scope.summary {
203                 ScopeSummary::Class(_) => break,
204                 ScopeSummary::Function(fd) => return fd.tparams,
205                 ScopeSummary::Method(md) => return md.tparams,
206                 _ => {}
207             }
208         }
209         &[]
210     }
212     fn is_in_debugger_eval_fun(&self) -> bool {
213         let mut cur = Some(self);
214         while let Some(scope) = cur {
215             match &scope.summary {
216                 ScopeSummary::Lambda(_) => {}
217                 ScopeSummary::Function(fd) => return fd.name.1 == "include",
218                 _ => break,
219             }
220             cur = scope.parent;
221         }
222         false
223     }
225     fn is_static(&self) -> bool {
226         for scope in self.walk_scope() {
227             match &scope.summary {
228                 ScopeSummary::Function(_) => return true,
229                 ScopeSummary::Method(md) => return md.static_,
230                 ScopeSummary::Lambda(_) => {}
231                 ScopeSummary::Class(_) | ScopeSummary::TopLevel => unreachable!(),
232             }
233         }
234         unreachable!();
235     }
237     fn make_scope_name(&self, ns: &RcOc<namespace_env::Env>) -> String {
238         let mut parts = Vec::new();
239         let mut iter = self.walk_scope();
240         while let Some(scope) = iter.next() {
241             match &scope.summary {
242                 ScopeSummary::Class(cd) => {
243                     parts.push(make_class_name(cd.name));
244                     break;
245                 }
246                 ScopeSummary::Function(fd) => {
247                     let fname = strip_id(fd.name);
248                     parts.push(fname.into());
249                     for sub_scope in iter {
250                         match &sub_scope.summary {
251                             ScopeSummary::Class(cd) => {
252                                 parts.push("::".into());
253                                 parts.push(make_class_name(cd.name));
254                                 break;
255                             }
256                             _ => {}
257                         }
258                     }
259                     break;
260                 }
261                 ScopeSummary::Method(x) => {
262                     parts.push(strip_id(x.name).to_string());
263                     if !parts.last().map_or(false, |x| x.ends_with("::")) {
264                         parts.push("::".into())
265                     };
266                 }
267                 ScopeSummary::Lambda(_) | ScopeSummary::TopLevel => {}
268             }
269         }
271         if parts.is_empty() {
272             if let Some(n) = &ns.name {
273                 format!("{}\\", n)
274             } else {
275                 String::new()
276             }
277         } else {
278             parts.reverse();
279             parts.join("")
280         }
281     }
283     /// Create a new Env which uses `self` as its parent.
284     fn new_child<'s>(
285         &'s self,
286         summary: ScopeSummary<'s, 'arena>,
287         variables: Variables,
288     ) -> Result<Scope<'s, 'arena>> {
289         Ok(Scope {
290             in_using: self.in_using,
291             closure_cnt_per_fun: self.closure_cnt_per_fun,
292             parent: Some(self),
293             summary,
294             variables,
295         })
296     }
298     fn scope_fmode(&self) -> Mode {
299         for scope in self.walk_scope() {
300             match &scope.summary {
301                 ScopeSummary::Class(cd) => return cd.mode,
302                 ScopeSummary::Function(fd) => return fd.mode,
303                 _ => {}
304             }
305         }
306         Mode::Mstrict
307     }
309     fn should_capture_var(&self, var: &str) -> bool {
310         // variable used in lambda should be captured if is:
311         // - not contained in lambda parameter list
312         if self.variables.parameter_names.contains(var) {
313             return false;
314         };
315         // AND
316         // - it exists in one of enclosing scopes
317         for scope in self.walk_scope().skip(1) {
318             let vars = &scope.variables;
319             if vars.all_vars.contains(var)
320                 || vars.parameter_names.contains(var)
321                 || vars.explicit_capture.contains(var)
322             {
323                 return true;
324             }
326             match &scope.summary {
327                 ScopeSummary::Lambda(ld) => {
328                     // A lambda contained within an anonymous function (a 'long'
329                     // lambda) shouldn't capture variables from outside the
330                     // anonymous function unless they're explicitly mentioned in
331                     // the function's use clause.
332                     if ld.explicit_capture.is_some() {
333                         return false;
334                     }
335                 }
336                 ScopeSummary::TopLevel => {}
337                 _ => return false,
338             }
339         }
341         false
342     }
344     fn span(&self) -> Option<&Pos> {
345         self.summary.span()
346     }
348     fn span_or_none<'s>(&'s self) -> Cow<'s, Pos> {
349         if let Some(pos) = self.span() {
350             Cow::Borrowed(pos)
351         } else {
352             Cow::Owned(Pos::make_none())
353         }
354     }
356     fn walk_scope<'s>(&'s self) -> ScopeIter<'b, 's, 'arena> {
357         ScopeIter(Some(self))
358     }
360     fn with_in_using<F, R>(&mut self, in_using: bool, mut f: F) -> R
361     where
362         F: FnMut(&mut Self) -> R,
363     {
364         let old_in_using = self.in_using;
365         self.in_using = in_using;
366         let r = f(self);
367         self.in_using = old_in_using;
368         r
369     }
372 struct ScopeIter<'b, 'c, 'arena>(Option<&'c Scope<'b, 'arena>>);
374 impl<'b, 'c, 'arena> Iterator for ScopeIter<'b, 'c, 'arena> {
375     type Item = &'c Scope<'c, 'arena>;
377     fn next(&mut self) -> Option<Self::Item> {
378         if let Some(cur) = self.0.take() {
379             self.0 = cur.parent;
380             Some(cur)
381         } else {
382             None
383         }
384     }
387 #[derive(Clone, Default)]
388 struct CaptureState {
389     this_: bool,
390     // Free variables computed so far
391     vars: IndexSet<String>,
392     generics: IndexSet<String>,
395 /// ReadOnlyState is split from State because it can be a simple ref in
396 /// ClosureVisitor.
397 struct ReadOnlyState<'a> {
398     /// How many existing classes are there?
399     class_count: usize,
400     // Empty namespace as constructed by parser
401     empty_namespace: RcOc<namespace_env::Env>,
402     /// For debugger eval
403     for_debugger_eval: bool,
404     /// Global compiler/hack options
405     options: &'a Options,
408 /// Mutable state used during visiting in ClosureVisitor. It's mutable and owned
409 /// so it needs to be moved as we push and pop scopes.
410 struct State<'arena> {
411     capture_state: CaptureState,
412     // Closure classes
413     closures: Vec<Class_>,
414     // accumulated information about program
415     global_state: GlobalState<'arena>,
416     /// Hoisted meth_caller functions
417     named_hoisted_functions: SMap<FunDef>,
418     // The current namespace environment
419     namespace: RcOc<namespace_env::Env>,
422 impl<'arena> State<'arena> {
423     fn initial_state(empty_namespace: RcOc<namespace_env::Env>) -> Self {
424         Self {
425             capture_state: Default::default(),
426             closures: vec![],
427             global_state: GlobalState::default(),
428             named_hoisted_functions: SMap::new(),
429             namespace: empty_namespace,
430         }
431     }
433     fn record_function_state(&mut self, key: String, coeffects_of_scope: HhasCoeffects<'arena>) {
434         if !coeffects_of_scope.get_static_coeffects().is_empty() {
435             self.global_state
436                 .lambda_coeffects_of_scope
437                 .insert(key.clone(), coeffects_of_scope);
438         }
439     }
441     /// Clear the variables, upon entering a lambda
442     fn enter_lambda(&mut self) {
443         self.capture_state.vars = Default::default();
444         self.capture_state.this_ = false;
445         self.capture_state.generics = Default::default();
446     }
448     fn set_namespace(&mut self, namespace: RcOc<namespace_env::Env>) {
449         self.namespace = namespace;
450     }
452     /// Add a variable to the captured variables
453     fn add_var<'s>(&mut self, scope: &Scope<'_, '_>, var: impl Into<Cow<'s, str>>) {
454         let var = var.into();
456         // Don't bother if it's $this, as this is captured implicitly
457         if var == special_idents::THIS {
458             self.capture_state.this_ = true;
459         } else if scope.should_capture_var(&var)
460             && (var != special_idents::DOLLAR_DOLLAR)
461             && !superglobals::is_superglobal(&var)
462         {
463             // If it's bound as a parameter or definite assignment, don't add it
464             // Also don't add the pipe variable and superglobals
465             self.capture_state.vars.insert(var.into_owned());
466         }
467     }
469     fn add_generic(&mut self, scope: &mut Scope<'_, '_>, var: &str) {
470         let reified_var_position = |is_fun| {
471             let is_reified_var =
472                 |param: &Tparam| param.reified != ReifyKind::Erased && param.name.1 == var;
473             if is_fun {
474                 scope.fun_tparams().iter().position(is_reified_var)
475             } else {
476                 scope.class_tparams().iter().position(is_reified_var)
477             }
478         };
480         if let Some(i) = reified_var_position(true) {
481             let var = string_utils::reified::captured_name(true, i);
482             self.capture_state.generics.insert(var);
483         } else if let Some(i) = reified_var_position(false) {
484             let var = string_utils::reified::captured_name(false, i);
485             self.capture_state.generics.insert(var);
486         }
487     }
490 fn compute_vars(
491     params: &[FunParam],
492     body: &impl aast_visitor::Node<AstParams<(), String>>,
493 ) -> Result<IndexSet<String>> {
494     hhbc::decl_vars::vars_from_ast(params, &body).map_err(Error::unrecoverable)
497 fn get_parameter_names(params: &[FunParam]) -> IndexSet<String> {
498     params.iter().map(|p| p.name.to_string()).collect()
501 fn strip_id(id: &Id) -> &str {
502     string_utils::strip_global_ns(&id.1)
505 fn make_class_name(name: &ClassName) -> String {
506     string_utils::mangle_xhp_id(strip_id(name).to_string())
509 fn make_closure_name(scope: &Scope<'_, '_>, state: &State<'_>) -> String {
510     let per_fun_idx = scope.closure_cnt_per_fun;
511     let name = scope.make_scope_name(&state.namespace);
512     string_utils::closures::mangle_closure(&name, per_fun_idx)
515 fn make_closure(
516     class_num: usize,
517     p: Pos,
518     scope: &Scope<'_, '_>,
519     state: &State<'_>,
520     ro_state: &ReadOnlyState<'_>,
521     lambda_vars: Vec<String>,
522     fun_tparams: Vec<Tparam>,
523     class_tparams: Vec<Tparam>,
524     is_static: bool,
525     mode: Mode,
526     mut fd: Fun_,
527 ) -> (Fun_, Class_) {
528     let md = Method_ {
529         span: fd.span.clone(),
530         annotation: fd.annotation,
531         final_: false,
532         abstract_: false,
533         static_: is_static,
534         readonly_this: fd.readonly_this.is_some(),
535         visibility: Visibility::Public,
536         name: Id(fd.name.0.clone(), "__invoke".into()),
537         tparams: fun_tparams,
538         where_constraints: fd.where_constraints.clone(),
539         params: fd.params.clone(),
540         ctxs: fd.ctxs.clone(),
541         unsafe_ctxs: None, // TODO(T70095684)
542         body: fd.body.clone(),
543         fun_kind: fd.fun_kind,
544         user_attributes: fd.user_attributes.clone(),
545         readonly_ret: fd.readonly_ret,
546         ret: fd.ret.clone(),
547         external: false,
548         doc_comment: fd.doc_comment.clone(),
549     };
551     let make_class_var = |name: &str| ClassVar {
552         final_: false,
553         xhp_attr: None,
554         abstract_: false,
555         readonly: false, // readonly on closure_convert
556         visibility: Visibility::Private,
557         type_: TypeHint((), None),
558         id: Id(p.clone(), name.into()),
559         expr: None,
560         user_attributes: vec![],
561         doc_comment: None,
562         is_promoted_variadic: false,
563         is_static: false,
564         span: p.clone(),
565     };
567     let cvl = lambda_vars
568         .iter()
569         .map(|name| make_class_var(string_utils::locals::strip_dollar(name)));
571     let cd = Class_ {
572         span: p.clone(),
573         annotation: fd.annotation,
574         mode,
575         user_attributes: vec![],
576         file_attributes: vec![],
577         final_: false,
578         is_xhp: false,
579         has_xhp_keyword: false,
580         kind: ClassishKind::Cclass(Abstraction::Concrete),
581         name: Id(p.clone(), make_closure_name(scope, state)),
582         tparams: class_tparams,
583         extends: vec![Hint(
584             p.clone(),
585             Box::new(Hint_::Happly(Id(p.clone(), "Closure".into()), vec![])),
586         )],
587         uses: vec![],
588         xhp_attr_uses: vec![],
589         xhp_category: None,
590         reqs: vec![],
591         implements: vec![],
592         where_constraints: vec![],
593         consts: vec![],
594         typeconsts: vec![],
595         vars: cvl.collect(),
596         methods: vec![md],
597         attributes: vec![],
598         xhp_children: vec![],
599         xhp_attrs: vec![],
600         namespace: RcOc::clone(&ro_state.empty_namespace),
601         enum_: None,
602         doc_comment: None,
603         emit_id: Some(EmitId::Anonymous),
604         // TODO(T116039119): Populate value with presence of internal attribute
605         internal: false,
606         // TODO: closures should have the visibility of the module they are defined in
607         module: None,
608     };
610     // TODO(hrust): can we reconstruct fd here from the scratch?
611     fd.name = Id(p.clone(), class_num.to_string());
612     (fd, cd)
615 /// Translate special identifiers `__CLASS__`, `__METHOD__` and `__FUNCTION__`
616 /// into literal strings. It's necessary to do this before closure conversion
617 /// because the enclosing class will be changed.
618 fn convert_id(scope: &Scope<'_, '_>, Id(p, s): Id) -> Expr_ {
619     let ret = Expr_::mk_string;
620     let name = |c: &ClassName| {
621         Expr_::mk_string(string_utils::mangle_xhp_id(strip_id(c).to_string()).into())
622     };
624     match s {
625         _ if s.eq_ignore_ascii_case(pseudo_consts::G__TRAIT__) => match scope.as_class_summary() {
626             Some(cd) if cd.kind == ClassishKind::Ctrait => name(cd.name),
627             _ => ret("".into()),
628         },
629         _ if s.eq_ignore_ascii_case(pseudo_consts::G__CLASS__) => match scope.as_class_summary() {
630             Some(cd) if cd.kind != ClassishKind::Ctrait => name(cd.name),
631             Some(_) => Expr_::mk_id(Id(p, s)),
632             None => ret("".into()),
633         },
634         _ if s.eq_ignore_ascii_case(pseudo_consts::G__METHOD__) => {
635             let (prefix, is_trait) = match scope.as_class_summary() {
636                 None => ("".into(), false),
637                 Some(cd) => (
638                     string_utils::mangle_xhp_id(strip_id(cd.name).to_string()) + "::",
639                     cd.kind == ClassishKind::Ctrait,
640                 ),
641             };
642             // for lambdas nested in trait methods HHVM replaces __METHOD__
643             // with enclosing method name - do the same and bubble up from lambdas *
644             let id_scope = if is_trait {
645                 scope.walk_scope().find(|x| {
646                     let scope_is_in_lambda = match &x.summary {
647                         ScopeSummary::Lambda(_) => true,
648                         _ => false,
649                     };
650                     !scope_is_in_lambda
651                 })
652             } else {
653                 scope.walk_scope().next()
654             };
656             match id_scope.map(|x| &x.summary) {
657                 Some(ScopeSummary::Function(fd)) => ret((prefix + strip_id(fd.name)).into()),
658                 Some(ScopeSummary::Method(md)) => ret((prefix + strip_id(md.name)).into()),
659                 Some(ScopeSummary::Lambda(_)) => ret((prefix + "{closure}").into()),
660                 // PHP weirdness: __METHOD__ inside a class outside a method returns class name
661                 Some(ScopeSummary::Class(cd)) => ret(strip_id(cd.name).into()),
662                 _ => ret("".into()),
663             }
664         }
665         _ if s.eq_ignore_ascii_case(pseudo_consts::G__FUNCTION__) => match &scope.summary {
666             ScopeSummary::Function(fd) => ret(strip_id(fd.name).into()),
667             ScopeSummary::Method(md) => ret(strip_id(md.name).into()),
668             ScopeSummary::Lambda(_) => ret("{closure}".into()),
669             _ => ret("".into()),
670         },
671         _ if s.eq_ignore_ascii_case(pseudo_consts::G__LINE__) => {
672             // If the expression goes on multi lines, we return the last line
673             let (_, line, _, _) = p.info_pos_extended();
674             Expr_::mk_int(line.to_string())
675         }
676         _ => Expr_::mk_id(Id(p, s)),
677     }
680 fn make_class_info(c: &ClassSummary<'_>) -> ClosureEnclosingClassInfo {
681     ClosureEnclosingClassInfo {
682         kind: c.kind,
683         name: c.name.1.clone(),
684         parent_class_name: match c.extends {
685             [x] => x.as_happly().map(|(id, _args)| id.1.clone()),
686             _ => None,
687         },
688     }
691 pub fn make_fn_param(pos: Pos, lid: &LocalId, is_variadic: bool, is_inout: bool) -> FunParam {
692     FunParam {
693         annotation: (),
694         type_hint: TypeHint((), None),
695         is_variadic,
696         pos: pos.clone(),
697         name: local_id::get_name(lid).clone(),
698         expr: None,
699         callconv: if is_inout {
700             ParamKind::Pinout(pos)
701         } else {
702             ParamKind::Pnormal
703         },
704         readonly: None, // TODO
705         user_attributes: vec![],
706         visibility: None,
707     }
710 fn make_dyn_meth_caller_lambda(pos: &Pos, cexpr: &Expr, fexpr: &Expr, force: bool) -> Expr_ {
711     let pos = || pos.clone();
712     let obj_var = Box::new(Lid(pos(), local_id::make_unscoped("$o")));
713     let meth_var = Box::new(Lid(pos(), local_id::make_unscoped("$m")));
714     let obj_lvar = Expr((), pos(), Expr_::Lvar(obj_var.clone()));
715     let meth_lvar = Expr((), pos(), Expr_::Lvar(meth_var.clone()));
716     // AST for: return $o-><func>(...$args);
717     let args_var = local_id::make_unscoped("$args");
718     let variadic_param = make_fn_param(pos(), &args_var, true, false);
719     let invoke_method = hack_expr!(
720         pos = pos(),
721         r#"#obj_lvar->#meth_lvar(...#{lvar(args_var)})"#
722     );
723     let attrs = if force {
724         vec![UserAttribute {
725             name: Id(pos(), "__DynamicMethCallerForce".into()),
726             params: vec![],
727         }]
728     } else {
729         vec![]
730     };
731     let ctxs = Some(Contexts(
732         pos(),
733         vec![Hint::new(
734             pos(),
735             Hint_::mk_happly(Id(pos(), string_utils::coeffects::CALLER.into()), vec![]),
736         )],
737     ));
739     let fd = Fun_ {
740         span: pos(),
741         annotation: (),
742         readonly_this: None, // TODO: readonly_this in closure_convert
743         readonly_ret: None,  // TODO: readonly_ret in closure convert
744         ret: TypeHint((), None),
745         name: Id(pos(), ";anonymous".to_string()),
746         tparams: vec![],
747         where_constraints: vec![],
748         params: vec![
749             make_fn_param(pos(), &obj_var.1, false, false),
750             make_fn_param(pos(), &meth_var.1, false, false),
751             variadic_param,
752         ],
753         ctxs,
754         unsafe_ctxs: None,
755         body: FuncBody {
756             fb_ast: vec![Stmt(pos(), Stmt_::Return(Box::new(Some(invoke_method))))],
757         },
758         fun_kind: FunKind::FSync,
759         user_attributes: attrs,
760         external: false,
761         doc_comment: None,
762     };
763     let force_val = if force { Expr_::True } else { Expr_::False };
764     let force_val_expr = Expr((), pos(), force_val);
765     let efun = Expr((), pos(), Expr_::mk_efun(fd, vec![]));
766     let fun_handle = hack_expr!(
767         pos = pos(),
768         r#"\__systemlib\dynamic_meth_caller(#{clone(cexpr)}, #{clone(fexpr)}, #efun, #force_val_expr)"#
769     );
770     fun_handle.2
773 fn add_reified_property(tparams: &[Tparam], vars: &mut Vec<ClassVar>) {
774     if !tparams.iter().all(|t| t.reified == ReifyKind::Erased) {
775         let p = Pos::make_none();
776         // varray/vec that holds a list of type structures
777         // this prop will be initilized during runtime
778         let hint = Hint(
779             p.clone(),
780             Box::new(Hint_::Happly(Id(p.clone(), "\\HH\\varray".into()), vec![])),
781         );
782         vars.insert(
783             0,
784             ClassVar {
785                 final_: false,
786                 xhp_attr: None,
787                 is_promoted_variadic: false,
788                 doc_comment: None,
789                 abstract_: false,
790                 readonly: false,
791                 visibility: Visibility::Private,
792                 type_: TypeHint((), Some(hint)),
793                 id: Id(p.clone(), string_utils::reified::PROP_NAME.into()),
794                 expr: None,
795                 user_attributes: vec![],
796                 is_static: false,
797                 span: p,
798             },
799         )
800     }
803 struct ClosureVisitor<'a, 'b, 'arena> {
804     alloc: &'arena bumpalo::Bump,
805     state: Option<State<'arena>>,
806     ro_state: &'a ReadOnlyState<'a>,
807     // We need 'b to be a real lifetime so that our `type Params` can refer to
808     // it - but we don't actually have any fields that use it - so we need a
809     // Phantom.
810     phantom: std::marker::PhantomData<&'b ()>,
813 impl<'ast, 'a: 'b, 'b, 'arena: 'a> VisitorMut<'ast> for ClosureVisitor<'a, 'b, 'arena> {
814     type Params = AstParams<Scope<'b, 'arena>, Error>;
816     fn object(&mut self) -> &mut dyn VisitorMut<'ast, Params = Self::Params> {
817         self
818     }
820     fn visit_method_(&mut self, scope: &mut Scope<'b, 'arena>, md: &mut Method_) -> Result<()> {
821         let cd = scope.as_class_summary().ok_or_else(|| {
822             Error::unrecoverable("unexpected scope shape - method is not inside the class")
823         })?;
824         let variables = Self::compute_variables_from_fun(&md.params, &md.body.fb_ast, None)?;
825         let coeffects = HhasCoeffects::from_ast(
826             self.alloc,
827             md.ctxs.as_ref(),
828             &md.params,
829             &md.tparams,
830             scope.class_tparams(),
831         );
832         let si = ScopeSummary::Method(MethodSummary {
833             coeffects,
834             fun_kind: md.fun_kind,
835             name: &md.name,
836             span: &md.span,
837             static_: md.static_,
838             tparams: &md.tparams,
839         });
840         self.with_subscope(scope, si, variables, |self_, scope| {
841             md.body.recurse(scope, self_)?;
842             let uid = get_unique_id_for_method(&cd.name.1, &md.name.1);
843             self_
844                 .state_mut()
845                 .record_function_state(uid, HhasCoeffects::default());
846             visit_mut(self_, scope, &mut md.params)?;
847             Ok(())
848         })?;
849         Ok(())
850     }
852     fn visit_class_(&mut self, scope: &mut Scope<'b, 'arena>, cd: &mut Class_) -> Result<()> {
853         let variables = Variables::default();
854         let si = ScopeSummary::Class(ClassSummary {
855             extends: &cd.extends,
856             kind: cd.kind,
857             mode: cd.mode,
858             name: &cd.name,
859             span: &cd.span,
860             tparams: &cd.tparams,
861         });
862         self.with_subscope(scope, si, variables, |self_, scope| -> Result<()> {
863             visit_mut(self_, scope, &mut cd.methods)?;
864             visit_mut(self_, scope, &mut cd.consts)?;
865             visit_mut(self_, scope, &mut cd.vars)?;
866             visit_mut(self_, scope, &mut cd.xhp_attrs)?;
867             visit_mut(self_, scope, &mut cd.user_attributes)?;
868             add_reified_property(&cd.tparams, &mut cd.vars);
869             Ok(())
870         })?;
871         Ok(())
872     }
874     fn visit_def(&mut self, scope: &mut Scope<'b, 'arena>, def: &mut Def) -> Result<()> {
875         match def {
876             // need to handle it ourselvses, because visit_fun_ is
877             // called both for toplevel functions and lambdas
878             Def::Fun(fd) => {
879                 let variables =
880                     Self::compute_variables_from_fun(&fd.fun.params, &fd.fun.body.fb_ast, None)?;
881                 let coeffects = HhasCoeffects::from_ast(
882                     self.alloc,
883                     fd.fun.ctxs.as_ref(),
884                     &fd.fun.params,
885                     &fd.fun.tparams,
886                     &[],
887                 );
888                 let si = ScopeSummary::Function(FunctionSummary {
889                     coeffects,
890                     fun_kind: fd.fun.fun_kind,
891                     mode: fd.mode,
892                     name: &fd.fun.name,
893                     span: &fd.fun.span,
894                     tparams: &fd.fun.tparams,
895                 });
896                 self.with_subscope(scope, si, variables, |self_, scope| {
897                     fd.fun.body.recurse(scope, self_)?;
898                     let uid = get_unique_id_for_function(&fd.fun.name.1);
899                     self_
900                         .state_mut()
901                         .record_function_state(uid, HhasCoeffects::default());
902                     visit_mut(self_, scope, &mut fd.fun.params)?;
903                     visit_mut(self_, scope, &mut fd.fun.user_attributes)?;
904                     Ok(())
905                 })?;
906                 Ok(())
907             }
908             _ => def.recurse(scope, self),
909         }
910     }
912     fn visit_hint_(&mut self, scope: &mut Scope<'b, 'arena>, hint: &mut Hint_) -> Result<()> {
913         if let Hint_::Happly(id, _) = hint {
914             self.state_mut().add_generic(scope, id.name())
915         };
916         hint.recurse(scope, self)
917     }
919     fn visit_stmt_(&mut self, scope: &mut Scope<'b, 'arena>, stmt: &mut Stmt_) -> Result<()> {
920         match stmt {
921             Stmt_::Awaitall(x) => {
922                 scope.check_if_in_async_context()?;
923                 x.recurse(scope, self)
924             }
925             Stmt_::Do(x) => {
926                 let (b, e) = &mut **x;
927                 scope.with_in_using(false, |scope| visit_mut(self, scope, b))?;
928                 self.visit_expr(scope, e)
929             }
930             Stmt_::While(x) => {
931                 let (e, b) = &mut **x;
932                 self.visit_expr(scope, e)?;
933                 scope.with_in_using(false, |scope| visit_mut(self, scope, b))
934             }
935             Stmt_::Foreach(x) => {
936                 if x.1.is_await_as_v() || x.1.is_await_as_kv() {
937                     scope.check_if_in_async_context()?
938                 }
939                 x.recurse(scope, self)
940             }
941             Stmt_::For(x) => {
942                 let (e1, e2, e3, b) = &mut **x;
944                 for e in e1 {
945                     self.visit_expr(scope, e)?;
946                 }
947                 if let Some(e) = e2 {
948                     self.visit_expr(scope, e)?;
949                 }
950                 scope.with_in_using(false, |scope| visit_mut(self, scope, b))?;
951                 for e in e3 {
952                     self.visit_expr(scope, e)?;
953                 }
954                 Ok(())
955             }
956             Stmt_::Switch(x) => {
957                 let (e, cl, dfl) = &mut **x;
958                 self.visit_expr(scope, e)?;
959                 scope.with_in_using(false, |scope| visit_mut(self, scope, cl))?;
960                 match dfl {
961                     None => Ok(()),
962                     Some(dfl) => scope.with_in_using(false, |scope| visit_mut(self, scope, dfl)),
963                 }
964             }
965             Stmt_::Using(x) => {
966                 if x.has_await {
967                     scope.check_if_in_async_context()?;
968                 }
969                 for e in &mut x.exprs.1 {
970                     self.visit_expr(scope, e)?;
971                 }
972                 scope.with_in_using(true, |scope| visit_mut(self, scope, &mut x.block))?;
973                 Ok(())
974             }
975             _ => stmt.recurse(scope, self),
976         }
977     }
979     fn visit_expr(
980         &mut self,
981         scope: &mut Scope<'b, 'arena>,
982         Expr(_, pos, e): &mut Expr,
983     ) -> Result<()> {
984         stack_limit::maybe_grow(|| {
985             *e = match strip_unsafe_casts(e) {
986                 Expr_::Efun(x) => self.convert_lambda(scope, x.0, Some(x.1))?,
987                 Expr_::Lfun(x) => self.convert_lambda(scope, x.0, None)?,
988                 Expr_::Lvar(id_orig) => {
989                     let id = if self.ro_state.for_debugger_eval
990                         && local_id::get_name(&id_orig.1) == special_idents::THIS
991                         && scope.is_in_debugger_eval_fun()
992                     {
993                         Box::new(Lid(id_orig.0, (0, "$__debugger$this".to_string())))
994                     } else {
995                         id_orig
996                     };
997                     self.state_mut().add_var(scope, local_id::get_name(&id.1));
998                     Expr_::Lvar(id)
999                 }
1000                 Expr_::Id(id) if id.name().starts_with('$') => {
1001                     let state = self.state_mut();
1002                     state.add_var(scope, id.name());
1003                     state.add_generic(scope, id.name());
1004                     Expr_::Id(id)
1005                 }
1006                 Expr_::Id(id) => {
1007                     self.state_mut().add_generic(scope, id.name());
1008                     convert_id(scope, *id)
1009                 }
1010                 Expr_::Call(x) if is_dyn_meth_caller(&x) => {
1011                     self.visit_dyn_meth_caller(scope, x, &*pos)?
1012                 }
1013                 Expr_::Call(x)
1014                     if is_meth_caller(&x)
1015                         && self
1016                             .ro_state
1017                             .options
1018                             .hhvm
1019                             .flags
1020                             .contains(HhvmFlags::EMIT_METH_CALLER_FUNC_POINTERS) =>
1021                 {
1022                     self.visit_meth_caller_funcptr(scope, x, &*pos)?
1023                 }
1024                 Expr_::Call(x) if is_meth_caller(&x) => self.visit_meth_caller(scope, x)?,
1025                 Expr_::Call(x)
1026                     if (x.0)
1027                         .as_class_get()
1028                         .and_then(|(id, _, _)| id.as_ciexpr())
1029                         .and_then(|x| x.as_id())
1030                         .map_or(false, string_utils::is_parent)
1031                         || (x.0)
1032                             .as_class_const()
1033                             .and_then(|(id, _)| id.as_ciexpr())
1034                             .and_then(|x| x.as_id())
1035                             .map_or(false, string_utils::is_parent) =>
1036                 {
1037                     self.state_mut().add_var(scope, "$this");
1038                     let mut res = Expr_::Call(x);
1039                     res.recurse(scope, self)?;
1040                     res
1041                 }
1042                 Expr_::As(x) if (x.1).is_hlike() => {
1043                     let mut res = x.0;
1044                     res.recurse(scope, self)?;
1045                     *pos = res.1;
1046                     res.2
1047                 }
1048                 Expr_::As(x)
1049                     if (x.1)
1050                         .as_happly()
1051                         .map(|(id, args)| {
1052                             (id.name() == fb::INCORRECT_TYPE
1053                                 || id.name() == fb::INCORRECT_TYPE_NO_NS)
1054                                 && args.len() == 1
1055                         })
1056                         .unwrap_or_default() =>
1057                 {
1058                     let mut res = x.0;
1059                     res.recurse(scope, self)?;
1060                     *pos = res.1;
1061                     res.2
1062                 }
1063                 Expr_::ClassGet(mut x) => {
1064                     if let ClassGetExpr::CGstring(id) = &x.1 {
1065                         // T43412864 claims that this does not need to be added into the
1066                         // closure and can be removed. There are no relevant HHVM tests
1067                         // checking for it, but there are flib test failures when you try
1068                         // to remove it.
1069                         self.state_mut().add_var(scope, &id.1);
1070                     };
1071                     x.recurse(scope, self)?;
1072                     Expr_::ClassGet(x)
1073                 }
1074                 Expr_::Await(mut x) => {
1075                     scope.check_if_in_async_context()?;
1076                     x.recurse(scope, self)?;
1077                     Expr_::Await(x)
1078                 }
1079                 Expr_::ReadonlyExpr(mut x) => {
1080                     x.recurse(scope, self)?;
1081                     Expr_::ReadonlyExpr(x)
1082                 }
1083                 Expr_::ExpressionTree(mut x) => {
1084                     x.runtime_expr.recurse(scope, self)?;
1085                     Expr_::ExpressionTree(x)
1086                 }
1087                 mut x => {
1088                     x.recurse(scope, self)?;
1089                     x
1090                 }
1091             };
1092             Ok(())
1093         })
1094     }
1097 impl<'a: 'b, 'b, 'arena: 'a + 'b> ClosureVisitor<'a, 'b, 'arena> {
1098     /// Calls a function in the scope of a sub-Scope as a child of `scope`.
1099     fn with_subscope<'s, F, R>(
1100         &mut self,
1101         scope: &'s Scope<'b, 'arena>,
1102         si: ScopeSummary<'s, 'arena>,
1103         variables: Variables,
1104         f: F,
1105     ) -> Result<(R, u32)>
1106     where
1107         'b: 's,
1108         F: FnOnce(&mut ClosureVisitor<'a, 's, 'arena>, &mut Scope<'s, 'arena>) -> Result<R>,
1109     {
1110         let mut scope = scope.new_child(si, variables)?;
1112         let mut self_ = ClosureVisitor {
1113             alloc: self.alloc,
1114             ro_state: self.ro_state,
1115             state: self.state.take(),
1116             phantom: Default::default(),
1117         };
1118         let res = f(&mut self_, &mut scope);
1119         self.state = self_.state;
1120         Ok((res?, scope.closure_cnt_per_fun))
1121     }
1123     fn state(&self) -> &State<'arena> {
1124         self.state.as_ref().unwrap()
1125     }
1127     fn state_mut(&mut self) -> &mut State<'arena> {
1128         self.state.as_mut().unwrap()
1129     }
1131     #[inline(never)]
1132     fn visit_dyn_meth_caller(
1133         &mut self,
1134         scope: &mut Scope<'b, 'arena>,
1135         mut x: CallExpr,
1136         pos: &Pos,
1137     ) -> Result<Expr_> {
1138         let force = if let Expr_::Id(ref id) = (x.0).2 {
1139             strip_id(id).eq_ignore_ascii_case("hh\\dynamic_meth_caller_force")
1140         } else {
1141             false
1142         };
1143         if let [(pk_c, cexpr), (pk_f, fexpr)] = &mut *x.2 {
1144             error::ensure_normal_paramkind(pk_c)?;
1145             error::ensure_normal_paramkind(pk_f)?;
1146             let mut res = make_dyn_meth_caller_lambda(pos, cexpr, fexpr, force);
1147             res.recurse(scope, self)?;
1148             Ok(res)
1149         } else {
1150             let mut res = Expr_::Call(x);
1151             res.recurse(scope, self)?;
1152             Ok(res)
1153         }
1154     }
1156     #[inline(never)]
1157     fn visit_meth_caller_funcptr(
1158         &mut self,
1159         scope: &mut Scope<'b, 'arena>,
1160         mut x: CallExpr,
1161         pos: &Pos,
1162     ) -> Result<Expr_> {
1163         if let [(pk_cls, Expr(_, pc, cls)), (pk_f, Expr(_, pf, func))] = &mut *x.2 {
1164             error::ensure_normal_paramkind(pk_cls)?;
1165             error::ensure_normal_paramkind(pk_f)?;
1166             match (&cls, func.as_string()) {
1167                 (Expr_::ClassConst(cc), Some(fname)) if string_utils::is_class(&(cc.1).1) => {
1168                     let mut cls_const = cls.as_class_const_mut();
1169                     let (cid, _) = match cls_const {
1170                         None => unreachable!(),
1171                         Some((ref mut cid, (_, cs))) => (cid, cs),
1172                     };
1173                     self.visit_class_id(scope, cid)?;
1174                     match &cid.2 {
1175                         cid if cid
1176                             .as_ciexpr()
1177                             .and_then(Expr::as_id)
1178                             .map_or(false, |id| !is_selflike_keyword(id)) =>
1179                         {
1180                             let alloc = bumpalo::Bump::new();
1181                             let id = cid.as_ciexpr().unwrap().as_id().unwrap();
1182                             let mangled_class_name =
1183                                 hhbc::ClassName::from_ast_name_and_mangle(&alloc, id.as_ref());
1184                             let mangled_class_name = mangled_class_name.unsafe_as_str();
1185                             Ok(self.convert_meth_caller_to_func_ptr(
1186                                 scope,
1187                                 &*pos,
1188                                 pc,
1189                                 mangled_class_name,
1190                                 pf,
1191                                 // FIXME: This is not safe--string literals are binary
1192                                 // strings. There's no guarantee that they're valid UTF-8.
1193                                 unsafe { std::str::from_utf8_unchecked(fname.as_slice()) },
1194                             ))
1195                         }
1196                         _ => Err(Error::fatal_parse(pc, "Invalid class")),
1197                     }
1198                 }
1199                 (Expr_::String(cls_name), Some(fname)) => Ok(self.convert_meth_caller_to_func_ptr(
1200                     scope,
1201                     &*pos,
1202                     pc,
1203                     // FIXME: This is not safe--string literals are binary strings.
1204                     // There's no guarantee that they're valid UTF-8.
1205                     unsafe { std::str::from_utf8_unchecked(cls_name.as_slice()) },
1206                     pf,
1207                     // FIXME: This is not safe--string literals are binary strings.
1208                     // There's no guarantee that they're valid UTF-8.
1209                     unsafe { std::str::from_utf8_unchecked(fname.as_slice()) },
1210                 )),
1211                 (_, Some(_)) => Err(Error::fatal_parse(
1212                     pc,
1213                     "Class must be a Class or string type",
1214                 )),
1215                 (_, _) => Err(Error::fatal_parse(
1216                     pf,
1217                     "Method name must be a literal string",
1218                 )),
1219             }
1220         } else {
1221             let mut res = Expr_::Call(x);
1222             res.recurse(scope, self)?;
1223             Ok(res)
1224         }
1225     }
1227     #[inline(never)]
1228     fn visit_meth_caller(
1229         &mut self,
1230         scope: &mut Scope<'b, 'arena>,
1231         mut x: CallExpr,
1232     ) -> Result<Expr_> {
1233         if let [(pk_cls, Expr(_, pc, cls)), (pk_f, Expr(_, pf, func))] = &mut *x.2 {
1234             error::ensure_normal_paramkind(pk_cls)?;
1235             error::ensure_normal_paramkind(pk_f)?;
1236             match (&cls, func.as_string()) {
1237                 (Expr_::ClassConst(cc), Some(_)) if string_utils::is_class(&(cc.1).1) => {
1238                     let mut cls_const = cls.as_class_const_mut();
1239                     let cid = match cls_const {
1240                         None => unreachable!(),
1241                         Some((ref mut cid, (_, _))) => cid,
1242                     };
1243                     if cid
1244                         .as_ciexpr()
1245                         .and_then(Expr::as_id)
1246                         .map_or(false, |id| !is_selflike_keyword(id))
1247                     {
1248                         let mut res = Expr_::Call(x);
1249                         res.recurse(scope, self)?;
1250                         Ok(res)
1251                     } else {
1252                         Err(Error::fatal_parse(pc, "Invalid class"))
1253                     }
1254                 }
1255                 (Expr_::String(_), Some(_)) => {
1256                     let mut res = Expr_::Call(x);
1257                     res.recurse(scope, self)?;
1258                     Ok(res)
1259                 }
1260                 (_, Some(_)) => Err(Error::fatal_parse(
1261                     pc,
1262                     "Class must be a Class or string type",
1263                 )),
1264                 (_, _) => Err(Error::fatal_parse(
1265                     pf,
1266                     "Method name must be a literal string",
1267                 )),
1268             }
1269         } else {
1270             let mut res = Expr_::Call(x);
1271             res.recurse(scope, self)?;
1272             Ok(res)
1273         }
1274     }
1276     fn visit_class_id(&mut self, scope: &mut Scope<'b, 'arena>, cid: &mut ClassId) -> Result<()> {
1277         if let ClassId(_, _, ClassId_::CIexpr(e)) = cid {
1278             self.visit_expr(scope, e)?;
1279         }
1280         Ok(())
1281     }
1283     // Closure-convert a lambda expression, with use_vars_opt = Some vars
1284     // if there is an explicit `use` clause.
1285     fn convert_lambda(
1286         &mut self,
1287         scope: &mut Scope<'b, 'arena>,
1288         mut fd: Fun_,
1289         use_vars_opt: Option<Vec<Lid>>,
1290     ) -> Result<Expr_> {
1291         let is_long_lambda = use_vars_opt.is_some();
1292         let state = self.state_mut();
1294         // Remember the current capture and defined set across the lambda
1295         let capture_state = state.capture_state.clone();
1296         let coeffects_of_scope = scope
1297             .coeffects_of_scope()
1298             .map_or_else(Default::default, |co| co.clone());
1299         state.enter_lambda();
1300         if let Some(user_vars) = &use_vars_opt {
1301             for Lid(p, id) in user_vars.iter() {
1302                 if local_id::get_name(id) == special_idents::THIS {
1303                     return Err(Error::fatal_parse(
1304                         p,
1305                         "Cannot use $this as lexical variable",
1306                     ));
1307                 }
1308             }
1309         }
1311         let explicit_capture: Option<IndexSet<String>> = use_vars_opt.as_ref().map(|vars| {
1312             vars.iter()
1313                 .map(|Lid(_, (_, name))| name.to_string())
1314                 .collect()
1315         });
1316         let variables =
1317             Self::compute_variables_from_fun(&fd.params, &fd.body.fb_ast, explicit_capture)?;
1318         let coeffects =
1319             HhasCoeffects::from_ast(self.alloc, fd.ctxs.as_ref(), &fd.params, &fd.tparams, &[]);
1320         let si = ScopeSummary::Lambda(LambdaSummary {
1321             coeffects,
1322             explicit_capture: use_vars_opt.as_deref(),
1323             fun_kind: fd.fun_kind,
1324             span: &fd.span,
1325         });
1326         let (_, closure_cnt_per_fun) =
1327             self.with_subscope(scope, si, variables, |self_, scope| {
1328                 fd.body.recurse(scope, self_)?;
1329                 for param in &mut fd.params {
1330                     visit_mut(self_, scope, &mut param.type_hint)?;
1331                 }
1332                 visit_mut(self_, scope, &mut fd.ret)?;
1333                 Ok(())
1334             })?;
1336         scope.closure_cnt_per_fun = closure_cnt_per_fun + 1;
1338         let state = self.state.as_mut().unwrap();
1339         let current_generics = state.capture_state.generics.clone();
1341         // TODO(hrust): produce real unique local ids
1342         let fresh_lid = |name: String| Lid(Pos::make_none(), (12345, name));
1344         let lambda_vars: Vec<&String> = state
1345             .capture_state
1346             .vars
1347             .iter()
1348             .chain(current_generics.iter())
1349             // HHVM lists lambda vars in descending order - do the same
1350             .sorted()
1351             .rev()
1352             .collect();
1354         // Remove duplicates, (not efficient, but unlikely to be large),
1355         // remove variables that are actually just parameters
1356         let use_vars_opt: Option<Vec<Lid>> = use_vars_opt.map(|use_vars| {
1357             let params = &fd.params;
1358             use_vars
1359                 .into_iter()
1360                 .rev()
1361                 .unique_by(|lid| lid.name().to_string())
1362                 .filter(|x| !params.iter().any(|y| x.name() == &y.name))
1363                 .collect::<Vec<_>>()
1364                 .into_iter()
1365                 .rev()
1366                 .collect()
1367         });
1369         // For lambdas with explicit `use` variables, we ignore the computed
1370         // capture set and instead use the explicit set
1371         let (lambda_vars, use_vars): (Vec<String>, Vec<Lid>) = match use_vars_opt {
1372             None => (
1373                 lambda_vars.iter().map(|x| x.to_string()).collect(),
1374                 lambda_vars
1375                     .iter()
1376                     .map(|x| fresh_lid(x.to_string()))
1377                     .collect(),
1378             ),
1379             Some(use_vars) => {
1380                 // We still need to append the generics
1381                 (
1382                     use_vars
1383                         .iter()
1384                         .map(|x| x.name())
1385                         .chain(current_generics.iter())
1386                         .map(|x| x.to_string())
1387                         .collect(),
1388                     use_vars
1389                         .iter()
1390                         .cloned()
1391                         .chain(current_generics.iter().map(|x| fresh_lid(x.to_string())))
1392                         .collect(),
1393                 )
1394             }
1395         };
1397         let fun_tparams = scope.fun_tparams().to_vec(); // hiddden .clone()
1398         let class_tparams = scope.class_tparams().to_vec(); // hiddden .clone()
1399         let class_num = state.closures.len() + self.ro_state.class_count;
1401         let is_static = if is_long_lambda {
1402             // long lambdas are never static
1403             false
1404         } else {
1405             // short lambdas can be made static if they don't capture this in
1406             // any form (including any nested lambdas)
1407             !state.capture_state.this_
1408         };
1410         // check if something can be promoted to static based on enclosing scope
1411         let is_static = is_static || scope.is_static();
1413         let pos = fd.span.clone();
1414         let lambda_vars_clone = lambda_vars.clone();
1415         let (inline_fundef, cd) = make_closure(
1416             class_num,
1417             pos,
1418             scope,
1419             state,
1420             self.ro_state,
1421             lambda_vars,
1422             fun_tparams,
1423             class_tparams,
1424             is_static,
1425             scope.scope_fmode(),
1426             fd,
1427         );
1429         if is_long_lambda {
1430             state
1431                 .global_state
1432                 .explicit_use_set
1433                 .insert(inline_fundef.name.1.clone());
1434         }
1436         let closure_class_name = &cd.name.1;
1437         if let Some(cd) = scope.as_class_summary() {
1438             state
1439                 .global_state
1440                 .closure_enclosing_classes
1441                 .insert(closure_class_name.clone(), make_class_info(cd));
1442         }
1444         // Restore capture and defined set
1445         // - adjust captured $this information if lambda that was just processed was
1446         //   converted into non-static one
1447         state.capture_state = CaptureState {
1448             this_: capture_state.this_ || !is_static,
1449             ..capture_state
1450         };
1451         state
1452             .global_state
1453             .closure_namespaces
1454             .insert(closure_class_name.clone(), state.namespace.clone());
1455         state.record_function_state(
1456             get_unique_id_for_method(&cd.name.1, &cd.methods.first().unwrap().name.1),
1457             coeffects_of_scope,
1458         );
1460         // Add lambda captured vars to current captured vars
1461         for var in lambda_vars_clone.into_iter() {
1462             state.add_var(scope, var)
1463         }
1464         for x in current_generics.iter() {
1465             state.capture_state.generics.insert(x.to_string());
1466         }
1468         state.closures.push(cd);
1470         Ok(Expr_::mk_efun(inline_fundef, use_vars))
1471     }
1473     fn convert_meth_caller_to_func_ptr(
1474         &mut self,
1475         scope: &Scope<'_, '_>,
1476         pos: &Pos,
1477         pc: &Pos,
1478         cls: &str,
1479         pf: &Pos,
1480         fname: &str,
1481     ) -> Expr_ {
1482         let pos = || pos.clone();
1483         let cname = match scope.as_class_summary() {
1484             Some(cd) => &cd.name.1,
1485             None => "",
1486         };
1487         let mangle_name = string_utils::mangle_meth_caller(cls, fname);
1488         let fun_handle = hack_expr!(
1489             pos = pos(),
1490             r#"\__systemlib\meth_caller(#{str(clone(mangle_name))})"#
1491         );
1492         if self
1493             .state()
1494             .named_hoisted_functions
1495             .contains_key(&mangle_name)
1496         {
1497             return fun_handle.2;
1498         }
1499         // AST for: invariant(is_a($o, <cls>), 'object must be an instance of <cls>');
1500         let obj_var = Box::new(Lid(pos(), local_id::make_unscoped("$o")));
1501         let obj_lvar = Expr((), pos(), Expr_::Lvar(obj_var.clone()));
1502         let msg = format!("object must be an instance of ({})", cls);
1503         let assert_invariant = hack_expr!(
1504             pos = pos(),
1505             r#"\HH\invariant(\is_a(#{clone(obj_lvar)}, #{str(clone(cls), pc)}), #{str(msg)})"#
1506         );
1507         // AST for: return $o-><func>(...$args);
1508         let args_var = local_id::make_unscoped("$args");
1509         let variadic_param = make_fn_param(pos(), &args_var, true, false);
1510         let meth_caller_handle = hack_expr!(
1511             pos = pos(),
1512             r#"#obj_lvar->#{id(clone(fname), pf)}(...#{lvar(args_var)})"#
1513         );
1515         let f = Fun_ {
1516             span: pos(),
1517             annotation: (),
1518             readonly_this: None, // TODO(readonly): readonly_this in closure_convert
1519             readonly_ret: None,
1520             ret: TypeHint((), None),
1521             name: Id(pos(), mangle_name.clone()),
1522             tparams: vec![],
1523             where_constraints: vec![],
1524             params: vec![
1525                 make_fn_param(pos(), &obj_var.1, false, false),
1526                 variadic_param,
1527             ],
1528             ctxs: None,
1529             unsafe_ctxs: None,
1530             body: FuncBody {
1531                 fb_ast: vec![
1532                     Stmt(pos(), Stmt_::Expr(Box::new(assert_invariant))),
1533                     Stmt(pos(), Stmt_::Return(Box::new(Some(meth_caller_handle)))),
1534                 ],
1535             },
1536             fun_kind: FunKind::FSync,
1537             user_attributes: vec![UserAttribute {
1538                 name: Id(pos(), "__MethCaller".into()),
1539                 params: vec![Expr((), pos(), Expr_::String(cname.into()))],
1540             }],
1541             external: false,
1542             doc_comment: None,
1543         };
1544         let fd = FunDef {
1545             file_attributes: vec![],
1546             namespace: RcOc::clone(&self.ro_state.empty_namespace),
1547             mode: scope.scope_fmode(),
1548             fun: f,
1549             // TODO(T116039119): Populate value with presence of internal attribute
1550             internal: false,
1551             // TODO: meth_caller should have the visibility of the module it is defined in
1552             module: None,
1553         };
1554         self.state_mut()
1555             .named_hoisted_functions
1556             .insert(mangle_name, fd);
1557         fun_handle.2
1558     }
1560     fn compute_variables_from_fun(
1561         params: &[FunParam],
1562         body: &[Stmt],
1563         explicit_capture: Option<IndexSet<String>>,
1564     ) -> Result<Variables> {
1565         let parameter_names = get_parameter_names(params);
1566         let all_vars = compute_vars(params, &body)?;
1567         let explicit_capture = explicit_capture.unwrap_or_default();
1568         Ok(Variables {
1569             parameter_names,
1570             all_vars,
1571             explicit_capture,
1572         })
1573     }
1576 /// Swap *e with Expr_::Null, then return it with UNSAFE_CAST stripped off.
1577 fn strip_unsafe_casts(e: &mut Expr_) -> Expr_ {
1578     let null = Expr_::mk_null();
1579     let mut e_owned = std::mem::replace(e, null);
1580     /*
1581         If this is a call of the form
1582           HH\FIXME\UNSAFE_CAST(e, ...)
1583         then treat as a no-op by transforming it to
1584           e
1585         Repeat in case there are nested occurrences
1586     */
1587     loop {
1588         match e_owned {
1589             // Must have at least one argument
1590             Expr_::Call(mut x)
1591                 if !x.2.is_empty() && {
1592                     // Function name should be HH\FIXME\UNSAFE_CAST
1593                     if let Expr_::Id(ref id) = (x.0).2 {
1594                         id.1 == pseudo_functions::UNSAFE_CAST
1595                     } else {
1596                         false
1597                     }
1598                 } =>
1599             {
1600                 // Select first argument
1601                 let Expr(_, _, e) = x.2.swap_remove(0).1;
1602                 e_owned = e;
1603             }
1604             _ => break e_owned,
1605         };
1606     }
1609 type CallExpr = Box<(Expr, Vec<Targ>, Vec<(ParamKind, Expr)>, Option<Expr>)>;
1611 fn is_dyn_meth_caller(x: &CallExpr) -> bool {
1612     if let Expr_::Id(ref id) = (x.0).2 {
1613         let name = strip_id(id);
1614         name.eq_ignore_ascii_case("hh\\dynamic_meth_caller")
1615             || name.eq_ignore_ascii_case("hh\\dynamic_meth_caller_force")
1616     } else {
1617         false
1618     }
1621 fn is_meth_caller(x: &CallExpr) -> bool {
1622     if let Expr_::Id(ref id) = (x.0).2 {
1623         let name = strip_id(id);
1624         name.eq_ignore_ascii_case("hh\\meth_caller") || name.eq_ignore_ascii_case("meth_caller")
1625     } else {
1626         false
1627     }
1630 fn is_selflike_keyword(id: &Id) -> bool {
1631     string_utils::is_self(id) || string_utils::is_parent(id) || string_utils::is_static(id)
1634 fn hoist_toplevel_functions(defs: &mut Vec<Def>) {
1635     // Reorder the functions so that they appear first.
1636     let (funs, nonfuns): (Vec<Def>, Vec<Def>) = defs.drain(..).partition(|x| x.is_fun());
1637     defs.extend(funs);
1638     defs.extend(nonfuns);
1641 fn prepare_defs(defs: &mut [Def]) -> usize {
1642     let mut class_count = 0;
1643     let mut typedef_count = 0;
1644     let mut const_count = 0;
1646     for def in defs.iter_mut() {
1647         match def {
1648             Def::Class(x) => {
1649                 x.emit_id = Some(EmitId::EmitId(class_count));
1650                 class_count += 1;
1651             }
1652             Def::Typedef(x) => {
1653                 x.emit_id = Some(EmitId::EmitId(typedef_count));
1654                 typedef_count += 1;
1655             }
1656             Def::Constant(x) => {
1657                 x.emit_id = Some(EmitId::EmitId(const_count));
1658                 const_count += 1;
1659             }
1660             Def::Namespace(_) => {
1661                 // This should have already been flattened by rewrite_program.
1662                 unreachable!()
1663             }
1664             Def::FileAttributes(_)
1665             | Def::Fun(_)
1666             | Def::Module(_)
1667             | Def::SetModule(_)
1668             | Def::NamespaceUse(_)
1669             | Def::SetNamespaceEnv(_)
1670             | Def::Stmt(_) => {}
1671         }
1672     }
1674     class_count as usize
1677 pub fn convert_toplevel_prog<'arena, 'decl>(
1678     e: &mut Emitter<'arena, 'decl>,
1679     defs: &mut Vec<Def>,
1680     namespace_env: RcOc<namespace_env::Env>,
1681 ) -> Result<()> {
1682     let class_count = prepare_defs(defs);
1684     let mut scope = Scope::toplevel(defs.as_slice())?;
1685     let ro_state = ReadOnlyState {
1686         class_count,
1687         empty_namespace: RcOc::clone(&namespace_env),
1688         for_debugger_eval: e.for_debugger_eval,
1689         options: e.options(),
1690     };
1691     let state = State::initial_state(namespace_env);
1693     let mut visitor = ClosureVisitor {
1694         alloc: e.alloc,
1695         state: Some(state),
1696         ro_state: &ro_state,
1697         phantom: Default::default(),
1698     };
1700     for def in defs.iter_mut() {
1701         visitor.visit_def(&mut scope, def)?;
1702         match def {
1703             Def::SetNamespaceEnv(x) => {
1704                 visitor.state_mut().set_namespace(RcOc::clone(&*x));
1705             }
1706             Def::Class(_)
1707             | Def::Constant(_)
1708             | Def::FileAttributes(_)
1709             | Def::Fun(_)
1710             | Def::Module(_)
1711             | Def::SetModule(_)
1712             | Def::Namespace(_)
1713             | Def::NamespaceUse(_)
1714             | Def::Stmt(_)
1715             | Def::Typedef(_) => {}
1716         }
1717     }
1719     let mut state = visitor.state.take().unwrap();
1720     state.record_function_state(get_unique_id_for_main(), HhasCoeffects::default());
1721     hoist_toplevel_functions(defs);
1722     let named_fun_defs = state
1723         .named_hoisted_functions
1724         .into_iter()
1725         .map(|(_, fd)| Def::mk_fun(fd));
1726     defs.splice(0..0, named_fun_defs);
1727     for class in state.closures.into_iter() {
1728         defs.push(Def::mk_class(class));
1729     }
1730     *e.emit_global_state_mut() = state.global_state;
1731     Ok(())