move Ctx to naming_special_names.rs
[hiphop-php.git] / hphp / hack / src / hhbc / hhbc_by_ref / hhas_coeffects.rs
blob0ec9eb0bec30458d86b9f0b21e897b8646a0c295
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 ffi::{Pair, Slice, Str, Triple};
7 use hhbc_by_ref_hhbc_string_utils as string_utils;
8 use hhbc_by_ref_hhbc_string_utils::strip_ns;
9 use naming_special_names_rust::{self as sn, coeffects as c, coeffects::Ctx};
10 use oxidized::{
11     aast as a,
12     aast_defs::{Hint, Hint_},
13     ast_defs::Id,
16 #[derive(Debug)]
17 #[repr(C)]
18 pub struct HhasCtxConstant<'arena> {
19     pub name: Str<'arena>,
20     pub coeffects: Pair<Slice<'arena, Ctx>, Slice<'arena, Str<'arena>>>,
21     pub is_abstract: bool,
24 #[derive(Clone, Debug, Default)]
25 #[repr(C)]
26 pub struct HhasCoeffects<'arena> {
27     static_coeffects: Slice<'arena, Ctx>,
28     unenforced_static_coeffects: Slice<'arena, Str<'arena>>,
29     fun_param: Slice<'arena, usize>,
30     cc_param: Slice<'arena, Pair<usize, Str<'arena>>>,
31     cc_this: Slice<'arena, Slice<'arena, Str<'arena>>>,
32     cc_reified: Slice<'arena, Triple<bool, usize, Slice<'arena, Str<'arena>>>>,
33     closure_parent_scope: bool,
34     generator_this: bool,
35     caller: bool,
38 impl<'arena> HhasCoeffects<'arena> {
39     pub fn vec_to_string<T, F: Fn(&T) -> String>(v: &[T], f: F) -> Option<String> {
40         if v.is_empty() {
41             return None;
42         }
43         Some(v.iter().map(|x| f(x)).collect::<Vec<String>>().join(" "))
44     }
46     pub fn coeffects_to_hhas(coeffects: &Self) -> Vec<String> {
47         let mut results = vec![];
48         let static_coeffect =
49             HhasCoeffects::vec_to_string(coeffects.get_static_coeffects(), |c| c.to_string());
50         let unenforced_static_coeffects =
51             HhasCoeffects::vec_to_string(coeffects.get_unenforced_static_coeffects(), |c| {
52                 c.unsafe_as_str().to_string()
53             });
54         match (static_coeffect, unenforced_static_coeffects) {
55             (None, None) => {}
56             (Some(s), None) | (None, Some(s)) => results.push(format!(".coeffects_static {};", s)),
57             (Some(s1), Some(s2)) => results.push(format!(".coeffects_static {} {};", s1, s2)),
58         };
59         if let Some(str) =
60             HhasCoeffects::vec_to_string(coeffects.get_fun_param(), |c| c.to_string())
61         {
62             results.push(format!(".coeffects_fun_param {};", str));
63         }
64         if let Some(str) = HhasCoeffects::vec_to_string(coeffects.get_cc_param(), |c| {
65             format!("{} {}", c.0, c.1.unsafe_as_str())
66         }) {
67             results.push(format!(".coeffects_cc_param {};", str));
68         }
69         for v in coeffects.get_cc_this() {
70             match HhasCoeffects::vec_to_string(v.as_ref(), |c| c.unsafe_as_str().to_string()) {
71                 Some(str) => results.push(format!(".coeffects_cc_this {};", str)),
72                 None => panic!("Not possible"),
73             }
74         }
75         for v in coeffects.get_cc_reified() {
76             match HhasCoeffects::vec_to_string(v.2.as_ref(), |c| c.unsafe_as_str().to_string()) {
77                 Some(str) => results.push(format!(
78                     ".coeffects_cc_reified {}{} {};",
79                     if v.0 { "isClass " } else { "" },
80                     v.1,
81                     str
82                 )),
83                 None => panic!("Not possible"),
84             }
85         }
86         if coeffects.is_closure_parent_scope() {
87             results.push(".coeffects_closure_parent_scope;".to_string());
88         }
89         if coeffects.generator_this() {
90             results.push(".coeffects_generator_this;".to_string());
91         }
92         if coeffects.caller() {
93             results.push(".coeffects_caller;".to_string());
94         }
95         results
96     }
98     fn from_type_static(hint: &Hint) -> Option<Ctx> {
99         let Hint(_, h) = hint;
100         match &**h {
101             Hint_::Happly(Id(_, id), _) => match strip_ns(id.as_str()) {
102                 c::DEFAULTS => Some(Ctx::Defaults),
103                 c::RX_LOCAL => Some(Ctx::RxLocal),
104                 c::RX_SHALLOW => Some(Ctx::RxShallow),
105                 c::RX => Some(Ctx::Rx),
106                 c::WRITE_THIS_PROPS => Some(Ctx::WriteThisProps),
107                 c::WRITE_PROPS => Some(Ctx::WriteProps),
108                 c::POLICIED_OF | c::ZONED_WITH => Some(Ctx::ZonedWith),
109                 c::POLICIED_LOCAL | c::ZONED_LOCAL => Some(Ctx::ZonedLocal),
110                 c::POLICIED_SHALLOW | c::ZONED_SHALLOW => Some(Ctx::ZonedShallow),
111                 c::POLICIED | c::ZONED => Some(Ctx::Zoned),
112                 c::CONTROLLED | c::LEAK_SAFE => Some(Ctx::LeakSafe),
113                 c::GLOBALS => Some(Ctx::Globals),
114                 c::READ_GLOBALS => Some(Ctx::ReadGlobals),
115                 _ => None,
116             },
117             _ => None,
118         }
119     }
121     pub fn local_to_shallow(coeffects: &[Ctx]) -> Vec<Ctx> {
122         use Ctx::*;
123         let mut result = vec![];
124         for c in coeffects.iter() {
125             result.push(match c {
126                 RxLocal => RxShallow,
127                 ZonedLocal => ZonedShallow,
128                 _ => *c,
129             })
130         }
131         result
132     }
134     pub fn from_ctx_constant(hint: &Hint) -> (Vec<Ctx>, Vec<String>) {
135         let mut static_coeffects = vec![];
136         let mut unenforced_static_coeffects = vec![];
137         let Hint(_, h) = hint;
138         match &**h {
139             Hint_::Hintersection(hl) if hl.is_empty() => static_coeffects.push(Ctx::Pure),
140             Hint_::Hintersection(hl) => {
141                 for h in hl {
142                     let Hint(_, h_inner) = h;
143                     match &**h_inner {
144                         Hint_::Happly(Id(_, id), _) => {
145                             if let Some(c) = HhasCoeffects::from_type_static(h) {
146                                 static_coeffects.push(c)
147                             } else {
148                                 unenforced_static_coeffects.push(strip_ns(id.as_str()).to_string());
149                             }
150                         }
151                         _ => {}
152                     }
153                 }
154             }
155             _ => {}
156         }
157         (static_coeffects, unenforced_static_coeffects)
158     }
160     pub fn from_ast<Ex, En>(
161         alloc: &'arena bumpalo::Bump,
162         ctxs_opt: &Option<a::Contexts>,
163         params: impl AsRef<[a::FunParam<Ex, En>]>,
164         fun_tparams: impl AsRef<[a::Tparam<Ex, En>]>,
165         cls_tparams: impl AsRef<[a::Tparam<Ex, En>]>,
166     ) -> Self {
167         let mut static_coeffects = vec![];
168         let mut unenforced_static_coeffects: Vec<Str<'arena>> = vec![];
169         let mut fun_param = vec![];
170         let mut cc_param: Vec<Pair<usize, Str<'arena>>> = vec![];
171         let mut cc_this: Vec<Slice<'arena, Str<'arena>>> = vec![];
172         let mut cc_reified: Vec<Triple<bool, usize, Slice<'arena, Str<'arena>>>> = vec![];
174         let get_arg_pos = |name: &String| -> usize {
175             if let Some(pos) = params.as_ref().iter().position(|x| x.name == *name) {
176                 pos
177             } else {
178                 panic!("Invalid context");
179             }
180         };
182         let is_reified_tparam = |name: &str, is_class: bool| -> Option<usize> {
183             let tparam = if is_class {
184                 cls_tparams.as_ref()
185             } else {
186                 fun_tparams.as_ref()
187             };
188             tparam
189                 .iter()
190                 .position(|tp| tp.reified == a::ReifyKind::Reified && tp.name.1 == name)
191         };
193         // From coeffect syntax
194         if let Some(ctxs) = ctxs_opt {
195             if ctxs.1.is_empty() {
196                 static_coeffects.push(Ctx::Pure);
197             }
198             for ctx in &ctxs.1 {
199                 let Hint(_, h) = ctx;
200                 match &**h {
201                     Hint_::Happly(Id(_, id), _) => {
202                         if let Some(c) = HhasCoeffects::from_type_static(ctx) {
203                             static_coeffects.push(c)
204                         } else {
205                             unenforced_static_coeffects
206                                 .push(alloc.alloc_str(strip_ns(id.as_str())).into());
207                         }
208                     }
209                     Hint_::HfunContext(name) => fun_param.push(get_arg_pos(name)),
210                     Hint_::Haccess(Hint(_, hint), sids) => match &**hint {
211                         Hint_::Happly(Id(_, id), _) if !sids.is_empty() => {
212                             if strip_ns(id.as_str()) == sn::typehints::THIS {
213                                 cc_this.push(Slice::from_vec(
214                                     alloc,
215                                     sids.into_iter()
216                                         .map(|Id(_, id)| alloc.alloc_str(id).into())
217                                         .collect(),
218                                 ));
219                             } else if let Some(idx) = is_reified_tparam(id.as_str(), false) {
220                                 cc_reified.push(
221                                     (
222                                         false,
223                                         idx,
224                                         Slice::from_vec(
225                                             alloc,
226                                             sids.into_iter()
227                                                 .map(|Id(_, id)| alloc.alloc_str(id).into())
228                                                 .collect(),
229                                         ),
230                                     )
231                                         .into(),
232                                 );
233                             } else if let Some(idx) = is_reified_tparam(id.as_str(), true) {
234                                 cc_reified.push(
235                                     (
236                                         true,
237                                         idx,
238                                         Slice::from_vec(
239                                             alloc,
240                                             sids.into_iter()
241                                                 .map(|Id(_, id)| alloc.alloc_str(id).into())
242                                                 .collect(),
243                                         ),
244                                     )
245                                         .into(),
246                                 );
247                             }
248                         }
249                         Hint_::Hvar(name) if sids.len() == 1 => {
250                             let pos = get_arg_pos(name);
251                             let Id(_, sid_name) = &sids[0];
252                             cc_param.push((pos, alloc.alloc_str(sid_name).into()).into());
253                         }
254                         _ => {}
255                     },
256                     _ => {}
257                 }
258             }
259         }
261         // If there are no static coeffects but there are coeffect rules, then
262         // the static coeffects are pure
263         if static_coeffects.is_empty()
264             && (!fun_param.is_empty()
265                 || !cc_param.is_empty()
266                 || !cc_this.is_empty()
267                 || !cc_reified.is_empty())
268         {
269             static_coeffects.push(Ctx::Pure);
270         }
272         Self {
273             static_coeffects: Slice::from_vec(alloc, static_coeffects),
274             unenforced_static_coeffects: Slice::from_vec(alloc, unenforced_static_coeffects),
275             fun_param: Slice::from_vec(alloc, fun_param),
276             cc_param: Slice::from_vec(alloc, cc_param),
277             cc_this: Slice::from_vec(alloc, cc_this),
278             cc_reified: Slice::from_vec(alloc, cc_reified),
279             ..HhasCoeffects::default()
280         }
281     }
283     pub fn pure(alloc: &'arena bumpalo::Bump) -> Self {
284         Self {
285             static_coeffects: Slice::from_vec(alloc, vec![Ctx::Pure]),
286             ..HhasCoeffects::default()
287         }
288     }
290     pub fn with_backdoor(&self, alloc: &'arena bumpalo::Bump) -> Self {
291         Self {
292             unenforced_static_coeffects: Slice::from_vec(alloc, vec![Str::from(c::BACKDOOR)]),
293             ..self.clone()
294         }
295     }
297     pub fn inherit_to_child_closure(&self, alloc: &'arena bumpalo::Bump) -> Self {
298         let static_coeffects = HhasCoeffects::local_to_shallow(self.get_static_coeffects());
299         if self.has_coeffect_rules() {
300             Self {
301                 static_coeffects: Slice::from_vec(alloc, static_coeffects),
302                 closure_parent_scope: true,
303                 ..HhasCoeffects::default()
304             }
305         } else {
306             Self {
307                 static_coeffects: Slice::from_vec(alloc, static_coeffects),
308                 ..self.clone()
309             }
310         }
311     }
313     pub fn with_gen_coeffect(&self) -> Self {
314         Self {
315             generator_this: true,
316             ..self.clone()
317         }
318     }
320     pub fn with_caller(&self, alloc: &'arena bumpalo::Bump) -> Self {
321         Self {
322             static_coeffects: Slice::from_vec(alloc, vec![Ctx::Pure]),
323             unenforced_static_coeffects: Slice::empty(),
324             caller: true,
325             ..self.clone()
326         }
327     }
329     pub fn get_static_coeffects(&self) -> &[Ctx] {
330         self.static_coeffects.as_ref()
331     }
333     pub fn get_unenforced_static_coeffects(&self) -> &[Str<'arena>] {
334         self.unenforced_static_coeffects.as_ref()
335     }
337     pub fn get_fun_param(&self) -> &[usize] {
338         self.fun_param.as_ref()
339     }
341     pub fn get_cc_param(&self) -> &[Pair<usize, Str<'arena>>] {
342         self.cc_param.as_ref()
343     }
345     pub fn get_cc_this(&self) -> &[Slice<'arena, Str<'arena>>] {
346         self.cc_this.as_ref()
347     }
349     pub fn get_cc_reified(&self) -> &[Triple<bool, usize, Slice<'arena, Str<'arena>>>] {
350         self.cc_reified.as_ref()
351     }
353     pub fn generator_this(&self) -> bool {
354         self.generator_this
355     }
357     pub fn caller(&self) -> bool {
358         self.caller
359     }
361     fn has_coeffect_rules(&self) -> bool {
362         !self.fun_param.is_empty()
363             || !self.cc_param.is_empty()
364             || !self.cc_this.is_empty()
365             || !self.cc_reified.is_empty()
366             || self.closure_parent_scope
367             || self.generator_this
368             || self.caller
369     }
371     pub fn has_coeffects_local(&self) -> bool {
372         self.has_coeffect_rules() && !self.generator_this()
373     }
375     pub fn is_closure_parent_scope(&self) -> bool {
376         self.closure_parent_scope
377     }
379     pub fn is_86caller(&self) -> bool {
380         !self.has_coeffect_rules()
381             && self.static_coeffects.len() == 0
382             && self.unenforced_static_coeffects.len() == 1
383             && self.unenforced_static_coeffects.as_ref()[0]
384                 == string_utils::coeffects::CALLER.into()
385     }
388 #[allow(clippy::needless_lifetimes)]
389 #[no_mangle]
390 pub unsafe extern "C" fn no_call_compile_only_USED_TYPES_hhas_ctx_constant<'arena>(
391     _: HhasCtxConstant<'arena>,
392 ) {
393     unimplemented!()
396 #[allow(clippy::needless_lifetimes)]
397 #[no_mangle]
398 pub unsafe extern "C" fn no_call_compile_only_USED_TYPES_hhas_coeffects<'arena>(
399     _: HhasCoeffects<'arena>,
400 ) {
401     unimplemented!()