// The Wax instance of the wasm type family, plus the small types the AST needs.
//
// `wasm_types` holds the Idx-generic spine. The four types below carry the
// annotated-array wrappers, which differ per language form, so the reference
// leaves them to each instance and so do we. In Wax they are arrays of NAMED,
// SPANNED entries, because Wax refers to types by name and a diagnostic must be
// able to point at the individual parameter or field.
///|
/// An identifier with its span.
///
/// This is the `Idx` the Wax AST instantiates the type family at. The reference
/// spells it `(string, location) annotated`; a named struct is the same shape
/// with readable field names.
pub(all) struct Ident {
name : String
loc : Location
} derive(Eq, Debug)
///|
/// A function signature.
///
/// Parameters may be anonymous (`fn(i32)`) or named (`fn(x: i32)`), hence the
/// optional name; results never are.
pub(all) struct FuncType {
params : Array[Annotated[(Ident?, ValType), Location]]
results : Array[ValType]
} derive(Eq, Debug)
///|
/// What a defined type actually is.
pub(all) enum CompType {
Func(FuncType)
Struct(Array[Annotated[(Ident, FieldType), Location]])
Array(FieldType)
/// A continuation type, from the stack-switching proposal.
Cont(Ident)
} derive(Eq, Debug)
///|
/// One member of a recursion group.
///
/// `descriptor`/`describes` are the custom-descriptors proposal's pairing; both
/// are present because the relation is navigable in each direction.
pub(all) struct SubType {
typ : CompType
supertype : Ident?
/// Whether the type forbids further subtypes.
final_ : Bool
descriptor : Ident?
describes : Ident?
} derive(Eq, Debug)
///|
/// A recursion group: types that may refer to each other.
///
/// A single `type t = ...` is a one-member group, so there is no separate
/// non-recursive form.
pub type RecType = Array[Annotated[(Ident, SubType), Location]]
///|
/// The field name marking a `..` splice at the head of a struct definition,
/// which inherits the supertype's fields.
///
/// `..` is not a valid identifier, so it can never collide with a real field.
/// The typer replaces it with the supertype's fields and the printer renders it
/// back as `..`; its field TYPE is a placeholder and is never inspected.
pub let splice_field_name : String = ".."
///|
pub fn is_splice_field(f : Annotated[(Ident, FieldType), Location]) -> Bool {
f.desc.0.name == splice_field_name
}
///|
/// Build the `..` splice sentinel at the given span.
pub fn splice_field(loc : Location) -> Annotated[(Ident, FieldType), Location] {
{
desc: ({ name: splice_field_name, loc }, { mut_: false, typ: Value(I32) }),
info: loc,
}
}
///|
/// Unary operators.
pub(all) enum UnOp {
Neg
Pos
Not
} derive(Eq, Debug)
///|
/// Binary operators.
///
/// Signedness is part of the operator rather than inferred, because
/// WebAssembly has genuinely different instructions for signed and unsigned
/// division, remainder, shift and comparison. It is optional on the operators
/// where floats also apply (`/`, `<`, `>`, `<=`, `>=`): `None` means the
/// spelling did not commit, and the typer resolves it from the operand type.
pub(all) enum BinOp {
Add
Sub
Mul
Div(Signage?)
Rem(Signage)
And
Or
Xor
Shl
Shr(Signage)
Eq
Ne
Lt(Signage?)
Gt(Signage?)
Le(Signage?)
Ge(Signage?)
} derive(Eq, Debug)
///|
/// A block label.
pub type Label = Ident
///|
/// What an `as` cast targets.
pub(all) enum CastType {
Value(ValType)
/// `&fn(...)`, a function reference.
Func(nullable~ : Bool, sign~ : FuncType)
/// A numeric conversion that states signedness, e.g. `i32_s`. `strict` marks
/// the trapping form, which rejects out-of-range values instead of saturating.
Signed(typ~ : NumType, signage~ : Signage, strict~ : Bool)
} derive(Eq, Debug)
///|
/// The four numeric types, where a construct admits only those.
pub(all) enum NumType {
I32
I64
F32
F64
} derive(Eq, Debug)
///|
pub fn NumType::to_str(self : NumType) -> String {
match self {
I32 => "i32"
I64 => "i64"
F32 => "f32"
F64 => "f64"
}
}
///|
/// Render a signed numeric cast type, e.g. `i32_s` or `i64_u_strict`.
pub fn format_signed_type(
typ : NumType,
signage : Signage,
strict : Bool,
) -> String {
let s = match signage {
Signed => "s"
Unsigned => "u"
}
let suffix = if strict { "_strict" } else { "" }
"\{typ.to_str()}_\{s}\{suffix}"
}
///|
/// A `try_table` catch clause.
///
/// The `Ref` variants deliver the exception reference itself alongside the
/// payload, so a handler can rethrow.
pub(all) enum Catch {
Catch(Ident, Label)
CatchRef(Ident, Label)
CatchAll(Label)
CatchAllRef(Label)
} derive(Eq, Debug)
///|
/// A stack-switching handler clause, `e on [tag -> 'label]`.
pub(all) enum OnClause {
OnLabel(Ident, Label)
OnSwitch(Ident)
} derive(Eq, Debug)
///|
/// A `match` arm pattern: an optionally-bound reference-type test, or a null
/// test.
pub(all) enum MatchPattern {
MatchCast(Ident?, RefType)
MatchNull
} derive(Eq, Debug)
///|
/// Advisory `metadata.code.*` metadata attached to an instruction.
///
/// Written in Wax as an attribute prefixing the instruction (`#[likely]`,
/// `#[freq = 16]`, `#[targets(f: 0.73)]`). These never affect behaviour: an
/// engine may ignore them, and dropping one changes performance, never
/// semantics.
///
/// A hint carries the span of the attribute it was written as, so a diagnostic
/// about a malformed hint blames the hint rather than the instruction it
/// decorates.
pub(all) struct Hint[T] {
value : T
loc : Location
} derive(Eq, Debug)
///|
pub(all) struct Hints {
/// `Some(true)` = branch likely taken.
branch : Hint[Bool]?
/// The raw wire byte of an `instr_freq` hint: an offset base-2 logarithm of
/// expected executions, so 32 means once. Kept raw so a value a hand-written
/// binary put outside the proposal's range still round-trips.
freq : Hint[Int]?
/// Likely targets of an indirect call, with each one's frequency as a
/// percentage.
targets : Hint[Array[(Ident, Int)]]?
} derive(Eq, Debug)
///|
/// An instruction with no hints, which is what everything carries unless an
/// attribute says otherwise.
pub let no_hints : Hints = { branch: None, freq: None, targets: None }
///|
pub fn Hints::is_empty(self : Hints) -> Bool {
self.branch is None && self.freq is None && self.targets is None
}