hphp/hack/src/hh_oxidize/README.md
This document provides comprehensive information about the hh_oxidize tool,
which converts OCaml AST definitions to Rust types, generating the oxidized
crate.
hh_oxidize is a command-line tool that parses OCaml source files and generates
corresponding Rust type definitions with owned, heap-allocated types.
graph TD
A[OCaml Source Files] --> B[hh_oxidize Tool]
B --> C[oxidized Crate]
F[Configuration Files] --> B
G[.mli Interface Files] --> B
subgraph "Configuration Files"
F1[copy_types.txt]
F4[safe_ints_types.txt]
end
subgraph "Generated Output"
C1[Owned Types
Box, Vec, String]
C2[Manual Implementations
40+ files]
C3[Visitor Pattern
AST traversal]
end
C --> C1
C --> C2
C --> C3
hh_oxidize.ml)Purpose: Command-line interface, argument parsing, and orchestration of the conversion process.
Key Functions:
rustfmt and sign filesmod.rs)Usage Examples:
# Generate oxidized crate
hh_oxidize \
--copy-types-file copy_types.txt \
--safe-ints-types-file safe_ints_types.txt \
--out-dir gen/ \
source_file.ml
configuration.ml)Purpose: Central configuration management for type conversion decisions.
Configuration Types:
type t = {
extern_types: string SMap.t; (* Type re-exports *)
copy_types: SSet.t option; (* Types implementing Copy *)
safe_ints_types: SSet.t; (* OCamlInt preservation *)
}
Key Decision Functions:
extern_type: Look up external type mappingscopy_type: Determine if type should implement Copysafe_ints: Check if int should be OCamlInt vs isizeconvert_type_decl.ml)Purpose: Core logic for converting OCaml type declarations to Rust equivalents.
Major Responsibilities:
let default_derives () =
[(Some "ocamlrep", "FromOcamlRep"); (Some "serde", "Deserialize")]
@ [
(None, "Clone"); (None, "Debug"); (None, "Eq");
(None, "Hash"); (None, "Ord"); (None, "PartialEq");
(None, "PartialOrd"); (Some "no_pos_hash", "NoPosHash");
(* ... more traits ... *)
]
rust_type.ml)Purpose: Abstract representation of Rust types with proper lifetime tracking.
Type Definition:
type t =
| Var of string (* Type variable *)
| Ref of (lifetime * t) (* &'a T *)
| Type of { (* Concrete type *)
name: string;
lifetimes: lifetime list;
params: t list;
}
Key Functions:
rust_type_to_string: Convert to Rust syntaxcontains_ref: Check for reference typesderef: Remove reference wrappertype_name_and_params: Extract type informationoutput.ml)Purpose: Accumulate and organize generated Rust code.
Output Structure:
type oxidized_module = {
extern_uses: SSet.t; (* External use statements *)
glob_uses: SSet.t; (* Glob imports *)
aliases: (string * string) list; (* Type aliases *)
includes: SSet.t; (* Module includes *)
ty_reexports: string list; (* Type re-exports *)
decls: (string * string) list; (* Type declarations *)
}
| Aspect | Description |
|---|---|
| Recursive Types | Box<T> |
| Collections | Vec<T> |
| Strings | String |
| Memory Management | Heap allocation |
| Lifetime Parameters | None |
| Memory Overhead | High (individual allocations) |
| Use Cases | Data persistence, serialization |
| Configuration Files | 2 files (copy_types, safe_ints) |
| Generated Structure | Rich (manual/, visitor/, impl_gen/) |
When generating the oxidized crate, hh_oxidize uses:
copy_types.txt (138 types): Enables Copy derive for small typessafe_ints_types.txt (1 type): Preserves OCaml integer representationResult: Owned types with extensive manual implementations and visitor patterns.
Source: Parses OCaml (** ... *) comments and .mli files Output: Rust
/// ... documentation comments
let convert_doc_comment doc =
doc
|> String.strip ~drop:(function | '*' | ' ' | '\n' | '\t' -> true | _ -> false)
|> String.split ~on:'\n'
|> List.fold ~f:(fun acc line -> sprintf "/// %s\n" line :: acc)
|> String.concat
OCaml Attributes → Rust Attributes:
[@value 42] → C-like enum discriminants[@deriving ...] → #[derive(...)]/// ...Automatic serde attributes for serialization:
#[derive(Serialize, Deserialize)]
.txt files if neededCommon Problems and Solutions:
| Problem | Solution |
|---|---|
| Missing Copy implementation | Add to copy_types.txt |
| Duplicate type definitions | Add to extern_types.txt |
| OCaml integer compatibility issues | Add to safe_ints_types.txt |
# Build the oxidizer tool
buck build @fbcode//mode/dev-nosan-lg fbcode//hphp/hack/src/hh_oxidize:hh_oxidize
# Regenerate oxidized crate
buck run @fbcode//mode/dev-nosan-lg fbcode//hphp/hack/src:oxidized_regen
Benefits:
Uses compiler-libs.common:
The tool intelligently skips certain type declarations:
exception Skip_type_decl of string
(* Examples of skipped types *)
raise (Skip_type_decl "polymorphic variants not supported")
raise (Skip_type_decl "it is a re-export of " ^ id)
raise (Skip_type_decl "Abstract types without manifest not supported")
Hierarchical logging with indentation for conversion tracing:
let log fmt = ksprintf (Printf.eprintf "%s%s\n%!" (String.make !log_indent ' ')) fmt
let with_log_indent f = (* ... manages indentation ... *)
src/ directory OCaml filesoxidized/gen/The modular design allows for:
hh_oxidize is the single source of truth for
OCaml → Rust conversion../oxidized/README.md../oxidized/copy_types.txt,
../oxidized/safe_ints_types.txtBUCK file for build system integrationcompiler-libs.common documentation for parsing
capabilities