123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198(* The inferred-type lattice used while type checking ([inferred_type]), the
mutable union-find cells that carry it ([Cell]), and the shared output
printers and type aliases built on top. [infer.mli] documents the interface
and keeps [Cell.t] abstract. *)openAstmoduleOutput=structincludeOutputletvaltypeft=Wax_utils.Printer.runf(funpp->Output.valtypeppt)letcomptypeft=Wax_utils.Printer.runf(funpp->Output.comptypeppt)letfieldtypeft=Wax_utils.Printer.runf(funpp->Output.fieldtypeppt)letinstrfi=Wax_utils.Printer.runf(funpp->Output.instrppi)endmoduleCell=structtype'astate=Linkof'at|Rootof'aand'at={mutablestate:'astate}letmakev={state=Rootv}(* The root of [node]'s class, compressing the path to it as a side effect:
every link followed is repointed straight at the root. *)letrecrepresentativenode=matchnode.statewith|Root_->node|Linknext->letroot=representativenextinifnext!=rootthennode.state<-Linkroot;rootletgetnode=letroot=representativenodein(* [representative] returns a root, so the [Link] case cannot arise. *)matchroot.statewith|Rootv->v|Link_->assertfalseletmerget1t2new_val=letroot1=representativet1inletroot2=representativet2inifroot1==root2thenroot1.state<-Rootnew_valelsebeginroot1.state<-Linkroot2;root2.state<-Rootnew_valendletsettnew_val=letroot=representativetinroot.state<-Rootnew_valendmoduleInternal=Wax_wasm.Types.InternalmoduleSimd=Wax_wasm.Simdtypeinferred_valtype={typ:valtype;internal:Internal.valtype;(* For a synthesized reference type that has no source name — a string's byte
array, an inline function-type cast target — the referenced composite type.
[typ] keeps the synthetic [<..>] name (so name-based type lookups still
resolve it), but diagnostics render this composite type inline (e.g.
[[mut i8]] or [fn(..) -> ..]) rather than the meaningless synthetic name.
[None] for every other type. *)anon_comptype:comptypeoption;}typeinferred_type=|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 (a call, a field/array access, …) reports one when it meets
an [Unknown] operand. *)|Error(** The recovery type of a value whose own typing already failed (a poison
local/global read, an out-of-range value, an unresolved construction,
…). An error has already been reported, so [Error] propagates silently
— instructions treat it like [Unknown] but raise no further error. *)|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]. Like [Unknown] it carries no concrete type
(so it behaves exactly like [Unknown] everywhere — reporting an error
at a compile-needs-the-type site), except that [subtype] knows it is a
reference: a subtype of every reference type but of no numeric or
vector type, so a numeric use of it is still rejected. *)|Null|Number|Int8|Int16|Int|LargeInt(* An integer literal too large for i32: it can still be i64, f32 or f64
(a numeric literal narrowed by context), but never i32 — and it defaults
to i64 rather than i32. Lets a decompiled out-of-range constant keep its
width instead of overflowing. *)|Float|Valtypeofinferred_valtype|Collectingofcollecting(** Transient state of a fresh cell used as the result / branch-target
type of a block whose result is being inferred (see
[block_infer_general]). A value checked against such a cell (a [br]
target value, or the fall-through) is recorded into [collected] rather
than unified — [subtype] would otherwise assert on an unconstrained
right-hand side. After the body is typed the list is joined by
[val_lub] to give the result. The cell never escapes inference, so
other uses treat it like [Unknown]. *)andcollecting={mutablecollected:(Ast.locationoption*inferred_typeCell.t)list;(** Each value reaching the block's exit (a [br]/[br_on_*] target value or
the fall-through), paired with the location it was produced at when
the caller has one, so a join failure can point at the offending exits
([None] otherwise). *)mutableexacts:(Ast.locationoption*inferred_typeCell.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
typed as the block result, so — unlike an ordinary exit, which need
only be a subtype — its type must equal the result exactly. Recorded
before the delivery pins the live cell; used to keep the annotation
unless every exact matches it, and to reject an inferred result that
differs from one. *)declared:inferred_typeCell.toption;(** The single result type the block already carries while it is being
inferred — a Wasm->Wax annotation under test, or [None] when omitted.
A consumer that needs a concrete type, rather than an ordinary exit
value (a [resume] handler reading its target label's type), resolves
to it. *)mutableneeded:bool;(** Set when [declared] is relied upon in a way the join cannot re-derive
(e.g. read by a [resume] handler), forcing the annotation to be kept.
*)}(* Render an inferred type into a styled printer (colour theme + width supplied
by the caller), so a type embedded in a diagnostic message shares that
message's theme and layout. The flexible-literal families render as their own
[Type]-styled words; concrete types delegate to the shared [Output]. *)letrecoutput_inferred_type_styledspty=letopenWax_utilsinletwords=Styled_printer.print_styledspColors.TypesinmatchCell.gettywith(* A block result still being inferred renders as the annotation under test (the
type a reader, e.g. a mismatched [br]/catch, is checked against), not [any]. *)|Collecting{declared=Somed;_}->output_inferred_type_styledspd|Unknown|Error|Collecting_->word"any"|UnknownRef->word"&_"|Null->word"null"(* These flexible literal types render by the family they resolve to, distinct
from one another and from the concrete valtypes: [number] is any numeric
(i32/i64/f32/f64), [int] an integer (i32/i64), [large number] a numeric
literal too big for i32 (i64/f32/f64, defaulting to i64 — float-capable, so
it belongs to the [number] family, not [int]), [float] a float (f32/f64). *)|Number->word"number"|Int->word"int"|LargeInt->word"large number"|Int16->word"i16"|Int8->word"i8"|Float->word"float"|Valtype{anon_comptype=Somec;_}->Output.comptype_styledspc|Valtypety->Output.valtype_styledspty.typ(* The plain-[Format] rendering, for the stack/debug printers and the editor's
hover string: run the styled renderer with an uncoloured theme. *)letoutput_inferred_typefty=Wax_utils.Printer.runf(funp->output_inferred_type_styled(Wax_utils.Styled_printer.create~printer:p~theme:Wax_utils.Colors.no_color~trivia:(Hashtbl.create0)())ty)(* [Unknown] (unreachable/branch), [Error] (recovery) and [UnknownRef] (a
reference of unknown heap type) all stand for "no concrete type known". They
behave identically except that an [Unknown]/[UnknownRef] operand still
triggers a fresh diagnostic at a compile-needs-the-type site, whereas an
[Error] operand stays silent. This predicate is for the many places that need
only the common "type unknown" test. *)letis_unknown_or_errorty=matchCell.gettywithUnknown|Error|UnknownRef->true|_->false(* The numeric value types, and shared cells holding them. A concrete base type
is never re-resolved during inference (only floating cells are unified into a
concrete type), so a base-type cell's value is invariant and one shared cell
per type is safe — no need to reallocate one on every use. *)leti32_valtype={typ=I32;internal=I32;anon_comptype=None}leti64_valtype={typ=I64;internal=I64;anon_comptype=None}letf32_valtype={typ=F32;internal=F32;anon_comptype=None}letf64_valtype={typ=F64;internal=F64;anon_comptype=None}(* Wrap a (fully resolved) value type in a fresh cell. *)letvaltype_cellv=Cell.make(Valtypev)leti32_cell=valtype_celli32_valtypeleti64_cell=valtype_celli64_valtypeletf32_cell=valtype_cellf32_valtypeletf64_cell=valtype_cellf64_valtype