renaming Ast_defs `class_kind` into `classish_kind`
[hiphop-php.git] / hphp / hack / src / hhbc / hhbc_by_ref / emit_class.rs
blob5f76efddf268ba26ef4518ed65acf0c78e456777
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 decl_provider::DeclProvider;
7 use ffi::{Maybe::*, Slice, Str};
8 use hhbc_by_ref_emit_attribute as emit_attribute;
9 use hhbc_by_ref_emit_body as emit_body;
10 use hhbc_by_ref_emit_expression as emit_expression;
11 use hhbc_by_ref_emit_fatal as emit_fatal;
12 use hhbc_by_ref_emit_memoize_method as emit_memoize_method;
13 use hhbc_by_ref_emit_method as emit_method;
14 use hhbc_by_ref_emit_pos as emit_pos;
15 use hhbc_by_ref_emit_property as emit_property;
16 use hhbc_by_ref_emit_symbol_refs as emit_symbol_refs;
17 use hhbc_by_ref_emit_type_constant as emit_type_constant;
18 use hhbc_by_ref_emit_type_hint as emit_type_hint;
19 use hhbc_by_ref_emit_xhp as emit_xhp;
20 use hhbc_by_ref_env::{emitter::Emitter, Env};
21 use hhbc_by_ref_hhas_attribute as hhas_attribute;
22 use hhbc_by_ref_hhas_class::{HhasClass, HhasClassFlags, TraitReqKind};
23 use hhbc_by_ref_hhas_coeffects::{HhasCoeffects, HhasCtxConstant};
24 use hhbc_by_ref_hhas_constant::{self as hhas_constant, HhasConstant};
25 use hhbc_by_ref_hhas_method::{HhasMethod, HhasMethodFlags};
26 use hhbc_by_ref_hhas_param::HhasParam;
27 use hhbc_by_ref_hhas_pos::Span;
28 use hhbc_by_ref_hhas_property::HhasProperty;
29 use hhbc_by_ref_hhas_type_const::HhasTypeConstant;
30 use hhbc_by_ref_hhas_xhp_attribute::HhasXhpAttribute;
31 use hhbc_by_ref_hhbc_ast::{FatalOp, FcallArgs, FcallFlags, ReadOnlyOp, SpecialClsRef};
32 use hhbc_by_ref_hhbc_id::r#const;
33 use hhbc_by_ref_hhbc_id::{self as hhbc_id, class, method, prop, Id};
34 use hhbc_by_ref_hhbc_string_utils as string_utils;
35 use hhbc_by_ref_instruction_sequence::{instr, InstrSeq, Result};
36 use hhbc_by_ref_label as label;
37 use hhbc_by_ref_local::Local;
38 use hhbc_by_ref_runtime::TypedValue;
39 use naming_special_names_rust as special_names;
40 use oxidized::{
41     ast::{self as tast, Hint, ReifyKind, Visibility},
42     namespace_env,
43     pos::Pos,
46 use std::collections::BTreeMap;
48 fn add_symbol_refs<'arena, 'decl, D: DeclProvider<'decl>>(
49     alloc: &'arena bumpalo::Bump,
50     emitter: &mut Emitter<'arena, 'decl, D>,
51     base: &Option<class::ClassType<'arena>>,
52     implements: &[class::ClassType<'arena>],
53     uses: &[&str],
54     requirements: &[(class::ClassType<'arena>, TraitReqKind)],
55 ) {
56     base.iter()
57         .for_each(|x| emit_symbol_refs::add_class(alloc, emitter, *x));
58     implements
59         .iter()
60         .for_each(|x| emit_symbol_refs::add_class(alloc, emitter, *x));
61     uses.iter().for_each(|x| {
62         emit_symbol_refs::add_class(alloc, emitter, class::ClassType::from_ast_name(alloc, x))
63     });
64     requirements
65         .iter()
66         .for_each(|(x, _)| emit_symbol_refs::add_class(alloc, emitter, *x));
69 fn make_86method<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
70     alloc: &'arena bumpalo::Bump,
71     emitter: &mut Emitter<'arena, 'decl, D>,
72     name: method::MethodType<'arena>,
73     params: Vec<HhasParam<'arena>>,
74     is_static: bool,
75     visibility: Visibility,
76     is_abstract: bool,
77     span: Span,
78     instrs: InstrSeq<'arena>,
79 ) -> Result<HhasMethod<'arena>> {
80     // TODO: move this. We just know that there are no iterators in 86methods
81     emitter.iterator_mut().reset();
83     let mut flags = HhasMethodFlags::empty();
84     flags.set(HhasMethodFlags::NO_INJECTION, true);
85     flags.set(HhasMethodFlags::IS_ABSTRACT, is_abstract);
86     flags.set(HhasMethodFlags::IS_STATIC, is_static);
88     let attributes = vec![];
89     let coeffects = HhasCoeffects::default();
91     let method_decl_vars = vec![];
92     let method_return_type = None;
93     let method_doc_comment = None;
94     let method_is_memoize_wrapper = false;
95     let method_is_memoize_wrapper_lsb = false;
96     let method_env = None;
98     let body = emit_body::make_body(
99         alloc,
100         emitter,
101         instrs,
102         method_decl_vars,
103         method_is_memoize_wrapper,
104         method_is_memoize_wrapper_lsb,
105         0,
106         vec![],
107         vec![],
108         params,
109         method_return_type,
110         method_doc_comment,
111         method_env,
112     )?;
114     Ok(HhasMethod {
115         body,
116         attributes,
117         name,
118         flags,
119         span,
120         coeffects,
121         visibility,
122     })
125 fn from_extends<'arena>(
126     alloc: &'arena bumpalo::Bump,
127     is_enum: bool,
128     is_enum_class: bool,
129     extends: &[tast::Hint],
130 ) -> Option<hhbc_id::class::ClassType<'arena>> {
131     if is_enum {
132         // Do not use special_names:: as there's a prefix \ which breaks HHVM
133         if is_enum_class {
134             Some(hhbc_id::class::from_raw_string(
135                 alloc,
136                 "HH\\BuiltinEnumClass",
137             ))
138         } else {
139             Some(hhbc_id::class::from_raw_string(alloc, "HH\\BuiltinEnum"))
140         }
141     } else {
142         extends
143             .first()
144             .map(|x| emit_type_hint::hint_to_class(alloc, x))
145     }
148 fn from_implements<'arena>(
149     alloc: &'arena bumpalo::Bump,
150     implements: &[tast::Hint],
151 ) -> Vec<hhbc_id::class::ClassType<'arena>> {
152     implements
153         .iter()
154         .map(|x| emit_type_hint::hint_to_class(alloc, x))
155         .collect()
158 fn from_includes<'arena>(
159     alloc: &'arena bumpalo::Bump,
160     includes: &[tast::Hint],
161 ) -> Vec<hhbc_id::class::ClassType<'arena>> {
162     includes
163         .iter()
164         .map(|x| emit_type_hint::hint_to_class(alloc, x))
165         .collect()
168 fn from_type_constant<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
169     alloc: &'arena bumpalo::Bump,
170     emitter: &mut Emitter<'arena, 'decl, D>,
171     tc: &'a tast::ClassTypeconstDef,
172 ) -> Result<HhasTypeConstant<'arena>> {
173     use tast::ClassTypeconst::*;
174     let name = tc.name.1.to_string();
176     let initializer = match &tc.kind {
177         TCAbstract(tast::ClassAbstractTypeconst { default: None, .. }) => None,
178         TCAbstract(tast::ClassAbstractTypeconst {
179             default: Some(init),
180             ..
181         })
182         | TCPartiallyAbstract(tast::ClassPartiallyAbstractTypeconst { type_: init, .. })
183         | TCConcrete(tast::ClassConcreteTypeconst { c_tc_type: init }) => {
184             // TODO: Deal with the constraint
185             // Type constants do not take type vars hence tparams:[]
186             Some(emit_type_constant::hint_to_type_constant(
187                 alloc,
188                 emitter.options(),
189                 &[],
190                 &BTreeMap::new(),
191                 init,
192                 false,
193                 false,
194             )?)
195         }
196     };
198     let is_abstract = match &tc.kind {
199         TCConcrete(_) => false,
200         _ => true,
201     };
203     Ok(HhasTypeConstant {
204         name,
205         initializer,
206         is_abstract,
207     })
210 fn from_ctx_constant(tc: &tast::ClassTypeconstDef) -> Result<HhasCtxConstant> {
211     use tast::ClassTypeconst::*;
212     let name = tc.name.1.to_string();
213     let coeffects = match &tc.kind {
214         TCAbstract(tast::ClassAbstractTypeconst { default: None, .. }) => (vec![], vec![]),
215         TCPartiallyAbstract(_) => (vec![], vec![]), // does not parse
216         TCAbstract(tast::ClassAbstractTypeconst {
217             default: Some(hint),
218             ..
219         })
220         | TCConcrete(tast::ClassConcreteTypeconst { c_tc_type: hint }) => {
221             HhasCoeffects::from_ctx_constant(hint)
222         }
223     };
224     let is_abstract = match &tc.kind {
225         TCConcrete(_) => false,
226         _ => true,
227     };
228     Ok(HhasCtxConstant {
229         name,
230         coeffects,
231         is_abstract,
232     })
235 fn from_class_elt_classvars<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
236     alloc: &'arena bumpalo::Bump,
237     emitter: &mut Emitter<'arena, 'decl, D>,
238     ast_class: &'a tast::Class_,
239     class_is_const: bool,
240     tparams: &[&str],
241 ) -> Result<Vec<HhasProperty<'arena>>> {
242     // TODO: we need to emit doc comments for each property,
243     // not one per all properties on the same line
244     // The doc comment is only for the first name in the list.
245     // Currently this is organized in the ast_to_nast module
246     ast_class
247         .vars
248         .iter()
249         .map(|cv| {
250             let hint = if cv.is_promoted_variadic {
251                 None
252             } else {
253                 cv.type_.1.as_ref()
254             };
256             emit_property::from_ast(
257                 alloc,
258                 emitter,
259                 ast_class,
260                 tparams,
261                 class_is_const,
262                 emit_property::FromAstArgs {
263                     user_attributes: &cv.user_attributes,
264                     id: &cv.id,
265                     initial_value: &cv.expr,
266                     typehint: hint,
267                     // Doc comments are weird. T40098274
268                     doc_comment: cv.doc_comment.clone(),
269                     visibility: cv.visibility, // This used to be cv_kinds
270                     is_static: cv.is_static,
271                     is_abstract: cv.abstract_,
272                     is_readonly: cv.readonly,
273                 },
274             )
275         })
276         .collect::<Result<Vec<_>>>()
279 fn from_class_elt_constants<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
280     emitter: &mut Emitter<'arena, 'decl, D>,
281     env: &Env<'a, 'arena>,
282     class_: &'a tast::Class_,
283 ) -> Result<Vec<HhasConstant<'arena>>> {
284     use oxidized::aast::ClassConstKind::*;
285     class_
286         .consts
287         .iter()
288         .map(|x| {
289             let (is_abstract, init_opt) = match &x.kind {
290                 CCAbstract(default) => (true, default.as_ref()),
291                 CCConcrete(expr) => (false, Some(expr)),
292             };
293             hhas_constant::from_ast(emitter, env, &x.id, is_abstract, init_opt)
294         })
295         .collect()
298 fn from_class_elt_requirements<'a, 'arena>(
299     alloc: &'arena bumpalo::Bump,
300     class_: &'a tast::Class_,
301 ) -> Vec<(hhbc_id::class::ClassType<'arena>, TraitReqKind)> {
302     class_
303         .reqs
304         .iter()
305         .map(|(h, is_extends)| {
306             let kind = if *is_extends {
307                 TraitReqKind::MustExtend
308             } else {
309                 TraitReqKind::MustImplement
310             };
311             (emit_type_hint::hint_to_class(alloc, h), kind)
312         })
313         .collect()
316 fn from_enum_type<'arena>(
317     alloc: &'arena bumpalo::Bump,
318     opt: Option<&tast::Enum_>,
319 ) -> Result<Option<hhbc_by_ref_hhas_type::Info<'arena>>> {
320     use hhbc_by_ref_hhas_type::constraint::*;
321     opt.map(|e| {
322         let type_info_user_type = Just(Str::new_str(
323             alloc,
324             emit_type_hint::fmt_hint(alloc, &[], true, &e.base)?,
325         ));
326         let type_info_type_constraint = Constraint::make(Nothing, ConstraintFlags::EXTENDED_HINT);
327         Ok(hhbc_by_ref_hhas_type::Info::make(
328             type_info_user_type,
329             type_info_type_constraint,
330         ))
331     })
332     .transpose()
335 fn validate_class_name(ns: &namespace_env::Env, tast::Id(p, class_name): &tast::Id) -> Result<()> {
336     let is_global_namespace = |ns: &namespace_env::Env| ns.name.is_none();
337     let is_hh_namespace = |ns: &namespace_env::Env| {
338         ns.name
339             .as_ref()
340             .map_or(false, |x| x.eq_ignore_ascii_case("hh"))
341     };
343     // global names are always reserved in any namespace.
344     // hh_reserved names are checked either if
345     // - class is in global namespace
346     // - class is in HH namespace *)
347     let is_special_class = class_name.contains('$');
348     let check_hh_name = is_global_namespace(ns) || is_hh_namespace(ns);
349     let name = string_utils::strip_ns(class_name);
350     let lower_name = name.to_ascii_lowercase();
351     let is_reserved_global_name = special_names::typehints::is_reserved_global_name(&lower_name);
352     let name_is_reserved = !is_special_class
353         && (is_reserved_global_name
354             || (check_hh_name && special_names::typehints::is_reserved_hh_name(&lower_name)));
355     if name_is_reserved {
356         Err(emit_fatal::raise_fatal_parse(
357             p,
358             format!(
359                 "Cannot use '{}' as class name as it is reserved",
360                 if is_reserved_global_name {
361                     &name
362                 } else {
363                     string_utils::strip_global_ns(class_name)
364                 }
365             ),
366         ))
367     } else {
368         Ok(())
369     }
372 fn emit_reified_extends_params<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
373     e: &mut Emitter<'arena, 'decl, D>,
374     env: &Env<'a, 'arena>,
375     ast_class: &'a tast::Class_,
376 ) -> Result<InstrSeq<'arena>> {
377     let alloc = env.arena;
378     match &ast_class.extends[..] {
379         [h, ..] => match h.1.as_happly() {
380             Some((_, l)) if !l.is_empty() => {
381                 return Ok(InstrSeq::gather(
382                     alloc,
383                     vec![
384                         emit_expression::emit_reified_targs(
385                             e,
386                             env,
387                             &ast_class.span,
388                             &l.iter().collect::<Vec<_>>(),
389                         )?,
390                         instr::record_reified_generic(alloc),
391                     ],
392                 ));
393             }
394             _ => {}
395         },
396         _ => {}
397     }
398     let tv = TypedValue::Vec(Slice::new(&[]));
399     Ok(instr::typedvalue(alloc, tv))
402 fn emit_reified_init_body<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
403     e: &mut Emitter<'arena, 'decl, D>,
404     env: &Env<'a, 'arena>,
405     num_reified: usize,
406     ast_class: &'a tast::Class_,
407 ) -> Result<InstrSeq<'arena>> {
408     use string_utils::reified::{INIT_METH_NAME, INIT_METH_PARAM_NAME, PROP_NAME};
410     let alloc = env.arena;
411     let check_length = InstrSeq::gather(
412         alloc,
413         vec![
414             instr::cgetl(
415                 alloc,
416                 Local::Named(Slice::new(INIT_METH_PARAM_NAME.as_bytes())),
417             ),
418             instr::check_reified_generic_mismatch(alloc),
419         ],
420     );
421     let set_prop = if num_reified == 0 {
422         instr::empty(alloc)
423     } else {
424         InstrSeq::gather(
425             alloc,
426             vec![
427                 check_length,
428                 instr::checkthis(alloc),
429                 instr::cgetl(
430                     alloc,
431                     Local::Named(Slice::new(INIT_METH_PARAM_NAME.as_bytes())),
432                 ),
433                 instr::baseh(alloc),
434                 instr::setm_pt(
435                     alloc,
436                     0,
437                     prop::from_raw_string(alloc, PROP_NAME),
438                     ReadOnlyOp::Any,
439                 ),
440                 instr::popc(alloc),
441             ],
442         )
443     };
444     let return_instr = InstrSeq::gather(alloc, vec![instr::null(alloc), instr::retc(alloc)]);
445     Ok(if ast_class.extends.is_empty() {
446         InstrSeq::gather(alloc, vec![set_prop, return_instr])
447     } else {
448         let generic_arr = emit_reified_extends_params(e, env, ast_class)?;
449         let call_parent = InstrSeq::gather(
450             alloc,
451             vec![
452                 instr::nulluninit(alloc),
453                 instr::nulluninit(alloc),
454                 generic_arr,
455                 instr::fcallclsmethodsd(
456                     alloc,
457                     FcallArgs::new(FcallFlags::default(), 1, Slice::new(&[]), None, 1, None),
458                     SpecialClsRef::Parent,
459                     method::from_raw_string(alloc, INIT_METH_NAME),
460                 ),
461                 instr::popc(alloc),
462             ],
463         );
464         InstrSeq::gather(alloc, vec![set_prop, call_parent, return_instr])
465     })
468 fn emit_reified_init_method<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
469     emitter: &mut Emitter<'arena, 'decl, D>,
470     env: &Env<'a, 'arena>,
471     ast_class: &'a tast::Class_,
472 ) -> Result<Option<HhasMethod<'arena>>> {
473     use hhbc_by_ref_hhas_type::{constraint::*, Info};
475     let alloc = env.arena;
476     let num_reified = ast_class
477         .tparams
478         .iter()
479         .filter(|x| x.reified != ReifyKind::Erased)
480         .count();
481     let maybe_has_reified_parents = match ast_class.extends.first().as_ref() {
482         Some(Hint(_, h)) if h.as_happly().map_or(false, |(_, l)| !l.is_empty()) => true,
483         _ => false, // Hack classes can only extend a single parent
484     };
485     if num_reified == 0 && !maybe_has_reified_parents {
486         Ok(None)
487     } else {
488         let tc = Constraint::make(Just("HH\\varray".into()), ConstraintFlags::empty());
489         let params = vec![HhasParam {
490             name: string_utils::reified::INIT_METH_PARAM_NAME.to_string(),
491             is_variadic: false,
492             is_inout: false,
493             user_attributes: vec![],
494             type_info: Some(Info::make(Just("HH\\varray".into()), tc)),
495             default_value: None,
496         }];
498         let body_instrs = emit_reified_init_body(emitter, env, num_reified, ast_class)?;
499         let instrs = emit_pos::emit_pos_then(alloc, &ast_class.span, body_instrs);
500         Ok(Some(make_86method(
501             alloc,
502             emitter,
503             (alloc, string_utils::reified::INIT_METH_NAME).into(),
504             params,
505             false, // is_static
506             Visibility::Protected,
507             false, // is_abstract
508             Span::from_pos(&ast_class.span),
509             instrs,
510         )?))
511     }
514 fn make_init_method<'a, 'arena, 'decl, D: DeclProvider<'decl>, F>(
515     alloc: &'arena bumpalo::Bump,
516     emitter: &mut Emitter<'arena, 'decl, D>,
517     properties: &[HhasProperty<'arena>],
518     filter: F,
519     name: &'static str,
520     span: Span,
521 ) -> Result<Option<HhasMethod<'arena>>>
522 where
523     F: Fn(&HhasProperty<'arena>) -> bool,
525     if properties
526         .iter()
527         .any(|p: &HhasProperty| p.initializer_instrs.is_just() && filter(p))
528     {
529         let instrs = InstrSeq::gather(
530             alloc,
531             properties
532                 .iter()
533                 .filter_map(|p| {
534                     if filter(p) {
535                         // TODO(hrust) this clone can be avoided by wrapping initializer_instrs by Rc
536                         // and also support Rc in InstrSeq
537                         std::convert::Into::<Option<_>>::into(p.initializer_instrs.as_ref())
538                             .map(|i| InstrSeq::clone(alloc, i))
539                     } else {
540                         None
541                     }
542                 })
543                 .collect(),
544         );
545         let instrs = InstrSeq::gather(alloc, vec![instrs, instr::null(alloc), instr::retc(alloc)]);
546         Ok(Some(make_86method(
547             alloc,
548             emitter,
549             (alloc, name).into(),
550             vec![],
551             true, // is_static
552             Visibility::Private,
553             false, // is_abstract
554             span,
555             instrs,
556         )?))
557     } else {
558         Ok(None)
559     }
562 pub fn emit_class<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
563     alloc: &'arena bumpalo::Bump,
564     emitter: &mut Emitter<'arena, 'decl, D>,
565     ast_class: &'a tast::Class_,
566 ) -> Result<HhasClass<'a, 'arena>> {
567     let namespace = &ast_class.namespace;
568     validate_class_name(namespace, &ast_class.name)?;
569     let mut env = Env::make_class_env(alloc, ast_class);
570     // TODO: communicate this without looking at the name
571     let is_closure = ast_class.name.1.starts_with("Closure$");
573     let mut attributes = emit_attribute::from_asts(alloc, emitter, &ast_class.user_attributes)?;
574     if !is_closure {
575         attributes
576             .extend(emit_attribute::add_reified_attribute(alloc, &ast_class.tparams).into_iter());
577         attributes.extend(
578             emit_attribute::add_reified_parent_attribute(&env, &ast_class.extends).into_iter(),
579         )
580     }
582     let is_const = hhas_attribute::has_const(&attributes);
583     // In the future, we intend to set class_no_dynamic_props independently from
584     // class_is_const, but for now class_is_const is the only thing that turns
585     // it on.
586     let no_dynamic_props = is_const;
587     let name = class::ClassType::from_ast_name(alloc, &ast_class.name.1);
588     let is_trait = ast_class.kind == tast::ClassishKind::Ctrait;
589     let is_interface = ast_class.kind == tast::ClassishKind::Cinterface;
591     let uses = ast_class
592         .uses
593         .iter()
594         .filter_map(|x| match x.1.as_ref() {
595             tast::Hint_::Happly(tast::Id(_, name), _) => {
596                 if is_interface {
597                     Some(Err(emit_fatal::raise_fatal_parse(
598                         &x.0,
599                         "Interfaces cannot use traits",
600                     )))
601                 } else {
602                     Some(Ok(name.as_str()))
603                 }
604             }
605             _ => None,
606         })
607         .collect::<Result<Vec<_>>>()?;
609     let elaborate_namespace_id =
610         |x: &'a tast::Id| hhbc_id::class::ClassType::from_ast_name(alloc, x.name());
611     let use_aliases = ast_class
612         .use_as_alias
613         .iter()
614         .map(|tast::UseAsAlias(ido1, id, ido2, vis)| {
615             let id1 = ido1.as_ref().map(|x| elaborate_namespace_id(x));
616             let id2 = ido2.as_ref().map(|x| (alloc, x.1.as_ref()).into());
617             (id1, (alloc, id.1.as_ref()).into(), id2, vis)
618         })
619         .collect();
621     let use_precedences = ast_class
622         .insteadof_alias
623         .iter()
624         .map(|tast::InsteadofAlias(id1, id2, ids)| {
625             let id1 = elaborate_namespace_id(id1);
626             let id2: hhbc_by_ref_hhbc_id::class::ClassType<'arena> = (alloc, id2.1.as_ref()).into();
627             let ids = ids.iter().map(|x| elaborate_namespace_id(x)).collect();
628             (id1, id2, ids)
629         })
630         .collect();
632     let enum_type = if ast_class.kind == tast::ClassishKind::Cenum {
633         from_enum_type(alloc, ast_class.enum_.as_ref())?
634     } else {
635         None
636     };
637     let is_enum_class = if ast_class.kind == tast::ClassishKind::Cenum {
638         match &ast_class.enum_ {
639             Some(info) => info.enum_class,
640             None => false,
641         }
642     } else {
643         false
644     };
645     let xhp_attributes: Vec<_> = ast_class
646         .xhp_attrs
647         .iter()
648         .map(
649             |tast::XhpAttr(type_, class_var, tag, maybe_enum)| HhasXhpAttribute {
650                 type_: type_.1.as_ref(),
651                 class_var,
652                 tag: *tag,
653                 maybe_enum: maybe_enum.as_ref(),
654             },
655         )
656         .collect();
658     let xhp_children = ast_class.xhp_children.first().map(|(p, sl)| (p, vec![sl]));
659     let xhp_categories: Option<(_, Vec<_>)> = ast_class
660         .xhp_category
661         .as_ref()
662         .map(|(p, c)| (p, c.iter().map(|x| &x.1).collect()));
664     let is_abstract = ast_class.kind == tast::ClassishKind::Cabstract;
665     let is_final = ast_class.final_ || is_trait;
666     let is_sealed = hhas_attribute::has_sealed(&attributes);
668     let tparams: Vec<&str> = ast_class
669         .tparams
670         .iter()
671         .map(|x| x.name.1.as_ref())
672         .collect();
674     let base = if is_interface {
675         None
676     } else {
677         from_extends(
678             alloc,
679             enum_type.is_some(),
680             is_enum_class,
681             &ast_class.extends,
682         )
683     };
685     if !is_closure
686         && base.as_ref().map_or(false, |cls| {
687             cls.to_raw_string().eq_ignore_ascii_case("closure")
688         })
689     {
690         return Err(emit_fatal::raise_fatal_runtime(
691             &ast_class.name.0,
692             "Class cannot extend Closure",
693         ));
694     }
695     let implements = if is_interface {
696         &ast_class.extends
697     } else {
698         &ast_class.implements
699     };
700     let implements = from_implements(alloc, implements);
701     let enum_includes = if ast_class.kind == tast::ClassishKind::Cenum {
702         match &ast_class.enum_ {
703             None => vec![],
704             Some(enum_) => from_includes(alloc, &enum_.includes),
705         }
706     } else {
707         vec![]
708     };
709     let span = Span::from_pos(&ast_class.span);
710     let mut additional_methods: Vec<HhasMethod<'arena>> = vec![];
711     if let Some(cats) = xhp_categories {
712         additional_methods.push(emit_xhp::from_category_declaration(
713             alloc, emitter, ast_class, &cats,
714         )?)
715     }
716     if let Some(children) = xhp_children {
717         additional_methods.push(emit_xhp::from_children_declaration(
718             alloc, emitter, ast_class, &children,
719         )?)
720     }
721     let no_xhp_attributes = xhp_attributes.is_empty() && ast_class.xhp_attr_uses.is_empty();
722     if !no_xhp_attributes {
723         additional_methods.push(emit_xhp::from_attribute_declaration(
724             alloc,
725             emitter,
726             ast_class,
727             &xhp_attributes,
728             &ast_class.xhp_attr_uses,
729         )?)
730     }
731     emitter.label_gen_mut().reset();
732     let mut properties = from_class_elt_classvars(alloc, emitter, &ast_class, is_const, &tparams)?;
733     let constants = from_class_elt_constants(emitter, &env, ast_class)?;
735     let requirements = from_class_elt_requirements(alloc, ast_class);
737     let pinit_filter = |p: &HhasProperty| !p.is_static();
738     let sinit_filter = |p: &HhasProperty| p.is_static() && !p.is_lsb();
739     let linit_filter = |p: &HhasProperty| p.is_static() && p.is_lsb();
741     let pinit_method =
742         make_init_method(alloc, emitter, &properties, &pinit_filter, "86pinit", span)?;
743     let sinit_method =
744         make_init_method(alloc, emitter, &properties, &sinit_filter, "86sinit", span)?;
745     let linit_method =
746         make_init_method(alloc, emitter, &properties, &linit_filter, "86linit", span)?;
748     let initialized_constants: Vec<_> = constants
749         .iter()
750         .filter_map(
751             |
752                 HhasConstant {
753                     ref name,
754                     ref initializer_instrs,
755                     ..
756                 },
757             | {
758                 initializer_instrs
759                     .as_ref()
760                     .map(|instrs| (name, emitter.label_gen_mut().next_regular(), instrs))
761                     .into()
762             },
763         )
764         .collect();
765     let cinit_method = if initialized_constants.is_empty() {
766         None
767     } else {
768         fn make_cinit_instrs<'arena, 'decl, D: DeclProvider<'decl>>(
769             alloc: &'arena bumpalo::Bump,
770             e: &mut Emitter<'arena, 'decl, D>,
771             default_label: label::Label,
772             pos: &Pos,
773             consts: &[(&r#const::ConstType<'arena>, label::Label, &InstrSeq<'arena>)],
774         ) -> InstrSeq<'arena> {
775             match consts {
776                 [] => InstrSeq::gather(
777                     alloc,
778                     vec![
779                         instr::label(alloc, default_label),
780                         emit_pos::emit_pos(alloc, pos),
781                         instr::string(alloc, "Could not find initializer for "),
782                         instr::cgetl(alloc, Local::Named(Slice::new("$constName".as_bytes()))),
783                         instr::string(alloc, " in 86cinit"),
784                         instr::concatn(alloc, 3),
785                         instr::fatal(alloc, FatalOp::Runtime),
786                     ],
787                 ),
788                 [(_, label, instrs), cs @ ..] => InstrSeq::gather(
789                     alloc,
790                     vec![
791                         instr::label(alloc, *label),
792                         InstrSeq::clone(alloc, *instrs),
793                         emit_pos::emit_pos(alloc, pos),
794                         instr::retc(alloc),
795                         make_cinit_instrs(alloc, e, default_label, pos, cs),
796                     ],
797                 ),
798             }
799         }
800         let default_label = emitter.label_gen_mut().next_regular();
802         let body_instrs = {
803             let mut cases =
804                 bumpalo::collections::Vec::with_capacity_in(initialized_constants.len() + 1, alloc);
805             for (name, label, _) in &initialized_constants {
806                 let n: &str = alloc.alloc_str((*name).to_raw_string());
807                 cases.push((n, *label))
808             }
809             cases.push((alloc.alloc_str("default"), default_label));
810             InstrSeq::gather(
811                 alloc,
812                 vec![
813                     instr::cgetl(alloc, Local::Named(Slice::new("$constName".as_bytes()))),
814                     instr::sswitch(alloc, cases),
815                     make_cinit_instrs(
816                         alloc,
817                         emitter,
818                         default_label,
819                         &ast_class.span,
820                         &initialized_constants[..],
821                     ),
822                 ],
823             )
824         };
825         let instrs = emit_pos::emit_pos_then(alloc, &ast_class.span, body_instrs);
826         let params = vec![HhasParam {
827             name: "$constName".to_string(),
828             is_variadic: false,
829             is_inout: false,
830             user_attributes: vec![],
831             type_info: None,
832             default_value: None,
833         }];
835         Some(make_86method(
836             alloc,
837             emitter,
838             (alloc, "86cinit").into(),
839             params,
840             true, /* is_static */
841             Visibility::Private,
842             is_interface, /* is_abstract */
843             span,
844             instrs,
845         )?)
846     };
848     let should_emit_reified_init = !(emitter.systemlib() || is_closure || is_interface || is_trait);
849     let reified_init_method = if should_emit_reified_init {
850         emit_reified_init_method(emitter, &env, ast_class)?
851     } else {
852         None
853     };
854     let needs_no_reifiedinit = reified_init_method.is_some() && ast_class.extends.is_empty();
855     additional_methods.extend(reified_init_method.into_iter());
856     additional_methods.extend(pinit_method.into_iter());
857     additional_methods.extend(sinit_method.into_iter());
858     additional_methods.extend(linit_method.into_iter());
859     additional_methods.extend(cinit_method.into_iter());
861     let mut methods = emit_method::from_asts(alloc, emitter, ast_class, &ast_class.methods)?;
862     methods.extend(additional_methods.into_iter());
863     let (ctxconsts, tconsts): (Vec<_>, Vec<_>) =
864         ast_class.typeconsts.iter().partition(|x| x.is_ctx);
865     let type_constants = tconsts
866         .iter()
867         .map(|x| from_type_constant(alloc, emitter, x))
868         .collect::<Result<Vec<HhasTypeConstant>>>()?;
869     let ctx_constants = ctxconsts
870         .iter()
871         .map(|x| from_ctx_constant(x))
872         .collect::<Result<Vec<HhasCtxConstant>>>()?;
873     let upper_bounds = emit_body::emit_generics_upper_bounds(alloc, &ast_class.tparams, &[], false);
875     if !no_xhp_attributes {
876         properties.extend(emit_xhp::properties_for_cache(
877             alloc, emitter, ast_class, is_const,
878         )?);
879     }
880     let info = emit_memoize_method::make_info(ast_class, name, &ast_class.methods)?;
881     methods.extend(emit_memoize_method::emit_wrapper_methods(
882         emitter,
883         &mut env,
884         &info,
885         ast_class,
886         &ast_class.methods,
887     )?);
888     let doc_comment = ast_class.doc_comment.clone();
889     let is_xhp = ast_class.is_xhp || ast_class.has_xhp_keyword;
891     let mut flags = HhasClassFlags::empty();
892     flags.set(HhasClassFlags::IS_FINAL, is_final);
893     flags.set(HhasClassFlags::IS_SEALED, is_sealed);
894     flags.set(HhasClassFlags::IS_ABSTRACT, is_abstract);
895     flags.set(HhasClassFlags::IS_INTERFACE, is_interface);
896     flags.set(HhasClassFlags::IS_TRAIT, is_trait);
897     flags.set(HhasClassFlags::IS_XHP, is_xhp);
898     flags.set(HhasClassFlags::IS_CONST, is_const);
899     flags.set(HhasClassFlags::NO_DYNAMIC_PROPS, no_dynamic_props);
900     flags.set(HhasClassFlags::NEEDS_NO_REIFIEDINIT, needs_no_reifiedinit);
902     add_symbol_refs(
903         alloc,
904         emitter,
905         &base,
906         &implements,
907         uses.as_ref(),
908         &requirements,
909     );
910     Ok(HhasClass {
911         attributes,
912         base,
913         implements,
914         enum_includes,
915         name,
916         span,
917         flags,
918         doc_comment,
919         uses,
920         use_aliases,
921         use_precedences,
922         methods,
923         enum_type,
924         upper_bounds,
925         properties,
926         requirements,
927         type_constants,
928         ctx_constants,
929         constants,
930     })
933 pub fn emit_classes_from_program<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
934     alloc: &'arena bumpalo::Bump,
935     emitter: &mut Emitter<'arena, 'decl, D>,
936     tast: &'a [tast::Def],
937 ) -> Result<Vec<HhasClass<'a, 'arena>>> {
938     tast.iter()
939         .filter_map(|class| {
940             if let tast::Def::Class(cd) = class {
941                 Some(emit_class(alloc, emitter, cd))
942             } else {
943                 None
944             }
945         })
946         .collect()