Semantic Diff

#9221 Read type nodes through accessors (POC for an int-indexed type arena)

3 commits

227b697763ca1c08773343bd9e4da9629b920037..pr-9221

be634915d · JonoPrest · Nov 04, 2026, 15:01 UTC

Add accessors for type nodes

The type checker reads and writes the fields of type_expr directly in about 1,000 places, which ties it to the boxed mutable record. Upstream OCaml (4.14 and later) made type_expr abstract behind get_desc, get_level, get_id and Transient_expr; this adds the same layer while the record stays visible, so modules can be converted one at a time and type_expr made abstract once none reads the fields.

get_desc returns the node's own descriptor and does not call repr, so callers keep calling repr first and path compression stays where it is. get_desc, get_level and the raw setters are field primitives, which compile to the same loads and stores as field access, also in dune's dev profile, which builds with -opaque.

Review

  • Does this change the representation or any output? No. The record and its field order are unchanged; only the way code reaches the fields is new.
9eb6866be · JonoPrest · Nov 04, 2026, 15:01 UTC

Read and write type nodes through the accessors in Btype

Btype now uses get_desc, get_level and get_id for every read of a type node and Transient_expr for creation and its unlogged writes (path compression, the backtracking trail, copy sessions, marking). Record patterns such as {desc = Tvar _} become matches on get_desc.

Review

  • Do repr's path compression and its trail entries change? No. repr_link takes the same arguments, compresses the same nodes and logs the same Ccompress entries.
75d97634b · JonoPrest · Nov 04, 2026, 15:01 UTC

Read and write type nodes through the accessors in Ctype

Ctype, the core of unification, instantiation, generalization and the occur checks, now reads type nodes only through get_desc, get_level and get_id, and makes its unlogged writes (copy marks, level marks, temporary links, writes after log_type) through Transient_expr. Its local get_level env p, the binding level of a path, is renamed get_path_level so that it does not shadow Types.get_level.

Review

  • Does the conversion change evaluation order, sharing or allocation? No. Writes still evaluate the new descriptor before storing it, filter_visited returns the same list cell, and the already-copied variant case in copy matches with a guard instead of building an option.