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 pub use crate::decl_defs::ty::{Exact, Prim};
7 use crate::reason::Reason;
8 use crate::typing_defs::tyvar::Tyvar;
10 use ocamlrep::{Allocator, OpaqueValue, ToOcamlRep};
11 use pos::{Positioned, Symbol, ToOxidized, TypeName};
14 // TODO: Share the representation from decl_defs
15 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
16 pub struct FunParam<R: Reason> {
18 pub name: Option<Symbol>,
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>>,
31 walkable!(FunType<R> => [params, ret]);
33 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
39 impl From<&oxidized::ast_defs::ParamKind> for ParamMode {
40 fn from(pk: &oxidized::ast_defs::ParamKind) -> Self {
42 oxidized::ast::ParamKind::Pinout(_) => Self::FPinout,
43 oxidized::ast::ParamKind::Pnormal => Self::FPnormal,
48 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
49 pub enum Ty_<R: Reason, TY> {
59 /// A wrapper around `FunType`, which contains the full type information
60 /// for a function, method, lambda, etc.
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
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
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>),
88 walkable!(impl<R: Reason, TY> for Ty_<R, TY> => {
92 Ty_::Tfun(fun_type) => [fun_type],
94 Ty_::Tgeneric(_, args) => [args],
95 Ty_::Tclass(_, _, args) => [args],
96 Ty_::Tunion(args) => [args],
97 Ty_::Toption(arg) => [arg],
101 impl<R: Reason> hcons::Consable for Ty_<R, Ty<R>> {
103 fn conser() -> &'static hcons::Conser<Ty_<R, Ty<R>>> {
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> {
115 pub fn new(reason: R, ty: Ty_<R, Ty<R>>) -> Self {
116 Self(reason, Hc::new(ty))
119 pub fn mixed(r: R) -> Ty<R> {
120 Self::new(r, Ty_::Tmixed)
123 pub fn nothing(r: R) -> Ty<R> {
124 Self::new(r, Ty_::Tmixed)
127 pub fn prim(r: R, prim: Prim) -> Ty<R> {
128 Self::new(r, Ty_::Tprim(prim))
131 pub fn null(r: R) -> Ty<R> {
132 Self::prim(r, Prim::Tnull)
134 pub fn void(r: R) -> Ty<R> {
135 Self::prim(r, Prim::Tvoid)
137 pub fn int(r: R) -> Ty<R> {
138 Self::prim(r, Prim::Tint)
140 pub fn float(r: R) -> Ty<R> {
141 Self::prim(r, Prim::Tfloat)
143 pub fn string(r: R) -> Ty<R> {
144 Self::prim(r, Prim::Tstring)
146 pub fn num(r: R) -> Ty<R> {
147 Self::prim(r, Prim::Tnum)
150 pub fn arraykey(r: R) -> Ty<R> {
151 Self::prim(r, Prim::Tarraykey)
154 pub fn fun(r: R, ft: FunType<R>) -> Ty<R> {
155 Self::new(r, Ty_::Tfun(ft))
158 pub fn option(r: R, ty: Ty<R>) -> Self {
159 Self::new(r, Ty_::Toption(ty))
162 pub fn union(r: R, tys: Vec<Ty<R>>) -> Self {
167 Self::new(r, Ty_::Tunion(tys))
171 pub fn var(r: R, tv: Tyvar) -> Self {
172 Self::new(r, Ty_::Tvar(tv))
175 pub fn any(r: R) -> Ty<R> {
176 Self::new(r, Ty_::Tany)
179 pub fn generic(r: R, ty_name: TypeName, args: Vec<Ty<R>>) -> Self {
180 Self::new(r, Ty_::Tgeneric(ty_name, args))
183 pub fn reason(&self) -> &R {
187 pub fn generic_name(&self) -> Option<&TypeName> {
189 Ty_::Tgeneric(name, _) => Some(name),
194 pub fn node(&self) -> &Hc<Ty_<R, Ty<R>>> {
199 impl<R: Reason> Deref for Ty<R> {
200 type Target = Ty_<R, Ty<R>>;
201 fn deref(&self) -> &Self::Target {
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),
225 &*arena.alloc_slice_fill_iter(
226 tys.iter().map(|ty| &*arena.alloc(ty.to_oxidized(arena))),
230 oxidized_by_ref::typing_defs::Ty(r, ty)
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).
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.
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
269 std::mem::transmute::<
270 &'_ oxidized_by_ref::typing_defs::Ty<'_>,
271 &'a oxidized_by_ref::typing_defs::Ty<'a>,
274 ty.to_ocamlrep(alloc)