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 mod reified_generics_helpers;
7 mod try_finally_rewriter;
9 use emit_statement::emit_final_stmts;
10 use reified_generics_helpers as RGH;
13 use aast_defs::{Hint, Hint_::*};
14 use decl_provider::DeclProvider;
16 use hhbc_by_ref_ast_body::AstBody;
17 use hhbc_by_ref_ast_class_expr::ClassExpr;
18 use hhbc_by_ref_ast_scope::{Scope, ScopeItem};
19 use hhbc_by_ref_decl_vars as decl_vars;
20 use hhbc_by_ref_emit_adata as emit_adata;
21 use hhbc_by_ref_emit_expression as emit_expression;
22 use hhbc_by_ref_emit_fatal::{emit_fatal_runtime, raise_fatal_parse};
23 use hhbc_by_ref_emit_param as emit_param;
24 use hhbc_by_ref_emit_pos::emit_pos;
25 use hhbc_by_ref_emit_type_hint as emit_type_hint;
26 use hhbc_by_ref_env::{emitter::Emitter, Env};
27 use hhbc_by_ref_generator as generator;
28 use hhbc_by_ref_hhas_body::{HhasBody, HhasBodyEnv};
29 use hhbc_by_ref_hhas_param::HhasParam;
30 use hhbc_by_ref_hhas_type::Info as HhasTypeInfo;
31 use hhbc_by_ref_hhbc_ast::{
32 ClassishKind, FcallArgs, FcallFlags, Instruct, IstypeOp, MemberKey, MemberOpMode, ParamId,
35 use hhbc_by_ref_hhbc_id::function;
36 use hhbc_by_ref_hhbc_string_utils as string_utils;
37 use hhbc_by_ref_instruction_sequence::{instr, unrecoverable, Error, InstrSeq, Result};
38 use hhbc_by_ref_label::Label;
39 use hhbc_by_ref_label_rewriter as label_rewriter;
40 use hhbc_by_ref_local::Local;
41 use hhbc_by_ref_options::{CompilerFlags, LangFlags};
42 use hhbc_by_ref_runtime::TypedValue;
43 use hhbc_by_ref_statement_state::StatementState;
44 use hhbc_by_ref_unique_id_builder::*;
46 use naming_special_names_rust::user_attributes as ua;
48 use ocamlrep::rc::RcOc;
50 aast, aast_defs, ast as tast, ast_defs, doc_comment::DocComment, namespace_env, pos::Pos,
53 use ffi::{Maybe::*, Slice, Str};
55 use bitflags::bitflags;
56 use indexmap::IndexSet;
57 use itertools::Either;
59 static THIS: &'static str = "$this";
61 /// Optional arguments for emit_body; use Args::default() for defaults
62 pub struct Args<'a, 'arena> {
63 pub immediate_tparams: &'a Vec<tast::Tparam>,
64 pub class_tparam_names: &'a [&'a str],
65 pub ast_params: &'a Vec<tast::FunParam>,
66 pub ret: Option<&'a tast::Hint>,
68 pub deprecation_info: &'a Option<&'a [TypedValue<'arena>]>,
69 pub doc_comment: Option<DocComment>,
70 pub default_dropthrough: Option<InstrSeq<'arena>>,
71 pub call_context: Option<String>,
76 pub struct Flags: u8 {
77 const HAS_COEFFECTS_LOCAL = 1 << 0;
78 const SKIP_AWAITABLE = 1 << 1;
79 const MEMOIZE = 1 << 2;
80 const CLOSURE_BODY = 1 << 3;
81 const NATIVE = 1 << 4;
83 const DEBUGGER_MODIFY_PROGRAM = 1 << 7;
87 pub fn emit_body_with_default_args<'b, 'arena, 'decl, D: DeclProvider<'decl>>(
88 alloc: &'arena bumpalo::Bump,
89 emitter: &mut Emitter<'arena, 'decl, D>,
90 namespace: RcOc<namespace_env::Env>,
91 body: &'b tast::Program,
92 return_value: InstrSeq<'arena>,
93 ) -> Result<HhasBody<'arena>> {
95 immediate_tparams: &vec![],
96 class_tparam_names: &vec![],
99 pos: &Pos::make_none(),
100 deprecation_info: &None,
102 default_dropthrough: None,
104 flags: Flags::empty(),
118 pub fn emit_body<'b, 'arena, 'decl, D: DeclProvider<'decl>>(
119 alloc: &'arena bumpalo::Bump,
120 emitter: &mut Emitter<'arena, 'decl, D>,
121 namespace: RcOc<namespace_env::Env>,
123 return_value: InstrSeq<'arena>,
125 args: Args<'_, 'arena>,
126 ) -> Result<(HhasBody<'arena>, bool, bool)> {
130 .map(|tp| tp.clone())
131 .collect::<Vec<tast::Tparam>>();
132 let mut tp_names = get_tp_names(&tparams);
133 let (is_generator, is_pair_generator) = generator::is_function_generator(&body);
135 emitter.label_gen_mut().reset();
136 emitter.iterator_mut().reset();
138 let return_type_info = make_return_type_info(
140 args.flags.contains(Flags::SKIP_AWAITABLE),
141 args.flags.contains(Flags::NATIVE),
146 let params = make_params(
155 let upper_bounds = emit_generics_upper_bounds(
157 args.immediate_tparams,
158 args.class_tparam_names,
159 args.flags.contains(Flags::SKIP_AWAITABLE),
161 let shadowed_tparams = emit_shadowed_tparams(args.immediate_tparams, args.class_tparam_names);
162 let decl_vars = make_decl_vars(
165 args.immediate_tparams,
170 let mut env = make_env(alloc, namespace, scope, args.call_context);
172 set_emit_statement_state(
180 args.default_dropthrough,
184 env.jump_targets_gen.reset();
186 let should_reserve_locals = set_function_jmp_targets(emitter, &mut env);
187 let num_closures = match emitter
190 .get(&get_unique_id_for_scope(&env.scope))
195 let local_gen = emitter.local_gen_mut();
196 local_gen.reset(params.len() + decl_vars.len());
197 if should_reserve_locals {
198 local_gen.reserve_retval_and_label_id_locals();
200 let body_instrs = make_body_instrs(
208 args.deprecation_info.clone(),
219 false, // is_memoize_wrapper
220 false, // is_memoize_wrapper_lsb
225 Some(return_type_info),
226 args.doc_comment.to_owned(),
234 fn make_body_instrs<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
235 emitter: &mut Emitter<'arena, 'decl, D>,
236 env: &mut Env<'a, 'arena>,
237 params: &[HhasParam<'arena>],
238 tparams: &[tast::Tparam],
239 decl_vars: &[String],
242 deprecation_info: Option<&[TypedValue<'arena>]>,
244 ast_params: &[tast::FunParam],
246 ) -> Result<InstrSeq<'arena>> {
247 let alloc = env.arena;
248 let stmt_instrs = if flags.contains(Flags::NATIVE) {
249 instr::nativeimpl(alloc)
251 env.do_function(emitter, &body, emit_ast_body)?
254 let (begin_label, default_value_setters) =
255 emit_param::emit_param_default_value_setter(emitter, env, pos, params)?;
257 let header_content = make_header_content(
269 let first_instr_is_label = match InstrSeq::first(&stmt_instrs) {
270 Some(Instruct::ILabel(_)) => true,
273 let header = if first_instr_is_label && InstrSeq::is_empty(&header_content) {
274 InstrSeq::gather(alloc, vec![begin_label, instr::entrynop(alloc)])
276 InstrSeq::gather(alloc, vec![begin_label, header_content])
279 let mut body_instrs = InstrSeq::gather(alloc, vec![header, stmt_instrs, default_value_setters]);
280 if flags.contains(Flags::DEBUGGER_MODIFY_PROGRAM) {
281 modify_prog_for_debugger_eval(&mut body_instrs);
286 fn make_header_content<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
287 emitter: &mut Emitter<'arena, 'decl, D>,
288 env: &mut Env<'a, 'arena>,
289 params: &[HhasParam<'arena>],
290 tparams: &[tast::Tparam],
291 _decl_vars: &[String],
293 deprecation_info: Option<&[TypedValue<'arena>]>,
295 ast_params: &[tast::FunParam],
297 ) -> Result<InstrSeq<'arena>> {
298 let alloc = env.arena;
299 let method_prolog = if flags.contains(Flags::NATIVE) {
302 emit_method_prolog(emitter, env, pos, params, ast_params, tparams)?
305 let deprecation_warning =
306 emit_deprecation_info(alloc, &env.scope, deprecation_info, emitter.systemlib())?;
308 let generator_info = if is_generator {
309 InstrSeq::gather(alloc, vec![instr::createcont(alloc), instr::popc(alloc)])
316 vec![method_prolog, deprecation_warning, generator_info],
320 fn make_decl_vars<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
321 emitter: &mut Emitter<'arena, 'decl, D>,
323 immediate_tparams: &[tast::Tparam],
324 params: &[HhasParam<'arena>],
327 ) -> Result<Vec<String>> {
328 let explicit_use_set = &emitter.emit_global_state().explicit_use_set;
331 decl_vars::from_ast(params, body, explicit_use_set).map_err(unrecoverable)?;
333 let mut decl_vars = if arg_flags.contains(Flags::CLOSURE_BODY) {
334 let mut captured_vars = scope.get_captured_vars();
335 move_this(&mut decl_vars);
336 decl_vars.retain(|v| !captured_vars.contains(v));
337 captured_vars.extend_from_slice(&decl_vars.as_slice());
340 match &scope.items[..] {
341 [] | [.., ScopeItem::Class(_), _] => move_this(&mut decl_vars),
347 if arg_flags.contains(Flags::HAS_COEFFECTS_LOCAL) {
348 decl_vars.insert(0, string_utils::coeffects::LOCAL_NAME.into());
351 if !arg_flags.contains(Flags::CLOSURE_BODY)
354 .any(|t| t.reified != tast::ReifyKind::Erased)
356 decl_vars.insert(0, string_utils::reified::GENERICS_LOCAL_NAME.into());
361 pub fn emit_return_type_info<'arena>(
362 alloc: &'arena bumpalo::Bump,
364 skip_awaitable: bool,
365 ret: Option<&aast::Hint>,
366 ) -> Result<HhasTypeInfo<'arena>> {
368 None => Ok(HhasTypeInfo::make(
369 Just(Str::new_str(alloc, "")),
370 hhbc_by_ref_hhas_type::constraint::Constraint::default(),
372 Some(hint) => emit_type_hint::hint_to_type_info(
374 &emit_type_hint::Kind::Return,
383 fn make_return_type_info<'arena>(
384 alloc: &'arena bumpalo::Bump,
385 skip_awaitable: bool,
387 ret: Option<&aast::Hint>,
389 ) -> Result<HhasTypeInfo<'arena>> {
390 let return_type_info = emit_return_type_info(alloc, tp_names, skip_awaitable, ret);
392 return return_type_info.map(|rti| {
393 emit_type_hint::emit_type_constraint_for_native_function(alloc, tp_names, ret, rti)
399 pub fn make_env<'a, 'arena>(
400 alloc: &'arena bumpalo::Bump,
401 namespace: RcOc<namespace_env::Env>,
403 call_context: Option<String>,
404 ) -> Env<'a, 'arena> {
405 let mut env = Env::default(alloc, namespace);
406 env.call_context = call_context;
411 fn make_params<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
412 alloc: &'arena bumpalo::Bump,
413 emitter: &mut Emitter<'arena, 'decl, D>,
414 tp_names: &mut Vec<&str>,
415 ast_params: &[tast::FunParam],
418 ) -> Result<Vec<HhasParam<'arena>>> {
419 let generate_defaults = !flags.contains(Flags::MEMOIZE);
420 emit_param::from_asts(
430 pub fn make_body<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
431 alloc: &'arena bumpalo::Bump,
432 emitter: &mut Emitter<'arena, 'decl, D>,
433 mut body_instrs: InstrSeq<'arena>,
434 decl_vars: Vec<String>,
435 is_memoize_wrapper: bool,
436 is_memoize_wrapper_lsb: bool,
438 upper_bounds: Vec<(String, Vec<HhasTypeInfo<'arena>>)>,
439 shadowed_tparams: Vec<String>,
440 mut params: Vec<HhasParam<'arena>>,
441 return_type_info: Option<HhasTypeInfo<'arena>>,
442 doc_comment: Option<DocComment>,
443 opt_env: Option<&Env<'a, 'arena>>,
444 ) -> Result<HhasBody<'arena>> {
445 emit_adata::rewrite_typed_values(alloc, emitter, &mut body_instrs)?;
449 .contains(CompilerFlags::RELABEL)
451 label_rewriter::relabel_function(alloc, &mut params, &mut body_instrs);
453 let num_iters = if is_memoize_wrapper {
456 emitter.iterator().count()
458 let body_env = if let Some(env) = opt_env {
459 let is_namespaced = env.namespace.name.is_none();
460 if let Some(cd) = env.scope.get_class() {
463 class_info: Just((cd.get_kind().into(), Str::new_str(alloc, cd.get_name_str()))),
464 parent_name: ClassExpr::get_parent_class_name(cd)
465 .map(|s| Str::new_str(alloc, s))
472 parent_name: Nothing,
480 decl_vars: Slice::fill_iter(alloc, decl_vars.into_iter().map(|s| Str::new_str(alloc, s))),
483 is_memoize_wrapper_lsb,
485 upper_bounds: Slice::fill_iter(
487 upper_bounds.into_iter().map(|(s, is)| {
489 Str::new_str(alloc, s),
490 Slice::fill_iter(alloc, is.into_iter()),
495 shadowed_tparams: Slice::fill_iter(
497 shadowed_tparams.into_iter().map(|s| Str::new_str(alloc, s)),
499 params: Slice::fill_iter(alloc, params.into_iter()),
501 doc_comment: doc_comment.map(|c| Str::new_str(alloc, &(c.0).1)),
506 fn emit_ast_body<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
507 env: &mut Env<'a, 'arena>,
508 e: &mut Emitter<'arena, 'decl, D>,
510 ) -> Result<InstrSeq<'arena>> {
512 Either::Left(p) => emit_defs(env, e, p),
513 Either::Right(b) => emit_final_stmts(e, env, b),
517 fn emit_defs<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
518 env: &mut Env<'a, 'arena>,
519 emitter: &mut Emitter<'arena, 'decl, D>,
521 ) -> Result<InstrSeq<'arena>> {
523 fn emit_def<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
524 env: &mut Env<'a, 'arena>,
525 emitter: &mut Emitter<'arena, 'decl, D>,
527 ) -> Result<InstrSeq<'arena>> {
528 let alloc = env.arena;
530 Def::Stmt(s) => emit_statement::emit_stmt(emitter, env, s),
531 Def::Namespace(ns) => emit_defs(env, emitter, &ns.1),
532 _ => Ok(instr::empty(alloc)),
535 fn aux<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
536 env: &mut Env<'a, 'arena>,
537 emitter: &mut Emitter<'arena, 'decl, D>,
539 ) -> Result<InstrSeq<'arena>> {
540 let alloc = env.arena;
542 [Def::SetNamespaceEnv(ns), ..] => {
543 env.namespace = RcOc::clone(&ns);
544 aux(env, emitter, &defs[1..])
546 [] => emit_statement::emit_dropthrough_return(emitter, env),
548 // emit last statement in the list as final statement
549 emit_statement::emit_final_stmt(emitter, env, &s)
551 [Def::Stmt(s1), Def::Stmt(s2)] => match s2.1.as_markup() {
552 Some(id) if id.1.is_empty() => emit_statement::emit_final_stmt(emitter, env, &s1),
553 _ => Ok(InstrSeq::gather(
556 emit_statement::emit_stmt(emitter, env, s1)?,
557 emit_statement::emit_final_stmt(emitter, env, &s2)?,
561 [def, ..] => Ok(InstrSeq::gather(
563 vec![emit_def(env, emitter, def)?, aux(env, emitter, &defs[1..])?],
567 let alloc = env.arena;
569 [Def::Stmt(s), ..] if s.1.is_markup() => Ok(InstrSeq::gather(
572 emit_statement::emit_markup(emitter, env, s.1.as_markup().unwrap(), true)?,
573 aux(env, emitter, &prog[1..])?,
576 [] | [_] => aux(env, emitter, &prog[..]),
578 let i1 = emit_def(env, emitter, def)?;
580 emit_defs(env, emitter, &prog[1..])
584 vec![i1, aux(env, emitter, &prog[1..])?],
591 pub fn has_type_constraint<'a, 'arena>(
592 env: &Env<'a, 'arena>,
593 ti: Option<&HhasTypeInfo>,
594 ast_param: &tast::FunParam,
595 ) -> (RGH::ReificationLevel, Option<tast::Hint>) {
596 use RGH::ReificationLevel as L;
597 match (ti, &ast_param.type_hint.1) {
598 (Some(ti), Some(h)) if ti.has_type_constraint() => {
599 // TODO(hrust): how to avoid clone on h
600 let h = RGH::remove_erased_generics(env, h.clone());
601 (RGH::has_reified_type_constraint(env, &h), Some(h))
603 _ => (L::Unconstrained, None),
607 ////////////////////////////////////////////////////////////////////////////////
612 use aast_defs::ReifyKind::Erased;
614 use hhbc_by_ref_instruction_sequence::{instr, unrecoverable, InstrSeq, Result};
615 use hhbc_by_ref_local::Local::Named;
618 fn strip_ns(name: &str) -> &str {
619 match name.chars().next() {
620 Some('\\') => &name[1..],
625 pub fn is_generic(name: &str, tparams: &[Tparam]) -> bool {
626 tparams.iter().any(|t| match t.name {
627 Id(_, ref s) => s == self::strip_ns(name),
631 pub fn is_erased_generic(name: &str, tparams: &[Tparam]) -> bool {
632 tparams.iter().any(|t| match t.name {
633 Id(_, ref s) if s == self::strip_ns(name) => t.reified == Erased,
638 pub fn index_of_generic(tparams: &[Tparam], name: &str) -> Result<usize> {
639 match tparams.iter().enumerate().find_map(|(i, t)| match t.name {
640 Id(_, ref s) if s == self::strip_ns(name) => Some(i),
644 None => Err(unrecoverable("Expected generic")),
648 pub fn emit_clscnsl<'arena>(
649 alloc: &'arena bumpalo::Bump,
650 param: &HhasParam<'arena>,
652 cls_instrs: InstrSeq<'arena>,
654 label_not_a_class: Label,
656 ) -> Result<InstrSeq<'arena>> {
657 let param_name = ¶m.name;
658 let loc = Named(Str::new_str(alloc, param_name));
663 instr::classgetc(alloc),
664 instr::clscnsl(alloc, loc.clone()),
665 instr::popl(alloc, loc),
666 instr::jmp(alloc, label_done.clone()),
667 instr::label(alloc, label_not_a_class),
668 emit_fatal_runtime(alloc, &pos, msg),
669 instr::label(alloc, label_done),
675 ////////////////////////////////////////////////////////////////////////////////
678 fn atom_instrs<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
679 emitter: &mut Emitter<'arena, 'decl, D>,
680 env: &mut Env<'a, 'arena>,
681 param: &HhasParam<'arena>,
682 ast_param: &tast::FunParam,
683 tparams: &[tast::Tparam],
684 ) -> Result<Option<InstrSeq<'arena>>> {
685 let alloc = env.arena;
690 .any(|a| a.is(|x| x == ua::VIA_LABEL))
692 return Ok(None); // Not an atom. Nothing to do.
694 match &ast_param.type_hint {
695 TypeHint(_, None) => Err(raise_fatal_parse(
697 ua::VIA_LABEL.to_owned() + " param type hint unavailable",
699 TypeHint(_, Some(Hint(_, h))) => {
700 let label_done = emitter.label_gen_mut().next_regular();
701 let label_not_a_class = emitter.label_gen_mut().next_regular();
703 Happly(ast_defs::Id(_, ref ctor), vec) if ctor == "\\HH\\MemberOf" => {
706 let Hint(_, e) = hint;
709 Happly(ast_defs::Id(pos, ref tag), _) => {
710 if atom_helpers::is_erased_generic(tag, tparams) {
711 Err(raise_fatal_parse(
713 "Erased generic as HH\\MemberOf enum type",
716 if !atom_helpers::is_generic(tag, tparams) {
717 //'tag' is just a name.
718 Ok(Some(atom_helpers::emit_clscnsl(
725 emit_expression::emit_expr(
738 emit_expression::emit_expr(
753 label_not_a_class.clone(),
755 emit_expression::emit_expr(
770 "Type is not a class",
775 //'tag' is a reified generic.
776 Ok(Some(atom_helpers::emit_clscnsl(
783 emit_expression::emit_reified_generic_instrs(
787 atom_helpers::index_of_generic(
791 instr::basec(alloc, 0, MemberOpMode::ModeNone),
802 instr::istypec(alloc, IstypeOp::OpNull),
803 instr::jmpnz(alloc, label_not_a_class.clone()),
806 "Generic type parameter does not resolve to a class",
814 Haccess(Hint(_, h), _) => {
816 Happly(ast_defs::Id(pos, ref tag), _) => {
817 if atom_helpers::is_erased_generic(tag, tparams) {
818 Err(raise_fatal_parse(
820 "Erased generic as HH\\MemberOf enum type",
823 //'tag' is a type constant.
824 Ok(Some(atom_helpers::emit_clscnsl(
828 InstrSeq::gather(alloc, vec![
829 emit_expression::get_type_structure_for_hint(
834 .map(|fp| fp.name.1.as_str())
840 instr::combine_and_resolve_type_struct(alloc, 1),
841 instr::basec(alloc, 0, MemberOpMode::ModeNone),
842 instr::querym(alloc, 1, QueryOp::CGetQuiet, MemberKey::ET(Str::from("classname"), ReadOnlyOp::Any)),
844 instr::istypec(alloc, IstypeOp::OpNull),
845 instr::jmpnz(alloc, label_not_a_class.clone()),
847 "Type constant does not resolve to a class",
853 _ => Err(unrecoverable(
854 "Unexpected case for HH\\MemberOf enum type",
859 Err(unrecoverable("Unexpected case for HH\\MemberOf enum type"))
863 _ => Err(unrecoverable(
864 "Wrong number of type arguments to HH\\MemberOf",
868 _ => Err(raise_fatal_parse(
870 "'".to_owned() + ua::VIA_LABEL + "' applied to a non-HH\\MemberOf parameter",
877 pub fn emit_method_prolog<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
878 emitter: &mut Emitter<'arena, 'decl, D>,
879 env: &mut Env<'a, 'arena>,
881 params: &[HhasParam<'arena>],
882 ast_params: &[tast::FunParam],
883 tparams: &[tast::Tparam],
884 ) -> Result<InstrSeq<'arena>> {
885 let alloc = env.arena;
886 let mut make_param_instr =
887 |(param, ast_param): (&HhasParam<'arena>, &tast::FunParam)| -> Result<Option<InstrSeq<'arena>>> {
888 let param_name = ¶m.name;
889 let param_name = || ParamId::ParamNamed(Str::new_str(alloc, param_name));
890 if param.is_variadic {
893 use RGH::ReificationLevel as L;
895 match has_type_constraint(env, Option::from(param.type_info.as_ref()), ast_param) {
896 (L::Unconstrained, _) => Ok(None),
897 (L::Not, _) => Ok(Some(instr::verify_param_type(alloc, param_name()))),
898 (L::Maybe, Some(h)) => Ok(Some(InstrSeq::gather(alloc, vec![
899 emit_expression::get_type_structure_for_hint(
904 .map(|fp| fp.name.1.as_str())
910 instr::verify_param_type_ts(alloc, param_name()),
912 (L::Definitely, Some(h)) => {
913 let check = instr::istypel(
915 Local::Named(Str::new_str(alloc, param.name.as_str())),
918 let verify_instr = instr::verify_param_type_ts(alloc, param_name());
919 RGH::simplify_verify_type(emitter, env, pos, check, &h, verify_instr)
922 _ => Err(unrecoverable("impossible")),
925 let atom_instrs = if emitter
930 .contains(LangFlags::ENABLE_ENUM_CLASSES)
932 atom_instrs(emitter, env, param, ast_param, tparams)?
937 match (param_checks, atom_instrs) {
938 (None, None) => Ok(None),
939 (Some(is), None) => Ok(Some(is)),
940 (Some(is), Some(js)) => Ok(Some(InstrSeq::gather(alloc, vec![is, js]))),
941 (None, Some(js)) => Ok(Some(js)),
946 let ast_params = ast_params
948 .filter(|p| !(p.is_variadic && p.name == "..."))
949 .collect::<Vec<_>>();
950 if params.len() != ast_params.len() {
951 return Err(Error::Unrecoverable("length mismatch".into()));
954 let param_instrs = params
956 .zip(ast_params.into_iter())
957 .filter_map(|p| make_param_instr(p).transpose())
958 .collect::<Result<Vec<_>>>()?;
959 let mut instrs = vec![emit_pos(alloc, pos)];
960 for i in param_instrs.iter() {
961 instrs.push(InstrSeq::clone(alloc, i));
963 Ok(InstrSeq::gather(alloc, instrs))
966 pub fn emit_deprecation_info<'a, 'arena>(
967 alloc: &'arena bumpalo::Bump,
969 deprecation_info: Option<&[TypedValue<'arena>]>,
971 ) -> Result<InstrSeq<'arena>> {
972 Ok(match deprecation_info {
973 None => instr::empty(alloc),
975 fn strip_id<'a>(id: &'a tast::Id) -> &'a str {
976 string_utils::strip_global_ns(id.1.as_str())
978 let (class_name, trait_instrs, concat_instruction): (String, _, _) =
979 match scope.get_class() {
980 None => ("".into(), instr::empty(alloc), instr::empty(alloc)),
981 Some(c) if c.get_kind() == tast::ClassishKind::Ctrait => (
983 InstrSeq::gather(alloc, vec![instr::self_(alloc), instr::classname(alloc)]),
984 instr::concat(alloc),
987 strip_id(c.get_name()).to_string() + "::",
993 let fn_name = match scope.items.last() {
994 Some(ScopeItem::Function(f)) => strip_id(f.get_name()),
995 Some(ScopeItem::Method(m)) => strip_id(m.get_name()),
997 return Err(Error::Unrecoverable(
998 "deprecated functions must have names".into(),
1002 let deprecation_string = class_name
1005 + (if args.is_empty() {
1006 "deprecated function"
1007 } else if let TypedValue::String(s) = &args[0] {
1010 return Err(Error::Unrecoverable(
1011 "deprecated attribute first argument is not a string".into(),
1014 let sampling_rate = if args.len() <= 1 {
1016 } else if let Some(TypedValue::Int(i)) = args.get(1) {
1019 return Err(Error::Unrecoverable(
1020 "deprecated attribute second argument is not an integer".into(),
1023 let error_code = if is_systemlib {
1027 /*E_USER_DEPRECATED*/
1031 if sampling_rate <= 0 {
1037 instr::nulluninit(alloc),
1038 instr::nulluninit(alloc),
1040 instr::string(alloc, deprecation_string),
1042 instr::int64(alloc, sampling_rate),
1043 instr::int(alloc, error_code),
1046 FcallArgs::new(FcallFlags::default(), 1, Slice::empty(), None, 3, None),
1047 function::from_raw_string(alloc, "trigger_sampled_error"),
1057 fn set_emit_statement_state<'arena, 'decl, D: DeclProvider<'decl>>(
1058 alloc: &'arena bumpalo::Bump,
1059 emitter: &mut Emitter<'arena, 'decl, D>,
1060 default_return_value: InstrSeq<'arena>,
1061 params: &[HhasParam<'arena>],
1062 return_type_info: &HhasTypeInfo,
1063 return_type: Option<&tast::Hint>,
1065 default_dropthrough: Option<InstrSeq<'arena>>,
1069 let verify_return = match &return_type_info.user_type {
1070 Just(s) if s.as_str() == "" => None,
1071 _ if return_type_info.has_type_constraint() && !is_generator => {
1072 return_type.map(|h| h.clone())
1076 let default_dropthrough = if default_dropthrough.is_some() {
1078 } else if flags.contains(Flags::ASYNC) && verify_return.is_some() {
1079 Some(InstrSeq::gather(
1083 instr::verify_ret_type_c(alloc),
1090 let (num_out, verify_out) = emit_verify_out(alloc, params);
1092 emit_statement::set_state(
1097 default_return_value,
1098 default_dropthrough,
1100 function_pos: pos.clone(),
1106 fn emit_verify_out<'arena>(
1107 alloc: &'arena bumpalo::Bump,
1108 params: &[HhasParam<'arena>],
1109 ) -> (usize, InstrSeq<'arena>) {
1110 let param_instrs: Vec<InstrSeq<'arena>> = params
1113 .filter_map(|(i, p)| {
1115 Some(InstrSeq::gather(
1118 instr::cgetl(alloc, Local::Named(Str::new_str(alloc, p.name.as_str()))),
1119 match p.type_info.as_ref() {
1120 Just(HhasTypeInfo { user_type, .. })
1121 if user_type.as_ref().map_or(true, |t| {
1122 !(t.as_str().ends_with("HH\\mixed")
1123 || t.as_str().ends_with("HH\\dynamic"))
1126 instr::verify_out_type(alloc, ParamId::ParamUnnamed(i as isize))
1128 _ => instr::empty(alloc),
1139 InstrSeq::gather(alloc, param_instrs.into_iter().rev().collect()),
1143 pub fn emit_generics_upper_bounds<'arena>(
1144 alloc: &'arena bumpalo::Bump,
1145 immediate_tparams: &[tast::Tparam],
1146 class_tparam_names: &[&str],
1147 skip_awaitable: bool,
1148 ) -> Vec<(String, Vec<HhasTypeInfo<'arena>>)> {
1149 let constraint_filter = |(kind, hint): &(ast_defs::ConstraintKind, tast::Hint)| {
1150 if let ast_defs::ConstraintKind::ConstraintAs = &kind {
1151 let mut tparam_names = get_tp_names(immediate_tparams);
1152 tparam_names.extend_from_slice(class_tparam_names);
1153 emit_type_hint::hint_to_type_info(
1155 &emit_type_hint::Kind::UpperBound,
1161 .ok() //TODO(hrust) propagate Err result
1166 let tparam_filter = |tparam: &tast::Tparam| {
1170 .filter_map(constraint_filter)
1171 .collect::<Vec<_>>();
1174 _ => Some((get_tp_name(tparam).to_owned(), ubs)),
1179 .filter_map(tparam_filter)
1180 .collect::<Vec<_>>()
1183 fn emit_shadowed_tparams(
1184 immediate_tparams: &[tast::Tparam],
1185 class_tparam_names: &[&str],
1187 let s1 = get_tp_names_set(immediate_tparams);
1188 let s2: HashSet<&str> = class_tparam_names.iter().cloned().collect();
1189 // TODO(hrust): remove sort after Rust emitter released
1192 .map(|s| (*s).into())
1193 .collect::<Vec<_>>();
1198 fn move_this(vars: &mut Vec<String>) {
1199 if vars.iter().any(|v| v == &THIS) {
1200 vars.retain(|s| s != THIS);
1201 vars.push(String::from(THIS));
1205 fn get_tp_name(tparam: &tast::Tparam) -> &str {
1206 let ast_defs::Id(_, name) = &tparam.name;
1210 pub fn get_tp_names(tparams: &[tast::Tparam]) -> Vec<&str> {
1211 tparams.iter().map(get_tp_name).collect()
1214 pub fn get_tp_names_set(tparams: &[tast::Tparam]) -> HashSet<&str> {
1215 tparams.iter().map(get_tp_name).collect()
1218 #[allow(clippy::needless_lifetimes)]
1219 fn modify_prog_for_debugger_eval<'arena>(_body_instrs: &mut InstrSeq<'arena>) {
1220 unimplemented!() // SF(2021-03-17): I found it like this.
1223 fn set_function_jmp_targets<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
1224 emitter: &mut Emitter<'arena, 'decl, D>,
1225 env: &mut Env<'a, 'arena>,
1227 let function_state_key = get_unique_id_for_scope(&env.scope);
1229 .emit_global_state()
1230 .functions_with_finally
1231 .contains(&function_state_key)