alexcrichton opened PR #10770 from alexcrichton:has-data to bytecodealliance:main:
This commit is a refactoring to the fundamentals of the
bindgen!macro
and the functions that it generates. Prior to this change the
fundamental entrypoint generated bybindgen!was a function
add_to_linker_get_hostwhich takes a value of typeG: GetHost. This
GetHostimplementation is effectively an alias for a closure whose
return value is able to close over the parameter given lfietime-wise.The
GetHostabstraction was added to Wasmtime originally to enable
using any type that implementsHosttraits, not just&mut Uas was
originally supported. The definition ofGetHostwas _just_ right to
enable a type such asMyThing<&mut T>to implementHostand a
closure could be provided that could return it. At the time that
GetHostwas added it was known to be problematic from an
understandability point of view, namely:
- It has a non-obvious definition.
It's pretty advanced Rust voodoo to understand what it's actually
doingUsing
GetHostrequired lots offor<'a> ...in places which is
unfamiliar syntax for many.
GetHostvalues couldn't be type-erased (e.g. put in a trait object)
as we couldn't figure out the lifetime syntax to do so.Despite these issues it was the only known solution at hand so we landed
it and kept the previousadd_to_linkerstyle (&mut T -> &mut U) as a
convenience. While this has worked reasonable well (most folks just try
to not look atGetHost) it has reached a breaking point in the WASIp3
work.In the WASIp3 work it's effectively now going to be required that the
G: GetHostvalue is packaged up and actually stored inside of
accessors provided to host functions. This means thatGetHostvalues
now need to not only be taken inadd_to_linkerbut additionally
provided to the rest of the system through an "accessor". This was made
possible in https://github.com/bytecodealliance/wasmtime/pull/10746 by moving theGetHosttype into Wasmtime itself (as
opposed to generated code where it lived prior).While this worked with WASIp3 and it was possible to plumb
G: GetHost
safely around, this ended up surfacing more issues. Namely all
"concurrent" host functions started getting significantly more
complicatedwhereclauses and type signatures. At the end of the day I
felt that we had reached the end of the road toGetHostand wanted to
search for alternatives, hence this change.The fundamental purpose of
GetHostwas to be able to express, in a
generic fashion:
- Give me a closure that takes
&mut Tand returnsD.- The
Dtype can close over the lifetime in&mut T.- The
Dtype must implementbindgen!-generated traits.A realization I had was that we could model this with a generic
associated type in Rust. Rust support for generic associated types is
relatively new and not something I've used much before, but it ended up
being a perfect model for this. The definition of the newHasData
trait is deceptively simple:trait HasData { type Data<'a>; }What this enables us to do though is to generate
add_to_linker
functions that look like this:fn add_to_linker<T, D>( linker: &mut Linker<T>, getter: fn(&mut T) -> D::Data<'_>, ) -> Result<()> where D: HasData, for<'a> D::Data<'a>: Host;This definition here models
G: GetHostas a literal function pointer,
and the ability to close over the&mut Tlifetime with type (not just
&mut U) is expressed through the type constructortype Data<'a>).
Ideally we could take a generic generic associated type (I'm not even
sure what to call that), but that's not something Rust has today.Overall this felt like a much simpler way of modeling
GetHostand its
requirements. This plumbed well throughout the WASIp3 work and the
signatures for concurrent functions felt much more appropriate in terms
of complexity after this change. Taking this change to the limit means
thatGetHostin its entirety could be purged since all usages of it
could be replaced withfn(&mut T) -> D::Data<'a>, a hopefully much
more understandable type.This change is not all rainbows however, there are some gotchas that
remain:
One is that all
add_to_linkergenerated functions have aD: HasDatatype parameter. This type parameter cannot be inferred and
must always be explicitly specified, and it's not easy to know what to
supply here without reading documentation. Actually supplying the type
parameter is quite easy once you know what to do (and what to fill
in), but it may involve defining a small struct with a custom
HasDataimplementation which can be non-obvious.Another is that the
G: GetHostvalue was previously a full Rust
closure, but now it's transitioning to a function pointer. This is
done in preparation for WASIp3 work where the function needs to be
passed around, and doing that behind a generic parameter is more
effort than it's worth. This means that embedders relying on the true
closure-like nature here will have to update to using a function
pointer instead.The function pointer is stored in locations that require
'static,
and whilefn(T)might be expected to be'staticregardless ofT
is is, in fact, not. This means that practicallyadd_to_linker
requiresT: 'static. Relative to just before this change this is a
possible regression in functionality, but there orthogonal reasons
beyond just this that we want to start requiringT: 'staticanyway.
That means that this isn't actually a regression relative to https://github.com/bytecodealliance/wasmtime/pull/10760, a
related change.The first point is partially ameliorated with WASIp3 work insofar that
theDtype parameter will start serving as a location to specify where
concurrent implementations are found. These concurrent methods don't
take&mut selfbut instead are implemented forT: HasDatatypes. In
that sense it's more justified to have this weird type parameter, but in
the meantime without this support it'll feel a bit odd to have this
little type parameter hanging off the side.This change has been integrated into the WASIp3 prototyping repository
with success. This has additionally been integrated into the Spin
embedding which has one of the more complicated reliances on
*_get_hostfunctions known. Given that it's expected that while this
is not necessarily a trivial change to rebase over it should at least be
possible.Finally the
HasDatatrait here has been included with what I'm hoping
is a sufficient amount of documentation to at least give folks a spring
board to understand it. If folks have confusion about thisDtype
parameter my hope is they'll make their way toHasDatawhich showcases
various patterns for "librarifying" host implementations of WIT
interfaces. These patterns are all used throughout Wasmtime and WASI
currently in crates and tests and such.
alexcrichton requested pchickey for a review on PR #10770.
alexcrichton requested wasmtime-core-reviewers for a review on PR #10770.
alexcrichton requested wasmtime-default-reviewers for a review on PR #10770.
alexcrichton commented on PR #10770:
cc @lann on this as well as it relates to Spin factors and such
alexcrichton updated PR #10770.
pchickey submitted PR review:
Thanks. I greatly prefer this to GetHost. Every time I had to use GetHost I had to look at other use sites and think hard about how to get it right, whereas this is immediately obvious. Also, changing from a closure to a function pointer should hopefully make it more clear that the purpose is projection, rather than causing a side effect, as it has been misused at least once in the past.
alexcrichton updated PR #10770.
alexcrichton has enabled auto merge for PR #10770.
alexcrichton merged PR #10770.
Last updated: Dec 06 2025 at 07:03 UTC