wasm_types (wasm v0.1.0)
View SourceType identity: when two declared types are the same type. Read this when a cast or an import you expected to succeed is being refused as a type mismatch.
WebAssembly's garbage collection proposal makes this non-trivial, because types live in recursive groups whose members may refer to one another and to themselves. Two questions have to be answered differently:
- Two structurally identical types in separate groups are the same type.
(type $a (func (param f32)))and(type $b (func (param f32)))are interchangeable, and a(ref $a)satisfies a(ref $b). - Two structurally identical types in one group are different types.
A
throwof the first must not be caught by acatchnaming the second.
So identity is the pair {canonical group, position within it}. A group
canonicalises to a structural key in which references inside the group are by
group-relative position and references outside it are by the referent's own
canonical identity, and equal keys are interned to one id.
Identity has to hold across modules, since one module imports a function
whose type another declared, so the interning table is node-wide and lives in
the store wasm_engine owns.
Summary
Types
Canonical identity of each type index: {GroupId, PositionInGroup}.
Functions
Every type Idx is a subtype of, canonically, including itself.
Assign every type index its canonical identity.
Whether type A is a subtype of type B, following declared supertypes.
Whether two type indices name the same type.
Types
-nominal canon() :: {non_neg_integer(), non_neg_integer()}.
Canonical identity of each type index: {GroupId, PositionInGroup}.
-type heaptype() :: func | extern | exn | any | eq | i31 | struct | array | nofunc | noextern | noexn | none | {type, typeidx()}.
-type mut() :: const | var.
-type numtype() :: i32 | i64 | f32 | f64.
-type reftype() :: {ref, null | nonull, heaptype()}.
-type typeidx() :: non_neg_integer().
-type vectype() :: v128.
Functions
-spec canon_supers(non_neg_integer(), tuple(), tuple()) -> [canon()].
Every type Idx is a subtype of, canonically, including itself.
Used where a value's type has to be tested against a declared one and the two may come from different modules, so indices cannot be compared.
-spec canonicalise([#subtype{final :: boolean(), supers :: [typeidx()], body :: #functype{params :: [valtype()], results :: [valtype()]} | #structtype{fields :: [#fieldtype{type :: valtype() | i8 | i16, mut :: mut()}]} | #arraytype{field :: #fieldtype{type :: valtype() | i8 | i16, mut :: mut()}}}], [{non_neg_integer(), pos_integer()}]) -> tuple().
Assign every type index its canonical identity.
Groups are processed in order, so by the time a group is canonicalised every group it refers to already has an identity, and references within the group itself are positional.
-spec is_subtype(non_neg_integer(), non_neg_integer(), tuple(), tuple()) -> boolean().
Whether type A is a subtype of type B, following declared supertypes.
Reflexive, and transitive through #subtype.supers. The declaration is what
counts: two structurally identical types are not in a subtype relation unless
one declares the other, which is what sub exists to say.
Each step compares canonically, not by index. A type may declare a supertype
that is a different index from the one being asked about while naming the same
type, which is exactly what happens when two recursive groups canonicalise
together: the chain 8 -> 6 -> 2 answers a question about type 0 when 0 and 2
are the same type.
-spec same(non_neg_integer(), non_neg_integer()) -> boolean().
Whether two type indices name the same type.