eliminate `lib` from ty crate
[hiphop-php.git] / hphp / hack / src / rupro / ty / typing_defs / ty.rs
blob534ba3b88780d472d292b87cbc1ddc6f33e57426
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 pub use crate::decl_defs::ty::{Exact, Prim};
7 use crate::reason::Reason;
8 use crate::typing_defs::tyvar::Tyvar;
9 use hcons::Hc;
10 use ocamlrep::{Allocator, OpaqueValue, ToOcamlRep};
11 use pos::{Positioned, Symbol, ToOxidized, TypeName};
12 use std::ops::Deref;
14 // TODO: Share the representation from decl_defs
15 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
16 pub struct FunParam<R: Reason> {
17     pub pos: R::Pos,
18     pub name: Option<Symbol>,
19     pub ty: Ty<R>,
22 walkable!(FunParam<R> => [ty]);
24 // TODO: Share the representation from decl_defs
25 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
26 pub struct FunType<R: Reason> {
27     pub params: Vec<FunParam<R>>,
28     pub ret: Ty<R>,
31 walkable!(FunType<R> => [params, ret]);
33 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34 pub enum ParamMode {
35     FPnormal,
36     FPinout,
39 impl From<&oxidized::ast_defs::ParamKind> for ParamMode {
40     fn from(pk: &oxidized::ast_defs::ParamKind) -> Self {
41         match pk {
42             oxidized::ast::ParamKind::Pinout(_) => Self::FPinout,
43             oxidized::ast::ParamKind::Pnormal => Self::FPnormal,
44         }
45     }
48 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
49 pub enum Ty_<R: Reason, TY> {
50     /// Top
51     Tmixed,
53     /// Bottom
54     Tnothing,
56     /// A primitive type
57     Tprim(Prim),
59     /// A wrapper around `FunType`, which contains the full type information
60     /// for a function, method, lambda, etc.
61     Tfun(FunType<R>),
63     /// Any type
64     /// TODO: any and err are a bit weird in that they are not actually types
65     /// but rather they represent a set of inconsistent bounds on a tyvar
66     /// we might want to rethink them prefering a sum type _or_
67     /// distinguishing types with `Tany` from those without
68     Tany,
70     /// The type of a generic parameter. The constraints on a generic parameter
71     /// are accessed through the lenv.tpenv component of the environment, which
72     /// is set up when checking the body of a function or method. See uses of
73     /// Typing_phase.add_generic_parameters_and_constraints. The list denotes
74     /// type arguments.
75     Tgeneric(TypeName, Vec<TY>),
77     /// An instance of a class or interface, ty list are the arguments
78     /// If exact=Exact, then this represents instances of *exactly* this class
79     /// If exact=Nonexact, this also includes subclasses
80     Tclass(Positioned<TypeName, R::Pos>, Exact, Vec<TY>),
82     Tvar(Tyvar),
84     Tunion(Vec<TY>),
85     Toption(TY),
88 walkable!(impl<R: Reason, TY> for Ty_<R, TY> =>  {
89     Ty_::Tmixed => [],
90     Ty_::Tnothing => [],
91     Ty_::Tprim(_) => [],
92     Ty_::Tfun(fun_type) => [fun_type],
93     Ty_::Tany => [],
94     Ty_::Tgeneric(_, args) => [args],
95     Ty_::Tclass(_, _, args) => [args],
96     Ty_::Tunion(args) => [args],
97     Ty_::Toption(arg) => [arg],
98     Ty_::Tvar(_) => [],
99 });
101 impl<R: Reason> hcons::Consable for Ty_<R, Ty<R>> {
102     #[inline]
103     fn conser() -> &'static hcons::Conser<Ty_<R, Ty<R>>> {
104         R::ty_conser()
105     }
108 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
109 pub struct Ty<R: Reason>(R, Hc<Ty_<R, Ty<R>>>);
111 walkable!(Ty<R> as visit_ty => [0, 1]);
113 impl<R: Reason> Ty<R> {
114     #[inline]
115     pub fn new(reason: R, ty: Ty_<R, Ty<R>>) -> Self {
116         Self(reason, Hc::new(ty))
117     }
119     pub fn mixed(r: R) -> Ty<R> {
120         Self::new(r, Ty_::Tmixed)
121     }
123     pub fn nothing(r: R) -> Ty<R> {
124         Self::new(r, Ty_::Tmixed)
125     }
127     pub fn prim(r: R, prim: Prim) -> Ty<R> {
128         Self::new(r, Ty_::Tprim(prim))
129     }
131     pub fn null(r: R) -> Ty<R> {
132         Self::prim(r, Prim::Tnull)
133     }
134     pub fn void(r: R) -> Ty<R> {
135         Self::prim(r, Prim::Tvoid)
136     }
137     pub fn int(r: R) -> Ty<R> {
138         Self::prim(r, Prim::Tint)
139     }
140     pub fn float(r: R) -> Ty<R> {
141         Self::prim(r, Prim::Tfloat)
142     }
143     pub fn string(r: R) -> Ty<R> {
144         Self::prim(r, Prim::Tstring)
145     }
146     pub fn num(r: R) -> Ty<R> {
147         Self::prim(r, Prim::Tnum)
148     }
150     pub fn arraykey(r: R) -> Ty<R> {
151         Self::prim(r, Prim::Tarraykey)
152     }
154     pub fn fun(r: R, ft: FunType<R>) -> Ty<R> {
155         Self::new(r, Ty_::Tfun(ft))
156     }
158     pub fn option(r: R, ty: Ty<R>) -> Self {
159         Self::new(r, Ty_::Toption(ty))
160     }
162     pub fn union(r: R, tys: Vec<Ty<R>>) -> Self {
163         let ln = tys.len();
164         if ln == 0 {
165             Self::nothing(r)
166         } else {
167             Self::new(r, Ty_::Tunion(tys))
168         }
169     }
171     pub fn var(r: R, tv: Tyvar) -> Self {
172         Self::new(r, Ty_::Tvar(tv))
173     }
175     pub fn any(r: R) -> Ty<R> {
176         Self::new(r, Ty_::Tany)
177     }
179     pub fn generic(r: R, ty_name: TypeName, args: Vec<Ty<R>>) -> Self {
180         Self::new(r, Ty_::Tgeneric(ty_name, args))
181     }
183     pub fn reason(&self) -> &R {
184         &self.0
185     }
187     pub fn generic_name(&self) -> Option<&TypeName> {
188         match self.deref() {
189             Ty_::Tgeneric(name, _) => Some(name),
190             _ => None,
191         }
192     }
194     pub fn node(&self) -> &Hc<Ty_<R, Ty<R>>> {
195         &self.1
196     }
199 impl<R: Reason> Deref for Ty<R> {
200     type Target = Ty_<R, Ty<R>>;
201     fn deref(&self) -> &Self::Target {
202         &self.1
203     }
206 impl<'a, R: Reason> ToOxidized<'a> for Ty<R> {
207     type Output = oxidized_by_ref::typing_defs::Ty<'a>;
209     fn to_oxidized(&self, arena: &'a bumpalo::Bump) -> Self::Output {
210         use oxidized_by_ref::typing_defs::Ty_ as OTy_;
211         let r = arena.alloc(self.reason().to_oxidized(arena));
212         let ty = match &**self.node() {
213             Ty_::Tvar(tv) => OTy_::Tvar((*tv).into()),
214             Ty_::Tprim(x) => OTy_::Tprim(arena.alloc(*x)),
215             Ty_::Tmixed => OTy_::Tmixed,
216             Ty_::Tnothing => todo!(),
217             Ty_::Toption(_) => todo!(),
218             Ty_::Tunion(_) => todo!(),
219             Ty_::Tfun(_) => todo!(),
220             Ty_::Tany => todo!(),
221             Ty_::Tgeneric(_, _) => todo!(),
222             Ty_::Tclass(pos_id, exact, tys) => OTy_::Tclass(&*arena.alloc((
223                 pos_id.to_oxidized(arena),
224                 *exact,
225                 &*arena.alloc_slice_fill_iter(
226                     tys.iter().map(|ty| &*arena.alloc(ty.to_oxidized(arena))),
227                 ),
228             ))),
229         };
230         oxidized_by_ref::typing_defs::Ty(r, ty)
231     }
234 impl<R: Reason> ToOcamlRep for Ty<R> {
235     fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> OpaqueValue<'a> {
236         // This implementation of `to_ocamlrep` (which allocates in an arena,
237         // converts to OCaml, then drops the arena) violates a `ToOcamlRep`
238         // requirement: we may not drop values after passing them to `alloc.add`
239         // or invoking `to_ocamlrep` (else memoization will behave incorrectly
240         // in `add_root`). This leads to bizarre behavior (particularly in
241         // optimized builds).
242         //
243         // For example, suppose we're converting a typed AST via ToOcamlRep, and
244         // it contains the types `int` and `float`. When converting `int`, we'll
245         // construct an arena and arena-allocate Tint, in order to construct the
246         // oxidized_by_ref value `Tprim(&Tint)`, and convert that to OCaml. The
247         // `ocamlrep::Allocator` will remember that the address of the `&Tint`
248         // pointer corresponds to a certain OCaml value, so that when it
249         // encounters future instances of that pointer, it can use that same
250         // OCaml value rather than allocating a new one. We'd then free the
251         // arena once we're finished converting that type. When converting the
252         // second type, we construct a new arena, arena-allocate Tfloat, and
253         // attempt to construct `Tprim(&Tfloat)`. But if the new arena was
254         // allocated in the same location as the old, it may choose the same
255         // address for our arena-allocated `Tfloat` as our `Tint` was, and our
256         // ocamlrep Allocator will incorrectly use the `Tint` OCaml value.
257         //
258         // This memoization behavior is only enabled if we invoke
259         // `ocamlrep::Allocator::add_root`, so we must take care not to use it
260         // (including indirectly, through macros like `ocaml_ffi`) on values
261         // containing this type.
262         let arena = &bumpalo::Bump::new();
263         let ty = self.to_oxidized(arena);
264         // SAFETY: Transmute away the lifetime to allow the arena-allocated
265         // value to be converted to OCaml. Won't break type safety in Rust, but
266         // will produce broken OCaml values if used with `add_root` (see above
267         // comment).
268         let ty = unsafe {
269             std::mem::transmute::<
270                 &'_ oxidized_by_ref::typing_defs::Ty<'_>,
271                 &'a oxidized_by_ref::typing_defs::Ty<'a>,
272             >(&ty)
273         };
274         ty.to_ocamlrep(alloc)
275     }