diff --git a/hphp/hack/src/annotated_ast/aast_defs.ml b/hphp/hack/src/annotated_ast/aast_defs.ml index e76e324a425a8c..bb0721ec0e076a 100644 --- a/hphp/hack/src/annotated_ast/aast_defs.ml +++ b/hphp/hack/src/annotated_ast/aast_defs.ml @@ -1228,6 +1228,10 @@ and hint_fun = { hf_is_readonly_return: Ast_defs.readonly_kind option; [@transform.opaque] } +and class_ptr_kind = + | CKclass + | CKenum + and hint_ = | Hprim of (tprim[@transform.opaque]) | Happly of class_name * hint list @@ -1235,7 +1239,7 @@ and hint_ = | Hlike of hint | Hfun of hint_fun | Htuple of tuple_info - | Hclass_ptr of hint + | Hclass_ptr of class_ptr_kind * hint | Hshape of nast_shape_info | Haccess of hint * sid list (** Accessing a type constant. Type constants are accessed like normal diff --git a/hphp/hack/src/decl/decl_hint.ml b/hphp/hack/src/decl/decl_hint.ml index 7bf22adeccd30f..879461e66572f9 100644 --- a/hphp/hack/src/decl/decl_hint.ml +++ b/hphp/hack/src/decl/decl_hint.ml @@ -118,7 +118,13 @@ and hint_ p env = function | Habstr (x, argl) -> let argl = List.map argl ~f:(hint env) in Tgeneric (x, argl) - | Hclass_ptr h -> Tclass_ptr (hint env h) + | Hclass_ptr (k, h) -> + let h = + match k with + | CKclass -> h + | CKenum -> (p, Happly ((p, SN.Classes.cHH_BuiltinEnum), [h])) + in + Tclass_ptr (hint env h) | Hoption h -> let h = hint env h in Toption h diff --git a/hphp/hack/src/decl/direct_decl_smart_constructors.rs b/hphp/hack/src/decl/direct_decl_smart_constructors.rs index 3db13138b33b1c..fa6951d3bd2b09 100644 --- a/hphp/hack/src/decl/direct_decl_smart_constructors.rs +++ b/hphp/hack/src/decl/direct_decl_smart_constructors.rs @@ -5748,29 +5748,34 @@ impl<'a, 'o, 't, S: SourceTextAllocator<'t, 'a>> FlattenSmartConstructors fn make_class_ptr_type_specifier( &mut self, - class: Self::Output, + kw: Self::Output, _lt: Self::Output, targ: Self::Output, _trailing_comma: Self::Output, gt: Self::Output, ) -> Self::Output { if self.opts.enable_class_pointer_hint { - let pos = self.merge_positions(class, gt); + let pos = self.merge_positions(kw, gt); let reason = self.alloc(Reason::FromWitnessDecl(self.alloc(WitnessDecl::Hint(pos)))); - let cls = match self.node_to_ty(targ) { - Some(ty) => ty, - None => return Node::Ignored(SK::ClassPtrTypeSpecifier), + let cls = match (kw.token_kind(), self.node_to_ty(targ)) { + (Some(TokenKind::Class), Some(ty)) => ty, + (Some(TokenKind::Enum), Some(ty)) => self.alloc(Ty( + reason, + Ty_::Tapply(self.alloc(( + (pos, naming_special_names::classes::HH_BUILTIN_ENUM), + self.alloc([ty]), + ))), + )), + _ => return Node::Ignored(SK::ClassPtrTypeSpecifier), }; Node::Ty(self.alloc(Ty(reason, Ty_::TclassPtr(cls)))) } else { - self.make_apply( - ( - self.get_pos(class), - naming_special_names::classes::CLASS_NAME, - ), - targ, - self.get_pos(targ), - ) + let id = match kw.token_kind() { + Some(TokenKind::Class) => naming_special_names::classes::CLASS_NAME, + Some(TokenKind::Enum) => naming_special_names::classes::ENUM_NAME, + _ => return Node::Ignored(SK::ClassPtrTypeSpecifier), + }; + self.make_apply((self.get_pos(kw), id), targ, self.get_pos(targ)) } } diff --git a/hphp/hack/src/elab/pass.rs b/hphp/hack/src/elab/pass.rs index 06d13ce3ce695d..a965f330953191 100644 --- a/hphp/hack/src/elab/pass.rs +++ b/hphp/hack/src/elab/pass.rs @@ -3,7 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the "hack" directory of this source tree. // -// @generated SignedSource<<6d53e3dd14519a317916703b215c4c38>> +// @generated SignedSource<> // // To regenerate this file, run: // hphp/hack/src/oxidized_regen.sh @@ -1136,6 +1136,22 @@ pub trait Pass: PassClone { Continue(()) } #[inline(always)] + fn on_ty_class_ptr_kind_top_down( + &mut self, + env: &Env, + elem: &mut ClassPtrKind, + ) -> ControlFlow<()> { + Continue(()) + } + #[inline(always)] + fn on_ty_class_ptr_kind_bottom_up( + &mut self, + env: &Env, + elem: &mut ClassPtrKind, + ) -> ControlFlow<()> { + Continue(()) + } + #[inline(always)] fn on_ty_hint__top_down(&mut self, env: &Env, elem: &mut Hint_) -> ControlFlow<()> { Continue(()) } @@ -2972,6 +2988,28 @@ impl Pass for Passes { Continue(()) } #[inline(always)] + fn on_ty_class_ptr_kind_top_down( + &mut self, + env: &Env, + elem: &mut ClassPtrKind, + ) -> ControlFlow<()> { + for pass in &mut self.passes { + pass.on_ty_class_ptr_kind_top_down(env, elem)?; + } + Continue(()) + } + #[inline(always)] + fn on_ty_class_ptr_kind_bottom_up( + &mut self, + env: &Env, + elem: &mut ClassPtrKind, + ) -> ControlFlow<()> { + for pass in &mut self.passes { + pass.on_ty_class_ptr_kind_bottom_up(env, elem)?; + } + Continue(()) + } + #[inline(always)] fn on_ty_hint__top_down(&mut self, env: &Env, elem: &mut Hint_) -> ControlFlow<()> { for pass in &mut self.passes { pass.on_ty_hint__top_down(env, elem)?; diff --git a/hphp/hack/src/elab/transform.rs b/hphp/hack/src/elab/transform.rs index 60bc2bfd78fbf4..2a4ddd0320d0d1 100644 --- a/hphp/hack/src/elab/transform.rs +++ b/hphp/hack/src/elab/transform.rs @@ -3,7 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the "hack" directory of this source tree. // -// @generated SignedSource<<5f86f1eac058855eef67b7959dd0012b>> +// @generated SignedSource<<6f5e023411c62a93f680149e18b781cf>> // // To regenerate this file, run: // hphp/hack/src/oxidized_regen.sh @@ -2356,6 +2356,22 @@ impl Transform for HintFun { } } } +impl Transform for ClassPtrKind { + fn transform(&mut self, env: &Env, pass: &mut (impl Pass + Clone)) { + let mut in_pass = pass.clone(); + if let Break(..) = pass.on_ty_class_ptr_kind_top_down(env, self) { + return; + } + stack_limit::maybe_grow(|| self.traverse(env, pass)); + in_pass.on_ty_class_ptr_kind_bottom_up(env, self); + } + fn traverse(&mut self, env: &Env, pass: &mut (impl Pass + Clone)) { + match self { + ClassPtrKind::CKclass => {} + ClassPtrKind::CKenum => {} + } + } +} impl Transform for Hint_ { fn transform(&mut self, env: &Env, pass: &mut (impl Pass + Clone)) { let mut in_pass = pass.clone(); @@ -2378,7 +2394,12 @@ impl Transform for Hint_ { Hint_::Hlike(ref mut __binding_0) => __binding_0.transform(env, &mut pass.clone()), Hint_::Hfun(ref mut __binding_0) => __binding_0.transform(env, &mut pass.clone()), Hint_::Htuple(ref mut __binding_0) => __binding_0.transform(env, &mut pass.clone()), - Hint_::HclassPtr(ref mut __binding_0) => __binding_0.transform(env, &mut pass.clone()), + Hint_::HclassPtr(ref mut __binding_0, ref mut __binding_1) => { + { + __binding_0.transform(env, &mut pass.clone()) + } + { __binding_1.transform(env, &mut pass.clone()) } + } Hint_::Hshape(ref mut __binding_0) => __binding_0.transform(env, &mut pass.clone()), Hint_::Haccess(ref mut __binding_0, ref mut __binding_1) => { { diff --git a/hphp/hack/src/elab/typed_local.rs b/hphp/hack/src/elab/typed_local.rs index 85a683db59b608..0cc12a0402e6f7 100644 --- a/hphp/hack/src/elab/typed_local.rs +++ b/hphp/hack/src/elab/typed_local.rs @@ -364,7 +364,7 @@ impl TypedLocal { fn simplify_hint(&self, hint: &mut Hint) { let Hint(pos, box hint_) = hint; match hint_ { - Hint_::Hoption(h) | Hint_::Hlike(h) | Hint_::HclassPtr(h) => self.simplify_hint(h), + Hint_::Hoption(h) | Hint_::Hlike(h) | Hint_::HclassPtr(_, h) => self.simplify_hint(h), Hint_::Htuple(_) => { // enforce tuples as just vec *hint_ = Hint_::Happly( diff --git a/hphp/hack/src/hackc/emitter/emit_type_constant.rs b/hphp/hack/src/hackc/emitter/emit_type_constant.rs index c889dca2724d00..4b65fde85d2baa 100644 --- a/hphp/hack/src/hackc/emitter/emit_type_constant.rs +++ b/hphp/hack/src/hackc/emitter/emit_type_constant.rs @@ -453,7 +453,7 @@ fn hint_to_type_constant_list( } } Hint_::Habstr(_, _) - | Hint_::HclassPtr(_) + | Hint_::HclassPtr(_, _) | Hint_::Hdynamic | Hint_::HfunContext(_) | Hint_::Hmixed diff --git a/hphp/hack/src/hackc/emitter/emit_type_hint.rs b/hphp/hack/src/hackc/emitter/emit_type_hint.rs index ef7cc110e5cccc..a4f6fbd366bf77 100644 --- a/hphp/hack/src/hackc/emitter/emit_type_hint.rs +++ b/hphp/hack/src/hackc/emitter/emit_type_hint.rs @@ -149,7 +149,7 @@ pub fn fmt_hint(tparams: &[&str], strip_tparams: bool, hint: &Hint) -> Result format!("({})", fmt_hints(tparams, required)?), Hlike(t) => format!("~{}", fmt_hint(tparams, false, t)?), Hsoft(t) => format!("@{}", fmt_hint(tparams, false, t)?), - HclassPtr(_) + HclassPtr(_, _) | HfunContext(_) | Hdynamic | Hintersection(_) @@ -175,7 +175,7 @@ fn hint_to_string<'a>(h: &'a Hint_) -> &'a str { Habstr(_, _) | Haccess(_, _) | Happly(_, _) - | HclassPtr(_) + | HclassPtr(_, _) | Hfun(_) | HfunContext(_) | Hlike(_) @@ -214,7 +214,7 @@ fn can_be_nullable(hint: &Hint_) -> bool { id != "\\HH\\dynamic" && id != "\\HH\\nonnull" && id != "\\HH\\mixed" } Habstr(_, _) - | HclassPtr(_) + | HclassPtr(_, _) | HfunContext(_) | Hintersection(_) | Hlike(_) @@ -317,7 +317,7 @@ fn hint_to_type_constraint( hint_to_type_constraint(kind, tparams, skipawaitable, hint)? } // TODO: should probably just return Result::Err for some of these - HclassPtr(_) + HclassPtr(_, _) | HfunContext(_) | Hnonnull | Hnothing @@ -431,7 +431,7 @@ fn param_hint_to_type_info( Habstr(s, hs) => hs.is_empty() && !tparams.contains(&s.as_str()), Hprim(_) | Htuple(_) - | HclassPtr(_) + | HclassPtr(_, _) | Hshape(_) | Hrefinement(_, _) | Hwildcard @@ -572,7 +572,7 @@ fn get_flags(tparams: &[&str], flags: TypeConstraintFlags, hint: &Hint_) -> Type } Habstr(_, _) | Happly(_, _) - | HclassPtr(_) + | HclassPtr(_, _) | Hdynamic | Hfun(_) | HfunContext(_) diff --git a/hphp/hack/src/hackc/emitter/reified_generics_helpers.rs b/hphp/hack/src/hackc/emitter/reified_generics_helpers.rs index f22c462f929ac8..dafae264c68c43 100644 --- a/hphp/hack/src/hackc/emitter/reified_generics_helpers.rs +++ b/hphp/hack/src/hackc/emitter/reified_generics_helpers.rs @@ -75,7 +75,7 @@ pub(crate) fn has_reified_type_constraint<'a>(env: &Env<'a>, h: &aast::Hint) -> }) } } - Hint_::Hsoft(h) | Hint_::Hlike(h) | Hint_::HclassPtr(h) | Hint_::Hoption(h) => { + Hint_::Hsoft(h) | Hint_::Hlike(h) | Hint_::HclassPtr(_, h) | Hint_::Hoption(h) => { has_reified_type_constraint(env, h) } Hint_::Hprim(_) @@ -122,7 +122,7 @@ fn remove_awaitable(aast::Hint(pos, hint): aast::Hint) -> aast::Hint { | Hint_::Hfun(_) | Hint_::Haccess(_, _) | Hint_::Hrefinement(_, _) - | Hint_::HclassPtr(_) + | Hint_::HclassPtr(_, _) | Hint_::Happly(_, _) | Hint_::HfunContext(_) | Hint_::Hvar(_) @@ -189,7 +189,7 @@ pub(crate) fn remove_erased_generics<'a>(env: &Env<'a>, h: aast::Hint) -> aast:: } Hint_::Hsoft(h) => Hint_::Hsoft(rec(env, h)), Hint_::Hlike(h) => Hint_::Hlike(rec(env, h)), - Hint_::HclassPtr(h) => Hint_::HclassPtr(rec(env, h)), + Hint_::HclassPtr(k, h) => Hint_::HclassPtr(k, rec(env, h)), Hint_::Hoption(h) => Hint_::Hoption(rec(env, h)), Hint_::Htuple(TupleInfo { required, extra }) => { let extra = match extra { diff --git a/hphp/hack/src/naming/naming_special_names.rs b/hphp/hack/src/naming/naming_special_names.rs index b73f210ab70942..1f68cb3f2ad188 100644 --- a/hphp/hack/src/naming/naming_special_names.rs +++ b/hphp/hack/src/naming/naming_special_names.rs @@ -65,6 +65,8 @@ pub mod classes { pub const CLASS_NAME: &str = "\\HH\\classname"; + pub const ENUM_NAME: &str = "\\HH\\enumname"; + pub const TYPE_NAME: &str = "\\HH\\typename"; pub const IDISPOSABLE: &str = "\\IDisposable"; diff --git a/hphp/hack/src/oxidized/aast_visitor/node_impl_gen.rs b/hphp/hack/src/oxidized/aast_visitor/node_impl_gen.rs index 837f6ff3849f61..730f9c924adbb2 100644 --- a/hphp/hack/src/oxidized/aast_visitor/node_impl_gen.rs +++ b/hphp/hack/src/oxidized/aast_visitor/node_impl_gen.rs @@ -3,7 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the "hack" directory of this source tree. // -// @generated SignedSource<<9082b9e1996be3d1efe1b22a9ce27050>> +// @generated SignedSource<> // // To regenerate this file, run: // hphp/hack/src/oxidized_regen.sh @@ -390,6 +390,25 @@ impl Node

for ClassId_ { } } } +impl Node

for ClassPtrKind { + fn accept<'node>( + &'node self, + c: &mut P::Context, + v: &mut dyn Visitor<'node, Params = P>, + ) -> Result<(), P::Error> { + v.visit_class_ptr_kind(c, self) + } + fn recurse<'node>( + &'node self, + c: &mut P::Context, + v: &mut dyn Visitor<'node, Params = P>, + ) -> Result<(), P::Error> { + match self { + ClassPtrKind::CKclass => Ok(()), + ClassPtrKind::CKenum => Ok(()), + } + } +} impl Node

for ClassReq { fn accept<'node>( &'node self, @@ -1412,7 +1431,10 @@ impl Node

for Hint_ { Hint_::Hlike(a0) => a0.accept(c, v), Hint_::Hfun(a0) => a0.accept(c, v), Hint_::Htuple(a0) => a0.accept(c, v), - Hint_::HclassPtr(a0) => a0.accept(c, v), + Hint_::HclassPtr(a0, a1) => { + a0.accept(c, v)?; + a1.accept(c, v) + } Hint_::Hshape(a0) => a0.accept(c, v), Hint_::Haccess(a0, a1) => { a0.accept(c, v)?; diff --git a/hphp/hack/src/oxidized/aast_visitor/node_mut_impl_gen.rs b/hphp/hack/src/oxidized/aast_visitor/node_mut_impl_gen.rs index f2f530862f3cf1..9cc2ba6d05888c 100644 --- a/hphp/hack/src/oxidized/aast_visitor/node_mut_impl_gen.rs +++ b/hphp/hack/src/oxidized/aast_visitor/node_mut_impl_gen.rs @@ -3,7 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the "hack" directory of this source tree. // -// @generated SignedSource<<07a3c9483c37d2d68f66b562ef1bdffc>> +// @generated SignedSource<<7203202959e16b93cba85b61420c6432>> // // To regenerate this file, run: // hphp/hack/src/oxidized_regen.sh @@ -390,6 +390,25 @@ impl NodeMut

for ClassId_ { } } } +impl NodeMut

for ClassPtrKind { + fn accept<'node>( + &'node mut self, + c: &mut P::Context, + v: &mut dyn VisitorMut<'node, Params = P>, + ) -> Result<(), P::Error> { + v.visit_class_ptr_kind(c, self) + } + fn recurse<'node>( + &'node mut self, + c: &mut P::Context, + v: &mut dyn VisitorMut<'node, Params = P>, + ) -> Result<(), P::Error> { + match self { + ClassPtrKind::CKclass => Ok(()), + ClassPtrKind::CKenum => Ok(()), + } + } +} impl NodeMut

for ClassReq { fn accept<'node>( &'node mut self, @@ -1412,7 +1431,10 @@ impl NodeMut

for Hint_ { Hint_::Hlike(a0) => a0.accept(c, v), Hint_::Hfun(a0) => a0.accept(c, v), Hint_::Htuple(a0) => a0.accept(c, v), - Hint_::HclassPtr(a0) => a0.accept(c, v), + Hint_::HclassPtr(a0, a1) => { + a0.accept(c, v)?; + a1.accept(c, v) + } Hint_::Hshape(a0) => a0.accept(c, v), Hint_::Haccess(a0, a1) => { a0.accept(c, v)?; diff --git a/hphp/hack/src/oxidized/aast_visitor/visitor.rs b/hphp/hack/src/oxidized/aast_visitor/visitor.rs index dd61b47fb76a3e..4b60203e585f37 100644 --- a/hphp/hack/src/oxidized/aast_visitor/visitor.rs +++ b/hphp/hack/src/oxidized/aast_visitor/visitor.rs @@ -3,7 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the "hack" directory of this source tree. // -// @generated SignedSource<<7d5815995a347c4b35bac98fdabc60e0>> +// @generated SignedSource<<1e201159a429e4edb3a1150e3cbe3668>> // // To regenerate this file, run: // hphp/hack/src/oxidized_regen.sh @@ -168,6 +168,13 @@ pub trait Visitor<'node> { ) -> Result<(), ::Error> { p.recurse(c, self.object()) } + fn visit_class_ptr_kind( + &mut self, + c: &mut ::Context, + p: &'node ClassPtrKind, + ) -> Result<(), ::Error> { + p.recurse(c, self.object()) + } fn visit_class_req( &mut self, c: &mut ::Context, diff --git a/hphp/hack/src/oxidized/aast_visitor/visitor_mut.rs b/hphp/hack/src/oxidized/aast_visitor/visitor_mut.rs index 794626dcd41fcb..bfcf3dfecb64b5 100644 --- a/hphp/hack/src/oxidized/aast_visitor/visitor_mut.rs +++ b/hphp/hack/src/oxidized/aast_visitor/visitor_mut.rs @@ -3,7 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the "hack" directory of this source tree. // -// @generated SignedSource<<66e00bf2756509d4d0b5348b42600e15>> +// @generated SignedSource<<489e58c9fe48b7f1b68b07c14538f4ba>> // // To regenerate this file, run: // hphp/hack/src/oxidized_regen.sh @@ -168,6 +168,13 @@ pub trait VisitorMut<'node> { ) -> Result<(), ::Error> { p.recurse(c, self.object()) } + fn visit_class_ptr_kind( + &mut self, + c: &mut ::Context, + p: &'node mut ClassPtrKind, + ) -> Result<(), ::Error> { + p.recurse(c, self.object()) + } fn visit_class_req( &mut self, c: &mut ::Context, diff --git a/hphp/hack/src/oxidized/copy_types.txt b/hphp/hack/src/oxidized/copy_types.txt index 73cb2296994736..0efe860c621865 100644 --- a/hphp/hack/src/oxidized/copy_types.txt +++ b/hphp/hack/src/oxidized/copy_types.txt @@ -1,4 +1,5 @@ aast_defs::Abstraction +aast_defs::ClassPtrKind aast_defs::ConstraintKind aast_defs::EmitId aast_defs::EnvAnnot diff --git a/hphp/hack/src/oxidized/gen/aast_defs.rs b/hphp/hack/src/oxidized/gen/aast_defs.rs index 90a16729eb26c0..841dcfdb414e7e 100644 --- a/hphp/hack/src/oxidized/gen/aast_defs.rs +++ b/hphp/hack/src/oxidized/gen/aast_defs.rs @@ -3,7 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the "hack" directory of this source tree. // -// @generated SignedSource<> +// @generated SignedSource<<5276513781460c921ef927e568c45b06>> // // To regenerate this file, run: // hphp/hack/src/oxidized_regen.sh @@ -2596,6 +2596,31 @@ pub struct HintFun { pub is_readonly_return: Option, } +#[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + FromOcamlRep, + FromOcamlRepIn, + Hash, + NoPosHash, + Ord, + PartialEq, + PartialOrd, + Serialize, + ToOcamlRep +)] +#[rust_to_ocaml(and)] +#[repr(u8)] +pub enum ClassPtrKind { + CKclass, + CKenum, +} +impl TrivialDrop for ClassPtrKind {} +arena_deserializer::impl_deserialize_in_arena!(ClassPtrKind); + #[derive( Clone, Debug, @@ -2620,7 +2645,7 @@ pub enum Hint_ { Hfun(HintFun), Htuple(TupleInfo), #[rust_to_ocaml(name = "Hclass_ptr")] - HclassPtr(Hint), + HclassPtr(ClassPtrKind, Hint), Hshape(NastShapeInfo), /// Accessing a type constant. Type constants are accessed like normal /// class constants, but in type positions. diff --git a/hphp/hack/src/oxidized/impl_gen/aast_defs_impl_gen.rs b/hphp/hack/src/oxidized/impl_gen/aast_defs_impl_gen.rs index 740dbb4b1e4911..a1b542d25b5820 100644 --- a/hphp/hack/src/oxidized/impl_gen/aast_defs_impl_gen.rs +++ b/hphp/hack/src/oxidized/impl_gen/aast_defs_impl_gen.rs @@ -3,7 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the "hack" directory of this source tree. // -// @generated SignedSource<<831ed8b36eab629e54260ff13520847b>> +// @generated SignedSource<<47c99ac218c37f51b8f1f0f8b3a1004b>> // // To regenerate this file, run: // hphp/hack/src/oxidized_regen.sh @@ -3516,6 +3516,26 @@ impl XhpChildOp { } } } +impl ClassPtrKind { + pub fn mk_ckclass() -> Self { + ClassPtrKind::CKclass + } + pub fn mk_ckenum() -> Self { + ClassPtrKind::CKenum + } + pub fn is_ckclass(&self) -> bool { + match self { + ClassPtrKind::CKclass => true, + _ => false, + } + } + pub fn is_ckenum(&self) -> bool { + match self { + ClassPtrKind::CKenum => true, + _ => false, + } + } +} impl Hint_ { pub fn mk_hprim(p0: Tprim) -> Self { Hint_::Hprim(p0) @@ -3535,8 +3555,8 @@ impl Hint_ { pub fn mk_htuple(p0: TupleInfo) -> Self { Hint_::Htuple(p0) } - pub fn mk_hclass_ptr(p0: Hint) -> Self { - Hint_::HclassPtr(p0) + pub fn mk_hclass_ptr(p0: ClassPtrKind, p1: Hint) -> Self { + Hint_::HclassPtr(p0, p1) } pub fn mk_hshape(p0: NastShapeInfo) -> Self { Hint_::Hshape(p0) @@ -3760,9 +3780,9 @@ impl Hint_ { _ => None, } } - pub fn as_hclass_ptr(&self) -> Option<&Hint> { + pub fn as_hclass_ptr(&self) -> Option<(&ClassPtrKind, &Hint)> { match self { - Hint_::HclassPtr(p0) => Some(p0), + Hint_::HclassPtr(p0, p1) => Some((p0, p1)), _ => None, } } @@ -3862,9 +3882,9 @@ impl Hint_ { _ => None, } } - pub fn as_hclass_ptr_mut(&mut self) -> Option<&mut Hint> { + pub fn as_hclass_ptr_mut(&mut self) -> Option<(&mut ClassPtrKind, &mut Hint)> { match self { - Hint_::HclassPtr(p0) => Some(p0), + Hint_::HclassPtr(p0, p1) => Some((p0, p1)), _ => None, } } @@ -3964,9 +3984,9 @@ impl Hint_ { _ => None, } } - pub fn as_hclass_ptr_into(self) -> Option { + pub fn as_hclass_ptr_into(self) -> Option<(ClassPtrKind, Hint)> { match self { - Hint_::HclassPtr(p0) => Some(p0), + Hint_::HclassPtr(p0, p1) => Some((p0, p1)), _ => None, } } diff --git a/hphp/hack/src/oxidized_by_ref/copy_types.txt b/hphp/hack/src/oxidized_by_ref/copy_types.txt index 1cb73c5b00a066..decddd723fcb22 100644 --- a/hphp/hack/src/oxidized_by_ref/copy_types.txt +++ b/hphp/hack/src/oxidized_by_ref/copy_types.txt @@ -1,3 +1,4 @@ +aast_defs::ClassPtrKind aast_defs::CtxRefinement aast_defs::Hint_ aast_defs::ParamKind diff --git a/hphp/hack/src/oxidized_by_ref/gen/aast_defs.rs b/hphp/hack/src/oxidized_by_ref/gen/aast_defs.rs index a383ff5ec168b5..4c0baa9e3d5c06 100644 --- a/hphp/hack/src/oxidized_by_ref/gen/aast_defs.rs +++ b/hphp/hack/src/oxidized_by_ref/gen/aast_defs.rs @@ -3,7 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the "hack" directory of this source tree. // -// @generated SignedSource<<934c469a8bd9fe3581ccf730a4296a4f>> +// @generated SignedSource<<56ad43bdc2f9f7a8ded994015c09f610>> // // To regenerate this file, run: // hphp/hack/src/oxidized_regen.sh @@ -14,6 +14,7 @@ pub use ast_defs::PositionedByteString; pub use ast_defs::Pstring; pub use local_id::LocalId; use no_pos_hash::NoPosHash; +use ocamlrep::FromOcamlRep; use ocamlrep::FromOcamlRepIn; use ocamlrep::ToOcamlRep; pub use oxidized::aast_defs::OgNullFlavor; @@ -3174,6 +3175,31 @@ pub struct HintFun<'a> { impl<'a> TrivialDrop for HintFun<'a> {} arena_deserializer::impl_deserialize_in_arena!(HintFun<'arena>); +#[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + FromOcamlRep, + FromOcamlRepIn, + Hash, + NoPosHash, + Ord, + PartialEq, + PartialOrd, + Serialize, + ToOcamlRep +)] +#[rust_to_ocaml(and)] +#[repr(u8)] +pub enum ClassPtrKind { + CKclass, + CKenum, +} +impl TrivialDrop for ClassPtrKind {} +arena_deserializer::impl_deserialize_in_arena!(ClassPtrKind); + #[derive( Clone, Copy, @@ -3207,7 +3233,8 @@ pub enum Hint_<'a> { Htuple(&'a TupleInfo<'a>), #[serde(deserialize_with = "arena_deserializer::arena", borrow)] #[rust_to_ocaml(name = "Hclass_ptr")] - HclassPtr(&'a Hint<'a>), + #[rust_to_ocaml(inline_tuple)] + HclassPtr(&'a (ClassPtrKind, &'a Hint<'a>)), #[serde(deserialize_with = "arena_deserializer::arena", borrow)] Hshape(&'a NastShapeInfo<'a>), /// Accessing a type constant. Type constants are accessed like normal diff --git a/hphp/hack/src/oxidized_by_ref/owned_types.txt b/hphp/hack/src/oxidized_by_ref/owned_types.txt index 427bb9904eca0d..d37248efdb84b9 100644 --- a/hphp/hack/src/oxidized_by_ref/owned_types.txt +++ b/hphp/hack/src/oxidized_by_ref/owned_types.txt @@ -1,4 +1,5 @@ aast_defs::Tprim +aast_defs::ClassPtrKind aast::Tprim decl_defs::CollectionStyle decl_defs::ConsistentKind diff --git a/hphp/hack/src/parser/core/type_parser.rs b/hphp/hack/src/parser/core/type_parser.rs index d729f77541dc28..2a248a19e9a9d9 100644 --- a/hphp/hack/src/parser/core/type_parser.rs +++ b/hphp/hack/src/parser/core/type_parser.rs @@ -218,6 +218,7 @@ where | TokenKind::Tilde => self.parse_like_type_specifier(), | TokenKind::At => self.parse_soft_type_specifier(), | TokenKind::LessThanLessThan if allow_attr => self.parse_attributized_specifier(), + | TokenKind::Enum | TokenKind::Class => self.parse_class_ptr_type_specifier(), | TokenKind::Classname => self.parse_classname_type_specifier(), | _ => { diff --git a/hphp/hack/src/parser/lowerer/lowerer.rs b/hphp/hack/src/parser/lowerer/lowerer.rs index 5d8d2cfe465feb..34e1bad70ded79 100644 --- a/hphp/hack/src/parser/lowerer/lowerer.rs +++ b/hphp/hack/src/parser/lowerer/lowerer.rs @@ -1077,13 +1077,20 @@ fn p_hint_<'a>(node: S<'a>, env: &mut Env<'a>) -> Result { ClassPtrTypeSpecifier(c) => { let cls = p_hint(&c.type_, env)?; if env.parser_options.enable_class_pointer_hint { - Ok(HclassPtr(cls)) + let kind = match token_kind(&c.keyword) { + Some(TK::Class) => ast::ClassPtrKind::CKclass, + Some(TK::Enum) => ast::ClassPtrKind::CKenum, + _ => missing_syntax("class pointer type", &c.keyword, env)?, + }; + Ok(HclassPtr(kind, cls)) } else { let p = p_pos(&c.keyword, env); - Ok(Happly( - Id(p, special_classes::CLASS_NAME.to_string()), - vec![cls], - )) + let id = match token_kind(&c.keyword) { + Some(TK::Class) => special_classes::CLASS_NAME, + Some(TK::Enum) => special_classes::ENUM_NAME, + _ => missing_syntax("class pointer type", &c.keyword, env)?, + }; + Ok(Happly(Id(p, id.to_string()), vec![cls])) } } NullableTypeSpecifier(c) => Ok(Hoption(p_hint(&c.type_, env)?)), diff --git a/hphp/hack/src/typing/hint_print.ml b/hphp/hack/src/typing/hint_print.ml index 18ebdad721775b..b67d163cdf83b2 100644 --- a/hphp/hack/src/typing/hint_print.ml +++ b/hphp/hack/src/typing/hint_print.ml @@ -108,8 +108,13 @@ let rec pp_hint ~is_ctx ppf (pos, hint_) = | Aast.Habstr (name, []) | Aast.Happly ((_, name), []) -> Fmt.string ppf name - | Aast.Hclass_ptr h -> - Fmt.(prefix (const string "class") @@ angles @@ pp_hint ~is_ctx:false) ppf h + | Aast.Hclass_ptr (kind, h) -> + let kind = + match kind with + | Aast.CKclass -> "class" + | Aast.CKenum -> "enum" + in + Fmt.(prefix (const string kind) @@ angles @@ pp_hint ~is_ctx:false) ppf h | Aast.Habstr (name, hints) | Aast.Happly ((_, name), hints) -> Fmt.( diff --git a/hphp/hack/src/typing/tast_check/instantiability_check.ml b/hphp/hack/src/typing/tast_check/instantiability_check.ml index 68889987a9bd0b..fb2266d4e8fb13 100644 --- a/hphp/hack/src/typing/tast_check/instantiability_check.ml +++ b/hphp/hack/src/typing/tast_check/instantiability_check.ml @@ -106,8 +106,7 @@ let rec check_hint env (pos, hint) = | Aast.Hvec_or_dict (hopt1, h2) -> Option.iter hopt1 ~f:(check_hint env); check_hint env h2 - | Aast.Hclass_ptr h -> - check_hint env h; + | Aast.Hclass_ptr (_, h) -> validate_classname (Tast_env.tast_env_as_typing_env env) h | Aast.Hoption h | Aast.Hlike h diff --git a/hphp/hack/src/typing/typing_type_wellformedness.ml b/hphp/hack/src/typing/typing_type_wellformedness.ml index 5bf537b0a3a135..52e53110dea98c 100644 --- a/hphp/hack/src/typing/typing_type_wellformedness.ml +++ b/hphp/hack/src/typing/typing_type_wellformedness.ml @@ -194,7 +194,7 @@ and hint_ ~in_signature env p h_ = hints env tup_optional @ hint_opt env tup_variadic | Hsplat h -> hint env h end - | Hclass_ptr h + | Hclass_ptr (_, h) | Hoption h | Hsoft h | Hlike h -> diff --git a/hphp/hack/src/typing/typing_variance.ml b/hphp/hack/src/typing/typing_variance.ml index 79c2fdc9ed6932..c574b10823f58e 100644 --- a/hphp/hack/src/typing/typing_variance.ml +++ b/hphp/hack/src/typing/typing_variance.ml @@ -656,7 +656,7 @@ let rec hint : Env.t -> variance -> Aast_defs.hint -> unit = env.Env.enclosing_class ~default:() ~f:(check_final_this_pos_variance env.Env.env variance pos) - | Hclass_ptr h + | Hclass_ptr (_, h) | Hoption h | Hlike h | Hsoft h diff --git a/hphp/hack/src/typing/write_symbol_info/pretty.ml b/hphp/hack/src/typing/write_symbol_info/pretty.ml index b90a54372b757d..16899b3015e4d4 100644 --- a/hphp/hack/src/typing/write_symbol_info/pretty.ml +++ b/hphp/hack/src/typing/write_symbol_info/pretty.ml @@ -100,8 +100,10 @@ let hint_to_string_and_symbols ~is_ctx (hint : Aast.hint) = | Hfun_context name -> append "ctx "; append (Typing_print.strip_ns name) - | Hclass_ptr hint -> - append "class<"; + | Hclass_ptr (kind, hint) -> + (match kind with + | CKclass -> append "class<" + | CKenum -> append "enum<"); parse ~is_ctx hint; append ">" | Hshape { nsi_allows_unknown_fields; nsi_field_map } -> @@ -272,7 +274,8 @@ let rec hint_to_angle h = let variadic = Option.map tup_variadic ~f:hint_to_angle in Hint.(Key (Tuple { req; opt; variadic })) | Hprim t -> Hint.(Key (Prim (Type.Key (Aast_defs.string_of_tprim t)))) - | Hclass_ptr hint -> Hint.(Key (Class_args (hint_to_angle hint))) + (* TODO(T199610905) update glean schema *) + | Hclass_ptr (_kind, hint) -> Hint.(Key (Class_args (hint_to_angle hint))) | Hfun_context c -> Hint.(Key (Fun_context c)) | Hvec_or_dict (maybe_k, v) -> Hint.( diff --git a/hphp/hack/test/decl/class_pointer_hint.php b/hphp/hack/test/decl/class_pointer_hint.php index 04410bb859ec23..61d1d9c7f06aa3 100644 --- a/hphp/hack/test/decl/class_pointer_hint.php +++ b/hphp/hack/test/decl/class_pointer_hint.php @@ -2,8 +2,13 @@ <> -function param(class $c): void {} +function param(class $c, enum $e): void {} function ret(): class { return C::class; } +function ret_enum(): enum { return E::class; } +enum E: int { + A = 1; +} class C { public static ?class $c = null; + public static ?enum $e = null; } diff --git a/hphp/hack/test/decl/class_pointer_hint.php.exp b/hphp/hack/test/decl/class_pointer_hint.php.exp index cbd81e97b5b2cb..69b32b660a92c4 100644 --- a/hphp/hack/test/decl/class_pointer_hint.php.exp +++ b/hphp/hack/test/decl/class_pointer_hint.php.exp @@ -16,10 +16,24 @@ { Typing_defs_flags.FunParam.accept_disposable = false; inout = false; is_optional = false; readonly = false; ignore_readonly_error = false; splat = false }; - fp_def_value = None } + fp_def_value = None }; + { fp_pos = [5:37-39]; fp_name = (Some "$e"); + fp_type = + (Rhint ([5:29-36]), + (Tclass_ptr + (Rhint ([5:29-36]), + (Tapply (([5:29-36], "\\HH\\BuiltinEnum"), + [(Rhint ([5:34-35]), + (Tapply (([5:34-35], "\\E"), [])))] + ))))); + fp_flags = + { Typing_defs_flags.FunParam.accept_disposable = false; + inout = false; is_optional = false; readonly = false; + ignore_readonly_error = false; splat = false }; + fp_def_value = None } ]; ft_implicit_params = { capability = (CapDefaults [5:10-15]) }; - ft_ret = (Rhint ([5:30-34]), (Tprim Tvoid)); + ft_ret = (Rhint ([5:42-46]), (Tprim Tvoid)); ft_flags = { Typing_defs_flags.Fun.return_disposable = false; async = false; generator = false; fun_kind = FSync; @@ -53,32 +67,107 @@ fe_pos = [6:10-13]; fe_php_std_lib = false; fe_support_dynamic_type = false; fe_no_auto_dynamic = false; fe_no_auto_likes = false })); + ("\\ret_enum", + (Shallow_decl_defs.Fun + { Typing_defs.fe_deprecated = None; fe_module = None; + fe_package = None; fe_internal = false; + fe_type = + (Rwitness_from_decl ([7:10-18]), + (Tfun + { ft_tparams = []; ft_where_constraints = []; ft_params = []; + ft_implicit_params = { capability = (CapDefaults [7:10-18]) }; + ft_ret = + (Rhint ([7:22-29]), + (Tclass_ptr + (Rhint ([7:22-29]), + (Tapply (([7:22-29], "\\HH\\BuiltinEnum"), + [(Rhint ([7:27-28]), (Tapply (([7:27-28], "\\E"), []))) + ] + ))))); + ft_flags = + { Typing_defs_flags.Fun.return_disposable = false; + async = false; generator = false; fun_kind = FSync; + is_function_pointer = false; returns_readonly = false; + readonly_this = false; support_dynamic_type = false; + is_memoized = false; variadic = false }; + ft_cross_package = None })); + fe_pos = [7:10-18]; fe_php_std_lib = false; + fe_support_dynamic_type = false; fe_no_auto_dynamic = false; + fe_no_auto_likes = false })); + ("\\E", + (Shallow_decl_defs.Class + { Shallow_decl_defs.sc_mode = Mstrict; sc_final = false; + sc_abstract = false; sc_is_xhp = false; sc_internal = false; + sc_has_xhp_keyword = false; sc_kind = Cenum; sc_module = None; + sc_name = ([8:6-7], "\\E"); sc_tparams = []; + sc_extends = + [(Rhint ([8:6-7]), + (Tapply (([8:6-7], "\\HH\\BuiltinEnum"), + [(Rhint ([8:6-7]), (Tapply (([8:6-7], "\\E"), [])))]))) + ]; + sc_uses = []; sc_xhp_attr_uses = []; sc_xhp_enum_values = {}; + sc_xhp_marked_empty = false; sc_req_extends = []; + sc_req_implements = []; sc_req_class = []; sc_implements = []; + sc_support_dynamic_type = false; + sc_consts = + [{ Shallow_decl_defs.scc_abstract = Typing_defs.CCConcrete; + scc_name = ([9:3-4], "A"); + scc_type = (Rwitness_from_decl ([9:7-8]), (Tprim Tint)); + scc_refs = []; scc_value = None } + ]; + sc_typeconsts = []; sc_props = []; sc_sprops = []; + sc_constructor = None; sc_static_methods = []; sc_methods = []; + sc_user_attributes = []; + sc_enum_type = + (Some { Typing_defs.te_base = (Rhint ([8:9-12]), (Tprim Tint)); + te_constraint = None; te_includes = [] }); + sc_docs_url = None; sc_package = None })); ("\\C", (Shallow_decl_defs.Class { Shallow_decl_defs.sc_mode = Mstrict; sc_final = false; sc_abstract = false; sc_is_xhp = false; sc_internal = false; sc_has_xhp_keyword = false; sc_kind = (Cclass Concrete); - sc_module = None; sc_name = ([7:7-8], "\\C"); sc_tparams = []; + sc_module = None; sc_name = ([11:7-8], "\\C"); sc_tparams = []; sc_extends = []; sc_uses = []; sc_xhp_attr_uses = []; sc_xhp_enum_values = {}; sc_xhp_marked_empty = false; sc_req_extends = []; sc_req_implements = []; sc_req_class = []; sc_implements = []; sc_support_dynamic_type = false; sc_consts = []; sc_typeconsts = []; sc_props = []; sc_sprops = - [{ Shallow_decl_defs.sp_name = ([8:27-29], "$c"); sp_xhp_attr = None; + [{ Shallow_decl_defs.sp_name = ([12:27-29], "$c"); + sp_xhp_attr = None; sp_type = - (Rhint ([8:17-26]), + (Rhint ([12:17-26]), (Toption - (Rhint ([8:18-26]), + (Rhint ([12:18-26]), (Tclass_ptr - (Rhint ([8:24-25]), (Tapply (([8:24-25], "\\C"), []))))))); + (Rhint ([12:24-25]), (Tapply (([12:24-25], "\\C"), []))))))); sp_visibility = Public; sp_flags = { Shallow_decl_defs.PropFlags.abstract = false; const = false; lateinit = false; lsb = false; needs_init = false; php_std_lib = false; readonly = false; safe_global_variable = false; no_auto_likes = false } - } + }; + { Shallow_decl_defs.sp_name = ([13:26-28], "$e"); + sp_xhp_attr = None; + sp_type = + (Rhint ([13:17-25]), + (Toption + (Rhint ([13:18-25]), + (Tclass_ptr + (Rhint ([13:18-25]), + (Tapply (([13:18-25], "\\HH\\BuiltinEnum"), + [(Rhint ([13:23-24]), + (Tapply (([13:23-24], "\\E"), [])))] + ))))))); + sp_visibility = Public; + sp_flags = + { Shallow_decl_defs.PropFlags.abstract = false; const = false; + lateinit = false; lsb = false; needs_init = false; + php_std_lib = false; readonly = false; + safe_global_variable = false; no_auto_likes = false } + } ]; sc_constructor = None; sc_static_methods = []; sc_methods = []; sc_user_attributes = []; sc_enum_type = None; sc_docs_url = None; diff --git a/hphp/hack/test/decl/class_pointer_hint.php.typecheck.exp b/hphp/hack/test/decl/class_pointer_hint.php.typecheck.exp index 1fe13659155ae6..28152cc1b7a405 100644 --- a/hphp/hack/test/decl/class_pointer_hint.php.typecheck.exp +++ b/hphp/hack/test/decl/class_pointer_hint.php.typecheck.exp @@ -4,3 +4,9 @@ Invalid return type (Typing[4110]) Expected `class` File "classname.hhi", line 12, characters 27-32: But got `string` +ERROR: File "class_pointer_hint.php", line 7, characters 39-46: +Invalid return type (Typing[4110]) + File "class_pointer_hint.php", line 7, characters 22-28: + Expected `class>` + File "classname.hhi", line 12, characters 27-32: + But got `string` diff --git a/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php b/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php index c0c1329a6d8c0b..6d2a4092f2bbaf 100644 --- a/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php +++ b/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php @@ -2,15 +2,22 @@ <> -function param(class $c): void { +function param(class $c, enum $e): void { hh_show($c); + hh_show($e); } function ret(): class { return C::class; } +function ret_enum(): enum { return E::class; } class C { public static ?class $c = null; + public static ?enum $e = null; +} +enum E: int { + A = 1; } function main(): void { hh_show(ret()); hh_show(C::$c); + hh_show(C::$e); } diff --git a/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php.exp b/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php.exp index 47cfc518bcb4cc..661643c9dc72c1 100644 --- a/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php.exp +++ b/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php.exp @@ -1,7 +1,11 @@ File "single.php", line 6, characters 3-13: classname -File "single.php", line 14, characters 3-16: +File "single.php", line 7, characters 3-13: + classname> +File "single.php", line 20, characters 3-16: classname -File "single.php", line 15, characters 3-16: +File "single.php", line 21, characters 3-16: ?classname +File "single.php", line 22, characters 3-16: + ?classname> No errors diff --git a/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php.imp_pess_exp b/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php.imp_pess_exp index 80d0b21b52d5c1..ac66ba4ee464e1 100644 --- a/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php.imp_pess_exp +++ b/hphp/hack/test/typecheck/class_pointers/disable_hint/single.php.imp_pess_exp @@ -1,7 +1,11 @@ File "single.php", line 6, characters 3-13: classname -File "single.php", line 14, characters 3-16: +File "single.php", line 7, characters 3-13: + classname> +File "single.php", line 20, characters 3-16: ~classname -File "single.php", line 15, characters 3-16: +File "single.php", line 21, characters 3-16: ~?classname +File "single.php", line 22, characters 3-16: + ~?classname> No errors diff --git a/hphp/hack/test/typecheck/class_pointers/type_separate/enum_hint.php b/hphp/hack/test/typecheck/class_pointers/type_separate/enum_hint.php new file mode 100644 index 00000000000000..48a258add7b7b9 --- /dev/null +++ b/hphp/hack/test/typecheck/class_pointers/type_separate/enum_hint.php @@ -0,0 +1,24 @@ + $o): HH\enumname { + return $o; +} +function g(enum $o): enum { + return $o; +} +function h(HH\enumname $o): HH\enumname { + return $o; +} +function j(enum $i): enum { + return $i; +} +function k(HH\enumname $i): HH\enumname { + return $i; +} diff --git a/hphp/hack/test/typecheck/class_pointers/type_separate/enum_hint.php.exp b/hphp/hack/test/typecheck/class_pointers/type_separate/enum_hint.php.exp new file mode 100644 index 00000000000000..28fba787dbe42b --- /dev/null +++ b/hphp/hack/test/typecheck/class_pointers/type_separate/enum_hint.php.exp @@ -0,0 +1,24 @@ +ERROR: File "enum_hint.php", line 11, characters 10-11: +Invalid return type (Typing[4110]) + File "enum_hint.php", line 10, characters 30-48: + Expected `classname>` + File "enum_hint.php", line 10, characters 12-23: + But got `class>` +ERROR: File "enum_hint.php", line 13, characters 35-37: +Not a valid class name (Typing[4148]) +ERROR: File "enum_hint.php", line 14, characters 10-11: +Invalid return type (Typing[4110]) + File "enum_hint.php", line 13, characters 35-37: + Expected `int` + File "enum_hint.php", line 13, characters 17-22: + But got `arraykey` arising from an implicit `as arraykey` constraint on this type +ERROR: File "enum_hint.php", line 17, characters 10-11: +Invalid return type (Typing[4110]) + File "enum_hint.php", line 16, characters 49-51: + Expected `int` + File "BuiltinEnum.hhi", line 77, characters 44-44: + via this generic `T` + File "enum_hint.php", line 16, characters 24-29: + But got `arraykey` arising from an implicit `as arraykey` constraint on this type +ERROR: File "enum_hint.php", line 19, characters 34-36: +Not a valid class name (Typing[4148])