Repository: bytecodealliance/wasmtime
Pull request #6886 (2023-08-22 20:40:04)

Host resources wit-bindgen code generation
Implements host export resource generation #6722 

After a discussion with @alexcrichton, I have broken out basic resource generation for host resource exports into it's own PR,
Host import resource generation will follow, as well as more ergonomic resource code generation that abstracts away the raw resource handles.

Example implementation: https://github.com/Nor2-io/wasmtime-resources-example/blob/main/example-host-export-index/src/main.rs

Example code generation output: https://github.com/Nor2-io/wasmtime-resources-example/blob/main/example-host-export-index/main%20copy%202.rs


Sneak peak of future additions that abstract away resources can be seen in the link below. (It doesn't work with nested resources yet.)
https://github.com/Nor2-io/wasmtime-resources-example/blob/main/example-host-import/src/main.rs
https://github.com/Nor2-io/wasmtime-resources-example/blob/main/example-host-export/src/main.rs

Added temporary exception for cargo deny on multiple versions until memfd has been updated: [#62](https://github.com/lucab/memfd-rs/pull/62)

github-actions[bot]: #### Subscribe to Label Action

cc @peterhuene

<details>
This issue or pull request has been labeled: "wasmtime:api"

Thus the following users have been cc'd because of the following labels:

* peterhuene: wasmtime:api

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>
alexcrichton: Ok I've worked on this a bit this morning and I've pushed up https://github.com/Nor2-io/wasmtime/pull/1 which contains another round of comments for me (and some fixes for the deny/vet-related things I think). As I mentioned over there I'm happy to go through anything if you'd like clarifications or explanations!
alexcrichton: One more round of changes at https://github.com/Nor2-io/wasmtime/pull/2 (again happy to go through anything if you'd like), and then I think this is good to go 👍 
alexcrichton: Also @silesmo would you be up for filing an issue for follow-up work?
silesmo: > Also @silesmo would you be up for filing an issue for follow-up work?

Yes I can do that! 🙂
alexcrichton: Pat's busy with streams and such so he'll take a look at this after-the-fact, so I'm going to go ahead and flag this for merge. Thanks again @silesmo!
alexcrichton: oh I think this may need a rebase for merging now?
silesmo: > oh I think this may need a rebase for merging now?

I merged the latest changes now 

Patch:
diff --git a/crates/wasmtime/src/component/linker.rs b/crates/wasmtime/src/component/linker.rs
index 15469923f..2317a833e 100644
--- a/crates/wasmtime/src/component/linker.rs
+++ b/crates/wasmtime/src/component/linker.rs
@@ -396,17 +396,21 @@ impl<T> LinkerInstance<'_, T> {
     /// along with the representation of the resource that was just destroyed.
     ///
     /// [`Resource<U>`]: crate::component::Resource
+    ///
+    /// # Errors
+    ///
+    /// The provided `dtor` closure returns an error if something goes wrong
+    /// when a guest calls the `dtor` to drop a `Resource<T>` such as
+    /// a runtime trap or a runtime limit being exceeded.
     pub fn resource<U: 'static>(
         &mut self,
         name: &str,
-        dtor: impl Fn(StoreContextMut<'_, T>, u32) + Send + Sync + 'static,
+        dtor: impl Fn(StoreContextMut<'_, T>, u32) -> Result<()> + Send + Sync + 'static,
     ) -> Result<()> {
         let name = self.strings.intern(name);
         let dtor = Arc::new(crate::func::HostFunc::wrap(
             &self.engine,
-            move |mut cx: crate::Caller<'_, T>, param: u32| {
-                dtor(cx.as_context_mut(), param);
-            },
+            move |mut cx: crate::Caller<'_, T>, param: u32| dtor(cx.as_context_mut(), param),
         ));
         self.insert(name, Definition::Resource(ResourceType::host::<U>(), dtor))
     }
diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs
index 09a47e212..44431ba2e 100644
--- a/crates/wit-bindgen/src/lib.rs
+++ b/crates/wit-bindgen/src/lib.rs
@@ -47,6 +47,7 @@ struct Wasmtime {
     sizes: SizeAlign,
     interface_names: HashMap<InterfaceId, InterfaceName>,
     with_name_counter: usize,
+    interface_last_seen_as_import: HashMap<InterfaceId, bool>,
 }
 
 struct ImportInterface {
@@ -173,7 +174,7 @@ impl Wasmtime {
         let world = &resolve.worlds[id];
         for (name, import) in world.imports.iter() {
             if !self.opts.only_interfaces || matches!(import, WorldItem::Interface(_)) {
-                self.import(resolve, name, import);
+                self.import(resolve, id, name, import);
             }
         }
         for (name, export) in world.exports.iter() {
@@ -184,24 +185,31 @@ impl Wasmtime {
         self.finish(resolve, id)
     }
 
-    fn import(&mut self, resolve: &Resolve, name: &WorldKey, item: &WorldItem) {
+    fn import(&mut self, resolve: &Resolve, world: WorldId, name: &WorldKey, item: &WorldItem) {
         let mut gen = InterfaceGenerator::new(self, resolve);
         match item {
             WorldItem::Function(func) => {
-                gen.generate_function_trait_sig(func);
+                // 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 {
+                    gen.generate_function_trait_sig(func);
+                }
                 let sig = mem::take(&mut gen.src).into();
-                gen.generate_add_function_to_linker(TypeOwner::None, func, "linker");
+                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 });
             }
             WorldItem::Interface(id) => {
+                gen.gen.interface_last_seen_as_import.insert(*id, true);
                 if gen.gen.name_interface(resolve, *id, name) {
                     return;
                 }
                 gen.current_interface = Some((*id, name, false));
                 gen.types(*id);
                 let key_name = resolve.name_world_key(name);
+
                 gen.generate_add_to_linker(*id, &key_name);
 
                 let module = &gen.src[..];
@@ -264,6 +272,7 @@ impl Wasmtime {
             }
             WorldItem::Type(_) => unreachable!(),
             WorldItem::Interface(id) => {
+                gen.gen.interface_last_seen_as_import.insert(*id, false);
                 gen.gen.name_interface(resolve, *id, name);
                 gen.current_interface = Some((*id, name, true));
                 gen.types(*id);
@@ -275,11 +284,18 @@ impl Wasmtime {
                 let camel = to_rust_upper_camel_case(iface_name);
                 uwriteln!(gen.src, "pub struct {camel} {{");
                 for (_, func) in iface.functions.iter() {
-                    uwriteln!(
-                        gen.src,
-                        "{}: wasmtime::component::Func,",
-                        func.name.to_snake_case()
-                    );
+                    match func.kind {
+                        FunctionKind::Freestanding => {
+                            uwriteln!(
+                                gen.src,
+                                "{}: wasmtime::component::Func,",
+                                func.name.to_snake_case()
+                            );
+                        }
+                        // Resource methods are handled separately in
+                        // `type_resource`.
+                        _ => {}
+                    }
                 }
                 uwriteln!(gen.src, "}}");
 
@@ -294,9 +310,16 @@ impl Wasmtime {
                 );
                 let mut fields = Vec::new();
                 for (_, func) in iface.functions.iter() {
-                    let (name, getter) = gen.extract_typed_function(func);
-                    uwriteln!(gen.src, "let {name} = {getter};");
-                    fields.push(name);
+                    match func.kind {
+                        FunctionKind::Freestanding => {
+                            let (name, getter) = gen.extract_typed_function(func);
+                            uwriteln!(gen.src, "let {name} = {getter};");
+                            fields.push(name);
+                        }
+                        // Resource methods are handled separately in
+                        // `type_resource`.
+                        _ => {}
+                    }
                 }
                 uwriteln!(gen.src, "Ok({camel} {{");
                 for name in fields {
@@ -305,7 +328,14 @@ impl Wasmtime {
                 uwriteln!(gen.src, "}})");
                 uwriteln!(gen.src, "}}");
                 for (_, func) in iface.functions.iter() {
-                    gen.define_rust_guest_export(resolve, Some(name), func);
+                    match func.kind {
+                        FunctionKind::Freestanding => {
+                            gen.define_rust_guest_export(resolve, Some(name), func);
+                        }
+                        // Resource methods are handled separately in
+                        // `type_resource`.
+                        _ => {}
+                    }
                 }
                 uwriteln!(gen.src, "}}");
 
@@ -537,8 +567,12 @@ impl Wasmtime {
 }
 
 impl Wasmtime {
+    fn has_world_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.import_functions.is_empty() {
+        if !self.has_world_trait(resolve, world) {
             return;
         }
 
@@ -546,7 +580,16 @@ impl Wasmtime {
         if self.opts.async_ {
             uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]")
         }
-        uwriteln!(self.src, "pub trait {world_camel}Imports {{");
+        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());
+        }
+        uwriteln!(self.src, " {{");
         for f in self.import_functions.iter() {
             self.src.push_str(&f.sig);
             self.src.push_str("\n");
@@ -555,7 +598,8 @@ impl Wasmtime {
     }
 
     fn toplevel_add_to_linker(&mut self, resolve: &Resolve, world: WorldId) {
-        if self.import_interfaces.is_empty() && self.import_functions.is_empty() {
+        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();
@@ -568,8 +612,9 @@ impl Wasmtime {
                     path.push_str(&pkg.name.to_snake_case());
                     path.push_str("::");
                 }
+
                 path.push_str(&import.snake);
-                interfaces.push(path)
+                interfaces.push(path);
             }
         }
 
@@ -588,10 +633,10 @@ impl Wasmtime {
         for (i, name) in interfaces
             .iter()
             .map(|n| format!("{n}::Host"))
-            .chain(if self.import_functions.is_empty() {
-                None
-            } else {
+            .chain(if has_world_trait {
                 Some(world_trait.clone())
+            } else {
+                None
             })
             .enumerate()
         {
@@ -600,21 +645,23 @@ impl Wasmtime {
             }
             self.src.push_str(&name);
         }
+
         let maybe_send = if self.opts.async_ {
             " + Send, T: Send"
         } else {
             ""
         };
+
         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)?;");
         }
-        if !self.import_functions.is_empty() {
+        if has_world_trait {
             uwriteln!(self.src, "Self::add_root_to_linker(linker, get)?;");
         }
         uwriteln!(self.src, "Ok(())\n}}");
-        if self.import_functions.is_empty() {
+        if !has_world_trait {
             return;
         }
 
@@ -630,6 +677,19 @@ impl Wasmtime {
                     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::<{camel}>(
+                    \"{name}\",
+                    move |mut store, rep| -> wasmtime::Result<()> {{
+                        Host{camel}::drop(get(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");
@@ -769,12 +829,75 @@ impl<'a> InterfaceGenerator<'a> {
             TypeDefKind::Type(t) => self.type_alias(id, name, t, &ty.docs),
             TypeDefKind::Future(_) => todo!("generate for future"),
             TypeDefKind::Stream(_) => todo!("generate for stream"),
-            TypeDefKind::Handle(_) => todo!("#6722"),
-            TypeDefKind::Resource => todo!("#6722"),
+            TypeDefKind::Handle(handle) => self.type_handle(id, name, handle, &ty.docs),
+            TypeDefKind::Resource => self.type_resource(id, name, ty, &ty.docs),
             TypeDefKind::Unknown => unreachable!(),
         }
     }
 
+    fn type_handle(&mut self, id: TypeId, name: &str, handle: &Handle, docs: &Docs) {
+        self.rustdoc(docs);
+        let name = name.to_upper_camel_case();
+        uwriteln!(self.src, "pub type {name} = ");
+        self.print_handle(handle);
+        self.push_str(";\n");
+        self.assert_type(id, &name);
+    }
+
+    fn type_resource(&mut self, id: TypeId, _name: &str, resource: &TypeDef, docs: &Docs) {
+        let camel = resource
+            .name
+            .as_ref()
+            .expect("resources are required to be named")
+            .to_upper_camel_case();
+
+        self.rustdoc(docs);
+        uwriteln!(self.src, "pub enum {camel} {{}}");
+
+        if self.gen.opts.async_ {
+            uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]")
+        }
+
+        uwriteln!(self.src, "pub trait Host{camel} {{");
+
+        let functions = match resource.owner {
+            TypeOwner::World(id) => self.resolve.worlds[id]
+                .imports
+                .values()
+                .filter_map(|item| match item {
+                    WorldItem::Function(f) => Some(f),
+                    _ => None,
+                })
+                .collect(),
+            TypeOwner::Interface(id) => self.resolve.interfaces[id]
+                .functions
+                .values()
+                .collect::<Vec<_>>(),
+            TypeOwner::None => {
+                panic!("A resource must be owned by a world or interface");
+            }
+        };
+
+        for func in functions {
+            match func.kind {
+                FunctionKind::Method(resource)
+                | FunctionKind::Static(resource)
+                | FunctionKind::Constructor(resource)
+                    if id == resource => {}
+                _ => continue,
+            }
+
+            self.generate_function_trait_sig(func);
+        }
+
+        uwrite!(
+            self.src,
+            "fn drop(&mut self, rep: wasmtime::component::Resource<{camel}>) -> wasmtime::Result<()>;"
+        );
+
+        uwriteln!(self.src, "}}");
+    }
+
     fn type_record(&mut self, id: TypeId, _name: &str, record: &Record, docs: &Docs) {
         let info = self.info(id);
         for (name, mode) in self.modes_of(id) {
@@ -788,9 +911,9 @@ impl<'a> InterfaceGenerator<'a> {
             self.push_str("#[derive(wasmtime::component::Lower)]\n");
             self.push_str("#[component(record)]\n");
 
-            if !info.has_list {
+            if info.is_copy() {
                 self.push_str("#[derive(Copy, Clone)]\n");
-            } else {
+            } else if info.is_clone() {
                 self.push_str("#[derive(Clone)]\n");
             }
             self.push_str(&format!("pub struct {}", name));
@@ -955,9 +1078,9 @@ impl<'a> InterfaceGenerator<'a> {
             }
             self.push_str("#[derive(wasmtime::component::Lower)]\n");
             self.push_str(&format!("#[component({})]\n", derive_component));
-            if !info.has_list {
-                self.push_str("#[derive(Clone, Copy)]\n");
-            } else {
+            if info.is_copy() {
+                self.push_str("#[derive(Copy, Clone)]\n");
+            } else if info.is_clone() {
                 self.push_str("#[derive(Clone)]\n");
             }
             self.push_str(&format!("pub enum {name}"));
@@ -1178,7 +1301,10 @@ impl<'a> InterfaceGenerator<'a> {
             self.push_str(" = ");
             self.print_ty(ty, mode);
             self.push_str(";\n");
-            self.assert_type(id, &name);
+            let def_id = resolve_type_definition_id(self.resolve, id);
+            if !matches!(self.resolve().types[def_id].kind, TypeDefKind::Resource) {
+                self.assert_type(id, &name);
+            }
         }
     }
 
@@ -1253,9 +1379,23 @@ impl<'a> InterfaceGenerator<'a> {
             uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]")
         }
         // Generate the `pub trait` which represents the host functionality for
-        // this import.
-        uwriteln!(self.src, "pub trait Host {{");
+        // 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());
+        }
+        uwriteln!(self.src, " {{");
         for (_, func) in iface.functions.iter() {
+            match func.kind {
+                FunctionKind::Freestanding => {}
+                _ => continue,
+            }
             self.generate_function_trait_sig(func);
         }
         uwriteln!(self.src, "}}");
@@ -1265,6 +1405,7 @@ impl<'a> InterfaceGenerator<'a> {
         } else {
             "U: Host".to_string()
         };
+
         uwriteln!(
             self.src,
             "
@@ -1272,11 +1413,25 @@ impl<'a> InterfaceGenerator<'a> {
                     linker: &mut wasmtime::component::Linker<T>,
                     get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
                 ) -> wasmtime::Result<()>
-                    where {where_clause},
+                    where {where_clause}
                 {{
             "
         );
         uwriteln!(self.src, "let mut inst = linker.instance(\"{name}\")?;");
+
+        for name in get_resources(self.resolve, id) {
+            let camel = name.to_upper_camel_case();
+            uwriteln!(
+                self.src,
+                "inst.resource::<{camel}>(
+                    \"{name}\",
+                    move |mut store, rep| -> wasmtime::Result<()> {{
+                        Host{camel}::drop(get(store.data_mut()), wasmtime::component::Resource::new_own(rep))
+                    }},
+                )?;"
+            )
+        }
+
         for (_, func) in iface.functions.iter() {
             self.generate_add_function_to_linker(owner, func, "inst");
         }
@@ -1302,16 +1457,18 @@ impl<'a> InterfaceGenerator<'a> {
     fn generate_guest_import_closure(&mut self, owner: TypeOwner, func: &Function) {
         // Generate the closure that's passed to a `Linker`, the final piece of
         // codegen here.
+
         self.src
             .push_str("move |mut caller: wasmtime::StoreContextMut<'_, T>, (");
         for (i, _param) in func.params.iter().enumerate() {
             uwrite!(self.src, "arg{},", i);
         }
         self.src.push_str(") : (");
-        for param in func.params.iter() {
+
+        for (_, ty) in func.params.iter() {
             // Lift is required to be impled for this type, so we can't use
             // a borrowed type:
-            self.print_ty(&param.1, TypeMode::Owned);
+            self.print_ty(ty, TypeMode::Owned);
             self.src.push_str(", ");
         }
         self.src.push_str(") |");
@@ -1361,8 +1518,26 @@ impl<'a> InterfaceGenerator<'a> {
         }
 
         self.src.push_str("let host = get(caller.data_mut());\n");
+        let func_name = rust_function_name(func);
+        let host_trait = match func.kind {
+            FunctionKind::Freestanding => match owner {
+                TypeOwner::World(id) => format!(
+                    "{}Imports",
+                    self.resolve.worlds[id].name.to_upper_camel_case()
+                ),
+                _ => "Host".to_string(),
+            },
+            FunctionKind::Method(id) | FunctionKind::Static(id) | FunctionKind::Constructor(id) => {
+                let resource = self.resolve.types[id]
+                    .name
+                    .as_ref()
+                    .unwrap()
+                    .to_upper_camel_case();
+                format!("Host{resource}")
+            }
+        };
+        uwrite!(self.src, "let r = {host_trait}::{func_name}(host, ");
 
-        uwrite!(self.src, "let r = host.{}(", func.name.to_snake_case());
         for (i, _) in func.params.iter().enumerate() {
             uwrite!(self.src, "arg{},", i);
         }
@@ -1411,7 +1586,7 @@ impl<'a> InterfaceGenerator<'a> {
             self.push_str("async ");
         }
         self.push_str("fn ");
-        self.push_str(&to_rust_ident(&func.name));
+        self.push_str(&rust_function_name(func));
         self.push_str("(&mut self, ");
         for (name, param) in func.params.iter() {
             let name = to_rust_ident(name);
@@ -1490,16 +1665,19 @@ impl<'a> InterfaceGenerator<'a> {
         };
 
         self.rustdoc(&func.docs);
+
         uwrite!(
             self.src,
             "pub {async_} fn call_{}<S: wasmtime::AsContextMut>(&self, mut store: S, ",
             func.name.to_snake_case(),
         );
+
         for (i, param) in func.params.iter().enumerate() {
             uwrite!(self.src, "arg{}: ", i);
             self.print_ty(&param.1, TypeMode::AllBorrowed("'_"));
             self.push_str(",");
         }
+
         self.src.push_str(") -> wasmtime::Result<");
         self.print_result_ty(&func.results, TypeMode::Owned);
 
@@ -1679,6 +1857,10 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> {
     fn info(&self, ty: TypeId) -> TypeInfo {
         self.gen.types.get(ty)
     }
+
+    fn is_imported_interface(&self, interface: InterfaceId) -> bool {
+        self.gen.interface_last_seen_as_import[&interface]
+    }
 }
 
 /// When an interface `use`s a type from another interface, it creates a new TypeId
@@ -1692,3 +1874,40 @@ fn resolve_type_definition_id(resolve: &Resolve, mut id: TypeId) -> TypeId {
         }
     }
 }
+
+fn rust_function_name(func: &Function) -> String {
+    match func.kind {
+        FunctionKind::Method(_) | FunctionKind::Static(_) => to_rust_ident(func.item_name()),
+        FunctionKind::Constructor(_) => "new".to_string(),
+        FunctionKind::Freestanding => to_rust_ident(&func.name),
+    }
+}
+
+fn get_resources<'a>(resolve: &'a Resolve, id: InterfaceId) -> impl Iterator<Item = &'a str> + 'a {
+    resolve.interfaces[id]
+        .types
+        .iter()
+        .filter_map(move |(name, ty)| match resolve.types[*ty].kind {
+            TypeDefKind::Resource => Some(name.as_str()),
+            _ => None,
+        })
+}
+
+fn get_world_resources<'a>(
+    resolve: &'a Resolve,
+    id: WorldId,
+) -> impl Iterator<Item = &'a str> + 'a {
+    resolve.worlds[id]
+        .imports
+        .iter()
+        .filter_map(move |(name, item)| match item {
+            WorldItem::Type(id) => match resolve.types[*id].kind {
+                TypeDefKind::Resource => Some(match name {
+                    WorldKey::Name(s) => s.as_str(),
+                    WorldKey::Interface(_) => unreachable!(),
+                }),
+                _ => None,
+            },
+            _ => None,
+        })
+}
diff --git a/crates/wit-bindgen/src/rust.rs b/crates/wit-bindgen/src/rust.rs
index 48100dfaf..009bebffb 100644
--- a/crates/wit-bindgen/src/rust.rs
+++ b/crates/wit-bindgen/src/rust.rs
@@ -14,6 +14,7 @@ pub trait RustGenerator<'a> {
     fn push_str(&mut self, s: &str);
     fn info(&self, ty: TypeId) -> TypeInfo;
     fn path_to_interface(&self, interface: InterfaceId) -> Option<String>;
+    fn is_imported_interface(&self, interface: InterfaceId) -> bool;
 
     /// This determines whether we generate owning types or (where appropriate)
     /// borrowing types.
@@ -86,13 +87,7 @@ pub trait RustGenerator<'a> {
             } else {
                 self.result_name(id)
             };
-            if let TypeOwner::Interface(id) = ty.owner {
-                if let Some(path) = self.path_to_interface(id) {
-                    self.push_str(&path);
-                    self.push_str("::");
-                }
-            }
-            self.push_str(&name);
+            self.print_type_name_in_interface(ty.owner, &name);
 
             // If the type recursively owns data and it's a
             // variant/record/list, then we need to place the
@@ -179,14 +174,26 @@ pub trait RustGenerator<'a> {
                 self.push_str(">");
             }
 
-            TypeDefKind::Handle(_) => todo!("#6722"),
-            TypeDefKind::Resource => todo!("#6722"),
+            TypeDefKind::Handle(handle) => {
+                self.print_handle(handle);
+            }
+            TypeDefKind::Resource => unreachable!(),
 
             TypeDefKind::Type(t) => self.print_ty(t, mode),
             TypeDefKind::Unknown => unreachable!(),
         }
     }
 
+    fn print_type_name_in_interface(&mut self, owner: TypeOwner, name: &str) {
+        if let TypeOwner::Interface(id) = owner {
+            if let Some(path) = self.path_to_interface(id) {
+                self.push_str(&path);
+                self.push_str("::");
+            }
+        }
+        self.push_str(name);
+    }
+
     fn print_list(&mut self, ty: &Type, mode: TypeMode) {
         let next_mode = if matches!(self.ownership(), Ownership::Owning) {
             TypeMode::Owned
@@ -212,6 +219,41 @@ pub trait RustGenerator<'a> {
         }
     }
 
+    fn print_handle(&mut self, handle: &Handle) {
+        // Handles are either printed as `ResourceAny` for any guest-defined
+        // resource or `Resource<T>` for all host-defined resources. This means
+        // that this function needs to determine if `handle` points to a host
+        // or a guest resource which is determined by:
+        //
+        // * For world-owned resources, they're always imported.
+        // * For interface-owned resources, it depends on the how bindings were
+        //   last generated for this interface.
+        //
+        // Additionally type aliases via `use` are "peeled" here to find the
+        // original definition of the resource since that's the one that we
+        // care about for determining whether it's imported or not.
+        let resource = match handle {
+            Handle::Own(t) | Handle::Borrow(t) => *t,
+        };
+        let ty = &self.resolve().types[resource];
+        let def_id = super::resolve_type_definition_id(self.resolve(), resource);
+        let ty_def = &self.resolve().types[def_id];
+        let is_host_defined = match ty_def.owner {
+            TypeOwner::Interface(i) => self.is_imported_interface(i),
+            _ => true,
+        };
+        if is_host_defined {
+            self.push_str("wasmtime::component::Resource<");
+            self.print_type_name_in_interface(
+                ty.owner,
+                &ty.name.as_ref().unwrap().to_upper_camel_case(),
+            );
+            self.push_str(">");
+        } else {
+            self.push_str("wasmtime::component::ResourceAny");
+        }
+    }
+
     fn print_generics(&mut self, lifetime: Option<&str>) {
         if lifetime.is_none() {
             return;
@@ -244,6 +286,75 @@ pub trait RustGenerator<'a> {
         result
     }
 
+    /// Writes the camel-cased 'name' of the passed type to `out`, as used to name union variants.
+    fn write_name(&self, ty: &Type, out: &mut String) {
+        match ty {
+            Type::Bool => out.push_str("Bool"),
+            Type::U8 => out.push_str("U8"),
+            Type::U16 => out.push_str("U16"),
+            Type::U32 => out.push_str("U32"),
+            Type::U64 => out.push_str("U64"),
+            Type::S8 => out.push_str("I8"),
+            Type::S16 => out.push_str("I16"),
+            Type::S32 => out.push_str("I32"),
+            Type::S64 => out.push_str("I64"),
+            Type::Float32 => out.push_str("F32"),
+            Type::Float64 => out.push_str("F64"),
+            Type::Char => out.push_str("Char"),
+            Type::String => out.push_str("String"),
+            Type::Id(id) => {
+                let ty = &self.resolve().types[*id];
+                match &ty.name {
+                    Some(name) => out.push_str(&name.to_upper_camel_case()),
+                    None => match &ty.kind {
+                        TypeDefKind::Option(ty) => {
+                            out.push_str("Optional");
+                            self.write_name(ty, out);
+                        }
+                        TypeDefKind::Result(_) => out.push_str("Result"),
+                        TypeDefKind::Tuple(_) => out.push_str("Tuple"),
+                        TypeDefKind::List(ty) => {
+                            self.write_name(ty, out);
+                            out.push_str("List")
+                        }
+                        TypeDefKind::Future(ty) => {
+                            self.write_optional_name(ty.as_ref(), out);
+                            out.push_str("Future");
+                        }
+                        TypeDefKind::Stream(s) => {
+                            self.write_optional_name(s.element.as_ref(), out);
+                            self.write_optional_name(s.end.as_ref(), out);
+                            out.push_str("Stream");
+                        }
+
+                        TypeDefKind::Type(ty) => self.write_name(ty, out),
+                        TypeDefKind::Record(_) => out.push_str("Record"),
+                        TypeDefKind::Flags(_) => out.push_str("Flags"),
+                        TypeDefKind::Variant(_) => out.push_str("Variant"),
+                        TypeDefKind::Enum(_) => out.push_str("Enum"),
+                        TypeDefKind::Handle(Handle::Borrow(id)) => {
+                            out.push_str("Borrow");
+                            self.write_name(&Type::Id(*id), out);
+                        }
+                        TypeDefKind::Handle(Handle::Own(id)) => {
+                            out.push_str("Own");
+                            self.write_name(&Type::Id(*id), out);
+                        }
+                        TypeDefKind::Resource => unreachable!(),
+                        TypeDefKind::Unknown => unreachable!(),
+                    },
+                }
+            }
+        }
+    }
+
+    fn write_optional_name(&self, ty: Option<&Type>, out: &mut String) {
+        match ty {
+            Some(ty) => self.write_name(ty, out),
+            None => out.push_str("()"),
+        }
+    }
+
     fn param_name(&self, ty: TypeId) -> String {
         let info = self.info(ty);
         let name = self.resolve().types[ty]
diff --git a/crates/wit-bindgen/src/types.rs b/crates/wit-bindgen/src/types.rs
index ad9d25b67..208b2f865 100644
--- a/crates/wit-bindgen/src/types.rs
+++ b/crates/wit-bindgen/src/types.rs
@@ -23,6 +23,9 @@ pub struct TypeInfo {
 
     /// Whether or not this type (transitively) has a list.
     pub has_list: bool,
+
+    /// Whether or not this type (transitively) has a handle.
+    pub has_handle: bool,
 }
 
 impl std::ops::BitOrAssign for TypeInfo {
@@ -31,6 +34,7 @@ impl std::ops::BitOrAssign for TypeInfo {
         self.owned |= rhs.owned;
         self.error |= rhs.error;
         self.has_list |= rhs.has_list;
+        self.has_handle |= rhs.has_handle;
     }
 }
 
@@ -153,7 +157,7 @@ impl Types {
                 info = self.optional_type_info(resolve, stream.element.as_ref());
                 info |= self.optional_type_info(resolve, stream.end.as_ref());
             }
-            TypeDefKind::Handle(_) => {}
+            TypeDefKind::Handle(_) => info.has_handle = true,
             TypeDefKind::Resource => {}
             TypeDefKind::Unknown => unreachable!(),
         }
@@ -178,3 +182,13 @@ impl Types {
         }
     }
 }
+
+impl TypeInfo {
+    pub fn is_copy(&self) -> bool {
+        !self.has_list && !self.has_handle
+    }
+
+    pub fn is_clone(&self) -> bool {
+        !self.has_handle
+    }
+}


Test patch:
diff --git a/crates/component-macro/tests/codegen/resources-import.wit b/crates/component-macro/tests/codegen/resources-import.wit
new file mode 100644
index 000000000..bfe0c40de
--- /dev/null
+++ b/crates/component-macro/tests/codegen/resources-import.wit
@@ -0,0 +1,87 @@
+package foo:foo
+
+interface resources {
+  resource bar {
+    constructor()
+    static-a: static func() -> u32
+    method-a: func() -> u32
+  }
+
+  bar-own-arg: func(x: own<bar>)
+  bar-borrow-arg: func(x: borrow<bar>)
+  bar-result: func() -> own<bar>
+
+  tuple-own-arg: func(x: tuple<own<bar>, u32>)
+  tuple-borrow-arg: func(x: tuple<own<bar>, u32>)
+  tuple-result: func() -> tuple<own<bar>, u32>
+
+  option-own-arg: func(x: option<own<bar>>)
+  option-borrow-arg: func(x: option<borrow<bar>>)
+  option-result: func() -> option<own<bar>>
+
+  result-own-arg: func(x: result<own<bar>>)
+  result-borrow-arg: func(x: result<borrow<bar>>)
+  result-result: func() -> result<own<bar>>
+
+  list-own-arg: func(x: list<own<bar>>)
+  list-borrow-arg: func(x: list<borrow<bar>>)
+  list-result: func() -> list<own<bar>>
+
+  record nested-own {
+    nested-bar: own<bar>
+  }
+
+  record nested-borrow {
+    nested-bar: borrow<bar>
+  }
+
+  record-own-arg: func(x: nested-own)
+  record-borrow-arg: func(x: nested-borrow)
+  record-result: func() -> nested-own
+
+  type some-handle = borrow<bar>
+  func-with-handle-typedef: func(x: some-handle)
+}
+
+world the-world {
+  import resources
+
+  resource world-resource {
+    constructor()
+
+    foo: func()
+    static-foo: static func()
+  }
+
+  import some-world-func: func() -> world-resource
+  export some-world-func2: func() -> world-resource
+
+  export uses-resource-transitively
+
+  import long-use-chain4
+}
+
+interface transitive-interface-with-resource {
+  resource foo
+}
+
+interface uses-resource-transitively {
+  use transitive-interface-with-resource.{foo}
+
+  handle: func(x: foo)
+}
+
+interface long-use-chain4 {
+  use long-use-chain3.{a}
+
+  foo: func() -> a
+}
+interface long-use-chain3 {
+  use long-use-chain2.{a}
+}
+interface long-use-chain2 {
+  use long-use-chain1.{a}
+}
+interface long-use-chain1 {
+  resource a
+}
diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs
index 6e340aef2..17d49b7c2 100644
--- a/crates/wast/src/spectest.rs
+++ b/crates/wast/src/spectest.rs
@@ -97,9 +97,11 @@ pub fn link_component_spectest<T>(linker: &mut component::Linker<T>) -> Result<(
         move |_, rep| {
             state.drops.fetch_add(1, SeqCst);
             state.last_drop.store(rep, SeqCst);
+
+            Ok(())
         }
     })?;
-    i.resource::<Resource2>("resource2", |_, _| {})?;
+    i.resource::<Resource2>("resource2", |_, _| Ok(()))?;
     // Currently the embedder API requires redefining the resource destructor
     // here despite this being the same type as before, and fixing that is left
     // for a future refactoring.
diff --git a/tests/all/component_model/bindgen.rs b/tests/all/component_model/bindgen.rs
index 5d3d00135..dbe65a3dd 100644
--- a/tests/all/component_model/bindgen.rs
+++ b/tests/all/component_model/bindgen.rs
@@ -120,3 +120,199 @@ mod one_import {
         Ok(())
     }
 }
+
+mod resources_at_world_level {
+    use super::*;
+    use wasmtime::component::Resource;
+
+    wasmtime::component::bindgen!({
+        inline: "
+            package foo:foo
+
+            world resources {
+                resource x {
+                    constructor()
+                }
+
+                export y: func(x: x)
+            }
+        ",
+    });
+
+    #[test]
+    fn run() -> Result<()> {
+        let engine = engine();
+
+        let component = Component::new(
+            &engine,
+            r#"
+                (component
+                    (import "x" (type $x (sub resource)))
+                    (import "[constructor]x" (func $ctor (result (own $x))))
+
+                    (core func $dtor (canon resource.drop $x))
+                    (core func $ctor (canon lower (func $ctor)))
+
+                    (core module $m
+                        (import "" "ctor" (func $ctor (result i32)))
+                        (import "" "dtor" (func $dtor (param i32)))
+
+                        (func (export "x") (param i32)
+                            (call $dtor (local.get 0))
+                            (call $dtor (call $ctor))
+                        )
+                    )
+                    (core instance $i (instantiate $m
+                        (with "" (instance
+                            (export "ctor" (func $ctor))
+                            (export "dtor" (func $dtor))
+                        ))
+                    ))
+                    (func (export "y") (param "x" (own $x))
+                        (canon lift (core func $i "x")))
+                )
+            "#,
+        )?;
+
+        #[derive(Default)]
+        struct MyImports {
+            ctor_hit: bool,
+            drops: usize,
+        }
+
+        impl HostX for MyImports {
+            fn new(&mut self) -> Result<Resource<X>> {
+                self.ctor_hit = true;
+                Ok(Resource::new_own(80))
+            }
+
+            fn drop(&mut self, val: Resource<X>) -> Result<()> {
+                match self.drops {
+                    0 => assert_eq!(val.rep(), 40),
+                    1 => assert_eq!(val.rep(), 80),
+                    _ => unreachable!(),
+                }
+                self.drops += 1;
+                Ok(())
+            }
+        }
+
+        impl ResourcesImports for MyImports {}
+
+        let mut linker = Linker::new(&engine);
+        Resources::add_to_linker(&mut linker, |f: &mut MyImports| f)?;
+        let mut store = Store::new(&engine, MyImports::default());
+        let (one_import, _) = Resources::instantiate(&mut store, &component, &linker)?;
+        one_import.call_y(&mut store, Resource::new_own(40))?;
+        assert!(store.data().ctor_hit);
+        assert_eq!(store.data().drops, 2);
+        Ok(())
+    }
+}
+
+mod resources_at_interface_level {
+    use super::*;
+    use wasmtime::component::Resource;
+
+    wasmtime::component::bindgen!({
+        inline: "
+            package foo:foo
+
+            interface def {
+                resource x {
+                    constructor()
+                }
+            }
+
+            interface user {
+                use def.{x}
+
+                y: func(x: x)
+            }
+
+            world resources {
+                export user
+            }
+        ",
+    });
+
+    #[test]
+    fn run() -> Result<()> {
+        let engine = engine();
+
+        let component = Component::new(
+            &engine,
+            r#"
+                (component
+                    (import (interface "foo:foo/def") (instance $i
+                        (export $x "x" (type (sub resource)))
+                        (export "[constructor]x" (func (result (own $x))))
+                    ))
+                    (alias export $i "x" (type $x))
+                    (core func $dtor (canon resource.drop $x))
+                    (core func $ctor (canon lower (func $i "[constructor]x")))
+
+                    (core module $m
+                        (import "" "ctor" (func $ctor (result i32)))
+                        (import "" "dtor" (func $dtor (param i32)))
+
+                        (func (export "x") (param i32)
+                            (call $dtor (local.get 0))
+                            (call $dtor (call $ctor))
+                        )
+                    )
+                    (core instance $i (instantiate $m
+                        (with "" (instance
+                            (export "ctor" (func $ctor))
+                            (export "dtor" (func $dtor))
+                        ))
+                    ))
+                    (func $y (param "x" (own $x))
+                        (canon lift (core func $i "x")))
+
+                    (instance (export (interface "foo:foo/user"))
+                        (export "y" (func $y))
+                    )
+                )
+            "#,
+        )?;
+
+        #[derive(Default)]
+        struct MyImports {
+            ctor_hit: bool,
+            drops: usize,
+        }
+
+        use foo::foo::def::X;
+
+        impl foo::foo::def::HostX for MyImports {
+            fn new(&mut self) -> Result<Resource<X>> {
+                self.ctor_hit = true;
+                Ok(Resource::new_own(80))
+            }
+
+            fn drop(&mut self, val: Resource<X>) -> Result<()> {
+                match self.drops {
+                    0 => assert_eq!(val.rep(), 40),
+                    1 => assert_eq!(val.rep(), 80),
+                    _ => unreachable!(),
+                }
+                self.drops += 1;
+                Ok(())
+            }
+        }
+
+        impl foo::foo::def::Host for MyImports {}
+
+        let mut linker = Linker::new(&engine);
+        Resources::add_to_linker(&mut linker, |f: &mut MyImports| f)?;
+        let mut store = Store::new(&engine, MyImports::default());
+        let (one_import, _) = Resources::instantiate(&mut store, &component, &linker)?;
+        one_import
+            .foo_foo_user()
+            .call_y(&mut store, Resource::new_own(40))?;
+        assert!(store.data().ctor_hit);
+        assert_eq!(store.data().drops, 2);
+        Ok(())
+    }
+}
diff --git a/tests/all/component_model/resources.rs b/tests/all/component_model/resources.rs
index e8dd9de4d..24487785c 100644
--- a/tests/all/component_model/resources.rs
+++ b/tests/all/component_model/resources.rs
@@ -37,8 +37,8 @@ fn host_resource_types() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<T>("t", |_, _| {})?;
-    linker.root().resource::<U>("u", |_, _| {})?;
+    linker.root().resource::<T>("t", |_, _| Ok(()))?;
+    linker.root().resource::<U>("u", |_, _| Ok(()))?;
     let i = linker.instantiate(&mut store, &c)?;
     let t1 = i.get_resource(&mut store, "t1").unwrap();
     let t2 = i.get_resource(&mut store, "t2").unwrap();
@@ -367,7 +367,7 @@ fn drop_host_twice() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<MyType>("t", |_, _| {})?;
+    linker.root().resource::<MyType>("t", |_, _| Ok(()))?;
     let i = linker.instantiate(&mut store, &c)?;
     let dtor = i.get_typed_func::<(&Resource<MyType>,), ()>(&mut store, "dtor")?;
 
@@ -435,6 +435,7 @@ fn manually_destroy() -> Result<()> {
         let data: &mut Data = cx.data_mut();
         data.drops += 1;
         data.last_drop = Some(rep);
+        Ok(())
     })?;
     let i = linker.instantiate(&mut store, &c)?;
     let t2_ctor = i.get_typed_func::<(u32,), (ResourceAny,)>(&mut store, "[constructor]t2")?;
@@ -496,7 +497,7 @@ fn dynamic_type() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<MyType>("t1", |_, _| {})?;
+    linker.root().resource::<MyType>("t1", |_, _| Ok(()))?;
     let i = linker.instantiate(&mut store, &c)?;
 
     let a = i.get_func(&mut store, "a").unwrap();
@@ -547,7 +548,7 @@ fn dynamic_val() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<MyType>("t1", |_, _| {})?;
+    linker.root().resource::<MyType>("t1", |_, _| Ok(()))?;
     let i = linker.instantiate(&mut store, &c)?;
 
     let a = i.get_func(&mut store, "a").unwrap();
@@ -664,7 +665,7 @@ fn active_borrows_at_end_of_call() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<MyType>("t", |_, _| {})?;
+    linker.root().resource::<MyType>("t", |_, _| Ok(()))?;
     let i = linker.instantiate(&mut store, &c)?;
 
     let f = i.get_typed_func::<(&Resource<MyType>,), ()>(&mut store, "f")?;
@@ -719,7 +720,7 @@ fn thread_through_borrow() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<MyType>("t", |_, _| {})?;
+    linker.root().resource::<MyType>("t", |_, _| Ok(()))?;
     linker
         .root()
         .func_wrap("f", |_cx, (r,): (Resource<MyType>,)| {
@@ -763,7 +764,7 @@ fn cannot_use_borrow_for_own() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<MyType>("t", |_, _| {})?;
+    linker.root().resource::<MyType>("t", |_, _| Ok(()))?;
     let i = linker.instantiate(&mut store, &c)?;
 
     let f = i.get_typed_func::<(&Resource<MyType>,), (Resource<MyType>,)>(&mut store, "f")?;
@@ -808,7 +809,7 @@ fn passthrough_wrong_type() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<MyType>("t", |_, _| {})?;
+    linker.root().resource::<MyType>("t", |_, _| Ok(()))?;
     linker
         .root()
         .func_wrap("f", |_cx, (r,): (Resource<MyType>,)| Ok((r,)))?;
@@ -848,7 +849,7 @@ fn pass_moved_resource() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<MyType>("t", |_, _| {})?;
+    linker.root().resource::<MyType>("t", |_, _| Ok(()))?;
     let i = linker.instantiate(&mut store, &c)?;
 
     let f = i.get_typed_func::<(&Resource<MyType>, &Resource<MyType>), ()>(&mut store, "f")?;
@@ -979,7 +980,7 @@ fn host_borrow_as_resource_any() -> Result<()> {
     // First test the above component where the host properly drops the argument
     {
         let mut linker = Linker::new(&engine);
-        linker.root().resource::<MyType>("t", |_, _| {})?;
+        linker.root().resource::<MyType>("t", |_, _| Ok(()))?;
         linker
             .root()
             .func_wrap("f", |mut cx, (r,): (ResourceAny,)| {
@@ -997,7 +998,7 @@ fn host_borrow_as_resource_any() -> Result<()> {
     // Then also test the case where the host forgets a drop
     {
         let mut linker = Linker::new(&engine);
-        linker.root().resource::<MyType>("t", |_, _| {})?;
+        linker.root().resource::<MyType>("t", |_, _| Ok(()))?;
         linker.root().func_wrap("f", |_cx, (_r,): (ResourceAny,)| {
             // ... no drop here
             Ok(())
@@ -1106,7 +1107,7 @@ fn pass_host_borrow_to_guest() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<MyType>("t", |_, _| {})?;
+    linker.root().resource::<MyType>("t", |_, _| Ok(()))?;
     let i = linker.instantiate(&mut store, &c)?;
     let take = i.get_typed_func::<(&Resource<MyType>,), ()>(&mut store, "take")?;
 
@@ -1179,7 +1180,7 @@ fn drop_on_owned_resource() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<MyType>("t", |_, _| {})?;
+    linker.root().resource::<MyType>("t", |_, _| Ok(()))?;
     linker.root().func_wrap("[constructor]t", |_cx, ()| {
         Ok((Resource::<MyType>::new_own(300),))
     })?;
@@ -1256,8 +1257,8 @@ fn guest_different_host_same() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    linker.root().resource::<MyType>("t1", |_, _| {})?;
-    linker.root().resource::<MyType>("t2", |_, _| {})?;
+    linker.root().resource::<MyType>("t1", |_, _| Ok(()))?;
+    linker.root().resource::<MyType>("t2", |_, _| Ok(()))?;
     linker.root().func_wrap(
         "f",
         |_cx, (r1, r2): (Resource<MyType>, Resource<MyType>)| {