Fix closure locals
[hiphop-php.git] / hphp / hack / src / hackc / ir / ir_core / func_builder_ex.rs
blobf855f24e694301d2dee8f471d4bdf6e26398cf2c
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 std::sync::Arc;
8 use crate::instr::Hhbc;
9 use crate::instr::Predicate;
10 use crate::type_struct::TypeStruct;
11 use crate::BaseType;
12 use crate::Constant;
13 use crate::EnforceableType;
14 use crate::FuncBuilder;
15 use crate::FunctionId;
16 use crate::Instr;
17 use crate::IsTypeOp;
18 use crate::LocId;
19 use crate::TypeConstraintFlags;
20 use crate::TypeStructResolveOp;
21 use crate::ValueId;
23 /// Helpers for emitting more complex constructs.
24 pub trait FuncBuilderEx {
25     /// Build an if/then:
26     ///     jmp_t true_br, false_br
27     ///     true_br:
28     ///         f()
29     ///         jmp false_br
30     ///     false_br:
31     ///
32     fn emit_if_then(&mut self, pred: ValueId, loc: LocId, f: impl FnOnce(&mut Self));
34     /// Build a conditional:
35     ///     pred ? b() : c()
36     ///
37     /// Note that although the callback only returns a single Instr it can emit
38     /// as many instrs as it wants simply returning the final one. In addition
39     /// it can return a tombstone to indicate that it already emitted everything
40     /// it needed.
41     fn emit_if_then_else(
42         &mut self,
43         pred: ValueId,
44         loc: LocId,
45         f_true: impl FnOnce(&mut Self) -> Instr,
46         f_false: impl FnOnce(&mut Self) -> Instr,
47     ) -> ValueId;
49     /// Emit a constant string.
50     fn emit_string(&mut self, s: &str) -> ValueId;
52     /// Build an `is` check.
53     fn is(&mut self, vid: ValueId, ty: &EnforceableType, loc: LocId) -> Instr;
55     /// Emit an `is` check.  This behaves like `is()` but instead of returning
56     /// the final Instr it emits it.
57     fn emit_is(&mut self, vid: ValueId, ty: &EnforceableType, loc: LocId) -> ValueId;
59     fn todo_fake_instr(&mut self, reason: &str, loc: LocId) -> Instr;
61     fn emit_todo_fake_instr(&mut self, reason: &str, loc: LocId) -> ValueId;
64 impl<'a> FuncBuilderEx for FuncBuilder<'a> {
65     fn emit_if_then(&mut self, pred: ValueId, loc: LocId, f: impl FnOnce(&mut Self)) {
66         let join_bid = self.alloc_bid();
67         let true_bid = self.alloc_bid();
68         self.emit(Instr::jmp_op(
69             pred,
70             Predicate::NonZero,
71             true_bid,
72             join_bid,
73             loc,
74         ));
75         self.start_block(true_bid);
76         f(self);
77         if !self.func.is_terminated(self.cur_bid()) {
78             self.emit(Instr::jmp(join_bid, loc));
79         }
80         self.start_block(join_bid);
81     }
83     fn emit_if_then_else(
84         &mut self,
85         pred: ValueId,
86         loc: LocId,
87         f_true: impl FnOnce(&mut Self) -> Instr,
88         f_false: impl FnOnce(&mut Self) -> Instr,
89     ) -> ValueId {
90         // b_true:
91         //   arg = f_true()
92         //   jmp b_join(arg)
93         // b_false:
94         //   arg = f_false()
95         //   jmp b_join(arg)
96         // b_join(arg):
98         let mut join_bid = None;
99         let true_bid = self.alloc_bid();
100         let false_bid = self.alloc_bid();
101         self.emit(Instr::jmp_op(
102             pred,
103             Predicate::NonZero,
104             true_bid,
105             false_bid,
106             loc,
107         ));
109         self.start_block(true_bid);
110         let instr = f_true(self);
111         if !instr.is_tombstone() {
112             let terminated = matches!(instr, Instr::Terminator(_));
113             let arg = self.emit(instr);
114             if !terminated {
115                 let target = self.alloc_bid();
116                 join_bid = Some(target);
117                 self.emit(Instr::jmp_args(target, &[arg], loc));
118             }
119         }
121         self.start_block(false_bid);
122         let instr = f_false(self);
123         if !instr.is_tombstone() {
124             let terminated = matches!(instr, Instr::Terminator(_));
125             let arg = self.emit(instr);
126             if !terminated {
127                 let target = join_bid.get_or_insert_with(|| self.alloc_bid());
128                 self.emit(Instr::jmp_args(*target, &[arg], loc));
129             }
130         }
132         if let Some(join_bid) = join_bid {
133             self.start_block(join_bid);
134             self.alloc_param()
135         } else {
136             ValueId::none()
137         }
138     }
140     fn emit_string(&mut self, s: &str) -> ValueId {
141         self.emit_constant(Constant::String(self.strings.intern_str(s)))
142     }
144     fn is(&mut self, vid: ValueId, ety: &EnforceableType, loc: LocId) -> Instr {
145         if ety.modifiers == TypeConstraintFlags::NoFlags
146             || ety.modifiers == TypeConstraintFlags::ExtendedHint
147         {
148             match ety.ty {
149                 BaseType::AnyArray => self.todo_fake_instr("BaseType::AnyArray", loc),
150                 BaseType::Arraykey => self.todo_fake_instr("BaseType::Arraykey", loc),
151                 BaseType::Bool => self.todo_fake_instr("BaseType::Bool", loc),
152                 BaseType::Class(cid) => {
153                     let constant = Constant::Array(Arc::new(
154                         TypeStruct::Unresolved(cid).into_typed_value(&self.strings),
155                     ));
156                     let adata = self.emit_constant(constant);
157                     Instr::Hhbc(Hhbc::IsTypeStructC(
158                         [vid, adata],
159                         TypeStructResolveOp::Resolve,
160                         loc,
161                     ))
162                 }
163                 BaseType::Classname => self.todo_fake_instr("BaseType::Classname", loc),
164                 BaseType::Darray => self.todo_fake_instr("BaseType::Darray", loc),
165                 BaseType::Dict => self.todo_fake_instr("BaseType::Dict", loc),
166                 BaseType::Float => self.todo_fake_instr("BaseType::Float", loc),
167                 BaseType::Int => Instr::Hhbc(Hhbc::IsTypeC(vid, IsTypeOp::Int, loc)),
168                 BaseType::Keyset => self.todo_fake_instr("BaseType::Keyset", loc),
169                 BaseType::Mixed => self.todo_fake_instr("BaseType::Mixed", loc),
170                 BaseType::None => self.todo_fake_instr("BaseType::None", loc),
171                 BaseType::Nonnull => self.todo_fake_instr("BaseType::Nonnull", loc),
172                 BaseType::Noreturn => self.todo_fake_instr("BaseType::Noreturn", loc),
173                 BaseType::Nothing => self.todo_fake_instr("BaseType::Nothing", loc),
174                 BaseType::Null => self.todo_fake_instr("BaseType::Null", loc),
175                 BaseType::Num => self.todo_fake_instr("BaseType::Num", loc),
176                 BaseType::Resource => self.todo_fake_instr("BaseType::Resource", loc),
177                 BaseType::String => Instr::Hhbc(Hhbc::IsTypeC(vid, IsTypeOp::Str, loc)),
178                 BaseType::This => Instr::Hhbc(Hhbc::IsLateBoundCls(vid, loc)),
179                 BaseType::Typename => self.todo_fake_instr("BaseType::Typename", loc),
180                 BaseType::Varray => self.todo_fake_instr("BaseType::Varray", loc),
181                 BaseType::VarrayOrDarray => self.todo_fake_instr("BaseType::VarrayOrDarray", loc),
182                 BaseType::Vec => self.todo_fake_instr("BaseType::Vec", loc),
183                 BaseType::VecOrDict => self.todo_fake_instr("BaseType::VecOrDict", loc),
184                 BaseType::Void => self.todo_fake_instr("BaseType::Void", loc),
185             }
186         } else {
187             self.todo_fake_instr(&format!("Unhandled modifiers: {:?}", ety.modifiers), loc)
188         }
189     }
191     fn emit_is(&mut self, vid: ValueId, ty: &EnforceableType, loc: LocId) -> ValueId {
192         let instr = self.is(vid, ty, loc);
193         self.emit(instr)
194     }
196     fn todo_fake_instr(&mut self, reason: &str, loc: LocId) -> Instr {
197         let op = self.emit_string(reason);
198         let fid = FunctionId::from_str("todo", &self.strings);
199         Instr::simple_call(fid, &[op], loc)
200     }
202     fn emit_todo_fake_instr(&mut self, reason: &str, loc: LocId) -> ValueId {
203         let instr = self.todo_fake_instr(reason, loc);
204         self.emit(instr)
205     }