hphp/hack/src/oxidized/README.md
This document provides comprehensive information about the oxidized crate and
the Hack oxidation system for generating owned Rust types from OCaml source
code.
The oxidized crate contains Rust type definitions generated from OCaml source
code using the hh_oxidize tool. This crate uses owned data structures (Box,
Vec, String) for heap allocation, making it suitable for contexts where data
needs to be owned and persisted.
| 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/) |
The oxidation process is controlled by two configuration files:
copy_types.txt (138 types)Purpose: Types that implement Copy and should be passed by value rather
than moved.
Effect: Enables direct copying instead of expensive moves for small types.
Criteria for inclusion:
Copy trait#[repr(u8)] or simple structs)Example types:
// Simple enums - perfect for copying
pub enum Visibility {
Private, Public, Protected, Internal
}
// Primitive-like types
pub enum Tprim {
Tnull, Tvoid, Tint, Tbool, Tfloat
}
safe_ints_types.txt (1 type)Purpose: Types where OCaml int values should be converted to
ocamlrep::OCamlInt instead of isize.
Single Entry: warnings_saved_state::ErrorHash
Use Case: When you need to preserve the exact OCaml integer representation rather than converting to Rust's native integer types. This is important for:
oxidized/
├── gen/ # Generated Rust files (basic type definitions)
│ ├── aast_defs.rs # AST definitions without lifetimes
│ ├── typing_defs.rs # Type system definitions
│ └── ... # 50+ generated modules
├── manual/ # Hand-written implementations
│ ├── aast_defs_impl.rs # Custom methods for AST types
│ ├── errors_impl.rs # Error handling implementations
│ ├── s_map.rs # String map implementations
│ └── ... # 40+ manual implementations
├── impl_gen/ # Generated trait implementations
│ ├── aast_defs_impl_gen.rs
│ └── ast_defs_impl_gen.rs
├── aast_visitor/ # Visitor pattern for AST traversal
│ ├── visitor.rs # Generic visitor trait
│ ├── visitor_mut.rs # Mutable visitor trait
│ └── node.rs # Node trait for visitable types
├── asts/ # AST type aliases and utilities
│ ├── ast.rs # Raw AST types
│ └── nast.rs # Named AST types
├── stubs/ # Stub implementations for external deps
├── copy_types.txt # Copy optimization configuration
├── safe_ints_types.txt # OCamlInt preservation configuration
├── lib.rs # Crate root and re-exports
└── Cargo.toml # Rust package metadata
gen/)Purpose: Basic type definitions generated from OCaml source.
Characteristics:
Clone, Debug, Serialize, etc.)FromOcamlRep for OCaml interopBox<T> for recursive typesExample:
// From gen/ast_defs.rs
#[derive(Clone, Debug, Deserialize, FromOcamlRep, Serialize)]
pub struct Id(pub Pos, pub String);
pub enum Hint_ {
Hoption(Box<Hint>), // Box for recursion
Htuple(Vec<Hint>), // Vec for collections
Happly(Sid, Vec<Hint>), // Owned strings and vectors
}
manual/)Purpose: Hand-written functionality that can't be automatically generated.
Provides:
Example from manual/aast_defs_impl.rs:
impl Lid {
pub fn new(p: Pos, s: String) -> Self {
Self(p, (0, s))
}
pub fn name(&self) -> &String {
crate::local_id::get_name(&self.1)
}
}
impl AsRef<str> for Visibility {
fn as_ref(&self) -> &str {
match self {
Visibility::Private => "private",
Visibility::Public => "public",
// ...
}
}
}
impl_gen/)Purpose: Automatically generated trait implementations for complex types.
Contains: Generated implementations that are too complex for the main generation pass but can be automated.
aast_visitor/)Purpose: Provides a visitor pattern for traversing and transforming AST nodes.
Key Components:
Visitor trait for immutable traversalVisitorMut trait for mutable transformationsNode trait implemented by all visitable typesExample Usage:
// Traverse an AST and collect function names
struct FunctionNameCollector {
names: Vec<String>,
}
impl<'node> Visitor<'node> for FunctionNameCollector {
type Params = MyParams;
fn visit_fun_def(&mut self, ctx: &mut Context, fun: &FunDef) -> Result<(), Error> {
self.names.push(fun.name.1.clone());
fun.recurse(ctx, self) // Continue traversal
}
}
asts/)Purpose: Convenient type aliases for different AST phases.
ast.rs: Raw AST types (immediately after parsing)nast.rs: Named AST types (after naming resolution)Example:
// Convenient aliases for common AST instantiations
pub type Expr = aast_defs::Expr<(), ()>;
pub type Stmt = aast_defs::Stmt<(), ()>;
pub type Program = aast_defs::Program<(), ()>;
copy_types.txtCheck these criteria:
Copy trait: #[derive(Copy, ...)]#[repr(u8)], simple enums, or small structsVec, String, or Box fields#[derive(Copy, Clone, Debug)] // ✅ Has Copy
#[repr(u8)] // ✅ Small size
pub enum SafeType {
Simple, // ✅ No data
WithSmallData(u32), // ✅ Small primitive
}
safe_ints_types.txtCheck these criteria:
// ❌ NOT safe for copy_types - too large
pub struct LargeStruct {
data: [u64; 1000], // ❌ Too much data to copy
name: String, // ❌ Heap allocated data
}
// ❌ NOT safe for copy_types - no Copy trait
#[derive(Clone, Debug)] // ❌ Missing Copy
pub struct NoCopyType {
value: u32,
}
buck run @fbcode//mode/dev-nosan-lg fbcode//hphp/hack/src:oxidized_regen
manual/ directorycopy_types.txt if appropriate// Implement a visitor to analyze AST
struct TypeAnalyzer {
type_count: HashMap<String, usize>,
}
impl<'node> Visitor<'node> for TypeAnalyzer {
type Params = AnalysisParams;
fn visit_hint(&mut self, ctx: &mut Context, hint: &Hint) -> Result<(), Error> {
// Analyze type hints
if let Hint_::Happly(name, _) = &hint.1.as_ref() {
*self.type_count.entry(name.1.clone()).or_insert(0) += 1;
}
hint.recurse(ctx, self)
}
}
// Common patterns in manual/ files
// 1. Constructor convenience methods
impl MyType {
pub fn new(field1: Type1, field2: Type2) -> Self {
Self { field1, field2 }
}
}
// 2. Conversion traits
impl AsRef<str> for MyEnum {
fn as_ref(&self) -> &str {
match self {
MyEnum::Variant1 => "variant1",
MyEnum::Variant2 => "variant2",
}
}
}
// 3. Display implementations
impl Display for MyType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_ref())
}
}
The oxidation is controlled via command-line arguments to hh_oxidize:
# Generate in ByBox mode (default)
hh_oxidize \
--copy-types-file copy_types.txt \
--safe-ints-types-file safe_ints_types.txt \
--out-dir gen/ \
source_file.ml
# Regenerate all oxidized files
buck run @fbcode//mode/dev-nosan-lg fbcode//hphp/hack/src:oxidized_regen
The oxidized crate serves as the foundation for:
oxidized when you need to own and persist dataocamlrephh_oxidize/ - Core oxidation tool implementationmanual/ directory - Examples of hand-written implementationsaast_visitor/ - Visitor pattern documentation and examples