1 // Copyright (c) Facebook, Inc. and its affiliates.
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};
10 use hhas_coeffects::HhasCoeffects;
11 use hhbc_assertion_utils::*;
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};
23 aast_visitor::{visit_mut, AstParams, NodeMut, VisitorMut},
27 local_id, namespace_env,
28 relative_path::{Prefix, RelativePath},
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?
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?
62 defined_record_count: usize,
63 /// How many existing functions are there?
65 defined_function_count: usize,
66 /// Are we immediately in a using statement?
68 /// Global compiler/hack options
71 for_debugger_eval: bool,
74 #[derive(Default, Clone)]
75 struct PerFunctionState {
76 pub has_finally: bool,
79 impl<'a, 'arena> Env<'a, 'arena> {
83 function_count: usize,
86 for_debugger_eval: bool,
88 let scope = Scope::toplevel();
89 let all_vars = get_vars(&[], Either::Left(defs))?;
92 pos: Pos::make_none(),
94 variable_scopes: vec![Variables {
96 parameter_names: HashSet::default(),
98 defined_class_count: class_count,
99 defined_record_count: record_count,
100 defined_function_count: function_count,
107 fn with_function_like_(
109 e: ScopeItem<'a, 'arena>,
110 _is_closure_body: bool,
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),
124 fn with_function_like(
126 e: ScopeItem<'a, 'arena>,
127 is_closure_body: bool,
130 self.with_function_like_(
135 &fd.body.fb_ast.as_slice(),
139 fn with_function(&mut self, fd: &FunDef) -> Result<()> {
140 self.with_function_like(
141 ScopeItem::Function(ast_scope::Fun::new_rc(fd)),
147 fn with_method(&mut self, md: &Method_) -> Result<()> {
148 self.with_function_like_(
149 ScopeItem::Method(ast_scope::Method::new_rc(md)),
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 {
165 self.with_function_like(ScopeItem::Lambda(lambda), true, fd)
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 {
176 self.with_function_like(ScopeItem::LongLambda(long_lambda), true, fd)
179 fn with_class(&mut self, cd: &Class_) {
180 self.scope = Scope::toplevel();
182 .push_item(ScopeItem::Class(ast_scope::Class::new_rc(cd)))
185 fn with_in_using<F, R>(&mut self, in_using: bool, mut f: F) -> R
187 F: FnMut(&mut Self) -> R,
189 let old_in_using = self.in_using;
190 self.in_using = in_using;
192 self.in_using = old_in_using;
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(
202 "Function '{}' contains 'await' but is not declared as async.",
203 string_utils::strip_global_ns(name)
210 let check_lambda = |is_async: bool| {
212 Err(emit_fatal::raise_fatal_parse(
214 "Await may only appear in an async function",
220 let head = self.scope.iter().next();
223 None => Err(emit_fatal::raise_fatal_parse(
225 "'await' can only be used inside a function",
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()),
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>,
242 captured_generics: UniqueList<String>,
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 {
260 namespace: RcOc::clone(&empty_namespace),
262 closure_cnt_per_fun: 0,
263 captured_vars: UniqueList::new(),
264 captured_this: false,
265 captured_generics: UniqueList::new(),
267 named_hoisted_functions: SMap::new(),
268 current_function_state: PerFunctionState::default(),
269 global_state: GlobalState::default(),
273 pub fn reset_function_counts(&mut self) {
274 self.closure_cnt_per_fun = 0;
277 pub fn record_function_state(
280 fun: PerFunctionState,
281 coeffects_of_scope: HhasCoeffects<'arena>,
285 self.global_state.functions_with_finally.insert(key.clone());
288 if !coeffects_of_scope.get_static_coeffects().is_empty() {
290 .lambda_coeffects_of_scope
291 .insert(key.clone(), coeffects_of_scope);
293 if num_closures > 0 {
294 self.global_state.num_closures.insert(key, num_closures);
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();
305 pub fn set_namespace(&mut self, namespace: RcOc<namespace_env::Env>) {
306 self.namespace = namespace;
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
320 .map(|x| x.parameter_names.contains(var))
325 // - it exists in one of enclosing scopes
329 .zip_longest(env.variable_scopes.iter().rev())
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) {
338 if !scope.is_in_lambda() {
342 Left(_) => return 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;
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))
359 st.captured_vars.add(var.to_string())
363 fn add_generic(env: &mut Env<'_, '_>, st: &mut State<'_>, var: &str) {
364 let reified_var_position = |is_fun| {
366 |param: &Tparam| param.reified != ReifyKind::Erased && param.name.1 == var;
368 env.scope.get_fun_tparams().iter().position(is_reified_var)
373 .position(is_reified_var)
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)
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() {
407 return match &ns.name {
408 None => String::new(),
409 Some(n) => format!("{}\\", n),
412 Some(ast_scope::ScopeItem::Class(x)) => {
413 parts.push(make_class_name(&x));
416 Some(ast_scope::ScopeItem::Function(x)) => {
417 let fname = strip_id(x.get_name());
419 Scope::get_subscope_class(sub_scope)
420 .map(|cd| make_class_name(cd) + "::")
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())
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)
449 lambda_vars: Vec<String>,
450 fun_tparams: Vec<Tparam>,
451 class_tparams: Vec<Tparam>,
455 ) -> (Fun_, Class_) {
457 span: fd.span.clone(),
458 annotation: fd.annotation,
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
477 doc_comment: fd.doc_comment.clone(),
480 let make_class_var = |name: &str| ClassVar {
484 readonly: false, // readonly on closure_convert
485 visibility: Visibility::Private,
486 type_: TypeHint((), None),
487 id: Id(p.clone(), name.into()),
489 user_attributes: vec![],
491 is_promoted_variadic: false,
496 let cvl = lambda_vars
498 .map(|name| make_class_var(string_utils::locals::strip_dollar(name)));
502 annotation: fd.annotation,
504 user_attributes: vec![],
505 file_attributes: vec![],
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,
514 Box::new(Hint_::Happly(Id(p.clone(), "Closure".into()), vec![])),
517 use_as_alias: vec![],
518 insteadof_alias: vec![],
519 xhp_attr_uses: vec![],
523 where_constraints: vec![],
529 xhp_children: vec![],
531 namespace: RcOc::clone(&st.empty_namespace),
534 emit_id: Some(EmitId::Anonymous),
537 // TODO(hrust): can we reconstruct fd here from the scratch?
538 fd.name = Id(p.clone(), class_num.to_string());
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())
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),
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()),
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),
565 string_utils::mangle_xhp_id(strip_id(cd.get_name()).to_string()) + "::",
566 cd.get_kind() == ClassishKind::Ctrait,
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()));
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())
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()),
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()),
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())
595 _ => Expr_::mk_id(Id(p, s)),
599 fn visit_class_id<'a, 'arena>(
600 env: &mut Env<'a, 'arena>,
601 self_: &mut ClosureConvertVisitor<'a, 'arena>,
604 Ok(if let ClassId(_, _, ClassId_::CIexpr(e)) = cid {
605 self_.visit_expr(env, e)?;
609 fn make_info(c: &ast_scope::Class<'_>) -> ClosureEnclosingClassInfo {
610 ClosureEnclosingClassInfo {
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()),
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>,
626 use_vars_opt: Option<Vec<aast_defs::Lid>>,
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);
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(
643 "Cannot use $this as lexical variable",
648 let lambda_env = &mut env.clone();
650 if use_vars_opt.is_some() {
651 lambda_env.with_longlambda(alloc, &fd)?
653 lambda_env.with_lambda(alloc, &fd)?
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)?;
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
672 .chain(current_generics.iter())
673 // HHVM lists lambda vars in descending order - do the same
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| {
684 .unique_by(|lid| lid.name().to_string())
685 .filter(|x| !fd.params.iter().any(|y| x.name() == &y.name))
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 {
696 lambda_vars.iter().map(|x| x.to_string()).collect(),
699 .map(|x| fresh_lid(x.to_string()))
703 // We still need to append the generics
708 .chain(current_generics.iter())
709 .map(|x| x.to_string())
714 .chain(current_generics.iter().map(|x| fresh_lid(x.to_string())))
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
728 // short lambdas can be made static if they don't capture this in
729 // any form (including any nested lambdas)
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(
747 get_scope_fmode(&env.scope),
754 .insert(inline_fundef.name.1.clone());
757 let closure_class_name = &cd.name.1;
758 if let Some(cd) = env.scope.get_class() {
760 .closure_enclosing_classes
761 .insert(closure_class_name.clone(), make_info(cd));
763 // adjust captured $this information if lambda that was just processed was converted into
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;
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),
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)
787 for x in current_generics.iter() {
788 st.captured_generics.add(x.to_string());
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 {
798 type_hint: TypeHint((), None),
801 name: local_id::get_name(lid).clone(),
803 callconv: if is_inout {
804 ParamKind::Pinout(pos)
808 readonly: None, // TODO
809 user_attributes: vec![],
814 fn get_scope_fmode(scope: &Scope<'_, '_>) -> Mode {
817 .find_map(|item| match item {
818 ScopeItem::Class(cd) => Some(cd.get_mode()),
819 ScopeItem::Function(fd) => Some(fd.get_mode()),
822 .unwrap_or(Mode::Mstrict)
825 fn convert_meth_caller_to_func_ptr(
827 st: &mut ClosureConvertVisitor<'_, '_>,
834 fn get_scope_fmode(scope: &Scope<'_, '_>) -> Mode {
837 .find_map(|item| match item {
838 ScopeItem::Class(cd) => Some(cd.get_mode()),
839 ScopeItem::Function(fd) => Some(fd.get_mode()),
842 .unwrap_or(Mode::Mstrict)
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,
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()),
858 Expr((), pos(), Expr_::mk_string(mangle_name.clone().into())),
862 if st.state.named_hoisted_functions.contains_key(&mangle_name) {
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(
872 expr_id("\\HH\\invariant".into()),
881 expr_id("\\is_a".into()),
884 (ParamKind::Pnormal, obj_lvar.clone()),
887 Expr((), pc.clone(), Expr_::String(cls.into())),
899 Expr_::String(format!("object must be an instance of ({})", cls).into()),
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(
916 Expr_::ObjGet(Box::new((
918 Expr((), pos(), Expr_::mk_id(Id(pf.clone(), fname.to_owned()))),
919 OgNullFlavor::OGNullthrows,
920 PropOrMethod::IsMethod,
925 Some(Expr((), pos(), Expr_::Lvar(args_var))),
931 annotation: dummy_saved_env,
932 readonly_this: None, // TODO(readonly): readonly_this in closure_convert
934 ret: TypeHint((), None),
935 name: Id(pos(), mangle_name.clone()),
937 where_constraints: vec![],
938 variadic: FunVariadicity::FVvariadicArg(variadic_param.clone()),
940 make_fn_param(pos(), &obj_var.1, false, false),
947 Stmt(pos(), Stmt_::Expr(Box::new(assert_invariant))),
948 Stmt(pos(), Stmt_::Return(Box::new(Some(meth_caller_handle)))),
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()))],
960 file_attributes: vec![],
961 namespace: RcOc::clone(&st.state.empty_namespace),
962 mode: get_scope_fmode(&env.scope),
965 st.state.named_hoisted_functions.insert(mangle_name, fd);
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(
987 Expr_::ObjGet(Box::new((
990 OgNullFlavor::OGNullthrows,
991 PropOrMethod::IsMethod,
996 Some(Expr((), pos(), Expr_::Lvar(args_var))),
999 let attrs = if force {
1000 vec![UserAttribute {
1001 name: Id(pos(), "__DynamicMethCallerForce".into()),
1007 let ctxs = Some(Contexts(
1011 Hint_::mk_happly(Id(pos(), string_utils::coeffects::CALLER.into()), vec![]),
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()),
1023 where_constraints: vec![],
1024 variadic: FunVariadicity::FVvariadicArg(variadic_param.clone()),
1026 make_fn_param(pos(), &obj_var.1, false, false),
1027 make_fn_param(pos(), &meth_var.1, false, false),
1033 fb_ast: vec![Stmt(pos(), Stmt_::Return(Box::new(Some(invoke_method))))],
1035 fun_kind: FunKind::FSync,
1036 user_attributes: attrs,
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()),
1046 (ParamKind::Pnormal, cexpr.clone()),
1047 (ParamKind::Pnormal, fexpr.clone()),
1050 Expr((), pos(), Expr_::mk_efun(fd, vec![])),
1052 (ParamKind::Pnormal, Expr((), pos(), force_val)),
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);
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
1079 Box::new(Hint_::Happly(Id(p.clone(), "\\HH\\varray".into()), vec![])),
1086 is_promoted_variadic: false,
1090 visibility: Visibility::Private,
1091 type_: TypeHint((), Some(hint)),
1092 id: Id(p.clone(), string_utils::reified::PROP_NAME.into()),
1094 user_attributes: vec![],
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> {
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")
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),
1135 HhasCoeffects::default(),
1136 self.state.closure_cnt_per_fun,
1138 visit_mut(self, &mut env, &mut md.params)
1141 fn visit_class_<'b>(&mut self, env: &mut Env<'a, 'arena>, cd: &'b mut Class_) -> Result<()> {
1142 let mut env = env.clone();
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))
1153 fn visit_def(&mut self, env: &mut Env<'a, 'arena>, def: &mut Def) -> Result<()> {
1155 // need to handle it ourselvses, because visit_fun_ is
1156 // called both for toplevel functions and lambdas
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),
1165 HhasCoeffects::default(),
1166 self.state.closure_cnt_per_fun,
1168 visit_mut(self, &mut env, &mut x.fun.params)?;
1169 visit_mut(self, &mut env, &mut x.fun.user_attributes)
1171 _ => def.recurse(env, self.object()),
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())
1179 hint.recurse(env, self.object())
1182 fn visit_stmt_(&mut self, env: &mut Env<'a, 'arena>, stmt: &mut Stmt_) -> Result<()> {
1184 Stmt_::Awaitall(x) => {
1185 env.check_if_in_async_context()?;
1186 x.recurse(env, self.object())
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)
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))
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()?
1202 x.recurse(env, self.object())
1205 let (e1, e2, e3, b) = (&mut x.0, &mut x.1, &mut x.2, &mut x.3);
1208 self.visit_expr(env, e)?;
1210 if let Some(e) = e2 {
1211 self.visit_expr(env, e)?;
1213 env.with_in_using(false, |env| visit_mut(self, env, b))?;
1215 self.visit_expr(env, e)?;
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))
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())
1231 Stmt_::Using(x) => {
1233 env.check_if_in_async_context()?;
1235 for e in &mut x.exprs.1 {
1236 self.visit_expr(env, e)?;
1238 env.with_in_using(true, |env| visit_mut(self, env, &mut x.block))?;
1239 Ok(self.state.current_function_state.has_finally = true)
1241 _ => stmt.recurse(env, self.object()),
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);
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
1254 Repeat in case there are nested occurrences
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
1268 // Select first argument
1269 let Expr(_, _, e) = x.2.swap_remove(0).1;
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()
1284 Box::new(Lid(id_orig.0, (0, "$__debugger$this".to_string())))
1288 add_var(env, &mut self.state, local_id::get_name(&id.1));
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());
1297 add_generic(env, &mut self.state, id.name());
1298 convert_id(env, *id)
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")
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")
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())?;
1322 let mut res = Expr_::Call(x);
1323 res.recurse(env, self.object())?;
1329 if let Expr_::Id(ref id) = (x.0).2 {
1330 let name = strip_id(id);
1331 ["hh\\meth_caller", "meth_caller"]
1333 .any(|n| n.eq_ignore_ascii_case(name))
1338 .contains(HhvmFlags::EMIT_METH_CALLER_FUNC_POINTERS)
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) =>
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),
1357 visit_class_id(env, self, cid)?;
1359 cid if cid.as_ciexpr().and_then(|x| x.as_id()).map_or(
1362 !(string_utils::is_self(id)
1363 || string_utils::is_parent(id)
1364 || string_utils::is_static(id))
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(
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()) },
1386 return Err(emit_fatal::raise_fatal_parse(pc, "Invalid class"));
1390 (Expr_::String(cls_name), Some(fname)) => convert_meth_caller_to_func_ptr(
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()) },
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()) },
1404 return Err(emit_fatal::raise_fatal_parse(
1406 "Class must be a Class or string type",
1410 return Err(emit_fatal::raise_fatal_parse(
1412 "Method name must be a literal string",
1417 let mut res = Expr_::Call(x);
1418 res.recurse(env, self.object())?;
1424 if let Expr_::Id(ref id) = (x.0).2 {
1425 let name = strip_id(id);
1426 ["hh\\meth_caller", "meth_caller"]
1428 .any(|n| n.eq_ignore_ascii_case(name))
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,
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))
1450 let mut res = Expr_::Call(x);
1451 res.recurse(env, self.object())?;
1454 return Err(emit_fatal::raise_fatal_parse(pc, "Invalid class"));
1457 (Expr_::String(_), Some(_)) => {
1458 let mut res = Expr_::Call(x);
1459 res.recurse(env, self.object())?;
1463 return Err(emit_fatal::raise_fatal_parse(
1465 "Class must be a Class or string type",
1469 return Err(emit_fatal::raise_fatal_parse(
1471 "Method name must be a literal string",
1476 let mut res = Expr_::Call(x);
1477 res.recurse(env, self.object())?;
1484 .and_then(|(id, _, _)| id.as_ciexpr())
1485 .and_then(|x| x.as_id())
1486 .map_or(false, string_utils::is_parent)
1489 .and_then(|(id, _)| id.as_ciexpr())
1490 .and_then(|x| x.as_id())
1491 .map_or(false, string_utils::is_parent) =>
1493 add_var(env, &mut self.state, "$this");
1494 let mut res = Expr_::Call(x);
1495 res.recurse(env, self.object())?;
1498 Expr_::As(x) if (x.1).is_hlike() => {
1500 res.recurse(env, self.object())?;
1508 (id.name() == fb::INCORRECT_TYPE || id.name() == fb::INCORRECT_TYPE_NO_NS)
1511 .unwrap_or_default() =>
1514 res.recurse(env, self.object())?;
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
1523 add_var(env, &mut self.state, &id.1);
1525 x.recurse(env, self.object())?;
1528 Expr_::Await(mut x) => {
1529 env.check_if_in_async_context()?;
1530 x.recurse(env, self.object())?;
1533 Expr_::ReadonlyExpr(mut x) => {
1534 x.recurse(env, self.object())?;
1535 Expr_::ReadonlyExpr(x)
1537 Expr_::ExpressionTree(mut x) => {
1538 x.runtime_expr.recurse(env, self.object())?;
1539 Expr_::ExpressionTree(x)
1542 x.recurse(env, self.object())?;
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());
1554 defs.extend(nonfuns);
1557 fn flatten_ns(defs: &mut Program) -> Program {
1559 .flat_map(|x| match x {
1560 Def::Namespace(mut x) => flatten_ns(&mut x.1),
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))?
1573 .collect::<Vec<_>>();
1574 // TODO(hrust) sort is only required when comparing Rust/Ocaml, remove sort after emitter shipped
1576 let mut stmts = stmts
1578 .filter_map(|x| x.as_stmt_into())
1579 .collect::<Vec<_>>();
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)))]
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| {
1595 Expr_::mk_lvar(Lid(p(), local_id::make_unscoped(n))),
1598 let mut unsets: Vec<_> = vars
1603 Stmt_::mk_expr(Expr(
1609 vec![(ParamKind::Pnormal, lv(&name))],
1624 Hint_::mk_happly(Id(p(), "__uninitSentinel".into()), vec![]),
1634 let sets: Vec<_> = vars
1637 let checkfunc = id("\\__systemlib\\__debugger_is_uninit");
1638 let isuninit = Expr(
1644 vec![(ParamKind::Pnormal, lv(name))],
1652 ClassId((), p(), ClassId_::CI(Id(p(), "__uninitSentinel".into()))),
1661 Stmt_::mk_expr(Expr(
1664 Expr_::mk_binop(Bop::mk_eq(None), lv(name), obj),
1667 Stmt(p(), Stmt_::mk_if(isuninit, vec![set], vec![]))
1670 vars.push("$__debugger$this".into());
1671 vars.push("$__debugger_exn$output".into());
1672 let params: Vec<_> = vars
1674 .map(|var| make_fn_param(p(), &local_id::make_unscoped(var), false, true))
1676 let exnvar = Lid(p(), local_id::make_unscoped("$__debugger_exn$output"));
1681 vec![Catch(Id(p(), "Throwable".into()), exnvar, vec![])],
1687 let pos = Pos::from_line_cols_offset(
1688 RcOc::new(RelativePath::make(Prefix::Dummy, PathBuf::from(""))),
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()),
1701 where_constraints: vec![],
1702 variadic: FunVariadicity::FVnonVariadic,
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()),
1716 namespace: RcOc::clone(empty_namespace),
1717 file_attributes: vec![],
1718 mode: Mode::Mstrict,
1721 let mut new_defs = vec![Def::mk_fun(fd)];
1722 new_defs.append(&mut defs);
1723 *all_defs = new_defs;
1727 pub fn convert_toplevel_prog<'arena, 'decl>(
1728 e: &mut Emitter<'arena, 'decl>,
1730 namespace_env: RcOc<namespace_env::Env>,
1733 .hack_compiler_flags
1734 .contains(CompilerFlags::CONSTANT_FOLDING)
1736 ast_constant_folder::fold_program(defs, e).map_err(|e| unrecoverable(format!("{}", e)))?;
1739 let mut env = Env::toplevel(
1740 count_classes(defs.as_slice()),
1741 count_records(defs.as_slice()),
1745 e.for_debugger_eval,
1747 *defs = flatten_ns(defs);
1748 if e.for_debugger_eval {
1749 extract_debugger_main(&namespace_env, defs).map_err(unrecoverable)?;
1752 let mut visitor = ClosureConvertVisitor {
1754 state: State::initial_state(namespace_env),
1755 phantom_lifetime_a: std::marker::PhantomData,
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)?;
1767 Def::Class(mut x) => {
1768 x.emit_id = Some(EmitId::EmitId(class_count));
1770 new_defs.push(Def::Class(x));
1772 Def::RecordDef(mut x) => {
1773 x.emit_id = Some(EmitId::EmitId(record_count));
1775 new_defs.push(Def::RecordDef(x));
1777 Def::Typedef(mut x) => {
1778 x.emit_id = Some(EmitId::EmitId(typedef_count));
1780 new_defs.push(Def::Typedef(x));
1782 Def::Constant(mut x) => {
1783 x.emit_id = Some(EmitId::EmitId(const_count));
1785 new_defs.push(Def::Constant(x));
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))
1792 def => new_defs.push(def),
1796 visitor.state.record_function_state(
1797 get_unique_id_for_main(),
1798 visitor.state.current_function_state.clone(),
1799 HhasCoeffects::default(),
1802 hoist_toplevel_functions(&mut new_defs);
1803 let named_fun_defs = visitor
1805 .named_hoisted_functions
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));
1813 *e.emit_global_state_mut() = visitor.state.global_state;