port emit_iter
[hiphop-php.git] / hphp / hack / src / hhbc / emit_expression.rs
blob0909e53d0ab180a90789ba594ed5c353bde525fa
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 #![allow(unused_variables, dead_code)]
8 use ast_constant_folder_rust as ast_constant_folder;
9 use ast_scope_rust::Scope;
10 use emit_fatal_rust as emit_fatal;
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};
14 use hhbc_ast_rust::*;
15 use hhbc_id_rust::{class, function, method, Id};
16 use hhbc_string_utils_rust as string_utils;
17 use instruction_sequence_rust::{unrecoverable, Error::Unrecoverable, InstrSeq, Result};
18 use label_rust::Label;
19 use naming_special_names_rust::{
20     emitter_special_functions, fb, pseudo_consts, pseudo_functions, special_functions,
21     special_idents, superglobals, user_attributes,
23 use options::{CompilerFlags, HhvmFlags, LangFlags, Options};
24 use oxidized::{aast, aast_defs, ast as tast, ast_defs, local_id, pos::Pos};
25 use runtime::TypedValue;
26 use scope_rust::scope;
28 use std::{collections::BTreeMap, convert::TryInto, iter};
30 #[derive(Debug)]
31 pub struct EmitJmpResult {
32     // generated instruction sequence
33     pub instrs: InstrSeq,
34     // does instruction sequence fall through
35     is_fallthrough: bool,
36     // was label associated with emit operation used
37     is_label_used: bool,
40 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
41 pub enum LValOp {
42     Set,
43     SetOp(EqOp),
44     IncDec(IncdecOp),
45     Unset,
48 impl LValOp {
49     fn is_incdec(&self) -> bool {
50         if let Self::IncDec(_) = self {
51             return true;
52         };
53         false
54     }
57 pub fn is_local_this(env: &Env, lid: &local_id::LocalId) -> bool {
58     local_id::get_name(lid) == special_idents::THIS
59         && Scope::has_this(&env.scope)
60         && !Scope::is_toplevel(&env.scope)
63 mod inout_locals {
64     use crate::*;
65     use oxidized::{aast_defs::Lid, aast_visitor, aast_visitor::Node, ast as tast, ast_defs};
66     use std::{collections::HashMap, marker::PhantomData};
68     struct AliasInfo {
69         first_inout: usize,
70         last_write: usize,
71         num_uses: usize,
72     }
74     impl Default for AliasInfo {
75         fn default() -> Self {
76             AliasInfo {
77                 first_inout: std::usize::MAX,
78                 last_write: std::usize::MIN,
79                 num_uses: 0,
80             }
81         }
82     }
84     impl AliasInfo {
85         pub fn add_inout(&mut self, i: usize) {
86             if i < self.first_inout {
87                 self.first_inout = i;
88             }
89         }
91         pub fn add_write(&mut self, i: usize) {
92             if i > self.last_write {
93                 self.last_write = i;
94             }
95         }
97         pub fn add_use(&mut self) {
98             self.num_uses += 1
99         }
101         pub fn in_range(&self, i: usize) -> bool {
102             i > self.first_inout || i <= self.last_write
103         }
105         pub fn has_single_ref(&self) -> bool {
106             self.num_uses < 2
107         }
108     }
110     type AliasInfoMap = HashMap<String, AliasInfo>;
112     fn add_write(name: String, i: usize, map: &mut AliasInfoMap) {
113         map.entry(name).or_default().add_write(i);
114     }
116     fn add_inout(name: String, i: usize, map: &mut AliasInfoMap) {
117         map.entry(name).or_default().add_inout(i);
118     }
120     fn add_use(name: String, map: &mut AliasInfoMap) {
121         map.entry(name).or_default().add_use();
122     }
124     // determines if value of a local 'name' that appear in parameter 'i'
125     // should be saved to local because it might be overwritten later
126     fn should_save_local_value(name: String, i: usize, aliases: &AliasInfoMap) -> bool {
127         aliases.get(&name).map_or(false, |alias| alias.in_range(i))
128     }
130     fn should_move_local_value(local: &local::Type, aliases: &AliasInfoMap) -> bool {
131         match local {
132             local::Type::Named(name) => aliases
133                 .get(&**name)
134                 .map_or(true, |alias| alias.has_single_ref()),
135             local::Type::Unnamed(_) => false,
136         }
137     }
139     fn collect_written_variables(env: &Env, args: &Vec<tast::Expr>) -> AliasInfoMap {
140         let mut acc = HashMap::new();
141         args.iter()
142             .enumerate()
143             .for_each(|(i, arg)| handle_arg(env, true, i, arg, &mut acc));
144         acc
145     }
147     fn handle_arg(env: &Env, is_top: bool, i: usize, arg: &tast::Expr, acc: &mut AliasInfoMap) {
148         use tast::{Expr, Expr_};
150         let Expr(_, e) = arg;
151         match e {
152             Expr_::Callconv(x) => {
153                 if let (ast_defs::ParamKind::Pinout, Expr(_, Expr_::Lvar(lid))) = &**x {
154                     let Lid(_, lid) = &**lid;
155                     if !is_local_this(env, &lid) {
156                         add_use(lid.1.to_string(), acc);
157                         if is_top {
158                             add_inout(lid.1.to_string(), i, acc);
159                         } else {
160                             add_write(lid.1.to_string(), i, acc);
161                         }
162                     }
163                 }
164             }
165             Expr_::Lvar(lid) => {
166                 let Lid(_, (_, id)) = &**lid;
167                 add_use(id.to_string(), acc);
168             }
169             _ => {
170                 // dive into argument value
171                 aast_visitor::visit(
172                     &mut Visitor {
173                         phantom: PhantomData,
174                     },
175                     &mut Ctx { state: acc, env, i },
176                     arg,
177                 );
178             }
179         }
180     }
182     struct Visitor<'a> {
183         phantom: PhantomData<&'a str>,
184     }
186     pub struct Ctx<'a> {
187         state: &'a mut AliasInfoMap,
188         env: &'a Env<'a>,
189         i: usize,
190     }
192     impl<'a> aast_visitor::Visitor for Visitor<'a> {
193         type Context = Ctx<'a>;
194         type Ex = ast_defs::Pos;
195         type Fb = ();
196         type En = ();
197         type Hi = ();
199         fn object(
200             &mut self,
201         ) -> &mut dyn aast_visitor::Visitor<
202             Context = Self::Context,
203             Ex = Self::Ex,
204             Fb = Self::Fb,
205             En = Self::En,
206             Hi = Self::Hi,
207         > {
208             self
209         }
211         fn visit_expr_(&mut self, c: &mut Self::Context, p: &tast::Expr_) {
212             p.recurse(c, self.object());
213             match p {
214                 tast::Expr_::Binop(expr) => {
215                     let (bop, left, _) = &**expr;
216                     if let ast_defs::Bop::Eq(_) = bop {
217                         collect_lvars_hs(c, left)
218                     }
219                 }
220                 tast::Expr_::Unop(expr) => {
221                     let (uop, e) = &**expr;
222                     match uop {
223                         ast_defs::Uop::Uincr | ast_defs::Uop::Udecr => collect_lvars_hs(c, e),
224                         _ => (),
225                     }
226                 }
227                 tast::Expr_::Lvar(expr) => {
228                     let Lid(_, (_, id)) = &**expr;
229                     add_use(id.to_string(), &mut c.state);
230                 }
231                 tast::Expr_::Call(expr) => {
232                     let (_, _, _, args, uarg) = &**expr;
233                     args.iter()
234                         .for_each(|arg| handle_arg(&c.env, false, c.i, arg, &mut c.state));
235                     uarg.as_ref()
236                         .map(|arg| handle_arg(&c.env, false, c.i, arg, &mut c.state));
237                 }
238                 _ => (),
239             }
240         }
241     }
243     // collect lvars on the left hand side of '=' operator
244     fn collect_lvars_hs(ctx: &mut Ctx, expr: &tast::Expr) {
245         let tast::Expr(_, e) = expr;
246         match &*e {
247             tast::Expr_::Lvar(lid) => {
248                 let Lid(_, lid) = &**lid;
249                 if !is_local_this(&ctx.env, &lid) {
250                     add_use(lid.1.to_string(), &mut ctx.state);
251                     add_write(lid.1.to_string(), ctx.i, &mut ctx.state);
252                 }
253             }
254             tast::Expr_::List(exprs) => exprs.iter().for_each(|expr| collect_lvars_hs(ctx, expr)),
255             _ => (),
256         }
257     }
260 pub fn get_type_structure_for_hint(
261     opts: &Options,
262     tparams: &[&str],
263     targ_map: &BTreeMap<&str, i64>,
264     hint: &aast::Hint,
265 ) -> InstrSeq {
266     let _tv =
267         emit_type_constant::hint_to_type_constant(opts, tparams, targ_map, &hint, false, false);
268     unimplemented!("TODO(hrust) after porting most of emit_adata")
271 pub struct Setrange {
272     pub op: SetrangeOp,
273     pub size: usize,
274     pub vec: bool,
277 /// kind of value stored in local
278 pub enum StoredValueKind {
279     Local,
280     Expr,
283 /// represents sequence of instructions interleaved with temp locals.
284 ///    <(i, None) :: rest> - is emitted i :: <rest> (commonly used for final instructions in sequence)
285 ///    <(i, Some(l, local_kind)) :: rest> is emitted as
287 ///    i
288 ///    .try {
289 ///      setl/popl l; depending on local_kind
290 ///      <rest>
291 ///    } .catch {
292 ///      unset l
293 ///      throw
294 ///    }
295 ///    unsetl l
296 type InstrSeqWithLocals = Vec<(InstrSeq, Option<(local::Type, StoredValueKind)>)>;
298 /// result of emit_array_get
299 enum ArrayGetInstr {
300     /// regular $a[..] that does not need to spill anything
301     Regular(InstrSeq),
302     /// subscript expression used as inout argument that need to spill intermediate values:
303     Inout {
304         /// instruction sequence with locals to load value
305         load: InstrSeqWithLocals,
306         /// instruction to set value back (can use locals defined in load part)
307         store: InstrSeq,
308     },
311 struct ArrayGetBaseData<T> {
312     base_instrs: T,
313     cls_instrs: InstrSeq,
314     setup_instrs: InstrSeq,
315     base_stack_size: usize,
316     cls_stack_size: usize,
319 /// result of emit_base
320 enum ArrayGetBase {
321     /// regular <base> part in <base>[..] that does not need to spill anything
322     Regular(ArrayGetBaseData<InstrSeq>),
323     /// base of subscript expression used as inout argument that need to spill
324     /// intermediate values
325     Inout {
326         /// instructions to load base part
327         load: ArrayGetBaseData<InstrSeqWithLocals>,
328         /// instruction to load base part for setting inout argument back
329         store: InstrSeq,
330     },
333 pub fn emit_expr(emitter: &mut Emitter, env: &Env, expression: &tast::Expr) -> Result {
334     use aast_defs::Lid;
335     use tast::Expr_;
336     let tast::Expr(pos, expr) = expression;
337     match expr {
338         Expr_::Float(_)
339         | Expr_::String(_)
340         | Expr_::Int(_)
341         | Expr_::Null
342         | Expr_::False
343         | Expr_::True => {
344             let v = ast_constant_folder::expr_to_typed_value(emitter, &env.namespace, expression)
345                 .map_err(|_| unrecoverable("expr_to_typed_value failed"))?;
346             emit_pos_then(emitter, pos, InstrSeq::make_typedvalue(v))
347         }
348         Expr_::PrefixedString(e) => emit_expr(emitter, env, &(&**e).1),
349         Expr_::ParenthesizedExpr(e) => emit_expr(emitter, env, &*e),
350         Expr_::Lvar(e) => {
351             let Lid(pos, _) = &**e;
352             Ok(InstrSeq::gather(vec![
353                 emit_pos(emitter, pos)?,
354                 emit_local(env, BareThisOp::Notice, &**e)?,
355             ]))
356         }
357         Expr_::ClassConst(e) => emit_class_const(env, pos, &**e),
358         Expr_::Unop(e) => emit_unop(emitter, env, pos, &**e),
359         Expr_::Binop(e) => emit_binop(emitter, env, pos, e.as_ref()),
360         Expr_::Pipe(e) => emit_pipe(env, &**e),
361         Expr_::Is(is_expr) => {
362             let (e, h) = &**is_expr;
363             Ok(InstrSeq::gather(vec![
364                 emit_expr(emitter, env, e)?,
365                 emit_is_hint(env, pos, h)?,
366             ]))
367         }
368         Expr_::As(e) => emit_as(env, pos, &**e),
369         Expr_::Cast(e) => emit_cast(env, pos, &**e),
370         Expr_::Eif(e) => emit_conditional_expr(env, pos, &**e),
371         Expr_::ExprList(es) => Ok(InstrSeq::gather(
372             es.iter()
373                 .map(|e| emit_expr(emitter, env, e).unwrap_or_default())
374                 .collect(),
375         )),
376         Expr_::ArrayGet(e) => {
377             let (base_expr, opt_elem_expr) = &**e;
378             match (base_expr.lvar_name(), opt_elem_expr) {
379                 (Some(name), Some(e)) if name == superglobals::GLOBALS => {
380                     Ok(InstrSeq::gather(vec![
381                         emit_expr(emitter, env, e)?,
382                         emit_pos(emitter, pos)?,
383                         InstrSeq::make_cgetg(),
384                     ]))
385                 }
386                 _ => emit_array_get(env, pos, QueryOp::CGet, &**e),
387             }
388         }
389         Expr_::ObjGet(e) => emit_obj_get(env, pos, QueryOp::CGet, &**e),
390         Expr_::Call(c) => emit_call_expr(emitter, env, pos, None, &*c),
391         Expr_::New(e) => emit_new(env, pos, &**e),
392         Expr_::Record(e) => emit_record(env, pos, &**e),
393         Expr_::Array(es) => emit_pos_then(emitter, pos, emit_collection(env, expression, es)?),
394         Expr_::Darray(e) => {
395             let instrs = emit_collection(env, expression, &mk_afkvalues(&(&**e).1))?;
396             emit_pos_then(emitter, pos, instrs)
397         }
398         Expr_::Varray(e) => {
399             let instrs = emit_collection(env, expression, &mk_afvalues(&(&**e).1))?;
400             emit_pos_then(emitter, pos, instrs)
401         }
402         Expr_::Collection(e) => emit_named_collection_str(env, expression, &**e),
403         Expr_::ValCollection(e) => {
404             let (kind, _, es) = &**e;
405             let fields = mk_afvalues(es);
406             let collection_typ = match kind {
407                 aast_defs::VcKind::Vector => CollectionType::Vector,
408                 aast_defs::VcKind::ImmVector => CollectionType::ImmVector,
409                 aast_defs::VcKind::Set => CollectionType::Set,
410                 aast_defs::VcKind::ImmSet => CollectionType::ImmSet,
411                 _ => return emit_collection(env, expression, &fields),
412             };
413             emit_named_collection(env, pos, expression, &fields, collection_typ)
414         }
415         Expr_::Pair(e) => {
416             let (e1, e2) = (**e).to_owned();
417             let fields = mk_afvalues(&vec![e1, e2]);
418             emit_named_collection(env, pos, expression, &fields, CollectionType::Pair)
419         }
420         Expr_::KeyValCollection(e) => {
421             let (kind, _, fields) = &**e;
422             let fields = mk_afkvalues(
423                 &fields
424                     .to_owned()
425                     .into_iter()
426                     .map(|tast::Field(e1, e2)| (e1, e2))
427                     .collect(),
428             );
429             let collection_typ = match kind {
430                 aast_defs::KvcKind::Map => CollectionType::Map,
431                 aast_defs::KvcKind::ImmMap => CollectionType::ImmMap,
432                 _ => return emit_collection(env, expression, &fields),
433             };
434             emit_named_collection(env, pos, expression, &fields, collection_typ)
435         }
436         Expr_::Clone(e) => emit_pos_then(emitter, pos, emit_clone(env, &**e)?),
437         Expr_::Shape(e) => emit_pos_then(emitter, pos, emit_shape(env, expression, e)?),
438         Expr_::Await(e) => emit_await(emitter, env, pos, &**e),
439         Expr_::Yield(e) => emit_yield(emitter, env, pos, &**e),
440         Expr_::Efun(e) => emit_pos_then(emitter, pos, emit_lambda(env, &**e)?),
441         Expr_::ClassGet(e) => emit_class_get(env, QueryOp::CGet, &**e),
442         Expr_::String2(es) => emit_string2(env, pos, es),
443         Expr_::BracedExpr(e) => emit_expr(emitter, env, &**e),
444         Expr_::Id(e) => {
445             let instrs = emit_id(emitter, env, &**e)?;
446             emit_pos_then(emitter, pos, instrs)
447         }
448         Expr_::Xml(e) => emit_xhp(env, pos, &**e),
449         Expr_::Callconv(e) => Err(Unrecoverable(
450             "emit_callconv: This should have been caught at emit_arg".into(),
451         )),
452         Expr_::Import(e) => emit_import(env, pos, &**e),
453         Expr_::Omitted => Ok(InstrSeq::Empty),
454         Expr_::YieldBreak => Err(Unrecoverable(
455             "yield break should be in statement position".into(),
456         )),
457         Expr_::YieldFrom(_) => Err(Unrecoverable("complex yield_from expression".into())),
458         Expr_::Lfun(_) => Err(Unrecoverable(
459             "expected Lfun to be converted to Efun during closure conversion emit_expr".into(),
460         )),
461         _ => unimplemented!("TODO(hrust)"),
462     }
465 fn emit_exprs(e: &mut Emitter, env: &Env, exprs: &[tast::Expr]) -> Result {
466     if exprs.is_empty() {
467         Ok(InstrSeq::Empty)
468     } else {
469         Ok(InstrSeq::gather(
470             exprs
471                 .iter()
472                 .map(|expr| emit_expr(e, env, expr))
473                 .collect::<Result<Vec<_>>>()?,
474         ))
475     }
478 fn emit_pos_then(emitter: &Emitter, pos: &Pos, instrs: InstrSeq) -> Result {
479     Ok(emit_pos_rust::emit_pos_then(emitter, pos, instrs))
482 fn emit_pos(emitter: &Emitter, pos: &Pos) -> Result {
483     Ok(emit_pos_rust::emit_pos(emitter, pos))
486 fn emit_id(emitter: &mut Emitter, env: &Env, id: &tast::Sid) -> Result {
487     use pseudo_consts::*;
488     use InstructLitConst::*;
490     let ast_defs::Id(p, s) = id;
491     let res = match s.as_str() {
492         G__FILE__ => InstrSeq::make_lit_const(File),
493         G__DIR__ => InstrSeq::make_lit_const(Dir),
494         G__METHOD__ => InstrSeq::make_lit_const(Method),
495         G__FUNCTION_CREDENTIAL__ => InstrSeq::make_lit_const(FuncCred),
496         G__CLASS__ => InstrSeq::gather(vec![InstrSeq::make_self(), InstrSeq::make_classname()]),
497         G__COMPILER_FRONTEND__ => InstrSeq::make_string("hackc"),
498         G__LINE__ => InstrSeq::make_int(p.info_pos_extended().1.try_into().map_err(|_| {
499             emit_fatal::raise_fatal_parse(p, "error converting end of line from usize to isize")
500         })?),
501         G__NAMESPACE__ => InstrSeq::make_string(env.namespace.name.as_ref().map_or("", |s| &s[..])),
502         EXIT | DIE => return emit_exit(emitter, env, None),
503         _ => {
504             //panic!("TODO: uncomment after D19350786 lands")
505             //let cid: ConstId = r#const::Type::from_ast_name(&s);
506             let cid: ConstId = string_utils::strip_global_ns(&s).to_string().into();
507             emit_symbol_refs::State::add_constant(emitter, cid.clone());
508             return emit_pos_then(emitter, p, InstrSeq::make_lit_const(CnsE(cid)));
509         }
510     };
511     Ok(res)
514 fn emit_exit(emitter: &mut Emitter, env: &Env, expr_opt: Option<&tast::Expr>) -> Result {
515     Ok(InstrSeq::gather(vec![
516         expr_opt.map_or_else(|| Ok(InstrSeq::make_int(0)), |e| emit_expr(emitter, env, e))?,
517         InstrSeq::make_exit(),
518     ]))
521 fn emit_xhp(
522     env: &Env,
523     pos: &Pos,
524     (id, attributes, children): &(tast::Sid, Vec<tast::XhpAttribute>, Vec<tast::Expr>),
525 ) -> Result {
526     unimplemented!("TODO(hrust)")
529 fn emit_yield(e: &mut Emitter, env: &Env, pos: &Pos, af: &tast::Afield) -> Result {
530     unimplemented!("TODO(hrust)")
533 fn emit_import(env: &Env, pos: &Pos, (flavor, expr): &(tast::ImportFlavor, tast::Expr)) -> Result {
534     unimplemented!("TODO(hrust)")
537 fn emit_string2(env: &Env, pos: &Pos, es: &Vec<tast::Expr>) -> Result {
538     unimplemented!("TODO(hrust)")
541 fn emit_clone(env: &Env, expr: &tast::Expr) -> Result {
542     unimplemented!("TODO(hrust)")
545 fn emit_lambda(env: &Env, (fndef, ids): &(tast::Fun_, Vec<aast_defs::Lid>)) -> Result {
546     unimplemented!("TODO(hrust)")
549 pub fn emit_await(emitter: &mut Emitter, env: &Env, pos: &Pos, expr: &tast::Expr) -> Result {
550     let tast::Expr(_, e) = expr;
551     let cant_inline_gen_functions = emitter
552         .options()
553         .hhvm
554         .flags
555         .contains(HhvmFlags::JIT_ENABLE_RENAME_FUNCTION);
556     match e.as_call() {
557         Some((_, tast::Expr(_, tast::Expr_::Id(id)), _, args, None))
558             if (cant_inline_gen_functions
559                 && args.len() == 1
560                 && string_utils::strip_global_ns(&(*id.1)) == "gena") =>
561         {
562             return inline_gena_call(emitter, env, &args[0])
563         }
564         _ => {
565             let after_await = emitter.label_gen_mut().next_regular();
566             let instrs = match e {
567                 tast::Expr_::Call(c) => {
568                     emit_call_expr(emitter, env, pos, Some(after_await.clone()), &*c)?
569                 }
570                 _ => emit_expr(emitter, env, expr)?,
571             };
572             Ok(InstrSeq::gather(vec![
573                 instrs,
574                 emit_pos(emitter, pos)?,
575                 InstrSeq::make_dup(),
576                 InstrSeq::make_istypec(IstypeOp::OpNull),
577                 InstrSeq::make_jmpnz(after_await.clone()),
578                 InstrSeq::make_await(),
579                 InstrSeq::make_label(after_await),
580             ]))
581         }
582     }
585 fn hack_arr_dv_arrs(opts: &Options) -> bool {
586     opts.hhvm.flags.contains(HhvmFlags::HACK_ARR_DV_ARRS)
589 fn inline_gena_call(emitter: &mut Emitter, env: &Env, arg: &tast::Expr) -> Result {
590     let load_arr = emit_expr(emitter, env, arg)?;
591     let async_eager_label = emitter.label_gen_mut().next_regular();
592     let hack_arr_dv_arrs = hack_arr_dv_arrs(emitter.options());
594     scope::with_unnamed_local(emitter, |e, arr_local| {
595         let before = InstrSeq::gather(vec![
596             load_arr,
597             if hack_arr_dv_arrs {
598                 InstrSeq::make_cast_dict()
599             } else {
600                 InstrSeq::make_cast_darray()
601             },
602             InstrSeq::make_popl(arr_local.clone()),
603         ]);
605         let inner = InstrSeq::gather(vec![
606             InstrSeq::make_nulluninit(),
607             InstrSeq::make_nulluninit(),
608             InstrSeq::make_nulluninit(),
609             InstrSeq::make_cgetl(arr_local.clone()),
610             InstrSeq::make_fcallclsmethodd(
611                 FcallArgs::new(
612                     FcallFlags::default(),
613                     1,
614                     vec![],
615                     Some(async_eager_label.clone()),
616                     1,
617                 ),
618                 method::from_raw_string(if hack_arr_dv_arrs {
619                     "fromDict"
620                 } else {
621                     "fromArray"
622                 }),
623                 class::from_raw_string("HH\\AwaitAllWaitHandle"),
624             ),
625             InstrSeq::make_await(),
626             InstrSeq::make_label(async_eager_label.clone()),
627             InstrSeq::make_popc(),
628             emit_iter(
629                 e,
630                 InstrSeq::make_cgetl(arr_local.clone()),
631                 |key_local, val_local| {
632                     InstrSeq::gather(vec![
633                         InstrSeq::make_cgetl(val_local),
634                         InstrSeq::make_whresult(),
635                         InstrSeq::make_basel(arr_local.clone(), MemberOpMode::Define),
636                         InstrSeq::make_setm(0, MemberKey::EL(key_local)),
637                         InstrSeq::make_popc(),
638                     ])
639                 },
640             ),
641         ]);
643         let after = InstrSeq::make_pushl(arr_local);
645         Ok((before, inner, after))
646     })
649 fn emit_iter<F: FnOnce(local::Type, local::Type) -> InstrSeq>(
650     e: &mut Emitter,
651     collection: InstrSeq,
652     f: F,
653 ) -> InstrSeq {
654     scope::with_unnamed_locals_and_iterators(e, |e| {
655         let iter_id = e.iterator_mut().get();
656         let val_id = e.local_gen_mut().get_unnamed();
657         let key_id = e.local_gen_mut().get_unnamed();
658         let loop_end = e.label_gen_mut().next_regular();
659         let loop_next = e.label_gen_mut().next_regular();
660         let iter_args = IterArgs {
661             iter_id,
662             key_id: Some(key_id.clone()),
663             val_id: val_id.clone(),
664         };
665         let iter_init = InstrSeq::gather(vec![
666             collection,
667             InstrSeq::make_iterinit(iter_args.clone(), loop_end.clone()),
668         ]);
669         let iterate = InstrSeq::gather(vec![
670             InstrSeq::make_label(loop_next.clone()),
671             f(val_id.clone(), key_id.clone()),
672             InstrSeq::make_iternext(iter_args, loop_next),
673         ]);
674         let iter_done = InstrSeq::gather(vec![
675             InstrSeq::make_unsetl(val_id),
676             InstrSeq::make_unsetl(key_id),
677             InstrSeq::make_label(loop_end),
678         ]);
679         (iter_init, iterate, iter_done)
680     })
683 fn emit_shape(
684     env: &Env,
685     expr: &tast::Expr,
686     fl: &Vec<(ast_defs::ShapeFieldName, tast::Expr)>,
687 ) -> Result {
688     unimplemented!("TODO(hrust)")
691 fn emit_named_collection(
692     env: &Env,
693     pos: &Pos,
694     expr: &tast::Expr,
695     fields: &Vec<tast::Afield>,
696     collection_typ: CollectionType,
697 ) -> Result {
698     unimplemented!("TODO(hrust)")
701 fn emit_named_collection_str(
702     env: &Env,
703     expr: &tast::Expr,
704     (ast_defs::Id(pos, name), _, fields): &(
705         tast::Sid,
706         Option<tast::CollectionTarg>,
707         Vec<tast::Afield>,
708     ),
709 ) -> Result {
710     unimplemented!("TODO(hrust)")
713 fn mk_afkvalues(es: &Vec<(tast::Expr, tast::Expr)>) -> Vec<tast::Afield> {
714     es.to_owned()
715         .into_iter()
716         .map(|(e1, e2)| tast::Afield::mk_afkvalue(e1, e2))
717         .collect()
720 fn mk_afvalues(es: &Vec<tast::Expr>) -> Vec<tast::Afield> {
721     es.to_owned()
722         .into_iter()
723         .map(|e| tast::Afield::mk_afvalue(e))
724         .collect()
727 fn emit_collection(env: &Env, expr: &tast::Expr, fields: &Vec<tast::Afield>) -> Result {
728     unimplemented!("TODO(hrust)")
731 fn emit_record(
732     env: &Env,
733     pos: &Pos,
734     (cid, is_array, es): &(tast::Sid, bool, Vec<(tast::Expr, tast::Expr)>),
735 ) -> Result {
736     let es = mk_afkvalues(es);
737     unimplemented!("TODO(hrust)")
740 fn emit_call_isset_exprs(e: &mut Emitter, env: &Env, pos: &Pos, exprs: &[tast::Expr]) -> Result {
741     unimplemented!()
744 fn emit_idx(e: &mut Emitter, env: &Env, pos: &Pos, es: &[tast::Expr]) -> Result {
745     let default = if es.len() == 2 {
746         InstrSeq::make_null()
747     } else {
748         InstrSeq::Empty
749     };
750     Ok(InstrSeq::gather(vec![
751         emit_exprs(e, env, es)?,
752         emit_pos(e, pos)?,
753         default,
754         InstrSeq::make_idx(),
755     ]))
758 fn emit_call(
759     e: &mut Emitter,
760     env: &Env,
761     pos: &Pos,
762     expr: &tast::Expr,
763     targs: &[tast::Targ],
764     args: &[tast::Expr],
765     uarg: Option<&tast::Expr>,
766     async_eager_label: Option<Label>,
767 ) -> Result {
768     if let Some(ast_defs::Id(_, s)) = expr.as_id() {
769         let fid = function::Type::from_ast_name(s);
770         emit_symbol_refs::add_function(e, fid);
771     }
772     let fcall_args = get_fcall_args(args, uarg, async_eager_label, false);
773     let FcallArgs(_, _, num_ret, _, _) = &fcall_args;
774     let num_uninit = num_ret - 1;
775     let default = scope::with_unnamed_locals(e, |e| {
776         let (lhs, fcall) = emit_call_lhs_and_fcall(e, env, expr, fcall_args, targs)?;
777         let (args, inout_setters) = emit_args_inout_setters(e, env, args)?;
778         let uargs = uarg.map_or(Ok(InstrSeq::Empty), |uarg| emit_expr(e, env, uarg))?;
779         Ok((
780             InstrSeq::Empty,
781             InstrSeq::gather(vec![
782                 InstrSeq::gather(
783                     iter::repeat(InstrSeq::make_nulluninit())
784                         .take(num_uninit)
785                         .collect::<Vec<_>>(),
786                 ),
787                 lhs,
788                 args,
789                 emit_pos(e, pos)?,
790                 fcall,
791                 inout_setters,
792             ]),
793             InstrSeq::Empty,
794         ))
795     })?;
796     expr.1
797         .as_id()
798         .and_then(|ast_defs::Id(_, id)| {
799             emit_special_function(e, env, pos, &expr.0, &id, args, uarg, &default).transpose()
800         })
801         .unwrap_or(Ok(default))
804 fn emit_reified_targs(e: &mut Emitter, env: &Env, pos: &Pos, targs: &[&tast::Hint]) -> Result {
805     unimplemented!()
808 fn get_erased_tparams<'a>(env: &'a Env<'a>) -> Vec<&'a str> {
809     env.scope
810         .get_tparams()
811         .iter()
812         .filter_map(|tparam| match tparam.reified {
813             tast::ReifyKind::Erased => Some(tparam.name.1.as_str()),
814             _ => None,
815         })
816         .collect()
819 fn has_non_tparam_generics_targs(env: &Env, targs: &[tast::Targ]) -> bool {
820     let erased_tparams = get_erased_tparams(env);
821     targs.iter().any(|targ| {
822         (targ.1)
823             .1
824             .as_happly()
825             .map_or(true, |(id, _)| !erased_tparams.contains(&id.1.as_str()))
826     })
829 fn emit_call_lhs_and_fcall(
830     e: &mut Emitter,
831     env: &Env,
832     expr: &tast::Expr,
833     mut fcall_args: FcallArgs,
834     targs: &[tast::Targ],
835 ) -> Result<(InstrSeq, InstrSeq)> {
836     let tast::Expr(pos, expr_) = expr;
837     use tast::Expr_ as E_;
839     let emit_generics = |e, env, fcall_args: &mut FcallArgs| {
840         let does_not_have_non_tparam_generics = !has_non_tparam_generics_targs(env, targs);
841         if does_not_have_non_tparam_generics {
842             Ok(InstrSeq::Empty)
843         } else {
844             *(&mut fcall_args.0) = fcall_args.0 | FcallFlags::HAS_GENERICS;
845             emit_reified_targs(
846                 e,
847                 env,
848                 pos,
849                 targs
850                     .iter()
851                     .map(|targ| &targ.1)
852                     .collect::<Vec<_>>()
853                     .as_slice(),
854             )
855         }
856     };
858     match expr_ {
859         E_::ObjGet(_) => unimplemented!(),
860         E_::ClassConst(_) => unimplemented!(),
861         E_::ClassGet(_) => unimplemented!(),
862         E_::Id(id) => {
863             let FcallArgs(flags, num_args, _, _, _) = fcall_args;
864             let fq_id = match string_utils::strip_global_ns(&id.1) {
865                 "min" if num_args == 2 && flags.contains(FcallFlags::HAS_UNPACK) => {
866                     function::Type::from_ast_name("__SystemLib\\min2")
867                 }
868                 "max" if num_args == 2 && flags.contains(FcallFlags::HAS_UNPACK) => {
869                     function::Type::from_ast_name("__SystemLib\\max2")
870                 }
871                 _ => {
872                     //TODO(hrust): enable `function::Type::from_ast_name(&id.1)`
873                     string_utils::strip_global_ns(&id.1).to_string().into()
874                 }
875             };
876             let generics = emit_generics(e, env, &mut fcall_args)?;
877             Ok((
878                 InstrSeq::gather(vec![
879                     InstrSeq::make_nulluninit(),
880                     InstrSeq::make_nulluninit(),
881                     InstrSeq::make_nulluninit(),
882                 ]),
883                 InstrSeq::gather(vec![generics, InstrSeq::make_fcallfuncd(fcall_args, fq_id)]),
884             ))
885         }
886         E_::String(s) => unimplemented!(),
887         _ => {
888             let tmp = e.local_gen_mut().get_unnamed();
889             Ok((
890                 InstrSeq::gather(vec![
891                     InstrSeq::make_nulluninit(),
892                     InstrSeq::make_nulluninit(),
893                     InstrSeq::make_nulluninit(),
894                     emit_expr(e, env, expr)?,
895                     InstrSeq::make_popl(tmp.clone()),
896                 ]),
897                 InstrSeq::gather(vec![
898                     InstrSeq::make_pushl(tmp),
899                     InstrSeq::make_fcallfunc(fcall_args),
900                 ]),
901             ))
902         }
903     }
906 fn emit_args_inout_setters(
907     e: &mut Emitter,
908     env: &Env,
909     args: &[tast::Expr],
910 ) -> Result<(InstrSeq, InstrSeq)> {
911     fn emit_arg_and_inout_setter(
912         e: &mut Emitter,
913         env: &Env,
914         i: usize,
915         arg: &tast::Expr,
916     ) -> Result<(InstrSeq, InstrSeq)> {
917         use tast::Expr_ as E_;
918         match &arg.1 {
919             E_::Callconv(cc) => {
920                 match &(cc.1).1 {
921                     // inout $var
922                     E_::Lvar(l) => unimplemented!(),
923                     // inout $arr[...][...]
924                     E_::ArrayGet(ag) => unimplemented!(),
925                     _ => Err(unrecoverable(
926                         "emit_arg_and_inout_setter: Unexpected inout expression type",
927                     )),
928                 }
929             }
930             _ => Ok((emit_expr(e, env, arg)?, InstrSeq::Empty)),
931         }
932     }
933     let (instr_args, instr_setters): (Vec<InstrSeq>, Vec<InstrSeq>) = args
934         .iter()
935         .enumerate()
936         .map(|(i, arg)| emit_arg_and_inout_setter(e, env, i, arg))
937         .collect::<Result<Vec<_>>>()?
938         .into_iter()
939         .unzip();
940     let instr_args = InstrSeq::gather(instr_args);
941     let instr_setters = InstrSeq::gather(instr_setters);
942     if has_inout_arg(args) {
943         let retval = e.local_gen_mut().get_unnamed();
944         Ok((
945             instr_args,
946             InstrSeq::gather(vec![
947                 InstrSeq::make_popl(retval.clone()),
948                 instr_setters,
949                 InstrSeq::make_pushl(retval),
950             ]),
951         ))
952     } else {
953         Ok((instr_args, InstrSeq::Empty))
954     }
957 fn get_fcall_args(
958     args: &[tast::Expr],
959     uarg: Option<&tast::Expr>,
960     async_eager_label: Option<Label>,
961     lock_while_unwinding: bool,
962 ) -> FcallArgs {
963     let num_args = args.len();
964     let num_rets = 1 + args.iter().filter(|x| is_inout_arg(*x)).count();
965     let mut flags = FcallFlags::default();
966     flags.set(FcallFlags::HAS_UNPACK, uarg.is_some());
967     flags.set(FcallFlags::LOCK_WHILE_UNWINDING, lock_while_unwinding);
968     let inouts: Vec<bool> = args.iter().map(is_inout_arg).collect();
969     FcallArgs::new(flags, num_rets, inouts, async_eager_label, num_args)
972 fn is_inout_arg(e: &tast::Expr) -> bool {
973     e.1.as_callconv().map_or(false, |cc| cc.0.is_pinout())
976 fn has_inout_arg(es: &[tast::Expr]) -> bool {
977     es.iter().any(is_inout_arg)
980 fn emit_special_function(
981     e: &mut Emitter,
982     env: &Env,
983     outer_pos: &Pos,
984     pos: &Pos,
985     id: &str,
986     args: &[tast::Expr],
987     uarg: Option<&tast::Expr>,
988     default: &InstrSeq,
989 ) -> Result<Option<InstrSeq>> {
990     let nargs = args.len() + uarg.map_or(0, |_| 1);
991     let fq = function::Type::from_ast_name(id);
992     let lower_fq_name = fq.to_raw_string();
993     match (lower_fq_name, args) {
994         (id, _) if id == special_functions::ECHO => Ok(Some(InstrSeq::gather(
995             args.iter()
996                 .enumerate()
997                 .map(|(i, arg)| {
998                     Ok(InstrSeq::gather(vec![
999                         emit_expr(e, env, arg)?,
1000                         emit_pos(e, pos)?,
1001                         InstrSeq::make_print(),
1002                         if i == nargs - 1 {
1003                             InstrSeq::Empty
1004                         } else {
1005                             InstrSeq::make_popc()
1006                         },
1007                     ]))
1008                 })
1009                 .collect::<Result<_>>()?,
1010         ))),
1011         _ => unimplemented!(),
1012     }
1015 fn emit_eval(e: &mut Emitter, env: &Env, pos: &Pos, expr: &tast::Expr) -> Result {
1016     Ok(InstrSeq::gather(vec![
1017         emit_expr(e, env, expr)?,
1018         emit_pos(e, pos)?,
1019         InstrSeq::make_eval(),
1020     ]))
1023 fn emit_call_expr(
1024     e: &mut Emitter,
1025     env: &Env,
1026     pos: &Pos,
1027     async_eager_label: Option<Label>,
1028     (_, expr, targs, args, uarg): &(
1029         tast::CallType,
1030         tast::Expr,
1031         Vec<tast::Targ>,
1032         Vec<tast::Expr>,
1033         Option<tast::Expr>,
1034     ),
1035 ) -> Result {
1036     let jit_enable_rename_function = e
1037         .options()
1038         .hhvm
1039         .flags
1040         .contains(HhvmFlags::JIT_ENABLE_RENAME_FUNCTION);
1041     use {tast::Expr as E, tast::Expr_ as E_};
1042     match (&expr.1, &args[..], uarg) {
1043         (E_::Id(id), [E(_, E_::String(data))], None) if id.1 == special_functions::HHAS_ADATA => {
1044             let v = TypedValue::HhasAdata(data.into());
1045             emit_pos_then(e, pos, InstrSeq::make_typedvalue(v))
1046         }
1047         (E_::Id(id), _, None) if id.1 == pseudo_functions::ISSET => {
1048             emit_call_isset_exprs(e, env, pos, args)
1049         }
1050         (E_::Id(id), args, None)
1051             if id.1 == fb::IDX
1052                 && !jit_enable_rename_function
1053                 && (args.len() == 2 || args.len() == 3) =>
1054         {
1055             emit_idx(e, env, pos, args)
1056         }
1057         (E_::Id(id), [arg1], None) if id.1 == emitter_special_functions::EVAL => {
1058             emit_eval(e, env, pos, arg1)
1059         }
1060         (E_::Id(id), [arg1], None) if id.1 == emitter_special_functions::SET_FRAME_METADATA => {
1061             Ok(InstrSeq::gather(vec![
1062                 emit_expr(e, env, arg1)?,
1063                 emit_pos(e, pos)?,
1064                 InstrSeq::make_popl(local::Type::Named("$86metadata".into())),
1065                 InstrSeq::make_null(),
1066             ]))
1067         }
1068         (E_::Id(id), [], None)
1069             if id.1 == pseudo_functions::EXIT || id.1 == pseudo_functions::DIE =>
1070         {
1071             let exit = emit_exit(e, env, None)?;
1072             emit_pos_then(e, pos, exit)
1073         }
1074         (E_::Id(id), [arg1], None)
1075             if id.1 == pseudo_functions::EXIT || id.1 == pseudo_functions::DIE =>
1076         {
1077             let exit = emit_exit(e, env, Some(arg1))?;
1078             emit_pos_then(e, pos, exit)
1079         }
1080         (_, _, _) => {
1081             let instrs = emit_call(
1082                 e,
1083                 env,
1084                 pos,
1085                 expr,
1086                 targs,
1087                 args,
1088                 uarg.as_ref(),
1089                 async_eager_label,
1090             )?;
1091             emit_pos_then(e, pos, instrs)
1092         }
1093     }
1096 fn emit_new(
1097     env: &Env,
1098     pos: &Pos,
1099     (cid, targs, args, uarg, _): &(
1100         tast::ClassId,
1101         Vec<tast::Targ>,
1102         Vec<tast::Expr>,
1103         Option<tast::Expr>,
1104         Pos,
1105     ),
1106 ) -> Result {
1107     unimplemented!("TODO(hrust)")
1110 fn emit_obj_get(
1111     env: &Env,
1112     pos: &Pos,
1113     query_op: QueryOp,
1114     (expr, prop, nullflavor): &(tast::Expr, tast::Expr, ast_defs::OgNullFlavor),
1115 ) -> Result {
1116     unimplemented!("TODO(hrust)")
1119 fn emit_array_get(
1120     env: &Env,
1121     pos: &Pos,
1122     query_op: QueryOp,
1123     (base_expr, opt_elem_expr): &(tast::Expr, Option<tast::Expr>),
1124 ) -> Result {
1125     unimplemented!("TODO(hrust)")
1128 fn emit_class_get(
1129     env: &Env,
1130     query_op: QueryOp,
1131     (cid, cls_get_expr): &(tast::ClassId, tast::ClassGetExpr),
1132 ) -> Result {
1133     unimplemented!("TODO(hrust)")
1136 fn emit_conditional_expr(
1137     env: &Env,
1138     pos: &Pos,
1139     (etest, etrue, efalse): &(tast::Expr, Option<tast::Expr>, tast::Expr),
1140 ) -> Result {
1141     unimplemented!("TODO(hrust)")
1144 fn emit_local(env: &Env, notice: BareThisOp, lid: &aast_defs::Lid) -> Result {
1145     unimplemented!("TODO(hrust)")
1148 fn emit_class_const(env: &Env, pos: &Pos, (ci, id): &(tast::ClassId, ast_defs::Pstring)) -> Result {
1149     unimplemented!("TODO(hrust)")
1152 fn emit_unop(
1153     e: &mut Emitter,
1154     env: &Env,
1155     pos: &Pos,
1156     (uop, expr): &(ast_defs::Uop, tast::Expr),
1157 ) -> Result {
1158     use ast_defs::Uop as U;
1159     match uop {
1160         U::Utild | U::Unot => Ok(InstrSeq::gather(vec![
1161             emit_expr(e, env, expr)?,
1162             emit_pos_then(e, pos, from_unop(e.options(), uop)?)?,
1163         ])),
1164         U::Uplus | U::Uminus => Ok(InstrSeq::gather(vec![
1165             emit_pos(e, pos)?,
1166             InstrSeq::make_int(0),
1167             emit_expr(e, env, expr)?,
1168             emit_pos_then(e, pos, from_unop(e.options(), uop)?)?,
1169         ])),
1170         U::Uincr | U::Udecr | U::Upincr | U::Updecr => emit_lval_op(
1171             e,
1172             env,
1173             pos,
1174             LValOp::IncDec(unop_to_incdec_op(e.options(), uop)?),
1175             expr,
1176             None,
1177             false,
1178         ),
1179         U::Usilence => e.local_scope(|e| {
1180             let temp_local = e.local_gen_mut().get_unnamed();
1181             Ok(InstrSeq::gather(vec![
1182                 emit_pos(e, pos)?,
1183                 InstrSeq::make_silence_start(temp_local.clone()),
1184                 {
1185                     let try_instrs = emit_expr(e, env, expr)?;
1186                     let catch_instrs = InstrSeq::gather(vec![
1187                         emit_pos(e, pos)?,
1188                         InstrSeq::make_silence_end(temp_local.clone()),
1189                     ]);
1190                     InstrSeq::create_try_catch(
1191                         e.label_gen_mut(),
1192                         None,
1193                         false, /* skip_throw */
1194                         try_instrs,
1195                         catch_instrs,
1196                     )
1197                 },
1198                 emit_pos(e, pos)?,
1199                 InstrSeq::make_silence_end(temp_local),
1200             ]))
1201         }),
1202     }
1205 fn unop_to_incdec_op(opts: &Options, op: &ast_defs::Uop) -> Result<IncdecOp> {
1206     let check_int_overflow = opts
1207         .hhvm
1208         .hack_lang_flags
1209         .contains(LangFlags::CHECK_INT_OVERFLOW);
1210     let if_check_or = |op1, op2| Ok(if check_int_overflow { op1 } else { op2 });
1211     use {ast_defs::Uop as U, IncdecOp as I};
1212     match op {
1213         U::Uincr => if_check_or(I::PreIncO, I::PreInc),
1214         U::Udecr => if_check_or(I::PreDecO, I::PreDec),
1215         U::Upincr => if_check_or(I::PostIncO, I::PostInc),
1216         U::Updecr => if_check_or(I::PostDecO, I::PostDec),
1217         _ => Err(Unrecoverable("invalid incdec op".into())),
1218     }
1221 fn from_unop(opts: &Options, op: &ast_defs::Uop) -> Result {
1222     let check_int_overflow = opts
1223         .hhvm
1224         .hack_lang_flags
1225         .contains(LangFlags::CHECK_INT_OVERFLOW);
1226     use ast_defs::Uop as U;
1227     Ok(match op {
1228         U::Utild => InstrSeq::make_bitnot(),
1229         U::Unot => InstrSeq::make_not(),
1230         U::Uplus => {
1231             if check_int_overflow {
1232                 InstrSeq::make_addo()
1233             } else {
1234                 InstrSeq::make_add()
1235             }
1236         }
1237         U::Uminus => {
1238             if check_int_overflow {
1239                 InstrSeq::make_addo()
1240             } else {
1241                 InstrSeq::make_add()
1242             }
1243         }
1244         _ => {
1245             return Err(Unrecoverable(
1246                 "this unary operation cannot be translated".into(),
1247             ))
1248         }
1249     })
1252 fn binop_to_eqop(opts: &Options, op: &ast_defs::Bop) -> Option<EqOp> {
1253     use {ast_defs::Bop as B, EqOp::*};
1254     let check_int_overflow = opts
1255         .hhvm
1256         .hack_lang_flags
1257         .contains(LangFlags::CHECK_INT_OVERFLOW);
1258     match op {
1259         B::Plus => Some(if check_int_overflow {
1260             PlusEqualO
1261         } else {
1262             PlusEqual
1263         }),
1264         B::Minus => Some(if check_int_overflow {
1265             MinusEqualO
1266         } else {
1267             MinusEqual
1268         }),
1269         B::Star => Some(if check_int_overflow {
1270             MulEqualO
1271         } else {
1272             MulEqual
1273         }),
1274         B::Slash => Some(DivEqual),
1275         B::Starstar => Some(PowEqual),
1276         B::Amp => Some(AndEqual),
1277         B::Bar => Some(OrEqual),
1278         B::Xor => Some(XorEqual),
1279         B::Ltlt => Some(SlEqual),
1280         B::Gtgt => Some(SrEqual),
1281         B::Percent => Some(ModEqual),
1282         B::Dot => Some(ConcatEqual),
1283         _ => None,
1284     }
1287 fn optimize_null_checks(e: &Emitter) -> bool {
1288     e.options()
1289         .hack_compiler_flags
1290         .contains(CompilerFlags::OPTIMIZE_NULL_CHECKS)
1293 fn from_binop(opts: &Options, op: &ast_defs::Bop) -> Result {
1294     let check_int_overflow = opts
1295         .hhvm
1296         .hack_lang_flags
1297         .contains(LangFlags::CHECK_INT_OVERFLOW);
1298     use ast_defs::Bop as B;
1299     Ok(match op {
1300         B::Plus => {
1301             if check_int_overflow {
1302                 InstrSeq::make_addo()
1303             } else {
1304                 InstrSeq::make_add()
1305             }
1306         }
1307         B::Minus => {
1308             if check_int_overflow {
1309                 InstrSeq::make_subo()
1310             } else {
1311                 InstrSeq::make_sub()
1312             }
1313         }
1314         B::Star => {
1315             if check_int_overflow {
1316                 InstrSeq::make_mulo()
1317             } else {
1318                 InstrSeq::make_mul()
1319             }
1320         }
1321         B::Slash => InstrSeq::make_div(),
1322         B::Eqeq => InstrSeq::make_eq(),
1323         B::Eqeqeq => InstrSeq::make_same(),
1324         B::Starstar => InstrSeq::make_pow(),
1325         B::Diff => InstrSeq::make_neq(),
1326         B::Diff2 => InstrSeq::make_nsame(),
1327         B::Lt => InstrSeq::make_lt(),
1328         B::Lte => InstrSeq::make_lte(),
1329         B::Gt => InstrSeq::make_gt(),
1330         B::Gte => InstrSeq::make_gte(),
1331         B::Dot => InstrSeq::make_concat(),
1332         B::Amp => InstrSeq::make_bitand(),
1333         B::Bar => InstrSeq::make_bitor(),
1334         B::Ltlt => InstrSeq::make_shl(),
1335         B::Gtgt => InstrSeq::make_shr(),
1336         B::Cmp => InstrSeq::make_cmp(),
1337         B::Percent => InstrSeq::make_mod(),
1338         B::Xor => InstrSeq::make_bitxor(),
1339         B::LogXor => InstrSeq::make_xor(),
1340         B::Eq(_) => return Err(Unrecoverable("assignment is emitted differently".into())),
1341         B::QuestionQuestion => {
1342             return Err(Unrecoverable(
1343                 "null coalescence is emitted differently".into(),
1344             ))
1345         }
1346         B::Barbar | B::Ampamp => {
1347             return Err(Unrecoverable(
1348                 "short-circuiting operator cannot be generated as a simple binop".into(),
1349             ))
1350         }
1351     })
1354 fn emit_two_exprs(
1355     e: &mut Emitter,
1356     env: &Env,
1357     outer_pos: &Pos,
1358     e1: &tast::Expr,
1359     e2: &tast::Expr,
1360 ) -> Result {
1361     unimplemented!()
1364 fn emit_quiet_expr(
1365     e: &mut Emitter,
1366     env: &Env,
1367     pos: &Pos,
1368     expr: &tast::Expr,
1369     null_coalesce_assignment: bool,
1370 ) -> Result<(InstrSeq, Option<NumParams>)> {
1371     unimplemented!()
1374 fn emit_null_coalesce_assignment(
1375     e: &mut Emitter,
1376     env: &Env,
1377     pos: &Pos,
1378     e1: &tast::Expr,
1379     e2: &tast::Expr,
1380 ) -> Result {
1381     unimplemented!()
1384 fn emit_binop(
1385     e: &mut Emitter,
1386     env: &Env,
1387     pos: &Pos,
1388     (op, e1, e2): &(ast_defs::Bop, tast::Expr, tast::Expr),
1389 ) -> Result {
1390     use ast_defs::Bop as B;
1391     match op {
1392         B::Ampamp | B::Barbar => unimplemented!("TODO(hrust)"),
1393         B::Eq(None) => emit_lval_op(e, env, pos, LValOp::Set, e1, Some(e2), false),
1394         B::Eq(Some(eop)) if eop.is_question_question() => {
1395             emit_null_coalesce_assignment(e, env, pos, e1, e2)
1396         }
1397         B::Eq(Some(eop)) => match binop_to_eqop(e.options(), eop) {
1398             None => Err(Unrecoverable("illegal eq op".into())),
1399             Some(op) => emit_lval_op(e, env, pos, LValOp::SetOp(op), e1, Some(e2), false),
1400         },
1401         B::QuestionQuestion => {
1402             let end_label = e.label_gen_mut().next_regular();
1403             Ok(InstrSeq::gather(vec![
1404                 emit_quiet_expr(e, env, pos, e1, false)?.0,
1405                 InstrSeq::make_dup(),
1406                 InstrSeq::make_istypec(IstypeOp::OpNull),
1407                 InstrSeq::make_not(),
1408                 InstrSeq::make_jmpnz(end_label.clone()),
1409                 InstrSeq::make_popc(),
1410                 emit_expr(e, env, e2)?,
1411                 InstrSeq::make_label(end_label),
1412             ]))
1413         }
1414         _ => {
1415             let default = |e: &mut Emitter| {
1416                 Ok(InstrSeq::gather(vec![
1417                     emit_two_exprs(e, env, pos, e1, e2)?,
1418                     from_binop(e.options(), op)?,
1419                 ]))
1420             };
1421             if optimize_null_checks(e) {
1422                 match op {
1423                     B::Eqeqeq if e2.1.is_null() => emit_is_null(e, env, e1),
1424                     B::Eqeqeq if e1.1.is_null() => emit_is_null(e, env, e2),
1425                     B::Diff2 if e2.1.is_null() => Ok(InstrSeq::gather(vec![
1426                         emit_is_null(e, env, e1)?,
1427                         InstrSeq::make_not(),
1428                     ])),
1429                     B::Diff2 if e1.1.is_null() => Ok(InstrSeq::gather(vec![
1430                         emit_is_null(e, env, e2)?,
1431                         InstrSeq::make_not(),
1432                     ])),
1433                     _ => default(e),
1434                 }
1435             } else {
1436                 default(e)
1437             }
1438         }
1439     }
1442 fn emit_pipe(env: &Env, (_, e1, e2): &(aast_defs::Lid, tast::Expr, tast::Expr)) -> Result {
1443     unimplemented!("TODO(hrust)")
1446 fn emit_is_hint(env: &Env, pos: &Pos, h: &aast_defs::Hint) -> Result {
1447     unimplemented!("TODO(hrust)")
1450 fn emit_as(
1451     env: &Env,
1452     pos: &Pos,
1453     (e, h, is_nullable): &(tast::Expr, aast_defs::Hint, bool),
1454 ) -> Result {
1455     unimplemented!("TODO(hrust)")
1458 fn emit_cast(env: &Env, pos: &Pos, (h, e): &(aast_defs::Hint, tast::Expr)) -> Result {
1459     unimplemented!("TODO(hrust)")
1462 pub fn emit_unset_expr<Ex, Fb, En, Hi>(env: &Env, e: &aast::Expr<Ex, Fb, En, Hi>) -> Result {
1463     unimplemented!("TODO(hrust)")
1466 pub fn emit_set_range_expr(
1467     e: &mut Emitter,
1468     env: &mut Env,
1469     pos: &Pos,
1470     name: &str,
1471     kind: Setrange,
1472     args: &[tast::Expr],
1473     last_arg: Option<&tast::Expr>,
1474 ) -> Result {
1475     let raise_fatal = |msg: &str| {
1476         Err(emit_fatal::raise_fatal_parse(
1477             pos,
1478             format!("{} {}", name, msg),
1479         ))
1480     };
1482     // NOTE(hrust) last_arg is separated because the caller
1483     // would otherwise need to clone both Vec<&Expr> and Expr,
1484     // or it would need to pass chained FixedSizeIterators
1485     let n = args.len();
1486     let (base, offset, src, n) = match last_arg {
1487         Some(a) if n >= 2 => (a, &args[n - 1], &args[n - 2], n - 2),
1488         None if n >= 3 => (&args[n - 1], &args[n - 2], &args[n - 3], n - 3),
1489         _ => return raise_fatal("expects at least 3 arguments"),
1490     };
1491     let count_instrs = match args.get(n - 1) {
1492         Some(c) if kind.vec => emit_expr(e, env, c)?,
1493         None => InstrSeq::make_int(-1),
1494         _ => {
1495             return if !kind.vec {
1496                 raise_fatal("expects no more than 3 arguments")
1497             } else {
1498                 raise_fatal("expects no more than 4 arguments")
1499             }
1500         }
1501     };
1502     let (base_expr, cls_expr, base_setup, base_stack, cls_stack) = emit_base(
1503         e,
1504         env,
1505         EmitBaseArgs {
1506             is_object: false,
1507             null_coalesce_assignment: None,
1508             base_offset: 3,
1509             rhs_stack_size: 3,
1510         },
1511         MemberOpMode::Define,
1512         base,
1513     )?;
1514     Ok(InstrSeq::gather(vec![
1515         base_expr,
1516         cls_expr,
1517         emit_expr(e, env, offset)?,
1518         emit_expr(e, env, src)?,
1519         count_instrs,
1520         base_setup,
1521         InstrSeq::make_instr(Instruct::IFinal(InstructFinal::SetRangeM(
1522             (base_stack + cls_stack)
1523                 .try_into()
1524                 .expect("StackIndex overflow"),
1525             kind.op,
1526             kind.size.try_into().expect("Setrange size overflow"),
1527         ))),
1528     ]))
1531 pub fn is_reified_tparam(env: &Env, is_fun: bool, name: &str) -> Option<(usize, bool)> {
1532     let is = |tparams: &Vec<tast::Tparam>| {
1533         let is_soft = |ual: &Vec<tast::UserAttribute>| {
1534             ual.iter().any(|ua| &ua.name.1 == user_attributes::SOFT)
1535         };
1536         use tast::ReifyKind::*;
1537         tparams.iter().enumerate().find_map(|(i, tp)| {
1538             if (tp.reified == Reified || tp.reified == SoftReified) && tp.name.1 == name {
1539                 Some((i, is_soft(&tp.user_attributes)))
1540             } else {
1541                 None
1542             }
1543         })
1544     };
1545     if is_fun {
1546         env.scope.get_fun_tparams().and_then(is)
1547     } else {
1548         is(&env.scope.get_class_params().list)
1549     }
1552 /// Emit code for a base expression `expr` that forms part of
1553 /// an element access `expr[elem]` or field access `expr->fld`.
1554 /// The instructions are divided into three sections:
1555 ///   1. base and element/property expression instructions:
1556 ///      push non-trivial base and key values on the stack
1557 ///   2. base selector instructions: a sequence of Base/Dim instructions that
1558 ///      actually constructs the base address from "member keys" that are inlined
1559 ///      in the instructions, or pulled from the key values that
1560 ///      were pushed on the stack in section 1.
1561 ///   3. (constructed by the caller) a final accessor e.g. QueryM or setter
1562 ///      e.g. SetOpM instruction that has the final key inlined in the
1563 ///      instruction, or pulled from the key values that were pushed on the
1564 ///      stack in section 1.
1565 /// The function returns a triple (base_instrs, base_setup_instrs, stack_size)
1566 /// where base_instrs is section 1 above, base_setup_instrs is section 2, and
1567 /// stack_size is the number of values pushed onto the stack by section 1.
1569 /// For example, the r-value expression $arr[3][$ix+2]
1570 /// will compile to
1571 ///   # Section 1, pushing the value of $ix+2 on the stack
1572 ///   Int 2
1573 ///   CGetL2 $ix
1574 ///   AddO
1575 ///   # Section 2, constructing the base address of $arr[3]
1576 ///   BaseL $arr Warn
1577 ///   Dim Warn EI:3
1578 ///   # Section 3, indexing the array using the value at stack position 0 (EC:0)
1579 ///   QueryM 1 CGet EC:0
1580 ///)
1581 fn emit_base(
1582     e: &mut Emitter,
1583     env: &Env,
1584     args: EmitBaseArgs,
1585     mode: MemberOpMode,
1586     ex: &tast::Expr,
1587 ) -> Result<(InstrSeq, InstrSeq, InstrSeq, StackIndex, StackIndex)> {
1588     let _notice = BareThisOp::Notice;
1589     unimplemented!("TODO(hrust)")
1592 #[derive(Debug, Default)]
1593 struct EmitBaseArgs {
1594     is_object: bool,
1595     null_coalesce_assignment: Option<bool>,
1596     base_offset: StackIndex,
1597     rhs_stack_size: StackIndex,
1600 pub fn emit_ignored_expr(emitter: &mut Emitter, env: &Env, pos: &Pos, expr: &tast::Expr) -> Result {
1601     if let Some(es) = expr.1.as_expr_list() {
1602         Ok(InstrSeq::gather(
1603             es.iter()
1604                 .map(|e| emit_ignored_expr(emitter, env, pos, e))
1605                 .collect::<Result<Vec<_>>>()?,
1606         ))
1607     } else {
1608         Ok(InstrSeq::gather(vec![
1609             emit_expr(emitter, env, expr)?,
1610             emit_pos_then(emitter, pos, InstrSeq::make_popc())?,
1611         ]))
1612     }
1615 pub fn emit_lval_op(
1616     e: &mut Emitter,
1617     env: &Env,
1618     pos: &Pos,
1619     op: LValOp,
1620     expr1: &tast::Expr,
1621     expr2: Option<&tast::Expr>,
1622     null_coalesce_assignment: bool,
1623 ) -> Result {
1624     match (op, &expr1.1, expr2) {
1625         (LValOp::Set, tast::Expr_::List(l), Some(expr2)) => {
1626             let instr_rhs = emit_expr(e, env, expr2)?;
1627             let has_elements = l.iter().any(|e| !e.1.is_omitted());
1628             if !has_elements {
1629                 Ok(instr_rhs)
1630             } else {
1631                 scope::with_unnamed_local(e, |e, local| {
1632                     let loc = if can_use_as_rhs_in_list_assignment(&expr2.1)? {
1633                         Some(local.clone())
1634                     } else {
1635                         None
1636                     };
1637                     let (instr_lhs, instr_assign) =
1638                         emit_lval_op_list(e, env, pos, loc, &[], expr1, false)?;
1639                     Ok((
1640                         InstrSeq::gather(vec![
1641                             instr_lhs,
1642                             instr_rhs,
1643                             InstrSeq::make_popl(local.clone()),
1644                         ]),
1645                         instr_assign,
1646                         InstrSeq::make_pushl(local),
1647                     ))
1648                 })
1649             }
1650         }
1651         _ => e.local_scope(|e| {
1652             let (rhs_instrs, rhs_stack_size) = match expr2 {
1653                 None => (InstrSeq::Empty, 0),
1654                 Some(tast::Expr(_, tast::Expr_::Yield(af))) => {
1655                     let temp = e.local_gen_mut().get_unnamed();
1656                     (
1657                         InstrSeq::gather(vec![
1658                             emit_yield(e, env, pos, af)?,
1659                             InstrSeq::make_setl(temp.clone()),
1660                             InstrSeq::make_popc(),
1661                             InstrSeq::make_pushl(temp),
1662                         ]),
1663                         1,
1664                     )
1665                 }
1666                 Some(expr) => (emit_expr(e, env, expr)?, 1),
1667             };
1668             emit_lval_op_nonlist(
1669                 e,
1670                 env,
1671                 pos,
1672                 op,
1673                 expr1,
1674                 rhs_instrs,
1675                 rhs_stack_size,
1676                 null_coalesce_assignment,
1677             )
1678         }),
1679     }
1682 fn can_use_as_rhs_in_list_assignment(expr: &tast::Expr_) -> Result<bool> {
1683     use aast::Expr_ as E_;
1684     Ok(match expr {
1685         E_::Call(c)
1686             if ((c.1).1)
1687                 .as_id()
1688                 .map_or(false, |id| id.1 == special_functions::ECHO) =>
1689         {
1690             false
1691         }
1692         E_::Lvar(_)
1693         | E_::ArrayGet(_)
1694         | E_::ObjGet(_)
1695         | E_::ClassGet(_)
1696         | E_::PUAtom(_)
1697         | E_::Call(_)
1698         | E_::FunctionPointer(_)
1699         | E_::New(_)
1700         | E_::Record(_)
1701         | E_::ExprList(_)
1702         | E_::Yield(_)
1703         | E_::Cast(_)
1704         | E_::Eif(_)
1705         | E_::Array(_)
1706         | E_::Varray(_)
1707         | E_::Darray(_)
1708         | E_::Collection(_)
1709         | E_::Clone(_)
1710         | E_::Unop(_)
1711         | E_::As(_)
1712         | E_::Await(_) => true,
1713         E_::Pipe(p) => can_use_as_rhs_in_list_assignment(&(p.2).1)?,
1714         E_::Binop(b) if b.0.is_eq() => can_use_as_rhs_in_list_assignment(&(b.2).1)?,
1715         E_::Binop(b) => b.0.is_plus() || b.0.is_question_question() || b.0.is_any_eq(),
1716         E_::PUIdentifier(_) => {
1717             return Err(Unrecoverable(
1718                 "TODO(T35357243): Pocket Universes syntax must be erased by now".into(),
1719             ))
1720         }
1721         _ => false,
1722     })
1725 pub fn emit_lval_op_list(
1726     e: &mut Emitter,
1727     env: &Env,
1728     outer_pos: &Pos,
1729     local: Option<local::Type>,
1730     indices: &[isize],
1731     expr: &tast::Expr,
1732     last_usage: bool,
1733 ) -> Result<(InstrSeq, InstrSeq)> {
1734     unimplemented!()
1737 pub fn emit_lval_op_nonlist(
1738     e: &mut Emitter,
1739     env: &Env,
1740     outer_pos: &Pos,
1741     op: LValOp,
1742     expr: &tast::Expr,
1743     rhs_instrs: InstrSeq,
1744     rhs_stack_size: usize,
1745     null_coalesce_assignment: bool,
1746 ) -> Result {
1747     emit_lval_op_nonlist_steps(
1748         e,
1749         env,
1750         outer_pos,
1751         op,
1752         expr,
1753         rhs_instrs,
1754         rhs_stack_size,
1755         null_coalesce_assignment,
1756     )
1757     .map(|(lhs, rhs, setop)| InstrSeq::gather(vec![lhs, rhs, setop]))
1760 pub fn emit_final_global_op(e: &mut Emitter, pos: &Pos, op: LValOp) -> Result {
1761     use LValOp as L;
1762     match op {
1763         L::Set => emit_pos_then(e, pos, InstrSeq::make_setg()),
1764         L::SetOp(op) => Ok(InstrSeq::make_setopg(op)),
1765         L::IncDec(op) => Ok(InstrSeq::make_incdecg(op)),
1766         L::Unset => emit_pos_then(e, pos, InstrSeq::make_unsetg()),
1767     }
1770 pub fn emit_final_local_op(e: &mut Emitter, pos: &Pos, op: LValOp, lid: local::Type) -> Result {
1771     use LValOp as L;
1772     emit_pos_then(
1773         e,
1774         pos,
1775         match op {
1776             L::Set => InstrSeq::make_setl(lid),
1777             L::SetOp(op) => InstrSeq::make_setopl(lid, op),
1778             L::IncDec(op) => InstrSeq::make_incdecl(lid, op),
1779             L::Unset => InstrSeq::make_unsetl(lid),
1780         },
1781     )
1784 pub fn emit_lval_op_nonlist_steps(
1785     e: &mut Emitter,
1786     env: &Env,
1787     outer_pos: &Pos,
1788     op: LValOp,
1789     expr: &tast::Expr,
1790     rhs_instrs: InstrSeq,
1791     rhs_stack_size: usize,
1792     null_coalesce_assignment: bool,
1793 ) -> Result<(InstrSeq, InstrSeq, InstrSeq)> {
1794     let f = |env: &mut Env| {
1795         use tast::Expr_ as E_;
1796         let pos = &expr.0;
1797         Ok(match &expr.1 {
1798             E_::Lvar(v) if superglobals::is_any_global(local_id::get_name(&v.1)) => (
1799                 emit_pos_then(
1800                     e,
1801                     &v.0,
1802                     InstrSeq::make_string(string_utils::lstrip(local_id::get_name(&v.1), "$")),
1803                 )?,
1804                 rhs_instrs,
1805                 emit_final_global_op(e, outer_pos, op)?,
1806             ),
1807             E_::Lvar(v) if is_local_this(env, &v.1) && op.is_incdec() => (
1808                 emit_local(env, BareThisOp::Notice, v)?,
1809                 rhs_instrs,
1810                 InstrSeq::Empty,
1811             ),
1812             E_::Lvar(v) if !is_local_this(env, &v.1) || op == LValOp::Unset => {
1813                 (InstrSeq::Empty, rhs_instrs, {
1814                     let lid = get_local(e, env, &v.0, &(v.1).1)?;
1815                     emit_final_local_op(e, outer_pos, op, lid)?
1816                 })
1817             }
1818             E_::ArrayGet(_) => unimplemented!(),
1819             E_::ObjGet(_) => unimplemented!(),
1820             E_::ClassGet(_) => unimplemented!(),
1821             E_::Unop(uop) => (
1822                 InstrSeq::Empty,
1823                 rhs_instrs,
1824                 InstrSeq::gather(vec![
1825                     emit_lval_op_nonlist(
1826                         e,
1827                         env,
1828                         pos,
1829                         op,
1830                         &uop.1,
1831                         InstrSeq::Empty,
1832                         rhs_stack_size,
1833                         false,
1834                     )?,
1835                     from_unop(e.options(), &uop.0)?,
1836                 ]),
1837             ),
1838             _ => {
1839                 return Err(emit_fatal::raise_fatal_parse(
1840                     pos,
1841                     "Can't use return value in write context",
1842                 ))
1843             }
1844         })
1845     };
1846     // TODO(shiqicao): remove clone!
1847     let mut env = env.clone();
1848     match op {
1849         LValOp::Set | LValOp::SetOp(_) | LValOp::IncDec(_) => env.with_allows_array_append(f),
1850         _ => f(&mut env),
1851     }
1854 pub fn emit_reified_arg(
1855     _emitter: &mut Emitter,
1856     _env: &Env,
1857     _pos: &Pos,
1858     isas: bool,
1859     hint: &tast::Hint,
1860 ) -> Result<(InstrSeq, bool)> {
1861     unimplemented!("TODO(hrust)")
1864 pub fn get_local(e: &mut Emitter, env: &Env, pos: &Pos, s: &str) -> Result<local::Type> {
1865     if s == special_idents::DOLLAR_DOLLAR {
1866         unimplemented!()
1867     } else if special_idents::is_tmp_var(s) {
1868         Ok(e.local_gen().get_unnamed_for_tempname(s).clone())
1869     } else {
1870         Ok(local::Type::Named(s.into()))
1871     }
1874 pub fn emit_is_null(e: &mut Emitter, env: &Env, expr: &tast::Expr) -> Result {
1875     if let Some(tast::Lid(pos, id)) = expr.1.as_lvar() {
1876         if !is_local_this(env, id) {
1877             return Ok(InstrSeq::make_istypel(
1878                 get_local(e, env, pos, local_id::get_name(id))?,
1879                 IstypeOp::OpNull,
1880             ));
1881         }
1882     }
1884     Ok(InstrSeq::gather(vec![
1885         emit_expr(e, env, expr)?,
1886         InstrSeq::make_istypec(IstypeOp::OpNull),
1887     ]))
1890 pub fn emit_jmpnz(
1891     e: &mut Emitter,
1892     env: &Env,
1893     pos: &Pos,
1894     expr_: &tast::Expr_,
1895     label: &Label,
1896 ) -> Result<EmitJmpResult> {
1897     unimplemented!()
1900 pub fn emit_jmpz(
1901     e: &mut Emitter,
1902     env: &Env,
1903     expr: &tast::Expr,
1904     label: &Label,
1905 ) -> Result<EmitJmpResult> {
1906     let tast::Expr(pos, expr_) = expr;
1907     let opt = optimize_null_checks(e);
1908     Ok(
1909         match ast_constant_folder::expr_to_typed_value(e, &env.namespace, expr) {
1910             Ok(v) => {
1911                 let b: bool = v.into();
1912                 if b {
1913                     EmitJmpResult {
1914                         instrs: emit_pos_then(e, pos, InstrSeq::Empty)?,
1915                         is_fallthrough: true,
1916                         is_label_used: false,
1917                     }
1918                 } else {
1919                     EmitJmpResult {
1920                         instrs: emit_pos_then(e, pos, InstrSeq::make_jmp(label.clone()))?,
1921                         is_fallthrough: false,
1922                         is_label_used: true,
1923                     }
1924                 }
1925             }
1926             Err(_) => {
1927                 use {ast_defs::Uop as U, tast::Expr_ as E};
1928                 match expr_ {
1929                     E::Unop(uo) if uo.0 == U::Unot => {
1930                         emit_jmpnz(e, env, &(uo.1).0, &(uo.1).1, label)?
1931                     }
1932                     E::Binop(bo) if bo.0.is_barbar() => unimplemented!(),
1933                     E::Binop(bo) if bo.0.is_ampamp() => unimplemented!(),
1934                     E::Binop(bo)
1935                         if bo.0.is_eqeqeq()
1936                             && ((bo.1).1.is_null() || (bo.2).1.is_null())
1937                             && opt =>
1938                     {
1939                         let is_null =
1940                             emit_is_null(e, env, if (bo.1).1.is_null() { &bo.2 } else { &bo.1 })?;
1941                         EmitJmpResult {
1942                             instrs: emit_pos_then(
1943                                 e,
1944                                 pos,
1945                                 InstrSeq::gather(vec![is_null, InstrSeq::make_jmpz(label.clone())]),
1946                             )?,
1947                             is_fallthrough: true,
1948                             is_label_used: true,
1949                         }
1950                     }
1951                     E::Binop(bo)
1952                         if bo.0.is_diff2() && ((bo.1).1.is_null() || (bo.2).1.is_null()) && opt =>
1953                     {
1954                         let is_null =
1955                             emit_is_null(e, env, if (bo.1).1.is_null() { &bo.2 } else { &bo.1 })?;
1956                         EmitJmpResult {
1957                             instrs: emit_pos_then(
1958                                 e,
1959                                 pos,
1960                                 InstrSeq::gather(vec![
1961                                     is_null,
1962                                     InstrSeq::make_jmpnz(label.clone()),
1963                                 ]),
1964                             )?,
1965                             is_fallthrough: true,
1966                             is_label_used: true,
1967                         }
1968                     }
1969                     _ => {
1970                         let instr = emit_expr(e, env, expr)?;
1971                         EmitJmpResult {
1972                             instrs: emit_pos_then(
1973                                 e,
1974                                 pos,
1975                                 InstrSeq::gather(vec![instr, InstrSeq::make_jmpz(label.clone())]),
1976                             )?,
1977                             is_fallthrough: true,
1978                             is_label_used: true,
1979                         }
1980                     }
1981                 }
1982             }
1983         },
1984     )