Add ReadonlyOp to MemberKey for member operation bytecodes 2/2
[hiphop-php.git] / hphp / hack / src / hhbc / emit_expression.rs
blob03584f74f4af3815bdecda7a4eef85e4c5678a17
1 // Copyright (c) Facebook, Inc. and its affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 use ast_class_expr_rust::ClassExpr;
7 use ast_constant_folder_rust as ast_constant_folder;
8 use emit_adata_rust as emit_adata;
9 use emit_fatal_rust as emit_fatal;
10 use emit_pos_rust::{emit_pos, emit_pos_then};
11 use emit_symbol_refs_rust as emit_symbol_refs;
12 use emit_type_constant_rust as emit_type_constant;
13 use env::{emitter::Emitter, local, Env, Flags as EnvFlags};
14 use hhas_symbol_refs_rust::IncludePath;
15 use hhbc_ast_rust::*;
16 use hhbc_id_rust::{class, r#const, function, method, prop, Id};
17 use hhbc_string_utils_rust as string_utils;
18 use instruction_sequence_rust::{
19     instr, unrecoverable,
20     Error::{self, Unrecoverable},
21     InstrSeq, Result,
23 use itertools::{Either, Itertools};
24 use label_rust::Label;
25 use lazy_static::lazy_static;
26 use naming_special_names_rust::{
27     emitter_special_functions, fb, pseudo_consts, pseudo_functions, special_functions,
28     special_idents, superglobals, typehints, user_attributes,
30 use options::{CompilerFlags, HhvmFlags, LangFlags, Options};
31 use oxidized::{
32     aast, aast_defs,
33     aast_visitor::{visit, visit_mut, AstParams, Node, NodeMut, Visitor, VisitorMut},
34     ast as tast, ast_defs, local_id,
35     pos::Pos,
37 use regex::Regex;
38 use runtime::TypedValue;
39 use scope_rust::scope;
41 use indexmap::IndexSet;
42 use std::{
43     collections::{BTreeMap, HashSet},
44     convert::TryInto,
45     iter,
46     result::Result as StdResult,
47     str::FromStr,
50 #[derive(Debug)]
51 pub struct EmitJmpResult {
52     // generated instruction sequence
53     pub instrs: InstrSeq,
54     // does instruction sequence fall through
55     is_fallthrough: bool,
56     // was label associated with emit operation used
57     is_label_used: bool,
60 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
61 pub enum LValOp {
62     Set,
63     SetOp(EqOp),
64     IncDec(IncdecOp),
65     Unset,
68 impl LValOp {
69     fn is_incdec(&self) -> bool {
70         if let Self::IncDec(_) = self {
71             return true;
72         };
73         false
74     }
77 pub fn is_local_this(env: &Env, lid: &local_id::LocalId) -> bool {
78     local_id::get_name(lid) == special_idents::THIS
79         && env.scope.has_this()
80         && !env.scope.is_toplevel()
83 mod inout_locals {
84     use crate::*;
85     use oxidized::{aast_defs::Lid, aast_visitor, aast_visitor::Node, ast as tast, ast_defs};
86     use std::{collections::HashMap, marker::PhantomData};
88     pub(super) struct AliasInfo {
89         first_inout: isize,
90         last_write: isize,
91         num_uses: usize,
92     }
94     impl Default for AliasInfo {
95         fn default() -> Self {
96             AliasInfo {
97                 first_inout: std::isize::MAX,
98                 last_write: std::isize::MIN,
99                 num_uses: 0,
100             }
101         }
102     }
104     impl AliasInfo {
105         pub(super) fn add_inout(&mut self, i: isize) {
106             if i < self.first_inout {
107                 self.first_inout = i;
108             }
109         }
111         pub(super) fn add_write(&mut self, i: isize) {
112             if i > self.last_write {
113                 self.last_write = i;
114             }
115         }
117         pub(super) fn add_use(&mut self) {
118             self.num_uses += 1
119         }
121         pub(super) fn in_range(&self, i: isize) -> bool {
122             i > self.first_inout || i <= self.last_write
123         }
125         pub(super) fn has_single_ref(&self) -> bool {
126             self.num_uses < 2
127         }
128     }
130     pub(super) type AliasInfoMap = HashMap<String, AliasInfo>;
132     fn add_write(name: String, i: usize, map: &mut AliasInfoMap) {
133         map.entry(name).or_default().add_write(i as isize);
134     }
136     fn add_inout(name: String, i: usize, map: &mut AliasInfoMap) {
137         map.entry(name).or_default().add_inout(i as isize);
138     }
140     fn add_use(name: String, map: &mut AliasInfoMap) {
141         map.entry(name).or_default().add_use();
142     }
144     // determines if value of a local 'name' that appear in parameter 'i'
145     // should be saved to local because it might be overwritten later
146     pub(super) fn should_save_local_value(name: &str, i: usize, aliases: &AliasInfoMap) -> bool {
147         aliases
148             .get(name)
149             .map_or(false, |alias| alias.in_range(i as isize))
150     }
152     pub(super) fn should_move_local_value(local: &local::Type, aliases: &AliasInfoMap) -> bool {
153         match local {
154             local::Type::Named(name) => aliases
155                 .get(&**name)
156                 .map_or(true, |alias| alias.has_single_ref()),
157             local::Type::Unnamed(_) => false,
158         }
159     }
161     pub(super) fn collect_written_variables(env: &Env, args: &[tast::Expr]) -> AliasInfoMap {
162         let mut acc = HashMap::new();
163         args.iter()
164             .enumerate()
165             .for_each(|(i, arg)| handle_arg(env, true, i, arg, &mut acc));
166         acc
167     }
169     fn handle_arg(env: &Env, is_top: bool, i: usize, arg: &tast::Expr, acc: &mut AliasInfoMap) {
170         use tast::{Expr, Expr_};
171         let Expr(_, e) = arg;
172         // inout $v
173         if let Some((ast_defs::ParamKind::Pinout, Expr(_, Expr_::Lvar(lid)))) = e.as_callconv() {
174             let Lid(_, lid) = &**lid;
175             if !is_local_this(env, &lid) {
176                 add_use(lid.1.to_string(), acc);
177                 return if is_top {
178                     add_inout(lid.1.to_string(), i, acc);
179                 } else {
180                     add_write(lid.1.to_string(), i, acc);
181                 };
182             }
183         }
184         // $v
185         if let Some(Lid(_, (_, id))) = e.as_lvar() {
186             return add_use(id.to_string(), acc);
187         }
188         // dive into argument value
189         aast_visitor::visit(
190             &mut Visitor {
191                 phantom: PhantomData,
192             },
193             &mut Ctx { state: acc, env, i },
194             arg,
195         )
196         .unwrap();
197     }
199     struct Visitor<'a> {
200         phantom: PhantomData<&'a str>,
201     }
203     pub struct Ctx<'a> {
204         // TODO(shiqicao): Change AliasInfoMap to AliasInfoMap<'ast>
205         state: &'a mut AliasInfoMap,
206         env: &'a Env<'a>,
207         i: usize,
208     }
210     impl<'ast, 'a> aast_visitor::Visitor<'ast> for Visitor<'a> {
211         type P = aast_visitor::AstParams<Ctx<'a>, ()>;
213         fn object(&mut self) -> &mut dyn aast_visitor::Visitor<'ast, P = Self::P> {
214             self
215         }
217         fn visit_expr_(
218             &mut self,
219             c: &mut Ctx<'a>,
220             p: &'ast tast::Expr_,
221         ) -> std::result::Result<(), ()> {
222             // f(inout $v) or f(&$v)
223             if let tast::Expr_::Call(expr) = p {
224                 let (_, _, args, uarg) = &**expr;
225                 args.iter()
226                     .for_each(|arg| handle_arg(&c.env, false, c.i, arg, &mut c.state));
227                 uarg.as_ref()
228                     .map(|arg| handle_arg(&c.env, false, c.i, arg, &mut c.state));
229                 Ok(())
230             } else {
231                 p.recurse(c, self.object())?;
232                 Ok(match p {
233                     // lhs op= _
234                     tast::Expr_::Binop(expr) => {
235                         let (bop, left, _) = &**expr;
236                         if let ast_defs::Bop::Eq(_) = bop {
237                             collect_lvars_hs(c, left)
238                         }
239                     }
240                     // $i++ or $i--
241                     tast::Expr_::Unop(expr) => {
242                         let (uop, e) = &**expr;
243                         match uop {
244                             ast_defs::Uop::Uincr | ast_defs::Uop::Udecr => collect_lvars_hs(c, e),
245                             _ => {}
246                         }
247                     }
248                     // $v
249                     tast::Expr_::Lvar(expr) => {
250                         let Lid(_, (_, id)) = &**expr;
251                         add_use(id.to_string(), &mut c.state);
252                     }
253                     _ => {}
254                 })
255             }
256         }
257     }
259     // collect lvars on the left hand side of '=' operator
260     fn collect_lvars_hs(ctx: &mut Ctx, expr: &tast::Expr) {
261         let tast::Expr(_, e) = expr;
262         match &*e {
263             tast::Expr_::Lvar(lid) => {
264                 let Lid(_, lid) = &**lid;
265                 if !is_local_this(&ctx.env, &lid) {
266                     add_use(lid.1.to_string(), &mut ctx.state);
267                     add_write(lid.1.to_string(), ctx.i, &mut ctx.state);
268                 }
269             }
270             tast::Expr_::List(exprs) => exprs.iter().for_each(|expr| collect_lvars_hs(ctx, expr)),
271             _ => {}
272         }
273     }
276 pub fn wrap_array_mark_legacy(e: &Emitter, ins: InstrSeq) -> InstrSeq {
277     if mark_as_legacy(e.options()) {
278         InstrSeq::gather(vec![
279             ins,
280             instr::false_(),
281             instr::instr(Instruct::IMisc(InstructMisc::ArrayMarkLegacy)),
282         ])
283     } else {
284         ins
285     }
288 pub fn wrap_array_unmark_legacy(e: &Emitter, ins: InstrSeq) -> InstrSeq {
289     if mark_as_legacy(e.options()) {
290         InstrSeq::gather(vec![
291             ins,
292             instr::false_(),
293             instr::instr(Instruct::IMisc(InstructMisc::ArrayUnmarkLegacy)),
294         ])
295     } else {
296         ins
297     }
300 pub fn get_type_structure_for_hint(
301     e: &mut Emitter,
302     tparams: &[&str],
303     targ_map: &IndexSet<&str>,
304     hint: &aast::Hint,
305 ) -> Result<InstrSeq> {
306     let targ_map: BTreeMap<&str, i64> = targ_map
307         .iter()
308         .enumerate()
309         .map(|(i, n)| (*n, i as i64))
310         .collect();
311     let tv = emit_type_constant::hint_to_type_constant(
312         e.options(),
313         tparams,
314         &targ_map,
315         &hint,
316         false,
317         false,
318     )?;
319     let i = emit_adata::get_array_identifier(e, &tv);
320     Ok(if hack_arr_dv_arrs(e.options()) {
321         instr::lit_const(InstructLitConst::Dict(i))
322     } else {
323         instr::lit_const(InstructLitConst::Array(i))
324     })
327 pub struct Setrange {
328     pub op: SetrangeOp,
329     pub size: usize,
330     pub vec: bool,
333 /// kind of value stored in local
334 #[derive(Debug, Clone, Copy)]
335 pub enum StoredValueKind {
336     Local,
337     Expr,
340 /// represents sequence of instructions interleaved with temp locals.
341 ///    <(i, None) :: rest> - is emitted i :: <rest> (commonly used for final instructions in sequence)
342 ///    <(i, Some(l, local_kind)) :: rest> is emitted as
344 ///    i
345 ///    .try {
346 ///      setl/popl l; depending on local_kind
347 ///      <rest>
348 ///    } .catch {
349 ///      unset l
350 ///      throw
351 ///    }
352 ///    unsetl l
353 type InstrSeqWithLocals = Vec<(InstrSeq, Option<(local::Type, StoredValueKind)>)>;
355 /// result of emit_array_get
356 enum ArrayGetInstr {
357     /// regular $a[..] that does not need to spill anything
358     Regular(InstrSeq),
359     /// subscript expression used as inout argument that need to spill intermediate values:
360     Inout {
361         /// instruction sequence with locals to load value
362         load: InstrSeqWithLocals,
363         /// instruction to set value back (can use locals defined in load part)
364         store: InstrSeq,
365     },
368 struct ArrayGetBaseData<T> {
369     base_instrs: T,
370     cls_instrs: InstrSeq,
371     setup_instrs: InstrSeq,
372     base_stack_size: StackIndex,
373     cls_stack_size: StackIndex,
376 /// result of emit_base
377 enum ArrayGetBase {
378     /// regular <base> part in <base>[..] that does not need to spill anything
379     Regular(ArrayGetBaseData<InstrSeq>),
380     /// base of subscript expression used as inout argument that need to spill
381     /// intermediate values
382     Inout {
383         /// instructions to load base part
384         load: ArrayGetBaseData<InstrSeqWithLocals>,
385         /// instruction to load base part for setting inout argument back
386         store: InstrSeq,
387     },
390 pub fn emit_expr(emitter: &mut Emitter, env: &Env, expression: &tast::Expr) -> Result {
391     use aast_defs::Lid;
392     use tast::Expr_;
393     let tast::Expr(pos, expr) = expression;
394     match expr {
395         Expr_::Float(_)
396         | Expr_::EnumAtom(_)
397         | Expr_::String(_)
398         | Expr_::Int(_)
399         | Expr_::Null
400         | Expr_::False
401         | Expr_::True => {
402             let v = ast_constant_folder::expr_to_typed_value(emitter, &env.namespace, expression)
403                 .map_err(|_| unrecoverable("expr_to_typed_value failed"))?;
404             Ok(emit_pos_then(pos, instr::typedvalue(v)))
405         }
406         Expr_::PrefixedString(e) => emit_expr(emitter, env, &e.1),
407         Expr_::Lvar(e) => {
408             let Lid(pos, _) = &**e;
409             Ok(InstrSeq::gather(vec![
410                 emit_pos(pos),
411                 emit_local(emitter, env, BareThisOp::Notice, e)?,
412             ]))
413         }
414         Expr_::ClassConst(e) => emit_class_const(emitter, env, pos, &e.0, &e.1),
415         Expr_::Unop(e) => emit_unop(emitter, env, pos, e),
416         Expr_::Binop(_) => emit_binop(emitter, env, pos, expression),
417         Expr_::Pipe(e) => emit_pipe(emitter, env, e),
418         Expr_::Is(is_expr) => {
419             let (e, h) = &**is_expr;
420             let is = emit_is(emitter, env, pos, h)?;
421             Ok(InstrSeq::gather(vec![emit_expr(emitter, env, e)?, is]))
422         }
423         Expr_::As(e) => emit_as(emitter, env, pos, e),
424         Expr_::Cast(e) => emit_cast(emitter, env, pos, &(e.0).1, &e.1),
425         Expr_::Eif(e) => emit_conditional_expr(emitter, env, pos, &e.0, &e.1, &e.2),
426         Expr_::ArrayGet(e) => {
427             let (base_expr, opt_elem_expr) = &**e;
428             Ok(emit_array_get(
429                 emitter,
430                 env,
431                 pos,
432                 None,
433                 QueryOp::CGet,
434                 base_expr,
435                 opt_elem_expr.as_ref(),
436                 false,
437                 false,
438             )?
439             .0)
440         }
441         Expr_::ObjGet(e) => {
442             if e.3 {
443                 // Case ($x->foo).
444                 let e = tast::Expr(
445                     pos.clone(),
446                     Expr_::ObjGet(Box::new((e.0.clone(), e.1.clone(), e.2.clone(), false))),
447                 );
448                 emit_expr(emitter, env, &e)
449             } else {
450                 // Case $x->foo.
451                 Ok(emit_obj_get(emitter, env, pos, QueryOp::CGet, &e.0, &e.1, &e.2, e.3)?.0)
452             }
453         }
454         Expr_::Call(c) => emit_call_expr(emitter, env, pos, None, c),
455         Expr_::New(e) => emit_new(emitter, env, pos, e, false),
456         Expr_::FunctionPointer(fp) => emit_function_pointer(emitter, env, pos, &fp.0, &fp.1),
457         Expr_::Record(e) => emit_record(emitter, env, pos, e),
458         Expr_::Darray(e) => Ok(emit_pos_then(
459             pos,
460             emit_collection(emitter, env, expression, &mk_afkvalues(&e.1), None)?,
461         )),
462         Expr_::Varray(e) => Ok(emit_pos_then(
463             pos,
464             emit_collection(emitter, env, expression, &mk_afvalues(&e.1), None)?,
465         )),
466         Expr_::Collection(e) => emit_named_collection_str(emitter, env, expression, e),
467         Expr_::ValCollection(e) => {
468             let (kind, _, es) = &**e;
469             let fields = mk_afvalues(es);
470             let collection_typ = match kind {
471                 aast_defs::VcKind::Vector => CollectionType::Vector,
472                 aast_defs::VcKind::ImmVector => CollectionType::ImmVector,
473                 aast_defs::VcKind::Set => CollectionType::Set,
474                 aast_defs::VcKind::ImmSet => CollectionType::ImmSet,
475                 _ => return emit_collection(emitter, env, expression, &fields, None),
476             };
477             emit_named_collection(emitter, env, pos, expression, &fields, collection_typ)
478         }
479         Expr_::Pair(e) => {
480             let (_, e1, e2) = (**e).to_owned();
481             let fields = mk_afvalues(&vec![e1, e2]);
482             emit_named_collection(emitter, env, pos, expression, &fields, CollectionType::Pair)
483         }
484         Expr_::KeyValCollection(e) => {
485             let (kind, _, fields) = &**e;
486             let fields = mk_afkvalues(
487                 &fields
488                     .to_owned()
489                     .into_iter()
490                     .map(|tast::Field(e1, e2)| (e1, e2))
491                     .collect(),
492             );
493             let collection_typ = match kind {
494                 aast_defs::KvcKind::Map => CollectionType::Map,
495                 aast_defs::KvcKind::ImmMap => CollectionType::ImmMap,
496                 _ => return emit_collection(emitter, env, expression, &fields, None),
497             };
498             emit_named_collection(emitter, env, pos, expression, &fields, collection_typ)
499         }
500         Expr_::Clone(e) => Ok(emit_pos_then(pos, emit_clone(emitter, env, e)?)),
501         Expr_::Shape(e) => Ok(emit_pos_then(pos, emit_shape(emitter, env, expression, e)?)),
502         Expr_::Await(e) => emit_await(emitter, env, pos, e),
503         // TODO: emit readonly expressions
504         Expr_::ReadonlyExpr(e) => emit_expr(emitter, env, e),
505         Expr_::Yield(e) => emit_yield(emitter, env, pos, e),
506         Expr_::Efun(e) => Ok(emit_pos_then(pos, emit_lambda(emitter, env, &e.0, &e.1)?)),
508         Expr_::ClassGet(e) => {
509             if !e.2 {
510                 emit_class_get(emitter, env, QueryOp::CGet, &e.0, &e.1)
511             } else {
512                 let e = tast::Expr(
513                     pos.clone(),
514                     Expr_::ClassGet(Box::new((e.0.clone(), e.1.clone(), false))),
515                 );
516                 emit_expr(emitter, env, &e)
517             }
518         }
520         Expr_::String2(es) => emit_string2(emitter, env, pos, es),
521         Expr_::Id(e) => Ok(emit_pos_then(pos, emit_id(emitter, env, e)?)),
522         Expr_::Xml(_) => Err(unrecoverable(
523             "emit_xhp: syntax should have been converted during rewriting",
524         )),
525         Expr_::Callconv(_) => Err(unrecoverable(
526             "emit_callconv: This should have been caught at emit_arg",
527         )),
528         Expr_::Import(e) => emit_import(emitter, env, pos, &e.0, &e.1),
529         Expr_::Omitted => Ok(instr::empty()),
530         Expr_::Lfun(_) => Err(unrecoverable(
531             "expected Lfun to be converted to Efun during closure conversion emit_expr",
532         )),
533         Expr_::List(_) => Err(emit_fatal::raise_fatal_parse(
534             pos,
535             "list() can only be used as an lvar. Did you mean to use tuple()?",
536         )),
537         Expr_::Any => Err(unrecoverable("Cannot codegen from an Any node")),
538         Expr_::This | Expr_::Lplaceholder(_) | Expr_::Dollardollar(_) => {
539             unimplemented!("TODO(hrust) Codegen after naming pass on AAST")
540         }
541         Expr_::ExpressionTree(et) => emit_expr(emitter, env, &et.desugared_expr),
542         _ => unimplemented!("TODO(hrust)"),
543     }
546 fn emit_exprs(e: &mut Emitter, env: &Env, exprs: &[tast::Expr]) -> Result {
547     if exprs.is_empty() {
548         Ok(instr::empty())
549     } else {
550         Ok(InstrSeq::gather(
551             exprs
552                 .iter()
553                 .map(|expr| emit_expr(e, env, expr))
554                 .collect::<Result<Vec<_>>>()?,
555         ))
556     }
559 fn emit_id(emitter: &mut Emitter, env: &Env, id: &tast::Sid) -> Result {
560     use pseudo_consts::*;
561     use InstructLitConst::*;
563     let ast_defs::Id(p, s) = id;
564     let res = match s.as_str() {
565         G__FILE__ => instr::lit_const(File),
566         G__DIR__ => instr::lit_const(Dir),
567         G__METHOD__ => instr::lit_const(Method),
568         G__FUNCTION_CREDENTIAL__ => instr::lit_const(FuncCred),
569         G__CLASS__ => InstrSeq::gather(vec![instr::self_(), instr::classname()]),
570         G__COMPILER_FRONTEND__ => instr::string("hackc"),
571         G__LINE__ => instr::int(p.info_pos_extended().1.try_into().map_err(|_| {
572             emit_fatal::raise_fatal_parse(p, "error converting end of line from usize to isize")
573         })?),
574         G__NAMESPACE__ => instr::string(env.namespace.name.as_ref().map_or("", |s| &s[..])),
575         EXIT | DIE => return emit_exit(emitter, env, None),
576         _ => {
577             // panic!("TODO: uncomment after D19350786 lands")
578             // let cid: ConstId = r#const::Type::from_ast_name(&s);
579             let cid: ConstId = string_utils::strip_global_ns(&s).to_string().into();
580             emit_symbol_refs::State::add_constant(emitter, cid.clone());
581             return Ok(emit_pos_then(p, instr::lit_const(CnsE(cid))));
582         }
583     };
584     Ok(res)
587 fn emit_exit(emitter: &mut Emitter, env: &Env, expr_opt: Option<&tast::Expr>) -> Result {
588     Ok(InstrSeq::gather(vec![
589         expr_opt.map_or_else(|| Ok(instr::int(0)), |e| emit_expr(emitter, env, e))?,
590         instr::exit(),
591     ]))
594 fn emit_yield(e: &mut Emitter, env: &Env, pos: &Pos, af: &tast::Afield) -> Result {
595     Ok(match af {
596         tast::Afield::AFvalue(v) => {
597             InstrSeq::gather(vec![emit_expr(e, env, v)?, emit_pos(pos), instr::yield_()])
598         }
599         tast::Afield::AFkvalue(k, v) => InstrSeq::gather(vec![
600             emit_expr(e, env, k)?,
601             emit_expr(e, env, v)?,
602             emit_pos(pos),
603             instr::yieldk(),
604         ]),
605     })
608 fn parse_include(e: &tast::Expr) -> IncludePath {
609     fn strip_backslash(s: &mut String) {
610         if s.starts_with("/") {
611             *s = s[1..].into()
612         }
613     }
614     fn split_var_lit(e: &tast::Expr) -> (String, String) {
615         match &e.1 {
616             tast::Expr_::Binop(x) if x.0.is_dot() => {
617                 let (v, l) = split_var_lit(&x.2);
618                 if v.is_empty() {
619                     let (var, lit) = split_var_lit(&x.1);
620                     (var, format!("{}{}", lit, l))
621                 } else {
622                     (v, String::new())
623                 }
624             }
625             tast::Expr_::String(lit) => (String::new(), lit.to_string()),
626             _ => (text_of_expr(e), String::new()),
627         }
628     }
629     let (mut var, mut lit) = split_var_lit(e);
630     if var == pseudo_consts::G__DIR__ {
631         var = String::new();
632         strip_backslash(&mut lit);
633     }
634     if var.is_empty() {
635         if std::path::Path::new(lit.as_str()).is_relative() {
636             IncludePath::SearchPathRelative(lit)
637         } else {
638             IncludePath::Absolute(lit)
639         }
640     } else {
641         strip_backslash(&mut lit);
642         IncludePath::IncludeRootRelative(var, lit)
643     }
646 fn text_of_expr(e: &tast::Expr) -> String {
647     match &e.1 {
648         tast::Expr_::String(s) => format!("\'{}\'", s),
649         tast::Expr_::Id(id) => id.1.to_string(),
650         tast::Expr_::Lvar(lid) => local_id::get_name(&lid.1).to_string(),
651         tast::Expr_::ArrayGet(x) => match ((x.0).1.as_lvar(), x.1.as_ref()) {
652             (Some(tast::Lid(_, id)), Some(e_)) => {
653                 format!("{}[{}]", local_id::get_name(&id), text_of_expr(e_))
654             }
655             _ => "unknown".into(),
656         },
657         _ => "unknown".into(),
658     }
661 fn text_of_class_id(cid: &tast::ClassId) -> String {
662     match &cid.1 {
663         tast::ClassId_::CIparent => "parent".into(),
664         tast::ClassId_::CIself => "self".into(),
665         tast::ClassId_::CIstatic => "static".into(),
666         tast::ClassId_::CIexpr(e) => text_of_expr(e),
667         tast::ClassId_::CI(ast_defs::Id(_, id)) => id.into(),
668     }
671 fn text_of_prop(prop: &tast::ClassGetExpr) -> String {
672     match prop {
673         tast::ClassGetExpr::CGstring((_, s)) => s.into(),
674         tast::ClassGetExpr::CGexpr(e) => text_of_expr(e),
675     }
678 fn emit_import(
679     e: &mut Emitter,
680     env: &Env,
681     pos: &Pos,
682     flavor: &tast::ImportFlavor,
683     expr: &tast::Expr,
684 ) -> Result {
685     use tast::ImportFlavor;
686     let inc = parse_include(expr);
687     emit_symbol_refs::State::add_include(e, inc.clone());
688     let (expr_instrs, import_op_instr) = match flavor {
689         ImportFlavor::Include => (emit_expr(e, env, expr)?, instr::incl()),
690         ImportFlavor::Require => (emit_expr(e, env, expr)?, instr::req()),
691         ImportFlavor::IncludeOnce => (emit_expr(e, env, expr)?, instr::inclonce()),
692         ImportFlavor::RequireOnce => {
693             match inc.into_doc_root_relative(e.options().hhvm.include_roots.get()) {
694                 IncludePath::DocRootRelative(path) => {
695                     let expr = tast::Expr(pos.clone(), tast::Expr_::String(path.into()));
696                     (emit_expr(e, env, &expr)?, instr::reqdoc())
697                 }
698                 _ => (emit_expr(e, env, expr)?, instr::reqonce()),
699             }
700         }
701     };
702     Ok(InstrSeq::gather(vec![
703         expr_instrs,
704         emit_pos(pos),
705         import_op_instr,
706     ]))
709 fn emit_string2(e: &mut Emitter, env: &Env, pos: &Pos, es: &Vec<tast::Expr>) -> Result {
710     if es.is_empty() {
711         Err(unrecoverable("String2 with zero araguments is impossible"))
712     } else if es.len() == 1 {
713         Ok(InstrSeq::gather(vec![
714             emit_expr(e, env, &es[0])?,
715             emit_pos(pos),
716             instr::cast_string(),
717         ]))
718     } else {
719         Ok(InstrSeq::gather(vec![
720             emit_two_exprs(e, env, &es[0].0, &es[0], &es[1])?,
721             emit_pos(pos),
722             instr::concat(),
723             InstrSeq::gather(
724                 (&es[2..])
725                     .iter()
726                     .map(|expr| {
727                         Ok(InstrSeq::gather(vec![
728                             emit_expr(e, env, expr)?,
729                             emit_pos(pos),
730                             instr::concat(),
731                         ]))
732                     })
733                     .collect::<Result<_>>()?,
734             ),
735         ]))
736     }
739 fn emit_clone(e: &mut Emitter, env: &Env, expr: &tast::Expr) -> Result {
740     Ok(InstrSeq::gather(vec![
741         emit_expr(e, env, expr)?,
742         instr::clone(),
743     ]))
746 fn emit_lambda(e: &mut Emitter, env: &Env, fndef: &tast::Fun_, ids: &[aast_defs::Lid]) -> Result {
747     use global_state::LazyState;
748     // Closure conversion puts the class number used for CreateCl in the "name"
749     // of the function definition
750     let fndef_name = &(fndef.name).1;
751     let cls_num = fndef_name
752         .parse::<isize>()
753         .map_err(|err| Unrecoverable(err.to_string()))?;
754     let explicit_use = e.emit_state().explicit_use_set.contains(fndef_name);
755     let is_in_lambda = env.scope.is_in_lambda();
756     Ok(InstrSeq::gather(vec![
757         InstrSeq::gather(
758             ids.iter()
759                 .map(|tast::Lid(pos, id)| {
760                     match string_utils::reified::is_captured_generic(local_id::get_name(id)) {
761                         Some((is_fun, i)) => {
762                             if is_in_lambda {
763                                 Ok(instr::cgetl(local::Type::Named(
764                                     string_utils::reified::reified_generic_captured_name(
765                                         is_fun, i as usize,
766                                     ),
767                                 )))
768                             } else {
769                                 emit_reified_generic_instrs(&Pos::make_none(), is_fun, i as usize)
770                             }
771                         }
772                         None => Ok({
773                             let lid = get_local(e, env, pos, local_id::get_name(id))?;
774                             if explicit_use {
775                                 instr::cgetl(lid)
776                             } else {
777                                 instr::cugetl(lid)
778                             }
779                         }),
780                     }
781                 })
782                 .collect::<Result<Vec<_>>>()?,
783         ),
784         instr::createcl(ids.len(), cls_num),
785     ]))
788 pub fn emit_await(emitter: &mut Emitter, env: &Env, pos: &Pos, expr: &tast::Expr) -> Result {
789     let tast::Expr(_, e) = expr;
790     let cant_inline_gen_functions = !emitter
791         .options()
792         .hhvm
793         .flags
794         .contains(HhvmFlags::JIT_ENABLE_RENAME_FUNCTION);
795     match e.as_call() {
796         Some((tast::Expr(_, tast::Expr_::Id(id)), _, args, None))
797             if (cant_inline_gen_functions
798                 && args.len() == 1
799                 && string_utils::strip_global_ns(&(*id.1)) == "gena") =>
800         {
801             return inline_gena_call(emitter, env, &args[0]);
802         }
803         _ => {
804             let after_await = emitter.label_gen_mut().next_regular();
805             let instrs = match e {
806                 tast::Expr_::Call(c) => {
807                     emit_call_expr(emitter, env, pos, Some(after_await.clone()), &*c)?
808                 }
809                 _ => emit_expr(emitter, env, expr)?,
810             };
811             Ok(InstrSeq::gather(vec![
812                 instrs,
813                 emit_pos(pos),
814                 instr::dup(),
815                 instr::istypec(IstypeOp::OpNull),
816                 instr::jmpnz(after_await.clone()),
817                 instr::await_(),
818                 instr::label(after_await),
819             ]))
820         }
821     }
824 fn hack_arr_dv_arrs(opts: &Options) -> bool {
825     opts.hhvm.flags.contains(HhvmFlags::HACK_ARR_DV_ARRS)
828 fn mark_as_legacy(opts: &Options) -> bool {
829     opts.hhvm.flags.contains(HhvmFlags::HACK_ARR_DV_ARRS)
830         && opts.hhvm.flags.contains(HhvmFlags::HACK_ARR_DV_ARR_MARK)
833 fn inline_gena_call(emitter: &mut Emitter, env: &Env, arg: &tast::Expr) -> Result {
834     let load_arr = emit_expr(emitter, env, arg)?;
835     let async_eager_label = emitter.label_gen_mut().next_regular();
836     let hack_arr_dv_arrs = hack_arr_dv_arrs(emitter.options());
838     scope::with_unnamed_local(emitter, |e, arr_local| {
839         let before = InstrSeq::gather(vec![
840             load_arr,
841             if hack_arr_dv_arrs {
842                 instr::cast_dict()
843             } else {
844                 instr::cast_darray()
845             },
846             instr::popl(arr_local.clone()),
847         ]);
849         let inner = InstrSeq::gather(vec![
850             instr::nulluninit(),
851             instr::nulluninit(),
852             instr::cgetl(arr_local.clone()),
853             instr::fcallclsmethodd(
854                 FcallArgs::new(
855                     FcallFlags::default(),
856                     1,
857                     vec![],
858                     Some(async_eager_label.clone()),
859                     1,
860                     None,
861                 ),
862                 method::from_raw_string(if hack_arr_dv_arrs {
863                     "fromDict"
864                 } else {
865                     "fromDArray"
866                 }),
867                 class::from_raw_string("HH\\AwaitAllWaitHandle"),
868             ),
869             instr::await_(),
870             instr::label(async_eager_label.clone()),
871             instr::popc(),
872             emit_iter(
873                 e,
874                 instr::cgetl(arr_local.clone()),
875                 |val_local, key_local| {
876                     InstrSeq::gather(vec![
877                         instr::cgetl(val_local),
878                         instr::whresult(),
879                         instr::basel(arr_local.clone(), MemberOpMode::Define),
880                         instr::setm(0, MemberKey::EL(key_local, ReadOnlyOp::Any)),
881                         instr::popc(),
882                     ])
883                 },
884             )?,
885         ]);
887         let after = instr::pushl(arr_local);
889         Ok((before, inner, after))
890     })
893 fn emit_iter<F: FnOnce(local::Type, local::Type) -> InstrSeq>(
894     e: &mut Emitter,
895     collection: InstrSeq,
896     f: F,
897 ) -> Result {
898     scope::with_unnamed_locals_and_iterators(e, |e| {
899         let iter_id = e.iterator_mut().get();
900         let val_id = e.local_gen_mut().get_unnamed();
901         let key_id = e.local_gen_mut().get_unnamed();
902         let loop_end = e.label_gen_mut().next_regular();
903         let loop_next = e.label_gen_mut().next_regular();
904         let iter_args = IterArgs {
905             iter_id,
906             key_id: Some(key_id.clone()),
907             val_id: val_id.clone(),
908         };
909         let iter_init = InstrSeq::gather(vec![
910             collection,
911             instr::iterinit(iter_args.clone(), loop_end.clone()),
912         ]);
913         let iterate = InstrSeq::gather(vec![
914             instr::label(loop_next.clone()),
915             f(val_id.clone(), key_id.clone()),
916             instr::iternext(iter_args, loop_next),
917         ]);
918         let iter_done = InstrSeq::gather(vec![
919             instr::unsetl(val_id),
920             instr::unsetl(key_id),
921             instr::label(loop_end),
922         ]);
923         Ok((iter_init, iterate, iter_done))
924     })
927 fn emit_shape(
928     emitter: &mut Emitter,
929     env: &Env,
930     expr: &tast::Expr,
931     fl: &[(ast_defs::ShapeFieldName, tast::Expr)],
932 ) -> Result {
933     fn extract_shape_field_name_pstring(
934         env: &Env,
935         pos: &Pos,
936         field: &ast_defs::ShapeFieldName,
937     ) -> Result<tast::Expr_> {
938         use ast_defs::ShapeFieldName as SF;
939         Ok(match field {
940             SF::SFlitInt(s) => tast::Expr_::mk_int(s.1.clone()),
941             SF::SFlitStr(s) => tast::Expr_::mk_string(s.1.clone()),
942             SF::SFclassConst(id, p) => {
943                 if is_reified_tparam(env, true, &id.1).is_some()
944                     || is_reified_tparam(env, false, &id.1).is_some()
945                 {
946                     return Err(emit_fatal::raise_fatal_parse(
947                         &id.0,
948                         "Reified generics cannot be used in shape keys",
949                     ));
950                 } else {
951                     tast::Expr_::mk_class_const(
952                         tast::ClassId(pos.clone(), tast::ClassId_::CI(id.clone())),
953                         p.clone(),
954                     )
955                 }
956             }
957         })
958     }
959     let pos = &expr.0;
960     // TODO(hrust): avoid clone
961     let fl = fl
962         .iter()
963         .map(|(f, e)| {
964             Ok((
965                 tast::Expr(pos.clone(), extract_shape_field_name_pstring(env, pos, f)?),
966                 e.clone(),
967             ))
968         })
969         .collect::<Result<Vec<_>>>()?;
970     emit_expr(
971         emitter,
972         env,
973         &tast::Expr(pos.clone(), tast::Expr_::mk_darray(None, fl)),
974     )
977 fn emit_vec_collection(
978     e: &mut Emitter,
979     env: &Env,
980     pos: &Pos,
981     fields: &Vec<tast::Afield>,
982 ) -> Result {
983     match ast_constant_folder::vec_to_typed_value(e, &env.namespace, pos, fields) {
984         Ok(tv) => emit_static_collection(e, env, None, pos, tv),
985         Err(_) => emit_value_only_collection(e, env, pos, fields, InstructLitConst::NewVec),
986     }
989 fn emit_named_collection(
990     e: &mut Emitter,
991     env: &Env,
992     pos: &Pos,
993     expr: &tast::Expr,
994     fields: &Vec<tast::Afield>,
995     collection_type: CollectionType,
996 ) -> Result {
997     let emit_vector_like = |e: &mut Emitter, collection_type| {
998         Ok(if fields.is_empty() {
999             emit_pos_then(pos, instr::newcol(collection_type))
1000         } else {
1001             InstrSeq::gather(vec![
1002                 emit_vec_collection(e, env, pos, fields)?,
1003                 instr::colfromarray(collection_type),
1004             ])
1005         })
1006     };
1007     let emit_map_or_set = |e: &mut Emitter, collection_type| {
1008         if fields.is_empty() {
1009             Ok(emit_pos_then(pos, instr::newcol(collection_type)))
1010         } else {
1011             emit_collection(e, env, expr, fields, Some(collection_type))
1012         }
1013     };
1014     use CollectionType as C;
1015     match collection_type {
1016         C::Dict | C::Vec | C::Keyset => {
1017             let instr = emit_collection(e, env, expr, fields, None)?;
1018             Ok(emit_pos_then(pos, instr))
1019         }
1020         C::Vector | C::ImmVector => emit_vector_like(e, collection_type),
1021         C::Map | C::ImmMap | C::Set | C::ImmSet => emit_map_or_set(e, collection_type),
1022         C::Pair => Ok(InstrSeq::gather(vec![
1023             InstrSeq::gather(
1024                 fields
1025                     .iter()
1026                     .map(|f| match f {
1027                         tast::Afield::AFvalue(v) => emit_expr(e, env, v),
1028                         _ => Err(unrecoverable("impossible Pair argument")),
1029                     })
1030                     .collect::<Result<_>>()?,
1031             ),
1032             instr::new_pair(),
1033         ])),
1034         _ => Err(unrecoverable("Unexpected named collection type")),
1035     }
1038 fn emit_named_collection_str(
1039     e: &mut Emitter,
1040     env: &Env,
1041     expr: &tast::Expr,
1042     (ast_defs::Id(pos, name), _, fields): &(
1043         tast::Sid,
1044         Option<tast::CollectionTarg>,
1045         Vec<tast::Afield>,
1046     ),
1047 ) -> Result {
1048     let name = string_utils::strip_ns(name);
1049     let name = string_utils::types::fix_casing(name.as_ref());
1050     use CollectionType::*;
1051     let ctype = match name {
1052         "dict" => Dict,
1053         "vec" => Vec,
1054         "keyset" => Keyset,
1055         "Vector" => Vector,
1056         "ImmVector" => ImmVector,
1057         "Map" => Map,
1058         "ImmMap" => ImmMap,
1059         "Set" => Set,
1060         "ImmSet" => ImmSet,
1061         "Pair" => Pair,
1062         _ => {
1063             return Err(unrecoverable(format!(
1064                 "collection: {} does not exist",
1065                 name
1066             )));
1067         }
1068     };
1069     emit_named_collection(e, env, pos, expr, fields, ctype)
1072 fn mk_afkvalues(es: &Vec<(tast::Expr, tast::Expr)>) -> Vec<tast::Afield> {
1073     es.to_owned()
1074         .into_iter()
1075         .map(|(e1, e2)| tast::Afield::mk_afkvalue(e1, e2))
1076         .collect()
1079 fn mk_afvalues(es: &Vec<tast::Expr>) -> Vec<tast::Afield> {
1080     es.to_owned()
1081         .into_iter()
1082         .map(|e| tast::Afield::mk_afvalue(e))
1083         .collect()
1086 fn emit_collection(
1087     e: &mut Emitter,
1088     env: &Env,
1089     expr: &tast::Expr,
1090     fields: &[tast::Afield],
1091     transform_to_collection: Option<CollectionType>,
1092 ) -> Result {
1093     let pos = &expr.0;
1094     match ast_constant_folder::expr_to_typed_value_(
1095         e,
1096         &env.namespace,
1097         expr,
1098         true,  /*allow_map*/
1099         false, /*force_class_const*/
1100     ) {
1101         Ok(tv) => emit_static_collection(e, env, transform_to_collection, pos, tv),
1102         Err(_) => emit_dynamic_collection(e, env, expr, fields),
1103     }
1106 fn emit_static_collection(
1107     e: &mut Emitter,
1108     env: &Env,
1109     transform_to_collection: Option<CollectionType>,
1110     pos: &Pos,
1111     tv: TypedValue,
1112 ) -> Result {
1113     let arrprov_enabled = e.options().hhvm.flags.contains(HhvmFlags::ARRAY_PROVENANCE);
1114     let transform_instr = match transform_to_collection {
1115         Some(collection_type) => instr::colfromarray(collection_type),
1116         _ => instr::empty(),
1117     };
1118     Ok(
1119         if arrprov_enabled && env.scope.has_function_attribute("__ProvenanceSkipFrame") {
1120             InstrSeq::gather(vec![
1121                 emit_pos(pos),
1122                 instr::typedvalue(tv),
1123                 instr::int(0),
1124                 instr::instr(Instruct::IMisc(InstructMisc::TagProvenanceHere)),
1125                 transform_instr,
1126             ])
1127         } else {
1128             InstrSeq::gather(vec![emit_pos(pos), instr::typedvalue(tv), transform_instr])
1129         },
1130     )
1133 fn expr_and_new(
1134     e: &mut Emitter,
1135     env: &Env,
1136     pos: &Pos,
1137     instr_to_add_new: InstrSeq,
1138     instr_to_add: InstrSeq,
1139     field: &tast::Afield,
1140 ) -> Result {
1141     match field {
1142         tast::Afield::AFvalue(v) => Ok(InstrSeq::gather(vec![
1143             emit_expr(e, env, v)?,
1144             emit_pos(pos),
1145             instr_to_add_new,
1146         ])),
1147         tast::Afield::AFkvalue(k, v) => Ok(InstrSeq::gather(vec![
1148             emit_two_exprs(e, env, &k.0, k, v)?,
1149             instr_to_add,
1150         ])),
1151     }
1154 fn emit_keyvalue_collection(
1155     e: &mut Emitter,
1156     env: &Env,
1157     pos: &Pos,
1158     fields: &[tast::Afield],
1159     ctype: CollectionType,
1160     constructor: InstructLitConst,
1161 ) -> Result {
1162     let (transform_instr, add_elem_instr) = match ctype {
1163         CollectionType::Dict | CollectionType::Array => (instr::empty(), instr::add_new_elemc()),
1164         _ => (
1165             instr::colfromarray(ctype),
1166             InstrSeq::gather(vec![instr::dup(), instr::add_elemc()]),
1167         ),
1168     };
1169     let emitted_pos = emit_pos(pos);
1170     Ok(InstrSeq::gather(vec![
1171         emitted_pos.clone(),
1172         instr::lit_const(constructor),
1173         fields
1174             .iter()
1175             .map(|f| expr_and_new(e, env, pos, add_elem_instr.clone(), instr::add_elemc(), f))
1176             .collect::<Result<_>>()
1177             .map(InstrSeq::gather)?,
1178         emitted_pos,
1179         transform_instr,
1180     ]))
1183 fn non_numeric(s: &str) -> bool {
1184     // Note(hrust): OCaml Int64.of_string and float_of_string ignore underscores
1185     let s = s.replace("_", "");
1186     lazy_static! {
1187         static ref HEX: Regex = Regex::new(r"(?P<sign>^-?)0[xX](?P<digits>.*)").unwrap();
1188         static ref OCTAL: Regex = Regex::new(r"(?P<sign>^-?)0[oO](?P<digits>.*)").unwrap();
1189         static ref BINARY: Regex = Regex::new(r"(?P<sign>^-?)0[bB](?P<digits>.*)").unwrap();
1190         static ref FLOAT: Regex =
1191             Regex::new(r"(?P<int>\d*)\.(?P<dec>[0-9--0]*)(?P<zeros>0*)").unwrap();
1192         static ref NEG_FLOAT: Regex =
1193             Regex::new(r"(?P<int>-\d*)\.(?P<dec>[0-9--0]*)(?P<zeros>0*)").unwrap();
1194         static ref HEX_RADIX: u32 = 16;
1195         static ref OCTAL_RADIX: u32 = 8;
1196         static ref BINARY_RADIX: u32 = 2;
1197     }
1198     fn int_from_str(s: &str) -> std::result::Result<i64, ()> {
1199         // Note(hrust): OCaml Int64.of_string reads decimal, hexadecimal, octal, and binary
1200         (if HEX.is_match(s) {
1201             u64::from_str_radix(&HEX.replace(s, "${sign}${digits}"), *HEX_RADIX).map(|x| x as i64)
1202         } else if OCTAL.is_match(s) {
1203             u64::from_str_radix(&OCTAL.replace(s, "${sign}${digits}"), *OCTAL_RADIX)
1204                 .map(|x| x as i64)
1205         } else if BINARY.is_match(s) {
1206             u64::from_str_radix(&BINARY.replace(s, "${sign}${digits}"), *BINARY_RADIX)
1207                 .map(|x| x as i64)
1208         } else {
1209             i64::from_str(&s)
1210         })
1211         .map_err(|_| ())
1212     }
1213     fn float_from_str_radix(s: &str, radix: u32) -> std::result::Result<f64, ()> {
1214         let i = i64::from_str_radix(&s.replace(".", ""), radix).map_err(|_| ())?;
1215         Ok(match s.matches(".").count() {
1216             0 => i as f64,
1217             1 => {
1218                 let pow = s.split('.').last().unwrap().len();
1219                 (i as f64) / f64::from(radix).powi(pow as i32)
1220             }
1221             _ => return Err(()),
1222         })
1223     }
1224     fn out_of_bounds(s: &str) -> bool {
1225         // compare strings instead of floats to avoid rounding imprecision
1226         if FLOAT.is_match(s) {
1227             FLOAT.replace(s, "${int}.${dec}").trim_end_matches(".") > i64::MAX.to_string().as_str()
1228         } else if NEG_FLOAT.is_match(s) {
1229             NEG_FLOAT.replace(s, "${int}.${dec}").trim_end_matches(".")
1230                 > i64::MIN.to_string().as_str()
1231         } else {
1232             false
1233         }
1234     }
1235     fn float_from_str(s: &str) -> std::result::Result<f64, ()> {
1236         // Note(hrust): OCaml float_of_string ignores leading whitespace, reads decimal and hexadecimal
1237         let s = s.trim_start();
1238         if HEX.is_match(s) {
1239             float_from_str_radix(&HEX.replace(s, "${sign}${digits}"), *HEX_RADIX)
1240         } else {
1241             let out_of_bounds =
1242                 |f: f64| out_of_bounds(s) && (f > i64::MAX as f64 || f < i64::MIN as f64);
1243             let validate_float = |f: f64| {
1244                 if out_of_bounds(f) || f.is_infinite() || f.is_nan() {
1245                     Err(())
1246                 } else {
1247                     Ok(f)
1248                 }
1249             };
1250             f64::from_str(s).map_err(|_| ()).and_then(validate_float)
1251         }
1252     }
1253     int_from_str(&s).is_err() && float_from_str(&s).is_err()
1256 fn is_struct_init(
1257     e: &mut Emitter,
1258     env: &Env,
1259     fields: &[tast::Afield],
1260     allow_numerics: bool,
1261 ) -> Result<bool> {
1262     let mut are_all_keys_non_numeric_strings = true;
1263     let mut uniq_keys = std::collections::HashSet::<bstr::BString>::new();
1264     for f in fields.iter() {
1265         if let tast::Afield::AFkvalue(key, _) = f {
1266             // TODO(hrust): if key is String, don't clone and call fold_expr
1267             let mut key = key.clone();
1268             ast_constant_folder::fold_expr(&mut key, e, &env.namespace)
1269                 .map_err(|e| unrecoverable(format!("{}", e)))?;
1270             if let tast::Expr(_, tast::Expr_::String(s)) = key {
1271                 are_all_keys_non_numeric_strings = are_all_keys_non_numeric_strings
1272                     && non_numeric(
1273                         // FIXME: This is not safe--string literals are binary strings.
1274                         // There's no guarantee that they're valid UTF-8.
1275                         unsafe { std::str::from_utf8_unchecked(s.as_slice().into()) },
1276                     );
1277                 uniq_keys.insert(s);
1278             } else {
1279                 are_all_keys_non_numeric_strings = false;
1280             }
1281             continue;
1282         }
1283         are_all_keys_non_numeric_strings = false;
1284     }
1285     let num_keys = fields.len();
1286     let limit = *(e.options().max_array_elem_size_on_the_stack.get()) as usize;
1287     Ok((allow_numerics || are_all_keys_non_numeric_strings)
1288         && uniq_keys.len() == num_keys
1289         && num_keys <= limit
1290         && num_keys != 0)
1293 fn emit_struct_array<C: FnOnce(&mut Emitter, Vec<String>) -> Result<InstrSeq>>(
1294     e: &mut Emitter,
1295     env: &Env,
1296     pos: &Pos,
1297     fields: &[tast::Afield],
1298     ctor: C,
1299 ) -> Result {
1300     use tast::{Expr as E, Expr_ as E_};
1301     let (keys, value_instrs) = fields
1302         .iter()
1303         .map(|f| match f {
1304             tast::Afield::AFkvalue(k, v) => match k {
1305                 E(_, E_::String(s)) => Ok((
1306                     // FIXME: This is not safe--string literals are binary strings.
1307                     // There's no guarantee that they're valid UTF-8.
1308                     unsafe { String::from_utf8_unchecked(s.clone().into()) },
1309                     emit_expr(e, env, v)?,
1310                 )),
1311                 _ => {
1312                     let mut k = k.clone();
1313                     ast_constant_folder::fold_expr(&mut k, e, &env.namespace)
1314                         .map_err(|e| unrecoverable(format!("{}", e)))?;
1315                     match k {
1316                         E(_, E_::String(s)) => Ok((
1317                             // FIXME: This is not safe--string literals are binary strings.
1318                             // There's no guarantee that they're valid UTF-8.
1319                             unsafe { String::from_utf8_unchecked(s.clone().into()) },
1320                             emit_expr(e, env, v)?,
1321                         )),
1322                         _ => Err(unrecoverable("Key must be a string")),
1323                     }
1324                 }
1325             },
1326             _ => Err(unrecoverable("impossible")),
1327         })
1328         .collect::<Result<Vec<(String, InstrSeq)>>>()?
1329         .into_iter()
1330         .unzip();
1331     Ok(InstrSeq::gather(vec![
1332         InstrSeq::gather(value_instrs),
1333         emit_pos(pos),
1334         ctor(e, keys)?,
1335     ]))
1338 fn emit_dynamic_collection(
1339     e: &mut Emitter,
1340     env: &Env,
1341     expr: &tast::Expr,
1342     fields: &[tast::Afield],
1343 ) -> Result {
1344     let pos = &expr.0;
1345     let count = fields.len();
1346     let emit_dict = |e: &mut Emitter| {
1347         if is_struct_init(e, env, fields, true)? {
1348             emit_struct_array(e, env, pos, fields, |_, x| Ok(instr::newstructdict(x)))
1349         } else {
1350             let ctor = InstructLitConst::NewDictArray(count as isize);
1351             emit_keyvalue_collection(e, env, pos, fields, CollectionType::Dict, ctor)
1352         }
1353     };
1354     let emit_collection_helper = |e: &mut Emitter, ctype| {
1355         if is_struct_init(e, env, fields, true)? {
1356             Ok(InstrSeq::gather(vec![
1357                 emit_struct_array(e, env, pos, fields, |_, x| Ok(instr::newstructdict(x)))?,
1358                 emit_pos(pos),
1359                 instr::colfromarray(ctype),
1360             ]))
1361         } else {
1362             let ctor = InstructLitConst::NewDictArray(count as isize);
1363             emit_keyvalue_collection(e, env, pos, fields, ctype, ctor)
1364         }
1365     };
1366     use tast::Expr_ as E_;
1367     match &expr.1 {
1368         E_::ValCollection(v) if v.0 == tast::VcKind::Vec => {
1369             emit_value_only_collection(e, env, pos, fields, InstructLitConst::NewVec)
1370         }
1371         E_::Collection(v) if (v.0).1 == "vec" => {
1372             emit_value_only_collection(e, env, pos, fields, InstructLitConst::NewVec)
1373         }
1374         E_::ValCollection(v) if v.0 == tast::VcKind::Keyset => {
1375             emit_value_only_collection(e, env, pos, fields, InstructLitConst::NewKeysetArray)
1376         }
1377         E_::Collection(v) if (v.0).1 == "keyset" => {
1378             emit_value_only_collection(e, env, pos, fields, InstructLitConst::NewKeysetArray)
1379         }
1380         E_::Collection(v) if (v.0).1 == "dict" => emit_dict(e),
1381         E_::KeyValCollection(v) if v.0 == tast::KvcKind::Dict => emit_dict(e),
1382         E_::Collection(v) if string_utils::strip_ns(&(v.0).1) == "Set" => {
1383             emit_collection_helper(e, CollectionType::Set)
1384         }
1385         E_::ValCollection(v) if v.0 == tast::VcKind::Set => {
1386             emit_collection_helper(e, CollectionType::Set)
1387         }
1388         E_::Collection(v) if string_utils::strip_ns(&(v.0).1) == "ImmSet" => {
1389             emit_collection_helper(e, CollectionType::ImmSet)
1390         }
1391         E_::ValCollection(v) if v.0 == tast::VcKind::ImmSet => {
1392             emit_collection_helper(e, CollectionType::ImmSet)
1393         }
1394         E_::Collection(v) if string_utils::strip_ns(&(v.0).1) == "Map" => {
1395             emit_collection_helper(e, CollectionType::Map)
1396         }
1397         E_::KeyValCollection(v) if v.0 == tast::KvcKind::Map => {
1398             emit_collection_helper(e, CollectionType::Map)
1399         }
1400         E_::Collection(v) if string_utils::strip_ns(&(v.0).1) == "ImmMap" => {
1401             emit_collection_helper(e, CollectionType::ImmMap)
1402         }
1403         E_::KeyValCollection(v) if v.0 == tast::KvcKind::ImmMap => {
1404             emit_collection_helper(e, CollectionType::ImmMap)
1405         }
1406         E_::Varray(_) => {
1407             let hack_arr_dv_arrs = hack_arr_dv_arrs(e.options());
1408             let instrs = emit_value_only_collection(e, env, pos, fields, |n| {
1409                 if hack_arr_dv_arrs {
1410                     InstructLitConst::NewVec(n)
1411                 } else {
1412                     InstructLitConst::NewVArray(n)
1413                 }
1414             });
1415             Ok(wrap_array_mark_legacy(e, instrs?))
1416         }
1417         E_::Darray(_) => {
1418             if is_struct_init(e, env, fields, false /* allow_numerics */)? {
1419                 let hack_arr_dv_arrs = hack_arr_dv_arrs(e.options());
1420                 let instrs = emit_struct_array(e, env, pos, fields, |_, arg| {
1421                     let instr = if hack_arr_dv_arrs {
1422                         instr::newstructdict(arg)
1423                     } else {
1424                         instr::newstructdarray(arg)
1425                     };
1426                     Ok(emit_pos_then(pos, instr))
1427                 });
1428                 Ok(wrap_array_mark_legacy(e, instrs?))
1429             } else {
1430                 let constr = if hack_arr_dv_arrs(e.options()) {
1431                     InstructLitConst::NewDictArray(count as isize)
1432                 } else {
1433                     InstructLitConst::NewDArray(count as isize)
1434                 };
1435                 let instrs =
1436                     emit_keyvalue_collection(e, env, pos, fields, CollectionType::Array, constr);
1437                 Ok(wrap_array_mark_legacy(e, instrs?))
1438             }
1439         }
1440         _ => Err(unrecoverable("plain PHP arrays cannot be constructed")),
1441     }
1444 fn emit_value_only_collection<F: FnOnce(isize) -> InstructLitConst>(
1445     e: &mut Emitter,
1446     env: &Env,
1447     pos: &Pos,
1448     fields: &[tast::Afield],
1449     constructor: F,
1450 ) -> Result {
1451     let limit = *(e.options().max_array_elem_size_on_the_stack.get()) as usize;
1452     let inline = |e: &mut Emitter, exprs: &[tast::Afield]| -> Result {
1453         Ok(InstrSeq::gather(vec![
1454             InstrSeq::gather(
1455                 exprs
1456                     .iter()
1457                     .map(|f| emit_expr(e, env, f.value()))
1458                     .collect::<Result<_>>()?,
1459             ),
1460             emit_pos(pos),
1461             instr::lit_const(constructor(exprs.len() as isize)),
1462         ]))
1463     };
1464     let outofline = |e: &mut Emitter, exprs: &[tast::Afield]| -> Result {
1465         Ok(InstrSeq::gather(
1466             exprs
1467                 .iter()
1468                 .map(|f| {
1469                     Ok(InstrSeq::gather(vec![
1470                         emit_expr(e, env, f.value())?,
1471                         instr::add_new_elemc(),
1472                     ]))
1473                 })
1474                 .collect::<Result<_>>()?,
1475         ))
1476     };
1477     let (x1, x2) = fields.split_at(std::cmp::min(fields.len(), limit));
1478     Ok(match (x1, x2) {
1479         ([], []) => instr::empty(),
1480         (_, []) => inline(e, x1)?,
1481         _ => {
1482             let outofline_instrs = outofline(e, x2)?;
1483             let inline_instrs = inline(e, x1)?;
1484             InstrSeq::gather(vec![inline_instrs, outofline_instrs])
1485         }
1486     })
1489 fn emit_record(
1490     e: &mut Emitter,
1491     env: &Env,
1492     pos: &Pos,
1493     (cid, es): &(tast::Sid, Vec<(tast::Expr, tast::Expr)>),
1494 ) -> Result {
1495     let es = mk_afkvalues(es);
1496     let id = class::Type::from_ast_name_and_mangle(&cid.1);
1497     emit_symbol_refs::State::add_class(e, id.clone());
1498     emit_struct_array(e, env, pos, &es, |_, keys| Ok(instr::new_record(id, keys)))
1501 fn emit_call_isset_expr(e: &mut Emitter, env: &Env, outer_pos: &Pos, expr: &tast::Expr) -> Result {
1502     let pos = &expr.0;
1503     if let Some((base_expr, opt_elem_expr)) = expr.1.as_array_get() {
1504         return Ok(emit_array_get(
1505             e,
1506             env,
1507             pos,
1508             None,
1509             QueryOp::Isset,
1510             base_expr,
1511             opt_elem_expr.as_ref(),
1512             false,
1513             false,
1514         )?
1515         .0);
1516     }
1517     if let Some((cid, id, _)) = expr.1.as_class_get() {
1518         return emit_class_get(e, env, QueryOp::Isset, cid, id);
1519     }
1520     if let Some((expr_, prop, nullflavor, _)) = expr.1.as_obj_get() {
1521         return Ok(emit_obj_get(e, env, pos, QueryOp::Isset, expr_, prop, nullflavor, false)?.0);
1522     }
1523     if let Some(lid) = expr.1.as_lvar() {
1524         let name = local_id::get_name(&lid.1);
1525         return Ok(if superglobals::is_any_global(&name) {
1526             InstrSeq::gather(vec![
1527                 emit_pos(outer_pos),
1528                 instr::string(string_utils::locals::strip_dollar(&name)),
1529                 emit_pos(outer_pos),
1530                 instr::issetg(),
1531             ])
1532         } else if is_local_this(env, &lid.1) && !env.flags.contains(env::Flags::NEEDS_LOCAL_THIS) {
1533             InstrSeq::gather(vec![
1534                 emit_pos(outer_pos),
1535                 emit_local(e, env, BareThisOp::NoNotice, lid)?,
1536                 emit_pos(outer_pos),
1537                 instr::istypec(IstypeOp::OpNull),
1538                 instr::not(),
1539             ])
1540         } else {
1541             emit_pos_then(outer_pos, instr::issetl(get_local(e, env, &lid.0, name)?))
1542         });
1543     }
1544     Ok(InstrSeq::gather(vec![
1545         emit_expr(e, env, expr)?,
1546         instr::istypec(IstypeOp::OpNull),
1547         instr::not(),
1548     ]))
1551 fn emit_call_isset_exprs(e: &mut Emitter, env: &Env, pos: &Pos, exprs: &[tast::Expr]) -> Result {
1552     match exprs {
1553         [] => Err(emit_fatal::raise_fatal_parse(
1554             pos,
1555             "Cannot use isset() without any arguments",
1556         )),
1557         [expr] => emit_call_isset_expr(e, env, pos, expr),
1558         _ => {
1559             let its_done = e.label_gen_mut().next_regular();
1560             Ok(InstrSeq::gather(vec![
1561                 InstrSeq::gather(
1562                     exprs
1563                         .iter()
1564                         .enumerate()
1565                         .map(|(i, expr)| {
1566                             Ok(InstrSeq::gather(vec![
1567                                 emit_call_isset_expr(e, env, pos, expr)?,
1568                                 if i < exprs.len() - 1 {
1569                                     InstrSeq::gather(vec![
1570                                         instr::dup(),
1571                                         instr::jmpz(its_done.clone()),
1572                                         instr::popc(),
1573                                     ])
1574                                 } else {
1575                                     instr::empty()
1576                                 },
1577                             ]))
1578                         })
1579                         .collect::<Result<Vec<_>>>()?,
1580                 ),
1581                 instr::label(its_done),
1582             ]))
1583         }
1584     }
1587 fn emit_tag_provenance_here(e: &mut Emitter, env: &Env, pos: &Pos, es: &[tast::Expr]) -> Result {
1588     let default = if es.len() == 1 {
1589         instr::int(0)
1590     } else {
1591         instr::empty()
1592     };
1593     let tag = instr::instr(Instruct::IMisc(InstructMisc::TagProvenanceHere));
1594     Ok(InstrSeq::gather(vec![
1595         emit_exprs(e, env, es)?,
1596         emit_pos(pos),
1597         default,
1598         tag,
1599     ]))
1602 fn emit_array_mark_legacy(
1603     e: &mut Emitter,
1604     env: &Env,
1605     pos: &Pos,
1606     es: &[tast::Expr],
1607     legacy: bool,
1608 ) -> Result {
1609     let default = if es.len() == 1 {
1610         instr::false_()
1611     } else {
1612         instr::empty()
1613     };
1614     let mark = if legacy {
1615         instr::instr(Instruct::IMisc(InstructMisc::ArrayMarkLegacy))
1616     } else {
1617         instr::instr(Instruct::IMisc(InstructMisc::ArrayUnmarkLegacy))
1618     };
1619     Ok(InstrSeq::gather(vec![
1620         emit_exprs(e, env, es)?,
1621         emit_pos(pos),
1622         default,
1623         mark,
1624     ]))
1627 fn emit_idx(e: &mut Emitter, env: &Env, pos: &Pos, es: &[tast::Expr]) -> Result {
1628     let default = if es.len() == 2 {
1629         instr::null()
1630     } else {
1631         instr::empty()
1632     };
1633     Ok(InstrSeq::gather(vec![
1634         emit_exprs(e, env, es)?,
1635         emit_pos(pos),
1636         default,
1637         instr::idx(),
1638     ]))
1641 fn emit_call(
1642     e: &mut Emitter,
1643     env: &Env,
1644     pos: &Pos,
1645     expr: &tast::Expr,
1646     targs: &[tast::Targ],
1647     args: &[tast::Expr],
1648     uarg: Option<&tast::Expr>,
1649     async_eager_label: Option<Label>,
1650 ) -> Result {
1651     if let Some(ast_defs::Id(_, s)) = expr.as_id() {
1652         let fid = function::Type::from_ast_name(s);
1653         emit_symbol_refs::add_function(e, fid);
1654     }
1655     let fcall_args = get_fcall_args(
1656         args,
1657         uarg,
1658         async_eager_label,
1659         env.call_context.clone(),
1660         false,
1661     );
1662     match expr.1.as_id() {
1663         None => emit_call_default(e, env, pos, expr, targs, args, uarg, fcall_args),
1664         Some(ast_defs::Id(_, id)) => {
1665             let fq = function::Type::from_ast_name(id);
1666             let lower_fq_name = fq.to_raw_string();
1667             emit_special_function(e, env, pos, args, uarg, lower_fq_name)
1668                 .transpose()
1669                 .unwrap_or_else(|| {
1670                     emit_call_default(e, env, pos, expr, targs, args, uarg, fcall_args)
1671                 })
1672         }
1673     }
1676 fn emit_call_default(
1677     e: &mut Emitter,
1678     env: &Env,
1679     pos: &Pos,
1680     expr: &tast::Expr,
1681     targs: &[tast::Targ],
1682     args: &[tast::Expr],
1683     uarg: Option<&tast::Expr>,
1684     fcall_args: FcallArgs,
1685 ) -> Result {
1686     scope::with_unnamed_locals(e, |e| {
1687         let FcallArgs(_, _, num_ret, _, _, _) = &fcall_args;
1688         let num_uninit = num_ret - 1;
1689         let (lhs, fcall) = emit_call_lhs_and_fcall(e, env, expr, fcall_args, targs)?;
1690         let (args, inout_setters) = emit_args_inout_setters(e, env, args)?;
1691         let uargs = uarg.map_or(Ok(instr::empty()), |uarg| emit_expr(e, env, uarg))?;
1692         Ok((
1693             instr::empty(),
1694             InstrSeq::gather(vec![
1695                 InstrSeq::gather(
1696                     iter::repeat(instr::nulluninit())
1697                         .take(num_uninit)
1698                         .collect::<Vec<_>>(),
1699                 ),
1700                 lhs,
1701                 args,
1702                 uargs,
1703                 emit_pos(pos),
1704                 fcall,
1705                 inout_setters,
1706             ]),
1707             instr::empty(),
1708         ))
1709     })
1712 pub fn emit_reified_targs(e: &mut Emitter, env: &Env, pos: &Pos, targs: &[&tast::Hint]) -> Result {
1713     let current_fun_tparams = env.scope.get_fun_tparams();
1714     let current_cls_tparams = env.scope.get_class_tparams();
1715     let is_in_lambda = env.scope.is_in_lambda();
1716     let is_soft =
1717         |ual: &Vec<tast::UserAttribute>| ual.iter().any(|ua| user_attributes::is_soft(&ua.name.1));
1718     let same_as_targs = |tparams: &[tast::Tparam]| {
1719         tparams.len() == targs.len()
1720             && tparams.iter().zip(targs).all(|(tp, ta)| {
1721                 ta.1.as_happly().map_or(false, |(id, hs)| {
1722                     id.1 == tp.name.1
1723                         && hs.is_empty()
1724                         && !is_soft(&tp.user_attributes)
1725                         && tp.reified.is_reified()
1726                 })
1727             })
1728     };
1729     Ok(if !is_in_lambda && same_as_targs(&current_fun_tparams) {
1730         instr::cgetl(local::Type::Named(
1731             string_utils::reified::GENERICS_LOCAL_NAME.into(),
1732         ))
1733     } else if !is_in_lambda && same_as_targs(&current_cls_tparams[..]) {
1734         InstrSeq::gather(vec![
1735             instr::checkthis(),
1736             instr::baseh(),
1737             instr::querym(
1738                 0,
1739                 QueryOp::CGet,
1740                 MemberKey::PT(
1741                     prop::from_raw_string(string_utils::reified::PROP_NAME),
1742                     ReadOnlyOp::Any,
1743                 ),
1744             ),
1745         ])
1746     } else {
1747         let instrs = InstrSeq::gather(vec![
1748             InstrSeq::gather(
1749                 targs
1750                     .iter()
1751                     .map(|h| Ok(emit_reified_arg(e, env, pos, false, h)?.0))
1752                     .collect::<Result<Vec<_>>>()?,
1753             ),
1754             if hack_arr_dv_arrs(e.options()) {
1755                 instr::new_vec_array(targs.len() as isize)
1756             } else {
1757                 instr::new_varray(targs.len() as isize)
1758             },
1759         ]);
1760         wrap_array_mark_legacy(e, instrs)
1761     })
1764 fn get_erased_tparams<'a>(env: &'a Env<'a>) -> Vec<&'a str> {
1765     env.scope
1766         .get_tparams()
1767         .iter()
1768         .filter_map(|tparam| match tparam.reified {
1769             tast::ReifyKind::Erased => Some(tparam.name.1.as_str()),
1770             _ => None,
1771         })
1772         .collect()
1775 pub fn has_non_tparam_generics(env: &Env, hints: &[tast::Hint]) -> bool {
1776     let erased_tparams = get_erased_tparams(env);
1777     hints.iter().any(|hint| {
1778         hint.1
1779             .as_happly()
1780             .map_or(true, |(id, _)| !erased_tparams.contains(&id.1.as_str()))
1781     })
1784 fn has_non_tparam_generics_targs(env: &Env, targs: &[tast::Targ]) -> bool {
1785     let erased_tparams = get_erased_tparams(env);
1786     targs.iter().any(|targ| {
1787         (targ.1)
1788             .1
1789             .as_happly()
1790             .map_or(true, |(id, _)| !erased_tparams.contains(&id.1.as_str()))
1791     })
1794 fn from_ast_null_flavor(nullflavor: tast::OgNullFlavor) -> ObjNullFlavor {
1795     match nullflavor {
1796         tast::OgNullFlavor::OGNullsafe => ObjNullFlavor::NullSafe,
1797         tast::OgNullFlavor::OGNullthrows => ObjNullFlavor::NullThrows,
1798     }
1801 fn emit_object_expr(e: &mut Emitter, env: &Env, expr: &tast::Expr) -> Result {
1802     match &expr.1 {
1803         tast::Expr_::Lvar(x) if is_local_this(env, &x.1) => Ok(instr::this()),
1804         _ => emit_expr(e, env, expr),
1805     }
1808 fn emit_call_lhs_and_fcall(
1809     e: &mut Emitter,
1810     env: &Env,
1811     expr: &tast::Expr,
1812     mut fcall_args: FcallArgs,
1813     targs: &[tast::Targ],
1814 ) -> Result<(InstrSeq, InstrSeq)> {
1815     let tast::Expr(pos, expr_) = expr;
1816     use tast::{Expr as E, Expr_ as E_};
1818     let emit_generics = |e: &mut Emitter, env, fcall_args: &mut FcallArgs| {
1819         let does_not_have_non_tparam_generics = !has_non_tparam_generics_targs(env, targs);
1820         if does_not_have_non_tparam_generics {
1821             Ok(instr::empty())
1822         } else {
1823             *(&mut fcall_args.0) = fcall_args.0 | FcallFlags::HAS_GENERICS;
1824             emit_reified_targs(
1825                 e,
1826                 env,
1827                 pos,
1828                 targs
1829                     .iter()
1830                     .map(|targ| &targ.1)
1831                     .collect::<Vec<_>>()
1832                     .as_slice(),
1833             )
1834         }
1835     };
1837     let emit_fcall_func = |
1838         e: &mut Emitter,
1839         env,
1840         expr: &tast::Expr,
1841         fcall_args: FcallArgs,
1842     | -> Result<(InstrSeq, InstrSeq)> {
1843         let tmp = e.local_gen_mut().get_unnamed();
1844         Ok((
1845             InstrSeq::gather(vec![
1846                 instr::nulluninit(),
1847                 instr::nulluninit(),
1848                 emit_expr(e, env, expr)?,
1849                 instr::popl(tmp.clone()),
1850             ]),
1851             InstrSeq::gather(vec![instr::pushl(tmp), instr::fcallfunc(fcall_args)]),
1852         ))
1853     };
1855     match expr_ {
1856         E_::ObjGet(o) => {
1857             if o.as_ref().3 {
1858                 // Case ($x->foo)(...).
1859                 let expr = E(
1860                     pos.clone(),
1861                     E_::ObjGet(Box::new((o.0.clone(), o.1.clone(), o.2.clone(), false))),
1862                 );
1863                 emit_fcall_func(e, env, &expr, fcall_args)
1864             } else {
1865                 // Case $x->foo(...).
1866                 let emit_id =
1867                     |e: &mut Emitter, obj, id, null_flavor: &tast::OgNullFlavor, mut fcall_args| {
1868                         // TODO(hrust): enable let name = method::Type::from_ast_name(id);
1869                         let name: method::Type =
1870                             string_utils::strip_global_ns(id).to_string().into();
1871                         let obj = emit_object_expr(e, env, obj)?;
1872                         let generics = emit_generics(e, env, &mut fcall_args)?;
1873                         let null_flavor = from_ast_null_flavor(*null_flavor);
1874                         Ok((
1875                             InstrSeq::gather(vec![obj, instr::nulluninit()]),
1876                             InstrSeq::gather(vec![
1877                                 generics,
1878                                 instr::fcallobjmethodd(fcall_args, name, null_flavor),
1879                             ]),
1880                         ))
1881                     };
1882                 match o.as_ref() {
1883                     (obj, E(_, E_::String(id)), null_flavor, _) => {
1884                         emit_id(
1885                             e,
1886                             obj,
1887                             // FIXME: This is not safe--string literals are binary strings.
1888                             // There's no guarantee that they're valid UTF-8.
1889                             unsafe { std::str::from_utf8_unchecked(id.as_slice().into()) },
1890                             null_flavor,
1891                             fcall_args,
1892                         )
1893                     }
1894                     (E(pos, E_::New(new_exp)), E(_, E_::Id(id)), null_flavor, _)
1895                         if fcall_args.1 == 0 =>
1896                     {
1897                         let cexpr = ClassExpr::class_id_to_class_expr(
1898                             e, false, false, &env.scope, &new_exp.0,
1899                         );
1900                         match &cexpr {
1901                             ClassExpr::Id(ast_defs::Id(_, name))
1902                                 if string_utils::strip_global_ns(name) == "ReflectionClass" =>
1903                             {
1904                                 let fid = match string_utils::strip_global_ns(&id.1) {
1905                                     "isAbstract" => {
1906                                         Some("__SystemLib\\reflection_class_is_abstract")
1907                                     }
1908                                     "isInterface" => {
1909                                         Some("__SystemLib\\reflection_class_is_interface")
1910                                     }
1911                                     "isFinal" => Some("__SystemLib\\reflection_class_is_final"),
1912                                     "getName" => Some("__SystemLib\\reflection_class_get_name"),
1913                                     _ => None,
1914                                 };
1915                                 match fid {
1916                                     None => {
1917                                         emit_id(e, &o.as_ref().0, &id.1, null_flavor, fcall_args)
1918                                     }
1919                                     Some(fid) => {
1920                                         let fcall_args = FcallArgs::new(
1921                                             FcallFlags::default(),
1922                                             1,
1923                                             vec![],
1924                                             None,
1925                                             1,
1926                                             None,
1927                                         );
1928                                         let newobj_instrs = emit_new(e, env, pos, &new_exp, true);
1929                                         Ok((
1930                                             InstrSeq::gather(vec![
1931                                                 instr::nulluninit(),
1932                                                 instr::nulluninit(),
1933                                                 newobj_instrs?,
1934                                             ]),
1935                                             InstrSeq::gather(vec![instr::fcallfuncd(
1936                                                 fcall_args,
1937                                                 function::Type::from_ast_name(fid),
1938                                             )]),
1939                                         ))
1940                                     }
1941                                 }
1942                             }
1943                             _ => emit_id(e, &o.as_ref().0, &id.1, null_flavor, fcall_args),
1944                         }
1945                     }
1946                     (obj, E(_, E_::Id(id)), null_flavor, _) => {
1947                         emit_id(e, obj, &id.1, null_flavor, fcall_args)
1948                     }
1949                     (obj, method_expr, null_flavor, _) => {
1950                         let obj = emit_object_expr(e, env, obj)?;
1951                         let tmp = e.local_gen_mut().get_unnamed();
1952                         let null_flavor = from_ast_null_flavor(*null_flavor);
1953                         Ok((
1954                             InstrSeq::gather(vec![
1955                                 obj,
1956                                 instr::nulluninit(),
1957                                 emit_expr(e, env, method_expr)?,
1958                                 instr::popl(tmp.clone()),
1959                             ]),
1960                             InstrSeq::gather(vec![
1961                                 instr::pushl(tmp),
1962                                 instr::fcallobjmethod(fcall_args, null_flavor),
1963                             ]),
1964                         ))
1965                     }
1966                 }
1967             }
1968         }
1969         E_::ClassConst(cls_const) => {
1970             let (cid, (_, id)) = &**cls_const;
1971             let mut cexpr = ClassExpr::class_id_to_class_expr(e, false, false, &env.scope, cid);
1972             if let ClassExpr::Id(ast_defs::Id(_, name)) = &cexpr {
1973                 if let Some(reified_var_cexpr) = get_reified_var_cexpr(env, pos, &name)? {
1974                     cexpr = reified_var_cexpr;
1975                 }
1976             }
1977             // TODO(hrust) enabel `let method_id = method::Type::from_ast_name(&id);`,
1978             // `from_ast_name` should be able to accpet Cow<str>
1979             let method_id: method::Type = string_utils::strip_global_ns(&id).to_string().into();
1980             Ok(match cexpr {
1981                 // Statically known
1982                 ClassExpr::Id(ast_defs::Id(_, cname)) => {
1983                     let cid = class::Type::from_ast_name_and_mangle(&cname);
1984                     emit_symbol_refs::State::add_class(e, cid.clone());
1985                     let generics = emit_generics(e, env, &mut fcall_args)?;
1986                     (
1987                         InstrSeq::gather(vec![instr::nulluninit(), instr::nulluninit()]),
1988                         InstrSeq::gather(vec![
1989                             generics,
1990                             instr::fcallclsmethodd(fcall_args, method_id, cid),
1991                         ]),
1992                     )
1993                 }
1994                 ClassExpr::Special(clsref) => {
1995                     let generics = emit_generics(e, env, &mut fcall_args)?;
1996                     (
1997                         InstrSeq::gather(vec![instr::nulluninit(), instr::nulluninit()]),
1998                         InstrSeq::gather(vec![
1999                             generics,
2000                             instr::fcallclsmethodsd(fcall_args, clsref, method_id),
2001                         ]),
2002                     )
2003                 }
2004                 ClassExpr::Expr(expr) => {
2005                     let generics = emit_generics(e, env, &mut fcall_args)?;
2006                     (
2007                         InstrSeq::gather(vec![instr::nulluninit(), instr::nulluninit()]),
2008                         InstrSeq::gather(vec![
2009                             generics,
2010                             instr::string(method_id.to_raw_string()),
2011                             emit_expr(e, env, &expr)?,
2012                             instr::classgetc(),
2013                             instr::fcallclsmethod(
2014                                 IsLogAsDynamicCallOp::DontLogAsDynamicCall,
2015                                 fcall_args,
2016                             ),
2017                         ]),
2018                     )
2019                 }
2020                 ClassExpr::Reified(instrs) => {
2021                     let tmp = e.local_gen_mut().get_unnamed();
2022                     (
2023                         InstrSeq::gather(vec![
2024                             instr::nulluninit(),
2025                             instr::nulluninit(),
2026                             instrs,
2027                             instr::popl(tmp.clone()),
2028                         ]),
2029                         InstrSeq::gather(vec![
2030                             instr::string(method_id.to_raw_string()),
2031                             instr::pushl(tmp),
2032                             instr::classgetc(),
2033                             instr::fcallclsmethod(
2034                                 IsLogAsDynamicCallOp::LogAsDynamicCall,
2035                                 fcall_args,
2036                             ),
2037                         ]),
2038                     )
2039                 }
2040             })
2041         }
2042         E_::ClassGet(c) => {
2043             if c.as_ref().2 {
2044                 // Case (Foo::$bar)(...).
2045                 let expr = E(
2046                     pos.clone(),
2047                     E_::ClassGet(Box::new((c.0.clone(), c.1.clone(), false))),
2048                 );
2049                 emit_fcall_func(e, env, &expr, fcall_args)
2050             } else {
2051                 // Case Foo::bar(...).
2052                 let (cid, cls_get_expr, _) = &**c;
2053                 let mut cexpr = ClassExpr::class_id_to_class_expr(e, false, false, &env.scope, cid);
2054                 if let ClassExpr::Id(ast_defs::Id(_, name)) = &cexpr {
2055                     if let Some(reified_var_cexpr) = get_reified_var_cexpr(env, pos, &name)? {
2056                         cexpr = reified_var_cexpr;
2057                     }
2058                 }
2059                 let emit_meth_name = |e: &mut Emitter| match &cls_get_expr {
2060                     tast::ClassGetExpr::CGstring((pos, id)) => Ok(emit_pos_then(
2061                         pos,
2062                         instr::cgetl(local::Type::Named(id.clone())),
2063                     )),
2064                     tast::ClassGetExpr::CGexpr(expr) => emit_expr(e, env, expr),
2065                 };
2066                 Ok(match cexpr {
2067                     ClassExpr::Id(cid) => {
2068                         let tmp = e.local_gen_mut().get_unnamed();
2069                         (
2070                             InstrSeq::gather(vec![
2071                                 instr::nulluninit(),
2072                                 instr::nulluninit(),
2073                                 emit_meth_name(e)?,
2074                                 instr::popl(tmp.clone()),
2075                             ]),
2076                             InstrSeq::gather(vec![
2077                                 instr::pushl(tmp),
2078                                 emit_known_class_id(e, &cid),
2079                                 instr::fcallclsmethod(
2080                                     IsLogAsDynamicCallOp::LogAsDynamicCall,
2081                                     fcall_args,
2082                                 ),
2083                             ]),
2084                         )
2085                     }
2086                     ClassExpr::Special(clsref) => {
2087                         let tmp = e.local_gen_mut().get_unnamed();
2088                         (
2089                             InstrSeq::gather(vec![
2090                                 instr::nulluninit(),
2091                                 instr::nulluninit(),
2092                                 emit_meth_name(e)?,
2093                                 instr::popl(tmp.clone()),
2094                             ]),
2095                             InstrSeq::gather(vec![
2096                                 instr::pushl(tmp),
2097                                 instr::fcallclsmethods(fcall_args, clsref),
2098                             ]),
2099                         )
2100                     }
2101                     ClassExpr::Expr(expr) => {
2102                         let cls = e.local_gen_mut().get_unnamed();
2103                         let meth = e.local_gen_mut().get_unnamed();
2104                         (
2105                             InstrSeq::gather(vec![
2106                                 instr::nulluninit(),
2107                                 instr::nulluninit(),
2108                                 emit_expr(e, env, &expr)?,
2109                                 instr::popl(cls.clone()),
2110                                 emit_meth_name(e)?,
2111                                 instr::popl(meth.clone()),
2112                             ]),
2113                             InstrSeq::gather(vec![
2114                                 instr::pushl(meth),
2115                                 instr::pushl(cls),
2116                                 instr::classgetc(),
2117                                 instr::fcallclsmethod(
2118                                     IsLogAsDynamicCallOp::LogAsDynamicCall,
2119                                     fcall_args,
2120                                 ),
2121                             ]),
2122                         )
2123                     }
2124                     ClassExpr::Reified(instrs) => {
2125                         let cls = e.local_gen_mut().get_unnamed();
2126                         let meth = e.local_gen_mut().get_unnamed();
2127                         (
2128                             InstrSeq::gather(vec![
2129                                 instr::nulluninit(),
2130                                 instr::nulluninit(),
2131                                 instrs,
2132                                 instr::popl(cls.clone()),
2133                                 emit_meth_name(e)?,
2134                                 instr::popl(meth.clone()),
2135                             ]),
2136                             InstrSeq::gather(vec![
2137                                 instr::pushl(meth),
2138                                 instr::pushl(cls),
2139                                 instr::classgetc(),
2140                                 instr::fcallclsmethod(
2141                                     IsLogAsDynamicCallOp::LogAsDynamicCall,
2142                                     fcall_args,
2143                                 ),
2144                             ]),
2145                         )
2146                     }
2147                 })
2148             }
2149         }
2150         E_::Id(id) => {
2151             let FcallArgs(flags, num_args, _, _, _, _) = fcall_args;
2152             let fq_id = match string_utils::strip_global_ns(&id.1) {
2153                 "min" if num_args == 2 && !flags.contains(FcallFlags::HAS_UNPACK) => {
2154                     function::Type::from_ast_name("__SystemLib\\min2")
2155                 }
2156                 "max" if num_args == 2 && !flags.contains(FcallFlags::HAS_UNPACK) => {
2157                     function::Type::from_ast_name("__SystemLib\\max2")
2158                 }
2159                 _ => {
2160                     //TODO(hrust): enable `function::Type::from_ast_name(&id.1)`
2161                     string_utils::strip_global_ns(&id.1).to_string().into()
2162                 }
2163             };
2164             let generics = emit_generics(e, env, &mut fcall_args)?;
2165             Ok((
2166                 InstrSeq::gather(vec![instr::nulluninit(), instr::nulluninit()]),
2167                 InstrSeq::gather(vec![generics, instr::fcallfuncd(fcall_args, fq_id)]),
2168             ))
2169         }
2170         E_::String(s) => {
2171             // TODO(hrust) should be able to accept `let fq_id = function::from_raw_string(s);`
2172             let fq_id = s.to_string().into();
2173             let generics = emit_generics(e, env, &mut fcall_args)?;
2174             Ok((
2175                 InstrSeq::gather(vec![instr::nulluninit(), instr::nulluninit()]),
2176                 InstrSeq::gather(vec![generics, instr::fcallfuncd(fcall_args, fq_id)]),
2177             ))
2178         }
2179         _ => emit_fcall_func(e, env, expr, fcall_args),
2180     }
2183 fn get_reified_var_cexpr(env: &Env, pos: &Pos, name: &str) -> Result<Option<ClassExpr>> {
2184     Ok(emit_reified_type_opt(env, pos, name)?.map(|instrs| {
2185         ClassExpr::Reified(InstrSeq::gather(vec![
2186             instrs,
2187             instr::basec(0, MemberOpMode::Warn),
2188             instr::querym(
2189                 1,
2190                 QueryOp::CGet,
2191                 MemberKey::ET("classname".into(), ReadOnlyOp::Any),
2192             ),
2193         ]))
2194     }))
2197 fn emit_args_inout_setters(
2198     e: &mut Emitter,
2199     env: &Env,
2200     args: &[tast::Expr],
2201 ) -> Result<(InstrSeq, InstrSeq)> {
2202     let aliases = if has_inout_arg(args) {
2203         inout_locals::collect_written_variables(env, args)
2204     } else {
2205         inout_locals::AliasInfoMap::new()
2206     };
2207     fn emit_arg_and_inout_setter(
2208         e: &mut Emitter,
2209         env: &Env,
2210         i: usize,
2211         arg: &tast::Expr,
2212         aliases: &inout_locals::AliasInfoMap,
2213     ) -> Result<(InstrSeq, InstrSeq)> {
2214         use tast::Expr_ as E_;
2215         match &arg.1 {
2216             E_::Callconv(cc) if (cc.0).is_pinout() => {
2217                 match &(cc.1).1 {
2218                     // inout $var
2219                     E_::Lvar(l) => {
2220                         let local = get_local(e, env, &l.0, local_id::get_name(&l.1))?;
2221                         let move_instrs = if !env.flags.contains(env::Flags::IN_TRY)
2222                             && inout_locals::should_move_local_value(&local, aliases)
2223                         {
2224                             InstrSeq::gather(vec![instr::null(), instr::popl(local.clone())])
2225                         } else {
2226                             instr::empty()
2227                         };
2228                         Ok((
2229                             InstrSeq::gather(vec![instr::cgetl(local.clone()), move_instrs]),
2230                             instr::popl(local),
2231                         ))
2232                     }
2233                     // inout $arr[...][...]
2234                     E_::ArrayGet(ag) => {
2235                         let array_get_result = emit_array_get_(
2236                             e,
2237                             env,
2238                             &(cc.1).0,
2239                             None,
2240                             QueryOp::InOut,
2241                             &ag.0,
2242                             ag.1.as_ref(),
2243                             false,
2244                             false,
2245                             Some((i, aliases)),
2246                         )?
2247                         .0;
2248                         Ok(match array_get_result {
2249                             ArrayGetInstr::Regular(instrs) => {
2250                                 let setter_base = emit_array_get(
2251                                     e,
2252                                     env,
2253                                     &(cc.1).0,
2254                                     Some(MemberOpMode::Define),
2255                                     QueryOp::InOut,
2256                                     &ag.0,
2257                                     ag.1.as_ref(),
2258                                     true,
2259                                     false,
2260                                 )?
2261                                 .0;
2262                                 let (mk, warninstr) =
2263                                     get_elem_member_key(e, env, 0, ag.1.as_ref(), false)?;
2264                                 let setter = InstrSeq::gather(vec![
2265                                     warninstr,
2266                                     setter_base,
2267                                     instr::setm(0, mk),
2268                                     instr::popc(),
2269                                 ]);
2270                                 (instrs, setter)
2271                             }
2272                             ArrayGetInstr::Inout { load, store } => {
2273                                 let (mut ld, mut st) = (vec![], vec![store]);
2274                                 for (instr, local_kind_opt) in load.into_iter() {
2275                                     match local_kind_opt {
2276                                         None => ld.push(instr),
2277                                         Some((l, kind)) => {
2278                                             let unset = instr::unsetl(l.clone());
2279                                             let set = match kind {
2280                                                 StoredValueKind::Expr => instr::setl(l),
2281                                                 _ => instr::popl(l),
2282                                             };
2283                                             ld.push(instr);
2284                                             ld.push(set);
2285                                             st.push(unset);
2286                                         }
2287                                     }
2288                                 }
2289                                 (InstrSeq::gather(ld), InstrSeq::gather(st))
2290                             }
2291                         })
2292                     }
2293                     _ => Err(unrecoverable(
2294                         "emit_arg_and_inout_setter: Unexpected inout expression type",
2295                     )),
2296                 }
2297             }
2298             _ => Ok((emit_expr(e, env, arg)?, instr::empty())),
2299         }
2300     }
2301     let (instr_args, instr_setters): (Vec<InstrSeq>, Vec<InstrSeq>) = args
2302         .iter()
2303         .enumerate()
2304         .map(|(i, arg)| emit_arg_and_inout_setter(e, env, i, arg, &aliases))
2305         .collect::<Result<Vec<_>>>()?
2306         .into_iter()
2307         .unzip();
2308     let instr_args = InstrSeq::gather(instr_args);
2309     let instr_setters = InstrSeq::gather(instr_setters);
2310     if has_inout_arg(args) {
2311         let retval = e.local_gen_mut().get_unnamed();
2312         Ok((
2313             instr_args,
2314             InstrSeq::gather(vec![
2315                 instr::popl(retval.clone()),
2316                 instr_setters,
2317                 instr::pushl(retval),
2318             ]),
2319         ))
2320     } else {
2321         Ok((instr_args, instr::empty()))
2322     }
2325 fn get_fcall_args(
2326     args: &[tast::Expr],
2327     uarg: Option<&tast::Expr>,
2328     async_eager_label: Option<Label>,
2329     context: Option<String>,
2330     lock_while_unwinding: bool,
2331 ) -> FcallArgs {
2332     let num_args = args.len();
2333     let num_rets = 1 + args.iter().filter(|x| is_inout_arg(*x)).count();
2334     let mut flags = FcallFlags::default();
2335     flags.set(FcallFlags::HAS_UNPACK, uarg.is_some());
2336     flags.set(FcallFlags::LOCK_WHILE_UNWINDING, lock_while_unwinding);
2337     let inouts: Vec<bool> = args.iter().map(is_inout_arg).collect();
2338     FcallArgs::new(
2339         flags,
2340         num_rets,
2341         inouts,
2342         async_eager_label,
2343         num_args,
2344         context,
2345     )
2348 fn is_inout_arg(e: &tast::Expr) -> bool {
2349     e.1.as_callconv().map_or(false, |cc| cc.0.is_pinout())
2352 fn has_inout_arg(es: &[tast::Expr]) -> bool {
2353     es.iter().any(is_inout_arg)
2356 fn emit_special_function(
2357     e: &mut Emitter,
2358     env: &Env,
2359     pos: &Pos,
2360     args: &[tast::Expr],
2361     uarg: Option<&tast::Expr>,
2362     lower_fq_name: &str,
2363 ) -> Result<Option<InstrSeq>> {
2364     use tast::{Expr as E, Expr_ as E_};
2365     let nargs = args.len() + uarg.map_or(0, |_| 1);
2366     let fun_and_clsmeth_disabled = e
2367         .options()
2368         .hhvm
2369         .hack_lang
2370         .flags
2371         .contains(LangFlags::DISALLOW_FUN_AND_CLS_METH_PSEUDO_FUNCS);
2372     match (lower_fq_name, args) {
2373         (id, _) if id == special_functions::ECHO => Ok(Some(InstrSeq::gather(
2374             args.iter()
2375                 .enumerate()
2376                 .map(|(i, arg)| {
2377                     Ok(InstrSeq::gather(vec![
2378                         emit_expr(e, env, arg)?,
2379                         emit_pos(pos),
2380                         instr::print(),
2381                         if i == nargs - 1 {
2382                             instr::empty()
2383                         } else {
2384                             instr::popc()
2385                         },
2386                     ]))
2387                 })
2388                 .collect::<Result<_>>()?,
2389         ))),
2390         ("HH\\invariant", args) if args.len() >= 2 => {
2391             let l = e.label_gen_mut().next_regular();
2392             let expr_id = tast::Expr(
2393                 pos.clone(),
2394                 tast::Expr_::mk_id(ast_defs::Id(
2395                     pos.clone(),
2396                     "\\hh\\invariant_violation".into(),
2397                 )),
2398             );
2399             let call = tast::Expr(
2400                 pos.clone(),
2401                 tast::Expr_::mk_call(expr_id, vec![], args[1..].to_owned(), uarg.cloned()),
2402             );
2403             let ignored_expr = emit_ignored_expr(e, env, &Pos::make_none(), &call)?;
2404             Ok(Some(InstrSeq::gather(vec![
2405                 emit_expr(e, env, &args[0])?,
2406                 instr::jmpnz(l.clone()),
2407                 ignored_expr,
2408                 emit_fatal::emit_fatal_runtime(pos, "invariant_violation"),
2409                 instr::label(l),
2410                 instr::null(),
2411             ])))
2412         }
2413         ("HH\\sequence", &[]) => Ok(Some(instr::null())),
2414         ("HH\\sequence", args) => Ok(Some(InstrSeq::gather(
2415             args.iter()
2416                 .map(|arg| emit_expr(e, env, arg))
2417                 .collect::<Result<Vec<_>>>()?
2418                 .into_iter()
2419                 .intersperse(instr::popc())
2420                 .collect::<Vec<_>>(),
2421         ))),
2422         ("class_exists", &[ref arg1, ..])
2423         | ("trait_exists", &[ref arg1, ..])
2424         | ("interface_exists", &[ref arg1, ..])
2425             if nargs == 1 || nargs == 2 =>
2426         {
2427             let class_kind = match lower_fq_name {
2428                 "class_exists" => ClassKind::Class,
2429                 "interface_exists" => ClassKind::Interface,
2430                 "trait_exists" => ClassKind::Trait,
2431                 _ => return Err(unrecoverable("emit_special_function: class_kind")),
2432             };
2433             Ok(Some(InstrSeq::gather(vec![
2434                 emit_expr(e, env, arg1)?,
2435                 instr::cast_string(),
2436                 if nargs == 1 {
2437                     instr::true_()
2438                 } else {
2439                     InstrSeq::gather(vec![emit_expr(e, env, &args[1])?, instr::cast_bool()])
2440                 },
2441                 instr::oodeclexists(class_kind),
2442             ])))
2443         }
2444         ("exit", _) | ("die", _) if nargs == 0 || nargs == 1 => {
2445             Ok(Some(emit_exit(e, env, args.first())?))
2446         }
2447         ("HH\\fun", _) => {
2448             if fun_and_clsmeth_disabled {
2449                 match args {
2450                     [tast::Expr(_, tast::Expr_::String(func_name))] => {
2451                         Err(emit_fatal::raise_fatal_parse(
2452                             pos,
2453                             format!(
2454                                 "`fun()` is disabled; switch to first-class references like `{}<>`",
2455                                 func_name
2456                             ),
2457                         ))
2458                     }
2459                     _ => Err(emit_fatal::raise_fatal_runtime(
2460                         pos,
2461                         "Constant string expected in fun()",
2462                     )),
2463                 }
2464             } else if nargs != 1 {
2465                 Err(emit_fatal::raise_fatal_runtime(
2466                     pos,
2467                     format!(
2468                         "fun() expects exactly 1 parameter, {} given",
2469                         nargs.to_string()
2470                     ),
2471                 ))
2472             } else {
2473                 match args {
2474                     [tast::Expr(_, tast::Expr_::String(func_name))] => {
2475                         Ok(Some(emit_hh_fun(
2476                             e,
2477                             env,
2478                             pos,
2479                             &vec![ /* targs */ ],
2480                             // FIXME: This is not safe--string literals are binary strings.
2481                             // There's no guarantee that they're valid UTF-8.
2482                             unsafe { std::str::from_utf8_unchecked(func_name.as_slice()) },
2483                         )?))
2484                     }
2485                     _ => Err(emit_fatal::raise_fatal_runtime(
2486                         pos,
2487                         "Constant string expected in fun()",
2488                     )),
2489                 }
2490             }
2491         }
2492         ("__systemlib\\meth_caller", _) => {
2493             // used by meth_caller() to directly emit func ptr
2494             if nargs != 1 {
2495                 return Err(emit_fatal::raise_fatal_runtime(
2496                     pos,
2497                     format!(
2498                         "fun() expects exactly 1 parameter, {} given",
2499                         nargs.to_string()
2500                     ),
2501                 ));
2502             }
2503             match args {
2504                 [E(_, E_::String(ref func_name))] => Ok(Some(instr::resolve_meth_caller(
2505                     // TODO(hrust) should accept functions::from_raw_string(func_name)
2506                     string_utils::strip_global_ns(
2507                         // FIXME: This is not safe--string literals are binary strings.
2508                         // There's no guarantee that they're valid UTF-8.
2509                         unsafe { std::str::from_utf8_unchecked(func_name.as_slice().into()) },
2510                     )
2511                     .to_string()
2512                     .into(),
2513                 ))),
2514                 _ => Err(emit_fatal::raise_fatal_runtime(
2515                     pos,
2516                     "Constant string expected in fun()",
2517                 )),
2518             }
2519         }
2520         ("__systemlib\\__debugger_is_uninit", _) => {
2521             if nargs != 1 {
2522                 Err(emit_fatal::raise_fatal_runtime(
2523                     pos,
2524                     format!(
2525                         "__debugger_is_uninit() expects exactly 1 parameter {} given",
2526                         nargs
2527                     ),
2528                 ))
2529             } else {
2530                 match args {
2531                     [E(_, E_::Lvar(id))] => {
2532                         Ok(Some(instr::isunsetl(get_local(e, env, pos, id.name())?)))
2533                     }
2534                     _ => Err(emit_fatal::raise_fatal_runtime(
2535                         pos,
2536                         "Local variable expected in __debugger_is_uninit()",
2537                     )),
2538                 }
2539             }
2540         }
2541         ("HH\\inst_meth", _) => match args {
2542             [obj_expr, method_name] => Ok(Some(emit_inst_meth(e, env, obj_expr, method_name)?)),
2543             _ => Err(emit_fatal::raise_fatal_runtime(
2544                 pos,
2545                 format!(
2546                     "inst_meth() expects exactly 2 parameters, {} given",
2547                     nargs.to_string()
2548                 ),
2549             )),
2550         },
2551         ("HH\\class_meth", _) if fun_and_clsmeth_disabled => Err(emit_fatal::raise_fatal_parse(
2552             pos,
2553             "`class_meth()` is disabled; switch to first-class references like `C::bar<>`",
2554         )),
2555         ("HH\\class_meth", &[ref cls, ref meth, ..]) if nargs == 2 => {
2556             if meth.1.is_string() {
2557                 if cls.1.is_string()
2558                     || cls
2559                         .1
2560                         .as_class_const()
2561                         .map_or(false, |(_, (_, id))| string_utils::is_class(id))
2562                     || cls
2563                         .1
2564                         .as_id()
2565                         .map_or(false, |ast_defs::Id(_, id)| id == pseudo_consts::G__CLASS__)
2566                 {
2567                     return Ok(Some(emit_class_meth(e, env, cls, meth)?));
2568                 }
2569             }
2570             Err(emit_fatal::raise_fatal_runtime(
2571                 pos,
2572                 concat!(
2573                     "class_meth() expects a literal class name or ::class constant, ",
2574                     "followed by a constant string that refers to a static method ",
2575                     "on that class"
2576                 ),
2577             ))
2578         }
2579         ("HH\\class_meth", _) => Err(emit_fatal::raise_fatal_runtime(
2580             pos,
2581             format!(
2582                 "class_meth() expects exactly 2 parameters, {} given",
2583                 nargs.to_string()
2584             ),
2585         )),
2586         ("HH\\global_set", _) => match args {
2587             &[ref gkey, ref gvalue] => Ok(Some(InstrSeq::gather(vec![
2588                 emit_expr(e, env, gkey)?,
2589                 emit_expr(e, env, gvalue)?,
2590                 emit_pos(pos),
2591                 instr::setg(),
2592                 instr::popc(),
2593                 instr::null(),
2594             ]))),
2595             _ => Err(emit_fatal::raise_fatal_runtime(
2596                 pos,
2597                 format!(
2598                     "global_set() expects exactly 2 parameters, {} given",
2599                     nargs.to_string()
2600                 ),
2601             )),
2602         },
2603         ("HH\\global_unset", _) => match args {
2604             &[ref gkey] => Ok(Some(InstrSeq::gather(vec![
2605                 emit_expr(e, env, gkey)?,
2606                 emit_pos(pos),
2607                 instr::unsetg(),
2608                 instr::null(),
2609             ]))),
2610             _ => Err(emit_fatal::raise_fatal_runtime(
2611                 pos,
2612                 format!(
2613                     "global_unset() expects exactly 1 parameter, {} given",
2614                     nargs.to_string()
2615                 ),
2616             )),
2617         },
2618         ("__hhvm_internal_whresult", &[E(_, E_::Lvar(ref param))]) if e.systemlib() => {
2619             Ok(Some(InstrSeq::gather(vec![
2620                 instr::cgetl(local::Type::Named(local_id::get_name(&param.1).into())),
2621                 instr::whresult(),
2622             ])))
2623         }
2624         ("__hhvm_internal_getmemokeyl", &[E(_, E_::Lvar(ref param))]) if e.systemlib() => Ok(Some(
2625             instr::getmemokeyl(local::Type::Named(local_id::get_name(&param.1).into())),
2626         )),
2627         ("HH\\array_mark_legacy", _) if args.len() == 1 || args.len() == 2 => {
2628             Ok(Some(emit_array_mark_legacy(e, env, pos, args, true)?))
2629         }
2630         ("HH\\array_unmark_legacy", _) if args.len() == 1 || args.len() == 2 => {
2631             Ok(Some(emit_array_mark_legacy(e, env, pos, args, false)?))
2632         }
2633         ("HH\\tag_provenance_here", _) if args.len() == 1 || args.len() == 2 => {
2634             Ok(Some(emit_tag_provenance_here(e, env, pos, args)?))
2635         }
2636         _ => Ok(
2637             match (
2638                 args,
2639                 istype_op(e.options(), lower_fq_name),
2640                 is_isexp_op(lower_fq_name),
2641             ) {
2642                 (&[ref arg_expr], _, Some(ref h)) => {
2643                     let is_expr = emit_is(e, env, pos, &h)?;
2644                     Some(InstrSeq::gather(vec![
2645                         emit_expr(e, env, &arg_expr)?,
2646                         is_expr,
2647                     ]))
2648                 }
2649                 (&[E(_, E_::Lvar(ref arg_id))], Some(i), _)
2650                     if superglobals::is_any_global(arg_id.name()) =>
2651                 {
2652                     Some(InstrSeq::gather(vec![
2653                         emit_local(e, env, BareThisOp::NoNotice, &arg_id)?,
2654                         emit_pos(pos),
2655                         instr::istypec(i),
2656                     ]))
2657                 }
2658                 (&[E(_, E_::Lvar(ref arg_id))], Some(i), _) if !is_local_this(env, &arg_id.1) => {
2659                     Some(instr::istypel(
2660                         get_local(e, env, &arg_id.0, &(arg_id.1).1)?,
2661                         i,
2662                     ))
2663                 }
2664                 (&[ref arg_expr], Some(i), _) => Some(InstrSeq::gather(vec![
2665                     emit_expr(e, env, &arg_expr)?,
2666                     emit_pos(pos),
2667                     instr::istypec(i),
2668                 ])),
2669                 _ => match get_call_builtin_func_info(e.options(), lower_fq_name) {
2670                     Some((nargs, i)) if nargs == args.len() => {
2671                         let inner = emit_exprs(e, env, args)?;
2672                         let unmarked_inner = match lower_fq_name {
2673                             "HH\\dict" | "HH\\vec" => wrap_array_unmark_legacy(e, inner),
2674                             _ => inner,
2675                         };
2676                         let instrs =
2677                             InstrSeq::gather(vec![unmarked_inner, emit_pos(pos), instr::instr(i)]);
2678                         match lower_fq_name {
2679                             "HH\\varray" | "HH\\darray" => Some(wrap_array_mark_legacy(e, instrs)),
2680                             _ => Some(instrs),
2681                         }
2682                     }
2683                     _ => None,
2684                 },
2685             },
2686         ),
2687     }
2690 fn emit_inst_meth(
2691     e: &mut Emitter,
2692     env: &Env,
2693     obj_expr: &tast::Expr,
2694     method_name: &tast::Expr,
2695 ) -> Result {
2696     let instrs = InstrSeq::gather(vec![
2697         emit_expr(e, env, obj_expr)?,
2698         emit_expr(e, env, method_name)?,
2699         if e.options()
2700             .hhvm
2701             .flags
2702             .contains(HhvmFlags::EMIT_INST_METH_POINTERS)
2703         {
2704             instr::resolve_obj_method()
2705         } else if hack_arr_dv_arrs(e.options()) {
2706             instr::new_vec_array(2)
2707         } else {
2708             instr::new_varray(2)
2709         },
2710     ]);
2711     if e.options()
2712         .hhvm
2713         .flags
2714         .contains(HhvmFlags::EMIT_INST_METH_POINTERS)
2715     {
2716         Ok(instrs)
2717     } else {
2718         Ok(wrap_array_mark_legacy(e, instrs))
2719     }
2722 fn emit_class_meth(e: &mut Emitter, env: &Env, cls: &tast::Expr, meth: &tast::Expr) -> Result {
2723     use tast::Expr_ as E_;
2724     if e.options()
2725         .hhvm
2726         .flags
2727         .contains(HhvmFlags::EMIT_CLS_METH_POINTERS)
2728     {
2729         let method_id = match &meth.1 {
2730             E_::String(method_name) => method_name.to_string().into(), // TODO(hrust) should accept method::from_raw_string(method_name),
2731             _ => return Err(unrecoverable("emit_class_meth: unhandled method")),
2732         };
2733         if let Some((cid, (_, id))) = cls.1.as_class_const() {
2734             if string_utils::is_class(id) {
2735                 return emit_class_meth_native(
2736                     e,
2737                     env,
2738                     &cls.0,
2739                     cid,
2740                     method_id,
2741                     &vec![ /* targs */ ],
2742                 );
2743             }
2744         }
2745         if let Some(ast_defs::Id(_, s)) = cls.1.as_id() {
2746             if s == pseudo_consts::G__CLASS__ {
2747                 return Ok(instr::resolveclsmethods(SpecialClsRef::Self_, method_id));
2748             }
2749         }
2750         if let Some(class_name) = cls.1.as_string() {
2751             return Ok(instr::resolveclsmethodd(
2752                 // TODO(hrust) should accept class::Type::from_raw_string(class_name)
2753                 string_utils::strip_global_ns(
2754                     // FIXME: This is not safe--string literals are binary strings.
2755                     // There's no guarantee that they're valid UTF-8.
2756                     unsafe { std::str::from_utf8_unchecked(class_name.as_slice().into()) },
2757                 )
2758                 .to_string()
2759                 .into(),
2760                 method_id,
2761             ));
2762         }
2763         Err(unrecoverable("emit_class_meth: unhandled method"))
2764     } else {
2765         let instrs = InstrSeq::gather(vec![
2766             emit_expr(e, env, cls)?,
2767             emit_expr(e, env, meth)?,
2768             if hack_arr_dv_arrs(e.options()) {
2769                 instr::new_vec_array(2)
2770             } else {
2771                 instr::new_varray(2)
2772             },
2773         ]);
2774         Ok(wrap_array_mark_legacy(e, instrs))
2775     }
2778 fn emit_class_meth_native(
2779     e: &mut Emitter,
2780     env: &Env,
2781     pos: &Pos,
2782     cid: &tast::ClassId,
2783     method_id: MethodId,
2784     targs: &[tast::Targ],
2785 ) -> Result {
2786     let mut cexpr = ClassExpr::class_id_to_class_expr(e, false, true, &env.scope, cid);
2787     if let ClassExpr::Id(ast_defs::Id(_, name)) = &cexpr {
2788         if let Some(reified_var_cexpr) = get_reified_var_cexpr(env, pos, &name)? {
2789             cexpr = reified_var_cexpr;
2790         }
2791     }
2792     let has_generics = has_non_tparam_generics_targs(env, targs);
2793     let mut emit_generics = || -> Result {
2794         emit_reified_targs(
2795             e,
2796             env,
2797             pos,
2798             &targs.iter().map(|targ| &targ.1).collect::<Vec<_>>(),
2799         )
2800     };
2801     Ok(match cexpr {
2802         ClassExpr::Id(ast_defs::Id(_, name)) if !has_generics => {
2803             instr::resolveclsmethodd(class::Type::from_ast_name_and_mangle(&name), method_id)
2804         }
2805         ClassExpr::Id(ast_defs::Id(_, name)) => InstrSeq::gather(vec![
2806             emit_generics()?,
2807             instr::resolverclsmethodd(class::Type::from_ast_name_and_mangle(&name), method_id),
2808         ]),
2809         ClassExpr::Special(clsref) if !has_generics => instr::resolveclsmethods(clsref, method_id),
2810         ClassExpr::Special(clsref) => InstrSeq::gather(vec![
2811             emit_generics()?,
2812             instr::resolverclsmethods(clsref, method_id),
2813         ]),
2814         ClassExpr::Reified(instrs) if !has_generics => InstrSeq::gather(vec![
2815             instrs,
2816             instr::classgetc(),
2817             instr::resolveclsmethod(method_id),
2818         ]),
2819         ClassExpr::Reified(instrs) => InstrSeq::gather(vec![
2820             instrs,
2821             instr::classgetc(),
2822             emit_generics()?,
2823             instr::resolverclsmethod(method_id),
2824         ]),
2825         ClassExpr::Expr(_) => {
2826             return Err(unrecoverable(
2827                 "emit_class_meth_native: ClassExpr::Expr should be impossible",
2828             ));
2829         }
2830     })
2833 fn get_call_builtin_func_info(opts: &Options, id: impl AsRef<str>) -> Option<(usize, Instruct)> {
2834     use {Instruct::*, InstructGet::*, InstructIsset::*, InstructMisc::*, InstructOperator::*};
2835     let hack_arr_dv_arrs = hack_arr_dv_arrs(opts);
2836     match id.as_ref() {
2837         "array_key_exists" => Some((2, IMisc(AKExists))),
2838         "hphp_array_idx" => Some((3, IMisc(ArrayIdx))),
2839         "intval" => Some((1, IOp(CastInt))),
2840         "boolval" => Some((1, IOp(CastBool))),
2841         "strval" => Some((1, IOp(CastString))),
2842         "floatval" | "doubleval" => Some((1, IOp(CastDouble))),
2843         "HH\\vec" => Some((1, IOp(CastVec))),
2844         "HH\\keyset" => Some((1, IOp(CastKeyset))),
2845         "HH\\dict" => Some((1, IOp(CastDict))),
2846         "HH\\varray" => Some((
2847             1,
2848             IOp(if hack_arr_dv_arrs {
2849                 CastVec
2850             } else {
2851                 CastVArray
2852             }),
2853         )),
2854         "HH\\darray" => Some((
2855             1,
2856             IOp(if hack_arr_dv_arrs {
2857                 CastDict
2858             } else {
2859                 CastDArray
2860             }),
2861         )),
2862         "HH\\global_get" => Some((1, IGet(CGetG))),
2863         "HH\\global_isset" => Some((1, IIsset(IssetG))),
2864         _ => None,
2865     }
2868 fn emit_function_pointer(
2869     e: &mut Emitter,
2870     env: &Env,
2871     pos: &Pos,
2872     fpid: &tast::FunctionPtrId,
2873     targs: &[tast::Targ],
2874 ) -> Result {
2875     let instrs = match fpid {
2876         // This is a function name. Equivalent to HH\fun('str')
2877         tast::FunctionPtrId::FPId(id) => emit_hh_fun(e, env, pos, targs, id.name())?,
2878         // class_meth
2879         tast::FunctionPtrId::FPClassConst(cid, method_id) => {
2880             // TODO(hrust) should accept `let method_id = method::Type::from_ast_name(&(cc.1).1);`
2881             let method_id: method::Type = string_utils::strip_global_ns(&method_id.1)
2882                 .to_string()
2883                 .into();
2884             emit_class_meth_native(e, env, pos, cid, method_id, targs)?
2885         }
2886     };
2887     Ok(emit_pos_then(pos, instrs))
2890 fn emit_hh_fun(
2891     e: &mut Emitter,
2892     env: &Env,
2893     pos: &Pos,
2894     targs: &[tast::Targ],
2895     fname: &str,
2896 ) -> Result<InstrSeq> {
2897     let fname = string_utils::strip_global_ns(fname);
2898     if has_non_tparam_generics_targs(env, targs) {
2899         let generics = emit_reified_targs(
2900             e,
2901             env,
2902             pos,
2903             targs
2904                 .iter()
2905                 .map(|targ| &targ.1)
2906                 .collect::<Vec<_>>()
2907                 .as_slice(),
2908         )?;
2909         Ok(InstrSeq::gather(vec![
2910             generics,
2911             instr::resolve_rfunc(fname.to_owned().into()),
2912         ]))
2913     } else {
2914         Ok(instr::resolve_func(fname.to_owned().into()))
2915     }
2918 fn emit_is(e: &mut Emitter, env: &Env, pos: &Pos, h: &tast::Hint) -> Result {
2919     let (ts_instrs, is_static) = emit_reified_arg(e, env, pos, true, h)?;
2920     Ok(if is_static {
2921         match &*h.1 {
2922             aast_defs::Hint_::Happly(ast_defs::Id(_, id), hs)
2923                 if hs.is_empty() && string_utils::strip_hh_ns(&id) == typehints::THIS =>
2924             {
2925                 instr::islateboundcls()
2926             }
2927             _ => InstrSeq::gather(vec![
2928                 get_type_structure_for_hint(e, &[], &IndexSet::new(), h)?,
2929                 instr::is_type_structc_resolve(),
2930             ]),
2931         }
2932     } else {
2933         InstrSeq::gather(vec![ts_instrs, instr::is_type_structc_dontresolve()])
2934     })
2937 fn istype_op(opts: &Options, id: impl AsRef<str>) -> Option<IstypeOp> {
2938     let hack_arr_dv_arrs = hack_arr_dv_arrs(opts);
2939     use IstypeOp::*;
2940     match id.as_ref() {
2941         "is_int" | "is_integer" | "is_long" => Some(OpInt),
2942         "is_bool" => Some(OpBool),
2943         "is_float" | "is_real" | "is_double" => Some(OpDbl),
2944         "is_string" => Some(OpStr),
2945         "is_object" => Some(OpObj),
2946         "is_null" => Some(OpNull),
2947         "is_scalar" => Some(OpScalar),
2948         "HH\\is_keyset" => Some(OpKeyset),
2949         "HH\\is_dict" => Some(OpDict),
2950         "HH\\is_vec" => Some(OpVec),
2951         "HH\\is_varray" => Some(if hack_arr_dv_arrs { OpVec } else { OpVArray }),
2952         "HH\\is_darray" => Some(if hack_arr_dv_arrs { OpDict } else { OpDArray }),
2953         "HH\\is_any_array" => Some(OpArrLike),
2954         "HH\\is_class_meth" => Some(OpClsMeth),
2955         "HH\\is_fun" => Some(OpFunc),
2956         "HH\\is_php_array" => Some(if hack_arr_dv_arrs {
2957             OpLegacyArrLike
2958         } else {
2959             OpPHPArr
2960         }),
2961         "HH\\is_array_marked_legacy" => Some(OpLegacyArrLike),
2962         "HH\\is_class" => Some(OpClass),
2963         _ => None,
2964     }
2967 fn is_isexp_op(lower_fq_id: impl AsRef<str>) -> Option<tast::Hint> {
2968     let h = |s: &str| {
2969         Some(tast::Hint::new(
2970             Pos::make_none(),
2971             tast::Hint_::mk_happly(tast::Id(Pos::make_none(), s.into()), vec![]),
2972         ))
2973     };
2974     match lower_fq_id.as_ref() {
2975         "is_int" | "is_integer" | "is_long" => h("\\HH\\int"),
2976         "is_bool" => h("\\HH\\bool"),
2977         "is_float" | "is_real" | "is_double" => h("\\HH\\float"),
2978         "is_string" => h("\\HH\\string"),
2979         "is_null" => h("\\HH\\void"),
2980         "HH\\is_keyset" => h("\\HH\\keyset"),
2981         "HH\\is_dict" => h("\\HH\\dict"),
2982         "HH\\is_vec" => h("\\HH\\vec"),
2983         _ => None,
2984     }
2987 fn emit_eval(e: &mut Emitter, env: &Env, pos: &Pos, expr: &tast::Expr) -> Result {
2988     Ok(InstrSeq::gather(vec![
2989         emit_expr(e, env, expr)?,
2990         emit_pos(pos),
2991         instr::eval(),
2992     ]))
2995 fn has_reified_types(env: &Env) -> bool {
2996     for param in env.scope.get_tparams() {
2997         match param.reified {
2998             oxidized::ast::ReifyKind::Reified => {
2999                 return true;
3000             }
3001             _ => {}
3002         }
3003     }
3004     false
3007 fn emit_call_expr(
3008     e: &mut Emitter,
3009     env: &Env,
3010     pos: &Pos,
3011     async_eager_label: Option<Label>,
3012     (expr, targs, args, uarg): &(
3013         tast::Expr,
3014         Vec<tast::Targ>,
3015         Vec<tast::Expr>,
3016         Option<tast::Expr>,
3017     ),
3018 ) -> Result {
3019     let jit_enable_rename_function = e
3020         .options()
3021         .hhvm
3022         .flags
3023         .contains(HhvmFlags::JIT_ENABLE_RENAME_FUNCTION);
3024     use {tast::Expr as E, tast::Expr_ as E_};
3025     match (&expr.1, &args[..], uarg) {
3026         (E_::Id(id), [E(_, E_::String(data))], None) if id.1 == special_functions::HHAS_ADATA => {
3027             // FIXME: This is not safe--string literals are binary strings.
3028             // There's no guarantee that they're valid UTF-8.
3029             let v =
3030                 TypedValue::HhasAdata(unsafe { String::from_utf8_unchecked(data.clone().into()) });
3031             Ok(emit_pos_then(pos, instr::typedvalue(v)))
3032         }
3033         (E_::Id(id), _, None) if id.1 == pseudo_functions::ISSET => {
3034             emit_call_isset_exprs(e, env, pos, args)
3035         }
3036         (E_::Id(id), args, None)
3037             if id.1 == fb::IDX
3038                 && !jit_enable_rename_function
3039                 && (args.len() == 2 || args.len() == 3) =>
3040         {
3041             emit_idx(e, env, pos, args)
3042         }
3043         (E_::Id(id), [arg1], None) if id.1 == emitter_special_functions::EVAL => {
3044             emit_eval(e, env, pos, arg1)
3045         }
3046         (E_::Id(id), [arg1], None) if id.1 == emitter_special_functions::SET_FRAME_METADATA => {
3047             Ok(InstrSeq::gather(vec![
3048                 emit_expr(e, env, arg1)?,
3049                 emit_pos(pos),
3050                 instr::popl(local::Type::Named("$86metadata".into())),
3051                 instr::null(),
3052             ]))
3053         }
3054         (E_::Id(id), [], None)
3055             if id.1 == pseudo_functions::EXIT || id.1 == pseudo_functions::DIE =>
3056         {
3057             let exit = emit_exit(e, env, None)?;
3058             Ok(emit_pos_then(pos, exit))
3059         }
3060         (E_::Id(id), [arg1], None)
3061             if id.1 == pseudo_functions::EXIT || id.1 == pseudo_functions::DIE =>
3062         {
3063             let exit = emit_exit(e, env, Some(arg1))?;
3064             Ok(emit_pos_then(pos, exit))
3065         }
3066         (E_::Id(id), [], _)
3067             if id.1 == emitter_special_functions::SYSTEMLIB_REIFIED_GENERICS
3068                 && e.systemlib()
3069                 && has_reified_types(env) =>
3070         {
3071             // Rewrite __systemlib_reified_generics() to $0ReifiedGenerics,
3072             // but only in systemlib functions that take a reified generic.
3073             let lvar = E::new(
3074                 pos.clone(),
3075                 E_::Lvar(Box::new(tast::Lid(
3076                     pos.clone(),
3077                     local_id::make_unscoped(string_utils::reified::GENERICS_LOCAL_NAME),
3078                 ))),
3079             );
3080             emit_expr(e, env, &lvar)
3081         }
3082         (_, _, _) => {
3083             let instrs = emit_call(
3084                 e,
3085                 env,
3086                 pos,
3087                 expr,
3088                 targs,
3089                 args,
3090                 uarg.as_ref(),
3091                 async_eager_label,
3092             )?;
3093             Ok(emit_pos_then(pos, instrs))
3094         }
3095     }
3098 pub fn emit_reified_generic_instrs(pos: &Pos, is_fun: bool, index: usize) -> Result {
3099     let base = if is_fun {
3100         instr::basel(
3101             local::Type::Named(string_utils::reified::GENERICS_LOCAL_NAME.into()),
3102             MemberOpMode::Warn,
3103         )
3104     } else {
3105         InstrSeq::gather(vec![
3106             instr::checkthis(),
3107             instr::baseh(),
3108             instr::dim_warn_pt(
3109                 prop::from_raw_string(string_utils::reified::PROP_NAME),
3110                 ReadOnlyOp::Any,
3111             ),
3112         ])
3113     };
3114     Ok(emit_pos_then(
3115         pos,
3116         InstrSeq::gather(vec![
3117             base,
3118             instr::querym(
3119                 0,
3120                 QueryOp::CGet,
3121                 MemberKey::EI(index.try_into().unwrap(), ReadOnlyOp::Any),
3122             ),
3123         ]),
3124     ))
3127 fn emit_reified_type(env: &Env, pos: &Pos, name: &str) -> Result<InstrSeq> {
3128     emit_reified_type_opt(env, pos, name)?
3129         .ok_or_else(|| emit_fatal::raise_fatal_runtime(&Pos::make_none(), "Invalid reified param"))
3132 fn emit_reified_type_opt(env: &Env, pos: &Pos, name: &str) -> Result<Option<InstrSeq>> {
3133     let is_in_lambda = env.scope.is_in_lambda();
3134     let cget_instr = |is_fun, i| {
3135         instr::cgetl(local::Type::Named(
3136             string_utils::reified::reified_generic_captured_name(is_fun, i),
3137         ))
3138     };
3139     let check = |is_soft| -> Result<()> {
3140         if is_soft {
3141             Err(emit_fatal::raise_fatal_parse(
3142                 pos,
3143                 format!(
3144                     "{} is annotated to be a soft reified generic, it cannot be used until the __Soft annotation is removed",
3145                     name
3146                 ),
3147             ))
3148         } else {
3149             Ok(())
3150         }
3151     };
3152     let emit = |(i, is_soft), is_fun| {
3153         check(is_soft)?;
3154         Ok(Some(if is_in_lambda {
3155             cget_instr(is_fun, i)
3156         } else {
3157             emit_reified_generic_instrs(pos, is_fun, i)?
3158         }))
3159     };
3160     match is_reified_tparam(env, true, name) {
3161         Some((i, is_soft)) => emit((i, is_soft), true),
3162         None => match is_reified_tparam(env, false, name) {
3163             Some((i, is_soft)) => emit((i, is_soft), false),
3164             None => Ok(None),
3165         },
3166     }
3169 fn emit_known_class_id(e: &mut Emitter, id: &ast_defs::Id) -> InstrSeq {
3170     let cid = class::Type::from_ast_name(&id.1);
3171     let cid_string = instr::string(cid.to_raw_string());
3172     emit_symbol_refs::State::add_class(e, cid);
3173     InstrSeq::gather(vec![cid_string, instr::classgetc()])
3176 fn emit_load_class_ref(e: &mut Emitter, env: &Env, pos: &Pos, cexpr: ClassExpr) -> Result {
3177     let instrs = match cexpr {
3178         ClassExpr::Special(SpecialClsRef::Self_) => instr::self_(),
3179         ClassExpr::Special(SpecialClsRef::Static) => instr::lateboundcls(),
3180         ClassExpr::Special(SpecialClsRef::Parent) => instr::parent(),
3181         ClassExpr::Id(id) => emit_known_class_id(e, &id),
3182         ClassExpr::Expr(expr) => InstrSeq::gather(vec![
3183             emit_pos(pos),
3184             emit_expr(e, env, &expr)?,
3185             instr::classgetc(),
3186         ]),
3187         ClassExpr::Reified(instrs) => {
3188             InstrSeq::gather(vec![emit_pos(pos), instrs, instr::classgetc()])
3189         }
3190     };
3191     Ok(emit_pos_then(pos, instrs))
3194 fn emit_new(
3195     e: &mut Emitter,
3196     env: &Env,
3197     pos: &Pos,
3198     (cid, targs, args, uarg, _): &(
3199         tast::ClassId,
3200         Vec<tast::Targ>,
3201         Vec<tast::Expr>,
3202         Option<tast::Expr>,
3203         Pos,
3204     ),
3205     is_reflection_class_builtin: bool,
3206 ) -> Result {
3207     if has_inout_arg(args) {
3208         return Err(unrecoverable("Unexpected inout arg in new expr"));
3209     }
3210     let resolve_self = match &cid.1.as_ciexpr() {
3211         Some(ci_expr) => match ci_expr.as_id() {
3212             Some(ast_defs::Id(_, n)) if string_utils::is_self(n) => env
3213                 .scope
3214                 .get_class_tparams()
3215                 .iter()
3216                 .all(|tp| tp.reified.is_erased()),
3217             Some(ast_defs::Id(_, n)) if string_utils::is_parent(n) => {
3218                 env.scope
3219                     .get_class()
3220                     .map_or(true, |cls| match cls.get_extends() {
3221                         [h, ..] => {
3222                             h.1.as_happly()
3223                                 .map_or(true, |(_, l)| !has_non_tparam_generics(env, l))
3224                         }
3225                         _ => true,
3226                     })
3227             }
3228             _ => true,
3229         },
3230         _ => true,
3231     };
3232     use HasGenericsOp as H;
3233     let cexpr = ClassExpr::class_id_to_class_expr(e, false, resolve_self, &env.scope, cid);
3234     let (cexpr, has_generics) = match &cexpr {
3235         ClassExpr::Id(ast_defs::Id(_, name)) => match emit_reified_type_opt(env, pos, name)? {
3236             Some(instrs) => {
3237                 if targs.is_empty() {
3238                     (ClassExpr::Reified(instrs), H::MaybeGenerics)
3239                 } else {
3240                     return Err(emit_fatal::raise_fatal_parse(
3241                         pos,
3242                         "Cannot have higher kinded reified generics",
3243                     ));
3244                 }
3245             }
3246             None if !has_non_tparam_generics_targs(env, targs) => (cexpr, H::NoGenerics),
3247             None => (cexpr, H::HasGenerics),
3248         },
3249         _ => (cexpr, H::NoGenerics),
3250     };
3251     if is_reflection_class_builtin {
3252         scope::with_unnamed_locals(e, |e| {
3253             let (instr_args, _) = emit_args_inout_setters(e, env, args)?;
3254             let instr_uargs = match uarg {
3255                 None => instr::empty(),
3256                 Some(uarg) => emit_expr(e, env, uarg)?,
3257             };
3258             Ok((
3259                 instr::empty(),
3260                 InstrSeq::gather(vec![instr_args, instr_uargs]),
3261                 instr::empty(),
3262             ))
3263         })
3264     } else {
3265         let newobj_instrs = match cexpr {
3266             ClassExpr::Id(ast_defs::Id(_, cname)) => {
3267                 let id = class::Type::from_ast_name_and_mangle(&cname);
3268                 emit_symbol_refs::State::add_class(e, id.clone());
3269                 match has_generics {
3270                     H::NoGenerics => InstrSeq::gather(vec![emit_pos(pos), instr::newobjd(id)]),
3271                     H::HasGenerics => InstrSeq::gather(vec![
3272                         emit_pos(pos),
3273                         emit_reified_targs(
3274                             e,
3275                             env,
3276                             pos,
3277                             &targs.iter().map(|t| &t.1).collect::<Vec<_>>(),
3278                         )?,
3279                         instr::newobjrd(id),
3280                     ]),
3281                     H::MaybeGenerics => {
3282                         return Err(unrecoverable(
3283                             "Internal error: This case should have been transformed",
3284                         ));
3285                     }
3286                 }
3287             }
3288             ClassExpr::Special(cls_ref) => {
3289                 InstrSeq::gather(vec![emit_pos(pos), instr::newobjs(cls_ref)])
3290             }
3291             ClassExpr::Reified(instrs) if has_generics == H::MaybeGenerics => {
3292                 InstrSeq::gather(vec![instrs, instr::classgetts(), instr::newobjr()])
3293             }
3294             _ => InstrSeq::gather(vec![
3295                 emit_load_class_ref(e, env, pos, cexpr)?,
3296                 instr::newobj(),
3297             ]),
3298         };
3299         scope::with_unnamed_locals(e, |e| {
3300             let (instr_args, _) = emit_args_inout_setters(e, env, args)?;
3301             let instr_uargs = match uarg {
3302                 None => instr::empty(),
3303                 Some(uarg) => emit_expr(e, env, uarg)?,
3304             };
3305             Ok((
3306                 instr::empty(),
3307                 InstrSeq::gather(vec![
3308                     newobj_instrs,
3309                     instr::dup(),
3310                     instr::nulluninit(),
3311                     instr_args,
3312                     instr_uargs,
3313                     emit_pos(pos),
3314                     instr::fcallctor(get_fcall_args(
3315                         args,
3316                         uarg.as_ref(),
3317                         None,
3318                         env.call_context.clone(),
3319                         true,
3320                     )),
3321                     instr::popc(),
3322                     instr::lockobj(),
3323                 ]),
3324                 instr::empty(),
3325             ))
3326         })
3327     }
3330 fn emit_obj_get(
3331     e: &mut Emitter,
3332     env: &Env,
3333     pos: &Pos,
3334     query_op: QueryOp,
3335     expr: &tast::Expr,
3336     prop: &tast::Expr,
3337     nullflavor: &ast_defs::OgNullFlavor,
3338     null_coalesce_assignment: bool,
3339 ) -> Result<(InstrSeq, Option<NumParams>)> {
3340     if let Some(tast::Lid(pos, id)) = expr.1.as_lvar() {
3341         if local_id::get_name(&id) == special_idents::THIS
3342             && nullflavor.eq(&ast_defs::OgNullFlavor::OGNullsafe)
3343         {
3344             return Err(emit_fatal::raise_fatal_parse(
3345                 pos,
3346                 "?-> is not allowed with $this",
3347             ));
3348         }
3349     }
3350     if let Some(ast_defs::Id(_, s)) = prop.1.as_id() {
3351         if string_utils::is_xhp(s) {
3352             return Ok((emit_xhp_obj_get(e, env, pos, &expr, s, nullflavor)?, None));
3353         }
3354     }
3355     let mode = if null_coalesce_assignment {
3356         MemberOpMode::Warn
3357     } else {
3358         get_querym_op_mode(&query_op)
3359     };
3360     let prop_stack_size = emit_prop_expr(e, env, nullflavor, 0, prop, null_coalesce_assignment)?.2;
3361     let (
3362         base_expr_instrs_begin,
3363         base_expr_instrs_end,
3364         base_setup_instrs,
3365         base_stack_size,
3366         cls_stack_size,
3367     ) = emit_base(
3368         e,
3369         env,
3370         expr,
3371         mode,
3372         true,
3373         BareThisOp::Notice,
3374         null_coalesce_assignment,
3375         prop_stack_size,
3376         0,
3377     )?;
3378     let (mk, prop_instrs, _) = emit_prop_expr(
3379         e,
3380         env,
3381         nullflavor,
3382         cls_stack_size,
3383         prop,
3384         null_coalesce_assignment,
3385     )?;
3386     let total_stack_size = (prop_stack_size + base_stack_size + cls_stack_size) as usize;
3387     let num_params = if null_coalesce_assignment {
3388         0
3389     } else {
3390         total_stack_size
3391     };
3392     let final_instr = instr::querym(num_params, query_op, mk);
3393     let querym_n_unpopped = if null_coalesce_assignment {
3394         Some(total_stack_size)
3395     } else {
3396         None
3397     };
3398     let instr = InstrSeq::gather(vec![
3399         base_expr_instrs_begin,
3400         prop_instrs,
3401         base_expr_instrs_end,
3402         emit_pos(pos),
3403         base_setup_instrs,
3404         final_instr,
3405     ]);
3406     Ok((instr, querym_n_unpopped))
3409 // Get the member key for a property, and return any instructions and
3410 // the size of the stack in the case that the property cannot be
3411 // placed inline in the instruction.
3412 fn emit_prop_expr(
3413     e: &mut Emitter,
3414     env: &Env,
3415     nullflavor: &ast_defs::OgNullFlavor,
3416     stack_index: StackIndex,
3417     prop: &tast::Expr,
3418     null_coalesce_assignment: bool,
3419 ) -> Result<(MemberKey, InstrSeq, StackIndex)> {
3420     let mk = match &prop.1 {
3421         tast::Expr_::Id(id) => {
3422             let ast_defs::Id(pos, name) = &**id;
3423             if name.starts_with("$") {
3424                 MemberKey::PL(get_local(e, env, pos, name)?, ReadOnlyOp::Any)
3425             } else {
3426                 // Special case for known property name
3428                 // TODO(hrust) enable `let pid = prop::Type::from_ast_name(name);`,
3429                 // `from_ast_name` should be able to accpet Cow<str>
3430                 let pid: prop::Type = string_utils::strip_global_ns(&name).to_string().into();
3431                 match nullflavor {
3432                     ast_defs::OgNullFlavor::OGNullthrows => MemberKey::PT(pid, ReadOnlyOp::Any),
3433                     ast_defs::OgNullFlavor::OGNullsafe => MemberKey::QT(pid, ReadOnlyOp::Any),
3434                 }
3435             }
3436         }
3437         // Special case for known property name
3438         tast::Expr_::String(name) => {
3439             // TODO(hrust) enable `let pid = prop::Type::from_ast_name(name);`,
3440             // `from_ast_name` should be able to accpet Cow<str>
3441             let pid: prop::Type = string_utils::strip_global_ns(
3442                 // FIXME: This is not safe--string literals are binary strings.
3443                 // There's no guarantee that they're valid UTF-8.
3444                 unsafe { std::str::from_utf8_unchecked(name.as_slice().into()) },
3445             )
3446             .to_string()
3447             .into();
3448             match nullflavor {
3449                 ast_defs::OgNullFlavor::OGNullthrows => MemberKey::PT(pid, ReadOnlyOp::Any),
3450                 ast_defs::OgNullFlavor::OGNullsafe => MemberKey::QT(pid, ReadOnlyOp::Any),
3451             }
3452         }
3453         tast::Expr_::Lvar(lid) if !(is_local_this(env, &lid.1)) => MemberKey::PL(
3454             get_local(e, env, &lid.0, local_id::get_name(&lid.1))?,
3455             ReadOnlyOp::Any,
3456         ),
3457         _ => {
3458             // General case
3459             MemberKey::PC(stack_index, ReadOnlyOp::Any)
3460         }
3461     };
3462     // For nullsafe access, insist that property is known
3463     Ok(match mk {
3464         MemberKey::PL(_, _) | MemberKey::PC(_, _)
3465             if nullflavor.eq(&ast_defs::OgNullFlavor::OGNullsafe) =>
3466         {
3467             return Err(emit_fatal::raise_fatal_parse(
3468                 &prop.0,
3469                 "?-> can only be used with scalar property names",
3470             ));
3471         }
3472         MemberKey::PC(_, _) => (mk, emit_expr(e, env, prop)?, 1),
3473         MemberKey::PL(local, ReadOnlyOp::Any) if null_coalesce_assignment => (
3474             MemberKey::PC(stack_index, ReadOnlyOp::Any),
3475             instr::cgetl(local),
3476             1,
3477         ),
3478         _ => (mk, instr::empty(), 0),
3479     })
3482 fn emit_xhp_obj_get(
3483     e: &mut Emitter,
3484     env: &Env,
3485     pos: &Pos,
3486     expr: &tast::Expr,
3487     s: &str,
3488     nullflavor: &ast_defs::OgNullFlavor,
3489 ) -> Result {
3490     use tast::Expr as E;
3491     use tast::Expr_ as E_;
3492     let f = E(
3493         pos.clone(),
3494         E_::mk_obj_get(
3495             expr.clone(),
3496             E(
3497                 pos.clone(),
3498                 E_::mk_id(ast_defs::Id(pos.clone(), "getAttribute".into())),
3499             ),
3500             nullflavor.clone(),
3501             false,
3502         ),
3503     );
3504     let args = vec![E(pos.clone(), E_::mk_string(string_utils::clean(s).into()))];
3505     emit_call(e, env, pos, &f, &[], &args[..], None, None)
3508 fn emit_array_get(
3509     e: &mut Emitter,
3510     env: &Env,
3511     outer_pos: &Pos,
3512     mode: Option<MemberOpMode>,
3513     query_op: QueryOp,
3514     base: &tast::Expr,
3515     elem: Option<&tast::Expr>,
3516     no_final: bool,
3517     null_coalesce_assignment: bool,
3518 ) -> Result<(InstrSeq, Option<usize>)> {
3519     let result = emit_array_get_(
3520         e,
3521         env,
3522         outer_pos,
3523         mode,
3524         query_op,
3525         base,
3526         elem,
3527         no_final,
3528         null_coalesce_assignment,
3529         None,
3530     )?;
3531     match result {
3532         (ArrayGetInstr::Regular(i), querym_n_unpopped) => Ok((i, querym_n_unpopped)),
3533         (ArrayGetInstr::Inout { .. }, _) => Err(unrecoverable("unexpected inout")),
3534     }
3537 fn emit_array_get_(
3538     e: &mut Emitter,
3539     env: &Env,
3540     outer_pos: &Pos,
3541     mode: Option<MemberOpMode>,
3542     query_op: QueryOp,
3543     base_expr: &tast::Expr,
3544     elem: Option<&tast::Expr>,
3545     no_final: bool,
3546     null_coalesce_assignment: bool,
3547     inout_param_info: Option<(usize, &inout_locals::AliasInfoMap)>,
3548 ) -> Result<(ArrayGetInstr, Option<usize>)> {
3549     use tast::Expr as E;
3550     match (base_expr, elem) {
3551         (E(pos, _), None) if !env.flags.contains(env::Flags::ALLOWS_ARRAY_APPEND) => Err(
3552             emit_fatal::raise_fatal_runtime(pos, "Can't use [] for reading"),
3553         ),
3554         _ => {
3555             let local_temp_kind = get_local_temp_kind(env, false, inout_param_info, elem);
3556             let mode = if null_coalesce_assignment {
3557                 MemberOpMode::Warn
3558             } else {
3559                 mode.unwrap_or(get_querym_op_mode(&query_op))
3560             };
3561             let (elem_instrs, elem_stack_size) =
3562                 emit_elem(e, env, elem, local_temp_kind, null_coalesce_assignment)?;
3563             let base_result = emit_base_(
3564                 e,
3565                 env,
3566                 base_expr,
3567                 mode,
3568                 false,
3569                 match query_op {
3570                     QueryOp::Isset => BareThisOp::NoNotice,
3571                     _ => BareThisOp::Notice,
3572                 },
3573                 null_coalesce_assignment,
3574                 elem_stack_size,
3575                 0,
3576                 inout_param_info,
3577             )?;
3578             let cls_stack_size = match &base_result {
3579                 ArrayGetBase::Regular(base) => base.cls_stack_size,
3580                 ArrayGetBase::Inout { load, .. } => load.cls_stack_size,
3581             };
3582             let (memberkey, warninstr) =
3583                 get_elem_member_key(e, env, cls_stack_size, elem, null_coalesce_assignment)?;
3584             let mut querym_n_unpopped = None;
3585             let mut make_final = |total_stack_size: StackIndex| -> InstrSeq {
3586                 if no_final {
3587                     instr::empty()
3588                 } else if null_coalesce_assignment {
3589                     querym_n_unpopped = Some(total_stack_size as usize);
3590                     instr::querym(0, query_op, memberkey.clone())
3591                 } else {
3592                     instr::querym(total_stack_size as usize, query_op, memberkey.clone())
3593                 }
3594             };
3595             let instr = match (base_result, local_temp_kind) {
3596                 (ArrayGetBase::Regular(base), None) =>
3597                 // neither base nor expression needs to store anything
3598                 {
3599                     ArrayGetInstr::Regular(InstrSeq::gather(vec![
3600                         warninstr,
3601                         base.base_instrs,
3602                         elem_instrs,
3603                         base.cls_instrs,
3604                         emit_pos(outer_pos),
3605                         base.setup_instrs,
3606                         make_final(base.base_stack_size + base.cls_stack_size + elem_stack_size),
3607                     ]))
3608                 }
3609                 (ArrayGetBase::Regular(base), Some(local_kind)) => {
3610                     // base does not need temp locals but index expression does
3611                     let local = e.local_gen_mut().get_unnamed();
3612                     let load = vec![
3613                         // load base and indexer, value of indexer will be saved in local
3614                         (
3615                             InstrSeq::gather(vec![base.base_instrs.clone(), elem_instrs]),
3616                             Some((local.clone(), local_kind)),
3617                         ),
3618                         // finish loading the value
3619                         (
3620                             InstrSeq::gather(vec![
3621                                 warninstr,
3622                                 base.base_instrs,
3623                                 emit_pos(outer_pos),
3624                                 base.setup_instrs,
3625                                 make_final(
3626                                     base.base_stack_size + base.cls_stack_size + elem_stack_size,
3627                                 ),
3628                             ]),
3629                             None,
3630                         ),
3631                     ];
3632                     let store = InstrSeq::gather(vec![
3633                         emit_store_for_simple_base(
3634                             e,
3635                             env,
3636                             outer_pos,
3637                             elem_stack_size,
3638                             base_expr,
3639                             local,
3640                             false,
3641                         )?,
3642                         instr::popc(),
3643                     ]);
3644                     ArrayGetInstr::Inout { load, store }
3645                 }
3646                 (
3647                     ArrayGetBase::Inout {
3648                         load:
3649                             ArrayGetBaseData {
3650                                 mut base_instrs,
3651                                 cls_instrs,
3652                                 setup_instrs,
3653                                 base_stack_size,
3654                                 cls_stack_size,
3655                             },
3656                         store,
3657                     },
3658                     None,
3659                 ) => {
3660                     // base needs temp locals, indexer - does not,
3661                     // simply concat two instruction sequences
3662                     base_instrs.push((
3663                         InstrSeq::gather(vec![
3664                             warninstr,
3665                             elem_instrs,
3666                             cls_instrs,
3667                             emit_pos(outer_pos),
3668                             setup_instrs,
3669                             make_final(base_stack_size + cls_stack_size + elem_stack_size),
3670                         ]),
3671                         None,
3672                     ));
3673                     let store =
3674                         InstrSeq::gather(vec![store, instr::setm(0, memberkey), instr::popc()]);
3675                     ArrayGetInstr::Inout {
3676                         load: base_instrs,
3677                         store,
3678                     }
3679                 }
3680                 (
3681                     ArrayGetBase::Inout {
3682                         load:
3683                             ArrayGetBaseData {
3684                                 mut base_instrs,
3685                                 cls_instrs,
3686                                 setup_instrs,
3687                                 base_stack_size,
3688                                 cls_stack_size,
3689                             },
3690                         store,
3691                     },
3692                     Some(local_kind),
3693                 ) => {
3694                     // both base and index need temp locals,
3695                     // create local for index value
3696                     let local = e.local_gen_mut().get_unnamed();
3697                     base_instrs.push((elem_instrs, Some((local.clone(), local_kind))));
3698                     base_instrs.push((
3699                         InstrSeq::gather(vec![
3700                             warninstr,
3701                             cls_instrs,
3702                             emit_pos(outer_pos),
3703                             setup_instrs,
3704                             make_final(base_stack_size + cls_stack_size + elem_stack_size),
3705                         ]),
3706                         None,
3707                     ));
3708                     let store = InstrSeq::gather(vec![
3709                         store,
3710                         instr::setm(0, MemberKey::EL(local, ReadOnlyOp::Any)),
3711                         instr::popc(),
3712                     ]);
3713                     ArrayGetInstr::Inout {
3714                         load: base_instrs,
3715                         store,
3716                     }
3717                 }
3718             };
3719             Ok((instr, querym_n_unpopped))
3720         }
3721     }
3724 fn is_special_class_constant_accessed_with_class_id(cname: &tast::ClassId_, id: &str) -> bool {
3725     let is_self_parent_or_static = match cname {
3726         tast::ClassId_::CIexpr(tast::Expr(_, tast::Expr_::Id(id))) => {
3727             string_utils::is_self(&id.1)
3728                 || string_utils::is_parent(&id.1)
3729                 || string_utils::is_static(&id.1)
3730         }
3731         _ => false,
3732     };
3733     string_utils::is_class(id) && !is_self_parent_or_static
3736 fn emit_elem(
3737     e: &mut Emitter,
3738     env: &Env,
3739     elem: Option<&tast::Expr>,
3740     local_temp_kind: Option<StoredValueKind>,
3741     null_coalesce_assignment: bool,
3742 ) -> Result<(InstrSeq, StackIndex)> {
3743     Ok(match elem {
3744         None => (instr::empty(), 0),
3745         Some(expr) if expr.1.is_int() || expr.1.is_string() => (instr::empty(), 0),
3746         Some(expr) => match &expr.1 {
3747             tast::Expr_::Lvar(x) if !is_local_this(env, &x.1) => {
3748                 if local_temp_kind.is_some() {
3749                     (
3750                         instr::cgetquietl(get_local(e, env, &x.0, local_id::get_name(&x.1))?),
3751                         0,
3752                     )
3753                 } else if null_coalesce_assignment {
3754                     (
3755                         instr::cgetl(get_local(e, env, &x.0, local_id::get_name(&x.1))?),
3756                         1,
3757                     )
3758                 } else {
3759                     (instr::empty(), 0)
3760                 }
3761             }
3762             tast::Expr_::ClassConst(x)
3763                 if is_special_class_constant_accessed_with_class_id(&(x.0).1, &(x.1).1) =>
3764             {
3765                 (instr::empty(), 0)
3766             }
3767             _ => (emit_expr(e, env, expr)?, 1),
3768         },
3769     })
3772 fn get_elem_member_key(
3773     e: &mut Emitter,
3774     env: &Env,
3775     stack_index: StackIndex,
3776     elem: Option<&tast::Expr>,
3777     null_coalesce_assignment: bool,
3778 ) -> Result<(MemberKey, InstrSeq)> {
3779     use tast::ClassId_ as CI_;
3780     use tast::Expr as E;
3781     use tast::Expr_ as E_;
3782     match elem {
3783         // ELement missing (so it's array append)
3784         None => Ok((MemberKey::W, instr::empty())),
3785         Some(elem_expr) => match &elem_expr.1 {
3786             // Special case for local
3787             E_::Lvar(x) if !is_local_this(env, &x.1) => Ok((
3788                 {
3789                     if null_coalesce_assignment {
3790                         MemberKey::EC(stack_index, ReadOnlyOp::Any)
3791                     } else {
3792                         MemberKey::EL(
3793                             get_local(e, env, &x.0, local_id::get_name(&x.1))?,
3794                             ReadOnlyOp::Any,
3795                         )
3796                     }
3797                 },
3798                 instr::empty(),
3799             )),
3800             // Special case for literal integer
3801             E_::Int(s) => {
3802                 match ast_constant_folder::expr_to_typed_value(e, &env.namespace, elem_expr) {
3803                     Ok(TypedValue::Int(i)) => {
3804                         Ok((MemberKey::EI(i, ReadOnlyOp::Any), instr::empty()))
3805                     }
3806                     _ => Err(Unrecoverable(format!("{} is not a valid integer index", s))),
3807                 }
3808             }
3809             // Special case for literal string
3810             E_::String(s) => {
3811                 Ok((
3812                     // FIXME: This is not safe--string literals are binary strings.
3813                     // There's no guarantee that they're valid UTF-8.
3814                     MemberKey::ET(
3815                         unsafe { String::from_utf8_unchecked(s.clone().into()) },
3816                         ReadOnlyOp::Any,
3817                     ),
3818                     instr::empty(),
3819                 ))
3820             }
3821             // Special case for class name
3822             E_::ClassConst(x)
3823                 if is_special_class_constant_accessed_with_class_id(&(x.0).1, &(x.1).1) =>
3824             {
3825                 let cname =
3826                     match (&(x.0).1, env.scope.get_class()) {
3827                         (CI_::CIself, Some(cd)) => string_utils::strip_global_ns(cd.get_name_str()),
3828                         (CI_::CIexpr(E(_, E_::Id(id))), _) => string_utils::strip_global_ns(&id.1),
3829                         (CI_::CI(id), _) => string_utils::strip_global_ns(&id.1),
3830                         _ => return Err(Unrecoverable(
3831                             "Unreachable due to is_special_class_constant_accessed_with_class_id"
3832                                 .into(),
3833                         )),
3834                     };
3835                 let fq_id = class::Type::from_ast_name(&cname).to_raw_string().into();
3836                 if e.options().emit_class_pointers() > 0 {
3837                     Ok((
3838                         MemberKey::ET(fq_id, ReadOnlyOp::Any),
3839                         instr::raise_class_string_conversion_warning(),
3840                     ))
3841                 } else {
3842                     Ok((MemberKey::ET(fq_id, ReadOnlyOp::Any), instr::empty()))
3843                 }
3844             }
3845             _ => {
3846                 // General case
3847                 Ok((MemberKey::EC(stack_index, ReadOnlyOp::Any), instr::empty()))
3848             }
3849         },
3850     }
3853 fn emit_store_for_simple_base(
3854     e: &mut Emitter,
3855     env: &Env,
3856     pos: &Pos,
3857     elem_stack_size: isize,
3858     base: &tast::Expr,
3859     local: local::Type,
3860     is_base: bool,
3861 ) -> Result {
3862     let (base_expr_instrs_begin, base_expr_instrs_end, base_setup_instrs, _, _) = emit_base(
3863         e,
3864         env,
3865         base,
3866         MemberOpMode::Define,
3867         false,
3868         BareThisOp::Notice,
3869         false,
3870         elem_stack_size,
3871         0,
3872     )?;
3873     let memberkey = MemberKey::EL(local, ReadOnlyOp::Any);
3874     Ok(InstrSeq::gather(vec![
3875         base_expr_instrs_begin,
3876         base_expr_instrs_end,
3877         emit_pos(pos),
3878         base_setup_instrs,
3879         if is_base {
3880             instr::dim(MemberOpMode::Define, memberkey)
3881         } else {
3882             instr::setm(0, memberkey)
3883         },
3884     ]))
3887 fn get_querym_op_mode(query_op: &QueryOp) -> MemberOpMode {
3888     match query_op {
3889         QueryOp::InOut => MemberOpMode::InOut,
3890         QueryOp::CGet => MemberOpMode::Warn,
3891         _ => MemberOpMode::ModeNone,
3892     }
3895 fn emit_class_get(
3896     e: &mut Emitter,
3897     env: &Env,
3898     query_op: QueryOp,
3899     cid: &tast::ClassId,
3900     prop: &tast::ClassGetExpr,
3901 ) -> Result {
3902     let cexpr = ClassExpr::class_id_to_class_expr(e, false, false, &env.scope, cid);
3903     Ok(InstrSeq::gather(vec![
3904         InstrSeq::from(emit_class_expr(e, env, cexpr, prop)?),
3905         match query_op {
3906             QueryOp::CGet => instr::cgets(ReadOnlyOp::Any),
3907             QueryOp::Isset => instr::issets(),
3908             QueryOp::CGetQuiet => return Err(Unrecoverable("emit_class_get: CGetQuiet".into())),
3909             QueryOp::InOut => return Err(Unrecoverable("emit_class_get: InOut".into())),
3910         },
3911     ]))
3914 fn emit_conditional_expr(
3915     e: &mut Emitter,
3916     env: &Env,
3917     pos: &Pos,
3918     etest: &tast::Expr,
3919     etrue: &Option<tast::Expr>,
3920     efalse: &tast::Expr,
3921 ) -> Result {
3922     Ok(match etrue.as_ref() {
3923         Some(etrue) => {
3924             let false_label = e.label_gen_mut().next_regular();
3925             let end_label = e.label_gen_mut().next_regular();
3926             let r = emit_jmpz(e, env, etest, &false_label)?;
3927             // only emit false branch if false_label is used
3928             let false_branch = if r.is_label_used {
3929                 InstrSeq::gather(vec![instr::label(false_label), emit_expr(e, env, efalse)?])
3930             } else {
3931                 instr::empty()
3932             };
3933             // only emit true branch if there is fallthrough from condition
3934             let true_branch = if r.is_fallthrough {
3935                 InstrSeq::gather(vec![
3936                     emit_expr(e, env, etrue)?,
3937                     emit_pos(pos),
3938                     instr::jmp(end_label.clone()),
3939                 ])
3940             } else {
3941                 instr::empty()
3942             };
3943             InstrSeq::gather(vec![
3944                 r.instrs,
3945                 true_branch,
3946                 false_branch,
3947                 // end_label is used to jump out of true branch so they should be emitted together
3948                 if r.is_fallthrough {
3949                     instr::label(end_label)
3950                 } else {
3951                     instr::empty()
3952                 },
3953             ])
3954         }
3955         None => {
3956             let end_label = e.label_gen_mut().next_regular();
3957             let efalse_instr = emit_expr(e, env, efalse)?;
3958             let etest_instr = emit_expr(e, env, etest)?;
3959             InstrSeq::gather(vec![
3960                 etest_instr,
3961                 instr::dup(),
3962                 instr::jmpnz(end_label.clone()),
3963                 instr::popc(),
3964                 efalse_instr,
3965                 instr::label(end_label),
3966             ])
3967         }
3968     })
3971 fn emit_local(e: &mut Emitter, env: &Env, notice: BareThisOp, lid: &aast_defs::Lid) -> Result {
3972     let tast::Lid(pos, id) = lid;
3973     let id_name = local_id::get_name(id);
3974     if superglobals::is_superglobal(id_name) {
3975         Ok(InstrSeq::gather(vec![
3976             instr::string(string_utils::locals::strip_dollar(id_name)),
3977             emit_pos(pos),
3978             instr::cgetg(),
3979         ]))
3980     } else {
3981         let local = get_local(e, env, pos, id_name)?;
3982         Ok(
3983             if is_local_this(env, id) && !env.flags.contains(EnvFlags::NEEDS_LOCAL_THIS) {
3984                 emit_pos_then(pos, instr::barethis(notice))
3985             } else {
3986                 instr::cgetl(local)
3987             },
3988         )
3989     }
3992 fn emit_class_const(
3993     e: &mut Emitter,
3994     env: &Env,
3995     pos: &Pos,
3996     cid: &tast::ClassId,
3997     id: &ast_defs::Pstring,
3998 ) -> Result {
3999     let mut cexpr = ClassExpr::class_id_to_class_expr(e, false, true, &env.scope, cid);
4000     if let ClassExpr::Id(ast_defs::Id(_, name)) = &cexpr {
4001         if let Some(reified_var_cexpr) = get_reified_var_cexpr(env, pos, &name)? {
4002             cexpr = reified_var_cexpr;
4003         }
4004     }
4005     match cexpr {
4006         ClassExpr::Id(ast_defs::Id(pos, name)) => {
4007             let cid = class::Type::from_ast_name_and_mangle(&name);
4008             let cname = cid.to_raw_string();
4009             Ok(if string_utils::is_class(&id.1) {
4010                 if e.options().emit_class_pointers() == 1 {
4011                     emit_pos_then(&pos, instr::resolveclass(cid))
4012                 } else if e.options().emit_class_pointers() == 2 {
4013                     emit_pos_then(&pos, instr::lazyclass(cid))
4014                 } else {
4015                     emit_pos_then(&pos, instr::string(cname))
4016                 }
4017             } else {
4018                 emit_symbol_refs::State::add_class(e, cid.clone());
4019                 // TODO(hrust) enabel `let const_id = r#const::Type::from_ast_name(&id.1);`,
4020                 // `from_ast_name` should be able to accpet Cow<str>
4021                 let const_id: r#const::Type =
4022                     string_utils::strip_global_ns(&id.1).to_string().into();
4023                 emit_pos_then(&pos, instr::clscnsd(const_id, cid))
4024             })
4025         }
4026         _ => {
4027             let load_const = if string_utils::is_class(&id.1) {
4028                 instr::classname()
4029             } else {
4030                 // TODO(hrust) enabel `let const_id = r#const::Type::from_ast_name(&id.1);`,
4031                 // `from_ast_name` should be able to accpet Cow<str>
4032                 let const_id: r#const::Type =
4033                     string_utils::strip_global_ns(&id.1).to_string().into();
4034                 instr::clscns(const_id)
4035             };
4036             if string_utils::is_class(&id.1) && e.options().emit_class_pointers() > 0 {
4037                 emit_load_class_ref(e, env, pos, cexpr)
4038             } else {
4039                 Ok(InstrSeq::gather(vec![
4040                     emit_load_class_ref(e, env, pos, cexpr)?,
4041                     load_const,
4042                 ]))
4043             }
4044         }
4045     }
4048 fn emit_unop(
4049     e: &mut Emitter,
4050     env: &Env,
4051     pos: &Pos,
4052     (uop, expr): &(ast_defs::Uop, tast::Expr),
4053 ) -> Result {
4054     use ast_defs::Uop as U;
4055     match uop {
4056         U::Utild | U::Unot => Ok(InstrSeq::gather(vec![
4057             emit_expr(e, env, expr)?,
4058             emit_pos_then(pos, from_unop(e.options(), uop)?),
4059         ])),
4060         U::Uplus | U::Uminus => Ok(InstrSeq::gather(vec![
4061             emit_pos(pos),
4062             instr::int(0),
4063             emit_expr(e, env, expr)?,
4064             emit_pos_then(pos, from_unop(e.options(), uop)?),
4065         ])),
4066         U::Uincr | U::Udecr | U::Upincr | U::Updecr => emit_lval_op(
4067             e,
4068             env,
4069             pos,
4070             LValOp::IncDec(unop_to_incdec_op(e.options(), uop)?),
4071             expr,
4072             None,
4073             false,
4074         ),
4075         U::Usilence => e.local_scope(|e| {
4076             let temp_local = e.local_gen_mut().get_unnamed();
4077             Ok(InstrSeq::gather(vec![
4078                 emit_pos(pos),
4079                 instr::silence_start(temp_local.clone()),
4080                 {
4081                     let try_instrs = emit_expr(e, env, expr)?;
4082                     let catch_instrs = InstrSeq::gather(vec![
4083                         emit_pos(pos),
4084                         instr::silence_end(temp_local.clone()),
4085                     ]);
4086                     InstrSeq::create_try_catch(
4087                         e.label_gen_mut(),
4088                         None,
4089                         false, /* skip_throw */
4090                         try_instrs,
4091                         catch_instrs,
4092                     )
4093                 },
4094                 emit_pos(pos),
4095                 instr::silence_end(temp_local),
4096             ]))
4097         }),
4098     }
4101 fn unop_to_incdec_op(opts: &Options, op: &ast_defs::Uop) -> Result<IncdecOp> {
4102     let if_check_or = |op1, op2| Ok(if opts.check_int_overflow() { op1 } else { op2 });
4103     use {ast_defs::Uop as U, IncdecOp as I};
4104     match op {
4105         U::Uincr => if_check_or(I::PreIncO, I::PreInc),
4106         U::Udecr => if_check_or(I::PreDecO, I::PreDec),
4107         U::Upincr => if_check_or(I::PostIncO, I::PostInc),
4108         U::Updecr => if_check_or(I::PostDecO, I::PostDec),
4109         _ => Err(Unrecoverable("invalid incdec op".into())),
4110     }
4113 fn from_unop(opts: &Options, op: &ast_defs::Uop) -> Result {
4114     use ast_defs::Uop as U;
4115     Ok(match op {
4116         U::Utild => instr::bitnot(),
4117         U::Unot => instr::not(),
4118         U::Uplus => {
4119             if opts.check_int_overflow() {
4120                 instr::addo()
4121             } else {
4122                 instr::add()
4123             }
4124         }
4125         U::Uminus => {
4126             if opts.check_int_overflow() {
4127                 instr::subo()
4128             } else {
4129                 instr::sub()
4130             }
4131         }
4132         _ => {
4133             return Err(Unrecoverable(
4134                 "this unary operation cannot be translated".into(),
4135             ));
4136         }
4137     })
4140 fn binop_to_eqop(opts: &Options, op: &ast_defs::Bop) -> Option<EqOp> {
4141     use {ast_defs::Bop as B, EqOp::*};
4142     match op {
4143         B::Plus => Some(if opts.check_int_overflow() {
4144             PlusEqualO
4145         } else {
4146             PlusEqual
4147         }),
4148         B::Minus => Some(if opts.check_int_overflow() {
4149             MinusEqualO
4150         } else {
4151             MinusEqual
4152         }),
4153         B::Star => Some(if opts.check_int_overflow() {
4154             MulEqualO
4155         } else {
4156             MulEqual
4157         }),
4158         B::Slash => Some(DivEqual),
4159         B::Starstar => Some(PowEqual),
4160         B::Amp => Some(AndEqual),
4161         B::Bar => Some(OrEqual),
4162         B::Xor => Some(XorEqual),
4163         B::Ltlt => Some(SlEqual),
4164         B::Gtgt => Some(SrEqual),
4165         B::Percent => Some(ModEqual),
4166         B::Dot => Some(ConcatEqual),
4167         _ => None,
4168     }
4171 fn optimize_null_checks(e: &Emitter) -> bool {
4172     e.options()
4173         .hack_compiler_flags
4174         .contains(CompilerFlags::OPTIMIZE_NULL_CHECKS)
4177 fn from_binop(opts: &Options, op: &ast_defs::Bop) -> Result {
4178     use ast_defs::Bop as B;
4179     Ok(match op {
4180         B::Plus => {
4181             if opts.check_int_overflow() {
4182                 instr::addo()
4183             } else {
4184                 instr::add()
4185             }
4186         }
4187         B::Minus => {
4188             if opts.check_int_overflow() {
4189                 instr::subo()
4190             } else {
4191                 instr::sub()
4192             }
4193         }
4194         B::Star => {
4195             if opts.check_int_overflow() {
4196                 instr::mulo()
4197             } else {
4198                 instr::mul()
4199             }
4200         }
4201         B::Slash => instr::div(),
4202         B::Eqeq => instr::eq(),
4203         B::Eqeqeq => instr::same(),
4204         B::Starstar => instr::pow(),
4205         B::Diff => instr::neq(),
4206         B::Diff2 => instr::nsame(),
4207         B::Lt => instr::lt(),
4208         B::Lte => instr::lte(),
4209         B::Gt => instr::gt(),
4210         B::Gte => instr::gte(),
4211         B::Dot => instr::concat(),
4212         B::Amp => instr::bitand(),
4213         B::Bar => instr::bitor(),
4214         B::Ltlt => instr::shl(),
4215         B::Gtgt => instr::shr(),
4216         B::Cmp => instr::cmp(),
4217         B::Percent => instr::mod_(),
4218         B::Xor => instr::bitxor(),
4219         B::LogXor => instr::xor(),
4220         B::Eq(_) => return Err(Unrecoverable("assignment is emitted differently".into())),
4221         B::QuestionQuestion => {
4222             return Err(Unrecoverable(
4223                 "null coalescence is emitted differently".into(),
4224             ));
4225         }
4226         B::Barbar | B::Ampamp => {
4227             return Err(Unrecoverable(
4228                 "short-circuiting operator cannot be generated as a simple binop".into(),
4229             ));
4230         }
4231     })
4234 fn emit_first_expr(e: &mut Emitter, env: &Env, expr: &tast::Expr) -> Result<(InstrSeq, bool)> {
4235     Ok(match &expr.1 {
4236         tast::Expr_::Lvar(l)
4237             if !((is_local_this(env, &l.1) && !env.flags.contains(EnvFlags::NEEDS_LOCAL_THIS))
4238                 || superglobals::is_any_global(local_id::get_name(&l.1))) =>
4239         {
4240             (
4241                 instr::cgetl2(get_local(e, env, &l.0, local_id::get_name(&l.1))?),
4242                 true,
4243             )
4244         }
4245         _ => (emit_expr(e, env, expr)?, false),
4246     })
4249 pub fn emit_two_exprs(
4250     e: &mut Emitter,
4251     env: &Env,
4252     outer_pos: &Pos,
4253     e1: &tast::Expr,
4254     e2: &tast::Expr,
4255 ) -> Result {
4256     let (instrs1, is_under_top) = emit_first_expr(e, env, e1)?;
4257     let instrs2 = emit_expr(e, env, e2)?;
4258     let instrs2_is_var = e2.1.is_lvar();
4259     Ok(InstrSeq::gather(if is_under_top {
4260         if instrs2_is_var {
4261             vec![emit_pos(outer_pos), instrs2, instrs1]
4262         } else {
4263             vec![instrs2, emit_pos(outer_pos), instrs1]
4264         }
4265     } else if instrs2_is_var {
4266         vec![instrs1, emit_pos(outer_pos), instrs2]
4267     } else {
4268         vec![instrs1, instrs2, emit_pos(outer_pos)]
4269     }))
4272 fn emit_quiet_expr(
4273     e: &mut Emitter,
4274     env: &Env,
4275     pos: &Pos,
4276     expr: &tast::Expr,
4277     null_coalesce_assignment: bool,
4278 ) -> Result<(InstrSeq, Option<NumParams>)> {
4279     match &expr.1 {
4280         tast::Expr_::Lvar(lid) if !is_local_this(env, &lid.1) => Ok((
4281             instr::cgetquietl(get_local(e, env, pos, local_id::get_name(&lid.1))?),
4282             None,
4283         )),
4284         tast::Expr_::ArrayGet(x) => emit_array_get(
4285             e,
4286             env,
4287             pos,
4288             None,
4289             QueryOp::CGetQuiet,
4290             &x.0,
4291             x.1.as_ref(),
4292             false,
4293             null_coalesce_assignment,
4294         ),
4295         tast::Expr_::ObjGet(x) => {
4296             if x.as_ref().3 {
4297                 Ok((emit_expr(e, env, expr)?, None))
4298             } else {
4299                 emit_obj_get(
4300                     e,
4301                     env,
4302                     pos,
4303                     QueryOp::CGetQuiet,
4304                     &x.0,
4305                     &x.1,
4306                     &x.2,
4307                     null_coalesce_assignment,
4308                 )
4309             }
4310         }
4311         _ => Ok((emit_expr(e, env, expr)?, None)),
4312     }
4315 fn emit_null_coalesce_assignment(
4316     e: &mut Emitter,
4317     env: &Env,
4318     pos: &Pos,
4319     e1: &tast::Expr,
4320     e2: &tast::Expr,
4321 ) -> Result {
4322     let end_label = e.label_gen_mut().next_regular();
4323     let do_set_label = e.label_gen_mut().next_regular();
4324     let l_nonnull = e.local_gen_mut().get_unnamed();
4325     let (quiet_instr, querym_n_unpopped) = emit_quiet_expr(e, env, pos, e1, true)?;
4326     let emit_popc_n = |n_unpopped| match n_unpopped {
4327         Some(n) => InstrSeq::gather(iter::repeat(instr::popc()).take(n).collect::<Vec<_>>()),
4328         None => instr::empty(),
4329     };
4330     Ok(InstrSeq::gather(vec![
4331         quiet_instr,
4332         instr::dup(),
4333         instr::istypec(IstypeOp::OpNull),
4334         instr::jmpnz(do_set_label.clone()),
4335         instr::popl(l_nonnull.clone()),
4336         emit_popc_n(querym_n_unpopped),
4337         instr::pushl(l_nonnull),
4338         instr::jmp(end_label.clone()),
4339         instr::label(do_set_label),
4340         instr::popc(),
4341         emit_lval_op(e, env, pos, LValOp::Set, e1, Some(e2), true)?,
4342         instr::label(end_label),
4343     ]))
4346 fn emit_short_circuit_op(e: &mut Emitter, env: &Env, pos: &Pos, expr: &tast::Expr) -> Result {
4347     let its_true = e.label_gen_mut().next_regular();
4348     let its_done = e.label_gen_mut().next_regular();
4349     let jmp_instrs = emit_jmpnz(e, env, expr, &its_true)?;
4350     Ok(if jmp_instrs.is_fallthrough {
4351         InstrSeq::gather(vec![
4352             jmp_instrs.instrs,
4353             emit_pos(pos),
4354             instr::false_(),
4355             instr::jmp(its_done.clone()),
4356             if jmp_instrs.is_label_used {
4357                 InstrSeq::gather(vec![instr::label(its_true), emit_pos(pos), instr::true_()])
4358             } else {
4359                 instr::empty()
4360             },
4361             instr::label(its_done),
4362         ])
4363     } else {
4364         InstrSeq::gather(vec![
4365             jmp_instrs.instrs,
4366             if jmp_instrs.is_label_used {
4367                 InstrSeq::gather(vec![instr::label(its_true), emit_pos(pos), instr::true_()])
4368             } else {
4369                 instr::empty()
4370             },
4371         ])
4372     })
4375 fn emit_binop(e: &mut Emitter, env: &Env, pos: &Pos, expr: &tast::Expr) -> Result {
4376     let (op, e1, e2) = expr.1.as_binop().unwrap();
4377     use ast_defs::Bop as B;
4378     match op {
4379         B::Ampamp | B::Barbar => emit_short_circuit_op(e, env, pos, &expr),
4380         B::Eq(None) => emit_lval_op(e, env, pos, LValOp::Set, e1, Some(e2), false),
4381         B::Eq(Some(eop)) if eop.is_question_question() => {
4382             emit_null_coalesce_assignment(e, env, pos, e1, e2)
4383         }
4384         B::Eq(Some(eop)) => match binop_to_eqop(e.options(), eop) {
4385             None => Err(Unrecoverable("illegal eq op".into())),
4386             Some(op) => emit_lval_op(e, env, pos, LValOp::SetOp(op), e1, Some(e2), false),
4387         },
4388         B::QuestionQuestion => {
4389             let end_label = e.label_gen_mut().next_regular();
4390             let rhs = emit_expr(e, env, e2)?;
4391             Ok(InstrSeq::gather(vec![
4392                 emit_quiet_expr(e, env, pos, e1, false)?.0,
4393                 instr::dup(),
4394                 instr::istypec(IstypeOp::OpNull),
4395                 instr::not(),
4396                 instr::jmpnz(end_label.clone()),
4397                 instr::popc(),
4398                 rhs,
4399                 instr::label(end_label),
4400             ]))
4401         }
4402         _ => {
4403             let default = |e: &mut Emitter| {
4404                 Ok(InstrSeq::gather(vec![
4405                     emit_two_exprs(e, env, pos, e1, e2)?,
4406                     from_binop(e.options(), op)?,
4407                 ]))
4408             };
4409             if optimize_null_checks(e) {
4410                 match op {
4411                     B::Eqeqeq if e2.1.is_null() => emit_is_null(e, env, e1),
4412                     B::Eqeqeq if e1.1.is_null() => emit_is_null(e, env, e2),
4413                     B::Diff2 if e2.1.is_null() => Ok(InstrSeq::gather(vec![
4414                         emit_is_null(e, env, e1)?,
4415                         instr::not(),
4416                     ])),
4417                     B::Diff2 if e1.1.is_null() => Ok(InstrSeq::gather(vec![
4418                         emit_is_null(e, env, e2)?,
4419                         instr::not(),
4420                     ])),
4421                     _ => default(e),
4422                 }
4423             } else {
4424                 default(e)
4425             }
4426         }
4427     }
4430 fn emit_pipe(
4431     e: &mut Emitter,
4432     env: &Env,
4433     (_, e1, e2): &(aast_defs::Lid, tast::Expr, tast::Expr),
4434 ) -> Result {
4435     let lhs_instrs = emit_expr(e, env, e1)?;
4436     scope::with_unnamed_local(e, |e, local| {
4437         // TODO(hrust) avoid cloning env
4438         let mut pipe_env = env.clone();
4439         pipe_env.with_pipe_var(local.clone());
4440         let rhs_instrs = emit_expr(e, &pipe_env, e2)?;
4441         Ok((
4442             InstrSeq::gather(vec![lhs_instrs, instr::popl(local.clone())]),
4443             rhs_instrs,
4444             instr::unsetl(local),
4445         ))
4446     })
4449 fn emit_as(
4450     e: &mut Emitter,
4451     env: &Env,
4452     pos: &Pos,
4453     (expr, h, is_nullable): &(tast::Expr, aast_defs::Hint, bool),
4454 ) -> Result {
4455     e.local_scope(|e| {
4456         let arg_local = e.local_gen_mut().get_unnamed();
4457         let type_struct_local = e.local_gen_mut().get_unnamed();
4458         let (ts_instrs, is_static) = emit_reified_arg(e, env, pos, true, h)?;
4459         let then_label = e.label_gen_mut().next_regular();
4460         let done_label = e.label_gen_mut().next_regular();
4461         let main_block = |ts_instrs, resolve| {
4462             InstrSeq::gather(vec![
4463                 ts_instrs,
4464                 instr::setl(type_struct_local.clone()),
4465                 match resolve {
4466                     TypestructResolveOp::Resolve => instr::is_type_structc_resolve(),
4467                     TypestructResolveOp::DontResolve => instr::is_type_structc_dontresolve(),
4468                 },
4469                 instr::jmpnz(then_label.clone()),
4470                 if *is_nullable {
4471                     InstrSeq::gather(vec![instr::null(), instr::jmp(done_label.clone())])
4472                 } else {
4473                     InstrSeq::gather(vec![
4474                         instr::pushl(arg_local.clone()),
4475                         instr::pushl(type_struct_local.clone()),
4476                         instr::throwastypestructexception(),
4477                     ])
4478                 },
4479             ])
4480         };
4481         let i2 = if is_static {
4482             main_block(
4483                 get_type_structure_for_hint(e, &[], &IndexSet::new(), h)?,
4484                 TypestructResolveOp::Resolve,
4485             )
4486         } else {
4487             main_block(ts_instrs, TypestructResolveOp::DontResolve)
4488         };
4489         let i1 = emit_expr(e, env, expr)?;
4490         Ok(InstrSeq::gather(vec![
4491             i1,
4492             instr::setl(arg_local.clone()),
4493             i2,
4494             instr::label(then_label),
4495             instr::pushl(arg_local),
4496             instr::unsetl(type_struct_local),
4497             instr::label(done_label),
4498         ]))
4499     })
4502 fn emit_cast(
4503     e: &mut Emitter,
4504     env: &Env,
4505     pos: &Pos,
4506     hint: &aast_defs::Hint_,
4507     expr: &tast::Expr,
4508 ) -> Result {
4509     use aast_defs::Hint_ as H_;
4510     let op = match hint {
4511         H_::Happly(ast_defs::Id(_, id), hints) if hints.is_empty() => {
4512             let id = string_utils::strip_ns(id);
4513             match string_utils::strip_hh_ns(&id).as_ref() {
4514                 typehints::INT => instr::cast_int(),
4515                 typehints::BOOL => instr::cast_bool(),
4516                 typehints::STRING => instr::cast_string(),
4517                 typehints::FLOAT => instr::cast_double(),
4518                 _ => {
4519                     return Err(emit_fatal::raise_fatal_parse(
4520                         pos,
4521                         format!("Invalid cast type: {}", id),
4522                     ));
4523                 }
4524             }
4525         }
4526         _ => return Err(emit_fatal::raise_fatal_parse(pos, "Invalid cast type")),
4527     };
4528     Ok(InstrSeq::gather(vec![
4529         emit_expr(e, env, expr)?,
4530         emit_pos(pos),
4531         op,
4532     ]))
4535 pub fn emit_unset_expr(e: &mut Emitter, env: &Env, expr: &tast::Expr) -> Result {
4536     emit_lval_op_nonlist(
4537         e,
4538         env,
4539         &expr.0,
4540         LValOp::Unset,
4541         expr,
4542         instr::empty(),
4543         0,
4544         false,
4545     )
4548 pub fn emit_set_range_expr(
4549     e: &mut Emitter,
4550     env: &mut Env,
4551     pos: &Pos,
4552     name: &str,
4553     kind: Setrange,
4554     args: &[&tast::Expr],
4555 ) -> Result {
4556     let raise_fatal = |msg: &str| {
4557         Err(emit_fatal::raise_fatal_parse(
4558             pos,
4559             format!("{} {}", name, msg),
4560         ))
4561     };
4563     let (base, offset, src, args) = if args.len() >= 3 {
4564         (&args[0], &args[1], &args[2], &args[3..])
4565     } else {
4566         return raise_fatal("expects at least 3 arguments");
4567     };
4569     let count_instrs = match (args, kind.vec) {
4570         ([c], true) => emit_expr(e, env, c)?,
4571         ([], _) => instr::int(-1),
4572         (_, false) => return raise_fatal("expects no more than 3 arguments"),
4573         (_, true) => return raise_fatal("expects no more than 4 arguments"),
4574     };
4576     let (base_expr, cls_expr, base_setup, base_stack, cls_stack) = emit_base(
4577         e,
4578         env,
4579         base,
4580         MemberOpMode::Define,
4581         false, /* is_object */
4582         BareThisOp::Notice,
4583         false, /*null_coalesce_assignment*/
4584         3,     /* base_offset */
4585         3,     /* rhs_stack_size */
4586     )?;
4587     Ok(InstrSeq::gather(vec![
4588         base_expr,
4589         cls_expr,
4590         emit_expr(e, env, offset)?,
4591         emit_expr(e, env, src)?,
4592         count_instrs,
4593         base_setup,
4594         instr::instr(Instruct::IFinal(InstructFinal::SetRangeM(
4595             (base_stack + cls_stack)
4596                 .try_into()
4597                 .expect("StackIndex overflow"),
4598             kind.size.try_into().expect("Setrange size overflow"),
4599             kind.op,
4600             ReadOnlyOp::Any,
4601         ))),
4602     ]))
4605 pub fn is_reified_tparam(env: &Env, is_fun: bool, name: &str) -> Option<(usize, bool)> {
4606     let is = |tparams: &[tast::Tparam]| {
4607         let is_soft = |ual: &Vec<tast::UserAttribute>| {
4608             ual.iter().any(|ua| user_attributes::is_soft(&ua.name.1))
4609         };
4610         use tast::ReifyKind::*;
4611         tparams.iter().enumerate().find_map(|(i, tp)| {
4612             if (tp.reified == Reified || tp.reified == SoftReified) && tp.name.1 == name {
4613                 Some((i, is_soft(&tp.user_attributes)))
4614             } else {
4615                 None
4616             }
4617         })
4618     };
4619     if is_fun {
4620         is(env.scope.get_fun_tparams())
4621     } else {
4622         is(&env.scope.get_class_tparams()[..])
4623     }
4626 /// Emit code for a base expression `expr` that forms part of
4627 /// an element access `expr[elem]` or field access `expr->fld`.
4628 /// The instructions are divided into three sections:
4629 ///   1. base and element/property expression instructions:
4630 ///      push non-trivial base and key values on the stack
4631 ///   2. base selector instructions: a sequence of Base/Dim instructions that
4632 ///      actually constructs the base address from "member keys" that are inlined
4633 ///      in the instructions, or pulled from the key values that
4634 ///      were pushed on the stack in section 1.
4635 ///   3. (constructed by the caller) a final accessor e.g. QueryM or setter
4636 ///      e.g. SetOpM instruction that has the final key inlined in the
4637 ///      instruction, or pulled from the key values that were pushed on the
4638 ///      stack in section 1.
4639 /// The function returns a triple (base_instrs, base_setup_instrs, stack_size)
4640 /// where base_instrs is section 1 above, base_setup_instrs is section 2, and
4641 /// stack_size is the number of values pushed onto the stack by section 1.
4643 /// For example, the r-value expression $arr[3][$ix+2]
4644 /// will compile to
4645 ///   # Section 1, pushing the value of $ix+2 on the stack
4646 ///   Int 2
4647 ///   CGetL2 $ix
4648 ///   AddO
4649 ///   # Section 2, constructing the base address of $arr[3]
4650 ///   BaseL $arr Warn
4651 ///   Dim Warn EI:3
4652 ///   # Section 3, indexing the array using the value at stack position 0 (EC:0)
4653 ///   QueryM 1 CGet EC:0
4654 ///)
4655 fn emit_base(
4656     e: &mut Emitter,
4657     env: &Env,
4658     expr: &tast::Expr,
4659     mode: MemberOpMode,
4660     is_object: bool,
4661     notice: BareThisOp,
4662     null_coalesce_assignment: bool,
4663     base_offset: StackIndex,
4664     rhs_stack_size: StackIndex,
4665 ) -> Result<(InstrSeq, InstrSeq, InstrSeq, StackIndex, StackIndex)> {
4666     let result = emit_base_(
4667         e,
4668         env,
4669         expr,
4670         mode,
4671         is_object,
4672         notice,
4673         null_coalesce_assignment,
4674         base_offset,
4675         rhs_stack_size,
4676         None,
4677     )?;
4678     match result {
4679         ArrayGetBase::Regular(i) => Ok((
4680             i.base_instrs,
4681             i.cls_instrs,
4682             i.setup_instrs,
4683             i.base_stack_size as isize,
4684             i.cls_stack_size as isize,
4685         )),
4686         ArrayGetBase::Inout { .. } => Err(unrecoverable("unexpected input")),
4687     }
4690 fn is_trivial(env: &Env, is_base: bool, expr: &tast::Expr) -> bool {
4691     use tast::Expr_ as E_;
4692     match &expr.1 {
4693         E_::Int(_) | E_::String(_) => true,
4694         E_::Lvar(x) => !is_local_this(env, &x.1) || env.flags.contains(EnvFlags::NEEDS_LOCAL_THIS),
4695         E_::ArrayGet(_) if !is_base => false,
4696         E_::ArrayGet(x) => {
4697             is_trivial(env, is_base, &x.0)
4698                 && (x.1)
4699                     .as_ref()
4700                     .map_or(true, |e| is_trivial(env, is_base, &e))
4701         }
4702         _ => false,
4703     }
4706 fn get_local_temp_kind(
4707     env: &Env,
4708     is_base: bool,
4709     inout_param_info: Option<(usize, &inout_locals::AliasInfoMap)>,
4710     expr: Option<&tast::Expr>,
4711 ) -> Option<StoredValueKind> {
4712     match (expr, inout_param_info) {
4713         (_, None) => None,
4714         (Some(tast::Expr(_, tast::Expr_::Lvar(id))), Some((i, aliases)))
4715             if inout_locals::should_save_local_value(id.name(), i, aliases) =>
4716         {
4717             Some(StoredValueKind::Local)
4718         }
4719         (Some(e), _) => {
4720             if is_trivial(env, is_base, e) {
4721                 None
4722             } else {
4723                 Some(StoredValueKind::Expr)
4724             }
4725         }
4726         (None, _) => None,
4727     }
4730 fn emit_base_(
4731     e: &mut Emitter,
4732     env: &Env,
4733     expr: &tast::Expr,
4734     mode: MemberOpMode,
4735     is_object: bool,
4736     notice: BareThisOp,
4737     null_coalesce_assignment: bool,
4738     base_offset: StackIndex,
4739     rhs_stack_size: StackIndex,
4740     inout_param_info: Option<(usize, &inout_locals::AliasInfoMap)>,
4741 ) -> Result<ArrayGetBase> {
4742     let pos = &expr.0;
4743     let expr_ = &expr.1;
4744     let base_mode = if mode == MemberOpMode::InOut {
4745         MemberOpMode::Warn
4746     } else {
4747         mode
4748     };
4749     let local_temp_kind = get_local_temp_kind(env, true, inout_param_info, Some(expr));
4750     let emit_default = |
4751         e: &mut Emitter,
4752         base_instrs,
4753         cls_instrs,
4754         setup_instrs,
4755         base_stack_size,
4756         cls_stack_size,
4757     | {
4758         match local_temp_kind {
4759             Some(local_temp) => {
4760                 let local = e.local_gen_mut().get_unnamed();
4761                 ArrayGetBase::Inout {
4762                     load: ArrayGetBaseData {
4763                         base_instrs: vec![(base_instrs, Some((local.clone(), local_temp)))],
4764                         cls_instrs,
4765                         setup_instrs,
4766                         base_stack_size,
4767                         cls_stack_size,
4768                     },
4769                     store: instr::basel(local, MemberOpMode::Define),
4770                 }
4771             }
4772             _ => ArrayGetBase::Regular(ArrayGetBaseData {
4773                 base_instrs,
4774                 cls_instrs,
4775                 setup_instrs,
4776                 base_stack_size,
4777                 cls_stack_size,
4778             }),
4779         }
4780     };
4782     let emit_expr_default = |e: &mut Emitter, env, expr: &tast::Expr| -> Result<ArrayGetBase> {
4783         let base_expr_instrs = emit_expr(e, env, expr)?;
4784         Ok(emit_default(
4785             e,
4786             base_expr_instrs,
4787             instr::empty(),
4788             emit_pos_then(pos, instr::basec(base_offset, base_mode)),
4789             1,
4790             0,
4791         ))
4792     };
4794     use tast::Expr_ as E_;
4795     match expr_ {
4796         E_::Lvar(x) if superglobals::is_superglobal(&(x.1).1) => {
4797             let base_instrs = emit_pos_then(
4798                 &x.0,
4799                 instr::string(string_utils::locals::strip_dollar(&(x.1).1)),
4800             );
4802             Ok(emit_default(
4803                 e,
4804                 base_instrs,
4805                 instr::empty(),
4806                 instr::basegc(base_offset, base_mode),
4807                 1,
4808                 0,
4809             ))
4810         }
4811         E_::Lvar(x) if is_object && &(x.1).1 == special_idents::THIS => {
4812             let base_instrs = emit_pos_then(&x.0, instr::checkthis());
4813             Ok(emit_default(
4814                 e,
4815                 base_instrs,
4816                 instr::empty(),
4817                 instr::baseh(),
4818                 0,
4819                 0,
4820             ))
4821         }
4822         E_::Lvar(x)
4823             if !is_local_this(env, &x.1) || env.flags.contains(EnvFlags::NEEDS_LOCAL_THIS) =>
4824         {
4825             let v = get_local(e, env, &x.0, &(x.1).1)?;
4826             let base_instr = if local_temp_kind.is_some() {
4827                 instr::cgetquietl(v.clone())
4828             } else {
4829                 instr::empty()
4830             };
4831             Ok(emit_default(
4832                 e,
4833                 base_instr,
4834                 instr::empty(),
4835                 instr::basel(v, base_mode),
4836                 0,
4837                 0,
4838             ))
4839         }
4840         E_::Lvar(lid) => {
4841             let local = emit_local(e, env, notice, lid)?;
4842             Ok(emit_default(
4843                 e,
4844                 local,
4845                 instr::empty(),
4846                 instr::basec(base_offset, base_mode),
4847                 1,
4848                 0,
4849             ))
4850         }
4851         E_::ArrayGet(x) => match (&(x.0).1, x.1.as_ref()) {
4852             // $a[] can not be used as the base of an array get unless as an lval
4853             (_, None) if !env.flags.contains(env::Flags::ALLOWS_ARRAY_APPEND) => {
4854                 return Err(emit_fatal::raise_fatal_runtime(
4855                     pos,
4856                     "Can't use [] for reading",
4857                 ));
4858             }
4859             // base is in turn array_get - do a specific handling for inout params
4860             // if necessary
4861             (_, opt_elem_expr) => {
4862                 let base_expr = &x.0;
4863                 let local_temp_kind =
4864                     get_local_temp_kind(env, false, inout_param_info, opt_elem_expr);
4865                 let (elem_instrs, elem_stack_size) = emit_elem(
4866                     e,
4867                     env,
4868                     opt_elem_expr,
4869                     local_temp_kind,
4870                     null_coalesce_assignment,
4871                 )?;
4872                 let base_result = emit_base_(
4873                     e,
4874                     env,
4875                     base_expr,
4876                     mode,
4877                     false,
4878                     notice,
4879                     null_coalesce_assignment,
4880                     base_offset + elem_stack_size,
4881                     rhs_stack_size,
4882                     inout_param_info,
4883                 )?;
4884                 let cls_stack_size = match &base_result {
4885                     ArrayGetBase::Regular(base) => base.cls_stack_size,
4886                     ArrayGetBase::Inout { load, .. } => load.cls_stack_size,
4887                 };
4888                 let (mk, warninstr) = get_elem_member_key(
4889                     e,
4890                     env,
4891                     base_offset + cls_stack_size,
4892                     opt_elem_expr,
4893                     null_coalesce_assignment,
4894                 )?;
4895                 let make_setup_instrs = |base_setup_instrs: InstrSeq| {
4896                     InstrSeq::gather(vec![
4897                         warninstr,
4898                         base_setup_instrs,
4899                         instr::dim(mode, mk.clone()),
4900                     ])
4901                 };
4902                 Ok(match (base_result, local_temp_kind) {
4903                     // both base and index don't use temps - fallback to default handler
4904                     (ArrayGetBase::Regular(base), None) => emit_default(
4905                         e,
4906                         InstrSeq::gather(vec![base.base_instrs, elem_instrs]),
4907                         base.cls_instrs,
4908                         make_setup_instrs(base.setup_instrs),
4909                         base.base_stack_size + elem_stack_size,
4910                         base.cls_stack_size,
4911                     ),
4912                     // base does not need temps but index does
4913                     (ArrayGetBase::Regular(base), Some(local_temp)) => {
4914                         let local = e.local_gen_mut().get_unnamed();
4915                         let base_instrs = InstrSeq::gather(vec![base.base_instrs, elem_instrs]);
4916                         ArrayGetBase::Inout {
4917                             load: ArrayGetBaseData {
4918                                 // store result of instr_begin to temp
4919                                 base_instrs: vec![(base_instrs, Some((local.clone(), local_temp)))],
4920                                 cls_instrs: base.cls_instrs,
4921                                 setup_instrs: make_setup_instrs(base.setup_instrs),
4922                                 base_stack_size: base.base_stack_size + elem_stack_size,
4923                                 cls_stack_size: base.cls_stack_size,
4924                             },
4925                             store: emit_store_for_simple_base(
4926                                 e,
4927                                 env,
4928                                 pos,
4929                                 elem_stack_size,
4930                                 base_expr,
4931                                 local,
4932                                 true,
4933                             )?,
4934                         }
4935                     }
4936                     // base needs temps, index - does not
4937                     (
4938                         ArrayGetBase::Inout {
4939                             load:
4940                                 ArrayGetBaseData {
4941                                     mut base_instrs,
4942                                     cls_instrs,
4943                                     setup_instrs,
4944                                     base_stack_size,
4945                                     cls_stack_size,
4946                                 },
4947                             store,
4948                         },
4949                         None,
4950                     ) => {
4951                         base_instrs.push((elem_instrs, None));
4952                         ArrayGetBase::Inout {
4953                             load: ArrayGetBaseData {
4954                                 base_instrs,
4955                                 cls_instrs,
4956                                 setup_instrs: make_setup_instrs(setup_instrs),
4957                                 base_stack_size: base_stack_size + elem_stack_size,
4958                                 cls_stack_size,
4959                             },
4960                             store: InstrSeq::gather(vec![
4961                                 store,
4962                                 instr::dim(MemberOpMode::Define, mk),
4963                             ]),
4964                         }
4965                     }
4966                     // both base and index needs locals
4967                     (
4968                         ArrayGetBase::Inout {
4969                             load:
4970                                 ArrayGetBaseData {
4971                                     mut base_instrs,
4972                                     cls_instrs,
4973                                     setup_instrs,
4974                                     base_stack_size,
4975                                     cls_stack_size,
4976                                 },
4977                             store,
4978                         },
4979                         Some(local_kind),
4980                     ) => {
4981                         let local = e.local_gen_mut().get_unnamed();
4982                         base_instrs.push((elem_instrs, Some((local.clone(), local_kind))));
4983                         ArrayGetBase::Inout {
4984                             load: ArrayGetBaseData {
4985                                 base_instrs,
4986                                 cls_instrs,
4987                                 setup_instrs: make_setup_instrs(setup_instrs),
4988                                 base_stack_size: base_stack_size + elem_stack_size,
4989                                 cls_stack_size,
4990                             },
4991                             store: InstrSeq::gather(vec![
4992                                 store,
4993                                 instr::dim(
4994                                     MemberOpMode::Define,
4995                                     MemberKey::EL(local, ReadOnlyOp::Any),
4996                                 ),
4997                             ]),
4998                         }
4999                     }
5000                 })
5001             }
5002         },
5003         E_::ObjGet(x) => {
5004             if x.as_ref().3 {
5005                 emit_expr_default(e, env, expr)
5006             } else {
5007                 let (base_expr, prop_expr, null_flavor, _) = &**x;
5008                 Ok(match prop_expr.1.as_id() {
5009                     Some(ast_defs::Id(_, s)) if string_utils::is_xhp(&s) => {
5010                         let base_instrs =
5011                             emit_xhp_obj_get(e, env, pos, base_expr, &s, null_flavor)?;
5012                         emit_default(
5013                             e,
5014                             base_instrs,
5015                             instr::empty(),
5016                             instr::basec(base_offset, base_mode),
5017                             1,
5018                             0,
5019                         )
5020                     }
5021                     _ => {
5022                         let prop_stack_size = emit_prop_expr(
5023                             e,
5024                             env,
5025                             null_flavor,
5026                             0,
5027                             prop_expr,
5028                             null_coalesce_assignment,
5029                         )?
5030                         .2;
5031                         let (
5032                             base_expr_instrs_begin,
5033                             base_expr_instrs_end,
5034                             base_setup_instrs,
5035                             base_stack_size,
5036                             cls_stack_size,
5037                         ) = emit_base(
5038                             e,
5039                             env,
5040                             base_expr,
5041                             mode,
5042                             true,
5043                             BareThisOp::Notice,
5044                             null_coalesce_assignment,
5045                             base_offset + prop_stack_size,
5046                             rhs_stack_size,
5047                         )?;
5048                         let (mk, prop_instrs, _) = emit_prop_expr(
5049                             e,
5050                             env,
5051                             null_flavor,
5052                             base_offset + cls_stack_size,
5053                             prop_expr,
5054                             null_coalesce_assignment,
5055                         )?;
5056                         let total_stack_size = prop_stack_size + base_stack_size;
5057                         let final_instr = instr::dim(mode, mk);
5058                         emit_default(
5059                             e,
5060                             InstrSeq::gather(vec![base_expr_instrs_begin, prop_instrs]),
5061                             base_expr_instrs_end,
5062                             InstrSeq::gather(vec![base_setup_instrs, final_instr]),
5063                             total_stack_size,
5064                             cls_stack_size,
5065                         )
5066                     }
5067                 })
5068             }
5069         }
5070         E_::ClassGet(x) => {
5071             if x.2 {
5072                 emit_expr_default(e, env, expr)
5073             } else {
5074                 let (cid, prop, _) = &**x;
5075                 let cexpr = ClassExpr::class_id_to_class_expr(e, false, false, &env.scope, cid);
5076                 let (cexpr_begin, cexpr_end) = emit_class_expr(e, env, cexpr, prop)?;
5077                 Ok(emit_default(
5078                     e,
5079                     cexpr_begin,
5080                     cexpr_end,
5081                     instr::basesc(base_offset + 1, rhs_stack_size, base_mode, ReadOnlyOp::Any),
5082                     1,
5083                     1,
5084                 ))
5085             }
5086         }
5087         _ => emit_expr_default(e, env, expr),
5088     }
5091 pub fn emit_ignored_exprs(
5092     emitter: &mut Emitter,
5093     env: &Env,
5094     pos: &Pos,
5095     exprs: &[tast::Expr],
5096 ) -> Result {
5097     exprs
5098         .iter()
5099         .map(|e| emit_ignored_expr(emitter, env, pos, e))
5100         .collect::<Result<Vec<_>>>()
5101         .map(InstrSeq::gather)
5104 // TODO(hrust): change pos from &Pos to Option<&Pos>, since Pos::make_none() still allocate mem.
5105 pub fn emit_ignored_expr(emitter: &mut Emitter, env: &Env, pos: &Pos, expr: &tast::Expr) -> Result {
5106     Ok(InstrSeq::gather(vec![
5107         emit_expr(emitter, env, expr)?,
5108         emit_pos_then(pos, instr::popc()),
5109     ]))
5112 pub fn emit_lval_op(
5113     e: &mut Emitter,
5114     env: &Env,
5115     pos: &Pos,
5116     op: LValOp,
5117     expr1: &tast::Expr,
5118     expr2: Option<&tast::Expr>,
5119     null_coalesce_assignment: bool,
5120 ) -> Result {
5121     match (op, &expr1.1, expr2) {
5122         (LValOp::Set, tast::Expr_::List(l), Some(expr2)) => {
5123             let instr_rhs = emit_expr(e, env, expr2)?;
5124             let has_elements = l.iter().any(|e| !e.1.is_omitted());
5125             if !has_elements {
5126                 Ok(instr_rhs)
5127             } else {
5128                 scope::with_unnamed_local(e, |e, local| {
5129                     let loc = if can_use_as_rhs_in_list_assignment(&expr2.1)? {
5130                         Some(&local)
5131                     } else {
5132                         None
5133                     };
5134                     let (instr_lhs, instr_assign) =
5135                         emit_lval_op_list(e, env, pos, loc, &[], expr1, false)?;
5136                     Ok((
5137                         InstrSeq::gather(vec![instr_lhs, instr_rhs, instr::popl(local.clone())]),
5138                         instr_assign,
5139                         instr::pushl(local),
5140                     ))
5141                 })
5142             }
5143         }
5144         _ => e.local_scope(|e| {
5145             let (rhs_instrs, rhs_stack_size) = match expr2 {
5146                 None => (instr::empty(), 0),
5147                 Some(tast::Expr(_, tast::Expr_::Yield(af))) => {
5148                     let temp = e.local_gen_mut().get_unnamed();
5149                     (
5150                         InstrSeq::gather(vec![
5151                             emit_yield(e, env, pos, af)?,
5152                             instr::setl(temp.clone()),
5153                             instr::popc(),
5154                             instr::pushl(temp),
5155                         ]),
5156                         1,
5157                     )
5158                 }
5159                 Some(expr) => (emit_expr(e, env, expr)?, 1),
5160             };
5161             emit_lval_op_nonlist(
5162                 e,
5163                 env,
5164                 pos,
5165                 op,
5166                 expr1,
5167                 rhs_instrs,
5168                 rhs_stack_size,
5169                 null_coalesce_assignment,
5170             )
5171         }),
5172     }
5175 fn can_use_as_rhs_in_list_assignment(expr: &tast::Expr_) -> Result<bool> {
5176     use aast::Expr_ as E_;
5177     Ok(match expr {
5178         E_::Call(c)
5179             if ((c.0).1)
5180                 .as_id()
5181                 .map_or(false, |id| id.1 == special_functions::ECHO) =>
5182         {
5183             false
5184         }
5185         E_::ObjGet(o) if !o.as_ref().3 => true,
5186         E_::ClassGet(c) if !c.as_ref().2 => true,
5187         E_::Lvar(_)
5188         | E_::ArrayGet(_)
5189         | E_::Call(_)
5190         | E_::FunctionPointer(_)
5191         | E_::New(_)
5192         | E_::Record(_)
5193         | E_::Yield(_)
5194         | E_::Cast(_)
5195         | E_::Eif(_)
5196         | E_::Varray(_)
5197         | E_::Darray(_)
5198         | E_::Collection(_)
5199         | E_::Clone(_)
5200         | E_::Unop(_)
5201         | E_::As(_)
5202         | E_::Await(_)
5203         | E_::ReadonlyExpr(_)
5204         | E_::ClassConst(_) => true,
5205         E_::Pipe(p) => can_use_as_rhs_in_list_assignment(&(p.2).1)?,
5206         E_::Binop(b) => {
5207             if let ast_defs::Bop::Eq(None) = &b.0 {
5208                 if (b.1).1.is_list() {
5209                     return can_use_as_rhs_in_list_assignment(&(b.2).1);
5210                 }
5211             }
5212             b.0.is_plus() || b.0.is_question_question() || b.0.is_any_eq()
5213         }
5214         _ => false,
5215     })
5218 // Given a local $local and a list of integer array indices i_1, ..., i_n,
5219 // generate code to extract the value of $local[i_n]...[i_1]:
5220 //   BaseL $local Warn
5221 //   Dim Warn EI:i_n ...
5222 //   Dim Warn EI:i_2
5223 //   QueryM 0 CGet EI:i_1
5224 fn emit_array_get_fixed(last_usage: bool, local: local::Type, indices: &[isize]) -> InstrSeq {
5225     let (base, stack_count) = if last_usage {
5226         (
5227             InstrSeq::gather(vec![
5228                 instr::pushl(local),
5229                 instr::basec(0, MemberOpMode::Warn),
5230             ]),
5231             1,
5232         )
5233     } else {
5234         (instr::basel(local, MemberOpMode::Warn), 0)
5235     };
5236     let indices = InstrSeq::gather(
5237         indices
5238             .iter()
5239             .enumerate()
5240             .rev()
5241             .map(|(i, ix)| {
5242                 let mk = MemberKey::EI(*ix as i64, ReadOnlyOp::Any);
5243                 if i == 0 {
5244                     instr::querym(stack_count, QueryOp::CGet, mk)
5245                 } else {
5246                     instr::dim(MemberOpMode::Warn, mk)
5247                 }
5248             })
5249             .collect(),
5250     );
5251     InstrSeq::gather(vec![base, indices])
5254 // Generate code for each lvalue assignment in a list destructuring expression.
5255 // Lvalues are assigned right-to-left, regardless of the nesting structure. So
5256 //      list($a, list($b, $c)) = $d
5257 //  and list(list($a, $b), $c) = $d
5258 //  will both assign to $c, $b and $a in that order.
5259 //  Returns a pair of instructions:
5260 //  1. initialization part of the left hand side
5261 //  2. assignment
5262 //  this is necessary to handle cases like:
5263 //  list($a[$f()]) = b();
5264 //  here f() should be invoked before b()
5265 pub fn emit_lval_op_list(
5266     e: &mut Emitter,
5267     env: &Env,
5268     outer_pos: &Pos,
5269     local: Option<&local::Type>,
5270     indices: &[isize],
5271     expr: &tast::Expr,
5272     last_usage: bool,
5273 ) -> Result<(InstrSeq, InstrSeq)> {
5274     use options::Php7Flags;
5275     use tast::Expr_ as E_;
5276     let is_ltr = e.options().php7_flags.contains(Php7Flags::LTR_ASSIGN);
5277     match &expr.1 {
5278         E_::List(exprs) => {
5279             let last_non_omitted = if last_usage {
5280                 // last usage of the local will happen when processing last non-omitted
5281                 // element in the list - find it
5282                 if is_ltr {
5283                     exprs.iter().rposition(|v| !v.1.is_omitted())
5284                 } else {
5285                     // in right-to-left case result list will be reversed
5286                     // so we need to find first non-omitted expression
5287                     exprs.iter().rev().rposition(|v| !v.1.is_omitted())
5288                 }
5289             } else {
5290                 None
5291             };
5292             let (lhs_instrs, set_instrs): (Vec<InstrSeq>, Vec<InstrSeq>) = exprs
5293                 .iter()
5294                 .enumerate()
5295                 .map(|(i, expr)| {
5296                     let mut new_indices = vec![i as isize];
5297                     new_indices.extend_from_slice(indices);
5298                     emit_lval_op_list(
5299                         e,
5300                         env,
5301                         outer_pos,
5302                         local,
5303                         &new_indices[..],
5304                         expr,
5305                         last_non_omitted.map_or(false, |j| j == i),
5306                     )
5307                 })
5308                 .collect::<Result<Vec<_>>>()?
5309                 .into_iter()
5310                 .unzip();
5311             Ok((
5312                 InstrSeq::gather(lhs_instrs),
5313                 InstrSeq::gather(if !is_ltr {
5314                     set_instrs.into_iter().rev().collect()
5315                 } else {
5316                     set_instrs
5317                 }),
5318             ))
5319         }
5320         E_::Omitted => Ok((instr::empty(), instr::empty())),
5321         _ => {
5322             // Generate code to access the element from the array
5323             let access_instrs = match (local, indices) {
5324                 (Some(loc), [_, ..]) => emit_array_get_fixed(last_usage, loc.to_owned(), indices),
5325                 (Some(loc), []) => {
5326                     if last_usage {
5327                         instr::pushl(loc.to_owned())
5328                     } else {
5329                         instr::cgetl(loc.to_owned())
5330                     }
5331                 }
5332                 (None, _) => instr::null(),
5333             };
5334             // Generate code to assign to the lvalue *)
5335             // Return pair: side effects to initialize lhs + assignment
5336             let (lhs_instrs, rhs_instrs, set_op) = emit_lval_op_nonlist_steps(
5337                 e,
5338                 env,
5339                 outer_pos,
5340                 LValOp::Set,
5341                 expr,
5342                 access_instrs,
5343                 1,
5344                 false,
5345             )?;
5346             Ok(if is_ltr {
5347                 (
5348                     instr::empty(),
5349                     InstrSeq::gather(vec![lhs_instrs, rhs_instrs, set_op, instr::popc()]),
5350                 )
5351             } else {
5352                 (
5353                     lhs_instrs,
5354                     InstrSeq::gather(vec![instr::empty(), rhs_instrs, set_op, instr::popc()]),
5355                 )
5356             })
5357         }
5358     }
5361 pub fn emit_lval_op_nonlist(
5362     e: &mut Emitter,
5363     env: &Env,
5364     outer_pos: &Pos,
5365     op: LValOp,
5366     expr: &tast::Expr,
5367     rhs_instrs: InstrSeq,
5368     rhs_stack_size: isize,
5369     null_coalesce_assignment: bool,
5370 ) -> Result {
5371     emit_lval_op_nonlist_steps(
5372         e,
5373         env,
5374         outer_pos,
5375         op,
5376         expr,
5377         rhs_instrs,
5378         rhs_stack_size,
5379         null_coalesce_assignment,
5380     )
5381     .map(|(lhs, rhs, setop)| InstrSeq::gather(vec![lhs, rhs, setop]))
5384 pub fn emit_final_global_op(pos: &Pos, op: LValOp) -> InstrSeq {
5385     use LValOp as L;
5386     match op {
5387         L::Set => emit_pos_then(pos, instr::setg()),
5388         L::SetOp(op) => instr::setopg(op),
5389         L::IncDec(op) => instr::incdecg(op),
5390         L::Unset => emit_pos_then(pos, instr::unsetg()),
5391     }
5394 pub fn emit_final_local_op(pos: &Pos, op: LValOp, lid: local::Type) -> InstrSeq {
5395     use LValOp as L;
5396     emit_pos_then(
5397         pos,
5398         match op {
5399             L::Set => instr::setl(lid),
5400             L::SetOp(op) => instr::setopl(lid, op),
5401             L::IncDec(op) => instr::incdecl(lid, op),
5402             L::Unset => instr::unsetl(lid),
5403         },
5404     )
5407 fn emit_final_member_op(stack_size: usize, op: LValOp, mk: MemberKey) -> InstrSeq {
5408     use LValOp as L;
5409     match op {
5410         L::Set => instr::setm(stack_size, mk),
5411         L::SetOp(op) => instr::setopm(stack_size, op, mk),
5412         L::IncDec(op) => instr::incdecm(stack_size, op, mk),
5413         L::Unset => instr::unsetm(stack_size, mk),
5414     }
5417 fn emit_final_static_op(cid: &tast::ClassId, prop: &tast::ClassGetExpr, op: LValOp) -> Result {
5418     use LValOp as L;
5419     Ok(match op {
5420         L::Set => instr::sets(ReadOnlyOp::Any),
5421         L::SetOp(op) => instr::setops(op, ReadOnlyOp::Any),
5422         L::IncDec(op) => instr::incdecs(op, ReadOnlyOp::Any),
5423         L::Unset => {
5424             let pos = match prop {
5425                 tast::ClassGetExpr::CGstring((pos, _))
5426                 | tast::ClassGetExpr::CGexpr(tast::Expr(pos, _)) => pos,
5427             };
5428             let cid = text_of_class_id(cid);
5429             let id = text_of_prop(prop);
5430             emit_fatal::emit_fatal_runtime(
5431                 pos,
5432                 format!(
5433                     "Attempt to unset static property {}::{}",
5434                     string_utils::strip_ns(&cid),
5435                     id,
5436                 ),
5437             )
5438         }
5439     })
5442 pub fn emit_lval_op_nonlist_steps(
5443     e: &mut Emitter,
5444     env: &Env,
5445     outer_pos: &Pos,
5446     op: LValOp,
5447     expr: &tast::Expr,
5448     rhs_instrs: InstrSeq,
5449     rhs_stack_size: isize,
5450     null_coalesce_assignment: bool,
5451 ) -> Result<(InstrSeq, InstrSeq, InstrSeq)> {
5452     let f = |env: &mut Env| {
5453         use tast::Expr_ as E_;
5454         let pos = &expr.0;
5455         Ok(match &expr.1 {
5456             E_::Lvar(v) if superglobals::is_any_global(local_id::get_name(&v.1)) => (
5457                 emit_pos_then(
5458                     &v.0,
5459                     instr::string(string_utils::lstrip(local_id::get_name(&v.1), "$")),
5460                 ),
5461                 rhs_instrs,
5462                 emit_final_global_op(outer_pos, op),
5463             ),
5464             E_::Lvar(v) if is_local_this(env, &v.1) && op.is_incdec() => (
5465                 emit_local(e, env, BareThisOp::Notice, v)?,
5466                 rhs_instrs,
5467                 instr::empty(),
5468             ),
5469             E_::Lvar(v) if !is_local_this(env, &v.1) || op == LValOp::Unset => {
5470                 (instr::empty(), rhs_instrs, {
5471                     let lid = get_local(e, env, &v.0, &(v.1).1)?;
5472                     emit_final_local_op(outer_pos, op, lid)
5473                 })
5474             }
5475             E_::ArrayGet(x) => match (&(x.0).1, x.1.as_ref()) {
5476                 (_, None) if !env.flags.contains(env::Flags::ALLOWS_ARRAY_APPEND) => {
5477                     return Err(emit_fatal::raise_fatal_runtime(
5478                         pos,
5479                         "Can't use [] for reading",
5480                     ));
5481                 }
5482                 (_, opt_elem_expr) => {
5483                     let mode = match op {
5484                         LValOp::Unset => MemberOpMode::Unset,
5485                         _ => MemberOpMode::Define,
5486                     };
5487                     let (mut elem_instrs, elem_stack_size) =
5488                         emit_elem(e, env, opt_elem_expr, None, null_coalesce_assignment)?;
5489                     if null_coalesce_assignment {
5490                         elem_instrs = instr::empty();
5491                     }
5492                     let base_offset = elem_stack_size + rhs_stack_size;
5493                     let (
5494                         base_expr_instrs_begin,
5495                         base_expr_instrs_end,
5496                         base_setup_instrs,
5497                         base_stack_size,
5498                         cls_stack_size,
5499                     ) = emit_base(
5500                         e,
5501                         env,
5502                         &x.0,
5503                         mode,
5504                         false,
5505                         BareThisOp::Notice,
5506                         null_coalesce_assignment,
5507                         base_offset,
5508                         rhs_stack_size,
5509                     )?;
5510                     let (mk, warninstr) = get_elem_member_key(
5511                         e,
5512                         env,
5513                         rhs_stack_size + cls_stack_size,
5514                         opt_elem_expr,
5515                         null_coalesce_assignment,
5516                     )?;
5517                     let total_stack_size = elem_stack_size + base_stack_size + cls_stack_size;
5518                     let final_instr =
5519                         emit_pos_then(pos, emit_final_member_op(total_stack_size as usize, op, mk));
5520                     (
5521                         if null_coalesce_assignment {
5522                             elem_instrs
5523                         } else {
5524                             InstrSeq::gather(vec![
5525                                 base_expr_instrs_begin,
5526                                 elem_instrs,
5527                                 base_expr_instrs_end,
5528                             ])
5529                         },
5530                         rhs_instrs,
5531                         InstrSeq::gather(vec![
5532                             emit_pos(pos),
5533                             warninstr,
5534                             base_setup_instrs,
5535                             final_instr,
5536                         ]),
5537                     )
5538                 }
5539             },
5540             E_::ObjGet(x) if !x.as_ref().3 => {
5541                 let (e1, e2, nullflavor, _) = &**x;
5542                 if nullflavor.eq(&ast_defs::OgNullFlavor::OGNullsafe) {
5543                     return Err(emit_fatal::raise_fatal_parse(
5544                         pos,
5545                         "?-> is not allowed in write context",
5546                     ));
5547                 }
5548                 let mode = match op {
5549                     LValOp::Unset => MemberOpMode::Unset,
5550                     _ => MemberOpMode::Define,
5551                 };
5552                 let prop_stack_size =
5553                     emit_prop_expr(e, env, nullflavor, 0, e2, null_coalesce_assignment)?.2;
5554                 let base_offset = prop_stack_size + rhs_stack_size;
5555                 let (
5556                     base_expr_instrs_begin,
5557                     base_expr_instrs_end,
5558                     base_setup_instrs,
5559                     base_stack_size,
5560                     cls_stack_size,
5561                 ) = emit_base(
5562                     e,
5563                     env,
5564                     e1,
5565                     mode,
5566                     true,
5567                     BareThisOp::Notice,
5568                     null_coalesce_assignment,
5569                     base_offset,
5570                     rhs_stack_size,
5571                 )?;
5572                 let (mk, mut prop_instrs, _) = emit_prop_expr(
5573                     e,
5574                     env,
5575                     nullflavor,
5576                     rhs_stack_size + cls_stack_size,
5577                     e2,
5578                     null_coalesce_assignment,
5579                 )?;
5580                 if null_coalesce_assignment {
5581                     prop_instrs = instr::empty();
5582                 }
5583                 let total_stack_size = prop_stack_size + base_stack_size + cls_stack_size;
5584                 let final_instr =
5585                     emit_pos_then(pos, emit_final_member_op(total_stack_size as usize, op, mk));
5586                 (
5587                     if null_coalesce_assignment {
5588                         prop_instrs
5589                     } else {
5590                         InstrSeq::gather(vec![
5591                             base_expr_instrs_begin,
5592                             prop_instrs,
5593                             base_expr_instrs_end,
5594                         ])
5595                     },
5596                     rhs_instrs,
5597                     InstrSeq::gather(vec![base_setup_instrs, final_instr]),
5598                 )
5599             }
5600             E_::ClassGet(x) if !x.as_ref().2 => {
5601                 let (cid, prop, _) = &**x;
5602                 let cexpr = ClassExpr::class_id_to_class_expr(e, false, false, &env.scope, cid);
5603                 let final_instr_ = emit_final_static_op(cid, prop, op)?;
5604                 let final_instr = emit_pos_then(pos, final_instr_);
5605                 (
5606                     InstrSeq::from(emit_class_expr(e, env, cexpr, prop)?),
5607                     rhs_instrs,
5608                     final_instr,
5609                 )
5610             }
5611             E_::Unop(uop) => (
5612                 instr::empty(),
5613                 rhs_instrs,
5614                 InstrSeq::gather(vec![
5615                     emit_lval_op_nonlist(
5616                         e,
5617                         env,
5618                         pos,
5619                         op,
5620                         &uop.1,
5621                         instr::empty(),
5622                         rhs_stack_size,
5623                         false,
5624                     )?,
5625                     from_unop(e.options(), &uop.0)?,
5626                 ]),
5627             ),
5628             _ => {
5629                 return Err(emit_fatal::raise_fatal_parse(
5630                     pos,
5631                     "Can't use return value in write context",
5632                 ));
5633             }
5634         })
5635     };
5636     // TODO(shiqicao): remove clone!
5637     let mut env = env.clone();
5638     match op {
5639         LValOp::Set | LValOp::SetOp(_) | LValOp::IncDec(_) => env.with_allows_array_append(f),
5640         _ => f(&mut env),
5641     }
5644 fn emit_class_expr(
5645     e: &mut Emitter,
5646     env: &Env,
5647     cexpr: ClassExpr,
5648     prop: &tast::ClassGetExpr,
5649 ) -> Result<(InstrSeq, InstrSeq)> {
5650     let load_prop = |e: &mut Emitter| match prop {
5651         tast::ClassGetExpr::CGstring((pos, id)) => Ok(emit_pos_then(
5652             pos,
5653             instr::string(string_utils::locals::strip_dollar(id)),
5654         )),
5655         tast::ClassGetExpr::CGexpr(expr) => emit_expr(e, env, expr),
5656     };
5657     Ok(match &cexpr {
5658         ClassExpr::Expr(expr)
5659             if expr.1.is_call()
5660                 || expr.1.is_binop()
5661                 || expr.1.is_class_get()
5662                 || expr
5663                     .1
5664                     .as_lvar()
5665                     .map_or(false, |tast::Lid(_, id)| local_id::get_name(id) == "$this") =>
5666         {
5667             let cexpr_local = emit_expr(e, env, expr)?;
5668             (
5669                 instr::empty(),
5670                 InstrSeq::gather(vec![
5671                     cexpr_local,
5672                     scope::stash_top_in_unnamed_local(e, load_prop)?,
5673                     instr::classgetc(),
5674                 ]),
5675             )
5676         }
5677         _ => {
5678             let pos = match prop {
5679                 tast::ClassGetExpr::CGstring((pos, _))
5680                 | tast::ClassGetExpr::CGexpr(tast::Expr(pos, _)) => pos,
5681             };
5682             (load_prop(e)?, emit_load_class_ref(e, env, pos, cexpr)?)
5683         }
5684     })
5687 pub fn fixup_type_arg<'a>(
5688     env: &Env,
5689     isas: bool,
5690     hint: &'a tast::Hint,
5691 ) -> Result<impl AsRef<tast::Hint> + 'a> {
5692     struct Checker<'s> {
5693         erased_tparams: &'s [&'s str],
5694         isas: bool,
5695     }
5696     impl<'ast, 's> Visitor<'ast> for Checker<'s> {
5697         type P = AstParams<(), Option<Error>>;
5699         fn object(&mut self) -> &mut dyn Visitor<'ast, P = Self::P> {
5700             self
5701         }
5703         fn visit_hint_fun(
5704             &mut self,
5705             c: &mut (),
5706             hf: &tast::HintFun,
5707         ) -> StdResult<(), Option<Error>> {
5708             hf.param_tys.accept(c, self.object())?;
5709             hf.return_ty.accept(c, self.object())
5710         }
5712         fn visit_hint(&mut self, c: &mut (), h: &tast::Hint) -> StdResult<(), Option<Error>> {
5713             use tast::{Hint_ as H_, Id};
5714             match h.1.as_ref() {
5715                 H_::Happly(Id(_, id), _)
5716                     if self.erased_tparams.contains(&id.as_str()) && self.isas =>
5717                 {
5718                     return Err(Some(emit_fatal::raise_fatal_parse(
5719                         &h.0,
5720                         "Erased generics are not allowed in is/as expressions",
5721                     )));
5722                 }
5723                 H_::Haccess(_, _) => return Ok(()),
5724                 _ => {}
5725             }
5726             h.recurse(c, self.object())
5727         }
5729         fn visit_hint_(&mut self, c: &mut (), h: &tast::Hint_) -> StdResult<(), Option<Error>> {
5730             use tast::{Hint_ as H_, Id};
5731             match h {
5732                 H_::Happly(Id(_, id), _) if self.erased_tparams.contains(&id.as_str()) => Err(None),
5733                 _ => h.recurse(c, self.object()),
5734             }
5735         }
5736     }
5738     struct Updater<'s> {
5739         erased_tparams: &'s [&'s str],
5740     }
5741     impl<'ast, 's> VisitorMut<'ast> for Updater<'s> {
5742         type P = AstParams<(), ()>;
5744         fn object(&mut self) -> &mut dyn VisitorMut<'ast, P = Self::P> {
5745             self
5746         }
5748         fn visit_hint_fun(&mut self, c: &mut (), hf: &mut tast::HintFun) -> StdResult<(), ()> {
5749             <Vec<tast::Hint> as NodeMut<Self::P>>::accept(&mut hf.param_tys, c, self.object())?;
5750             <tast::Hint as NodeMut<Self::P>>::accept(&mut hf.return_ty, c, self.object())
5751         }
5753         fn visit_hint_(&mut self, c: &mut (), h: &mut tast::Hint_) -> StdResult<(), ()> {
5754             use tast::{Hint_ as H_, Id};
5755             match h {
5756                 H_::Happly(Id(_, id), _) if self.erased_tparams.contains(&id.as_str()) => {
5757                     Ok(*id = "_".into())
5758                 }
5759                 _ => h.recurse(c, self.object()),
5760             }
5761         }
5762     }
5763     let erased_tparams = get_erased_tparams(env);
5764     let erased_tparams = erased_tparams.as_slice();
5765     let mut checker = Checker {
5766         erased_tparams,
5767         isas,
5768     };
5769     match visit(&mut checker, &mut (), hint) {
5770         Ok(()) => Ok(Either::Left(hint)),
5771         Err(Some(error)) => Err(error),
5772         Err(None) => {
5773             let mut updater = Updater { erased_tparams };
5774             let mut hint = hint.clone();
5775             visit_mut(&mut updater, &mut (), &mut hint).unwrap();
5776             Ok(Either::Right(hint))
5777         }
5778     }
5781 pub fn emit_reified_arg(
5782     e: &mut Emitter,
5783     env: &Env,
5784     pos: &Pos,
5785     isas: bool,
5786     hint: &tast::Hint,
5787 ) -> Result<(InstrSeq, bool)> {
5788     struct Collector<'ast, 'a> {
5789         current_tags: &'a HashSet<&'a str>,
5790         acc: IndexSet<&'ast str>,
5791     }
5793     impl<'ast, 'a> Collector<'ast, 'a> {
5794         fn add_name(&mut self, name: &'ast str) {
5795             if self.current_tags.contains(name) && !self.acc.contains(name) {
5796                 self.acc.insert(name);
5797             }
5798         }
5799     }
5801     impl<'ast, 'a> Visitor<'ast> for Collector<'ast, 'a> {
5802         type P = AstParams<(), ()>;
5804         fn object(&mut self) -> &mut dyn Visitor<'ast, P = Self::P> {
5805             self
5806         }
5808         fn visit_hint_(&mut self, c: &mut (), h_: &'ast tast::Hint_) -> StdResult<(), ()> {
5809             use tast::{Hint_ as H_, Id};
5810             match h_ {
5811                 H_::Haccess(_, sids) => Ok(sids.iter().for_each(|Id(_, name)| self.add_name(name))),
5812                 H_::Habstr(name, h) | H_::Happly(Id(_, name), h) => {
5813                     self.add_name(name);
5814                     h.accept(c, self.object())
5815                 }
5816                 _ => h_.recurse(c, self.object()),
5817             }
5818         }
5819     }
5820     let hint = fixup_type_arg(env, isas, hint)?;
5821     let hint = hint.as_ref();
5822     fn f<'a>(mut acc: HashSet<&'a str>, tparam: &'a tast::Tparam) -> HashSet<&'a str> {
5823         if tparam.reified != tast::ReifyKind::Erased {
5824             acc.insert(&tparam.name.1);
5825         }
5826         acc
5827     }
5828     let current_tags = env
5829         .scope
5830         .get_fun_tparams()
5831         .iter()
5832         .fold(HashSet::<&str>::new(), |acc, t| f(acc, &*t));
5833     let class_tparams = env.scope.get_class_tparams();
5834     let current_tags = class_tparams
5835         .iter()
5836         .fold(current_tags, |acc, t| f(acc, &*t));
5837     let mut collector = Collector {
5838         current_tags: &current_tags,
5839         acc: IndexSet::new(),
5840     };
5841     visit(&mut collector, &mut (), hint).unwrap();
5842     match hint.1.as_ref() {
5843         tast::Hint_::Happly(tast::Id(_, name), hs)
5844             if hs.is_empty() && current_tags.contains(name.as_str()) =>
5845         {
5846             Ok((emit_reified_type(env, pos, name)?, false))
5847         }
5848         _ => {
5849             let ts = get_type_structure_for_hint(e, &[], &collector.acc, hint)?;
5850             let ts_list = if collector.acc.is_empty() {
5851                 ts
5852             } else {
5853                 let values = collector
5854                     .acc
5855                     .iter()
5856                     .map(|v| emit_reified_type(env, pos, v))
5857                     .collect::<Result<Vec<_>>>()?;
5858                 InstrSeq::gather(vec![InstrSeq::gather(values), ts])
5859             };
5860             Ok((
5861                 InstrSeq::gather(vec![
5862                     ts_list,
5863                     instr::combine_and_resolve_type_struct((collector.acc.len() + 1) as isize),
5864                 ]),
5865                 collector.acc.is_empty(),
5866             ))
5867         }
5868     }
5871 pub fn get_local(e: &mut Emitter, env: &Env, pos: &Pos, s: &str) -> Result<local::Type> {
5872     if s == special_idents::DOLLAR_DOLLAR {
5873         match &env.pipe_var {
5874             None => Err(emit_fatal::raise_fatal_runtime(
5875                 pos,
5876                 "Pipe variables must occur only in the RHS of pipe expressions",
5877             )),
5878             Some(var) => Ok(var.clone()),
5879         }
5880     } else if special_idents::is_tmp_var(s) {
5881         Ok(e.local_gen().get_unnamed_for_tempname(s).clone())
5882     } else {
5883         Ok(local::Type::Named(s.into()))
5884     }
5887 pub fn emit_is_null(e: &mut Emitter, env: &Env, expr: &tast::Expr) -> Result {
5888     if let Some(tast::Lid(pos, id)) = expr.1.as_lvar() {
5889         if !is_local_this(env, id) {
5890             return Ok(instr::istypel(
5891                 get_local(e, env, pos, local_id::get_name(id))?,
5892                 IstypeOp::OpNull,
5893             ));
5894         }
5895     }
5897     Ok(InstrSeq::gather(vec![
5898         emit_expr(e, env, expr)?,
5899         instr::istypec(IstypeOp::OpNull),
5900     ]))
5903 pub fn emit_jmpnz(
5904     e: &mut Emitter,
5905     env: &Env,
5906     expr: &tast::Expr,
5907     label: &Label,
5908 ) -> Result<EmitJmpResult> {
5909     let tast::Expr(pos, expr_) = expr;
5910     let opt = optimize_null_checks(e);
5911     Ok(
5912         match ast_constant_folder::expr_to_typed_value(e, &env.namespace, expr) {
5913             Ok(tv) => {
5914                 if Into::<bool>::into(tv) {
5915                     EmitJmpResult {
5916                         instrs: emit_pos_then(pos, instr::jmp(label.clone())),
5917                         is_fallthrough: false,
5918                         is_label_used: true,
5919                     }
5920                 } else {
5921                     EmitJmpResult {
5922                         instrs: emit_pos_then(pos, instr::empty()),
5923                         is_fallthrough: true,
5924                         is_label_used: false,
5925                     }
5926                 }
5927             }
5928             Err(_) => {
5929                 use {ast_defs::Uop as U, tast::Expr_ as E};
5930                 match expr_ {
5931                     E::Unop(uo) if uo.0 == U::Unot => emit_jmpz(e, env, &uo.1, label)?,
5932                     E::Binop(bo) if bo.0.is_barbar() => {
5933                         let r1 = emit_jmpnz(e, env, &bo.1, label)?;
5934                         if r1.is_fallthrough {
5935                             let r2 = emit_jmpnz(e, env, &bo.2, label)?;
5936                             EmitJmpResult {
5937                                 instrs: emit_pos_then(
5938                                     pos,
5939                                     InstrSeq::gather(vec![r1.instrs, r2.instrs]),
5940                                 ),
5941                                 is_fallthrough: r2.is_fallthrough,
5942                                 is_label_used: r1.is_label_used || r2.is_label_used,
5943                             }
5944                         } else {
5945                             r1
5946                         }
5947                     }
5948                     E::Binop(bo) if bo.0.is_ampamp() => {
5949                         let skip_label = e.label_gen_mut().next_regular();
5950                         let r1 = emit_jmpz(e, env, &bo.1, &skip_label)?;
5951                         if !r1.is_fallthrough {
5952                             EmitJmpResult {
5953                                 instrs: emit_pos_then(
5954                                     pos,
5955                                     InstrSeq::gather(vec![
5956                                         r1.instrs,
5957                                         InstrSeq::optional(
5958                                             r1.is_label_used,
5959                                             vec![instr::label(skip_label)],
5960                                         ),
5961                                     ]),
5962                                 ),
5963                                 is_fallthrough: r1.is_label_used,
5964                                 is_label_used: false,
5965                             }
5966                         } else {
5967                             let r2 = emit_jmpnz(e, env, &bo.2, label)?;
5968                             EmitJmpResult {
5969                                 instrs: emit_pos_then(
5970                                     pos,
5971                                     InstrSeq::gather(vec![
5972                                         r1.instrs,
5973                                         r2.instrs,
5974                                         InstrSeq::optional(
5975                                             r1.is_label_used,
5976                                             vec![instr::label(skip_label)],
5977                                         ),
5978                                     ]),
5979                                 ),
5980                                 is_fallthrough: r2.is_fallthrough || r1.is_label_used,
5981                                 is_label_used: r2.is_label_used,
5982                             }
5983                         }
5984                     }
5985                     E::Binop(bo)
5986                         if bo.0.is_eqeqeq()
5987                             && ((bo.1).1.is_null() || (bo.2).1.is_null())
5988                             && opt =>
5989                     {
5990                         let is_null =
5991                             emit_is_null(e, env, if (bo.1).1.is_null() { &bo.2 } else { &bo.1 })?;
5992                         EmitJmpResult {
5993                             instrs: emit_pos_then(
5994                                 pos,
5995                                 InstrSeq::gather(vec![is_null, instr::jmpnz(label.clone())]),
5996                             ),
5997                             is_fallthrough: true,
5998                             is_label_used: true,
5999                         }
6000                     }
6001                     E::Binop(bo)
6002                         if bo.0.is_diff2() && ((bo.1).1.is_null() || (bo.2).1.is_null()) && opt =>
6003                     {
6004                         let is_null =
6005                             emit_is_null(e, env, if (bo.1).1.is_null() { &bo.2 } else { &bo.1 })?;
6006                         EmitJmpResult {
6007                             instrs: emit_pos_then(
6008                                 pos,
6009                                 InstrSeq::gather(vec![is_null, instr::jmpz(label.clone())]),
6010                             ),
6011                             is_fallthrough: true,
6012                             is_label_used: true,
6013                         }
6014                     }
6015                     _ => {
6016                         let instr = emit_expr(e, env, expr)?;
6017                         EmitJmpResult {
6018                             instrs: emit_pos_then(
6019                                 pos,
6020                                 InstrSeq::gather(vec![instr, instr::jmpnz(label.clone())]),
6021                             ),
6022                             is_fallthrough: true,
6023                             is_label_used: true,
6024                         }
6025                     }
6026                 }
6027             }
6028         },
6029     )
6032 pub fn emit_jmpz(
6033     e: &mut Emitter,
6034     env: &Env,
6035     expr: &tast::Expr,
6036     label: &Label,
6037 ) -> Result<EmitJmpResult> {
6038     let tast::Expr(pos, expr_) = expr;
6039     let opt = optimize_null_checks(e);
6040     Ok(
6041         match ast_constant_folder::expr_to_typed_value(e, &env.namespace, expr) {
6042             Ok(v) => {
6043                 let b: bool = v.into();
6044                 if b {
6045                     EmitJmpResult {
6046                         instrs: emit_pos_then(pos, instr::empty()),
6047                         is_fallthrough: true,
6048                         is_label_used: false,
6049                     }
6050                 } else {
6051                     EmitJmpResult {
6052                         instrs: emit_pos_then(pos, instr::jmp(label.clone())),
6053                         is_fallthrough: false,
6054                         is_label_used: true,
6055                     }
6056                 }
6057             }
6058             Err(_) => {
6059                 use {ast_defs::Uop as U, tast::Expr_ as E};
6060                 match expr_ {
6061                     E::Unop(uo) if uo.0 == U::Unot => emit_jmpnz(e, env, &uo.1, label)?,
6062                     E::Binop(bo) if bo.0.is_barbar() => {
6063                         let skip_label = e.label_gen_mut().next_regular();
6064                         let r1 = emit_jmpnz(e, env, &bo.1, &skip_label)?;
6065                         if !r1.is_fallthrough {
6066                             EmitJmpResult {
6067                                 instrs: emit_pos_then(
6068                                     pos,
6069                                     InstrSeq::gather(vec![
6070                                         r1.instrs,
6071                                         InstrSeq::optional(
6072                                             r1.is_label_used,
6073                                             vec![instr::label(skip_label)],
6074                                         ),
6075                                     ]),
6076                                 ),
6077                                 is_fallthrough: r1.is_label_used,
6078                                 is_label_used: false,
6079                             }
6080                         } else {
6081                             let r2 = emit_jmpz(e, env, &bo.2, label)?;
6082                             EmitJmpResult {
6083                                 instrs: emit_pos_then(
6084                                     pos,
6085                                     InstrSeq::gather(vec![
6086                                         r1.instrs,
6087                                         r2.instrs,
6088                                         InstrSeq::optional(
6089                                             r1.is_label_used,
6090                                             vec![instr::label(skip_label)],
6091                                         ),
6092                                     ]),
6093                                 ),
6094                                 is_fallthrough: r2.is_fallthrough || r1.is_label_used,
6095                                 is_label_used: r2.is_label_used,
6096                             }
6097                         }
6098                     }
6099                     E::Binop(bo) if bo.0.is_ampamp() => {
6100                         let r1 = emit_jmpz(e, env, &bo.1, label)?;
6101                         if r1.is_fallthrough {
6102                             let r2 = emit_jmpz(e, env, &bo.2, label)?;
6103                             EmitJmpResult {
6104                                 instrs: emit_pos_then(
6105                                     pos,
6106                                     InstrSeq::gather(vec![r1.instrs, r2.instrs]),
6107                                 ),
6108                                 is_fallthrough: r2.is_fallthrough,
6109                                 is_label_used: r1.is_label_used || r2.is_label_used,
6110                             }
6111                         } else {
6112                             EmitJmpResult {
6113                                 instrs: emit_pos_then(pos, r1.instrs),
6114                                 is_fallthrough: false,
6115                                 is_label_used: r1.is_label_used,
6116                             }
6117                         }
6118                     }
6119                     E::Binop(bo)
6120                         if bo.0.is_eqeqeq()
6121                             && ((bo.1).1.is_null() || (bo.2).1.is_null())
6122                             && opt =>
6123                     {
6124                         let is_null =
6125                             emit_is_null(e, env, if (bo.1).1.is_null() { &bo.2 } else { &bo.1 })?;
6126                         EmitJmpResult {
6127                             instrs: emit_pos_then(
6128                                 pos,
6129                                 InstrSeq::gather(vec![is_null, instr::jmpz(label.clone())]),
6130                             ),
6131                             is_fallthrough: true,
6132                             is_label_used: true,
6133                         }
6134                     }
6135                     E::Binop(bo)
6136                         if bo.0.is_diff2() && ((bo.1).1.is_null() || (bo.2).1.is_null()) && opt =>
6137                     {
6138                         let is_null =
6139                             emit_is_null(e, env, if (bo.1).1.is_null() { &bo.2 } else { &bo.1 })?;
6140                         EmitJmpResult {
6141                             instrs: emit_pos_then(
6142                                 pos,
6143                                 InstrSeq::gather(vec![is_null, instr::jmpnz(label.clone())]),
6144                             ),
6145                             is_fallthrough: true,
6146                             is_label_used: true,
6147                         }
6148                     }
6149                     _ => {
6150                         let instr = emit_expr(e, env, expr)?;
6151                         EmitJmpResult {
6152                             instrs: emit_pos_then(
6153                                 pos,
6154                                 InstrSeq::gather(vec![instr, instr::jmpz(label.clone())]),
6155                             ),
6156                             is_fallthrough: true,
6157                             is_label_used: true,
6158                         }
6159                     }
6160                 }
6161             }
6162         },
6163     )