Rename directory hhbc to hackc
[hiphop-php.git] / hphp / hack / src / hackc / compile / closure_convert.rs
blob4c6cd93b5b9561ba3f302a7c83b4f70be119fc3d
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 ast_scope::{Lambda, LongLambda, Scope as AstScope, ScopeItem as AstScopeItem};
7 use env::emitter::Emitter;
8 use global_state::{ClosureEnclosingClassInfo, GlobalState};
9 use hash::HashSet;
10 use hhas_coeffects::HhasCoeffects;
11 use hhbc_assertion_utils::*;
12 use hhbc_id::class;
13 use hhbc_string_utils as string_utils;
14 use instruction_sequence::{unrecoverable, Error, Result};
15 use itertools::{Either, EitherOrBoth::*, Itertools};
16 use naming_special_names_rust::{
17     fb, pseudo_consts, pseudo_functions, special_idents, superglobals,
19 use ocamlrep::rc::RcOc;
20 use options::{CompilerFlags, HhvmFlags, Options};
21 use oxidized::{
22     aast_defs,
23     aast_visitor::{visit_mut, AstParams, NodeMut, VisitorMut},
24     ast::*,
25     ast_defs::ParamKind,
26     file_info::Mode,
27     local_id, namespace_env,
28     relative_path::{Prefix, RelativePath},
29     s_map::SMap,
31 use std::path::PathBuf;
32 use unique_id_builder::*;
33 use unique_list::UniqueList;
35 type Scope<'a, 'arena> = AstScope<'a, 'arena>;
36 type ScopeItem<'a, 'arena> = AstScopeItem<'a, 'arena>;
38 #[derive(Debug, Clone)] // TODO(hrust): Clone is used when bactracking now, can we somehow avoid it?
39 struct Variables {
40     /// all variables declared/used in the scope
41     all_vars: HashSet<String>,
42     /// names of parameters if scope correspond to a function
43     parameter_names: HashSet<String>,
46 #[derive(Debug, Clone)] // TODO(hrust): do we need clone
47 struct Env<'a, 'arena> {
48     /// What is the current context?
49     // TODO(hrust) VisitorMut doesn't provide an interface
50     // where a reference to visited NodeMut outlives the Context type (in this case Env<'a>),
51     // so we have no choice but to clone in ach ScopeItem (i.e., can't se Borrowed for 'a)
53     /// Span of function/method body
54     pos: Pos, // TODO(hrust) change to &'a Pos after dependent Visitor/Node lifetime is fixed.
55     /// What is the current context?
56     scope: Scope<'a, 'arena>,
57     variable_scopes: Vec<Variables>,
58     /// How many existing classes are there?
59     defined_class_count: usize,
60     /// How many existing records are there?
61     #[allow(dead_code)]
62     defined_record_count: usize,
63     /// How many existing functions are there?
64     #[allow(dead_code)]
65     defined_function_count: usize,
66     /// Are we immediately in a using statement?
67     in_using: bool,
68     /// Global compiler/hack options
69     options: &'a Options,
70     /// For debugger eval
71     for_debugger_eval: bool,
74 #[derive(Default, Clone)]
75 struct PerFunctionState {
76     pub has_finally: bool,
79 impl<'a, 'arena> Env<'a, 'arena> {
80     pub fn toplevel(
81         class_count: usize,
82         record_count: usize,
83         function_count: usize,
84         defs: &[Def],
85         options: &'a Options,
86         for_debugger_eval: bool,
87     ) -> Result<Self> {
88         let scope = Scope::toplevel();
89         let all_vars = get_vars(&[], Either::Left(defs))?;
91         Ok(Self {
92             pos: Pos::make_none(),
93             scope,
94             variable_scopes: vec![Variables {
95                 all_vars,
96                 parameter_names: HashSet::default(),
97             }],
98             defined_class_count: class_count,
99             defined_record_count: record_count,
100             defined_function_count: function_count,
101             in_using: false,
102             options,
103             for_debugger_eval,
104         })
105     }
107     fn with_function_like_(
108         &mut self,
109         e: ScopeItem<'a, 'arena>,
110         _is_closure_body: bool,
111         params: &[FunParam],
112         pos: Pos,
113         body: &[Stmt],
114     ) -> Result<()> {
115         self.pos = pos;
116         self.scope.push_item(e);
117         let all_vars = get_vars(params, Either::Right(body))?;
118         Ok(self.variable_scopes.push(Variables {
119             parameter_names: get_parameter_names(params),
120             all_vars,
121         }))
122     }
124     fn with_function_like(
125         &mut self,
126         e: ScopeItem<'a, 'arena>,
127         is_closure_body: bool,
128         fd: &Fun_,
129     ) -> Result<()> {
130         self.with_function_like_(
131             e,
132             is_closure_body,
133             &fd.params,
134             fd.span.clone(),
135             &fd.body.fb_ast.as_slice(),
136         )
137     }
139     fn with_function(&mut self, fd: &FunDef) -> Result<()> {
140         self.with_function_like(
141             ScopeItem::Function(ast_scope::Fun::new_rc(fd)),
142             false,
143             &fd.fun,
144         )
145     }
147     fn with_method(&mut self, md: &Method_) -> Result<()> {
148         self.with_function_like_(
149             ScopeItem::Method(ast_scope::Method::new_rc(md)),
150             false,
151             &md.params,
152             md.span.clone(),
153             &md.body.fb_ast,
154         )
155     }
157     fn with_lambda(&mut self, alloc: &'arena bumpalo::Bump, fd: &Fun_) -> Result<()> {
158         let is_async = fd.fun_kind.is_async();
159         let coeffects = HhasCoeffects::from_ast(alloc, &fd.ctxs, &fd.params, vec![], vec![]);
161         let lambda = Lambda {
162             is_async,
163             coeffects,
164         };
165         self.with_function_like(ScopeItem::Lambda(lambda), true, fd)
166     }
168     fn with_longlambda(&mut self, alloc: &'arena bumpalo::Bump, fd: &Fun_) -> Result<()> {
169         let is_async = fd.fun_kind.is_async();
170         let coeffects = HhasCoeffects::from_ast(alloc, &fd.ctxs, &fd.params, vec![], vec![]);
172         let long_lambda = LongLambda {
173             is_async,
174             coeffects,
175         };
176         self.with_function_like(ScopeItem::LongLambda(long_lambda), true, fd)
177     }
179     fn with_class(&mut self, cd: &Class_) {
180         self.scope = Scope::toplevel();
181         self.scope
182             .push_item(ScopeItem::Class(ast_scope::Class::new_rc(cd)))
183     }
185     fn with_in_using<F, R>(&mut self, in_using: bool, mut f: F) -> R
186     where
187         F: FnMut(&mut Self) -> R,
188     {
189         let old_in_using = self.in_using;
190         self.in_using = in_using;
191         let r = f(self);
192         self.in_using = old_in_using;
193         r
194     }
196     fn check_if_in_async_context(&self) -> Result<()> {
197         let check_valid_fun_kind = |name, kind: FunKind| {
198             if !kind.is_async() {
199                 Err(emit_fatal::raise_fatal_parse(
200                     &self.pos,
201                     format!(
202                         "Function '{}' contains 'await' but is not declared as async.",
203                         string_utils::strip_global_ns(name)
204                     ),
205                 ))
206             } else {
207                 Ok(())
208             }
209         };
210         let check_lambda = |is_async: bool| {
211             if !is_async {
212                 Err(emit_fatal::raise_fatal_parse(
213                     &self.pos,
214                     "Await may only appear in an async function",
215                 ))
216             } else {
217                 Ok(())
218             }
219         };
220         let head = self.scope.iter().next();
221         use ScopeItem as S;
222         match head {
223             None => Err(emit_fatal::raise_fatal_parse(
224                 &self.pos,
225                 "'await' can only be used inside a function",
226             )),
227             Some(S::Lambda(l)) => check_lambda(l.is_async),
228             Some(S::LongLambda(l)) => check_lambda(l.is_async),
229             Some(S::Class(_)) => Ok(()), /* Syntax error, wont get here */
230             Some(S::Function(fd)) => check_valid_fun_kind(&fd.get_name().1, fd.get_fun_kind()),
231             Some(S::Method(md)) => check_valid_fun_kind(&md.get_name().1, md.get_fun_kind()),
232         }
233     }
236 struct State<'arena> {
237     // Number of closures created in the current function
238     closure_cnt_per_fun: u32,
239     // Free variables computed so far
240     captured_vars: UniqueList<String>,
241     captured_this: bool,
242     captured_generics: UniqueList<String>,
243     // Closure classes
244     closures: Vec<Class_>,
245     /// Hoisted meth_caller functions
246     named_hoisted_functions: SMap<FunDef>,
247     // The current namespace environment
248     namespace: RcOc<namespace_env::Env>,
249     // Empty namespace as constructed by parser
250     empty_namespace: RcOc<namespace_env::Env>,
251     // information about current function
252     current_function_state: PerFunctionState,
253     // accumulated information about program
254     global_state: GlobalState<'arena>,
257 impl<'arena> State<'arena> {
258     pub fn initial_state(empty_namespace: RcOc<namespace_env::Env>) -> Self {
259         Self {
260             namespace: RcOc::clone(&empty_namespace),
261             empty_namespace,
262             closure_cnt_per_fun: 0,
263             captured_vars: UniqueList::new(),
264             captured_this: false,
265             captured_generics: UniqueList::new(),
266             closures: vec![],
267             named_hoisted_functions: SMap::new(),
268             current_function_state: PerFunctionState::default(),
269             global_state: GlobalState::default(),
270         }
271     }
273     pub fn reset_function_counts(&mut self) {
274         self.closure_cnt_per_fun = 0;
275     }
277     pub fn record_function_state(
278         &mut self,
279         key: String,
280         fun: PerFunctionState,
281         coeffects_of_scope: HhasCoeffects<'arena>,
282         num_closures: u32,
283     ) {
284         if fun.has_finally {
285             self.global_state.functions_with_finally.insert(key.clone());
286         }
288         if !coeffects_of_scope.get_static_coeffects().is_empty() {
289             self.global_state
290                 .lambda_coeffects_of_scope
291                 .insert(key.clone(), coeffects_of_scope);
292         }
293         if num_closures > 0 {
294             self.global_state.num_closures.insert(key, num_closures);
295         }
296     }
298     // Clear the variables, upon entering a lambda
299     pub fn enter_lambda(&mut self) {
300         self.captured_vars = UniqueList::new();
301         self.captured_this = false;
302         self.captured_generics = UniqueList::new();
303     }
305     pub fn set_namespace(&mut self, namespace: RcOc<namespace_env::Env>) {
306         self.namespace = namespace;
307     }
310 fn total_class_count(env: &Env<'_, '_>, st: &State<'_>) -> usize {
311     st.closures.len() + env.defined_class_count
314 fn should_capture_var(env: &Env<'_, '_>, var: &str) -> bool {
315     // variable used in lambda should be captured if is:
316     // - not contained in lambda parameter list
317     if let Some(true) = env
318         .variable_scopes
319         .last()
320         .map(|x| x.parameter_names.contains(var))
321     {
322         return false;
323     };
324     // AND
325     // - it exists in one of enclosing scopes
326     for x in env
327         .scope
328         .iter()
329         .zip_longest(env.variable_scopes.iter().rev())
330         .skip(1)
331     {
332         match x {
333             Right(vars) => return vars.all_vars.contains(var),
334             Both(scope, vars) => {
335                 if vars.all_vars.contains(var) || vars.parameter_names.contains(var) {
336                     return true;
337                 }
338                 if !scope.is_in_lambda() {
339                     return false;
340                 }
341             }
342             Left(_) => return false,
343         }
344     }
345     false
348 // Add a variable to the captured variables
349 fn add_var(env: &Env<'_, '_>, st: &mut State<'_>, var: &str) {
350     // Don't bother if it's $this, as this is captured implicitly
351     if var == special_idents::THIS {
352         st.captured_this = true;
353     } else if
354     // If it's bound as a parameter or definite assignment, don't add it
355     // Also don't add the pipe variable and superglobals
356     (should_capture_var(env, var))
357         && !(var == special_idents::DOLLAR_DOLLAR || superglobals::is_superglobal(var))
358     {
359         st.captured_vars.add(var.to_string())
360     }
363 fn add_generic(env: &mut Env<'_, '_>, st: &mut State<'_>, var: &str) {
364     let reified_var_position = |is_fun| {
365         let is_reified_var =
366             |param: &Tparam| param.reified != ReifyKind::Erased && param.name.1 == var;
367         if is_fun {
368             env.scope.get_fun_tparams().iter().position(is_reified_var)
369         } else {
370             env.scope
371                 .get_class_tparams()
372                 .iter()
373                 .position(is_reified_var)
374         }
375     };
377     if let Some(i) = reified_var_position(true) {
378         let var = string_utils::reified::captured_name(true, i);
379         st.captured_generics.add(var)
380     } else if let Some(i) = reified_var_position(false) {
381         let var = string_utils::reified::captured_name(false, i);
382         st.captured_generics.add(var)
383     }
386 fn get_vars(params: &[FunParam], body: ast_body::AstBody<'_>) -> Result<HashSet<String>> {
387     decl_vars::vars_from_ast(params, &body).map_err(unrecoverable)
390 fn get_parameter_names(params: &[FunParam]) -> HashSet<String> {
391     params.iter().map(|p| p.name.to_string()).collect()
394 fn strip_id(id: &Id) -> &str {
395     string_utils::strip_global_ns(&id.1)
398 fn make_class_name(cd: &ast_scope::Class<'_>) -> String {
399     string_utils::mangle_xhp_id(strip_id(cd.get_name()).to_string())
402 fn make_scope_name(ns: &RcOc<namespace_env::Env>, scope: &ast_scope::Scope<'_, '_>) -> String {
403     let mut parts: Vec<String> = vec![];
404     for sub_scope in scope.iter_subscopes() {
405         match sub_scope.last() {
406             None => {
407                 return match &ns.name {
408                     None => String::new(),
409                     Some(n) => format!("{}\\", n),
410                 };
411             }
412             Some(ast_scope::ScopeItem::Class(x)) => {
413                 parts.push(make_class_name(&x));
414                 break;
415             }
416             Some(ast_scope::ScopeItem::Function(x)) => {
417                 let fname = strip_id(x.get_name());
418                 parts.push(
419                     Scope::get_subscope_class(sub_scope)
420                         .map(|cd| make_class_name(cd) + "::")
421                         .unwrap_or_default()
422                         + fname,
423                 );
424                 break;
425             }
426             Some(ast_scope::ScopeItem::Method(x)) => {
427                 parts.push(strip_id(x.get_name()).to_string());
428                 if !parts.last().map_or(false, |x| x.ends_with("::")) {
429                     parts.push("::".into())
430                 };
431             }
432             _ => {}
433         }
434     }
435     parts.reverse();
436     parts.join("")
439 fn make_closure_name(env: &Env<'_, '_>, st: &State<'_>) -> String {
440     let per_fun_idx = st.closure_cnt_per_fun;
441     string_utils::closures::mangle_closure(&make_scope_name(&st.namespace, &env.scope), per_fun_idx)
444 fn make_closure(
445     class_num: usize,
446     p: Pos,
447     env: &Env<'_, '_>,
448     st: &State<'_>,
449     lambda_vars: Vec<String>,
450     fun_tparams: Vec<Tparam>,
451     class_tparams: Vec<Tparam>,
452     is_static: bool,
453     mode: Mode,
454     mut fd: Fun_,
455 ) -> (Fun_, Class_) {
456     let md = Method_ {
457         span: fd.span.clone(),
458         annotation: fd.annotation,
459         final_: false,
460         abstract_: false,
461         static_: is_static,
462         readonly_this: fd.readonly_this.is_some(),
463         visibility: Visibility::Public,
464         name: Id(fd.name.0.clone(), "__invoke".into()),
465         tparams: fun_tparams,
466         where_constraints: fd.where_constraints.clone(),
467         variadic: fd.variadic.clone(),
468         params: fd.params.clone(),
469         ctxs: fd.ctxs.clone(),
470         unsafe_ctxs: None, // TODO(T70095684)
471         body: fd.body.clone(),
472         fun_kind: fd.fun_kind,
473         user_attributes: fd.user_attributes.clone(),
474         readonly_ret: None, // readonly_ret on closure_convert
475         ret: fd.ret.clone(),
476         external: false,
477         doc_comment: fd.doc_comment.clone(),
478     };
480     let make_class_var = |name: &str| ClassVar {
481         final_: false,
482         xhp_attr: None,
483         abstract_: false,
484         readonly: false, // readonly on closure_convert
485         visibility: Visibility::Private,
486         type_: TypeHint((), None),
487         id: Id(p.clone(), name.into()),
488         expr: None,
489         user_attributes: vec![],
490         doc_comment: None,
491         is_promoted_variadic: false,
492         is_static: false,
493         span: p.clone(),
494     };
496     let cvl = lambda_vars
497         .iter()
498         .map(|name| make_class_var(string_utils::locals::strip_dollar(name)));
500     let cd = Class_ {
501         span: p.clone(),
502         annotation: fd.annotation,
503         mode,
504         user_attributes: vec![],
505         file_attributes: vec![],
506         final_: false,
507         is_xhp: false,
508         has_xhp_keyword: false,
509         kind: ClassishKind::Cclass(Abstraction::Concrete),
510         name: Id(p.clone(), make_closure_name(env, st)),
511         tparams: class_tparams,
512         extends: vec![Hint(
513             p.clone(),
514             Box::new(Hint_::Happly(Id(p.clone(), "Closure".into()), vec![])),
515         )],
516         uses: vec![],
517         use_as_alias: vec![],
518         insteadof_alias: vec![],
519         xhp_attr_uses: vec![],
520         xhp_category: None,
521         reqs: vec![],
522         implements: vec![],
523         where_constraints: vec![],
524         consts: vec![],
525         typeconsts: vec![],
526         vars: cvl.collect(),
527         methods: vec![md],
528         attributes: vec![],
529         xhp_children: vec![],
530         xhp_attrs: vec![],
531         namespace: RcOc::clone(&st.empty_namespace),
532         enum_: None,
533         doc_comment: None,
534         emit_id: Some(EmitId::Anonymous),
535     };
537     // TODO(hrust): can we reconstruct fd here from the scratch?
538     fd.name = Id(p.clone(), class_num.to_string());
539     (fd, cd)
542 // Translate special identifiers __CLASS__, __METHOD__ and __FUNCTION__ into
543 // literal strings. It's necessary to do this before closure conversion
544 // because the enclosing class will be changed.
545 fn convert_id(env: &Env<'_, '_>, Id(p, s): Id) -> Expr_ {
546     let ret = |newstr| Expr_::mk_string(newstr);
547     let name = |c: &ast_scope::Class<'_>| {
548         Expr_::mk_string(string_utils::mangle_xhp_id(strip_id(c.get_name()).to_string()).into())
549     };
551     match s {
552         _ if s.eq_ignore_ascii_case(pseudo_consts::G__TRAIT__) => match env.scope.get_class() {
553             Some(c) if c.get_kind() == ClassishKind::Ctrait => name(c),
554             _ => ret("".into()),
555         },
556         _ if s.eq_ignore_ascii_case(pseudo_consts::G__CLASS__) => match env.scope.get_class() {
557             Some(c) if c.get_kind() != ClassishKind::Ctrait => name(c),
558             Some(_) => Expr_::mk_id(Id(p, s)),
559             None => ret("".into()),
560         },
561         _ if s.eq_ignore_ascii_case(pseudo_consts::G__METHOD__) => {
562             let (prefix, is_trait) = match env.scope.get_class() {
563                 None => ("".into(), false),
564                 Some(cd) => (
565                     string_utils::mangle_xhp_id(strip_id(cd.get_name()).to_string()) + "::",
566                     cd.get_kind() == ClassishKind::Ctrait,
567                 ),
568             };
569             // for lambdas nested in trait methods HHVM replaces __METHOD__
570             // with enclosing method name - do the same and bubble up from lambdas *
571             let scope = env.scope.iter().find(|x| !(is_trait && x.is_in_lambda()));
573             match scope {
574                 Some(ScopeItem::Function(fd)) => ret((prefix + strip_id(fd.get_name())).into()),
575                 Some(ScopeItem::Method(md)) => ret((prefix + strip_id(md.get_name())).into()),
576                 Some(ScopeItem::Lambda(_)) | Some(ScopeItem::LongLambda(_)) => {
577                     ret((prefix + "{closure}").into())
578                 }
579                 // PHP weirdness: __METHOD__ inside a class outside a method returns class name
580                 Some(ScopeItem::Class(cd)) => ret(strip_id(cd.get_name()).into()),
581                 _ => ret("".into()),
582             }
583         }
584         _ if s.eq_ignore_ascii_case(pseudo_consts::G__FUNCTION__) => match env.scope.items.last() {
585             Some(ScopeItem::Function(fd)) => ret(strip_id(fd.get_name()).into()),
586             Some(ScopeItem::Method(md)) => ret(strip_id(md.get_name()).into()),
587             Some(ScopeItem::Lambda(_)) | Some(ScopeItem::LongLambda(_)) => ret("{closure}".into()),
588             _ => ret("".into()),
589         },
590         _ if s.eq_ignore_ascii_case(pseudo_consts::G__LINE__) => {
591             // If the expression goes on multi lines, we return the last line
592             let (_, line, _, _) = p.info_pos_extended();
593             Expr_::mk_int(line.to_string())
594         }
595         _ => Expr_::mk_id(Id(p, s)),
596     }
599 fn visit_class_id<'a, 'arena>(
600     env: &mut Env<'a, 'arena>,
601     self_: &mut ClosureConvertVisitor<'a, 'arena>,
602     cid: &mut ClassId,
603 ) -> Result<()> {
604     Ok(if let ClassId(_, _, ClassId_::CIexpr(e)) = cid {
605         self_.visit_expr(env, e)?;
606     })
609 fn make_info(c: &ast_scope::Class<'_>) -> ClosureEnclosingClassInfo {
610     ClosureEnclosingClassInfo {
611         kind: c.get_kind(),
612         name: c.get_name().1.clone(),
613         parent_class_name: match c.get_extends() {
614             [x] => x.as_happly().map(|(id, _args)| id.1.clone()),
615             _ => None,
616         },
617     }
619 // Closure-convert a lambda expression, with use_vars_opt = Some vars
620 // if there is an explicit `use` clause.
621 fn convert_lambda<'a, 'arena>(
622     alloc: &'arena bumpalo::Bump,
623     env: &mut Env<'a, 'arena>,
624     self_: &mut ClosureConvertVisitor<'a, 'arena>,
625     mut fd: Fun_,
626     use_vars_opt: Option<Vec<aast_defs::Lid>>,
627 ) -> Result<Expr_> {
628     let is_long_lambda = use_vars_opt.is_some();
629     let st = &mut self_.state;
631     // Remember the current capture and defined set across the lambda
632     let captured_this = st.captured_this;
633     let captured_vars = st.captured_vars.clone();
634     let captured_generics = st.captured_generics.clone();
635     let old_function_state = st.current_function_state.clone();
636     let coeffects_of_scope = env.scope.coeffects_of_scope(alloc);
637     st.enter_lambda();
638     if let Some(user_vars) = &use_vars_opt {
639         for aast_defs::Lid(p, id) in user_vars.iter() {
640             if local_id::get_name(id) == special_idents::THIS {
641                 return Err(emit_fatal::raise_fatal_parse(
642                     p,
643                     "Cannot use $this as lexical variable",
644                 ));
645             }
646         }
647     }
648     let lambda_env = &mut env.clone();
650     if use_vars_opt.is_some() {
651         lambda_env.with_longlambda(alloc, &fd)?
652     } else {
653         lambda_env.with_lambda(alloc, &fd)?
654     };
655     let function_state = convert_function_like_body(self_, lambda_env, &mut fd.body)?;
656     for param in &mut fd.params {
657         self_.visit_type_hint(lambda_env, &mut param.type_hint)?;
658     }
659     self_.visit_type_hint(lambda_env, &mut fd.ret)?;
661     let st = &mut self_.state;
662     st.closure_cnt_per_fun += 1;
664     let current_generics = st.captured_generics.clone();
666     // TODO(hrust): produce real unique local ids
667     let fresh_lid = |name: String| aast_defs::Lid(Pos::make_none(), (12345, name));
669     let lambda_vars: Vec<&String> = st
670         .captured_vars
671         .iter()
672         .chain(current_generics.iter())
673         // HHVM lists lambda vars in descending order - do the same
674         .sorted()
675         .rev()
676         .collect();
678     // Remove duplicates, (not efficient, but unlikely to be large),
679     // remove variables that are actually just parameters
680     let use_vars_opt: Option<Vec<Lid>> = use_vars_opt.map(|use_vars| {
681         use_vars
682             .into_iter()
683             .rev()
684             .unique_by(|lid| lid.name().to_string())
685             .filter(|x| !fd.params.iter().any(|y| x.name() == &y.name))
686             .collect::<Vec<_>>()
687             .into_iter()
688             .rev()
689             .collect()
690     });
692     // For lambdas with explicit `use` variables, we ignore the computed
693     // capture set and instead use the explicit set
694     let (lambda_vars, use_vars): (Vec<String>, Vec<Lid>) = match use_vars_opt {
695         None => (
696             lambda_vars.iter().map(|x| x.to_string()).collect(),
697             lambda_vars
698                 .iter()
699                 .map(|x| fresh_lid(x.to_string()))
700                 .collect(),
701         ),
702         Some(use_vars) => {
703             // We still need to append the generics
704             (
705                 use_vars
706                     .iter()
707                     .map(|x| x.name())
708                     .chain(current_generics.iter())
709                     .map(|x| x.to_string())
710                     .collect(),
711                 use_vars
712                     .iter()
713                     .cloned()
714                     .chain(current_generics.iter().map(|x| fresh_lid(x.to_string())))
715                     .collect(),
716             )
717         }
718     };
720     let fun_tparams = lambda_env.scope.get_fun_tparams().to_vec();
721     let class_tparams = lambda_env.scope.get_class_tparams().to_vec();
722     let class_num = total_class_count(lambda_env, st);
724     let is_static = if is_long_lambda {
725         // long lambdas are never static
726         false
727     } else {
728         // short lambdas can be made static if they don't capture this in
729         // any form (including any nested lambdas)
730         !st.captured_this
731     };
733     // check if something can be promoted to static based on enclosing scope
734     let is_static = is_static || lambda_env.scope.is_static();
736     let pos = fd.span.clone();
737     let lambda_vars_clone = lambda_vars.clone();
738     let (inline_fundef, cd) = make_closure(
739         class_num,
740         pos,
741         lambda_env,
742         st,
743         lambda_vars,
744         fun_tparams,
745         class_tparams,
746         is_static,
747         get_scope_fmode(&env.scope),
748         fd,
749     );
751     if is_long_lambda {
752         st.global_state
753             .explicit_use_set
754             .insert(inline_fundef.name.1.clone());
755     }
757     let closure_class_name = &cd.name.1;
758     if let Some(cd) = env.scope.get_class() {
759         st.global_state
760             .closure_enclosing_classes
761             .insert(closure_class_name.clone(), make_info(cd));
762     }
763     // adjust captured $this information if lambda that was just processed was converted into
764     // non-static one
765     let captured_this = captured_this || !is_static;
767     // Restore capture and defined set
768     st.captured_vars = captured_vars;
769     st.captured_this = captured_this;
770     st.captured_generics = captured_generics;
771     st.current_function_state = old_function_state;
772     st.global_state
773         .closure_namespaces
774         .insert(closure_class_name.clone(), st.namespace.clone());
775     st.record_function_state(
776         get_unique_id_for_method(&cd.name.1, &cd.methods.first().unwrap().name.1),
777         function_state,
778         coeffects_of_scope,
779         0,
780     );
781     // back to using env instead of lambda_env here
783     // Add lambda captured vars to current captured vars
784     for var in lambda_vars_clone.iter() {
785         add_var(env, st, var)
786     }
787     for x in current_generics.iter() {
788         st.captured_generics.add(x.to_string());
789     }
791     st.closures.push(cd);
792     Ok(Expr_::mk_efun(inline_fundef, use_vars))
795 fn make_fn_param(pos: Pos, lid: &LocalId, is_variadic: bool, is_inout: bool) -> FunParam {
796     FunParam {
797         annotation: (),
798         type_hint: TypeHint((), None),
799         is_variadic,
800         pos: pos.clone(),
801         name: local_id::get_name(lid).clone(),
802         expr: None,
803         callconv: if is_inout {
804             ParamKind::Pinout(pos)
805         } else {
806             ParamKind::Pnormal
807         },
808         readonly: None, // TODO
809         user_attributes: vec![],
810         visibility: None,
811     }
814 fn get_scope_fmode(scope: &Scope<'_, '_>) -> Mode {
815     scope
816         .iter()
817         .find_map(|item| match item {
818             ScopeItem::Class(cd) => Some(cd.get_mode()),
819             ScopeItem::Function(fd) => Some(fd.get_mode()),
820             _ => None,
821         })
822         .unwrap_or(Mode::Mstrict)
825 fn convert_meth_caller_to_func_ptr(
826     env: &Env<'_, '_>,
827     st: &mut ClosureConvertVisitor<'_, '_>,
828     pos: &Pos,
829     pc: &Pos,
830     cls: &str,
831     pf: &Pos,
832     fname: &str,
833 ) -> Expr_ {
834     fn get_scope_fmode(scope: &Scope<'_, '_>) -> Mode {
835         scope
836             .iter()
837             .find_map(|item| match item {
838                 ScopeItem::Class(cd) => Some(cd.get_mode()),
839                 ScopeItem::Function(fd) => Some(fd.get_mode()),
840                 _ => None,
841             })
842             .unwrap_or(Mode::Mstrict)
843     }
844     // TODO: Move dummy variable to tasl.rs once it exists.
845     let dummy_saved_env = ();
846     let pos = || pos.clone();
847     let expr_id = |name: String| Expr((), pos(), Expr_::mk_id(Id(pos(), name)));
848     let cname = match env.scope.get_class() {
849         Some(cd) => &cd.get_name().1,
850         None => "",
851     };
852     let mangle_name = string_utils::mangle_meth_caller(cls, fname);
853     let fun_handle: Expr_ = Expr_::mk_call(
854         expr_id("\\__systemlib\\meth_caller".into()),
855         vec![],
856         vec![(
857             ParamKind::Pnormal,
858             Expr((), pos(), Expr_::mk_string(mangle_name.clone().into())),
859         )],
860         None,
861     );
862     if st.state.named_hoisted_functions.contains_key(&mangle_name) {
863         return fun_handle;
864     }
865     // AST for: invariant(is_a($o, <cls>), 'object must be an instance of <cls>');
866     let obj_var = Box::new(Lid(pos(), local_id::make_unscoped("$o")));
867     let obj_lvar = Expr((), pos(), Expr_::Lvar(obj_var.clone()));
868     let assert_invariant = Expr(
869         (),
870         pos(),
871         Expr_::mk_call(
872             expr_id("\\HH\\invariant".into()),
873             vec![],
874             vec![
875                 (
876                     ParamKind::Pnormal,
877                     Expr(
878                         (),
879                         pos(),
880                         Expr_::mk_call(
881                             expr_id("\\is_a".into()),
882                             vec![],
883                             vec![
884                                 (ParamKind::Pnormal, obj_lvar.clone()),
885                                 (
886                                     ParamKind::Pnormal,
887                                     Expr((), pc.clone(), Expr_::String(cls.into())),
888                                 ),
889                             ],
890                             None,
891                         ),
892                     ),
893                 ),
894                 (
895                     ParamKind::Pnormal,
896                     Expr(
897                         (),
898                         pos(),
899                         Expr_::String(format!("object must be an instance of ({})", cls).into()),
900                     ),
901                 ),
902             ],
903             None,
904         ),
905     );
906     // AST for: return $o-><func>(...$args);
907     let args_var = Box::new(Lid(pos(), local_id::make_unscoped("$args")));
908     let variadic_param = make_fn_param(pos(), &args_var.1, true, false);
909     let meth_caller_handle = Expr(
910         (),
911         pos(),
912         Expr_::mk_call(
913             Expr(
914                 (),
915                 pos(),
916                 Expr_::ObjGet(Box::new((
917                     obj_lvar,
918                     Expr((), pos(), Expr_::mk_id(Id(pf.clone(), fname.to_owned()))),
919                     OgNullFlavor::OGNullthrows,
920                     PropOrMethod::IsMethod,
921                 ))),
922             ),
923             vec![],
924             vec![],
925             Some(Expr((), pos(), Expr_::Lvar(args_var))),
926         ),
927     );
929     let f = Fun_ {
930         span: pos(),
931         annotation: dummy_saved_env,
932         readonly_this: None, // TODO(readonly): readonly_this in closure_convert
933         readonly_ret: None,
934         ret: TypeHint((), None),
935         name: Id(pos(), mangle_name.clone()),
936         tparams: vec![],
937         where_constraints: vec![],
938         variadic: FunVariadicity::FVvariadicArg(variadic_param.clone()),
939         params: vec![
940             make_fn_param(pos(), &obj_var.1, false, false),
941             variadic_param,
942         ],
943         ctxs: None,
944         unsafe_ctxs: None,
945         body: FuncBody {
946             fb_ast: vec![
947                 Stmt(pos(), Stmt_::Expr(Box::new(assert_invariant))),
948                 Stmt(pos(), Stmt_::Return(Box::new(Some(meth_caller_handle)))),
949             ],
950         },
951         fun_kind: FunKind::FSync,
952         user_attributes: vec![UserAttribute {
953             name: Id(pos(), "__MethCaller".into()),
954             params: vec![Expr((), pos(), Expr_::String(cname.into()))],
955         }],
956         external: false,
957         doc_comment: None,
958     };
959     let fd = FunDef {
960         file_attributes: vec![],
961         namespace: RcOc::clone(&st.state.empty_namespace),
962         mode: get_scope_fmode(&env.scope),
963         fun: f,
964     };
965     st.state.named_hoisted_functions.insert(mangle_name, fd);
966     fun_handle
969 fn make_dyn_meth_caller_lambda(pos: &Pos, cexpr: &Expr, fexpr: &Expr, force: bool) -> Expr_ {
970     // TODO: Move dummy variable to tasl.rs once it exists.
971     let dummy_saved_env = ();
972     let pos = || pos.clone();
973     let obj_var = Box::new(Lid(pos(), local_id::make_unscoped("$o")));
974     let meth_var = Box::new(Lid(pos(), local_id::make_unscoped("$m")));
975     let obj_lvar = Expr((), pos(), Expr_::Lvar(obj_var.clone()));
976     let meth_lvar = Expr((), pos(), Expr_::Lvar(meth_var.clone()));
977     // AST for: return $o-><func>(...$args);
978     let args_var = Box::new(Lid(pos(), local_id::make_unscoped("$args")));
979     let variadic_param = make_fn_param(pos(), &args_var.1, true, false);
980     let invoke_method = Expr(
981         (),
982         pos(),
983         Expr_::mk_call(
984             Expr(
985                 (),
986                 pos(),
987                 Expr_::ObjGet(Box::new((
988                     obj_lvar,
989                     meth_lvar,
990                     OgNullFlavor::OGNullthrows,
991                     PropOrMethod::IsMethod,
992                 ))),
993             ),
994             vec![],
995             vec![],
996             Some(Expr((), pos(), Expr_::Lvar(args_var))),
997         ),
998     );
999     let attrs = if force {
1000         vec![UserAttribute {
1001             name: Id(pos(), "__DynamicMethCallerForce".into()),
1002             params: vec![],
1003         }]
1004     } else {
1005         vec![]
1006     };
1007     let ctxs = Some(Contexts(
1008         pos(),
1009         vec![Hint::new(
1010             pos(),
1011             Hint_::mk_happly(Id(pos(), string_utils::coeffects::CALLER.into()), vec![]),
1012         )],
1013     ));
1015     let fd = Fun_ {
1016         span: pos(),
1017         annotation: dummy_saved_env,
1018         readonly_this: None, // TODO: readonly_this in closure_convert
1019         readonly_ret: None,  // TODO: readonly_ret in closure convert
1020         ret: TypeHint((), None),
1021         name: Id(pos(), ";anonymous".to_string()),
1022         tparams: vec![],
1023         where_constraints: vec![],
1024         variadic: FunVariadicity::FVvariadicArg(variadic_param.clone()),
1025         params: vec![
1026             make_fn_param(pos(), &obj_var.1, false, false),
1027             make_fn_param(pos(), &meth_var.1, false, false),
1028             variadic_param,
1029         ],
1030         ctxs,
1031         unsafe_ctxs: None,
1032         body: FuncBody {
1033             fb_ast: vec![Stmt(pos(), Stmt_::Return(Box::new(Some(invoke_method))))],
1034         },
1035         fun_kind: FunKind::FSync,
1036         user_attributes: attrs,
1037         external: false,
1038         doc_comment: None,
1039     };
1040     let expr_id = |name: String| Expr((), pos(), Expr_::mk_id(Id(pos(), name)));
1041     let force_val = if force { Expr_::True } else { Expr_::False };
1042     let fun_handle: Expr_ = Expr_::mk_call(
1043         expr_id("\\__systemlib\\dynamic_meth_caller".into()),
1044         vec![],
1045         vec![
1046             (ParamKind::Pnormal, cexpr.clone()),
1047             (ParamKind::Pnormal, fexpr.clone()),
1048             (
1049                 ParamKind::Pnormal,
1050                 Expr((), pos(), Expr_::mk_efun(fd, vec![])),
1051             ),
1052             (ParamKind::Pnormal, Expr((), pos(), force_val)),
1053         ],
1054         None,
1055     );
1056     fun_handle
1059 fn convert_function_like_body<'a, 'arena>(
1060     self_: &mut ClosureConvertVisitor<'a, 'arena>,
1061     env: &mut Env<'a, 'arena>,
1062     body: &mut FuncBody,
1063 ) -> Result<PerFunctionState> {
1064     // reset has_finally values on the state
1065     let old_state = std::mem::take(&mut self_.state.current_function_state);
1066     body.recurse(env, self_.object())?;
1067     // restore old has_finally values
1068     let function_state = std::mem::replace(&mut self_.state.current_function_state, old_state);
1069     Ok(function_state)
1072 fn add_reified_property(tparams: &[Tparam], vars: &mut Vec<ClassVar>) {
1073     if !tparams.iter().all(|t| t.reified == ReifyKind::Erased) {
1074         let p = Pos::make_none();
1075         // varray/vec that holds a list of type structures
1076         // this prop will be initilized during runtime
1077         let hint = Hint(
1078             p.clone(),
1079             Box::new(Hint_::Happly(Id(p.clone(), "\\HH\\varray".into()), vec![])),
1080         );
1081         vars.insert(
1082             0,
1083             ClassVar {
1084                 final_: false,
1085                 xhp_attr: None,
1086                 is_promoted_variadic: false,
1087                 doc_comment: None,
1088                 abstract_: false,
1089                 readonly: false,
1090                 visibility: Visibility::Private,
1091                 type_: TypeHint((), Some(hint)),
1092                 id: Id(p.clone(), string_utils::reified::PROP_NAME.into()),
1093                 expr: None,
1094                 user_attributes: vec![],
1095                 is_static: false,
1096                 span: p,
1097             },
1098         )
1099     }
1102 fn count_classes(defs: &[Def]) -> usize {
1103     defs.iter().filter(|x| x.is_class()).count()
1106 fn count_records(defs: &[Def]) -> usize {
1107     defs.iter().filter(|x| x.is_record_def()).count()
1110 struct ClosureConvertVisitor<'a, 'arena> {
1111     alloc: &'arena bumpalo::Bump,
1112     state: State<'arena>,
1113     phantom_lifetime_a: std::marker::PhantomData<&'a ()>,
1116 impl<'ast, 'a, 'arena> VisitorMut<'ast> for ClosureConvertVisitor<'a, 'arena> {
1117     type P = AstParams<Env<'a, 'arena>, Error>;
1119     fn object(&mut self) -> &mut dyn VisitorMut<'ast, P = Self::P> {
1120         self
1121     }
1123     fn visit_method_(&mut self, env: &mut Env<'a, 'arena>, md: &mut Method_) -> Result<()> {
1124         let cls = env.scope.get_class().ok_or_else(|| {
1125             unrecoverable("unexpected scope shape - method is not inside the class")
1126         })?;
1127         // TODO(hrust): not great to have to clone env constantly
1128         let mut env = env.clone();
1129         env.with_method(md)?;
1130         self.state.reset_function_counts();
1131         let function_state = convert_function_like_body(self, &mut env, &mut md.body)?;
1132         self.state.record_function_state(
1133             get_unique_id_for_method(cls.get_name_str(), &md.name.1),
1134             function_state,
1135             HhasCoeffects::default(),
1136             self.state.closure_cnt_per_fun,
1137         );
1138         visit_mut(self, &mut env, &mut md.params)
1139     }
1141     fn visit_class_<'b>(&mut self, env: &mut Env<'a, 'arena>, cd: &'b mut Class_) -> Result<()> {
1142         let mut env = env.clone();
1143         env.with_class(cd);
1144         self.state.reset_function_counts();
1145         visit_mut(self, &mut env, &mut cd.methods)?;
1146         visit_mut(self, &mut env, &mut cd.consts)?;
1147         visit_mut(self, &mut env, &mut cd.vars)?;
1148         visit_mut(self, &mut env, &mut cd.xhp_attrs)?;
1149         visit_mut(self, &mut env, &mut cd.user_attributes)?;
1150         Ok(add_reified_property(&cd.tparams, &mut cd.vars))
1151     }
1153     fn visit_def(&mut self, env: &mut Env<'a, 'arena>, def: &mut Def) -> Result<()> {
1154         match def {
1155             // need to handle it ourselvses, because visit_fun_ is
1156             // called both for toplevel functions and lambdas
1157             Def::Fun(x) => {
1158                 let mut env = env.clone();
1159                 env.with_function(&x)?;
1160                 self.state.reset_function_counts();
1161                 let function_state = convert_function_like_body(self, &mut env, &mut x.fun.body)?;
1162                 self.state.record_function_state(
1163                     get_unique_id_for_function(&x.fun.name.1),
1164                     function_state,
1165                     HhasCoeffects::default(),
1166                     self.state.closure_cnt_per_fun,
1167                 );
1168                 visit_mut(self, &mut env, &mut x.fun.params)?;
1169                 visit_mut(self, &mut env, &mut x.fun.user_attributes)
1170             }
1171             _ => def.recurse(env, self.object()),
1172         }
1173     }
1175     fn visit_hint_(&mut self, env: &mut Env<'a, 'arena>, hint: &mut Hint_) -> Result<()> {
1176         if let Hint_::Happly(id, _) = hint {
1177             add_generic(env, &mut self.state, id.name())
1178         };
1179         hint.recurse(env, self.object())
1180     }
1182     fn visit_stmt_(&mut self, env: &mut Env<'a, 'arena>, stmt: &mut Stmt_) -> Result<()> {
1183         match stmt {
1184             Stmt_::Awaitall(x) => {
1185                 env.check_if_in_async_context()?;
1186                 x.recurse(env, self.object())
1187             }
1188             Stmt_::Do(x) => {
1189                 let (b, e) = (&mut x.0, &mut x.1);
1190                 env.with_in_using(false, |env| visit_mut(self, env, b))?;
1191                 self.visit_expr(env, e)
1192             }
1193             Stmt_::While(x) => {
1194                 let (e, b) = (&mut x.0, &mut x.1);
1195                 self.visit_expr(env, e)?;
1196                 env.with_in_using(false, |env| visit_mut(self, env, b))
1197             }
1198             Stmt_::Foreach(x) => {
1199                 if x.1.is_await_as_v() || x.1.is_await_as_kv() {
1200                     env.check_if_in_async_context()?
1201                 }
1202                 x.recurse(env, self.object())
1203             }
1204             Stmt_::For(x) => {
1205                 let (e1, e2, e3, b) = (&mut x.0, &mut x.1, &mut x.2, &mut x.3);
1207                 for e in e1 {
1208                     self.visit_expr(env, e)?;
1209                 }
1210                 if let Some(e) = e2 {
1211                     self.visit_expr(env, e)?;
1212                 }
1213                 env.with_in_using(false, |env| visit_mut(self, env, b))?;
1214                 for e in e3 {
1215                     self.visit_expr(env, e)?;
1216                 }
1217                 Ok(())
1218             }
1219             Stmt_::Switch(x) => {
1220                 let (e, cl) = (&mut x.0, &mut x.1);
1221                 self.visit_expr(env, e)?;
1222                 env.with_in_using(false, |env| visit_mut(self, env, cl))
1223             }
1224             Stmt_::Try(x) => {
1225                 let (b1, cl, b2) = (&mut x.0, &mut x.1, &mut x.2);
1226                 visit_mut(self, env, b1)?;
1227                 visit_mut(self, env, cl)?;
1228                 visit_mut(self, env, b2)?;
1229                 Ok(self.state.current_function_state.has_finally |= !x.2.is_empty())
1230             }
1231             Stmt_::Using(x) => {
1232                 if x.has_await {
1233                     env.check_if_in_async_context()?;
1234                 }
1235                 for e in &mut x.exprs.1 {
1236                     self.visit_expr(env, e)?;
1237                 }
1238                 env.with_in_using(true, |env| visit_mut(self, env, &mut x.block))?;
1239                 Ok(self.state.current_function_state.has_finally = true)
1240             }
1241             _ => stmt.recurse(env, self.object()),
1242         }
1243     }
1245     //TODO(hrust): do we need special handling for Awaitall?
1246     fn visit_expr(&mut self, env: &mut Env<'a, 'arena>, Expr(_, pos, e): &mut Expr) -> Result<()> {
1247         let null = Expr_::mk_null();
1248         let mut e_owned = std::mem::replace(e, null);
1249         /*
1250             If this is a call of the form
1251               HH\FIXME\UNSAFE_CAST(e, ...)
1252             then treat as a no-op by transforming it to
1253               e
1254             Repeat in case there are nested occurrences
1255         */
1256         loop {
1257             match e_owned {
1258             Expr_::Call(mut x)
1259               // Must have at least one argument
1260               if !x.2.is_empty() && {
1261                 // Function name should be HH\FIXME\UNSAFE_CAST
1262                 if let Expr_::Id(ref id) = (x.0).2 {
1263                   id.1 == pseudo_functions::UNSAFE_CAST
1264                 } else {
1265                   false
1266                 }
1267               } =>{
1268                 // Select first argument
1269                 let Expr(_, _, e) = x.2.swap_remove(0).1;
1270                 e_owned = e;
1271               }
1272             _ => { break; }
1273            };
1274         }
1276         *e = match e_owned {
1277             Expr_::Efun(x) => convert_lambda(self.alloc, env, self, x.0, Some(x.1))?,
1278             Expr_::Lfun(x) => convert_lambda(self.alloc, env, self, x.0, None)?,
1279             Expr_::Lvar(id_orig) => {
1280                 let id = if env.for_debugger_eval
1281                     && local_id::get_name(&id_orig.1) == special_idents::THIS
1282                     && env.scope.is_in_debugger_eval_fun()
1283                 {
1284                     Box::new(Lid(id_orig.0, (0, "$__debugger$this".to_string())))
1285                 } else {
1286                     id_orig
1287                 };
1288                 add_var(env, &mut self.state, local_id::get_name(&id.1));
1289                 Expr_::Lvar(id)
1290             }
1291             Expr_::Id(id) if id.name().starts_with('$') => {
1292                 add_var(env, &mut self.state, id.name());
1293                 add_generic(env, &mut self.state, id.name());
1294                 Expr_::Id(id)
1295             }
1296             Expr_::Id(id) => {
1297                 add_generic(env, &mut self.state, id.name());
1298                 convert_id(env, *id)
1299             }
1300             Expr_::Call(mut x)
1301                 if {
1302                     if let Expr_::Id(ref id) = (x.0).2 {
1303                         strip_id(id).eq_ignore_ascii_case("hh\\dynamic_meth_caller")
1304                             || strip_id(id).eq_ignore_ascii_case("hh\\dynamic_meth_caller_force")
1305                     } else {
1306                         false
1307                     }
1308                 } =>
1309             {
1310                 let force = if let Expr_::Id(ref id) = (x.0).2 {
1311                     strip_id(id).eq_ignore_ascii_case("hh\\dynamic_meth_caller_force")
1312                 } else {
1313                     false
1314                 };
1315                 if let [(pk_c, cexpr), (pk_f, fexpr)] = &mut *x.2 {
1316                     ensure_normal_paramkind(pk_c)?;
1317                     ensure_normal_paramkind(pk_f)?;
1318                     let mut res = make_dyn_meth_caller_lambda(&*pos, &cexpr, &fexpr, force);
1319                     res.recurse(env, self.object())?;
1320                     res
1321                 } else {
1322                     let mut res = Expr_::Call(x);
1323                     res.recurse(env, self.object())?;
1324                     res
1325                 }
1326             }
1327             Expr_::Call(mut x)
1328                 if {
1329                     if let Expr_::Id(ref id) = (x.0).2 {
1330                         let name = strip_id(id);
1331                         ["hh\\meth_caller", "meth_caller"]
1332                             .iter()
1333                             .any(|n| n.eq_ignore_ascii_case(name))
1334                             && env
1335                                 .options
1336                                 .hhvm
1337                                 .flags
1338                                 .contains(HhvmFlags::EMIT_METH_CALLER_FUNC_POINTERS)
1339                     } else {
1340                         false
1341                     }
1342                 } =>
1343             {
1344                 if let [(pk_cls, Expr(_, pc, cls)), (pk_f, Expr(_, pf, func))] = &mut *x.2 {
1345                     ensure_normal_paramkind(pk_cls)?;
1346                     ensure_normal_paramkind(pk_f)?;
1347                     match (&cls, func.as_string()) {
1348                         (Expr_::ClassConst(cc), Some(fname))
1349                             if string_utils::is_class(&(cc.1).1) =>
1350                         {
1351                             let mut cls_const = cls.as_class_const_mut();
1352                             let (cid, _) = match cls_const {
1353                                 None => unreachable!(),
1354                                 Some((ref mut cid, (_, cs))) => (cid, cs),
1355                             };
1356                             use hhbc_id::Id;
1357                             visit_class_id(env, self, cid)?;
1358                             match &cid.2 {
1359                                 cid if cid.as_ciexpr().and_then(|x| x.as_id()).map_or(
1360                                     false,
1361                                     |id| {
1362                                         !(string_utils::is_self(id)
1363                                             || string_utils::is_parent(id)
1364                                             || string_utils::is_static(id))
1365                                     },
1366                                 ) =>
1367                                 {
1368                                     let alloc = bumpalo::Bump::new();
1369                                     let id = cid.as_ciexpr().unwrap().as_id().unwrap();
1370                                     let mangled_class_name =
1371                                         class::ClassType::from_ast_name(&alloc, id.as_ref());
1372                                     let mangled_class_name = mangled_class_name.to_raw_string();
1373                                     convert_meth_caller_to_func_ptr(
1374                                         env,
1375                                         self,
1376                                         &*pos,
1377                                         pc,
1378                                         mangled_class_name,
1379                                         pf,
1380                                         // FIXME: This is not safe--string literals are binary strings.
1381                                         // There's no guarantee that they're valid UTF-8.
1382                                         unsafe { std::str::from_utf8_unchecked(fname.as_slice()) },
1383                                     )
1384                                 }
1385                                 _ => {
1386                                     return Err(emit_fatal::raise_fatal_parse(pc, "Invalid class"));
1387                                 }
1388                             }
1389                         }
1390                         (Expr_::String(cls_name), Some(fname)) => convert_meth_caller_to_func_ptr(
1391                             env,
1392                             self,
1393                             &*pos,
1394                             pc,
1395                             // FIXME: This is not safe--string literals are binary strings.
1396                             // There's no guarantee that they're valid UTF-8.
1397                             unsafe { std::str::from_utf8_unchecked(cls_name.as_slice()) },
1398                             pf,
1399                             // FIXME: This is not safe--string literals are binary strings.
1400                             // There's no guarantee that they're valid UTF-8.
1401                             unsafe { std::str::from_utf8_unchecked(fname.as_slice()) },
1402                         ),
1403                         (_, Some(_)) => {
1404                             return Err(emit_fatal::raise_fatal_parse(
1405                                 pc,
1406                                 "Class must be a Class or string type",
1407                             ));
1408                         }
1409                         (_, _) => {
1410                             return Err(emit_fatal::raise_fatal_parse(
1411                                 pf,
1412                                 "Method name must be a literal string",
1413                             ));
1414                         }
1415                     }
1416                 } else {
1417                     let mut res = Expr_::Call(x);
1418                     res.recurse(env, self.object())?;
1419                     res
1420                 }
1421             }
1422             Expr_::Call(mut x)
1423                 if {
1424                     if let Expr_::Id(ref id) = (x.0).2 {
1425                         let name = strip_id(id);
1426                         ["hh\\meth_caller", "meth_caller"]
1427                             .iter()
1428                             .any(|n| n.eq_ignore_ascii_case(name))
1429                     } else {
1430                         false
1431                     }
1432                 } =>
1433             {
1434                 if let [(pk_cls, Expr(_, pc, cls)), (pk_f, Expr(_, pf, func))] = &mut *x.2 {
1435                     ensure_normal_paramkind(pk_cls)?;
1436                     ensure_normal_paramkind(pk_f)?;
1437                     match (&cls, func.as_string()) {
1438                         (Expr_::ClassConst(cc), Some(_)) if string_utils::is_class(&(cc.1).1) => {
1439                             let mut cls_const = cls.as_class_const_mut();
1440                             let cid = match cls_const {
1441                                 None => unreachable!(),
1442                                 Some((ref mut cid, (_, _))) => cid,
1443                             };
1444                             #[allow(clippy::blocks_in_if_conditions)]
1445                             if cid.as_ciexpr().and_then(|x| x.as_id()).map_or(false, |id| {
1446                                 !(string_utils::is_self(id)
1447                                     || string_utils::is_parent(id)
1448                                     || string_utils::is_static(id))
1449                             }) {
1450                                 let mut res = Expr_::Call(x);
1451                                 res.recurse(env, self.object())?;
1452                                 res
1453                             } else {
1454                                 return Err(emit_fatal::raise_fatal_parse(pc, "Invalid class"));
1455                             }
1456                         }
1457                         (Expr_::String(_), Some(_)) => {
1458                             let mut res = Expr_::Call(x);
1459                             res.recurse(env, self.object())?;
1460                             res
1461                         }
1462                         (_, Some(_)) => {
1463                             return Err(emit_fatal::raise_fatal_parse(
1464                                 pc,
1465                                 "Class must be a Class or string type",
1466                             ));
1467                         }
1468                         (_, _) => {
1469                             return Err(emit_fatal::raise_fatal_parse(
1470                                 pf,
1471                                 "Method name must be a literal string",
1472                             ));
1473                         }
1474                     }
1475                 } else {
1476                     let mut res = Expr_::Call(x);
1477                     res.recurse(env, self.object())?;
1478                     res
1479                 }
1480             }
1481             Expr_::Call(x)
1482                 if (x.0)
1483                     .as_class_get()
1484                     .and_then(|(id, _, _)| id.as_ciexpr())
1485                     .and_then(|x| x.as_id())
1486                     .map_or(false, string_utils::is_parent)
1487                     || (x.0)
1488                         .as_class_const()
1489                         .and_then(|(id, _)| id.as_ciexpr())
1490                         .and_then(|x| x.as_id())
1491                         .map_or(false, string_utils::is_parent) =>
1492             {
1493                 add_var(env, &mut self.state, "$this");
1494                 let mut res = Expr_::Call(x);
1495                 res.recurse(env, self.object())?;
1496                 res
1497             }
1498             Expr_::As(x) if (x.1).is_hlike() => {
1499                 let mut res = x.0;
1500                 res.recurse(env, self.object())?;
1501                 *pos = res.1;
1502                 res.2
1503             }
1504             Expr_::As(x)
1505                 if (x.1)
1506                     .as_happly()
1507                     .map(|(id, args)| {
1508                         (id.name() == fb::INCORRECT_TYPE || id.name() == fb::INCORRECT_TYPE_NO_NS)
1509                             && args.len() == 1
1510                     })
1511                     .unwrap_or_default() =>
1512             {
1513                 let mut res = x.0;
1514                 res.recurse(env, self.object())?;
1515                 *pos = res.1;
1516                 res.2
1517             }
1518             Expr_::ClassGet(mut x) => {
1519                 if let ClassGetExpr::CGstring(id) = &x.1 {
1520                     // T43412864 claims that this does not need to be added into the closure and can be removed
1521                     // There are no relevant HHVM tests checking for it, but there are flib test failures when you try
1522                     // to remove it.
1523                     add_var(env, &mut self.state, &id.1);
1524                 };
1525                 x.recurse(env, self.object())?;
1526                 Expr_::ClassGet(x)
1527             }
1528             Expr_::Await(mut x) => {
1529                 env.check_if_in_async_context()?;
1530                 x.recurse(env, self.object())?;
1531                 Expr_::Await(x)
1532             }
1533             Expr_::ReadonlyExpr(mut x) => {
1534                 x.recurse(env, self.object())?;
1535                 Expr_::ReadonlyExpr(x)
1536             }
1537             Expr_::ExpressionTree(mut x) => {
1538                 x.runtime_expr.recurse(env, self.object())?;
1539                 Expr_::ExpressionTree(x)
1540             }
1541             mut x => {
1542                 x.recurse(env, self.object())?;
1543                 x
1544             }
1545         };
1546         Ok(())
1547     }
1550 fn hoist_toplevel_functions(defs: &mut Program) {
1551     // Reorder the functions so that they appear first.
1552     let (funs, nonfuns): (Vec<Def>, Vec<Def>) = defs.drain(..).partition(|x| x.is_fun());
1553     defs.extend(funs);
1554     defs.extend(nonfuns);
1557 fn flatten_ns(defs: &mut Program) -> Program {
1558     defs.drain(..)
1559         .flat_map(|x| match x {
1560             Def::Namespace(mut x) => flatten_ns(&mut x.1),
1561             _ => vec![x],
1562         })
1563         .collect()
1566 fn extract_debugger_main(
1567     empty_namespace: &RcOc<namespace_env::Env>,
1568     all_defs: &mut Program,
1569 ) -> std::result::Result<(), String> {
1570     let (stmts, mut defs): (Vec<Def>, Vec<Def>) = all_defs.drain(..).partition(|x| x.is_stmt());
1571     let mut vars = decl_vars::vars_from_ast(&[], &Either::Left(&stmts))?
1572         .into_iter()
1573         .collect::<Vec<_>>();
1574     // TODO(hrust) sort is only required when comparing Rust/Ocaml, remove sort after emitter shipped
1575     vars.sort();
1576     let mut stmts = stmts
1577         .into_iter()
1578         .filter_map(|x| x.as_stmt_into())
1579         .collect::<Vec<_>>();
1580     let stmts =
1581         if defs.is_empty() && stmts.len() == 2 && stmts[0].1.is_markup() && stmts[1].1.is_expr() {
1582             let Stmt(p, s) = stmts.pop().unwrap();
1583             let e = s.as_expr_into().unwrap();
1584             let m = stmts.pop().unwrap();
1585             vec![m, Stmt::new(p, Stmt_::mk_return(Some(e)))]
1586         } else {
1587             stmts
1588         };
1589     let p = || Pos::make_none();
1590     let id = |n: &str| Expr((), p(), Expr_::mk_id(Id(p(), n.into())));
1591     let lv = |n: &String| {
1592         Expr(
1593             (),
1594             p(),
1595             Expr_::mk_lvar(Lid(p(), local_id::make_unscoped(n))),
1596         )
1597     };
1598     let mut unsets: Vec<_> = vars
1599         .iter()
1600         .map(|name| {
1601             let unset = Stmt(
1602                 p(),
1603                 Stmt_::mk_expr(Expr(
1604                     (),
1605                     p(),
1606                     Expr_::mk_call(
1607                         id("unset"),
1608                         vec![],
1609                         vec![(ParamKind::Pnormal, lv(&name))],
1610                         None,
1611                     ),
1612                 )),
1613             );
1614             Stmt(
1615                 p(),
1616                 Stmt_::mk_if(
1617                     Expr(
1618                         (),
1619                         p(),
1620                         Expr_::mk_is(
1621                             lv(&name),
1622                             Hint::new(
1623                                 p(),
1624                                 Hint_::mk_happly(Id(p(), "__uninitSentinel".into()), vec![]),
1625                             ),
1626                         ),
1627                     ),
1628                     vec![unset],
1629                     vec![],
1630                 ),
1631             )
1632         })
1633         .collect();
1634     let sets: Vec<_> = vars
1635         .iter()
1636         .map(|name| {
1637             let checkfunc = id("\\__systemlib\\__debugger_is_uninit");
1638             let isuninit = Expr(
1639                 (),
1640                 p(),
1641                 Expr_::mk_call(
1642                     checkfunc,
1643                     vec![],
1644                     vec![(ParamKind::Pnormal, lv(name))],
1645                     None,
1646                 ),
1647             );
1648             let obj = Expr(
1649                 (),
1650                 p(),
1651                 Expr_::mk_new(
1652                     ClassId((), p(), ClassId_::CI(Id(p(), "__uninitSentinel".into()))),
1653                     vec![],
1654                     vec![],
1655                     None,
1656                     (),
1657                 ),
1658             );
1659             let set = Stmt(
1660                 p(),
1661                 Stmt_::mk_expr(Expr(
1662                     (),
1663                     p(),
1664                     Expr_::mk_binop(Bop::mk_eq(None), lv(name), obj),
1665                 )),
1666             );
1667             Stmt(p(), Stmt_::mk_if(isuninit, vec![set], vec![]))
1668         })
1669         .collect();
1670     vars.push("$__debugger$this".into());
1671     vars.push("$__debugger_exn$output".into());
1672     let params: Vec<_> = vars
1673         .iter()
1674         .map(|var| make_fn_param(p(), &local_id::make_unscoped(var), false, true))
1675         .collect();
1676     let exnvar = Lid(p(), local_id::make_unscoped("$__debugger_exn$output"));
1677     let catch = Stmt(
1678         p(),
1679         Stmt_::mk_try(
1680             stmts,
1681             vec![Catch(Id(p(), "Throwable".into()), exnvar, vec![])],
1682             sets,
1683         ),
1684     );
1685     unsets.push(catch);
1686     let body = unsets;
1687     let pos = Pos::from_line_cols_offset(
1688         RcOc::new(RelativePath::make(Prefix::Dummy, PathBuf::from(""))),
1689         1,
1690         0..0,
1691         0,
1692     );
1693     let f = Fun_ {
1694         span: pos,
1695         annotation: (),
1696         readonly_this: None, // TODO(readonly): readonly_this in closure_convert
1697         readonly_ret: None,  // TODO(readonly): readonly_ret in closure_convert
1698         ret: TypeHint((), None),
1699         name: Id(Pos::make_none(), "include".into()),
1700         tparams: vec![],
1701         where_constraints: vec![],
1702         variadic: FunVariadicity::FVnonVariadic,
1703         params,
1704         ctxs: None,        // TODO(T70095684)
1705         unsafe_ctxs: None, // TODO(T70095684)
1706         body: FuncBody { fb_ast: body },
1707         fun_kind: FunKind::FSync,
1708         user_attributes: vec![UserAttribute {
1709             name: Id(Pos::make_none(), "__DebuggerMain".into()),
1710             params: vec![],
1711         }],
1712         external: false,
1713         doc_comment: None,
1714     };
1715     let fd = FunDef {
1716         namespace: RcOc::clone(empty_namespace),
1717         file_attributes: vec![],
1718         mode: Mode::Mstrict,
1719         fun: f,
1720     };
1721     let mut new_defs = vec![Def::mk_fun(fd)];
1722     new_defs.append(&mut defs);
1723     *all_defs = new_defs;
1724     Ok(())
1727 pub fn convert_toplevel_prog<'arena, 'decl>(
1728     e: &mut Emitter<'arena, 'decl>,
1729     defs: &mut Program,
1730     namespace_env: RcOc<namespace_env::Env>,
1731 ) -> Result<()> {
1732     if e.options()
1733         .hack_compiler_flags
1734         .contains(CompilerFlags::CONSTANT_FOLDING)
1735     {
1736         ast_constant_folder::fold_program(defs, e).map_err(|e| unrecoverable(format!("{}", e)))?;
1737     }
1739     let mut env = Env::toplevel(
1740         count_classes(defs.as_slice()),
1741         count_records(defs.as_slice()),
1742         1,
1743         defs.as_slice(),
1744         e.options(),
1745         e.for_debugger_eval,
1746     )?;
1747     *defs = flatten_ns(defs);
1748     if e.for_debugger_eval {
1749         extract_debugger_main(&namespace_env, defs).map_err(unrecoverable)?;
1750     }
1752     let mut visitor = ClosureConvertVisitor {
1753         alloc: e.alloc,
1754         state: State::initial_state(namespace_env),
1755         phantom_lifetime_a: std::marker::PhantomData,
1756     };
1758     let mut new_defs = vec![];
1759     let mut class_count = 0;
1760     let mut record_count = 0;
1761     let mut typedef_count = 0;
1762     let mut const_count = 0;
1764     for mut def in defs.drain(..) {
1765         visitor.visit_def(&mut env, &mut def)?;
1766         match def {
1767             Def::Class(mut x) => {
1768                 x.emit_id = Some(EmitId::EmitId(class_count));
1769                 class_count += 1;
1770                 new_defs.push(Def::Class(x));
1771             }
1772             Def::RecordDef(mut x) => {
1773                 x.emit_id = Some(EmitId::EmitId(record_count));
1774                 record_count += 1;
1775                 new_defs.push(Def::RecordDef(x));
1776             }
1777             Def::Typedef(mut x) => {
1778                 x.emit_id = Some(EmitId::EmitId(typedef_count));
1779                 typedef_count += 1;
1780                 new_defs.push(Def::Typedef(x));
1781             }
1782             Def::Constant(mut x) => {
1783                 x.emit_id = Some(EmitId::EmitId(const_count));
1784                 const_count += 1;
1785                 new_defs.push(Def::Constant(x));
1786             }
1787             Def::Namespace(x) => new_defs.extend_from_slice((*x).1.as_slice()),
1788             Def::SetNamespaceEnv(x) => {
1789                 visitor.state.set_namespace(RcOc::clone(&*x));
1790                 new_defs.push(Def::SetNamespaceEnv(x))
1791             }
1792             def => new_defs.push(def),
1793         }
1794     }
1796     visitor.state.record_function_state(
1797         get_unique_id_for_main(),
1798         visitor.state.current_function_state.clone(),
1799         HhasCoeffects::default(),
1800         0,
1801     );
1802     hoist_toplevel_functions(&mut new_defs);
1803     let named_fun_defs = visitor
1804         .state
1805         .named_hoisted_functions
1806         .into_iter()
1807         .map(|(_, fd)| Def::mk_fun(fd));
1808     *defs = named_fun_defs.collect();
1809     defs.extend(new_defs.drain(..));
1810     for class in visitor.state.closures.into_iter() {
1811         defs.push(Def::mk_class(class));
1812     }
1813     *e.emit_global_state_mut() = visitor.state.global_state;
1814     Ok(())