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 bytecode_printer::{print_expr, Context, ExprEnv};
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::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;
49 use oxidized::{aast, aast_defs, ast, ast_defs, doc_comment::DocComment, namespace_env, pos::Pos};
51 use ffi::{Maybe, Maybe::*, Pair, Slice, Str};
53 use bitflags::bitflags;
54 use indexmap::IndexSet;
55 use itertools::Either;
57 static THIS: &'static str = "$this";
59 /// Optional arguments for emit_body; use Args::default() for defaults
60 pub struct Args<'a, 'arena> {
61 pub immediate_tparams: &'a Vec<ast::Tparam>,
62 pub class_tparam_names: &'a [&'a str],
63 pub ast_params: &'a Vec<ast::FunParam>,
64 pub ret: Option<&'a ast::Hint>,
66 pub deprecation_info: &'a Option<&'a [TypedValue<'arena>]>,
67 pub doc_comment: Option<DocComment>,
68 pub default_dropthrough: Option<InstrSeq<'arena>>,
69 pub call_context: Option<String>,
74 pub struct Flags: u8 {
75 const HAS_COEFFECTS_LOCAL = 1 << 0;
76 const SKIP_AWAITABLE = 1 << 1;
77 const MEMOIZE = 1 << 2;
78 const CLOSURE_BODY = 1 << 3;
79 const NATIVE = 1 << 4;
81 const DEBUGGER_MODIFY_PROGRAM = 1 << 7;
85 pub fn emit_body_with_default_args<'b, 'arena, 'decl>(
86 alloc: &'arena bumpalo::Bump,
87 emitter: &mut Emitter<'arena, 'decl>,
88 namespace: RcOc<namespace_env::Env>,
89 body: &'b ast::Program,
90 return_value: InstrSeq<'arena>,
91 ) -> Result<HhasBody<'arena>> {
93 immediate_tparams: &vec![],
94 class_tparam_names: &vec![],
97 pos: &Pos::make_none(),
98 deprecation_info: &None,
100 default_dropthrough: None,
102 flags: Flags::empty(),
116 pub fn emit_body<'b, 'arena, 'decl>(
117 alloc: &'arena bumpalo::Bump,
118 emitter: &mut Emitter<'arena, 'decl>,
119 namespace: RcOc<namespace_env::Env>,
121 return_value: InstrSeq<'arena>,
122 scope: Scope<'_, 'arena>,
123 args: Args<'_, 'arena>,
124 ) -> Result<(HhasBody<'arena>, bool, bool)> {
128 .map(|tp| tp.clone())
129 .collect::<Vec<ast::Tparam>>();
130 let mut tp_names = get_tp_names(&tparams);
131 let (is_generator, is_pair_generator) = generator::is_function_generator(&body);
133 emitter.label_gen_mut().reset();
134 emitter.iterator_mut().reset();
136 let return_type_info = make_return_type_info(
138 args.flags.contains(Flags::SKIP_AWAITABLE),
139 args.flags.contains(Flags::NATIVE),
144 let params = make_params(emitter, &mut tp_names, args.ast_params, &scope, args.flags)?;
146 let upper_bounds = emit_generics_upper_bounds(
148 args.immediate_tparams,
149 args.class_tparam_names,
150 args.flags.contains(Flags::SKIP_AWAITABLE),
152 let shadowed_tparams = emit_shadowed_tparams(args.immediate_tparams, args.class_tparam_names);
153 let decl_vars = make_decl_vars(
156 args.immediate_tparams,
161 let mut env = make_env(alloc, namespace, scope, args.call_context);
163 set_emit_statement_state(
171 args.default_dropthrough,
175 env.jump_targets_gen.reset();
177 let should_reserve_locals = set_function_jmp_targets(emitter, &mut env);
178 let num_closures = match emitter
181 .get(&get_unique_id_for_scope(&env.scope))
186 let local_gen = emitter.local_gen_mut();
187 local_gen.reset(params.len() + decl_vars.len());
188 if should_reserve_locals {
189 local_gen.reserve_retval_and_label_id_locals();
191 let body_instrs = make_body_instrs(
199 args.deprecation_info.clone(),
210 false, // is_memoize_wrapper
211 false, // is_memoize_wrapper_lsb
216 Some(return_type_info),
217 args.doc_comment.to_owned(),
225 fn make_body_instrs<'a, 'arena, 'decl>(
226 emitter: &mut Emitter<'arena, 'decl>,
227 env: &mut Env<'a, 'arena>,
228 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
229 tparams: &[ast::Tparam],
230 decl_vars: &[String],
233 deprecation_info: Option<&[TypedValue<'arena>]>,
235 ast_params: &[ast::FunParam],
237 ) -> Result<InstrSeq<'arena>> {
238 let alloc = env.arena;
239 let stmt_instrs = if flags.contains(Flags::NATIVE) {
240 instr::nativeimpl(alloc)
242 env.do_function(emitter, &body, emit_ast_body)?
245 let (begin_label, default_value_setters) =
246 emit_param::emit_param_default_value_setter(emitter, env, pos, params)?;
248 let header_content = make_header_content(
260 let first_instr_is_label = match InstrSeq::first(&stmt_instrs) {
261 Some(Instruct::ILabel(_)) => true,
264 let header = if first_instr_is_label && InstrSeq::is_empty(&header_content) {
265 InstrSeq::gather(alloc, vec![begin_label, instr::entrynop(alloc)])
267 InstrSeq::gather(alloc, vec![begin_label, header_content])
270 let mut body_instrs = InstrSeq::gather(alloc, vec![header, stmt_instrs, default_value_setters]);
271 if flags.contains(Flags::DEBUGGER_MODIFY_PROGRAM) {
272 modify_prog_for_debugger_eval(&mut body_instrs);
277 fn make_header_content<'a, 'arena, 'decl>(
278 emitter: &mut Emitter<'arena, 'decl>,
279 env: &mut Env<'a, 'arena>,
280 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
281 tparams: &[ast::Tparam],
282 _decl_vars: &[String],
284 deprecation_info: Option<&[TypedValue<'arena>]>,
286 ast_params: &[ast::FunParam],
288 ) -> Result<InstrSeq<'arena>> {
289 let alloc = env.arena;
290 let method_prolog = if flags.contains(Flags::NATIVE) {
293 emit_method_prolog(emitter, env, pos, params, ast_params, tparams)?
296 let deprecation_warning =
297 emit_deprecation_info(alloc, &env.scope, deprecation_info, emitter.systemlib())?;
299 let generator_info = if is_generator {
300 InstrSeq::gather(alloc, vec![instr::createcont(alloc), instr::popc(alloc)])
307 vec![method_prolog, deprecation_warning, generator_info],
311 fn make_decl_vars<'a, 'arena, 'decl>(
312 emitter: &mut Emitter<'arena, 'decl>,
313 scope: &Scope<'a, 'arena>,
314 immediate_tparams: &[ast::Tparam],
315 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
318 ) -> Result<Vec<String>> {
319 let explicit_use_set = &emitter.emit_global_state().explicit_use_set;
322 decl_vars::from_ast(params, body, explicit_use_set).map_err(unrecoverable)?;
324 let mut decl_vars = if arg_flags.contains(Flags::CLOSURE_BODY) {
325 let mut captured_vars = scope.get_captured_vars();
326 move_this(&mut decl_vars);
327 decl_vars.retain(|v| !captured_vars.contains(v));
328 captured_vars.extend_from_slice(&decl_vars.as_slice());
331 match &scope.items[..] {
332 [] | [.., ScopeItem::Class(_), _] => move_this(&mut decl_vars),
338 if arg_flags.contains(Flags::HAS_COEFFECTS_LOCAL) {
339 decl_vars.insert(0, string_utils::coeffects::LOCAL_NAME.into());
342 if !arg_flags.contains(Flags::CLOSURE_BODY)
345 .any(|t| t.reified != ast::ReifyKind::Erased)
347 decl_vars.insert(0, string_utils::reified::GENERICS_LOCAL_NAME.into());
352 pub fn emit_return_type_info<'arena>(
353 alloc: &'arena bumpalo::Bump,
355 skip_awaitable: bool,
356 ret: Option<&aast::Hint>,
357 ) -> Result<HhasTypeInfo<'arena>> {
359 None => Ok(HhasTypeInfo::make(
360 Just(Str::new_str(alloc, "")),
361 hhbc_by_ref_hhas_type::constraint::Constraint::default(),
363 Some(hint) => emit_type_hint::hint_to_type_info(
365 &emit_type_hint::Kind::Return,
374 fn make_return_type_info<'arena>(
375 alloc: &'arena bumpalo::Bump,
376 skip_awaitable: bool,
378 ret: Option<&aast::Hint>,
380 ) -> Result<HhasTypeInfo<'arena>> {
381 let return_type_info = emit_return_type_info(alloc, tp_names, skip_awaitable, ret);
383 return return_type_info.map(|rti| {
384 emit_type_hint::emit_type_constraint_for_native_function(alloc, tp_names, ret, rti)
390 pub fn make_env<'a, 'arena>(
391 alloc: &'arena bumpalo::Bump,
392 namespace: RcOc<namespace_env::Env>,
393 scope: Scope<'a, 'arena>,
394 call_context: Option<String>,
395 ) -> Env<'a, 'arena> {
396 let mut env = Env::default(alloc, namespace);
397 env.call_context = call_context;
402 fn make_params<'a, 'arena, 'decl>(
403 emitter: &mut Emitter<'arena, 'decl>,
404 tp_names: &mut Vec<&str>,
405 ast_params: &[ast::FunParam],
406 scope: &Scope<'a, 'arena>,
408 ) -> Result<Vec<(HhasParam<'arena>, Option<(Label, ast::Expr)>)>> {
409 let generate_defaults = !flags.contains(Flags::MEMOIZE);
410 emit_param::from_asts(emitter, tp_names, generate_defaults, scope, ast_params)
413 pub fn make_body<'a, 'arena, 'decl>(
414 alloc: &'arena bumpalo::Bump,
415 emitter: &mut Emitter<'arena, 'decl>,
416 mut body_instrs: InstrSeq<'arena>,
417 decl_vars: Vec<String>,
418 is_memoize_wrapper: bool,
419 is_memoize_wrapper_lsb: bool,
421 upper_bounds: Vec<Pair<Str<'arena>, Slice<'arena, HhasTypeInfo<'arena>>>>,
422 shadowed_tparams: Vec<String>,
423 mut params: Vec<(HhasParam<'arena>, Option<(Label, ast::Expr)>)>,
424 return_type_info: Option<HhasTypeInfo<'arena>>,
425 doc_comment: Option<DocComment>,
426 opt_env: Option<&Env<'a, 'arena>>,
427 ) -> Result<HhasBody<'arena>> {
428 emit_adata::rewrite_typed_values(emitter, &mut body_instrs)?;
432 .contains(CompilerFlags::RELABEL)
434 label_rewriter::relabel_function(alloc, &mut params, &mut body_instrs);
436 let num_iters = if is_memoize_wrapper {
439 emitter.iterator().count()
441 let body_env = if let Some(env) = opt_env {
442 let is_namespaced = env.namespace.name.is_none();
443 if let Some(cd) = env.scope.get_class() {
447 (cd.get_kind().into(), Str::new_str(alloc, cd.get_name_str())).into(),
449 parent_name: ClassExpr::get_parent_class_name(cd)
451 .map(|s| Str::new_str(alloc, s))
458 parent_name: Nothing,
464 params.iter_mut().for_each(|(p, default_value)| {
465 p.default_value = Maybe::from(
469 let mut ctx = Context::new(emitter, None, false, emitter.systemlib());
470 let expr_env = ExprEnv {
471 codegen_env: body_env.as_ref(),
473 let mut buf = Vec::new();
474 print_expr(&mut ctx, &mut buf, &expr_env, &expr)
475 .map(|_| Pair(l.clone(), Str::from_vec(alloc, buf)))
484 decl_vars: Slice::fill_iter(
486 decl_vars.into_iter().map(|s| Str::new_str(alloc, &s)),
490 is_memoize_wrapper_lsb,
492 upper_bounds: Slice::fill_iter(alloc, upper_bounds.into_iter()),
493 shadowed_tparams: Slice::fill_iter(
497 .map(|s| Str::new_str(alloc, &s)),
499 params: Slice::fill_iter(alloc, params.into_iter().map(|x| x.0.to_owned())),
500 return_type_info: Maybe::from(return_type_info),
501 doc_comment: Maybe::from(doc_comment.map(|c| Str::new_str(alloc, &(c.0).1))),
502 env: Maybe::from(body_env),
506 fn emit_ast_body<'a, 'arena, 'decl>(
507 env: &mut Env<'a, 'arena>,
508 e: &mut Emitter<'arena, 'decl>,
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>(
518 env: &mut Env<'a, 'arena>,
519 emitter: &mut Emitter<'arena, 'decl>,
521 ) -> Result<InstrSeq<'arena>> {
523 fn emit_def<'a, 'arena, 'decl>(
524 env: &mut Env<'a, 'arena>,
525 emitter: &mut Emitter<'arena, 'decl>,
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>(
536 env: &mut Env<'a, 'arena>,
537 emitter: &mut Emitter<'arena, 'decl>,
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: &ast::FunParam,
595 ) -> (RGH::ReificationLevel, Option<ast::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;
615 use hhbc_by_ref_instruction_sequence::{instr, unrecoverable, InstrSeq, Result};
616 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 loc = Named(Str::new_str(alloc, param.name.unsafe_as_str()));
662 instr::classgetc(alloc),
663 instr::clscnsl(alloc, loc.clone()),
664 instr::popl(alloc, loc),
665 instr::jmp(alloc, label_done.clone()),
666 instr::label(alloc, label_not_a_class),
667 emit_fatal_runtime(alloc, &pos, msg),
668 instr::label(alloc, label_done),
674 ////////////////////////////////////////////////////////////////////////////////
677 fn atom_instrs<'a, 'arena, 'decl>(
678 emitter: &mut Emitter<'arena, 'decl>,
679 env: &mut Env<'a, 'arena>,
680 param: &HhasParam<'arena>,
681 ast_param: &ast::FunParam,
682 tparams: &[ast::Tparam],
683 ) -> Result<Option<InstrSeq<'arena>>> {
684 let alloc = env.arena;
689 .any(|a| a.is(|x| x == ua::VIA_LABEL))
691 return Ok(None); // Not an atom. Nothing to do.
693 match &ast_param.type_hint {
694 TypeHint(_, None) => Err(raise_fatal_parse(
696 ua::VIA_LABEL.to_owned() + " param type hint unavailable",
698 TypeHint(_, Some(Hint(_, h))) => {
699 let label_done = emitter.label_gen_mut().next_regular();
700 let label_not_a_class = emitter.label_gen_mut().next_regular();
702 Happly(ast_defs::Id(_, ref ctor), vec) if ctor == "\\HH\\MemberOf" => {
705 let Hint(_, e) = hint;
708 Happly(ast_defs::Id(pos, ref tag), _) => {
709 if atom_helpers::is_erased_generic(tag, tparams) {
710 Err(raise_fatal_parse(
712 "Erased generic as HH\\MemberOf enum type",
715 if !atom_helpers::is_generic(tag, tparams) {
716 //'tag' is just a name.
717 Ok(Some(atom_helpers::emit_clscnsl(
724 emit_expression::emit_expr(
737 emit_expression::emit_expr(
752 label_not_a_class.clone(),
754 emit_expression::emit_expr(
769 "Type is not a class",
774 //'tag' is a reified generic.
775 Ok(Some(atom_helpers::emit_clscnsl(
782 emit_expression::emit_reified_generic_instrs(
786 atom_helpers::index_of_generic(
790 instr::basec(alloc, 0, MemberOpMode::ModeNone),
801 instr::istypec(alloc, IstypeOp::OpNull),
802 instr::jmpnz(alloc, label_not_a_class.clone()),
805 "Generic type parameter does not resolve to a class",
813 Haccess(Hint(_, h), _) => {
815 Happly(ast_defs::Id(pos, ref tag), _) => {
816 if atom_helpers::is_erased_generic(tag, tparams) {
817 Err(raise_fatal_parse(
819 "Erased generic as HH\\MemberOf enum type",
822 //'tag' is a type constant.
823 Ok(Some(atom_helpers::emit_clscnsl(
827 InstrSeq::gather(alloc, vec![
828 emit_expression::get_type_structure_for_hint(
832 .map(|fp| fp.name.1.as_str())
838 instr::combine_and_resolve_type_struct(alloc, 1),
839 instr::basec(alloc, 0, MemberOpMode::ModeNone),
840 instr::querym(alloc, 1, QueryOp::CGetQuiet, MemberKey::ET(Str::from("classname"), ReadonlyOp::Any)),
842 instr::istypec(alloc, IstypeOp::OpNull),
843 instr::jmpnz(alloc, label_not_a_class.clone()),
845 "Type constant does not resolve to a class",
851 _ => Err(unrecoverable(
852 "Unexpected case for HH\\MemberOf enum type",
857 Err(unrecoverable("Unexpected case for HH\\MemberOf enum type"))
861 _ => Err(unrecoverable(
862 "Wrong number of type arguments to HH\\MemberOf",
866 _ => Err(raise_fatal_parse(
868 "'".to_owned() + ua::VIA_LABEL + "' applied to a non-HH\\MemberOf parameter",
875 pub fn emit_method_prolog<'a, 'arena, 'decl>(
876 emitter: &mut Emitter<'arena, 'decl>,
877 env: &mut Env<'a, 'arena>,
879 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
880 ast_params: &[ast::FunParam],
881 tparams: &[ast::Tparam],
882 ) -> Result<InstrSeq<'arena>> {
883 let alloc = env.arena;
884 let mut make_param_instr =
885 |(param, ast_param): (&HhasParam<'arena>, &ast::FunParam)| -> Result<Option<InstrSeq<'arena>>> {
886 let param_name = || ParamId::ParamNamed(Str::new_str(alloc, param.name.unsafe_as_str()));
887 if param.is_variadic {
890 use RGH::ReificationLevel as L;
892 match has_type_constraint(env, Option::from(param.type_info.as_ref()), ast_param) {
893 (L::Unconstrained, _) => Ok(None),
894 (L::Not, _) => Ok(Some(instr::verify_param_type(alloc, param_name()))),
895 (L::Maybe, Some(h)) => {
896 if RGH::happly_decl_has_no_reified_generics(emitter, &h) {
897 Ok(Some(instr::verify_param_type(alloc, param_name())))
899 Ok(Some(InstrSeq::gather(alloc, vec![
900 emit_expression::get_type_structure_for_hint(
904 .map(|fp| fp.name.1.as_str())
910 instr::verify_param_type_ts(alloc, param_name()),
914 (L::Definitely, Some(h)) => {
915 if RGH::happly_decl_has_no_reified_generics(emitter, &h) {
916 Ok(Some(instr::verify_param_type(alloc, param_name())))
918 let check = instr::istypel(
920 Local::Named(Str::new_str(alloc, param.name.unsafe_as_str())),
923 let verify_instr = instr::verify_param_type_ts(alloc, param_name());
924 RGH::simplify_verify_type(emitter, env, pos, check, &h, verify_instr)
928 _ => Err(unrecoverable("impossible")),
931 let atom_instrs = if emitter
936 .contains(LangFlags::ENABLE_ENUM_CLASSES)
938 atom_instrs(emitter, env, param, ast_param, tparams)?
943 match (param_checks, atom_instrs) {
944 (None, None) => Ok(None),
945 (Some(is), None) => Ok(Some(is)),
946 (Some(is), Some(js)) => Ok(Some(InstrSeq::gather(alloc, vec![is, js]))),
947 (None, Some(js)) => Ok(Some(js)),
952 let ast_params = ast_params
954 .filter(|p| !(p.is_variadic && p.name == "..."))
955 .collect::<Vec<_>>();
956 if params.len() != ast_params.len() {
957 return Err(Error::Unrecoverable("length mismatch".into()));
960 let param_instrs = params
962 .zip(ast_params.into_iter())
963 .filter_map(|((param, _), ast_param)| make_param_instr((param, ast_param)).transpose())
964 .collect::<Result<Vec<_>>>()?;
965 let mut instrs = vec![emit_pos(alloc, pos)];
966 for i in param_instrs.iter() {
967 instrs.push(InstrSeq::clone(alloc, i));
969 Ok(InstrSeq::gather(alloc, instrs))
972 pub fn emit_deprecation_info<'a, 'arena>(
973 alloc: &'arena bumpalo::Bump,
974 scope: &Scope<'a, 'arena>,
975 deprecation_info: Option<&[TypedValue<'arena>]>,
977 ) -> Result<InstrSeq<'arena>> {
978 Ok(match deprecation_info {
979 None => instr::empty(alloc),
981 fn strip_id<'a>(id: &'a ast::Id) -> &'a str {
982 string_utils::strip_global_ns(id.1.as_str())
984 let (class_name, trait_instrs, concat_instruction): (String, _, _) =
985 match scope.get_class() {
986 None => ("".into(), instr::empty(alloc), instr::empty(alloc)),
987 Some(c) if c.get_kind() == ast::ClassishKind::Ctrait => (
989 InstrSeq::gather(alloc, vec![instr::self_(alloc), instr::classname(alloc)]),
990 instr::concat(alloc),
993 strip_id(c.get_name()).to_string() + "::",
999 let fn_name = match scope.items.last() {
1000 Some(ScopeItem::Function(f)) => strip_id(f.get_name()),
1001 Some(ScopeItem::Method(m)) => strip_id(m.get_name()),
1003 return Err(Error::Unrecoverable(
1004 "deprecated functions must have names".into(),
1008 let deprecation_string = class_name
1011 + (if args.is_empty() {
1012 "deprecated function"
1013 } else if let TypedValue::String(s) = &args[0] {
1016 return Err(Error::Unrecoverable(
1017 "deprecated attribute first argument is not a string".into(),
1020 let sampling_rate = if args.len() <= 1 {
1022 } else if let Some(TypedValue::Int(i)) = args.get(1) {
1025 return Err(Error::Unrecoverable(
1026 "deprecated attribute second argument is not an integer".into(),
1029 let error_code = if is_systemlib {
1033 /*E_USER_DEPRECATED*/
1037 if sampling_rate <= 0 {
1043 instr::nulluninit(alloc),
1044 instr::nulluninit(alloc),
1046 instr::string(alloc, deprecation_string),
1048 instr::int64(alloc, sampling_rate),
1049 instr::int(alloc, error_code),
1053 FcallFlags::default(),
1061 function::from_raw_string(alloc, "trigger_sampled_error"),
1071 fn set_emit_statement_state<'arena, 'decl>(
1072 alloc: &'arena bumpalo::Bump,
1073 emitter: &mut Emitter<'arena, 'decl>,
1074 default_return_value: InstrSeq<'arena>,
1075 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
1076 return_type_info: &HhasTypeInfo<'_>,
1077 return_type: Option<&ast::Hint>,
1079 default_dropthrough: Option<InstrSeq<'arena>>,
1083 let verify_return = match &return_type_info.user_type {
1084 Just(s) if s.unsafe_as_str() == "" => None,
1085 _ if return_type_info.has_type_constraint() && !is_generator => {
1086 return_type.map(|h| h.clone())
1090 let default_dropthrough = if default_dropthrough.is_some() {
1092 } else if flags.contains(Flags::ASYNC) && verify_return.is_some() {
1093 Some(InstrSeq::gather(
1097 instr::verify_ret_type_c(alloc),
1104 let (num_out, verify_out) = emit_verify_out(alloc, params);
1106 emit_statement::set_state(
1111 default_return_value,
1112 default_dropthrough,
1114 function_pos: pos.clone(),
1120 fn emit_verify_out<'arena>(
1121 alloc: &'arena bumpalo::Bump,
1122 params: &[(HhasParam<'arena>, Option<(Label, ast::Expr)>)],
1123 ) -> (usize, InstrSeq<'arena>) {
1124 let param_instrs: Vec<InstrSeq<'arena>> = params
1127 .filter_map(|(i, (p, _))| {
1129 Some(InstrSeq::gather(
1134 Local::Named(Str::new_str(alloc, p.name.unsafe_as_str())),
1136 match p.type_info.as_ref() {
1137 Just(HhasTypeInfo { user_type, .. })
1138 if user_type.as_ref().map_or(true, |t| {
1139 !(t.unsafe_as_str().ends_with("HH\\mixed")
1140 || t.unsafe_as_str().ends_with("HH\\dynamic"))
1143 instr::verify_out_type(alloc, ParamId::ParamUnnamed(i as isize))
1145 _ => instr::empty(alloc),
1156 InstrSeq::gather(alloc, param_instrs.into_iter().rev().collect()),
1160 pub fn emit_generics_upper_bounds<'arena>(
1161 alloc: &'arena bumpalo::Bump,
1162 immediate_tparams: &[ast::Tparam],
1163 class_tparam_names: &[&str],
1164 skip_awaitable: bool,
1165 ) -> Vec<Pair<Str<'arena>, Slice<'arena, HhasTypeInfo<'arena>>>> {
1166 let constraint_filter = |(kind, hint): &(ast_defs::ConstraintKind, ast::Hint)| {
1167 if let ast_defs::ConstraintKind::ConstraintAs = &kind {
1168 let mut tparam_names = get_tp_names(immediate_tparams);
1169 tparam_names.extend_from_slice(class_tparam_names);
1170 emit_type_hint::hint_to_type_info(
1172 &emit_type_hint::Kind::UpperBound,
1178 .ok() //TODO(hrust) propagate Err result
1183 let tparam_filter = |tparam: &ast::Tparam| {
1187 .filter_map(constraint_filter)
1188 .collect::<Vec<_>>();
1193 Str::new_str(alloc, get_tp_name(tparam)),
1194 Slice::fill_iter(alloc, ubs.into_iter()),
1202 .filter_map(tparam_filter)
1203 .collect::<Vec<_>>()
1206 fn emit_shadowed_tparams(
1207 immediate_tparams: &[ast::Tparam],
1208 class_tparam_names: &[&str],
1210 let s1 = get_tp_names_set(immediate_tparams);
1211 let s2: HashSet<&str> = class_tparam_names.iter().cloned().collect();
1212 // TODO(hrust): remove sort after Rust emitter released
1215 .map(|s| (*s).into())
1216 .collect::<Vec<_>>();
1221 fn move_this(vars: &mut Vec<String>) {
1222 if vars.iter().any(|v| v == &THIS) {
1223 vars.retain(|s| s != THIS);
1224 vars.push(String::from(THIS));
1228 fn get_tp_name(tparam: &ast::Tparam) -> &str {
1229 let ast_defs::Id(_, name) = &tparam.name;
1233 pub fn get_tp_names(tparams: &[ast::Tparam]) -> Vec<&str> {
1234 tparams.iter().map(get_tp_name).collect()
1237 pub fn get_tp_names_set(tparams: &[ast::Tparam]) -> HashSet<&str> {
1238 tparams.iter().map(get_tp_name).collect()
1241 #[allow(clippy::needless_lifetimes)]
1242 fn modify_prog_for_debugger_eval<'arena>(_body_instrs: &mut InstrSeq<'arena>) {
1243 unimplemented!() // SF(2021-03-17): I found it like this.
1246 fn set_function_jmp_targets<'a, 'arena, 'decl>(
1247 emitter: &mut Emitter<'arena, 'decl>,
1248 env: &mut Env<'a, 'arena>,
1250 let function_state_key = get_unique_id_for_scope(&env.scope);
1252 .emit_global_state()
1253 .functions_with_finally
1254 .contains(&function_state_key)