1 // Copyright (c) Facebook, Inc. and its affiliates.
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 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};
12 aast_defs::{Hint, Hint_},
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)]
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,
38 impl<'arena> HhasCoeffects<'arena> {
39 pub fn vec_to_string<T, F: Fn(&T) -> String>(v: &[T], f: F) -> Option<String> {
43 Some(v.iter().map(|x| f(x)).collect::<Vec<String>>().join(" "))
46 pub fn coeffects_to_hhas(coeffects: &Self) -> Vec<String> {
47 let mut results = vec![];
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()
54 match (static_coeffect, unenforced_static_coeffects) {
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)),
60 HhasCoeffects::vec_to_string(coeffects.get_fun_param(), |c| c.to_string())
62 results.push(format!(".coeffects_fun_param {};", str));
64 if let Some(str) = HhasCoeffects::vec_to_string(coeffects.get_cc_param(), |c| {
65 format!("{} {}", c.0, c.1.unsafe_as_str())
67 results.push(format!(".coeffects_cc_param {};", str));
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"),
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 { "" },
83 None => panic!("Not possible"),
86 if coeffects.is_closure_parent_scope() {
87 results.push(".coeffects_closure_parent_scope;".to_string());
89 if coeffects.generator_this() {
90 results.push(".coeffects_generator_this;".to_string());
92 if coeffects.caller() {
93 results.push(".coeffects_caller;".to_string());
98 fn from_type_static(hint: &Hint) -> Option<Ctx> {
99 let Hint(_, h) = hint;
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),
121 pub fn local_to_shallow(coeffects: &[Ctx]) -> Vec<Ctx> {
123 let mut result = vec![];
124 for c in coeffects.iter() {
125 result.push(match c {
126 RxLocal => RxShallow,
127 ZonedLocal => ZonedShallow,
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;
139 Hint_::Hintersection(hl) if hl.is_empty() => static_coeffects.push(Ctx::Pure),
140 Hint_::Hintersection(hl) => {
142 let Hint(_, h_inner) = h;
144 Hint_::Happly(Id(_, id), _) => {
145 if let Some(c) = HhasCoeffects::from_type_static(h) {
146 static_coeffects.push(c)
148 unenforced_static_coeffects.push(strip_ns(id.as_str()).to_string());
157 (static_coeffects, unenforced_static_coeffects)
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>]>,
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) {
178 panic!("Invalid context");
182 let is_reified_tparam = |name: &str, is_class: bool| -> Option<usize> {
183 let tparam = if is_class {
190 .position(|tp| tp.reified == a::ReifyKind::Reified && tp.name.1 == name)
193 // From coeffect syntax
194 if let Some(ctxs) = ctxs_opt {
195 if ctxs.1.is_empty() {
196 static_coeffects.push(Ctx::Pure);
199 let Hint(_, h) = ctx;
201 Hint_::Happly(Id(_, id), _) => {
202 if let Some(c) = HhasCoeffects::from_type_static(ctx) {
203 static_coeffects.push(c)
205 unenforced_static_coeffects
206 .push(alloc.alloc_str(strip_ns(id.as_str())).into());
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(
216 .map(|Id(_, id)| alloc.alloc_str(id).into())
219 } else if let Some(idx) = is_reified_tparam(id.as_str(), false) {
227 .map(|Id(_, id)| alloc.alloc_str(id).into())
233 } else if let Some(idx) = is_reified_tparam(id.as_str(), true) {
241 .map(|Id(_, id)| alloc.alloc_str(id).into())
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());
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())
269 static_coeffects.push(Ctx::Pure);
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()
283 pub fn pure(alloc: &'arena bumpalo::Bump) -> Self {
285 static_coeffects: Slice::from_vec(alloc, vec![Ctx::Pure]),
286 ..HhasCoeffects::default()
290 pub fn with_backdoor(&self, alloc: &'arena bumpalo::Bump) -> Self {
292 unenforced_static_coeffects: Slice::from_vec(alloc, vec![Str::from(c::BACKDOOR)]),
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() {
301 static_coeffects: Slice::from_vec(alloc, static_coeffects),
302 closure_parent_scope: true,
303 ..HhasCoeffects::default()
307 static_coeffects: Slice::from_vec(alloc, static_coeffects),
313 pub fn with_gen_coeffect(&self) -> Self {
315 generator_this: true,
320 pub fn with_caller(&self, alloc: &'arena bumpalo::Bump) -> Self {
322 static_coeffects: Slice::from_vec(alloc, vec![Ctx::Pure]),
323 unenforced_static_coeffects: Slice::empty(),
329 pub fn get_static_coeffects(&self) -> &[Ctx] {
330 self.static_coeffects.as_ref()
333 pub fn get_unenforced_static_coeffects(&self) -> &[Str<'arena>] {
334 self.unenforced_static_coeffects.as_ref()
337 pub fn get_fun_param(&self) -> &[usize] {
338 self.fun_param.as_ref()
341 pub fn get_cc_param(&self) -> &[Pair<usize, Str<'arena>>] {
342 self.cc_param.as_ref()
345 pub fn get_cc_this(&self) -> &[Slice<'arena, Str<'arena>>] {
346 self.cc_this.as_ref()
349 pub fn get_cc_reified(&self) -> &[Triple<bool, usize, Slice<'arena, Str<'arena>>>] {
350 self.cc_reified.as_ref()
353 pub fn generator_this(&self) -> bool {
357 pub fn caller(&self) -> bool {
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
371 pub fn has_coeffects_local(&self) -> bool {
372 self.has_coeffect_rules() && !self.generator_this()
375 pub fn is_closure_parent_scope(&self) -> bool {
376 self.closure_parent_scope
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()
388 #[allow(clippy::needless_lifetimes)]
390 pub unsafe extern "C" fn no_call_compile_only_USED_TYPES_hhas_ctx_constant<'arena>(
391 _: HhasCtxConstant<'arena>,
396 #[allow(clippy::needless_lifetimes)]
398 pub unsafe extern "C" fn no_call_compile_only_USED_TYPES_hhas_coeffects<'arena>(
399 _: HhasCoeffects<'arena>,