Repository: bytecodealliance/wasmtime
Pull request #8620 (2024-05-14 20:10:35)

[21.0.0] Add `GetHost` to generated bindings for more flexible linking 
Backport of #8448 to the upcoming release branch


Patch:
diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs
index 2cb360a8e..074c1e353 100644
--- a/crates/component-macro/src/bindgen.rs
+++ b/crates/component-macro/src/bindgen.rs
@@ -144,6 +144,7 @@ impl Parse for Config {
                             .map(|p| p.into_token_stream().to_string())
                             .collect()
                     }
+                    Opt::SkipMutForwardingImpls(val) => opts.skip_mut_forwarding_impls = val,
                 }
             }
         } else {
@@ -213,6 +214,7 @@ mod kw {
     syn::custom_keyword!(only_imports);
     syn::custom_keyword!(trappable_imports);
     syn::custom_keyword!(additional_derives);
+    syn::custom_keyword!(skip_mut_forwarding_impls);
 }
 
 enum Opt {
@@ -227,6 +229,7 @@ enum Opt {
     With(HashMap<String, String>),
     TrappableImports(TrappableImports),
     AdditionalDerives(Vec<syn::Path>),
+    SkipMutForwardingImpls(bool),
 }
 
 impl Parse for Opt {
@@ -367,6 +370,12 @@ impl Parse for Opt {
             syn::bracketed!(contents in input);
             let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
             Ok(Opt::AdditionalDerives(list.iter().cloned().collect()))
+        } else if l.peek(kw::skip_mut_forwarding_impls) {
+            input.parse::<kw::skip_mut_forwarding_impls>()?;
+            input.parse::<Token![:]>()?;
+            Ok(Opt::SkipMutForwardingImpls(
+                input.parse::<syn::LitBool>()?.value,
+            ))
         } else {
             Err(l.error())
         }
diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs
index cbccaa883..2d29d286f 100644
--- a/crates/wasi-http/src/lib.rs
+++ b/crates/wasi-http/src/lib.rs
@@ -27,7 +27,7 @@
 //! 2. Add WASI HTTP interfaces to a [`wasmtime::component::Linker<T>`]. This is either
 //!    done through functions like [`proxy::add_to_linker`] (which bundles all interfaces
 //!    in the `wasi:http/proxy` world together) or through individual interfaces like the
-//!    [`bindings::http::outgoing_handler::add_to_linker`] function.
+//!    [`bindings::http::outgoing_handler::add_to_linker_get_host`] function.
 //! 3. Use the previous [`wasmtime::component::Linker<T>::instantiate`] to instantiate
 //!    a [`wasmtime::component::Component`] within a [`wasmtime::Store<T>`]. If you're
 //!    targeting the `wasi:http/proxy` world, you can instantiate the component with
@@ -78,6 +78,7 @@ pub mod bindings {
         trappable_error_type: {
             "wasi:http/types/error-code" => crate::HttpError,
         },
+        skip_mut_forwarding_impls: true,
     });
 
     pub use wasi::http;
diff --git a/crates/wasi-http/src/proxy.rs b/crates/wasi-http/src/proxy.rs
index e904f3e4d..e3889d544 100644
--- a/crates/wasi-http/src/proxy.rs
+++ b/crates/wasi-http/src/proxy.rs
@@ -83,26 +83,37 @@ pub fn add_to_linker<T>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Resul
 where
     T: WasiHttpView + wasmtime_wasi::WasiView,
 {
-    wasmtime_wasi::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?;
-    wasmtime_wasi::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?;
-    wasmtime_wasi::bindings::io::poll::add_to_linker(l, |t| t)?;
-    wasmtime_wasi::bindings::io::error::add_to_linker(l, |t| t)?;
-    wasmtime_wasi::bindings::io::streams::add_to_linker(l, |t| t)?;
-    wasmtime_wasi::bindings::cli::stdin::add_to_linker(l, |t| t)?;
-    wasmtime_wasi::bindings::cli::stdout::add_to_linker(l, |t| t)?;
-    wasmtime_wasi::bindings::cli::stderr::add_to_linker(l, |t| t)?;
-    wasmtime_wasi::bindings::random::random::add_to_linker(l, |t| t)?;
+    let closure = type_annotate::<T, _>(|t| t);
+    wasmtime_wasi::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
+    wasmtime_wasi::bindings::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
+    wasmtime_wasi::bindings::io::poll::add_to_linker_get_host(l, closure)?;
+    wasmtime_wasi::bindings::io::error::add_to_linker_get_host(l, closure)?;
+    wasmtime_wasi::bindings::io::streams::add_to_linker_get_host(l, closure)?;
+    wasmtime_wasi::bindings::cli::stdin::add_to_linker_get_host(l, closure)?;
+    wasmtime_wasi::bindings::cli::stdout::add_to_linker_get_host(l, closure)?;
+    wasmtime_wasi::bindings::cli::stderr::add_to_linker_get_host(l, closure)?;
+    wasmtime_wasi::bindings::random::random::add_to_linker_get_host(l, closure)?;
 
     add_only_http_to_linker(l)
 }
 
+// NB: workaround some rustc inference - a future refactoring may make this
+// obsolete.
+fn type_annotate<T, F>(val: F) -> F
+where
+    F: Fn(&mut T) -> &mut T,
+{
+    val
+}
+
 #[doc(hidden)]
 pub fn add_only_http_to_linker<T>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()>
 where
     T: WasiHttpView + wasmtime_wasi::WasiView + crate::bindings::http::types::Host,
 {
-    crate::bindings::http::outgoing_handler::add_to_linker(l, |t| t)?;
-    crate::bindings::http::types::add_to_linker(l, |t| t)?;
+    let closure = type_annotate::<T, _>(|t| t);
+    crate::bindings::http::outgoing_handler::add_to_linker_get_host(l, closure)?;
+    crate::bindings::http::types::add_to_linker_get_host(l, closure)?;
 
     Ok(())
 }
@@ -185,15 +196,17 @@ pub mod sync {
     where
         T: WasiHttpView + wasmtime_wasi::WasiView,
     {
-        wasmtime_wasi::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?;
-        wasmtime_wasi::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?;
-        wasmtime_wasi::bindings::sync::io::poll::add_to_linker(l, |t| t)?;
-        wasmtime_wasi::bindings::sync::io::streams::add_to_linker(l, |t| t)?;
-        wasmtime_wasi::bindings::io::error::add_to_linker(l, |t| t)?;
-        wasmtime_wasi::bindings::cli::stdin::add_to_linker(l, |t| t)?;
-        wasmtime_wasi::bindings::cli::stdout::add_to_linker(l, |t| t)?;
-        wasmtime_wasi::bindings::cli::stderr::add_to_linker(l, |t| t)?;
-        wasmtime_wasi::bindings::random::random::add_to_linker(l, |t| t)?;
+        let closure = super::type_annotate::<T, _>(|t| t);
+
+        wasmtime_wasi::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
+        wasmtime_wasi::bindings::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
+        wasmtime_wasi::bindings::sync::io::poll::add_to_linker_get_host(l, closure)?;
+        wasmtime_wasi::bindings::sync::io::streams::add_to_linker_get_host(l, closure)?;
+        wasmtime_wasi::bindings::io::error::add_to_linker_get_host(l, closure)?;
+        wasmtime_wasi::bindings::cli::stdin::add_to_linker_get_host(l, closure)?;
+        wasmtime_wasi::bindings::cli::stdout::add_to_linker_get_host(l, closure)?;
+        wasmtime_wasi::bindings::cli::stderr::add_to_linker_get_host(l, closure)?;
+        wasmtime_wasi::bindings::random::random::add_to_linker_get_host(l, closure)?;
 
         add_only_http_to_linker(l)?;
 
@@ -206,8 +219,10 @@ pub mod sync {
     where
         T: WasiHttpView + wasmtime_wasi::WasiView + crate::bindings::http::types::Host,
     {
-        crate::bindings::http::outgoing_handler::add_to_linker(l, |t| t)?;
-        crate::bindings::http::types::add_to_linker(l, |t| t)?;
+        let closure = super::type_annotate::<T, _>(|t| t);
+
+        crate::bindings::http::outgoing_handler::add_to_linker_get_host(l, closure)?;
+        crate::bindings::http::types::add_to_linker_get_host(l, closure)?;
 
         Ok(())
     }
diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs
index 28ab76863..acc401d30 100644
--- a/crates/wasi-http/src/types.rs
+++ b/crates/wasi-http/src/types.rs
@@ -30,7 +30,7 @@ impl WasiHttpCtx {
 }
 
 /// A trait which provides internal WASI HTTP state.
-pub trait WasiHttpView: Send {
+pub trait WasiHttpView {
     /// Returns a mutable reference to the WASI HTTP context.
     fn ctx(&mut self) -> &mut WasiHttpCtx;
 
@@ -71,10 +71,7 @@ pub trait WasiHttpView: Send {
         &mut self,
         request: hyper::Request<HyperOutgoingBody>,
         config: OutgoingRequestConfig,
-    ) -> crate::HttpResult<HostFutureIncomingResponse>
-    where
-        Self: Sized,
-    {
+    ) -> crate::HttpResult<HostFutureIncomingResponse> {
         Ok(default_send_request(request, config))
     }
 
@@ -84,6 +81,37 @@ pub trait WasiHttpView: Send {
     }
 }
 
+impl<T: ?Sized + WasiHttpView> WasiHttpView for &mut T {
+    fn ctx(&mut self) -> &mut WasiHttpCtx {
+        T::ctx(self)
+    }
+
+    fn table(&mut self) -> &mut ResourceTable {
+        T::table(self)
+    }
+
+    fn new_response_outparam(
+        &mut self,
+        result: tokio::sync::oneshot::Sender<
+            Result<hyper::Response<HyperOutgoingBody>, types::ErrorCode>,
+        >,
+    ) -> wasmtime::Result<Resource<HostResponseOutparam>> {
+        T::new_response_outparam(self, result)
+    }
+
+    fn send_request(
+        &mut self,
+        request: hyper::Request<HyperOutgoingBody>,
+        config: OutgoingRequestConfig,
+    ) -> crate::HttpResult<HostFutureIncomingResponse> {
+        T::send_request(self, request, config)
+    }
+
+    fn is_forbidden_header(&mut self, name: &HeaderName) -> bool {
+        T::is_forbidden_header(self, name)
+    }
+}
+
 /// Returns `true` when the header is forbidden according to this [`WasiHttpView`] implementation.
 pub(crate) fn is_forbidden_header(view: &mut dyn WasiHttpView, name: &HeaderName) -> bool {
     static FORBIDDEN_HEADERS: [HeaderName; 10] = [
diff --git a/crates/wasi-nn/Cargo.toml b/crates/wasi-nn/Cargo.toml
index d12f3af2a..1b8dc95d9 100644
--- a/crates/wasi-nn/Cargo.toml
+++ b/crates/wasi-nn/Cargo.toml
@@ -16,7 +16,7 @@ workspace = true
 
 [dependencies]
 # These dependencies are necessary for the WITX-generation macros to work:
-anyhow = { workspace = true }
+anyhow = { workspace = true, features = ['std'] }
 wiggle = { workspace = true, features = ["wasmtime"] }
 
 # This dependency is necessary for the WIT-generation macros to work:
diff --git a/crates/wasi/src/bindings.rs b/crates/wasi/src/bindings.rs
index 737301403..8bc4add33 100644
--- a/crates/wasi/src/bindings.rs
+++ b/crates/wasi/src/bindings.rs
@@ -33,7 +33,8 @@ pub mod sync {
                 "wasi:io/poll/pollable": super::super::io::poll::Pollable,
                 "wasi:io/streams/input-stream": super::super::io::streams::InputStream,
                 "wasi:io/streams/output-stream": super::super::io::streams::OutputStream,
-            }
+            },
+            skip_mut_forwarding_impls: true,
         });
     }
     pub use self::generated::exports;
@@ -49,10 +50,6 @@ pub mod sync {
     /// component through the [`Command::wasi_cli_run`] method plus
     /// [`Guest::call_run`](exports::wasi::cli::run::Guest::call_run).
     ///
-    /// > **Note**: it's recommended to use
-    /// > [`wasmtime_wasi::add_to_linker_sync`] instead of the auto-generated
-    /// > [`Command::add_to_linker`] here.
-    ///
     /// [async]: wasmtime::Config::async_support
     /// [`wasmtime_wasi::add_to_linker_sync`]: crate::add_to_linker_sync
     ///
@@ -202,6 +199,7 @@ mod async_io {
             "wasi:cli/terminal-input/terminal-input": crate::stdio::TerminalInput,
             "wasi:cli/terminal-output/terminal-output": crate::stdio::TerminalOutput,
         },
+        skip_mut_forwarding_impls: true,
     });
 }
 
@@ -218,9 +216,6 @@ pub use self::async_io::wasi::*;
 /// through the [`Command::wasi_cli_run`] method plus
 /// [`Guest::call_run`](exports::wasi::cli::run::Guest::call_run).
 ///
-/// > **Note**: it's recommended to use [`wasmtime_wasi::add_to_linker_async`]
-/// > instead of the auto-generated [`Command::add_to_linker`] here.
-///
 /// [async]: wasmtime::Config::async_support
 /// [`wasmtime_wasi::add_to_linker_async`]: crate::add_to_linker_async
 ///
diff --git a/crates/wasi/src/ctx.rs b/crates/wasi/src/ctx.rs
index 8959bc8bb..62c323503 100644
--- a/crates/wasi/src/ctx.rs
+++ b/crates/wasi/src/ctx.rs
@@ -581,6 +581,15 @@ pub trait WasiView: Send {
     fn ctx(&mut self) -> &mut WasiCtx;
 }
 
+impl<T: ?Sized + WasiView> WasiView for &mut T {
+    fn table(&mut self) -> &mut ResourceTable {
+        T::table(self)
+    }
+    fn ctx(&mut self) -> &mut WasiCtx {
+        T::ctx(self)
+    }
+}
+
 /// Per-[`Store`] state which holds state necessary to implement WASI from this
 /// crate.
 ///
diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs
index 0bc65d724..575f0e0d8 100644
--- a/crates/wasi/src/lib.rs
+++ b/crates/wasi/src/lib.rs
@@ -285,33 +285,44 @@ pub use wasmtime::component::{ResourceTable, ResourceTableError};
 /// ```
 pub fn add_to_linker_async<T: WasiView>(linker: &mut Linker<T>) -> anyhow::Result<()> {
     let l = linker;
-    crate::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?;
-    crate::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?;
-    crate::bindings::filesystem::types::add_to_linker(l, |t| t)?;
-    crate::bindings::filesystem::preopens::add_to_linker(l, |t| t)?;
-    crate::bindings::io::error::add_to_linker(l, |t| t)?;
-    crate::bindings::io::poll::add_to_linker(l, |t| t)?;
-    crate::bindings::io::streams::add_to_linker(l, |t| t)?;
-    crate::bindings::random::random::add_to_linker(l, |t| t)?;
-    crate::bindings::random::insecure::add_to_linker(l, |t| t)?;
-    crate::bindings::random::insecure_seed::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::exit::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::environment::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::stdin::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::stdout::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::stderr::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::terminal_input::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::terminal_output::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::terminal_stdin::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::terminal_stdout::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::tcp::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::udp::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::instance_network::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::network::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?;
+
+    // NB: workaround some rustc inference - a future refactoring may make this
+    // obsolete.
+    fn type_annotate<T, F>(val: F) -> F
+    where
+        F: Fn(&mut T) -> &mut T,
+    {
+        val
+    }
+    let closure = type_annotate::<T, _>(|t| t);
+
+    crate::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
+    crate::bindings::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
+    crate::bindings::filesystem::types::add_to_linker_get_host(l, closure)?;
+    crate::bindings::filesystem::preopens::add_to_linker_get_host(l, closure)?;
+    crate::bindings::io::error::add_to_linker_get_host(l, closure)?;
+    crate::bindings::io::poll::add_to_linker_get_host(l, closure)?;
+    crate::bindings::io::streams::add_to_linker_get_host(l, closure)?;
+    crate::bindings::random::random::add_to_linker_get_host(l, closure)?;
+    crate::bindings::random::insecure::add_to_linker_get_host(l, closure)?;
+    crate::bindings::random::insecure_seed::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::exit::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::environment::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::stdin::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::stdout::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::stderr::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::terminal_input::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::terminal_output::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::terminal_stdin::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::terminal_stdout::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::terminal_stderr::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::tcp::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::tcp_create_socket::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::udp::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::udp_create_socket::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::instance_network::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::network::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::ip_name_lookup::add_to_linker_get_host(l, closure)?;
     Ok(())
 }
 
@@ -373,32 +384,43 @@ pub fn add_to_linker_sync<T: WasiView>(
     linker: &mut wasmtime::component::Linker<T>,
 ) -> anyhow::Result<()> {
     let l = linker;
-    crate::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?;
-    crate::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?;
-    crate::bindings::sync::filesystem::types::add_to_linker(l, |t| t)?;
-    crate::bindings::filesystem::preopens::add_to_linker(l, |t| t)?;
-    crate::bindings::io::error::add_to_linker(l, |t| t)?;
-    crate::bindings::sync::io::poll::add_to_linker(l, |t| t)?;
-    crate::bindings::sync::io::streams::add_to_linker(l, |t| t)?;
-    crate::bindings::random::random::add_to_linker(l, |t| t)?;
-    crate::bindings::random::insecure::add_to_linker(l, |t| t)?;
-    crate::bindings::random::insecure_seed::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::exit::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::environment::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::stdin::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::stdout::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::stderr::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::terminal_input::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::terminal_output::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::terminal_stdin::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::terminal_stdout::add_to_linker(l, |t| t)?;
-    crate::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::tcp::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::udp::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::instance_network::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::network::add_to_linker(l, |t| t)?;
-    crate::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?;
+
+    // NB: workaround some rustc inference - a future refactoring may make this
+    // obsolete.
+    fn type_annotate<T, F>(val: F) -> F
+    where
+        F: Fn(&mut T) -> &mut T,
+    {
+        val
+    }
+    let closure = type_annotate::<T, _>(|t| t);
+
+    crate::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
+    crate::bindings::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sync::filesystem::types::add_to_linker_get_host(l, closure)?;
+    crate::bindings::filesystem::preopens::add_to_linker_get_host(l, closure)?;
+    crate::bindings::io::error::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sync::io::poll::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sync::io::streams::add_to_linker_get_host(l, closure)?;
+    crate::bindings::random::random::add_to_linker_get_host(l, closure)?;
+    crate::bindings::random::insecure::add_to_linker_get_host(l, closure)?;
+    crate::bindings::random::insecure_seed::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::exit::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::environment::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::stdin::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::stdout::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::stderr::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::terminal_input::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::terminal_output::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::terminal_stdin::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::terminal_stdout::add_to_linker_get_host(l, closure)?;
+    crate::bindings::cli::terminal_stderr::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::tcp::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::tcp_create_socket::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::udp::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::udp_create_socket::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::instance_network::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::network::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::ip_name_lookup::add_to_linker_get_host(l, closure)?;
     Ok(())
 }
diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs
index 8278f7395..617513130 100644
--- a/crates/wit-bindgen/src/lib.rs
+++ b/crates/wit-bindgen/src/lib.rs
@@ -56,8 +56,9 @@ struct Wasmtime {
 }
 
 struct ImportFunction {
+    func: Function,
     add_to_linker: String,
-    sig: String,
+    sig: Option<String>,
 }
 
 #[derive(Default)]
@@ -119,6 +120,11 @@ pub struct Opts {
     ///
     /// These derive attributes will be added to any generated structs or enums
     pub additional_derive_attributes: Vec<String>,
+
+    /// Temporary option to skip `impl<T: Trait> Trait for &mut T` for the
+    /// `wasmtime-wasi` crate while that's given a chance to update its b
+    /// indings.
+    pub skip_mut_forwarding_impls: bool,
 }
 
 #[derive(Debug, Clone)]
@@ -325,14 +331,19 @@ impl Wasmtime {
                 // Only generate a trait signature for free functions since
                 // resource-related functions get their trait signatures
                 // during `type_resource`.
-                if let FunctionKind::Freestanding = func.kind {
+                let sig = if let FunctionKind::Freestanding = func.kind {
                     gen.generate_function_trait_sig(func);
-                }
-                let sig = mem::take(&mut gen.src).into();
+                    Some(mem::take(&mut gen.src).into())
+                } else {
+                    None
+                };
                 gen.generate_add_function_to_linker(TypeOwner::World(world), func, "linker");
                 let add_to_linker = gen.src.into();
-                self.import_functions
-                    .push(ImportFunction { sig, add_to_linker });
+                self.import_functions.push(ImportFunction {
+                    func: func.clone(),
+                    sig,
+                    add_to_linker,
+                });
             }
             WorldItem::Interface(id) => {
                 gen.gen.interface_last_seen_as_import.insert(*id, true);
@@ -542,7 +553,7 @@ impl Wasmtime {
         assert!(prev.is_none());
     }
 
-    fn build_struct(&mut self, resolve: &Resolve, world: WorldId) {
+    fn build_world_struct(&mut self, resolve: &Resolve, world: WorldId) {
         let camel = to_rust_upper_camel_case(&resolve.worlds[world].name);
         uwriteln!(self.src, "pub struct {camel} {{");
         for (name, (ty, _)) in self.exports.fields.iter() {
@@ -550,13 +561,7 @@ impl Wasmtime {
         }
         self.src.push_str("}\n");
 
-        let (async_, async__, send, await_) = if self.opts.async_.maybe_async() {
-            ("async", "_async", ":Send", ".await")
-        } else {
-            ("", "", "", "")
-        };
-
-        self.toplevel_import_trait(resolve, world);
+        self.world_imports_trait(resolve, world);
 
         uwriteln!(self.src, "const _: () = {{");
         uwriteln!(
@@ -568,7 +573,13 @@ impl Wasmtime {
         );
 
         uwriteln!(self.src, "impl {camel} {{");
-        self.toplevel_add_to_linker(resolve, world);
+        self.world_add_to_linker(resolve, world);
+
+        let (async_, async__, send, await_) = if self.opts.async_.maybe_async() {
+            ("async", "_async", ":Send", ".await")
+        } else {
+            ("", "", "", "")
+        };
         uwriteln!(
             self.src,
             "
@@ -646,7 +657,7 @@ impl Wasmtime {
         }
 
         if !self.opts.only_interfaces {
-            self.build_struct(resolve, world)
+            self.build_world_struct(resolve, world)
         }
 
         let imports = mem::take(&mut self.import_interfaces);
@@ -866,12 +877,12 @@ impl Wasmtime {
 }
 
 impl Wasmtime {
-    fn has_world_trait(&self, resolve: &Resolve, world: WorldId) -> bool {
+    fn has_world_imports_trait(&self, resolve: &Resolve, world: WorldId) -> bool {
         !self.import_functions.is_empty() || get_world_resources(resolve, world).count() > 0
     }
 
-    fn toplevel_import_trait(&mut self, resolve: &Resolve, world: WorldId) {
-        if !self.has_world_trait(resolve, world) {
+    fn world_imports_trait(&mut self, resolve: &Resolve, world: WorldId) {
+        if !self.has_world_imports_trait(resolve, world) {
             return;
         }
 
@@ -880,114 +891,184 @@ impl Wasmtime {
             uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]")
         }
         uwrite!(self.src, "pub trait {world_camel}Imports");
-        for (i, resource) in get_world_resources(resolve, world).enumerate() {
-            if i == 0 {
-                uwrite!(self.src, ": ");
-            } else {
-                uwrite!(self.src, " + ");
-            }
-            uwrite!(self.src, "Host{}", resource.to_upper_camel_case());
+        let mut supertraits = vec![];
+        if self.opts.async_.maybe_async() {
+            supertraits.push("Send".to_string());
+        }
+        for resource in get_world_resources(resolve, world) {
+            supertraits.push(format!("Host{}", resource.to_upper_camel_case()));
+        }
+        if !supertraits.is_empty() {
+            uwrite!(self.src, ": {}", supertraits.join(" + "));
         }
         uwriteln!(self.src, " {{");
         for f in self.import_functions.iter() {
-            self.src.push_str(&f.sig);
-            self.src.push_str("\n");
+            if let Some(sig) = &f.sig {
+                self.src.push_str(sig);
+                self.src.push_str(";\n");
+            }
         }
         uwriteln!(self.src, "}}");
-    }
 
-    fn toplevel_add_to_linker(&mut self, resolve: &Resolve, world: WorldId) {
-        let has_world_trait = self.has_world_trait(resolve, world);
-        if self.import_interfaces.is_empty() && !has_world_trait {
-            return;
-        }
-        let mut interfaces = Vec::new();
-        for (_, name) in self.import_interfaces.iter() {
-            let path = match name {
-                InterfaceName::Remapped { .. } => unreachable!("imported a remapped module"),
-                InterfaceName::Path(path) => path,
-            };
-            interfaces.push(path.join("::"));
-        }
-
-        uwrite!(
+        uwriteln!(
             self.src,
             "
-                pub fn add_to_linker<T, U>(
-                    linker: &mut wasmtime::component::Linker<T>,
-                    get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
-                ) -> wasmtime::Result<()>
-                    where U: \
+                pub trait {world_camel}ImportsGetHost<T>:
+                    Fn(T) -> <Self as {world_camel}ImportsGetHost<T>>::Host
+                        + Send
+                        + Sync
+                        + Copy
+                        + 'static
+                {{
+                    type Host: {world_camel}Imports;
+                }}
+
+                impl<F, T, O> {world_camel}ImportsGetHost<T> for F
+                where
+                    F: Fn(T) -> O + Send + Sync + Copy + 'static,
+                    O: {world_camel}Imports
+                {{
+                    type Host = O;
+                }}
             "
         );
-        let world_camel = to_rust_upper_camel_case(&resolve.worlds[world].name);
-        let world_trait = format!("{world_camel}Imports");
-        for (i, name) in interfaces
-            .iter()
-            .map(|n| format!("{n}::Host"))
-            .chain(if has_world_trait {
-                Some(world_trait.clone())
-            } else {
-                None
-            })
-            .enumerate()
-        {
-            if i > 0 {
-                self.src.push_str(" + ");
-            }
-            self.src.push_str(&name);
-        }
 
-        let maybe_send = if self.opts.async_.maybe_async() {
-            " + Send, T: Send"
+        // Generate impl WorldImports for &mut WorldImports
+        let (async_trait, maybe_send) = if self.opts.async_.maybe_async() {
+            (
+                "#[wasmtime::component::__internal::async_trait]\n",
+                "+ Send",
+            )
         } else {
-            ""
+            ("", "")
         };
+        if !self.opts.skip_mut_forwarding_impls {
+            uwriteln!(
+                self.src,
+                "{async_trait}impl<_T: {world_camel}Imports + ?Sized {maybe_send}> {world_camel}Imports for &mut _T {{"
+            );
+            // Forward each method call to &mut T
+            for f in self.import_functions.iter() {
+                if let Some(sig) = &f.sig {
+                    self.src.push_str(sig);
+                    uwrite!(
+                        self.src,
+                        "{{ {world_camel}Imports::{}(*self,",
+                        rust_function_name(&f.func)
+                    );
+                    for (name, _) in f.func.params.iter() {
+                        uwrite!(self.src, "{},", to_rust_ident(name));
+                    }
+                    uwrite!(self.src, ")");
+                    if self.opts.async_.is_import_async(&f.func.name) {
+                        uwrite!(self.src, ".await");
+                    }
+                    uwriteln!(self.src, "}}");
+                }
+            }
+            uwriteln!(self.src, "}}");
+        }
+    }
+
+    fn import_interface_paths(&self) -> Vec<String> {
+        self.import_interfaces
+            .iter()
+            .map(|(_, name)| match name {
+                InterfaceName::Path(path) => path.join("::"),
+                InterfaceName::Remapped { .. } => unreachable!("imported a remapped module"),
+            })
+            .collect()
+    }
 
-        self.src.push_str(maybe_send);
-        self.src.push_str(",\n{\n");
-        for name in interfaces.iter() {
-            uwriteln!(self.src, "{name}::add_to_linker(linker, get)?;");
+    fn world_host_traits(&self, resolve: &Resolve, world: WorldId) -> Vec<String> {
+        let mut traits = self
+            .import_interface_paths()
+            .iter()
+            .map(|path| format!("{path}::Host"))
+            .collect::<Vec<_>>();
+        if self.has_world_imports_trait(resolve, world) {
+            let world_camel = to_rust_upper_camel_case(&resolve.worlds[world].name);
+            traits.push(format!("{world_camel}Imports"));
         }
-        if has_world_trait {
-            uwriteln!(self.src, "Self::add_root_to_linker(linker, get)?;");
+        if self.opts.async_.maybe_async() {
+            traits.push("Send".to_string());
         }
-        uwriteln!(self.src, "Ok(())\n}}");
-        if !has_world_trait {
+        traits
+    }
+
+    fn world_add_to_linker(&mut self, resolve: &Resolve, world: WorldId) {
+        let has_world_imports_trait = self.has_world_imports_trait(resolve, world);
+        if self.import_interfaces.is_empty() && !has_world_imports_trait {
             return;
         }
 
-        uwrite!(
-            self.src,
-            "
-                pub fn add_root_to_linker<T, U>(
-                    linker: &mut wasmtime::component::Linker<T>,
-                    get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
-                ) -> wasmtime::Result<()>
-                    where U: {world_trait}{maybe_send}
-                {{
-                    let mut linker = linker.root();
-            ",
-        );
-        for name in get_world_resources(resolve, world) {
-            let camel = name.to_upper_camel_case();
-            uwriteln!(
+        let camel = to_rust_upper_camel_case(&resolve.worlds[world].name);
+        let data_bounds = if self.opts.async_.maybe_async() {
+            "T: Send,"
+        } else {
+            ""
+        };
+        if has_world_imports_trait {
+            uwrite!(
                 self.src,
-                "linker.resource(
-                    \"{name}\",
-                    wasmtime::component::ResourceType::host::<{camel}>(),
-                    move |mut store, rep| -> wasmtime::Result<()> {{
-                        Host{camel}::drop(get(store.data_mut()), wasmtime::component::Resource::new_own(rep))
-                    }},
-                )?;"
-            )
+                "
+                    pub fn add_to_linker_imports_get_host<T>(
+                        linker: &mut wasmtime::component::Linker<T>,
+                        host_getter: impl for<'a> {camel}ImportsGetHost<&'a mut T>,
+                    ) -> wasmtime::Result<()>
+                        where {data_bounds}
+                    {{
+                        let mut linker = linker.root();
+                "
+            );
+            for name in get_world_resources(resolve, world) {
+                let camel = name.to_upper_camel_case();
+                uwriteln!(
+                    self.src,
+                    "
+                        linker.resource(
+                            \"{name}\",
+                            wasmtime::component::ResourceType::host::<{camel}>(),
+                            move |mut store, rep| -> wasmtime::Result<()> {{
+                                Host{camel}::drop(&mut host_getter(store.data_mut()), wasmtime::component::Resource::new_own(rep))
+                            }},
+                        )?;"
+                );
+            }
+            for f in self.import_functions.iter() {
+                self.src.push_str(&f.add_to_linker);
+                self.src.push_str("\n");
+            }
+            uwriteln!(self.src, "Ok(())\n}}");
         }
 
-        for f in self.import_functions.iter() {
-            self.src.push_str(&f.add_to_linker);
-            self.src.push_str("\n");
+        let host_bounds = format!("U: {}", self.world_host_traits(resolve, world).join(" + "));
+
+        if !self.opts.skip_mut_forwarding_impls {
+            uwriteln!(
+                self.src,
+                "
+                    pub fn add_to_linker<T, U>(
+                        linker: &mut wasmtime::component::Linker<T>,
+                        get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
+                    ) -> wasmtime::Result<()>
+                        where
+                            {data_bounds}
+                            {host_bounds}
+                    {{
+                "
+            );
+            if has_world_imports_trait {
+                uwriteln!(
+                    self.src,
+                    "Self::add_to_linker_imports_get_host(linker, get)?;"
+                );
+            }
+            for path in self.import_interface_paths() {
+                uwriteln!(self.src, "{path}::add_to_linker(linker, get)?;");
+            }
+            uwriteln!(self.src, "Ok(())\n}}");
         }
-        uwriteln!(self.src, "Ok(())\n}}");
     }
 }
 
@@ -1147,13 +1228,13 @@ impl<'a> InterfaceGenerator<'a> {
                 }
             }
 
+            // Generate resource trait
             if self.gen.opts.async_.maybe_async() {
                 uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]")
             }
-
             uwriteln!(self.src, "pub trait Host{camel} {{");
 
-            let functions = match resource.owner {
+            let mut functions = match resource.owner {
                 TypeOwner::World(id) => self.resolve.worlds[id]
                     .imports
                     .values()
@@ -1171,16 +1252,16 @@ impl<'a> InterfaceGenerator<'a> {
                 }
             };
 
-            for func in functions {
-                match func.kind {
-                    FunctionKind::Method(resource)
-                    | FunctionKind::Static(resource)
-                    | FunctionKind::Constructor(resource)
-                        if id == resource => {}
-                    _ => continue,
-                }
+            functions.retain(|func| match func.kind {
+                FunctionKind::Freestanding => false,
+                FunctionKind::Method(resource)
+                | FunctionKind::Static(resource)
+                | FunctionKind::Constructor(resource) => id == resource,
+            });
 
+            for func in &functions {
                 self.generate_function_trait_sig(func);
+                self.push_str(";\n");
             }
 
             uwrite!(
@@ -1188,6 +1269,44 @@ impl<'a> InterfaceGenerator<'a> {
                 "fn drop(&mut self, rep: wasmtime::component::Resource<{camel}>) -> wasmtime::Result<()>;");
 
             uwriteln!(self.src, "}}");
+
+            // Generate impl HostResource for &mut HostResource
+            if !self.gen.opts.skip_mut_forwarding_impls {
+                let (async_trait, maybe_send) = if self.gen.opts.async_.maybe_async() {
+                    (
+                        "#[wasmtime::component::__internal::async_trait]\n",
+                        "+ Send",
+                    )
+                } else {
+                    ("", "")
+                };
+                uwriteln!(
+                    self.src,
+                    "{async_trait}impl <_T: Host{camel} + ?Sized {maybe_send}> Host{camel} for &mut _T {{"
+                );
+                for func in &functions {
+                    self.generate_function_trait_sig(func);
+                    uwrite!(
+                        self.src,
+                        "{{ Host{camel}::{}(*self,",
+                        rust_function_name(func)
+                    );
+                    for (name, _) in func.params.iter() {
+                        uwrite!(self.src, "{},", to_rust_ident(name));
+                    }
+                    uwrite!(self.src, ")");
+                    if self.gen.opts.async_.is_import_async(&func.name) {
+                        uwrite!(self.src, ".await");
+                    }
+                    uwriteln!(self.src, "}}");
+                }
+                uwriteln!(self.src, "
+                    fn drop(&mut self, rep: wasmtime::component::Resource<{camel}>) -> wasmtime::Result<()> {{
+                        Host{camel}::drop(*self, rep)
+                    }}",
+                );
+                uwriteln!(self.src, "}}");
+            }
         } else {
             self.rustdoc(docs);
             uwriteln!(
@@ -1745,20 +1864,23 @@ impl<'a> InterfaceGenerator<'a> {
         let iface = &self.resolve.interfaces[id];
         let owner = TypeOwner::Interface(id);
 
-        if self.gen.opts.async_.maybe_async() {
+        let is_maybe_async = self.gen.opts.async_.maybe_async();
+        if is_maybe_async {
             uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]")
         }
         // Generate the `pub trait` which represents the host functionality for
         // this import which additionally inherits from all resource traits
         // for this interface defined by `type_resource`.
         uwrite!(self.src, "pub trait Host");
-        for (i, resource) in get_resources(self.resolve, id).enumerate() {
-            if i == 0 {
-                uwrite!(self.src, ": ");
-            } else {
-                uwrite!(self.src, " + ");
-            }
-            uwrite!(self.src, "Host{}", resource.to_upper_camel_case());
+        let mut host_supertraits = vec![];
+        if is_maybe_async {
+            host_supertraits.push("Send".to_string());
+        }
+        for resource in get_resources(self.resolve, id) {
+            host_supertraits.push(format!("Host{}", resource.to_upper_camel_case()));
+        }
+        if !host_supertraits.is_empty() {
+            uwrite!(self.src, ": {}", host_supertraits.join(" + "));
         }
         uwriteln!(self.src, " {{");
         for (_, func) in iface.functions.iter() {
@@ -1767,12 +1889,13 @@ impl<'a> InterfaceGenerator<'a> {
                 _ => continue,
             }
             self.generate_function_trait_sig(func);
+            self.push_str(";\n");
         }
 
         // Generate `convert_*` functions to convert custom trappable errors
         // into the representation required by Wasmtime's component API.
         let mut required_conversion_traits = IndexSet::new();
-        let mut errors_converted = IndexSet::new();
+        let mut errors_converted = IndexMap::new();
         let my_error_types = iface
             .types
             .iter()
@@ -1783,9 +1906,10 @@ impl<'a> InterfaceGenerator<'a> {
             .iter()
             .filter_map(|(_, func)| self.special_case_trappable_error(&func.results))
             .map(|(_, id, _)| id);
-        for err in my_error_types.chain(used_error_types).collect::<Vec<_>>() {
-            let custom_name = &self.gen.trappable_errors[&err];
-            let err = &self.resolve.types[resolve_type_definition_id(self.resolve, err)];
+        let root = self.path_to_root();
+        for err_id in my_error_types.chain(used_error_types).collect::<Vec<_>>() {
+            let custom_name = &self.gen.trappable_errors[&err_id];
+            let err = &self.resolve.types[resolve_type_definition_id(self.resolve, err_id)];
             let err_name = err.name.as_ref().unwrap();
             let err_snake = err_name.to_snake_case();
             let err_camel = err_name.to_upper_camel_case();
@@ -1798,8 +1922,7 @@ impl<'a> InterfaceGenerator<'a> {
                     required_conversion_traits.insert(format!("{path}::Host"));
                 }
                 None => {
-                    if errors_converted.insert(err_name) {
-                        let root = self.path_to_root();
+                    if errors_converted.insert(err_name, err_id).is_none() {
                         uwriteln!(
                             self.src,
                             "fn convert_{err_snake}(&mut self, err: {root}{custom_name}) -> wasmtime::Result<{err_camel}>;"
@@ -1810,25 +1933,41 @@ impl<'a> InterfaceGenerator<'a> {
         }
         uwriteln!(self.src, "}}");
 
-        let mut where_clause = if self.gen.opts.async_.maybe_async() {
-            "T: Send, U: Host + Send".to_string()
+        let (data_bounds, mut host_bounds) = if self.gen.opts.async_.maybe_async() {
+            ("T: Send,", "Host + Send".to_string())
         } else {
-            "U: Host".to_string()
+            ("", "Host".to_string())
         };
-
-        for t in required_conversion_traits {
-            where_clause.push_str(" + ");
-            where_clause.push_str(&t);
+        for ty in required_conversion_traits {
+            uwrite!(host_bounds, " + {ty}");
         }
 
         uwriteln!(
             self.src,
             "
-                pub fn add_to_linker<T, U>(
+                pub trait GetHost<T>:
+                    Fn(T) -> <Self as GetHost<T>>::Host
+                        + Send
+                        + Sync
+                        + Copy
+                        + 'static
+                {{
+                    type Host: {host_bounds};
+                }}
+
+                impl<F, T, O> GetHost<T> for F
+                where
+                    F: Fn(T) -> O + Send + Sync + Copy + 'static,
+                    O: {host_bounds},
+                {{
+                    type Host = O;
+                }}
+
+                pub fn add_to_linker_get_host<T>(
                     linker: &mut wasmtime::component::Linker<T>,
-                    get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
+                    host_getter: impl for<'a> GetHost<&'a mut T>,
                 ) -> wasmtime::Result<()>
-                    where {where_clause}
+                    where {data_bounds}
                 {{
             "
         );
@@ -1842,7 +1981,7 @@ impl<'a> InterfaceGenerator<'a> {
                     \"{name}\",
                     wasmtime::component::ResourceType::host::<{camel}>(),
                     move |mut store, rep| -> wasmtime::Result<()> {{
-                        Host{camel}::drop(get(store.data_mut()), wasmtime::component::Resource::new_own(rep))
+                        Host{camel}::drop(&mut host_getter(store.data_mut()), wasmtime::component::Resource::new_own(rep))
                     }},
                 )?;"
             )
@@ -1853,6 +1992,68 @@ impl<'a> InterfaceGenerator<'a> {
         }
         uwriteln!(self.src, "Ok(())");
         uwriteln!(self.src, "}}");
+
+        if !self.gen.opts.skip_mut_forwarding_impls {
+            // Generate add_to_linker (with closure)
+            uwriteln!(
+                self.src,
+                "
+                pub fn add_to_linker<T, U>(
+                    linker: &mut wasmtime::component::Linker<T>,
+                    get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
+                ) -> wasmtime::Result<()>
+                    where
+                        U: {host_bounds}, {data_bounds}
+                {{
+                    add_to_linker_get_host(linker, get)
+                }}
+                "
+            );
+
+            // Generate impl Host for &mut Host
+            let (async_trait, maybe_send) = if is_maybe_async {
+                (
+                    "#[wasmtime::component::__internal::async_trait]\n",
+                    "+ Send",
+                )
+            } else {
+                ("", "")
+            };
+
+            uwriteln!(
+                self.src,
+                "{async_trait}impl<_T: Host + ?Sized {maybe_send}> Host for &mut _T {{"
+            );
+            // Forward each method call to &mut T
+            for (_, func) in iface.functions.iter() {
+                match func.kind {
+                    FunctionKind::Freestanding => {}
+                    _ => continue,
+                }
+                self.generate_function_trait_sig(func);
+                uwrite!(self.src, "{{ Host::{}(*self,", rust_function_name(func));
+                for (name, _) in func.params.iter() {
+                    uwrite!(self.src, "{},", to_rust_ident(name));
+                }
+                uwrite!(self.src, ")");
+                if self.gen.opts.async_.is_import_async(&func.name) {
+                    uwrite!(self.src, ".await");
+                }
+                uwriteln!(self.src, "}}");
+            }
+            for (err_name, err_id) in errors_converted {
+                uwriteln!(
+                    self.src,
+                    "fn convert_{err_snake}(&mut self, err: {root}{custom_name}) -> wasmtime::Result<{err_camel}> {{
+                        Host::convert_{err_snake}(*self, err)
+                    }}",
+                    custom_name = self.gen.trappable_errors[&err_id],
+                    err_snake = err_name.to_snake_case(),
+                    err_camel = err_name.to_upper_camel_case(),
+                );
+            }
+            uwriteln!(self.src, "}}");
+        }
     }
 
     fn generate_add_function_to_linker(&mut self, owner: TypeOwner, func: &Function, linker: &str) {
@@ -1934,7 +2135,8 @@ impl<'a> InterfaceGenerator<'a> {
             );
         }
 
-        self.src.push_str("let host = get(caller.data_mut());\n");
+        self.src
+            .push_str("let host = &mut host_getter(caller.data_mut());\n");
         let func_name = rust_function_name(func);
         let host_trait = match func.kind {
             FunctionKind::Freestanding => match owner {
@@ -2053,8 +2255,6 @@ impl<'a> InterfaceGenerator<'a> {
             self.print_result_ty(&func.results, TypeMode::Owned);
             self.push_str(">");
         }
-
-        self.push_str(";\n");
     }
 
     fn extract_typed_function(&mut self, func: &Function) -> (String, String) {


Test patch:
diff --git a/tests/all/component_model/bindgen.rs b/tests/all/component_model/bindgen.rs
index 0a6bc3636..71397b9a0 100644
--- a/tests/all/component_model/bindgen.rs
+++ b/tests/all/component_model/bindgen.rs
@@ -325,6 +325,9 @@ mod async_config {
             package foo:foo;
 
             world t1 {
+                import foo: interface {
+                    foo: func();
+                }
                 import x: func();
                 import y: func();
                 export z: func();