Module Wax_lang.InferSource

The inferred-type lattice used while type checking, the mutable cells (Cell) that carry it, and the shared printers/type aliases built on top.

Sourcemodule Output : sig ... end

Output printers wrapping Output so they take a Format.formatter directly (rather than a Wax_utils.Printer.t), for use in diagnostics.

Sourcemodule Cell : sig ... end

A mutable cell carrying a value, backed by union-find: merge unifies two cells so they share one value, and get resolves a cell to that value.

Sourcemodule Simd = Wax_wasm.Simd
Sourcetype inferred_valtype = {
  1. typ : Ast.valtype;
  2. internal : Internal.valtype;
  3. anon_comptype : Ast.comptype option;
    (*

    For a synthesized reference type with no source name — a string's byte array, an inline function-type cast target — the referenced composite type, which diagnostics render inline (e.g. [mut i8]) instead of the meaningless synthetic <..> name kept in typ. None otherwise.

    *)
}
Sourcetype inferred_type =
  1. | Unknown
    (*

    The genuinely polymorphic type of a value taken off the stack of unreachable or branch-terminated code. No error has been reported for it: an instruction that needs its operand's concrete type to be compiled reports one when it meets an Unknown operand.

    *)
  2. | Error
    (*

    The recovery type of a value whose own typing already failed. An error has already been reported, so Error propagates silently — treated like Unknown but raising no further error.

    *)
  3. | UnknownRef
    (*

    A non-null reference of unknown heap type — the Wax counterpart of the Wasm (ref bot). Produced when a reference is recovered from an otherwise-polymorphic value (null! / br_on_null on an Unknown operand or a bare null). Behaves like Unknown everywhere except that subtype knows it is a reference: a subtype of every reference type but of no numeric or vector type.

    *)
  4. | Null
    (*

    A bare null literal: a null reference whose heap type is not yet fixed. Narrowed to a concrete reference type by context; with none it takes the bottom of the relevant hierarchy.

    *)
  5. | Number
    (*

    Any numeric literal: narrows to i32, i64, f32 or f64, defaulting to i32. The bottom of the flexible-literal lattice.

    *)
  6. | Int8
    (*

    A packed narrow read — a load8 or an i8 struct/array field — which yields an i32 value tracked as 8-bit wide so a following widening cast (as i64_s/i64_u) fuses into the read. Defaults to i32.

    *)
  7. | Int16
    (*

    A packed narrow read — a load16 or an i16 field — as Int8 but 16-bit. Defaults to i32.

    *)
  8. | Int
    (*

    An integer literal committed to the integer family (e.g. by a bitwise or shift operator): narrows to i32 or i64, defaulting to i32. It can no longer become a float by inference — only an explicit as cast, which emits a conversion, does that.

    *)
  9. | LargeInt
    (*

    A numeric literal too large for i32: narrows to i64, f32 or f64 (never i32), defaulting to i64. Despite the name it is float-capable and so belongs to the number family, not Int — it is Number with i32 excluded by magnitude. Lets a decompiled out-of-range constant keep its width instead of overflowing, and renders as large number.

    *)
  10. | Float
    (*

    A floating-point literal: narrows to f32 or f64, defaulting to f64.

    *)
  11. | Valtype of inferred_valtype
  12. | Collecting of collecting
    (*

    Transient state of a fresh cell used as the result / branch-target type of a block whose result is being inferred. A value checked against it is recorded into collected rather than unified, then joined by val_lub once the body is typed. The cell never escapes inference, so other uses treat it like Unknown.

    *)
Sourceand collecting = {
  1. mutable collected : (Ast.location option * inferred_type Cell.t) list;
    (*

    Each value reaching the block's exit, paired with the location it was produced at (when known) so a join failure can point at the offending exits.

    *)
  2. mutable exacts : (Ast.location option * inferred_type Cell.t) list;
    (*

    Snapshots of the natural types of values delivered by br_if (and other pass-through branches). Such a value continues on the stack and is typed as the block result, so — unlike an ordinary exit, which need only be a subtype — its type must be exactly the result. Recorded before the delivery pins the live cell; used to decide the keep-bool (drop the annotation only if every exact matches it) and to reject an inferred block whose result would differ from an exact.

    *)
  3. declared : inferred_type Cell.t option;
    (*

    The single result type the block already carries while being inferred — a Wasm->Wax annotation under test, or None when omitted.

    *)
  4. mutable needed : bool;
    (*

    Set when declared is relied upon in a way the join cannot re-derive, forcing the annotation to be kept.

    *)
}
Sourceval output_inferred_type_styled : Wax_utils.Styled_printer.t -> inferred_type Cell.t -> unit

Render an inferred type into a styled printer, so it shares a diagnostic message's colour theme and width (an unresolved one prints as any). This is what the typer's Message typ combinator is built on.

Sourceval output_inferred_type : Format.formatter -> inferred_type Cell.t -> unit

Render an inferred type as plain (uncoloured) text — for the editor's hover string and the stack/debug printers. Diagnostics use output_inferred_type_styled instead.

Sourceval is_unknown_or_error : inferred_type Cell.t -> bool

Whether a cell resolves to Unknown, Error or UnknownRef — the common "no concrete type known" test.

The numeric value types, and shared cells holding them. A concrete base type is never re-resolved during inference, so a base-type cell's value never changes and a single shared cell per type can stand in for a fresh one.

Sourceval i32_valtype : inferred_valtype
Sourceval i64_valtype : inferred_valtype
Sourceval f32_valtype : inferred_valtype
Sourceval f64_valtype : inferred_valtype

valtype_cell v wraps a fully resolved value type in a fresh cell.