pub enum ClosureBody {
Tree {
params: Vec<Param>,
block: Arc<Block>,
decl: Option<Arc<FnDecl>>,
},
Linear(LinearClosure),
}Expand description
Where a closure’s body is, which is the one thing about a closure the two backends do not agree on.
Everything else a closure is — what it captured, how many parameters it
declares, which module it resolves names in, whether it is async — is
the same fact whichever backend made it, and a host that receives one
reads those the same way either way. The body is not: the interpreter
walks a tree and the linear-memory backend runs a lowered function, and
neither can run the other’s.
The declaration is part of the body, not part of the closure. The
parameters an interpreted call binds against and the return type it
coerces to are syntax, and syntax is one backend’s form of a body: a
lowered function has neither, because cove_ir::lower spent both when it
chose the slots and emitted the conversions. Keeping them beside the
Arc<Block> they came from is what lets every field of a Closure
outside this enum be a fact both backends state the same way, so that
reaching syntax means reaching past the one variant that has any — which
is the direction issue #109 asks for.
So this is an enum rather than a second Value variant. Issue #109 asks
that the internal representation become less exposed to an embedder,
not more, and a Value::LoweredClosure beside Value::Closure would make
every host that already handles a callback handle two — while the
difference between them is one field that no host reads. A host calls a
closure back through crate::host::Reentry, which hands it to the
backend that made it, and that backend is the only party that has to know
which of these it is.
Variants§
Tree
The syntax crate::interp::Interpreter walks, and the declaration
it walks it under.
Fields
params: Vec<Param>The parameters as source wrote them.
Interpreter::bind_params reads every field of one: the name to
match a labelled argument against, variadic to know which
arguments to gather, default to evaluate in the callee’s
environment when an argument was left out, and is_var to bind
the caller’s place rather than a copy — which is also what
Interpreter::call_shared_method reads off the first parameter
of a lock closure. The lowering answers that last question when
it chooses the slots, and has no use for the other three.
decl: Option<Arc<FnDecl>>The declaration this closure came from, and None for a lambda,
which has none of its own.
Read for the written return type: a dyn Trait in it is what
tells the interpreter to wrap what the body produced. The lowered
form needs no equivalent, because cove_ir::lower boxes what a
function declared as erased returns, so the answer that leaves a
lowered body is already wrapped.
Linear(LinearClosure)
The lowered function cove_runtime::vm runs, addressed in the
cove_ir::Program that run was given, together with the
environment object it closes over.
A closure built by one run cannot be called by another, which is true
of the tree form as well: both name something a particular run owns.
Which of the two a Closure holds is the question which backend made
this, and crate::host::Reentry is the only thing that asks it.
The environment is here and the captures are not. A cove-ir
closure’s captures are inline in a heap object, at the widths its
layout says, and copying them out into Closure::captures would be
materialising a value nothing asked for and losing the identity of the
storage they came from. So the object crosses instead, as a
LinearClosure, and Closure::captures is empty.
Trait Implementations§
Source§impl Clone for ClosureBody
impl Clone for ClosureBody
Source§fn clone(&self) -> ClosureBody
fn clone(&self) -> ClosureBody
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more