// What a checked module can be turned into.
///|
/// What can stop a checked module from becoming output.
///
/// Three packages sit on this path and each raises its own error: the lowering
/// to the wasm module model, the binary encoder, and the text printer. A caller
/// handed the open `Error` type can only print what it catches; naming the
/// three lets it match on them, and says up front that there is no fourth.
///
/// None of these are a rejected module. A module the checker rejected is
/// reported through `Session::reports` and `rejected` before anything is
/// lowered -- these are the lowering's own gaps, and reaching one on a module
/// that checked clean is a bug in this port rather than in the input. The one
/// exception is `LowerError::AmbiguousBinding`, which only a generated tree can
/// reach and which names what the generator has to fix.
pub suberror CompileError {
/// The Wax module would not lower: a construct not handled yet, a name the
/// checker accepted that did not survive, or one the binary format cannot
/// hold at all.
Lower(@to_wasm.LowerError)
/// The lowered module would not encode.
Encode(@wasm_bin.EncodeError)
/// The lowered module would not print as WebAssembly text.
Wat(@to_wat.WatError)
}
///|
/// Delegated to the wrapped error, which already renders with its own span:
/// the wrapper says which stage, and the stage's own message is the answer.
pub impl Show for CompileError with fn output(self, logger) {
match self {
Lower(e) => Show::output(e, logger)
Encode(e) => Show::output(e, logger)
Wat(e) => Show::output(e, logger)
}
}
///|
/// A module that has been through the type checker.
///
/// It holds the four things the lowering needs and that a caller would
/// otherwise have to keep together by hand: the session (for its type store),
/// the module context the checker built, the source AST, and the per-node
/// inferred types the checker left alongside it. The lowering reads the types
/// off `typed`, which is the entire interface between the two stages.
///
/// Holding a `Checked` is NOT a claim that the module is valid -- see
/// `Session::check`. Lowering one the checker rejected raises, or produces a
/// module derived from something known to be wrong.
pub struct Checked {
/// The session it was checked in; its `store` is what numbers the types.
session : Session
/// Names and signatures, as the checker resolved them.
context : @typing_env.ModuleContext
/// The module as written.
source_module : @ast.LocModule
/// The same module with an inferred type on every node.
typed : Array[
@basic.Annotated[
@ast.ModuleField[
(Array[@infer.Cell[@infer.InferredType]], @basic.Location),
],
@basic.Location,
],
]
}
///|
/// Lower to the wasm module model, shared by both output forms.
///
/// `keep_conditionals` retains the conditional sections rather than resolving
/// them, which only the text form wants: a module with an unresolved
/// conditional has no single binary form, and that is what a conditional is
/// for.
pub fn Checked::to_module(
self : Checked,
keep_conditionals? : Bool,
) -> @wasm_bin.Module raise @to_wasm.LowerError {
@to_wasm.lower_module(
self.context,
self.session.store,
self.source_module,
self.typed,
keep_conditionals?,
)
}
///|
/// Encode to a `.wasm` binary.
pub fn Checked::to_bytes(self : Checked) -> Bytes raise CompileError {
let m = self.to_module() catch { e => raise Lower(e) }
@wasm_bin.encode(m) catch {
e => raise Encode(e)
}
}
///|
/// Print in the WebAssembly text format.
///
/// The same lowered module the binary form comes from, printed rather than
/// encoded, so the two cannot drift. `trivia` carries the source comments; they
/// were written against Wax source and come out as WAT comments, with the same
/// text and different delimiters.
pub fn Checked::to_wat(
self : Checked,
trivia? : @trivia.Context,
) -> String raise CompileError {
let m = self.to_module(keep_conditionals=true) catch { e => raise Lower(e) }
@to_wat.print_module(m, trivia~) catch {
e => raise Wat(e)
}
}