invert the sense of happly_decl_has_no_reified_generics()
[hiphop-php.git] / hphp / hack / src / hackc / emitter / emit_body.rs
blob96699a2955cb5dcf3aa43d57d96cd1e737764d3c
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.
5 mod emit_statement;
6 mod reified_generics_helpers;
7 mod try_finally_rewriter;
9 use emit_statement::emit_final_stmts;
10 use reified_generics_helpers as RGH;
12 use ast_body::AstBody;
13 use ast_class_expr::ClassExpr;
14 use ast_scope::{Scope, ScopeItem};
15 use emit_pos::emit_pos;
16 use env::{emitter::Emitter, Env};
17 use hash::HashSet;
18 use hhas_body::{HhasBody, HhasBodyEnv};
19 use hhas_param::HhasParam;
20 use hhas_type::HhasTypeInfo;
21 use hhbc_ast::{FCallArgsFlags, FcallArgs, Instruct, IsTypeOp, ParamId, Pseudo};
22 use hhbc_id::function;
23 use hhbc_string_utils as string_utils;
24 use instruction_sequence::{instr, unrecoverable, Error, InstrSeq, Result};
25 use label::Label;
26 use local::{Local, LocalId};
27 use options::CompilerFlags;
28 use runtime::TypedValue;
29 use statement_state::StatementState;
30 use unique_id_builder::*;
32 use ocamlrep::rc::RcOc;
33 use oxidized::{aast, ast, ast_defs, doc_comment::DocComment, namespace_env, pos::Pos};
35 use ffi::{Maybe, Maybe::*, Pair, Slice, Str};
37 use bitflags::bitflags;
38 use indexmap::IndexSet;
39 use itertools::Either;
41 static THIS: &str = "$this";
43 /// Optional arguments for emit_body; use Args::default() for defaults
44 pub struct Args<'a, 'arena> {
45     pub immediate_tparams: &'a Vec<ast::Tparam>,
46     pub class_tparam_names: &'a [&'a str],
47     pub ast_params: &'a Vec<ast::FunParam>,
48     pub ret: Option<&'a ast::Hint>,
49     pub pos: &'a Pos,
50     pub deprecation_info: &'a Option<&'a [TypedValue<'arena>]>,
51     pub doc_comment: Option<DocComment>,
52     pub default_dropthrough: Option<InstrSeq<'arena>>,
53     pub call_context: Option<String>,
54     pub flags: Flags,
57 bitflags! {
58     pub struct Flags: u8 {
59         const HAS_COEFFECTS_LOCAL = 1 << 0;
60         const SKIP_AWAITABLE = 1 << 1;
61         const MEMOIZE = 1 << 2;
62         const CLOSURE_BODY = 1 << 3;
63         const NATIVE = 1 << 4;
64         const ASYNC = 1 << 6;
65         const DEBUGGER_MODIFY_PROGRAM = 1 << 7;
66     }
69 pub fn emit_body_with_default_args<'b, 'arena, 'decl>(
70     alloc: &'arena bumpalo::Bump,
71     emitter: &mut Emitter<'arena, 'decl>,
72     namespace: RcOc<namespace_env::Env>,
73     body: &'b ast::Program,
74     return_value: InstrSeq<'arena>,
75 ) -> Result<HhasBody<'arena>> {
76     let args = Args {
77         immediate_tparams: &vec![],
78         class_tparam_names: &[],
79         ast_params: &vec![],
80         ret: None,
81         pos: &Pos::make_none(),
82         deprecation_info: &None,
83         doc_comment: None,
84         default_dropthrough: None,
85         call_context: None,
86         flags: Flags::empty(),
87     };
88     emit_body(
89         alloc,
90         emitter,
91         namespace,
92         Either::Left(body.as_slice()),
93         return_value,
94         Scope::toplevel(),
95         args,
96     )
97     .map(|r| r.0)
100 pub fn emit_body<'b, 'arena, 'decl>(
101     alloc: &'arena bumpalo::Bump,
102     emitter: &mut Emitter<'arena, 'decl>,
103     namespace: RcOc<namespace_env::Env>,
104     body: AstBody<'b>,
105     return_value: InstrSeq<'arena>,
106     scope: Scope<'_, 'arena>,
107     args: Args<'_, 'arena>,
108 ) -> Result<(HhasBody<'arena>, bool, bool)> {
109     let tparams: Vec<ast::Tparam> = scope.get_tparams().into_iter().cloned().collect();
110     let mut tp_names = get_tp_names(&tparams);
111     let (is_generator, is_pair_generator) = generator::is_function_generator(&body);
113     emitter.label_gen_mut().reset();
114     emitter.iterator_mut().reset();
116     let return_type_info = make_return_type_info(
117         alloc,
118         args.flags.contains(Flags::SKIP_AWAITABLE),
119         args.flags.contains(Flags::NATIVE),
120         args.ret,
121         &tp_names,
122     )?;
124     let params = make_params(emitter, &mut tp_names, args.ast_params, &scope, args.flags)?;
126     let upper_bounds = emit_generics_upper_bounds(
127         alloc,
128         args.immediate_tparams,
129         args.class_tparam_names,
130         args.flags.contains(Flags::SKIP_AWAITABLE),
131     );
132     let shadowed_tparams = emit_shadowed_tparams(args.immediate_tparams, args.class_tparam_names);
133     let decl_vars = make_decl_vars(
134         emitter,
135         &scope,
136         args.immediate_tparams,
137         &params,
138         &body,
139         args.flags,
140     )?;
141     let mut env = make_env(alloc, namespace, scope, args.call_context);
143     set_emit_statement_state(
144         alloc,
145         emitter,
146         return_value,
147         &params,
148         &return_type_info,
149         args.ret,
150         args.pos,
151         args.default_dropthrough,
152         args.flags,
153         is_generator,
154     );
155     env.jump_targets_gen.reset();
157     let should_reserve_locals = set_function_jmp_targets(emitter, &mut env);
158     let local_gen = emitter.local_gen_mut();
159     let num_locals = params.len() + decl_vars.len();
160     local_gen.reset(LocalId::from_usize(num_locals));
161     if should_reserve_locals {
162         local_gen.reserve_retval_and_label_id_locals();
163     };
164     let body_instrs = make_body_instrs(
165         emitter,
166         &mut env,
167         &params,
168         &tparams,
169         &decl_vars,
170         body,
171         is_generator,
172         args.deprecation_info.clone(),
173         args.pos,
174         args.ast_params,
175         args.flags,
176     )?;
177     Ok((
178         make_body(
179             alloc,
180             emitter,
181             body_instrs,
182             decl_vars,
183             false, // is_memoize_wrapper
184             false, // is_memoize_wrapper_lsb
185             upper_bounds,
186             shadowed_tparams,
187             params,
188             Some(return_type_info),
189             args.doc_comment.to_owned(),
190             Some(&env),
191         )?,
192         is_generator,
193         is_pair_generator,
194     ))
197 fn make_body_instrs<'a, 'arena, 'decl>(
198     emitter: &mut Emitter<'arena, 'decl>,
199     env: &mut Env<'a, 'arena>,
200     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
201     tparams: &[ast::Tparam],
202     decl_vars: &[String],
203     body: AstBody<'_>,
204     is_generator: bool,
205     deprecation_info: Option<&[TypedValue<'arena>]>,
206     pos: &Pos,
207     ast_params: &[ast::FunParam],
208     flags: Flags,
209 ) -> Result<InstrSeq<'arena>> {
210     let stmt_instrs = if flags.contains(Flags::NATIVE) {
211         instr::nativeimpl()
212     } else {
213         env.do_function(emitter, &body, emit_ast_body)?
214     };
216     let (begin_label, default_value_setters) =
217         emit_param::emit_param_default_value_setter(emitter, env, pos, params)?;
219     let header_content = make_header_content(
220         emitter,
221         env,
222         params,
223         tparams,
224         decl_vars,
225         is_generator,
226         deprecation_info,
227         pos,
228         ast_params,
229         flags,
230     )?;
231     let first_instr_is_label = match InstrSeq::first(&stmt_instrs) {
232         Some(Instruct::Pseudo(Pseudo::Label(_))) => true,
233         _ => false,
234     };
235     let header = if first_instr_is_label && InstrSeq::is_empty(&header_content) {
236         InstrSeq::gather(vec![begin_label, instr::entrynop()])
237     } else {
238         InstrSeq::gather(vec![begin_label, header_content])
239     };
241     let mut body_instrs = InstrSeq::gather(vec![header, stmt_instrs, default_value_setters]);
242     if flags.contains(Flags::DEBUGGER_MODIFY_PROGRAM) {
243         modify_prog_for_debugger_eval(&mut body_instrs);
244     };
245     Ok(body_instrs)
248 fn make_header_content<'a, 'arena, 'decl>(
249     emitter: &mut Emitter<'arena, 'decl>,
250     env: &mut Env<'a, 'arena>,
251     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
252     tparams: &[ast::Tparam],
253     _decl_vars: &[String],
254     is_generator: bool,
255     deprecation_info: Option<&[TypedValue<'arena>]>,
256     pos: &Pos,
257     ast_params: &[ast::FunParam],
258     flags: Flags,
259 ) -> Result<InstrSeq<'arena>> {
260     let alloc = env.arena;
261     let method_prolog = if flags.contains(Flags::NATIVE) {
262         instr::empty()
263     } else {
264         emit_method_prolog(emitter, env, pos, params, ast_params, tparams)?
265     };
267     let deprecation_warning =
268         emit_deprecation_info(alloc, &env.scope, deprecation_info, emitter.systemlib())?;
270     let generator_info = if is_generator {
271         InstrSeq::gather(vec![instr::createcont(), instr::popc()])
272     } else {
273         instr::empty()
274     };
276     Ok(InstrSeq::gather(vec![
277         method_prolog,
278         deprecation_warning,
279         generator_info,
280     ]))
283 fn make_decl_vars<'a, 'arena, 'decl>(
284     emitter: &mut Emitter<'arena, 'decl>,
285     scope: &Scope<'a, 'arena>,
286     immediate_tparams: &[ast::Tparam],
287     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
288     body: &AstBody<'_>,
289     arg_flags: Flags,
290 ) -> Result<Vec<String>> {
291     let explicit_use_set = &emitter.emit_global_state().explicit_use_set;
293     let mut decl_vars =
294         decl_vars::from_ast(params, body, explicit_use_set).map_err(unrecoverable)?;
296     let mut decl_vars = if arg_flags.contains(Flags::CLOSURE_BODY) {
297         let mut captured_vars = scope.get_captured_vars();
298         move_this(&mut decl_vars);
299         decl_vars.retain(|v| !captured_vars.contains(v));
300         captured_vars.extend_from_slice(decl_vars.as_slice());
301         captured_vars
302     } else {
303         match &scope.items[..] {
304             [] | [.., ScopeItem::Class(_), _] => move_this(&mut decl_vars),
305             _ => {}
306         };
307         decl_vars
308     };
310     if arg_flags.contains(Flags::HAS_COEFFECTS_LOCAL) {
311         decl_vars.insert(0, string_utils::coeffects::LOCAL_NAME.into());
312     }
314     if !arg_flags.contains(Flags::CLOSURE_BODY)
315         && immediate_tparams
316             .iter()
317             .any(|t| t.reified != ast::ReifyKind::Erased)
318     {
319         decl_vars.insert(0, string_utils::reified::GENERICS_LOCAL_NAME.into());
320     }
321     Ok(decl_vars)
324 pub fn emit_return_type_info<'arena>(
325     alloc: &'arena bumpalo::Bump,
326     tp_names: &[&str],
327     skip_awaitable: bool,
328     ret: Option<&aast::Hint>,
329 ) -> Result<HhasTypeInfo<'arena>> {
330     match ret {
331         None => Ok(HhasTypeInfo::make(
332             Just(Str::new_str(alloc, "")),
333             hhas_type::constraint::Constraint::default(),
334         )),
335         Some(hint) => emit_type_hint::hint_to_type_info(
336             alloc,
337             &emit_type_hint::Kind::Return,
338             skip_awaitable,
339             false, // nullable
340             tp_names,
341             hint,
342         ),
343     }
346 fn make_return_type_info<'arena>(
347     alloc: &'arena bumpalo::Bump,
348     skip_awaitable: bool,
349     is_native: bool,
350     ret: Option<&aast::Hint>,
351     tp_names: &[&str],
352 ) -> Result<HhasTypeInfo<'arena>> {
353     let return_type_info = emit_return_type_info(alloc, tp_names, skip_awaitable, ret);
354     if is_native {
355         return return_type_info.map(|rti| {
356             emit_type_hint::emit_type_constraint_for_native_function(alloc, tp_names, ret, rti)
357         });
358     };
359     return_type_info
362 pub fn make_env<'a, 'arena>(
363     alloc: &'arena bumpalo::Bump,
364     namespace: RcOc<namespace_env::Env>,
365     scope: Scope<'a, 'arena>,
366     call_context: Option<String>,
367 ) -> Env<'a, 'arena> {
368     let mut env = Env::default(alloc, namespace);
369     env.call_context = call_context;
370     env.scope = scope;
371     env
374 fn make_params<'a, 'arena, 'decl>(
375     emitter: &mut Emitter<'arena, 'decl>,
376     tp_names: &mut Vec<&str>,
377     ast_params: &[ast::FunParam],
378     scope: &Scope<'a, 'arena>,
379     flags: Flags,
380 ) -> Result<Vec<(HhasParam<'arena>, Option<(Label, ast::Expr)>)>> {
381     let generate_defaults = !flags.contains(Flags::MEMOIZE);
382     emit_param::from_asts(emitter, tp_names, generate_defaults, scope, ast_params)
385 pub fn make_body<'a, 'arena, 'decl>(
386     alloc: &'arena bumpalo::Bump,
387     emitter: &mut Emitter<'arena, 'decl>,
388     mut body_instrs: InstrSeq<'arena>,
389     decl_vars: Vec<String>,
390     is_memoize_wrapper: bool,
391     is_memoize_wrapper_lsb: bool,
392     upper_bounds: Vec<Pair<Str<'arena>, Slice<'arena, HhasTypeInfo<'arena>>>>,
393     shadowed_tparams: Vec<String>,
394     mut params: Vec<(HhasParam<'arena>, Option<(Label, ast::Expr)>)>,
395     return_type_info: Option<HhasTypeInfo<'arena>>,
396     doc_comment: Option<DocComment>,
397     opt_env: Option<&Env<'a, 'arena>>,
398 ) -> Result<HhasBody<'arena>> {
399     if emitter
400         .options()
401         .hack_compiler_flags
402         .contains(CompilerFlags::RELABEL)
403     {
404         label_rewriter::relabel_function(&mut params, &mut body_instrs);
405     }
406     let num_iters = if is_memoize_wrapper {
407         0
408     } else {
409         emitter.iterator().count()
410     };
411     let body_env = if let Some(env) = opt_env {
412         let is_namespaced = env.namespace.name.is_none();
413         if let Some(cd) = env.scope.get_class() {
414             Some(HhasBodyEnv {
415                 is_namespaced,
416                 class_info: Just(
417                     (cd.get_kind().into(), Str::new_str(alloc, cd.get_name_str())).into(),
418                 ),
419                 parent_name: ClassExpr::get_parent_class_name(cd)
420                     .as_ref()
421                     .map(|s| Str::new_str(alloc, s))
422                     .into(),
423             })
424         } else {
425             Some(HhasBodyEnv {
426                 is_namespaced,
427                 class_info: Nothing,
428                 parent_name: Nothing,
429             })
430         }
431     } else {
432         None
433     };
435     // Pretty-print the DV initializer expression as a Hack source code string,
436     // to make it available for reflection.
437     params.iter_mut().for_each(|(p, default_value)| {
438         p.default_value = Maybe::from(
439             default_value
440                 .as_ref()
441                 .map(|(l, expr)| {
442                     use print_expr::{Context, ExprEnv};
443                     let ctx = Context::new(emitter);
444                     let expr_env = ExprEnv {
445                         codegen_env: body_env.as_ref(),
446                     };
447                     let mut buf = Vec::new();
448                     print_expr::print_expr(&ctx, &mut buf, &expr_env, expr)
449                         .map(|_| Pair(l.clone(), Str::from_vec(alloc, buf)))
450                         .ok()
451                 })
452                 .flatten(),
453         );
454     });
456     Ok(HhasBody {
457         body_instrs: body_instrs.compact(alloc),
458         decl_vars: Slice::fill_iter(
459             alloc,
460             decl_vars.into_iter().map(|s| Str::new_str(alloc, &s)),
461         ),
462         num_iters,
463         is_memoize_wrapper,
464         is_memoize_wrapper_lsb,
465         upper_bounds: Slice::fill_iter(alloc, upper_bounds.into_iter()),
466         shadowed_tparams: Slice::fill_iter(
467             alloc,
468             shadowed_tparams
469                 .into_iter()
470                 .map(|s| Str::new_str(alloc, &s)),
471         ),
472         params: Slice::fill_iter(alloc, params.into_iter().map(|(p, _)| p)),
473         return_type_info: return_type_info.into(),
474         doc_comment: doc_comment.map(|c| Str::new_str(alloc, &(c.0).1)).into(),
475         env: body_env.into(),
476     })
479 fn emit_ast_body<'a, 'arena, 'decl>(
480     env: &mut Env<'a, 'arena>,
481     e: &mut Emitter<'arena, 'decl>,
482     body: &AstBody<'_>,
483 ) -> Result<InstrSeq<'arena>> {
484     match body {
485         Either::Left(p) => emit_defs(env, e, p),
486         Either::Right(b) => emit_final_stmts(e, env, b),
487     }
490 fn emit_defs<'a, 'arena, 'decl>(
491     env: &mut Env<'a, 'arena>,
492     emitter: &mut Emitter<'arena, 'decl>,
493     prog: &[ast::Def],
494 ) -> Result<InstrSeq<'arena>> {
495     use ast::Def;
496     fn emit_def<'a, 'arena, 'decl>(
497         env: &mut Env<'a, 'arena>,
498         emitter: &mut Emitter<'arena, 'decl>,
499         def: &ast::Def,
500     ) -> Result<InstrSeq<'arena>> {
501         match def {
502             Def::Stmt(s) => emit_statement::emit_stmt(emitter, env, s),
503             Def::Namespace(ns) => emit_defs(env, emitter, &ns.1),
504             _ => Ok(instr::empty()),
505         }
506     }
507     fn aux<'a, 'arena, 'decl>(
508         env: &mut Env<'a, 'arena>,
509         emitter: &mut Emitter<'arena, 'decl>,
510         defs: &[ast::Def],
511     ) -> Result<InstrSeq<'arena>> {
512         match defs {
513             [Def::SetNamespaceEnv(ns), ..] => {
514                 env.namespace = RcOc::clone(ns);
515                 aux(env, emitter, &defs[1..])
516             }
517             [] => emit_statement::emit_dropthrough_return(emitter, env),
518             [Def::Stmt(s)] => {
519                 // emit last statement in the list as final statement
520                 emit_statement::emit_final_stmt(emitter, env, s)
521             }
522             [Def::Stmt(s1), Def::Stmt(s2)] => match s2.1.as_markup() {
523                 Some(id) if id.1.is_empty() => emit_statement::emit_final_stmt(emitter, env, s1),
524                 _ => Ok(InstrSeq::gather(vec![
525                     emit_statement::emit_stmt(emitter, env, s1)?,
526                     emit_statement::emit_final_stmt(emitter, env, s2)?,
527                 ])),
528             },
529             [def, ..] => Ok(InstrSeq::gather(vec![
530                 emit_def(env, emitter, def)?,
531                 aux(env, emitter, &defs[1..])?,
532             ])),
533         }
534     }
536     match prog {
537         [Def::Stmt(s), ..] if s.1.is_markup() => Ok(InstrSeq::gather(vec![
538             emit_statement::emit_markup(emitter, env, s.1.as_markup().unwrap(), true)?,
539             aux(env, emitter, &prog[1..])?,
540         ])),
541         [] | [_] => aux(env, emitter, prog),
542         [def, ..] => {
543             let i1 = emit_def(env, emitter, def)?;
544             if i1.is_empty() {
545                 emit_defs(env, emitter, &prog[1..])
546             } else {
547                 Ok(InstrSeq::gather(vec![i1, aux(env, emitter, &prog[1..])?]))
548             }
549         }
550     }
553 pub fn has_type_constraint<'a, 'arena>(
554     env: &Env<'a, 'arena>,
555     ti: Option<&HhasTypeInfo<'_>>,
556     ast_param: &ast::FunParam,
557 ) -> (RGH::ReificationLevel, Option<ast::Hint>) {
558     use RGH::ReificationLevel as L;
559     match (ti, &ast_param.type_hint.1) {
560         (Some(ti), Some(h)) if ti.has_type_constraint() => {
561             // TODO(hrust): how to avoid clone on h
562             let h = RGH::remove_erased_generics(env, h.clone());
563             (RGH::has_reified_type_constraint(env, &h), Some(h))
564         }
565         _ => (L::Unconstrained, None),
566     }
569 pub fn emit_method_prolog<'a, 'arena, 'decl>(
570     emitter: &mut Emitter<'arena, 'decl>,
571     env: &mut Env<'a, 'arena>,
572     pos: &Pos,
573     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
574     ast_params: &[ast::FunParam],
575     tparams: &[ast::Tparam],
576 ) -> Result<InstrSeq<'arena>> {
577     let alloc = env.arena;
578     let mut make_param_instr =
579         |param: &HhasParam<'arena>, ast_param: &ast::FunParam| -> Result<InstrSeq<'arena>> {
580             let param_name =
581                 || ParamId::ParamNamed(Str::new_str(alloc, param.name.unsafe_as_str()));
582             if param.is_variadic {
583                 Ok(instr::empty())
584             } else {
585                 use RGH::ReificationLevel as L;
586                 match has_type_constraint(env, Option::from(param.type_info.as_ref()), ast_param) {
587                     (L::Unconstrained, _) => Ok(instr::empty()),
588                     (L::Not, _) => Ok(instr::verify_param_type(param_name())),
589                     (L::Maybe, Some(h)) => {
590                         if !RGH::happly_decl_has_reified_generics(emitter, &h) {
591                             Ok(instr::verify_param_type(param_name()))
592                         } else {
593                             Ok(InstrSeq::gather(vec![
594                                 emit_expression::get_type_structure_for_hint(
595                                     emitter,
596                                     tparams
597                                         .iter()
598                                         .map(|fp| fp.name.1.as_str())
599                                         .collect::<Vec<_>>()
600                                         .as_slice(),
601                                     &IndexSet::new(),
602                                     &h,
603                                 )?,
604                                 instr::verify_param_type_ts(param_name()),
605                             ]))
606                         }
607                     }
608                     (L::Definitely, Some(h)) => {
609                         if !RGH::happly_decl_has_reified_generics(emitter, &h) {
610                             Ok(instr::verify_param_type(param_name()))
611                         } else {
612                             let check = instr::istypel(
613                                 Local::Named(Str::new_str(alloc, param.name.unsafe_as_str())),
614                                 IsTypeOp::Null,
615                             );
616                             let verify_instr = instr::verify_param_type_ts(param_name());
617                             RGH::simplify_verify_type(emitter, env, pos, check, &h, verify_instr)
618                         }
619                     }
620                     _ => Err(unrecoverable("impossible")),
621                 }
622             }
623         };
625     let ast_params = ast_params
626         .iter()
627         .filter(|p| !(p.is_variadic && p.name == "..."))
628         .collect::<Vec<_>>();
629     if params.len() != ast_params.len() {
630         return Err(Error::Unrecoverable("length mismatch".into()));
631     }
633     let mut instrs = Vec::with_capacity(1 + params.len());
634     instrs.push(emit_pos(pos));
635     for ((param, _), ast_param) in params.iter().zip(ast_params.into_iter()) {
636         instrs.push(make_param_instr(param, ast_param)?);
637     }
638     Ok(InstrSeq::gather(instrs))
641 pub fn emit_deprecation_info<'a, 'arena>(
642     alloc: &'arena bumpalo::Bump,
643     scope: &Scope<'a, 'arena>,
644     deprecation_info: Option<&[TypedValue<'arena>]>,
645     is_systemlib: bool,
646 ) -> Result<InstrSeq<'arena>> {
647     Ok(match deprecation_info {
648         None => instr::empty(),
649         Some(args) => {
650             fn strip_id<'a>(id: &'a ast::Id) -> &'a str {
651                 string_utils::strip_global_ns(id.1.as_str())
652             }
653             let (class_name, trait_instrs, concat_instruction): (String, _, _) =
654                 match scope.get_class() {
655                     None => ("".into(), instr::empty(), instr::empty()),
656                     Some(c) if c.get_kind() == ast::ClassishKind::Ctrait => (
657                         "::".into(),
658                         InstrSeq::gather(vec![instr::selfcls(), instr::classname()]),
659                         instr::concat(),
660                     ),
661                     Some(c) => (
662                         strip_id(c.get_name()).to_string() + "::",
663                         instr::empty(),
664                         instr::empty(),
665                     ),
666                 };
668             let fn_name = match scope.items.last() {
669                 Some(ScopeItem::Function(f)) => strip_id(f.get_name()),
670                 Some(ScopeItem::Method(m)) => strip_id(m.get_name()),
671                 _ => {
672                     return Err(Error::Unrecoverable(
673                         "deprecated functions must have names".into(),
674                     ));
675                 }
676             };
677             let deprecation_string = class_name
678                 + fn_name
679                 + ": "
680                 + (if args.is_empty() {
681                     "deprecated function"
682                 } else if let TypedValue::String(s) = &args[0] {
683                     s.unsafe_as_str()
684                 } else {
685                     return Err(Error::Unrecoverable(
686                         "deprecated attribute first argument is not a string".into(),
687                     ));
688                 });
689             let sampling_rate = if args.len() <= 1 {
690                 1i64
691             } else if let Some(TypedValue::Int(i)) = args.get(1) {
692                 *i
693             } else {
694                 return Err(Error::Unrecoverable(
695                     "deprecated attribute second argument is not an integer".into(),
696                 ));
697             };
698             let error_code = if is_systemlib {
699                 /*E_DEPRECATED*/
700                 8192
701             } else {
702                 /*E_USER_DEPRECATED*/
703                 16384
704             };
706             if sampling_rate <= 0 {
707                 instr::empty()
708             } else {
709                 InstrSeq::gather(vec![
710                     instr::nulluninit(),
711                     instr::nulluninit(),
712                     trait_instrs,
713                     instr::string(alloc, deprecation_string),
714                     concat_instruction,
715                     instr::int(sampling_rate),
716                     instr::int(error_code),
717                     instr::fcallfuncd(
718                         FcallArgs::new(
719                             FCallArgsFlags::default(),
720                             1,
721                             3,
722                             Slice::empty(),
723                             Slice::empty(),
724                             None,
725                             None,
726                         ),
727                         function::from_raw_string(alloc, "trigger_sampled_error"),
728                     ),
729                     instr::popc(),
730                 ])
731             }
732         }
733     })
736 fn set_emit_statement_state<'arena, 'decl>(
737     alloc: &'arena bumpalo::Bump,
738     emitter: &mut Emitter<'arena, 'decl>,
739     default_return_value: InstrSeq<'arena>,
740     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
741     return_type_info: &HhasTypeInfo<'_>,
742     return_type: Option<&ast::Hint>,
743     pos: &Pos,
744     default_dropthrough: Option<InstrSeq<'arena>>,
745     flags: Flags,
746     is_generator: bool,
747 ) {
748     let verify_return = match &return_type_info.user_type {
749         Just(s) if s.unsafe_as_str() == "" => None,
750         _ if return_type_info.has_type_constraint() && !is_generator => return_type.cloned(),
751         _ => None,
752     };
753     let default_dropthrough = if default_dropthrough.is_some() {
754         default_dropthrough
755     } else if flags.contains(Flags::ASYNC) && verify_return.is_some() {
756         Some(InstrSeq::gather(vec![
757             instr::null(),
758             instr::verify_ret_type_c(),
759             instr::retc(),
760         ]))
761     } else {
762         None
763     };
764     let (num_out, verify_out) = emit_verify_out(alloc, params);
766     emit_statement::set_state(
767         emitter,
768         StatementState {
769             verify_return,
770             default_return_value,
771             default_dropthrough,
772             verify_out,
773             function_pos: pos.clone(),
774             num_out,
775         },
776     )
779 fn emit_verify_out<'arena>(
780     alloc: &'arena bumpalo::Bump,
781     params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
782 ) -> (usize, InstrSeq<'arena>) {
783     let param_instrs: Vec<InstrSeq<'arena>> = params
784         .iter()
785         .enumerate()
786         .filter_map(|(i, (p, _))| {
787             if p.is_inout {
788                 Some(InstrSeq::gather(vec![
789                     instr::cgetl(Local::Named(Str::new_str(alloc, p.name.unsafe_as_str()))),
790                     match p.type_info.as_ref() {
791                         Just(HhasTypeInfo { user_type, .. })
792                             if user_type.as_ref().map_or(true, |t| {
793                                 !(t.unsafe_as_str().ends_with("HH\\mixed")
794                                     || t.unsafe_as_str().ends_with("HH\\dynamic"))
795                             }) =>
796                         {
797                             instr::verify_out_type(ParamId::ParamUnnamed(i as isize))
798                         }
799                         _ => instr::empty(),
800                     },
801                 ]))
802             } else {
803                 None
804             }
805         })
806         .collect();
807     (
808         param_instrs.len(),
809         InstrSeq::gather(param_instrs.into_iter().rev().collect()),
810     )
813 pub fn emit_generics_upper_bounds<'arena>(
814     alloc: &'arena bumpalo::Bump,
815     immediate_tparams: &[ast::Tparam],
816     class_tparam_names: &[&str],
817     skip_awaitable: bool,
818 ) -> Vec<Pair<Str<'arena>, Slice<'arena, HhasTypeInfo<'arena>>>> {
819     let constraint_filter = |(kind, hint): &(ast_defs::ConstraintKind, ast::Hint)| {
820         if let ast_defs::ConstraintKind::ConstraintAs = &kind {
821             let mut tparam_names = get_tp_names(immediate_tparams);
822             tparam_names.extend_from_slice(class_tparam_names);
823             emit_type_hint::hint_to_type_info(
824                 alloc,
825                 &emit_type_hint::Kind::UpperBound,
826                 skip_awaitable,
827                 false, // nullable
828                 &tparam_names,
829                 hint,
830             )
831             .ok() //TODO(hrust) propagate Err result
832         } else {
833             None
834         }
835     };
836     let tparam_filter = |tparam: &ast::Tparam| {
837         let ubs = tparam
838             .constraints
839             .iter()
840             .filter_map(constraint_filter)
841             .collect::<Vec<_>>();
842         match &ubs[..] {
843             [] => None,
844             _ => Some(
845                 (
846                     Str::new_str(alloc, get_tp_name(tparam)),
847                     Slice::fill_iter(alloc, ubs.into_iter()),
848                 )
849                     .into(),
850             ),
851         }
852     };
853     immediate_tparams
854         .iter()
855         .filter_map(tparam_filter)
856         .collect::<Vec<_>>()
859 fn emit_shadowed_tparams(
860     immediate_tparams: &[ast::Tparam],
861     class_tparam_names: &[&str],
862 ) -> Vec<String> {
863     let s1 = get_tp_names_set(immediate_tparams);
864     let s2: HashSet<&str> = class_tparam_names.iter().cloned().collect();
865     // TODO(hrust): remove sort after Rust emitter released
866     let mut r = s1
867         .intersection(&s2)
868         .map(|s| (*s).into())
869         .collect::<Vec<_>>();
870     r.sort();
871     r
874 fn move_this(vars: &mut Vec<String>) {
875     if vars.iter().any(|v| v == THIS) {
876         vars.retain(|s| s != THIS);
877         vars.push(String::from(THIS));
878     }
881 fn get_tp_name(tparam: &ast::Tparam) -> &str {
882     let ast_defs::Id(_, name) = &tparam.name;
883     name
886 pub fn get_tp_names(tparams: &[ast::Tparam]) -> Vec<&str> {
887     tparams.iter().map(get_tp_name).collect()
890 pub fn get_tp_names_set(tparams: &[ast::Tparam]) -> HashSet<&str> {
891     tparams.iter().map(get_tp_name).collect()
894 fn modify_prog_for_debugger_eval<'arena>(_body_instrs: &mut InstrSeq<'arena>) {
895     unimplemented!() // SF(2021-03-17): I found it like this.
898 fn set_function_jmp_targets<'a, 'arena, 'decl>(
899     emitter: &mut Emitter<'arena, 'decl>,
900     env: &mut Env<'a, 'arena>,
901 ) -> bool {
902     let function_state_key = get_unique_id_for_scope(&env.scope);
903     emitter
904         .emit_global_state()
905         .functions_with_finally
906         .contains(&function_state_key)