Repository: bytecodealliance/wasmtime
Pull request #5317 (2022-11-22 19:49:42)

Add a `wasmtime::component::bindgen!` macro
This PR represents merging the `wit-bindgen-gen-host-wasmtime-rust` crate from the `wit-bindgen` repository into this repository. This is made possible by recent infrastructural work around movement of the dependencies involved here, and this is the final process of extracting the macro to this repository. This commit creates a `bindgen!` macro in the `wasmtime::component` submodule. which is the `wit_bindgen_host_wasmtime_rust::generate` macro, more-or-less.

A new `crates/wit-bindgen` folder is created here with a `wasmtime-wit-bindgen` crate which is the current contents of the generation in the `wit-bindgen` repsitory. I opted to not merge the full history here since recent revisions by Pat have removed most of the historical support of lifting/lowering/async/etc so the history isn't really all that interesting. Additionally parts of the `wit-bindgen-core` and various other crates were merged in here and adapted to just work for this wasmtime generator.

This is intended to be the "final" interface for how developers will work with `*.wit` files and Wasmtime. This `bindgen!` macro takes in a `*.wit` file and will, hopefully, forevermore be how this all works. This is all included in the `wasmtime` crate when the `component-model` feature is enabled. Further development of this macro is expected to happen in this repository, not externally. I'll in short order delete the sources in the `wit-bindgen` repository in favor of the sources here.

Tests have been migrated here, mostly the `codegen` ones. Otherwise though the component model is already thoroughly tested so the `runtime/*/host.rs` tests were not migrated. A few small ones have been added to ensure that the generated structure works but otherwise testing is largely left to the already tested embedding API.

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

cc @kubkon

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

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

* kubkon: wasi

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: Of particular note, I've added `cargo vet` entries for the `pulldown-cmark` crate as well as the `unicase` crate pulled in by `wit-parser`.
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>

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index e25b37e05..ac6545cd1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -472,6 +472,14 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "component-macro-test-helpers"
+version = "0.0.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
 [[package]]
 name = "component-test-util"
 version = "0.0.0"
@@ -2235,6 +2243,17 @@ dependencies = [
  "cc",
 ]
 
+[[package]]
+name = "pulldown-cmark"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
+dependencies = [
+ "bitflags",
+ "memchr",
+ "unicase",
+]
+
 [[package]]
 name = "quick-error"
 version = "1.2.3"
@@ -2969,6 +2988,15 @@ version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
 
+[[package]]
+name = "unicase"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+dependencies = [
+ "version_check",
+]
+
 [[package]]
 name = "unicode-bidi"
 version = "0.3.8"
@@ -3496,10 +3524,15 @@ dependencies = [
 name = "wasmtime-component-macro"
 version = "5.0.0"
 dependencies = [
+ "component-macro-test-helpers",
  "proc-macro2",
  "quote",
  "syn",
+ "tracing",
+ "wasmtime",
  "wasmtime-component-util",
+ "wasmtime-wit-bindgen",
+ "wit-parser",
 ]
 
 [[package]]
@@ -3757,6 +3790,14 @@ dependencies = [
  "winch-codegen",
 ]
 
+[[package]]
+name = "wasmtime-wit-bindgen"
+version = "5.0.0"
+dependencies = [
+ "heck",
+ "wit-parser",
+]
+
 [[package]]
 name = "wast"
 version = "35.0.2"
@@ -3986,6 +4027,19 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "wit-parser"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "893834cffb239f88413eead7cf91862a6f24c2233afae15d7808256d8c58f91e"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap",
+ "pulldown-cmark",
+ "unicode-xid",
+]
+
 [[package]]
 name = "witx"
 version = "0.9.1"
diff --git a/Cargo.toml b/Cargo.toml
index 9c5071c44..7f9cf233a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -133,6 +133,7 @@ wasi-tokio = { path = "crates/wasi-common/tokio", version = "=5.0.0" }
 wasi-cap-std-sync = { path = "crates/wasi-common/cap-std-sync", version = "=5.0.0" }
 wasmtime-fuzzing = { path = "crates/fuzzing" }
 wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=5.0.0" }
+wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=5.0.0" }
 
 cranelift-wasm = { path = "cranelift/wasm", version = "0.92.0" }
 cranelift-codegen = { path = "cranelift/codegen", version = "0.92.0" }
@@ -162,6 +163,7 @@ wasmprinter = "0.2.44"
 wasm-encoder = "0.20.0"
 wasm-smith = "0.11.9"
 wasm-mutate = "0.2.12"
+wit-parser = "0.3"
 windows-sys = "0.42.0"
 env_logger = "0.9"
 rustix = "0.36.0"
@@ -179,6 +181,7 @@ tracing = "0.1.26"
 bitflags = "1.2"
 thiserror = "1.0.15"
 async-trait = "0.1.42"
+heck = "0.4"
 
 [features]
 default = [
diff --git a/cranelift/codegen/src/alias_analysis.rs b/cranelift/codegen/src/alias_analysis.rs
index 2e2087b06..f2c1e6e08 100644
--- a/cranelift/codegen/src/alias_analysis.rs
+++ b/cranelift/codegen/src/alias_analysis.rs
@@ -76,7 +76,7 @@ use cranelift_entity::{packed_option::PackedOption, EntityRef};
 /// For a given program point, the vector of last-store instruction
 /// indices for each disjoint category of abstract state.
 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
-struct LastStores {
+pub struct LastStores {
     heap: PackedOption<Inst>,
     table: PackedOption<Inst>,
     vmctx: PackedOption<Inst>,
@@ -179,9 +179,6 @@ struct MemoryLoc {
 
 /// An alias-analysis pass.
 pub struct AliasAnalysis<'a> {
-    /// The function we're analyzing.
-    func: &'a mut Function,
-
     /// The domtree for the function.
     domtree: &'a DominatorTree,
 
@@ -198,23 +195,22 @@ pub struct AliasAnalysis<'a> {
 
 impl<'a> AliasAnalysis<'a> {
     /// Perform an alias analysis pass.
-    pub fn new(func: &'a mut Function, domtree: &'a DominatorTree) -> AliasAnalysis<'a> {
+    pub fn new(func: &Function, domtree: &'a DominatorTree) -> AliasAnalysis<'a> {
         trace!("alias analysis: input is:\n{:?}", func);
         let mut analysis = AliasAnalysis {
-            func,
             domtree,
             block_input: FxHashMap::default(),
             mem_values: FxHashMap::default(),
         };
 
-        analysis.compute_block_input_states();
+        analysis.compute_block_input_states(func);
         analysis
     }
 
-    fn compute_block_input_states(&mut self) {
+    fn compute_block_input_states(&mut self, func: &Function) {
         let mut queue = vec![];
         let mut queue_set = FxHashSet::default();
-        let entry = self.func.layout.entry_block().unwrap();
+        let entry = func.layout.entry_block().unwrap();
         queue.push(entry);
         queue_set.insert(entry);
 
@@ -232,19 +228,13 @@ impl<'a> AliasAnalysis<'a> {
                 state
             );
 
-            for inst in self.func.layout.block_insts(block) {
-                state.update(self.func, inst);
+            for inst in func.layout.block_insts(block) {
+                state.update(func, inst);
                 trace!("after inst{}: state is {:?}", inst.index(), state);
             }
 
-            visit_block_succs(self.func, block, |_inst, succ, _from_table| {
-                let succ_first_inst = self
-                    .func
-                    .layout
-                    .block_insts(succ)
-                    .into_iter()
-                    .next()
-                    .unwrap();
+            visit_block_succs(func, block, |_inst, succ, _from_table| {
+                let succ_first_inst = func.layout.block_insts(succ).into_iter().next().unwrap();
                 let updated = match self.block_input.get_mut(&succ) {
                     Some(succ_state) => {
                         let old = succ_state.clone();
@@ -264,117 +254,145 @@ impl<'a> AliasAnalysis<'a> {
         }
     }
 
-    /// Make a pass and update known-redundant loads to aliased
-    /// values. We interleave the updates with the memory-location
-    /// tracking because resolving some aliases may expose others
-    /// (e.g. in cases of double-indirection with two separate chains
-    /// of loads).
-    pub fn compute_and_update_aliases(&mut self) {
-        let mut pos = FuncCursor::new(self.func);
+    /// Get the starting state for a block.
+    pub fn block_starting_state(&self, block: Block) -> LastStores {
+        self.block_input
+            .get(&block)
+            .cloned()
+            .unwrap_or_else(|| LastStores::default())
+    }
 
-        while let Some(block) = pos.next_block() {
-            let mut state = self
-                .block_input
-                .get(&block)
-                .cloned()
-                .unwrap_or_else(|| LastStores::default());
+    /// Process one instruction. Meant to be invoked in program order
+    /// within a block, and ideally in RPO or at least some domtree
+    /// preorder for maximal reuse.
+    ///
+    /// Returns `true` if instruction was removed.
+    pub fn process_inst(
+        &mut self,
+        func: &mut Function,
+        state: &mut LastStores,
+        inst: Inst,
+    ) -> Option<Value> {
+        trace!(
+            "alias analysis: scanning at inst{} with state {:?} ({:?})",
+            inst.index(),
+            state,
+            func.dfg[inst],
+        );
 
-            while let Some(inst) = pos.next_inst() {
+        let replacing_value = if let Some((address, offset, ty)) = inst_addr_offset_type(func, inst)
+        {
+            let address = func.dfg.resolve_aliases(address);
+            let opcode = func.dfg[inst].opcode();
+
+            if opcode.can_store() {
+                let store_data = inst_store_data(func, inst).unwrap();
+                let store_data = func.dfg.resolve_aliases(store_data);
+                let mem_loc = MemoryLoc {
+                    last_store: inst.into(),
+                    address,
+                    offset,
+                    ty,
+                    extending_opcode: get_ext_opcode(opcode),
+                };
                 trace!(
-                    "alias analysis: scanning at inst{} with state {:?} ({:?})",
+                    "alias analysis: at inst{}: store with data v{} at loc {:?}",
                     inst.index(),
-                    state,
-                    pos.func.dfg[inst],
+                    store_data.index(),
+                    mem_loc
                 );
+                self.mem_values.insert(mem_loc, (inst, store_data));
 
-                if let Some((address, offset, ty)) = inst_addr_offset_type(pos.func, inst) {
-                    let address = pos.func.dfg.resolve_aliases(address);
-                    let opcode = pos.func.dfg[inst].opcode();
+                None
+            } else if opcode.can_load() {
+                let last_store = state.get_last_store(func, inst);
+                let load_result = func.dfg.inst_results(inst)[0];
+                let mem_loc = MemoryLoc {
+                    last_store,
+                    address,
+                    offset,
+                    ty,
+                    extending_opcode: get_ext_opcode(opcode),
+                };
+                trace!(
+                    "alias analysis: at inst{}: load with last_store inst{} at loc {:?}",
+                    inst.index(),
+                    last_store.map(|inst| inst.index()).unwrap_or(usize::MAX),
+                    mem_loc
+                );
 
-                    if opcode.can_store() {
-                        let store_data = inst_store_data(pos.func, inst).unwrap();
-                        let store_data = pos.func.dfg.resolve_aliases(store_data);
-                        let mem_loc = MemoryLoc {
-                            last_store: inst.into(),
-                            address,
-                            offset,
-                            ty,
-                            extending_opcode: get_ext_opcode(opcode),
-                        };
+                // Is there a Value already known to be stored
+                // at this specific memory location?  If so,
+                // we can alias the load result to this
+                // already-known Value.
+                //
+                // Check if the definition dominates this
+                // location; it might not, if it comes from a
+                // load (stores will always dominate though if
+                // their `last_store` survives through
+                // meet-points to this use-site).
+                let aliased =
+                    if let Some((def_inst, value)) = self.mem_values.get(&mem_loc).cloned() {
                         trace!(
-                            "alias analysis: at inst{}: store with data v{} at loc {:?}",
-                            inst.index(),
-                            store_data.index(),
-                            mem_loc
+                            " -> sees known value v{} from inst{}",
+                            value.index(),
+                            def_inst.index()
                         );
-                        self.mem_values.insert(mem_loc, (inst, store_data));
-                    } else if opcode.can_load() {
-                        let last_store = state.get_last_store(pos.func, inst);
-                        let load_result = pos.func.dfg.inst_results(inst)[0];
-                        let mem_loc = MemoryLoc {
-                            last_store,
-                            address,
-                            offset,
-                            ty,
-                            extending_opcode: get_ext_opcode(opcode),
-                        };
-                        trace!(
-                            "alias analysis: at inst{}: load with last_store inst{} at loc {:?}",
-                            inst.index(),
-                            last_store.map(|inst| inst.index()).unwrap_or(usize::MAX),
-                            mem_loc
-                        );
-
-                        // Is there a Value already known to be stored
-                        // at this specific memory location?  If so,
-                        // we can alias the load result to this
-                        // already-known Value.
-                        //
-                        // Check if the definition dominates this
-                        // location; it might not, if it comes from a
-                        // load (stores will always dominate though if
-                        // their `last_store` survives through
-                        // meet-points to this use-site).
-                        let aliased = if let Some((def_inst, value)) =
-                            self.mem_values.get(&mem_loc).cloned()
-                        {
-                            trace!(
-                                " -> sees known value v{} from inst{}",
-                                value.index(),
-                                def_inst.index()
-                            );
-                            if self.domtree.dominates(def_inst, inst, &pos.func.layout) {
-                                trace!(
-                                    " -> dominates; value equiv from v{} to v{} inserted",
-                                    load_result.index(),
-                                    value.index()
-                                );
-
-                                pos.func.dfg.detach_results(inst);
-                                pos.func.dfg.change_to_alias(load_result, value);
-                                pos.remove_inst_and_step_back();
-                                true
-                            } else {
-                                false
-                            }
-                        } else {
-                            false
-                        };
-
-                        // Otherwise, we can keep *this* load around
-                        // as a new equivalent value.
-                        if !aliased {
+                        if self.domtree.dominates(def_inst, inst, &func.layout) {
                             trace!(
-                                " -> inserting load result v{} at loc {:?}",
+                                " -> dominates; value equiv from v{} to v{} inserted",
                                 load_result.index(),
-                                mem_loc
+                                value.index()
                             );
-                            self.mem_values.insert(mem_loc, (inst, load_result));
+                            Some(value)
+                        } else {
+                            None
                         }
-                    }
+                    } else {
+                        None
+                    };
+
+                // Otherwise, we can keep *this* load around
+                // as a new equivalent value.
+                if aliased.is_none() {
+                    trace!(
+                        " -> inserting load result v{} at loc {:?}",
+                        load_result.index(),
+                        mem_loc
+                    );
+                    self.mem_values.insert(mem_loc, (inst, load_result));
                 }
 
-                state.update(pos.func, inst);
+                aliased
+            } else {
+                None
+            }
+        } else {
+            None
+        };
+
+        state.update(func, inst);
+
+        replacing_value
+    }
+
+    /// Make a pass and update known-redundant loads to aliased
+    /// values. We interleave the updates with the memory-location
+    /// tracking because resolving some aliases may expose others
+    /// (e.g. in cases of double-indirection with two separate chains
+    /// of loads).
+    pub fn compute_and_update_aliases(&mut self, func: &mut Function) {
+        let mut pos = FuncCursor::new(func);
+
+        while let Some(block) = pos.next_block() {
+            let mut state = self.block_starting_state(block);
+            while let Some(inst) = pos.next_inst() {
+                if let Some(replaced_result) = self.process_inst(pos.func, &mut state, inst) {
+                    let result = pos.func.dfg.inst_results(inst)[0];
+                    pos.func.dfg.detach_results(inst);
+                    pos.func.dfg.change_to_alias(result, replaced_result);
+                    pos.remove_inst_and_step_back();
+                }
             }
         }
     }
diff --git a/cranelift/codegen/src/context.rs b/cranelift/codegen/src/context.rs
index f7817a967..8a5fed38e 100644
--- a/cranelift/codegen/src/context.rs
+++ b/cranelift/codegen/src/context.rs
@@ -365,8 +365,8 @@ impl Context {
     /// by a store instruction to the same instruction (so-called
     /// "store-to-load forwarding").
     pub fn replace_redundant_loads(&mut self) -> CodegenResult<()> {
-        let mut analysis = AliasAnalysis::new(&mut self.func, &self.domtree);
-        analysis.compute_and_update_aliases();
+        let mut analysis = AliasAnalysis::new(&self.func, &self.domtree);
+        analysis.compute_and_update_aliases(&mut self.func);
         Ok(())
     }
 
diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle
index 439775bec..a33575d0a 100644
--- a/cranelift/codegen/src/isa/riscv64/inst.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst.isle
@@ -10,6 +10,14 @@
       (rd WritableReg)
       (imm Imm20))
 
+    (LoadConst32
+      (rd WritableReg)
+      (imm u32))
+
+    (LoadConst64
+      (rd WritableReg)
+      (imm u64))
+
      (Auipc
       (rd WritableReg)
       (imm Imm20))
@@ -209,11 +217,7 @@
       (rd WritableReg)
       (op ReferenceCheckOP)
       (x Reg))
-    
-    (BrTableCheck
-      (index Reg)
-      (targets_len i32)
-      (default_ BranchTarget))
+
     (BrTable
       (index Reg)
       (tmp1 WritableReg)
diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
index b27916be2..fb7295d1b 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
@@ -635,6 +635,20 @@ impl MachInstEmit for Inst {
                 let x: u32 = 0b0110111 | reg_to_gpr_num(rd.to_reg()) << 7 | (imm.as_u32() << 12);
                 sink.put4(x);
             }
+            &Inst::LoadConst32 { rd, imm } => {
+                let rd = allocs.next_writable(rd);
+                LoadConstant::U32(imm)
+                    .load_constant(rd, &mut |_| rd)
+                    .into_iter()
+                    .for_each(|inst| inst.emit(&[], sink, emit_info, state));
+            }
+            &Inst::LoadConst64 { rd, imm } => {
+                let rd = allocs.next_writable(rd);
+                LoadConstant::U64(imm)
+                    .load_constant(rd, &mut |_| rd)
+                    .into_iter()
+                    .for_each(|inst| inst.emit(&[], sink, emit_info, state));
+            }
             &Inst::FpuRR {
                 frm,
                 alu_op,
@@ -1109,14 +1123,15 @@ impl MachInstEmit for Inst {
                     }
                 }
             }
-            &Inst::BrTableCheck {
+            &Inst::BrTable {
                 index,
-                targets_len,
-                default_,
+                tmp1,
+                ref targets,
             } => {
                 let index = allocs.next(index);
-                // load
-                Inst::load_constant_u32(writable_spilltmp_reg(), targets_len as u64, &mut |_| {
+                let tmp1 = allocs.next_writable(tmp1);
+
+                Inst::load_constant_u32(writable_spilltmp_reg(), targets.len() as u64, &mut |_| {
                     writable_spilltmp_reg()
                 })
                 .iter()
@@ -1133,20 +1148,13 @@ impl MachInstEmit for Inst {
                 .emit(&[], sink, emit_info, state);
                 sink.use_label_at_offset(
                     sink.cur_offset(),
-                    default_.as_label().unwrap(),
+                    targets[0].as_label().unwrap(),
                     LabelUse::PCRel32,
                 );
                 Inst::construct_auipc_and_jalr(None, writable_spilltmp_reg(), 0)
                     .iter()
                     .for_each(|i| i.emit(&[], sink, emit_info, state));
-            }
-            &Inst::BrTable {
-                index,
-                tmp1,
-                ref targets,
-            } => {
-                let index = allocs.next(index);
-                let tmp1 = allocs.next_writable(tmp1);
+
                 let mut insts = SmallInstVec::new();
                 // get current pc.
                 insts.push(Inst::Auipc {
@@ -1175,7 +1183,7 @@ impl MachInstEmit for Inst {
 
                 // here is all the jumps.
                 let mut need_label_use = vec![];
-                for t in targets {
+                for t in targets.iter().skip(1) {
                     need_label_use.push((insts.len(), t.clone()));
                     insts.extend(Inst::construct_auipc_and_jalr(
                         None,
diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
index c3b53bd56..aa6627818 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
@@ -17,7 +17,7 @@ pub use crate::ir::condcodes::FloatCC;
 
 use alloc::vec::Vec;
 use regalloc2::{PRegSet, VReg};
-use smallvec::SmallVec;
+use smallvec::{smallvec, SmallVec};
 use std::boxed::Box;
 use std::string::{String, ToString};
 
@@ -235,7 +235,12 @@ impl Inst {
         alloc_tmp: &mut F,
     ) -> SmallInstVec<Inst> {
         let insts = Inst::load_const_imm(rd, value, alloc_tmp);
-        insts.unwrap_or(LoadConstant::U32(value as u32).load_constant(rd, alloc_tmp))
+        insts.unwrap_or_else(|| {
+            smallvec![Inst::LoadConst32 {
+                rd,
+                imm: value as u32
+            }]
+        })
     }
 
     pub fn load_constant_u64<F: FnMut(Type) -> Writable<Reg>>(
@@ -244,7 +249,7 @@ impl Inst {
         alloc_tmp: &mut F,
     ) -> SmallInstVec<Inst> {
         let insts = Inst::load_const_imm(rd, value, alloc_tmp);
-        insts.unwrap_or(LoadConstant::U64(value).load_constant(rd, alloc_tmp))
+        insts.unwrap_or_else(|| smallvec![Inst::LoadConst64 { rd, imm: value }])
     }
 
     pub(crate) fn construct_auipc_and_jalr(
@@ -337,11 +342,10 @@ fn riscv64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
             collector.reg_use(index);
             collector.reg_early_def(tmp1);
         }
-        &Inst::BrTableCheck { index, .. } => {
-            collector.reg_use(index);
-        }
         &Inst::Auipc { rd, .. } => collector.reg_def(rd),
         &Inst::Lui { rd, .. } => collector.reg_def(rd),
+        &Inst::LoadConst32 { rd, .. } => collector.reg_def(rd),
+        &Inst::LoadConst64 { rd, .. } => collector.reg_def(rd),
         &Inst::AluRRR { rd, rs1, rs2, .. } => {
             collector.reg_use(rs1);
             collector.reg_use(rs2);
@@ -695,9 +699,7 @@ impl MachInst for Inst {
             &Inst::CondBr { .. } => MachTerminator::Cond,
             &Inst::Jalr { .. } => MachTerminator::Uncond,
             &Inst::Ret { .. } => MachTerminator::Ret,
-            // BrTableCheck is a check before BrTable
-            // can lead transfer to default_.
-            &Inst::BrTable { .. } | &Inst::BrTableCheck { .. } => MachTerminator::Indirect,
+            &Inst::BrTable { .. } => MachTerminator::Indirect,
             _ => MachTerminator::None,
         }
     }
@@ -1202,17 +1204,6 @@ impl Inst {
                 let dst = format_regs(&dst[..], allocs);
                 format!("{} {},{},{}##ty={}", op.op_name(), dst, x, y, ty,)
             }
-            &Inst::BrTableCheck {
-                index,
-                targets_len,
-                default_,
-            } => {
-                let index = format_reg(index, allocs);
-                format!(
-                    "br_table_check {}##targets_len={} default_={}",
-                    index, targets_len, default_
-                )
-            }
             &Inst::BrTable {
                 index,
                 tmp1,
@@ -1249,7 +1240,28 @@ impl Inst {
             &Inst::Lui { rd, ref imm } => {
                 format!("{} {},{}", "lui", format_reg(rd.to_reg(), allocs), imm.bits)
             }
+            &Inst::LoadConst32 { rd, imm } => {
+                use std::fmt::Write;
 
+                let rd = format_reg(rd.to_reg(), allocs);
+                let mut buf = String::new();
+                write!(&mut buf, "auipc {},0; ", rd).unwrap();
+                write!(&mut buf, "ld {},12({}); ", rd, rd).unwrap();
+                write!(&mut buf, "j {}; ", Inst::INSTRUCTION_SIZE + 4).unwrap();
+                write!(&mut buf, ".4byte 0x{:x}", imm).unwrap();
+                buf
+            }
+            &Inst::LoadConst64 { rd, imm } => {
+                use std::fmt::Write;
+
+                let rd = format_reg(rd.to_reg(), allocs);
+                let mut buf = String::new();
+                write!(&mut buf, "auipc {},0; ", rd).unwrap();
+                write!(&mut buf, "ld {},12({}); ", rd, rd).unwrap();
+                write!(&mut buf, "j {}; ", Inst::INSTRUCTION_SIZE + 8).unwrap();
+                write!(&mut buf, ".8byte 0x{:x}", imm).unwrap();
+                buf
+            }
             &Inst::AluRRR {
                 alu_op,
                 rd,
diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
index 2ee373257..6b89815f2 100644
--- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
@@ -430,21 +430,15 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
         }
     }
     fn lower_br_table(&mut self, index: Reg, targets: &VecMachLabel) -> InstOutput {
-        let tmp = self.temp_writable_reg(I64);
-        let default_ = BranchTarget::Label(targets[0]);
+        let tmp1 = self.temp_writable_reg(I64);
         let targets: Vec<BranchTarget> = targets
-            .iter()
-            .skip(1)
-            .map(|bix| BranchTarget::Label(*bix))
+            .into_iter()
+            .copied()
+            .map(BranchTarget::Label)
             .collect();
-        self.emit(&MInst::BrTableCheck {
-            index,
-            targets_len: targets.len() as i32,
-            default_,
-        });
         self.emit(&MInst::BrTable {
             index,
-            tmp1: tmp,
+            tmp1,
             targets,
         });
         InstOutput::default()
diff --git a/crates/component-macro/Cargo.toml b/crates/component-macro/Cargo.toml
index 882f49709..ecc319821 100644
--- a/crates/component-macro/Cargo.toml
+++ b/crates/component-macro/Cargo.toml
@@ -12,12 +12,24 @@ edition.workspace = true
 
 [lib]
 proc-macro = true
+test = false
+doctest = false
 
 [dependencies]
 proc-macro2 = "1.0"
 quote = "1.0"
 syn = { version = "1.0", features = ["extra-traits"] }
 wasmtime-component-util = { workspace = true }
+wasmtime-wit-bindgen = { workspace = true }
+wit-parser = { workspace = true }
 
 [badges]
 maintenance = { status = "actively-developed" }
+
+[dev-dependencies]
+wasmtime = { path = '../wasmtime', features = ['component-model'] }
+component-macro-test-helpers = { path = 'test-helpers' }
+tracing = { workspace = true }
+
+[features]
+async = []
diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs
new file mode 100644
index 000000000..b132cfdf1
--- /dev/null
+++ b/crates/component-macro/src/bindgen.rs
@@ -0,0 +1,137 @@
+use proc_macro2::{Span, TokenStream};
+use std::path::{Path, PathBuf};
+use syn::parse::{Error, Parse, ParseStream, Result};
+use syn::punctuated::Punctuated;
+use syn::token;
+use syn::Token;
+use wasmtime_wit_bindgen::Opts;
+use wit_parser::World;
+
+#[derive(Default)]
+pub struct Config {
+    opts: Opts, // ...
+    world: World,
+    files: Vec<String>,
+}
+
+pub fn expand(input: &Config) -> Result<TokenStream> {
+    if !cfg!(feature = "async") && input.opts.async_ {
+        return Err(Error::new(
+            Span::call_site(),
+            "cannot enable async bindings unless `async` crate feature is active",
+        ));
+    }
+
+    let src = input.opts.generate(&input.world);
+    let mut contents = src.parse::<TokenStream>().unwrap();
+
+    // Include a dummy `include_str!` for any files we read so rustc knows that
+    // we depend on the contents of those files.
+    let cwd = std::env::var("CARGO_MANIFEST_DIR").unwrap();
+    for file in input.files.iter() {
+        contents.extend(
+            format!(
+                "const _: &str = include_str!(r#\"{}\"#);\n",
+                Path::new(&cwd).join(file).display()
+            )
+            .parse::<TokenStream>()
+            .unwrap(),
+        );
+    }
+
+    Ok(contents)
+}
+
+impl Parse for Config {
+    fn parse(input: ParseStream<'_>) -> Result<Self> {
+        let call_site = Span::call_site();
+        let mut world = None;
+        let mut ret = Config::default();
+
+        if input.peek(token::Brace) {
+            let content;
+            syn::braced!(content in input);
+            let fields = Punctuated::<Opt, Token![,]>::parse_terminated(&content)?;
+            for field in fields.into_pairs() {
+                match field.into_value() {
+                    Opt::Path(path) => {
+                        if world.is_some() {
+                            return Err(Error::new(path.span(), "cannot specify second world"));
+                        }
+                        world = Some(ret.parse(path)?);
+                    }
+                    Opt::Inline(span, w) => {
+                        if world.is_some() {
+                            return Err(Error::new(span, "cannot specify second world"));
+                        }
+                        world = Some(w);
+                    }
+                    Opt::Tracing(val) => ret.opts.tracing = val,
+                    Opt::Async(val) => ret.opts.async_ = val,
+                }
+            }
+        } else {
+            let s = input.parse::<syn::LitStr>()?;
+            world = Some(ret.parse(s)?);
+        }
+        ret.world = world.ok_or_else(|| {
+            Error::new(
+                call_site,
+                "must specify a `*.wit` file to generate bindings for",
+            )
+        })?;
+        Ok(ret)
+    }
+}
+
+impl Config {
+    fn parse(&mut self, path: syn::LitStr) -> Result<World> {
+        let span = path.span();
+        let path = path.value();
+        let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
+        let path = manifest_dir.join(path);
+        self.files.push(path.to_str().unwrap().to_string());
+        World::parse_file(path).map_err(|e| Error::new(span, e))
+    }
+}
+
+mod kw {
+    syn::custom_keyword!(path);
+    syn::custom_keyword!(inline);
+    syn::custom_keyword!(tracing);
+}
+
+enum Opt {
+    Path(syn::LitStr),
+    Inline(Span, World),
+    Tracing(bool),
+    Async(bool),
+}
+
+impl Parse for Opt {
+    fn parse(input: ParseStream<'_>) -> Result<Self> {
+        let l = input.lookahead1();
+        if l.peek(kw::path) {
+            input.parse::<kw::path>()?;
+            input.parse::<Token![:]>()?;
+            Ok(Opt::Path(input.parse()?))
+        } else if l.peek(kw::inline) {
+            let span = input.parse::<kw::inline>()?.span;
+            input.parse::<Token![:]>()?;
+            let s = input.parse::<syn::LitStr>()?;
+            let world =
+                World::parse("<macro-input>", &s.value()).map_err(|e| Error::new(s.span(), e))?;
+            Ok(Opt::Inline(span, world))
+        } else if l.peek(kw::tracing) {
+            input.parse::<kw::tracing>()?;
+            input.parse::<Token![:]>()?;
+            Ok(Opt::Tracing(input.parse::<syn::LitBool>()?.value))
+        } else if l.peek(Token![async]) {
+            input.parse::<Token![async]>()?;
+            input.parse::<Token![:]>()?;
+            Ok(Opt::Async(input.parse::<syn::LitBool>()?.value))
+        } else {
+            Err(l.error())
+        }
+    }
+}
diff --git a/crates/component-macro/src/component.rs b/crates/component-macro/src/component.rs
new file mode 100644
index 000000000..b87fb6cb3
--- /dev/null
+++ b/crates/component-macro/src/component.rs
@@ -0,0 +1,1191 @@
+use proc_macro2::{Literal, TokenStream, TokenTree};
+use quote::{format_ident, quote};
+use std::collections::HashSet;
+use std::fmt;
+use syn::parse::{Parse, ParseStream};
+use syn::punctuated::Punctuated;
+use syn::{braced, parse_quote, Data, DeriveInput, Error, Result, Token};
+use wasmtime_component_util::{DiscriminantSize, FlagsSize};
+
+#[derive(Debug, Copy, Clone)]
+pub enum VariantStyle {
+    Variant,
+    Enum,
+    Union,
+}
+
+impl fmt::Display for VariantStyle {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(match self {
+            Self::Variant => "variant",
+            Self::Enum => "enum",
+            Self::Union => "union",
+        })
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+enum Style {
+    Record,
+    Variant(VariantStyle),
+}
+
+fn find_style(input: &DeriveInput) -> Result<Style> {
+    let mut style = None;
+
+    for attribute in &input.attrs {
+        if attribute.path.leading_colon.is_some() || attribute.path.segments.len() != 1 {
+            continue;
+        }
+
+        let ident = &attribute.path.segments[0].ident;
+
+        if "component" != &ident.to_string() {
+            continue;
+        }
+
+        let syntax_error = || {
+            Err(Error::new_spanned(
+                &attribute.tokens,
+                "expected `component(<style>)` syntax",
+            ))
+        };
+
+        let style_string = if let [TokenTree::Group(group)] =
+            &attribute.tokens.clone().into_iter().collect::<Vec<_>>()[..]
+        {
+            if let [TokenTree::Ident(style)] = &group.stream().into_iter().collect::<Vec<_>>()[..] {
+                style.to_string()
+            } else {
+                return syntax_error();
+            }
+        } else {
+            return syntax_error();
+        };
+
+        if style.is_some() {
+            return Err(Error::new(ident.span(), "duplicate `component` attribute"));
+        }
+
+        style = Some(match style_string.as_ref() {
+            "record" => Style::Record,
+            "variant" => Style::Variant(VariantStyle::Variant),
+            "enum" => Style::Variant(VariantStyle::Enum),
+            "union" => Style::Variant(VariantStyle::Union),
+            "flags" => {
+                return Err(Error::new_spanned(
+                    &attribute.tokens,
+                    "`flags` not allowed here; \
+                     use `wasmtime::component::flags!` macro to define `flags` types",
+                ))
+            }
+            _ => {
+                return Err(Error::new_spanned(
+                    &attribute.tokens,
+                    "unrecognized component type keyword \
+                     (expected `record`, `variant`, `enum`, or `union`)",
+                ))
+            }
+        });
+    }
+
+    style.ok_or_else(|| Error::new_spanned(input, "missing `component` attribute"))
+}
+
+fn find_rename(attributes: &[syn::Attribute]) -> Result<Option<Literal>> {
+    let mut name = None;
+
+    for attribute in attributes {
+        if attribute.path.leading_colon.is_some() || attribute.path.segments.len() != 1 {
+            continue;
+        }
+
+        let ident = &attribute.path.segments[0].ident;
+
+        if "component" != &ident.to_string() {
+            continue;
+        }
+
+        let syntax_error = || {
+            Err(Error::new_spanned(
+                &attribute.tokens,
+                "expected `component(name = <name literal>)` syntax",
+            ))
+        };
+
+        let name_literal = if let [TokenTree::Group(group)] =
+            &attribute.tokens.clone().into_iter().collect::<Vec<_>>()[..]
+        {
+            match &group.stream().into_iter().collect::<Vec<_>>()[..] {
+                [TokenTree::Ident(key), TokenTree::Punct(op), TokenTree::Literal(literal)]
+                    if "name" == &key.to_string() && '=' == op.as_char() =>
+                {
+                    literal.clone()
+                }
+                _ => return syntax_error(),
+            }
+        } else {
+            return syntax_error();
+        };
+
+        if name.is_some() {
+            return Err(Error::new(ident.span(), "duplicate field rename attribute"));
+        }
+
+        name = Some(name_literal);
+    }
+
+    Ok(name)
+}
+
+fn add_trait_bounds(generics: &syn::Generics, bound: syn::TypeParamBound) -> syn::Generics {
+    let mut generics = generics.clone();
+    for param in &mut generics.params {
+        if let syn::GenericParam::Type(ref mut type_param) = *param {
+            type_param.bounds.push(bound.clone());
+        }
+    }
+    generics
+}
+
+pub struct VariantCase<'a> {
+    attrs: &'a [syn::Attribute],
+    ident: &'a syn::Ident,
+    ty: Option<&'a syn::Type>,
+}
+
+pub trait Expander {
+    fn expand_record(
+        &self,
+        name: &syn::Ident,
+        generics: &syn::Generics,
+        fields: &[&syn::Field],
+    ) -> Result<TokenStream>;
+
+    fn expand_variant(
+        &self,
+        name: &syn::Ident,
+        generics: &syn::Generics,
+        discriminant_size: DiscriminantSize,
+        cases: &[VariantCase],
+        style: VariantStyle,
+    ) -> Result<TokenStream>;
+}
+
+pub fn expand(expander: &dyn Expander, input: &DeriveInput) -> Result<TokenStream> {
+    match find_style(input)? {
+        Style::Record => expand_record(expander, input),
+        Style::Variant(style) => expand_variant(expander, input, style),
+    }
+}
+
+fn expand_record(expander: &dyn Expander, input: &DeriveInput) -> Result<TokenStream> {
+    let name = &input.ident;
+
+    let body = if let Data::Struct(body) = &input.data {
+        body
+    } else {
+        return Err(Error::new(
+            name.span(),
+            "`record` component types can only be derived for Rust `struct`s",
+        ));
+    };
+
+    match &body.fields {
+        syn::Fields::Named(fields) => expander.expand_record(
+            &input.ident,
+            &input.generics,
+            &fields.named.iter().collect::<Vec<_>>(),
+        ),
+
+        syn::Fields::Unnamed(_) | syn::Fields::Unit => Err(Error::new(
+            name.span(),
+            "`record` component types can only be derived for `struct`s with named fields",
+        )),
+    }
+}
+
+fn expand_variant(
+    expander: &dyn Expander,
+    input: &DeriveInput,
+    style: VariantStyle,
+) -> Result<TokenStream> {
+    let name = &input.ident;
+
+    let body = if let Data::Enum(body) = &input.data {
+        body
+    } else {
+        return Err(Error::new(
+            name.span(),
+            format!(
+                "`{}` component types can only be derived for Rust `enum`s",
+                style
+            ),
+        ));
+    };
+
+    if body.variants.is_empty() {
+        return Err(Error::new(
+            name.span(),
+            format!("`{}` component types can only be derived for Rust `enum`s with at least one variant", style),
+        ));
+    }
+
+    let discriminant_size = DiscriminantSize::from_count(body.variants.len()).ok_or_else(|| {
+        Error::new(
+            input.ident.span(),
+            "`enum`s with more than 2^32 variants are not supported",
+        )
+    })?;
+
+    let cases = body
+        .variants
+        .iter()
+        .map(
+            |syn::Variant {
+                 attrs,
+                 ident,
+                 fields,
+                 ..
+             }| {
+                Ok(VariantCase {
+                    attrs,
+                    ident,
+                    ty: match fields {
+                        syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
+                            Some(&fields.unnamed[0].ty)
+                        }
+                        syn::Fields::Unit => None,
+                        _ => {
+                            return Err(Error::new(
+                                name.span(),
+                                format!(
+                                    "`{}` component types can only be derived for Rust `enum`s \
+                                     containing variants with {}",
+                                    style,
+                                    match style {
+                                        VariantStyle::Variant => "at most one unnamed field each",
+                                        VariantStyle::Enum => "no fields",
+                                        VariantStyle::Union => "exactly one unnamed field each",
+                                    }
+                                ),
+                            ))
+                        }
+                    },
+                })
+            },
+        )
+        .collect::<Result<Vec<_>>>()?;
+
+    expander.expand_variant(
+        &input.ident,
+        &input.generics,
+        discriminant_size,
+        &cases,
+        style,
+    )
+}
+
+fn expand_record_for_component_type(
+    name: &syn::Ident,
+    generics: &syn::Generics,
+    fields: &[&syn::Field],
+    typecheck: TokenStream,
+    typecheck_argument: TokenStream,
+) -> Result<TokenStream> {
+    let internal = quote!(wasmtime::component::__internal);
+
+    let mut lower_generic_params = TokenStream::new();
+    let mut lower_generic_args = TokenStream::new();
+    let mut lower_field_declarations = TokenStream::new();
+    let mut abi_list = TokenStream::new();
+    let mut unique_types = HashSet::new();
+
+    for (index, syn::Field { ident, ty, .. }) in fields.iter().enumerate() {
+        let generic = format_ident!("T{}", index);
+
+        lower_generic_params.extend(quote!(#generic: Copy,));
+        lower_generic_args.extend(quote!(<#ty as wasmtime::component::ComponentType>::Lower,));
+
+        lower_field_declarations.extend(quote!(#ident: #generic,));
+
+        abi_list.extend(quote!(
+            <#ty as wasmtime::component::ComponentType>::ABI,
+        ));
+
+        unique_types.insert(ty);
+    }
+
+    let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::ComponentType));
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+    let lower = format_ident!("Lower{}", name);
+
+    // You may wonder why we make the types of all the fields of the #lower struct generic.  This is to work
+    // around the lack of [perfect derive support in
+    // rustc](https://smallcultfollowing.com/babysteps//blog/2022/04/12/implied-bounds-and-perfect-derive/#what-is-perfect-derive)
+    // as of this writing.
+    //
+    // If the struct we're deriving a `ComponentType` impl for has any generic parameters, then #lower needs
+    // generic parameters too.  And if we just copy the parameters and bounds from the impl to #lower, then the
+    // `#[derive(Clone, Copy)]` will fail unless the original generics were declared with those bounds, which
+    // we don't want to require.
+    //
+    // Alternatively, we could just pass the `Lower` associated type of each generic type as arguments to
+    // #lower, but that would require distinguishing between generic and concrete types when generating
+    // #lower_field_declarations, which would require some form of symbol resolution.  That doesn't seem worth
+    // the trouble.
+
+    let expanded = quote! {
+        #[doc(hidden)]
+        #[derive(Clone, Copy)]
+        #[repr(C)]
+        pub struct #lower <#lower_generic_params> {
+            #lower_field_declarations
+            _align: [wasmtime::ValRaw; 0],
+        }
+
+        unsafe impl #impl_generics wasmtime::component::ComponentType for #name #ty_generics #where_clause {
+            type Lower = #lower <#lower_generic_args>;
+
+            const ABI: #internal::CanonicalAbiInfo =
+                #internal::CanonicalAbiInfo::record_static(&[#abi_list]);
+
+            #[inline]
+            fn typecheck(
+                ty: &#internal::InterfaceType,
+                types: &#internal::ComponentTypes,
+            ) -> #internal::anyhow::Result<()> {
+                #internal::#typecheck(ty, types, &[#typecheck_argument])
+            }
+        }
+    };
+
+    Ok(quote!(const _: () = { #expanded };))
+}
+
+fn quote(size: DiscriminantSize, discriminant: usize) -> TokenStream {
+    match size {
+        DiscriminantSize::Size1 => {
+            let discriminant = u8::try_from(discriminant).unwrap();
+            quote!(#discriminant)
+        }
+        DiscriminantSize::Size2 => {
+            let discriminant = u16::try_from(discriminant).unwrap();
+            quote!(#discriminant)
+        }
+        DiscriminantSize::Size4 => {
+            let discriminant = u32::try_from(discriminant).unwrap();
+            quote!(#discriminant)
+        }
+    }
+}
+
+pub struct LiftExpander;
+
+impl Expander for LiftExpander {
+    fn expand_record(
+        &self,
+        name: &syn::Ident,
+        generics: &syn::Generics,
+        fields: &[&syn::Field],
+    ) -> Result<TokenStream> {
+        let internal = quote!(wasmtime::component::__internal);
+
+        let mut lifts = TokenStream::new();
+        let mut loads = TokenStream::new();
+
+        for syn::Field { ident, ty, .. } in fields {
+            lifts.extend(quote!(#ident: <#ty as wasmtime::component::Lift>::lift(
+                store, options, &src.#ident
+            )?,));
+
+            loads.extend(quote!(#ident: <#ty as wasmtime::component::Lift>::load(
+                memory,
+                &bytes
+                    [<#ty as wasmtime::component::ComponentType>::ABI.next_field32_size(&mut offset)..]
+                    [..<#ty as wasmtime::component::ComponentType>::SIZE32]
+            )?,));
+        }
+
+        let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::Lift));
+        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+        let expanded = quote! {
+            unsafe impl #impl_generics wasmtime::component::Lift for #name #ty_generics #where_clause {
+                #[inline]
+                fn lift(
+                    store: &#internal::StoreOpaque,
+                    options: &#internal::Options,
+                    src: &Self::Lower,
+                ) -> #internal::anyhow::Result<Self> {
+                    Ok(Self {
+                        #lifts
+                    })
+                }
+
+                #[inline]
+                fn load(memory: &#internal::Memory, bytes: &[u8]) -> #internal::anyhow::Result<Self> {
+                    debug_assert!(
+                        (bytes.as_ptr() as usize)
+                            % (<Self as wasmtime::component::ComponentType>::ALIGN32 as usize)
+                            == 0
+                    );
+                    let mut offset = 0;
+                    Ok(Self {
+                        #loads
+                    })
+                }
+            }
+        };
+
+        Ok(expanded)
+    }
+
+    fn expand_variant(
+        &self,
+        name: &syn::Ident,
+        generics: &syn::Generics,
+        discriminant_size: DiscriminantSize,
+        cases: &[VariantCase],
+        _style: VariantStyle,
+    ) -> Result<TokenStream> {
+        let internal = quote!(wasmtime::component::__internal);
+
+        let mut lifts = TokenStream::new();
+        let mut loads = TokenStream::new();
+
+        for (index, VariantCase { ident, ty, .. }) in cases.iter().enumerate() {
+            let index_u32 = u32::try_from(index).unwrap();
+
+            let index_quoted = quote(discriminant_size, index);
+
+            if let Some(ty) = ty {
+                lifts.extend(
+                    quote!(#index_u32 => Self::#ident(<#ty as wasmtime::component::Lift>::lift(
+                        store, options, unsafe { &src.payload.#ident }
+                    )?),),
+                );
+
+                loads.extend(
+                    quote!(#index_quoted => Self::#ident(<#ty as wasmtime::component::Lift>::load(
+                        memory, &payload[..<#ty as wasmtime::component::ComponentType>::SIZE32]
+                    )?),),
+                );
+            } else {
+                lifts.extend(quote!(#index_u32 => Self::#ident,));
+
+                loads.extend(quote!(#index_quoted => Self::#ident,));
+            }
+        }
+
+        let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::Lift));
+        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+        let from_bytes = match discriminant_size {
+            DiscriminantSize::Size1 => quote!(bytes[0]),
+            DiscriminantSize::Size2 => quote!(u16::from_le_bytes(bytes[0..2].try_into()?)),
+            DiscriminantSize::Size4 => quote!(u32::from_le_bytes(bytes[0..4].try_into()?)),
+        };
+
+        let expanded = quote! {
+            unsafe impl #impl_generics wasmtime::component::Lift for #name #ty_generics #where_clause {
+                #[inline]
+                fn lift(
+                    store: &#internal::StoreOpaque,
+                    options: &#internal::Options,
+                    src: &Self::Lower,
+                ) -> #internal::anyhow::Result<Self> {
+                    Ok(match src.tag.get_u32() {
+                        #lifts
+                        discrim => #internal::anyhow::bail!("unexpected discriminant: {}", discrim),
+                    })
+                }
+
+                #[inline]
+                fn load(memory: &#internal::Memory, bytes: &[u8]) -> #internal::anyhow::Result<Self> {
+                    let align = <Self as wasmtime::component::ComponentType>::ALIGN32;
+                    debug_assert!((bytes.as_ptr() as usize) % (align as usize) == 0);
+                    let discrim = #from_bytes;
+                    let payload_offset = <Self as #internal::ComponentVariant>::PAYLOAD_OFFSET32;
+                    let payload = &bytes[payload_offset..];
+                    Ok(match discrim {
+                        #loads
+                        discrim => #internal::anyhow::bail!("unexpected discriminant: {}", discrim),
+                    })
+                }
+            }
+        };
+
+        Ok(expanded)
+    }
+}
+
+pub struct LowerExpander;
+
+impl Expander for LowerExpander {
+    fn expand_record(
+        &self,
+        name: &syn::Ident,
+        generics: &syn::Generics,
+        fields: &[&syn::Field],
+    ) -> Result<TokenStream> {
+        let internal = quote!(wasmtime::component::__internal);
+
+        let mut lowers = TokenStream::new();
+        let mut stores = TokenStream::new();
+
+        for syn::Field { ident, ty, .. } in fields {
+            lowers.extend(quote!(wasmtime::component::Lower::lower(
+                &self.#ident, store, options, #internal::map_maybe_uninit!(dst.#ident)
+            )?;));
+
+            stores.extend(quote!(wasmtime::component::Lower::store(
+                &self.#ident,
+                memory,
+                <#ty as wasmtime::component::ComponentType>::ABI.next_field32_size(&mut offset),
+            )?;));
+        }
+
+        let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::Lower));
+        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+        let expanded = quote! {
+            unsafe impl #impl_generics wasmtime::component::Lower for #name #ty_generics #where_clause {
+                #[inline]
+                fn lower<T>(
+                    &self,
+                    store: &mut wasmtime::StoreContextMut<T>,
+                    options: &#internal::Options,
+                    dst: &mut std::mem::MaybeUninit<Self::Lower>,
+                ) -> #internal::anyhow::Result<()> {
+                    #lowers
+                    Ok(())
+                }
+
+                #[inline]
+                fn store<T>(
+                    &self,
+                    memory: &mut #internal::MemoryMut<'_, T>,
+                    mut offset: usize
+                ) -> #internal::anyhow::Result<()> {
+                    debug_assert!(offset % (<Self as wasmtime::component::ComponentType>::ALIGN32 as usize) == 0);
+                    #stores
+                    Ok(())
+                }
+            }
+        };
+
+        Ok(expanded)
+    }
+
+    fn expand_variant(
+        &self,
+        name: &syn::Ident,
+        generics: &syn::Generics,
+        discriminant_size: DiscriminantSize,
+        cases: &[VariantCase],
+        _style: VariantStyle,
+    ) -> Result<TokenStream> {
+        let internal = quote!(wasmtime::component::__internal);
+
+        let mut lowers = TokenStream::new();
+        let mut stores = TokenStream::new();
+
+        for (index, VariantCase { ident, ty, .. }) in cases.iter().enumerate() {
+            let index_u32 = u32::try_from(index).unwrap();
+
+            let index_quoted = quote(discriminant_size, index);
+
+            let discriminant_size = usize::from(discriminant_size);
+
+            let pattern;
+            let lower;
+            let store;
+
+            if ty.is_some() {
+                pattern = quote!(Self::#ident(value));
+                lower = quote!(value.lower(store, options, dst));
+                store = quote!(value.store(
+                    memory,
+                    offset + <Self as #internal::ComponentVariant>::PAYLOAD_OFFSET32,
+                ));
+            } else {
+                pattern = quote!(Self::#ident);
+                lower = quote!(Ok(()));
+                store = quote!(Ok(()));
+            }
+
+            lowers.extend(quote!(#pattern => {
+                #internal::map_maybe_uninit!(dst.tag).write(wasmtime::ValRaw::u32(#index_u32));
+                unsafe {
+                    #internal::lower_payload(
+                        #internal::map_maybe_uninit!(dst.payload),
+                        |payload| #internal::map_maybe_uninit!(payload.#ident),
+                        |dst| #lower,
+                    )
+                }
+            }));
+
+            stores.extend(quote!(#pattern => {
+                *memory.get::<#discriminant_size>(offset) = #index_quoted.to_le_bytes();
+                #store
+            }));
+        }
+
+        let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::Lower));
+        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+        let expanded = quote! {
+            unsafe impl #impl_generics wasmtime::component::Lower for #name #ty_generics #where_clause {
+                #[inline]
+                fn lower<T>(
+                    &self,
+                    store: &mut wasmtime::StoreContextMut<T>,
+                    options: &#internal::Options,
+                    dst: &mut std::mem::MaybeUninit<Self::Lower>,
+                ) -> #internal::anyhow::Result<()> {
+                    match self {
+                        #lowers
+                    }
+                }
+
+                #[inline]
+                fn store<T>(
+                    &self,
+                    memory: &mut #internal::MemoryMut<'_, T>,
+                    mut offset: usize
+                ) -> #internal::anyhow::Result<()> {
+                    debug_assert!(offset % (<Self as wasmtime::component::ComponentType>::ALIGN32 as usize) == 0);
+                    match self {
+                        #stores
+                    }
+                }
+            }
+        };
+
+        Ok(expanded)
+    }
+}
+
+pub struct ComponentTypeExpander;
+
+impl Expander for ComponentTypeExpander {
+    fn expand_record(
+        &self,
+        name: &syn::Ident,
+        generics: &syn::Generics,
+        fields: &[&syn::Field],
+    ) -> Result<TokenStream> {
+        expand_record_for_component_type(
+            name,
+            generics,
+            fields,
+            quote!(typecheck_record),
+            fields
+                .iter()
+                .map(
+                    |syn::Field {
+                         attrs, ident, ty, ..
+                     }| {
+                        let name = find_rename(attrs)?.unwrap_or_else(|| {
+                            Literal::string(&ident.as_ref().unwrap().to_string())
+                        });
+
+                        Ok(quote!((#name, <#ty as wasmtime::component::ComponentType>::typecheck),))
+                    },
+                )
+                .collect::<Result<_>>()?,
+        )
+    }
+
+    fn expand_variant(
+        &self,
+        name: &syn::Ident,
+        generics: &syn::Generics,
+        _discriminant_size: DiscriminantSize,
+        cases: &[VariantCase],
+        style: VariantStyle,
+    ) -> Result<TokenStream> {
+        let internal = quote!(wasmtime::component::__internal);
+
+        let mut case_names_and_checks = TokenStream::new();
+        let mut lower_payload_generic_params = TokenStream::new();
+        let mut lower_payload_generic_args = TokenStream::new();
+        let mut lower_payload_case_declarations = TokenStream::new();
+        let mut lower_generic_args = TokenStream::new();
+        let mut abi_list = TokenStream::new();
+        let mut unique_types = HashSet::new();
+
+        for (index, VariantCase { attrs, ident, ty }) in cases.iter().enumerate() {
+            let rename = find_rename(attrs)?;
+
+            if let (Some(_), VariantStyle::Union) = (&rename, style) {
+                return Err(Error::new(
+                    ident.span(),
+                    "renaming `union` cases is not permitted; only the type is used",
+                ));
+            }
+
+            let name = rename.unwrap_or_else(|| Literal::string(&ident.to_string()));
+
+            if let Some(ty) = ty {
+                abi_list.extend(quote!(Some(<#ty as wasmtime::component::ComponentType>::ABI),));
+
+                case_names_and_checks.extend(match style {
+                    VariantStyle::Variant => {
+                        quote!((#name, Some(<#ty as wasmtime::component::ComponentType>::typecheck)),)
+                    }
+                    VariantStyle::Union => {
+                        quote!(<#ty as wasmtime::component::ComponentType>::typecheck,)
+                    }
+                    VariantStyle::Enum => {
+                        return Err(Error::new(
+                            ident.span(),
+                            "payloads are not permitted for `enum` cases",
+                        ))
+                    }
+                });
+
+                let generic = format_ident!("T{}", index);
+
+                lower_payload_generic_params.extend(quote!(#generic: Copy,));
+                lower_payload_generic_args.extend(quote!(#generic,));
+                lower_payload_case_declarations.extend(quote!(#ident: #generic,));
+                lower_generic_args
+                    .extend(quote!(<#ty as wasmtime::component::ComponentType>::Lower,));
+
+                unique_types.insert(ty);
+            } else {
+                abi_list.extend(quote!(None,));
+                case_names_and_checks.extend(match style {
+                    VariantStyle::Variant => {
+                        quote!((#name, None),)
+                    }
+                    VariantStyle::Union => {
+                        quote!(<() as wasmtime::component::ComponentType>::typecheck,)
+                    }
+                    VariantStyle::Enum => quote!(#name,),
+                });
+                lower_payload_case_declarations.extend(quote!(#ident: [wasmtime::ValRaw; 0],));
+            }
+        }
+
+        let typecheck = match style {
+            VariantStyle::Variant => quote!(typecheck_variant),
+            VariantStyle::Union => quote!(typecheck_union),
+            VariantStyle::Enum => quote!(typecheck_enum),
+        };
+
+        let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::ComponentType));
+        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+        let lower = format_ident!("Lower{}", name);
+        let lower_payload = format_ident!("LowerPayload{}", name);
+
+        // You may wonder why we make the types of all the fields of the #lower struct and #lower_payload union
+        // generic.  This is to work around a [normalization bug in
+        // rustc](https://github.com/rust-lang/rust/issues/90903) such that the compiler does not understand that
+        // e.g. `<i32 as ComponentType>::Lower` is `Copy` despite the bound specified in `ComponentType`'s
+        // definition.
+        //
+        // See also the comment in `Self::expand_record` above for another reason why we do this.
+
+        let expanded = quote! {
+            #[doc(hidden)]
+            #[derive(Clone, Copy)]
+            #[repr(C)]
+            pub struct #lower<#lower_payload_generic_params> {
+                tag: wasmtime::ValRaw,
+                payload: #lower_payload<#lower_payload_generic_args>
+            }
+
+            #[doc(hidden)]
+            #[allow(non_snake_case)]
+            #[derive(Clone, Copy)]
+            #[repr(C)]
+            union #lower_payload<#lower_payload_generic_params> {
+                #lower_payload_case_declarations
+            }
+
+            unsafe impl #impl_generics wasmtime::component::ComponentType for #name #ty_generics #where_clause {
+                type Lower = #lower<#lower_generic_args>;
+
+                #[inline]
+                fn typecheck(
+                    ty: &#internal::InterfaceType,
+                    types: &#internal::ComponentTypes,
+                ) -> #internal::anyhow::Result<()> {
+                    #internal::#typecheck(ty, types, &[#case_names_and_checks])
+                }
+
+                const ABI: #internal::CanonicalAbiInfo =
+                    #internal::CanonicalAbiInfo::variant_static(&[#abi_list]);
+            }
+
+            unsafe impl #impl_generics #internal::ComponentVariant for #name #ty_generics #where_clause {
+                const CASES: &'static [Option<#internal::CanonicalAbiInfo>] = &[#abi_list];
+            }
+        };
+
+        Ok(quote!(const _: () = { #expanded };))
+    }
+}
+
+#[derive(Debug)]
+struct Flag {
+    rename: Option<String>,
+    name: String,
+}
+
+impl Parse for Flag {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let attributes = syn::Attribute::parse_outer(input)?;
+
+        let rename = find_rename(&attributes)?
+            .map(|literal| {
+                let s = literal.to_string();
+
+                s.strip_prefix('"')
+                    .and_then(|s| s.strip_suffix('"'))
+                    .map(|s| s.to_owned())
+                    .ok_or_else(|| Error::new(literal.span(), "expected string literal"))
+            })
+            .transpose()?;
+
+        input.parse::<Token![const]>()?;
+        let name = input.parse::<syn::Ident>()?.to_string();
+
+        Ok(Self { rename, name })
+    }
+}
+
+#[derive(Debug)]
+pub struct Flags {
+    name: String,
+    flags: Vec<Flag>,
+}
+
+impl Parse for Flags {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let name = input.parse::<syn::Ident>()?.to_string();
+
+        let content;
+        braced!(content in input);
+
+        let flags = content
+            .parse_terminated::<_, Token![;]>(Flag::parse)?
+            .into_iter()
+            .collect();
+
+        Ok(Self { name, flags })
+    }
+}
+
+pub fn expand_flags(flags: &Flags) -> Result<TokenStream> {
+    let size = FlagsSize::from_count(flags.flags.len());
+
+    let ty;
+    let eq;
+
+    let count = flags.flags.len();
+
+    match size {
+        FlagsSize::Size0 => {
+            ty = quote!(());
+            eq = quote!(true);
+        }
+        FlagsSize::Size1 => {
+            ty = quote!(u8);
+
+            eq = if count == 8 {
+                quote!(self.__inner0.eq(&rhs.__inner0))
+            } else {
+                let mask = !(0xFF_u8 << count);
+
+                quote!((self.__inner0 & #mask).eq(&(rhs.__inner0 & #mask)))
+            };
+        }
+        FlagsSize::Size2 => {
+            ty = quote!(u16);
+
+            eq = if count == 16 {
+                quote!(self.__inner0.eq(&rhs.__inner0))
+            } else {
+                let mask = !(0xFFFF_u16 << count);
+
+                quote!((self.__inner0 & #mask).eq(&(rhs.__inner0 & #mask)))
+            };
+        }
+        FlagsSize::Size4Plus(n) => {
+            ty = quote!(u32);
+
+            let comparisons = (0..(n - 1))
+                .map(|index| {
+                    let field = format_ident!("__inner{}", index);
+
+                    quote!(self.#field.eq(&rhs.#field) &&)
+                })
+                .collect::<TokenStream>();
+
+            let field = format_ident!("__inner{}", n - 1);
+
+            eq = if count % 32 == 0 {
+                quote!(#comparisons self.#field.eq(&rhs.#field))
+            } else {
+                let mask = !(0xFFFF_FFFF_u32 << (count % 32));
+
+                quote!(#comparisons (self.#field & #mask).eq(&(rhs.#field & #mask)))
+            }
+        }
+    }
+
+    let count;
+    let mut as_array;
+    let mut bitor;
+    let mut bitor_assign;
+    let mut bitand;
+    let mut bitand_assign;
+    let mut bitxor;
+    let mut bitxor_assign;
+    let mut not;
+
+    match size {
+        FlagsSize::Size0 => {
+            count = 0;
+            as_array = quote!([]);
+            bitor = quote!(Self {});
+            bitor_assign = quote!();
+            bitand = quote!(Self {});
+            bitand_assign = quote!();
+            bitxor = quote!(Self {});
+            bitxor_assign = quote!();
+            not = quote!(Self {});
+        }
+        FlagsSize::Size1 | FlagsSize::Size2 => {
+            count = 1;
+            as_array = quote!([self.__inner0 as u32]);
+            bitor = quote!(Self {
+                __inner0: self.__inner0.bitor(rhs.__inner0)
+            });
+            bitor_assign = quote!(self.__inner0.bitor_assign(rhs.__inner0));
+            bitand = quote!(Self {
+                __inner0: self.__inner0.bitand(rhs.__inner0)
+            });
+            bitand_assign = quote!(self.__inner0.bitand_assign(rhs.__inner0));
+            bitxor = quote!(Self {
+                __inner0: self.__inner0.bitxor(rhs.__inner0)
+            });
+            bitxor_assign = quote!(self.__inner0.bitxor_assign(rhs.__inner0));
+            not = quote!(Self {
+                __inner0: self.__inner0.not()
+            });
+        }
+        FlagsSize::Size4Plus(n) => {
+            count = usize::from(n);
+            as_array = TokenStream::new();
+            bitor = TokenStream::new();
+            bitor_assign = TokenStream::new();
+            bitand = TokenStream::new();
+            bitand_assign = TokenStream::new();
+            bitxor = TokenStream::new();
+            bitxor_assign = TokenStream::new();
+            not = TokenStream::new();
+
+            for index in 0..n {
+                let field = format_ident!("__inner{}", index);
+
+                as_array.extend(quote!(self.#field,));
+                bitor.extend(quote!(#field: self.#field.bitor(rhs.#field),));
+                bitor_assign.extend(quote!(self.#field.bitor_assign(rhs.#field);));
+                bitand.extend(quote!(#field: self.#field.bitand(rhs.#field),));
+                bitand_assign.extend(quote!(self.#field.bitand_assign(rhs.#field);));
+                bitxor.extend(quote!(#field: self.#field.bitxor(rhs.#field),));
+                bitxor_assign.extend(quote!(self.#field.bitxor_assign(rhs.#field);));
+                not.extend(quote!(#field: self.#field.not(),));
+            }
+
+            as_array = quote!([#as_array]);
+            bitor = quote!(Self { #bitor });
+            bitand = quote!(Self { #bitand });
+            bitxor = quote!(Self { #bitxor });
+            not = quote!(Self { #not });
+        }
+    };
+
+    let name = format_ident!("{}", flags.name);
+
+    let mut constants = TokenStream::new();
+    let mut rust_names = TokenStream::new();
+    let mut component_names = TokenStream::new();
+
+    for (index, Flag { name, rename }) in flags.flags.iter().enumerate() {
+        rust_names.extend(quote!(#name,));
+
+        let component_name = rename.as_ref().unwrap_or(name);
+        component_names.extend(quote!(#component_name,));
+
+        let fields = match size {
+            FlagsSize::Size0 => quote!(),
+            FlagsSize::Size1 => {
+                let init = 1_u8 << index;
+                quote!(__inner0: #init)
+            }
+            FlagsSize::Size2 => {
+                let init = 1_u16 << index;
+                quote!(__inner0: #init)
+            }
+            FlagsSize::Size4Plus(n) => (0..n)
+                .map(|i| {
+                    let field = format_ident!("__inner{}", i);
+
+                    let init = if index / 32 == usize::from(i) {
+                        1_u32 << (index % 32)
+                    } else {
+                        0
+                    };
+
+                    quote!(#field: #init,)
+                })
+                .collect::<TokenStream>(),
+        };
+
+        let name = format_ident!("{}", name);
+
+        constants.extend(quote!(pub const #name: Self = Self { #fields };));
+    }
+
+    let generics = syn::Generics {
+        lt_token: None,
+        params: Punctuated::new(),
+        gt_token: None,
+        where_clause: None,
+    };
+
+    let fields = {
+        let ty = syn::parse2::<syn::Type>(ty.clone())?;
+
+        (0..count)
+            .map(|index| syn::Field {
+                attrs: Vec::new(),
+                vis: syn::Visibility::Inherited,
+                ident: Some(format_ident!("__inner{}", index)),
+                colon_token: None,
+                ty: ty.clone(),
+            })
+            .collect::<Vec<_>>()
+    };
+
+    let fields = fields.iter().collect::<Vec<_>>();
+
+    let component_type_impl = expand_record_for_component_type(
+        &name,
+        &generics,
+        &fields,
+        quote!(typecheck_flags),
+        component_names,
+    )?;
+
+    let lower_impl = LowerExpander.expand_record(&name, &generics, &fields)?;
+
+    let lift_impl = LiftExpander.expand_record(&name, &generics, &fields)?;
+
+    let internal = quote!(wasmtime::component::__internal);
+
+    let fields = fields
+        .iter()
+        .map(|syn::Field { ident, .. }| quote!(#[doc(hidden)] #ident: #ty,))
+        .collect::<TokenStream>();
+
+    let expanded = quote! {
+        #[derive(Copy, Clone, Default)]
+        pub struct #name { #fields }
+
+        impl #name {
+            #constants
+
+            pub fn as_array(&self) -> [u32; #count] {
+                #as_array
+            }
+
+            pub fn empty() -> Self {
+                Self::default()
+            }
+
+            pub fn all() -> Self {
+                use std::ops::Not;
+                Self::default().not()
+            }
+        }
+
+        impl std::cmp::PartialEq for #name {
+            fn eq(&self, rhs: &#name) -> bool {
+                #eq
+            }
+        }
+
+        impl std::cmp::Eq for #name { }
+
+        impl std::fmt::Debug for #name {
+            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                #internal::format_flags(&self.as_array(), &[#rust_names], f)
+            }
+        }
+
+        impl std::ops::BitOr for #name {
+            type Output = #name;
+
+            fn bitor(self, rhs: #name) -> #name {
+                #bitor
+            }
+        }
+
+        impl std::ops::BitOrAssign for #name {
+            fn bitor_assign(&mut self, rhs: #name) {
+                #bitor_assign
+            }
+        }
+
+        impl std::ops::BitAnd for #name {
+            type Output = #name;
+
+            fn bitand(self, rhs: #name) -> #name {
+                #bitand
+            }
+        }
+
+        impl std::ops::BitAndAssign for #name {
+            fn bitand_assign(&mut self, rhs: #name) {
+                #bitand_assign
+            }
+        }
+
+        impl std::ops::BitXor for #name {
+            type Output = #name;
+
+            fn bitxor(self, rhs: #name) -> #name {
+                #bitxor
+            }
+        }
+
+        impl std::ops::BitXorAssign for #name {
+            fn bitxor_assign(&mut self, rhs: #name) {
+                #bitxor_assign
+            }
+        }
+
+        impl std::ops::Not for #name {
+            type Output = #name;
+
+            fn not(self) -> #name {
+                #not
+            }
+        }
+
+        #component_type_impl
+
+        #lower_impl
+
+        #lift_impl
+    };
+
+    Ok(expanded)
+}
diff --git a/crates/component-macro/src/lib.rs b/crates/component-macro/src/lib.rs
index 0524ef260..d8026c31d 100644
--- a/crates/component-macro/src/lib.rs
+++ b/crates/component-macro/src/lib.rs
@@ -1,1222 +1,48 @@
-use proc_macro2::{Literal, TokenStream, TokenTree};
-use quote::{format_ident, quote};
-use std::collections::HashSet;
-use std::fmt;
-use syn::parse::{Parse, ParseStream};
-use syn::punctuated::Punctuated;
-use syn::{braced, parse_macro_input, parse_quote, Data, DeriveInput, Error, Result, Token};
-use wasmtime_component_util::{DiscriminantSize, FlagsSize};
+use syn::{parse_macro_input, DeriveInput, Error};
 
-#[derive(Debug, Copy, Clone)]
-enum VariantStyle {
-    Variant,
-    Enum,
-    Union,
-}
-
-impl fmt::Display for VariantStyle {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(match self {
-            Self::Variant => "variant",
-            Self::Enum => "enum",
-            Self::Union => "union",
-        })
-    }
-}
-
-#[derive(Debug, Copy, Clone)]
-enum Style {
-    Record,
-    Variant(VariantStyle),
-}
-
-fn find_style(input: &DeriveInput) -> Result<Style> {
-    let mut style = None;
-
-    for attribute in &input.attrs {
-        if attribute.path.leading_colon.is_some() || attribute.path.segments.len() != 1 {
-            continue;
-        }
-
-        let ident = &attribute.path.segments[0].ident;
-
-        if "component" != &ident.to_string() {
-            continue;
-        }
-
-        let syntax_error = || {
-            Err(Error::new_spanned(
-                &attribute.tokens,
-                "expected `component(<style>)` syntax",
-            ))
-        };
-
-        let style_string = if let [TokenTree::Group(group)] =
-            &attribute.tokens.clone().into_iter().collect::<Vec<_>>()[..]
-        {
-            if let [TokenTree::Ident(style)] = &group.stream().into_iter().collect::<Vec<_>>()[..] {
-                style.to_string()
-            } else {
-                return syntax_error();
-            }
-        } else {
-            return syntax_error();
-        };
-
-        if style.is_some() {
-            return Err(Error::new(ident.span(), "duplicate `component` attribute"));
-        }
-
-        style = Some(match style_string.as_ref() {
-            "record" => Style::Record,
-            "variant" => Style::Variant(VariantStyle::Variant),
-            "enum" => Style::Variant(VariantStyle::Enum),
-            "union" => Style::Variant(VariantStyle::Union),
-            "flags" => {
-                return Err(Error::new_spanned(
-                    &attribute.tokens,
-                    "`flags` not allowed here; \
-                     use `wasmtime::component::flags!` macro to define `flags` types",
-                ))
-            }
-            _ => {
-                return Err(Error::new_spanned(
-                    &attribute.tokens,
-                    "unrecognized component type keyword \
-                     (expected `record`, `variant`, `enum`, or `union`)",
-                ))
-            }
-        });
-    }
-
-    style.ok_or_else(|| Error::new_spanned(input, "missing `component` attribute"))
-}
-
-fn find_rename(attributes: &[syn::Attribute]) -> Result<Option<Literal>> {
-    let mut name = None;
-
-    for attribute in attributes {
-        if attribute.path.leading_colon.is_some() || attribute.path.segments.len() != 1 {
-            continue;
-        }
-
-        let ident = &attribute.path.segments[0].ident;
-
-        if "component" != &ident.to_string() {
-            continue;
-        }
-
-        let syntax_error = || {
-            Err(Error::new_spanned(
-                &attribute.tokens,
-                "expected `component(name = <name literal>)` syntax",
-            ))
-        };
-
-        let name_literal = if let [TokenTree::Group(group)] =
-            &attribute.tokens.clone().into_iter().collect::<Vec<_>>()[..]
-        {
-            match &group.stream().into_iter().collect::<Vec<_>>()[..] {
-                [TokenTree::Ident(key), TokenTree::Punct(op), TokenTree::Literal(literal)]
-                    if "name" == &key.to_string() && '=' == op.as_char() =>
-                {
-                    literal.clone()
-                }
-                _ => return syntax_error(),
-            }
-        } else {
-            return syntax_error();
-        };
-
-        if name.is_some() {
-            return Err(Error::new(ident.span(), "duplicate field rename attribute"));
-        }
-
-        name = Some(name_literal);
-    }
-
-    Ok(name)
-}
-
-fn add_trait_bounds(generics: &syn::Generics, bound: syn::TypeParamBound) -> syn::Generics {
-    let mut generics = generics.clone();
-    for param in &mut generics.params {
-        if let syn::GenericParam::Type(ref mut type_param) = *param {
-            type_param.bounds.push(bound.clone());
-        }
-    }
-    generics
-}
-
-struct VariantCase<'a> {
-    attrs: &'a [syn::Attribute],
-    ident: &'a syn::Ident,
-    ty: Option<&'a syn::Type>,
-}
-
-trait Expander {
-    fn expand_record(
-        &self,
-        name: &syn::Ident,
-        generics: &syn::Generics,
-        fields: &[&syn::Field],
-    ) -> Result<TokenStream>;
-
-    fn expand_variant(
-        &self,
-        name: &syn::Ident,
-        generics: &syn::Generics,
-        discriminant_size: DiscriminantSize,
-        cases: &[VariantCase],
-        style: VariantStyle,
-    ) -> Result<TokenStream>;
-}
-
-fn expand(expander: &dyn Expander, input: &DeriveInput) -> Result<TokenStream> {
-    match find_style(input)? {
-        Style::Record => expand_record(expander, input),
-        Style::Variant(style) => expand_variant(expander, input, style),
-    }
-}
-
-fn expand_record(expander: &dyn Expander, input: &DeriveInput) -> Result<TokenStream> {
-    let name = &input.ident;
-
-    let body = if let Data::Struct(body) = &input.data {
-        body
-    } else {
-        return Err(Error::new(
-            name.span(),
-            "`record` component types can only be derived for Rust `struct`s",
-        ));
-    };
-
-    match &body.fields {
-        syn::Fields::Named(fields) => expander.expand_record(
-            &input.ident,
-            &input.generics,
-            &fields.named.iter().collect::<Vec<_>>(),
-        ),
-
-        syn::Fields::Unnamed(_) | syn::Fields::Unit => Err(Error::new(
-            name.span(),
-            "`record` component types can only be derived for `struct`s with named fields",
-        )),
-    }
-}
-
-fn expand_variant(
-    expander: &dyn Expander,
-    input: &DeriveInput,
-    style: VariantStyle,
-) -> Result<TokenStream> {
-    let name = &input.ident;
-
-    let body = if let Data::Enum(body) = &input.data {
-        body
-    } else {
-        return Err(Error::new(
-            name.span(),
-            format!(
-                "`{}` component types can only be derived for Rust `enum`s",
-                style
-            ),
-        ));
-    };
-
-    if body.variants.is_empty() {
-        return Err(Error::new(
-            name.span(),
-            format!("`{}` component types can only be derived for Rust `enum`s with at least one variant", style),
-        ));
-    }
-
-    let discriminant_size = DiscriminantSize::from_count(body.variants.len()).ok_or_else(|| {
-        Error::new(
-            input.ident.span(),
-            "`enum`s with more than 2^32 variants are not supported",
-        )
-    })?;
-
-    let cases = body
-        .variants
-        .iter()
-        .map(
-            |syn::Variant {
-                 attrs,
-                 ident,
-                 fields,
-                 ..
-             }| {
-                Ok(VariantCase {
-                    attrs,
-                    ident,
-                    ty: match fields {
-                        syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
-                            Some(&fields.unnamed[0].ty)
-                        }
-                        syn::Fields::Unit => None,
-                        _ => {
-                            return Err(Error::new(
-                                name.span(),
-                                format!(
-                                    "`{}` component types can only be derived for Rust `enum`s \
-                                     containing variants with {}",
-                                    style,
-                                    match style {
-                                        VariantStyle::Variant => "at most one unnamed field each",
-                                        VariantStyle::Enum => "no fields",
-                                        VariantStyle::Union => "exactly one unnamed field each",
-                                    }
-                                ),
-                            ))
-                        }
-                    },
-                })
-            },
-        )
-        .collect::<Result<Vec<_>>>()?;
-
-    expander.expand_variant(
-        &input.ident,
-        &input.generics,
-        discriminant_size,
-        &cases,
-        style,
-    )
-}
-
-fn expand_record_for_component_type(
-    name: &syn::Ident,
-    generics: &syn::Generics,
-    fields: &[&syn::Field],
-    typecheck: TokenStream,
-    typecheck_argument: TokenStream,
-) -> Result<TokenStream> {
-    let internal = quote!(wasmtime::component::__internal);
-
-    let mut lower_generic_params = TokenStream::new();
-    let mut lower_generic_args = TokenStream::new();
-    let mut lower_field_declarations = TokenStream::new();
-    let mut abi_list = TokenStream::new();
-    let mut unique_types = HashSet::new();
-
-    for (index, syn::Field { ident, ty, .. }) in fields.iter().enumerate() {
-        let generic = format_ident!("T{}", index);
-
-        lower_generic_params.extend(quote!(#generic: Copy,));
-        lower_generic_args.extend(quote!(<#ty as wasmtime::component::ComponentType>::Lower,));
-
-        lower_field_declarations.extend(quote!(#ident: #generic,));
-
-        abi_list.extend(quote!(
-            <#ty as wasmtime::component::ComponentType>::ABI,
-        ));
-
-        unique_types.insert(ty);
-    }
-
-    let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::ComponentType));
-    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
-    let lower = format_ident!("Lower{}", name);
-
-    // You may wonder why we make the types of all the fields of the #lower struct generic.  This is to work
-    // around the lack of [perfect derive support in
-    // rustc](https://smallcultfollowing.com/babysteps//blog/2022/04/12/implied-bounds-and-perfect-derive/#what-is-perfect-derive)
-    // as of this writing.
-    //
-    // If the struct we're deriving a `ComponentType` impl for has any generic parameters, then #lower needs
-    // generic parameters too.  And if we just copy the parameters and bounds from the impl to #lower, then the
-    // `#[derive(Clone, Copy)]` will fail unless the original generics were declared with those bounds, which
-    // we don't want to require.
-    //
-    // Alternatively, we could just pass the `Lower` associated type of each generic type as arguments to
-    // #lower, but that would require distinguishing between generic and concrete types when generating
-    // #lower_field_declarations, which would require some form of symbol resolution.  That doesn't seem worth
-    // the trouble.
-
-    let expanded = quote! {
-        #[doc(hidden)]
-        #[derive(Clone, Copy)]
-        #[repr(C)]
-        pub struct #lower <#lower_generic_params> {
-            #lower_field_declarations
-            _align: [wasmtime::ValRaw; 0],
-        }
-
-        unsafe impl #impl_generics wasmtime::component::ComponentType for #name #ty_generics #where_clause {
-            type Lower = #lower <#lower_generic_args>;
-
-            const ABI: #internal::CanonicalAbiInfo =
-                #internal::CanonicalAbiInfo::record_static(&[#abi_list]);
-
-            #[inline]
-            fn typecheck(
-                ty: &#internal::InterfaceType,
-                types: &#internal::ComponentTypes,
-            ) -> #internal::anyhow::Result<()> {
-                #internal::#typecheck(ty, types, &[#typecheck_argument])
-            }
-        }
-    };
-
-    Ok(quote!(const _: () = { #expanded };))
-}
-
-fn quote(size: DiscriminantSize, discriminant: usize) -> TokenStream {
-    match size {
-        DiscriminantSize::Size1 => {
-            let discriminant = u8::try_from(discriminant).unwrap();
-            quote!(#discriminant)
-        }
-        DiscriminantSize::Size2 => {
-            let discriminant = u16::try_from(discriminant).unwrap();
-            quote!(#discriminant)
-        }
-        DiscriminantSize::Size4 => {
-            let discriminant = u32::try_from(discriminant).unwrap();
-            quote!(#discriminant)
-        }
-    }
-}
+mod bindgen;
+mod component;
 
 #[proc_macro_derive(Lift, attributes(component))]
 pub fn lift(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    expand(&LiftExpander, &parse_macro_input!(input as DeriveInput))
-        .unwrap_or_else(Error::into_compile_error)
-        .into()
-}
-
-struct LiftExpander;
-
-impl Expander for LiftExpander {
-    fn expand_record(
-        &self,
-        name: &syn::Ident,
-        generics: &syn::Generics,
-        fields: &[&syn::Field],
-    ) -> Result<TokenStream> {
-        let internal = quote!(wasmtime::component::__internal);
-
-        let mut lifts = TokenStream::new();
-        let mut loads = TokenStream::new();
-
-        for syn::Field { ident, ty, .. } in fields {
-            lifts.extend(quote!(#ident: <#ty as wasmtime::component::Lift>::lift(
-                store, options, &src.#ident
-            )?,));
-
-            loads.extend(quote!(#ident: <#ty as wasmtime::component::Lift>::load(
-                memory,
-                &bytes
-                    [<#ty as wasmtime::component::ComponentType>::ABI.next_field32_size(&mut offset)..]
-                    [..<#ty as wasmtime::component::ComponentType>::SIZE32]
-            )?,));
-        }
-
-        let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::Lift));
-        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
-
-        let expanded = quote! {
-            unsafe impl #impl_generics wasmtime::component::Lift for #name #ty_generics #where_clause {
-                #[inline]
-                fn lift(
-                    store: &#internal::StoreOpaque,
-                    options: &#internal::Options,
-                    src: &Self::Lower,
-                ) -> #internal::anyhow::Result<Self> {
-                    Ok(Self {
-                        #lifts
-                    })
-                }
-
-                #[inline]
-                fn load(memory: &#internal::Memory, bytes: &[u8]) -> #internal::anyhow::Result<Self> {
-                    debug_assert!(
-                        (bytes.as_ptr() as usize)
-                            % (<Self as wasmtime::component::ComponentType>::ALIGN32 as usize)
-                            == 0
-                    );
-                    let mut offset = 0;
-                    Ok(Self {
-                        #loads
-                    })
-                }
-            }
-        };
-
-        Ok(expanded)
-    }
-
-    fn expand_variant(
-        &self,
-        name: &syn::Ident,
-        generics: &syn::Generics,
-        discriminant_size: DiscriminantSize,
-        cases: &[VariantCase],
-        _style: VariantStyle,
-    ) -> Result<TokenStream> {
-        let internal = quote!(wasmtime::component::__internal);
-
-        let mut lifts = TokenStream::new();
-        let mut loads = TokenStream::new();
-
-        for (index, VariantCase { ident, ty, .. }) in cases.iter().enumerate() {
-            let index_u32 = u32::try_from(index).unwrap();
-
-            let index_quoted = quote(discriminant_size, index);
-
-            if let Some(ty) = ty {
-                lifts.extend(
-                    quote!(#index_u32 => Self::#ident(<#ty as wasmtime::component::Lift>::lift(
-                        store, options, unsafe { &src.payload.#ident }
-                    )?),),
-                );
-
-                loads.extend(
-                    quote!(#index_quoted => Self::#ident(<#ty as wasmtime::component::Lift>::load(
-                        memory, &payload[..<#ty as wasmtime::component::ComponentType>::SIZE32]
-                    )?),),
-                );
-            } else {
-                lifts.extend(quote!(#index_u32 => Self::#ident,));
-
-                loads.extend(quote!(#index_quoted => Self::#ident,));
-            }
-        }
-
-        let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::Lift));
-        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
-
-        let from_bytes = match discriminant_size {
-            DiscriminantSize::Size1 => quote!(bytes[0]),
-            DiscriminantSize::Size2 => quote!(u16::from_le_bytes(bytes[0..2].try_into()?)),
-            DiscriminantSize::Size4 => quote!(u32::from_le_bytes(bytes[0..4].try_into()?)),
-        };
-
-        let expanded = quote! {
-            unsafe impl #impl_generics wasmtime::component::Lift for #name #ty_generics #where_clause {
-                #[inline]
-                fn lift(
-                    store: &#internal::StoreOpaque,
-                    options: &#internal::Options,
-                    src: &Self::Lower,
-                ) -> #internal::anyhow::Result<Self> {
-                    Ok(match src.tag.get_u32() {
-                        #lifts
-                        discrim => #internal::anyhow::bail!("unexpected discriminant: {}", discrim),
-                    })
-                }
-
-                #[inline]
-                fn load(memory: &#internal::Memory, bytes: &[u8]) -> #internal::anyhow::Result<Self> {
-                    let align = <Self as wasmtime::component::ComponentType>::ALIGN32;
-                    debug_assert!((bytes.as_ptr() as usize) % (align as usize) == 0);
-                    let discrim = #from_bytes;
-                    let payload_offset = <Self as #internal::ComponentVariant>::PAYLOAD_OFFSET32;
-                    let payload = &bytes[payload_offset..];
-                    Ok(match discrim {
-                        #loads
-                        discrim => #internal::anyhow::bail!("unexpected discriminant: {}", discrim),
-                    })
-                }
-            }
-        };
-
-        Ok(expanded)
-    }
+    component::expand(
+        &component::LiftExpander,
+        &parse_macro_input!(input as DeriveInput),
+    )
+    .unwrap_or_else(Error::into_compile_error)
+    .into()
 }
 
 #[proc_macro_derive(Lower, attributes(component))]
 pub fn lower(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    expand(&LowerExpander, &parse_macro_input!(input as DeriveInput))
-        .unwrap_or_else(Error::into_compile_error)
-        .into()
-}
-
-struct LowerExpander;
-
-impl Expander for LowerExpander {
-    fn expand_record(
-        &self,
-        name: &syn::Ident,
-        generics: &syn::Generics,
-        fields: &[&syn::Field],
-    ) -> Result<TokenStream> {
-        let internal = quote!(wasmtime::component::__internal);
-
-        let mut lowers = TokenStream::new();
-        let mut stores = TokenStream::new();
-
-        for syn::Field { ident, ty, .. } in fields {
-            lowers.extend(quote!(wasmtime::component::Lower::lower(
-                &self.#ident, store, options, #internal::map_maybe_uninit!(dst.#ident)
-            )?;));
-
-            stores.extend(quote!(wasmtime::component::Lower::store(
-                &self.#ident,
-                memory,
-                <#ty as wasmtime::component::ComponentType>::ABI.next_field32_size(&mut offset),
-            )?;));
-        }
-
-        let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::Lower));
-        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
-
-        let expanded = quote! {
-            unsafe impl #impl_generics wasmtime::component::Lower for #name #ty_generics #where_clause {
-                #[inline]
-                fn lower<T>(
-                    &self,
-                    store: &mut wasmtime::StoreContextMut<T>,
-                    options: &#internal::Options,
-                    dst: &mut std::mem::MaybeUninit<Self::Lower>,
-                ) -> #internal::anyhow::Result<()> {
-                    #lowers
-                    Ok(())
-                }
-
-                #[inline]
-                fn store<T>(
-                    &self,
-                    memory: &mut #internal::MemoryMut<'_, T>,
-                    mut offset: usize
-                ) -> #internal::anyhow::Result<()> {
-                    debug_assert!(offset % (<Self as wasmtime::component::ComponentType>::ALIGN32 as usize) == 0);
-                    #stores
-                    Ok(())
-                }
-            }
-        };
-
-        Ok(expanded)
-    }
-
-    fn expand_variant(
-        &self,
-        name: &syn::Ident,
-        generics: &syn::Generics,
-        discriminant_size: DiscriminantSize,
-        cases: &[VariantCase],
-        _style: VariantStyle,
-    ) -> Result<TokenStream> {
-        let internal = quote!(wasmtime::component::__internal);
-
-        let mut lowers = TokenStream::new();
-        let mut stores = TokenStream::new();
-
-        for (index, VariantCase { ident, ty, .. }) in cases.iter().enumerate() {
-            let index_u32 = u32::try_from(index).unwrap();
-
-            let index_quoted = quote(discriminant_size, index);
-
-            let discriminant_size = usize::from(discriminant_size);
-
-            let pattern;
-            let lower;
-            let store;
-
-            if ty.is_some() {
-                pattern = quote!(Self::#ident(value));
-                lower = quote!(value.lower(store, options, dst));
-                store = quote!(value.store(
-                    memory,
-                    offset + <Self as #internal::ComponentVariant>::PAYLOAD_OFFSET32,
-                ));
-            } else {
-                pattern = quote!(Self::#ident);
-                lower = quote!(Ok(()));
-                store = quote!(Ok(()));
-            }
-
-            lowers.extend(quote!(#pattern => {
-                #internal::map_maybe_uninit!(dst.tag).write(wasmtime::ValRaw::u32(#index_u32));
-                unsafe {
-                    #internal::lower_payload(
-                        #internal::map_maybe_uninit!(dst.payload),
-                        |payload| #internal::map_maybe_uninit!(payload.#ident),
-                        |dst| #lower,
-                    )
-                }
-            }));
-
-            stores.extend(quote!(#pattern => {
-                *memory.get::<#discriminant_size>(offset) = #index_quoted.to_le_bytes();
-                #store
-            }));
-        }
-
-        let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::Lower));
-        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
-
-        let expanded = quote! {
-            unsafe impl #impl_generics wasmtime::component::Lower for #name #ty_generics #where_clause {
-                #[inline]
-                fn lower<T>(
-                    &self,
-                    store: &mut wasmtime::StoreContextMut<T>,
-                    options: &#internal::Options,
-                    dst: &mut std::mem::MaybeUninit<Self::Lower>,
-                ) -> #internal::anyhow::Result<()> {
-                    match self {
-                        #lowers
-                    }
-                }
-
-                #[inline]
-                fn store<T>(
-                    &self,
-                    memory: &mut #internal::MemoryMut<'_, T>,
-                    mut offset: usize
-                ) -> #internal::anyhow::Result<()> {
-                    debug_assert!(offset % (<Self as wasmtime::component::ComponentType>::ALIGN32 as usize) == 0);
-                    match self {
-                        #stores
-                    }
-                }
-            }
-        };
-
-        Ok(expanded)
-    }
+    component::expand(
+        &component::LowerExpander,
+        &parse_macro_input!(input as DeriveInput),
+    )
+    .unwrap_or_else(Error::into_compile_error)
+    .into()
 }
 
 #[proc_macro_derive(ComponentType, attributes(component))]
 pub fn component_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    expand(
-        &ComponentTypeExpander,
+    component::expand(
+        &component::ComponentTypeExpander,
         &parse_macro_input!(input as DeriveInput),
     )
     .unwrap_or_else(Error::into_compile_error)
     .into()
 }
 
-struct ComponentTypeExpander;
-
-impl Expander for ComponentTypeExpander {
-    fn expand_record(
-        &self,
-        name: &syn::Ident,
-        generics: &syn::Generics,
-        fields: &[&syn::Field],
-    ) -> Result<TokenStream> {
-        expand_record_for_component_type(
-            name,
-            generics,
-            fields,
-            quote!(typecheck_record),
-            fields
-                .iter()
-                .map(
-                    |syn::Field {
-                         attrs, ident, ty, ..
-                     }| {
-                        let name = find_rename(attrs)?.unwrap_or_else(|| {
-                            Literal::string(&ident.as_ref().unwrap().to_string())
-                        });
-
-                        Ok(quote!((#name, <#ty as wasmtime::component::ComponentType>::typecheck),))
-                    },
-                )
-                .collect::<Result<_>>()?,
-        )
-    }
-
-    fn expand_variant(
-        &self,
-        name: &syn::Ident,
-        generics: &syn::Generics,
-        _discriminant_size: DiscriminantSize,
-        cases: &[VariantCase],
-        style: VariantStyle,
-    ) -> Result<TokenStream> {
-        let internal = quote!(wasmtime::component::__internal);
-
-        let mut case_names_and_checks = TokenStream::new();
-        let mut lower_payload_generic_params = TokenStream::new();
-        let mut lower_payload_generic_args = TokenStream::new();
-        let mut lower_payload_case_declarations = TokenStream::new();
-        let mut lower_generic_args = TokenStream::new();
-        let mut abi_list = TokenStream::new();
-        let mut unique_types = HashSet::new();
-
-        for (index, VariantCase { attrs, ident, ty }) in cases.iter().enumerate() {
-            let rename = find_rename(attrs)?;
-
-            if let (Some(_), VariantStyle::Union) = (&rename, style) {
-                return Err(Error::new(
-                    ident.span(),
-                    "renaming `union` cases is not permitted; only the type is used",
-                ));
-            }
-
-            let name = rename.unwrap_or_else(|| Literal::string(&ident.to_string()));
-
-            if let Some(ty) = ty {
-                abi_list.extend(quote!(Some(<#ty as wasmtime::component::ComponentType>::ABI),));
-
-                case_names_and_checks.extend(match style {
-                    VariantStyle::Variant => {
-                        quote!((#name, Some(<#ty as wasmtime::component::ComponentType>::typecheck)),)
-                    }
-                    VariantStyle::Union => {
-                        quote!(<#ty as wasmtime::component::ComponentType>::typecheck,)
-                    }
-                    VariantStyle::Enum => {
-                        return Err(Error::new(
-                            ident.span(),
-                            "payloads are not permitted for `enum` cases",
-                        ))
-                    }
-                });
-
-                let generic = format_ident!("T{}", index);
-
-                lower_payload_generic_params.extend(quote!(#generic: Copy,));
-                lower_payload_generic_args.extend(quote!(#generic,));
-                lower_payload_case_declarations.extend(quote!(#ident: #generic,));
-                lower_generic_args
-                    .extend(quote!(<#ty as wasmtime::component::ComponentType>::Lower,));
-
-                unique_types.insert(ty);
-            } else {
-                abi_list.extend(quote!(None,));
-                case_names_and_checks.extend(match style {
-                    VariantStyle::Variant => {
-                        quote!((#name, None),)
-                    }
-                    VariantStyle::Union => {
-                        quote!(<() as wasmtime::component::ComponentType>::typecheck,)
-                    }
-                    VariantStyle::Enum => quote!(#name,),
-                });
-                lower_payload_case_declarations.extend(quote!(#ident: [wasmtime::ValRaw; 0],));
-            }
-        }
-
-        let typecheck = match style {
-            VariantStyle::Variant => quote!(typecheck_variant),
-            VariantStyle::Union => quote!(typecheck_union),
-            VariantStyle::Enum => quote!(typecheck_enum),
-        };
-
-        let generics = add_trait_bounds(generics, parse_quote!(wasmtime::component::ComponentType));
-        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
-        let lower = format_ident!("Lower{}", name);
-        let lower_payload = format_ident!("LowerPayload{}", name);
-
-        // You may wonder why we make the types of all the fields of the #lower struct and #lower_payload union
-        // generic.  This is to work around a [normalization bug in
-        // rustc](https://github.com/rust-lang/rust/issues/90903) such that the compiler does not understand that
-        // e.g. `<i32 as ComponentType>::Lower` is `Copy` despite the bound specified in `ComponentType`'s
-        // definition.
-        //
-        // See also the comment in `Self::expand_record` above for another reason why we do this.
-
-        let expanded = quote! {
-            #[doc(hidden)]
-            #[derive(Clone, Copy)]
-            #[repr(C)]
-            pub struct #lower<#lower_payload_generic_params> {
-                tag: wasmtime::ValRaw,
-                payload: #lower_payload<#lower_payload_generic_args>
-            }
-
-            #[doc(hidden)]
-            #[allow(non_snake_case)]
-            #[derive(Clone, Copy)]
-            #[repr(C)]
-            union #lower_payload<#lower_payload_generic_params> {
-                #lower_payload_case_declarations
-            }
-
-            unsafe impl #impl_generics wasmtime::component::ComponentType for #name #ty_generics #where_clause {
-                type Lower = #lower<#lower_generic_args>;
-
-                #[inline]
-                fn typecheck(
-                    ty: &#internal::InterfaceType,
-                    types: &#internal::ComponentTypes,
-                ) -> #internal::anyhow::Result<()> {
-                    #internal::#typecheck(ty, types, &[#case_names_and_checks])
-                }
-
-                const ABI: #internal::CanonicalAbiInfo =
-                    #internal::CanonicalAbiInfo::variant_static(&[#abi_list]);
-            }
-
-            unsafe impl #impl_generics #internal::ComponentVariant for #name #ty_generics #where_clause {
-                const CASES: &'static [Option<#internal::CanonicalAbiInfo>] = &[#abi_list];
-            }
-        };
-
-        Ok(quote!(const _: () = { #expanded };))
-    }
-}
-
-#[derive(Debug)]
-struct Flag {
-    rename: Option<String>,
-    name: String,
-}
-
-impl Parse for Flag {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let attributes = syn::Attribute::parse_outer(input)?;
-
-        let rename = find_rename(&attributes)?
-            .map(|literal| {
-                let s = literal.to_string();
-
-                s.strip_prefix('"')
-                    .and_then(|s| s.strip_suffix('"'))
-                    .map(|s| s.to_owned())
-                    .ok_or_else(|| Error::new(literal.span(), "expected string literal"))
-            })
-            .transpose()?;
-
-        input.parse::<Token![const]>()?;
-        let name = input.parse::<syn::Ident>()?.to_string();
-
-        Ok(Self { rename, name })
-    }
-}
-
-#[derive(Debug)]
-struct Flags {
-    name: String,
-    flags: Vec<Flag>,
-}
-
-impl Parse for Flags {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let name = input.parse::<syn::Ident>()?.to_string();
-
-        let content;
-        braced!(content in input);
-
-        let flags = content
-            .parse_terminated::<_, Token![;]>(Flag::parse)?
-            .into_iter()
-            .collect();
-
-        Ok(Self { name, flags })
-    }
-}
-
 #[proc_macro]
 pub fn flags(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    expand_flags(&parse_macro_input!(input as Flags))
+    component::expand_flags(&parse_macro_input!(input as component::Flags))
         .unwrap_or_else(Error::into_compile_error)
         .into()
 }
 
-fn expand_flags(flags: &Flags) -> Result<TokenStream> {
-    let size = FlagsSize::from_count(flags.flags.len());
-
-    let ty;
-    let eq;
-
-    let count = flags.flags.len();
-
-    match size {
-        FlagsSize::Size0 => {
-            ty = quote!(());
-            eq = quote!(true);
-        }
-        FlagsSize::Size1 => {
-            ty = quote!(u8);
-
-            eq = if count == 8 {
-                quote!(self.__inner0.eq(&rhs.__inner0))
-            } else {
-                let mask = !(0xFF_u8 << count);
-
-                quote!((self.__inner0 & #mask).eq(&(rhs.__inner0 & #mask)))
-            };
-        }
-        FlagsSize::Size2 => {
-            ty = quote!(u16);
-
-            eq = if count == 16 {
-                quote!(self.__inner0.eq(&rhs.__inner0))
-            } else {
-                let mask = !(0xFFFF_u16 << count);
-
-                quote!((self.__inner0 & #mask).eq(&(rhs.__inner0 & #mask)))
-            };
-        }
-        FlagsSize::Size4Plus(n) => {
-            ty = quote!(u32);
-
-            let comparisons = (0..(n - 1))
-                .map(|index| {
-                    let field = format_ident!("__inner{}", index);
-
-                    quote!(self.#field.eq(&rhs.#field) &&)
-                })
-                .collect::<TokenStream>();
-
-            let field = format_ident!("__inner{}", n - 1);
-
-            eq = if count % 32 == 0 {
-                quote!(#comparisons self.#field.eq(&rhs.#field))
-            } else {
-                let mask = !(0xFFFF_FFFF_u32 << (count % 32));
-
-                quote!(#comparisons (self.#field & #mask).eq(&(rhs.#field & #mask)))
-            }
-        }
-    }
-
-    let count;
-    let mut as_array;
-    let mut bitor;
-    let mut bitor_assign;
-    let mut bitand;
-    let mut bitand_assign;
-    let mut bitxor;
-    let mut bitxor_assign;
-    let mut not;
-
-    match size {
-        FlagsSize::Size0 => {
-            count = 0;
-            as_array = quote!([]);
-            bitor = quote!(Self {});
-            bitor_assign = quote!();
-            bitand = quote!(Self {});
-            bitand_assign = quote!();
-            bitxor = quote!(Self {});
-            bitxor_assign = quote!();
-            not = quote!(Self {});
-        }
-        FlagsSize::Size1 | FlagsSize::Size2 => {
-            count = 1;
-            as_array = quote!([self.__inner0 as u32]);
-            bitor = quote!(Self {
-                __inner0: self.__inner0.bitor(rhs.__inner0)
-            });
-            bitor_assign = quote!(self.__inner0.bitor_assign(rhs.__inner0));
-            bitand = quote!(Self {
-                __inner0: self.__inner0.bitand(rhs.__inner0)
-            });
-            bitand_assign = quote!(self.__inner0.bitand_assign(rhs.__inner0));
-            bitxor = quote!(Self {
-                __inner0: self.__inner0.bitxor(rhs.__inner0)
-            });
-            bitxor_assign = quote!(self.__inner0.bitxor_assign(rhs.__inner0));
-            not = quote!(Self {
-                __inner0: self.__inner0.not()
-            });
-        }
-        FlagsSize::Size4Plus(n) => {
-            count = usize::from(n);
-            as_array = TokenStream::new();
-            bitor = TokenStream::new();
-            bitor_assign = TokenStream::new();
-            bitand = TokenStream::new();
-            bitand_assign = TokenStream::new();
-            bitxor = TokenStream::new();
-            bitxor_assign = TokenStream::new();
-            not = TokenStream::new();
-
-            for index in 0..n {
-                let field = format_ident!("__inner{}", index);
-
-                as_array.extend(quote!(self.#field,));
-                bitor.extend(quote!(#field: self.#field.bitor(rhs.#field),));
-                bitor_assign.extend(quote!(self.#field.bitor_assign(rhs.#field);));
-                bitand.extend(quote!(#field: self.#field.bitand(rhs.#field),));
-                bitand_assign.extend(quote!(self.#field.bitand_assign(rhs.#field);));
-                bitxor.extend(quote!(#field: self.#field.bitxor(rhs.#field),));
-                bitxor_assign.extend(quote!(self.#field.bitxor_assign(rhs.#field);));
-                not.extend(quote!(#field: self.#field.not(),));
-            }
-
-            as_array = quote!([#as_array]);
-            bitor = quote!(Self { #bitor });
-            bitand = quote!(Self { #bitand });
-            bitxor = quote!(Self { #bitxor });
-            not = quote!(Self { #not });
-        }
-    };
-
-    let name = format_ident!("{}", flags.name);
-
-    let mut constants = TokenStream::new();
-    let mut rust_names = TokenStream::new();
-    let mut component_names = TokenStream::new();
-
-    for (index, Flag { name, rename }) in flags.flags.iter().enumerate() {
-        rust_names.extend(quote!(#name,));
-
-        let component_name = rename.as_ref().unwrap_or(name);
-        component_names.extend(quote!(#component_name,));
-
-        let fields = match size {
-            FlagsSize::Size0 => quote!(),
-            FlagsSize::Size1 => {
-                let init = 1_u8 << index;
-                quote!(__inner0: #init)
-            }
-            FlagsSize::Size2 => {
-                let init = 1_u16 << index;
-                quote!(__inner0: #init)
-            }
-            FlagsSize::Size4Plus(n) => (0..n)
-                .map(|i| {
-                    let field = format_ident!("__inner{}", i);
-
-                    let init = if index / 32 == usize::from(i) {
-                        1_u32 << (index % 32)
-                    } else {
-                        0
-                    };
-
-                    quote!(#field: #init,)
-                })
-                .collect::<TokenStream>(),
-        };
-
-        let name = format_ident!("{}", name);
-
-        constants.extend(quote!(pub const #name: Self = Self { #fields };));
-    }
-
-    let generics = syn::Generics {
-        lt_token: None,
-        params: Punctuated::new(),
-        gt_token: None,
-        where_clause: None,
-    };
-
-    let fields = {
-        let ty = syn::parse2::<syn::Type>(ty.clone())?;
-
-        (0..count)
-            .map(|index| syn::Field {
-                attrs: Vec::new(),
-                vis: syn::Visibility::Inherited,
-                ident: Some(format_ident!("__inner{}", index)),
-                colon_token: None,
-                ty: ty.clone(),
-            })
-            .collect::<Vec<_>>()
-    };
-
-    let fields = fields.iter().collect::<Vec<_>>();
-
-    let component_type_impl = expand_record_for_component_type(
-        &name,
-        &generics,
-        &fields,
-        quote!(typecheck_flags),
-        component_names,
-    )?;
-
-    let lower_impl = LowerExpander.expand_record(&name, &generics, &fields)?;
-
-    let lift_impl = LiftExpander.expand_record(&name, &generics, &fields)?;
-
-    let internal = quote!(wasmtime::component::__internal);
-
-    let fields = fields
-        .iter()
-        .map(|syn::Field { ident, .. }| quote!(#[doc(hidden)] #ident: #ty,))
-        .collect::<TokenStream>();
-
-    let expanded = quote! {
-        #[derive(Copy, Clone, Default)]
-        pub struct #name { #fields }
-
-        impl #name {
-            #constants
-
-            pub fn as_array(&self) -> [u32; #count] {
-                #as_array
-            }
-
-            pub fn empty() -> Self {
-                Self::default()
-            }
-
-            pub fn all() -> Self {
-                use std::ops::Not;
-                Self::default().not()
-            }
-        }
-
-        impl std::cmp::PartialEq for #name {
-            fn eq(&self, rhs: &#name) -> bool {
-                #eq
-            }
-        }
-
-        impl std::cmp::Eq for #name { }
-
-        impl std::fmt::Debug for #name {
-            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-                #internal::format_flags(&self.as_array(), &[#rust_names], f)
-            }
-        }
-
-        impl std::ops::BitOr for #name {
-            type Output = #name;
-
-            fn bitor(self, rhs: #name) -> #name {
-                #bitor
-            }
-        }
-
-        impl std::ops::BitOrAssign for #name {
-            fn bitor_assign(&mut self, rhs: #name) {
-                #bitor_assign
-            }
-        }
-
-        impl std::ops::BitAnd for #name {
-            type Output = #name;
-
-            fn bitand(self, rhs: #name) -> #name {
-                #bitand
-            }
-        }
-
-        impl std::ops::BitAndAssign for #name {
-            fn bitand_assign(&mut self, rhs: #name) {
-                #bitand_assign
-            }
-        }
-
-        impl std::ops::BitXor for #name {
-            type Output = #name;
-
-            fn bitxor(self, rhs: #name) -> #name {
-                #bitxor
-            }
-        }
-
-        impl std::ops::BitXorAssign for #name {
-            fn bitxor_assign(&mut self, rhs: #name) {
-                #bitxor_assign
-            }
-        }
-
-        impl std::ops::Not for #name {
-            type Output = #name;
-
-            fn not(self) -> #name {
-                #not
-            }
-        }
-
-        #component_type_impl
-
-        #lower_impl
-
-        #lift_impl
-    };
-
-    Ok(expanded)
+#[proc_macro]
+pub fn bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    bindgen::expand(&parse_macro_input!(input as bindgen::Config))
+        .unwrap_or_else(Error::into_compile_error)
+        .into()
 }
diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs
index a0628abf3..37f389252 100644
--- a/crates/fuzzing/src/oracles.rs
+++ b/crates/fuzzing/src/oracles.rs
@@ -315,7 +315,7 @@ pub fn instantiate_with_dummy(store: &mut Store<StoreLimits>, module: &Module) -
     }
 
     // Also allow failures to instantiate as a result of hitting instance limits
-    if string.contains("concurrent instances has been reached") {
+    if string.contains("maximum concurrent instance limit") {
         log::debug!("failed to instantiate: {}", string);
         return None;
     }
diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml
index c59a17f2e..2372b51e5 100644
--- a/crates/wasmtime/Cargo.toml
+++ b/crates/wasmtime/Cargo.toml
@@ -88,7 +88,12 @@ cache = ["dep:wasmtime-cache"]
 
 # Enables support for "async stores" as well as defining host functions as
 # `async fn` and calling functions asynchronously.
-async = ["dep:wasmtime-fiber", "wasmtime-runtime/async", "dep:async-trait"]
+async = [
+  "dep:wasmtime-fiber",
+  "wasmtime-runtime/async",
+  "dep:async-trait",
+  "wasmtime-component-macro?/async",
+]
 
 # Enables support for the pooling instance allocation strategy
 pooling-allocator = ["wasmtime-runtime/pooling-allocator"]
diff --git a/crates/wasmtime/src/component/error.rs b/crates/wasmtime/src/component/error.rs
new file mode 100644
index 000000000..8ca532fb8
--- /dev/null
+++ b/crates/wasmtime/src/component/error.rs
@@ -0,0 +1,126 @@
+/// Type alias for the standard library [`Result`](std::result::Result) type
+/// to specifie [`Error`] as the error payload.
+pub type Result<A, E> = std::result::Result<A, Error<E>>;
+
+/// Error type used by the [`bindgen!`](crate::component::bindgen) macro.
+///
+/// This error type represents either the typed error `T` specified here or a
+/// trap, represented with [`anyhow::Error`].
+pub struct Error<T> {
+    err: anyhow::Error,
+    ty: std::marker::PhantomData<T>,
+}
+
+impl<T> Error<T> {
+    /// Creates a new typed version of this error from the `T` specified.
+    ///
+    /// This error, if it makes its way to the guest, will be returned to the
+    /// guest and the guest will be able to act upon it.
+    ///
+    /// Alternatively errors can be created with [`Error::trap`] which will
+    /// cause the guest to trap and be unable to act upon it.
+    pub fn new(err: T) -> Error<T>
+    where
+        T: std::error::Error + Send + Sync + 'static,
+    {
+        Error {
+            err: err.into(),
+            ty: std::marker::PhantomData,
+        }
+    }
+
+    /// Creates a custom "trap" which will abort guest execution and have the
+    /// specified `err` as the payload context returned from the original
+    /// invocation.
+    ///
+    /// Note that if `err` here actually has type `T` then the error will not be
+    /// considered a trap and will instead be dynamically detected as a normal
+    /// error to communicate to the original module.
+    pub fn trap(err: impl std::error::Error + Send + Sync + 'static) -> Error<T> {
+        Error {
+            err: anyhow::Error::from(err),
+            ty: std::marker::PhantomData,
+        }
+    }
+
+    /// Attempts to dynamically downcast this error internally to the `T`
+    /// representation.
+    ///
+    /// If this error is internally represented as a `T` then `Ok(val)` will be
+    /// returned. If this error is instead represented as a trap then
+    /// `Err(trap)` will be returned instead.
+    pub fn downcast(self) -> anyhow::Result<T>
+    where
+        T: std::error::Error + Send + Sync + 'static,
+    {
+        self.err.downcast::<T>()
+    }
+
+    /// Attempts to dynamically downcast this error to peek at the inner
+    /// contents of `T` if present.
+    pub fn downcast_ref(&self) -> Option<&T>
+    where
+        T: std::error::Error + Send + Sync + 'static,
+    {
+        self.err.downcast_ref::<T>()
+    }
+
+    /// Attempts to dynamically downcast this error to peek at the inner
+    /// contents of `T` if present.
+    pub fn downcast_mut(&mut self) -> Option<&mut T>
+    where
+        T: std::error::Error + Send + Sync + 'static,
+    {
+        self.err.downcast_mut::<T>()
+    }
+
+    /// Converts this error into an `anyhow::Error` which loses the `T` type
+    /// information tagged to this error.
+    pub fn into_inner(self) -> anyhow::Error {
+        self.err
+    }
+
+    /// Same as [`anyhow::Error::context`], attaches a contextual message to
+    /// this error.
+    pub fn context<C>(self, context: C) -> Error<T>
+    where
+        C: std::fmt::Display + Send + Sync + 'static,
+    {
+        self.err.context(context).into()
+    }
+}
+
+impl<T> std::ops::Deref for Error<T> {
+    type Target = dyn std::error::Error + Send + Sync + 'static;
+    fn deref(&self) -> &Self::Target {
+        self.err.deref()
+    }
+}
+impl<T> std::ops::DerefMut for Error<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.err.deref_mut()
+    }
+}
+
+impl<T> std::fmt::Display for Error<T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.err.fmt(f)
+    }
+}
+
+impl<T> std::fmt::Debug for Error<T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.err.fmt(f)
+    }
+}
+
+impl<T> std::error::Error for Error<T> {}
+
+impl<T> From<anyhow::Error> for Error<T> {
+    fn from(err: anyhow::Error) -> Error<T> {
+        Error {
+            err,
+            ty: std::marker::PhantomData,
+        }
+    }
+}
diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs
index fef7fa53f..339788b39 100644
--- a/crates/wasmtime/src/component/mod.rs
+++ b/crates/wasmtime/src/component/mod.rs
@@ -4,6 +4,7 @@
 //! probably buggy implementation of the component model.
 
 mod component;
+mod error;
 mod func;
 mod instance;
 mod linker;
@@ -13,6 +14,7 @@ mod store;
 pub mod types;
 mod values;
 pub use self::component::Component;
+pub use self::error::{Error, Result};
 pub use self::func::{
     ComponentNamedList, ComponentType, Func, Lift, Lower, TypedFunc, WasmList, WasmStr,
 };
@@ -20,7 +22,7 @@ pub use self::instance::{ExportInstance, Exports, Instance, InstancePre};
 pub use self::linker::{Linker, LinkerInstance};
 pub use self::types::Type;
 pub use self::values::Val;
-pub use wasmtime_component_macro::{flags, ComponentType, Lift, Lower};
+pub use wasmtime_component_macro::{bindgen, flags, ComponentType, Lift, Lower};
 
 // These items are expected to be used by an eventual
 // `#[derive(ComponentType)]`, they are not part of Wasmtime's API stability
@@ -35,6 +37,8 @@ pub mod __internal {
     pub use crate::map_maybe_uninit;
     pub use crate::store::StoreOpaque;
     pub use anyhow;
+    #[cfg(feature = "async")]
+    pub use async_trait::async_trait;
     pub use wasmtime_environ;
     pub use wasmtime_environ::component::{CanonicalAbiInfo, ComponentTypes, InterfaceType};
 }
diff --git a/crates/wiggle/generate/Cargo.toml b/crates/wiggle/generate/Cargo.toml
index 2a84e3262..b26054bd3 100644
--- a/crates/wiggle/generate/Cargo.toml
+++ b/crates/wiggle/generate/Cargo.toml
@@ -17,7 +17,7 @@ include = ["src/**/*", "README.md", "LICENSE"]
 witx = { version = "0.9.1", path = "../../wasi-common/WASI/tools/witx" }
 quote = "1.0"
 proc-macro2 = "1.0"
-heck = "0.4"
+heck = { workspace = true }
 anyhow = { workspace = true }
 syn = { version = "1.0", features = ["full"] }
 shellexpand = "2.0"
diff --git a/crates/wit-bindgen/Cargo.toml b/crates/wit-bindgen/Cargo.toml
new file mode 100644
index 000000000..7511f7b1c
--- /dev/null
+++ b/crates/wit-bindgen/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "wasmtime-wit-bindgen"
+version.workspace = true
+authors.workspace = true
+description = "Internal `*.wit` support for the `wasmtime` crate's macros"
+license = "Apache-2.0 WITH LLVM-exception"
+repository = "https://github.com/bytecodealliance/wasmtime"
+documentation = "https://docs.rs/wasmtime-wit-bindgen/"
+edition.workspace = true
+
+[dependencies]
+heck = { workspace = true }
+wit-parser = { workspace = true }
diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs
new file mode 100644
index 000000000..87ce222b9
--- /dev/null
+++ b/crates/wit-bindgen/src/lib.rs
@@ -0,0 +1,1143 @@
+use crate::rust::{to_rust_ident, RustGenerator, TypeMode};
+use crate::types::{TypeInfo, Types};
+use heck::*;
+use std::collections::BTreeMap;
+use std::fmt::Write as _;
+use std::io::{Read, Write};
+use std::mem;
+use std::process::{Command, Stdio};
+use wit_parser::*;
+
+macro_rules! uwrite {
+    ($dst:expr, $($arg:tt)*) => {
+        write!($dst, $($arg)*).unwrap()
+    };
+}
+
+macro_rules! uwriteln {
+    ($dst:expr, $($arg:tt)*) => {
+        writeln!($dst, $($arg)*).unwrap()
+    };
+}
+
+mod rust;
+mod source;
+mod types;
+use source::Source;
+
+#[derive(Default)]
+struct Wasmtime {
+    src: Source,
+    opts: Opts,
+    imports: Vec<String>,
+    exports: Exports,
+}
+
+#[derive(Default)]
+struct Exports {
+    fields: BTreeMap<String, (String, String)>,
+    funcs: Vec<String>,
+}
+
+#[derive(Default, Debug, Clone)]
+#[cfg_attr(feature = "clap", derive(clap::Args))]
+pub struct Opts {
+    /// Whether or not `rustfmt` is executed to format generated code.
+    #[cfg_attr(feature = "clap", arg(long))]
+    pub rustfmt: bool,
+
+    /// Whether or not to emit `tracing` macro calls on function entry/exit.
+    #[cfg_attr(feature = "clap", arg(long))]
+    pub tracing: bool,
+
+    /// Whether or not to use async rust functions and traits.
+    #[cfg_attr(feature = "clap", arg(long = "async"))]
+    pub async_: bool,
+}
+
+impl Opts {
+    pub fn generate(&self, world: &World) -> String {
+        let mut r = Wasmtime::default();
+        r.opts = self.clone();
+        r.generate(world)
+    }
+}
+
+impl Wasmtime {
+    fn generate(&mut self, world: &World) -> String {
+        for (name, import) in world.imports.iter() {
+            self.import(name, import);
+        }
+        for (name, export) in world.exports.iter() {
+            self.export(name, export);
+        }
+        if let Some(iface) = &world.default {
+            self.export_default(&world.name, iface);
+        }
+        self.finish(world)
+    }
+
+    fn import(&mut self, name: &str, iface: &Interface) {
+        let mut gen = InterfaceGenerator::new(self, iface, TypeMode::Owned);
+        gen.types();
+        gen.generate_from_error_impls();
+        gen.generate_add_to_linker(name);
+
+        let snake = name.to_snake_case();
+        let module = &gen.src[..];
+
+        uwriteln!(
+            self.src,
+            "
+                #[allow(clippy::all)]
+                pub mod {snake} {{
+                    #[allow(unused_imports)]
+                    use wasmtime::component::__internal::anyhow;
+
+                    {module}
+                }}
+            "
+        );
+
+        self.imports.push(snake);
+    }
+
+    fn export(&mut self, name: &str, iface: &Interface) {
+        let mut gen = InterfaceGenerator::new(self, iface, TypeMode::AllBorrowed("'a"));
+        gen.types();
+        gen.generate_from_error_impls();
+
+        let camel = name.to_upper_camel_case();
+        uwriteln!(gen.src, "pub struct {camel} {{");
+        for func in iface.functions.iter() {
+            uwriteln!(
+                gen.src,
+                "{}: wasmtime::component::Func,",
+                func.name.to_snake_case()
+            );
+        }
+        uwriteln!(gen.src, "}}");
+
+        uwriteln!(gen.src, "impl {camel} {{");
+        uwrite!(
+            gen.src,
+            "
+                pub fn new(
+                    __exports: &mut wasmtime::component::ExportInstance<'_, '_>,
+                ) -> anyhow::Result<{camel}> {{
+            "
+        );
+        let fields = gen.extract_typed_functions();
+        for (name, getter) in fields.iter() {
+            uwriteln!(gen.src, "let {name} = {getter};");
+        }
+        uwriteln!(gen.src, "Ok({camel} {{");
+        for (name, _) in fields.iter() {
+            uwriteln!(gen.src, "{name},");
+        }
+        uwriteln!(gen.src, "}})");
+        uwriteln!(gen.src, "}}");
+        for func in iface.functions.iter() {
+            gen.define_rust_guest_export(Some(name), func);
+        }
+        uwriteln!(gen.src, "}}");
+
+        let snake = name.to_snake_case();
+        let module = &gen.src[..];
+
+        uwriteln!(
+            self.src,
+            "
+                #[allow(clippy::all)]
+                pub mod {snake} {{
+                    #[allow(unused_imports)]
+                    use wasmtime::component::__internal::anyhow;
+
+                    {module}
+                }}
+            "
+        );
+
+        let getter = format!(
+            "\
+                {snake}::{camel}::new(
+                    &mut __exports.instance(\"{name}\")
+                        .ok_or_else(|| anyhow::anyhow!(\"exported instance `{name}` not present\"))?
+                )?\
+            "
+        );
+        let prev = self
+            .exports
+            .fields
+            .insert(snake.clone(), (format!("{snake}::{camel}"), getter));
+        assert!(prev.is_none());
+        self.exports.funcs.push(format!(
+            "
+                pub fn {snake}(&self) -> &{snake}::{camel} {{
+                    &self.{snake}
+                }}
+            "
+        ));
+    }
+
+    fn export_default(&mut self, _name: &str, iface: &Interface) {
+        let mut gen = InterfaceGenerator::new(self, iface, TypeMode::AllBorrowed("'a"));
+        gen.types();
+        gen.generate_from_error_impls();
+        let fields = gen.extract_typed_functions();
+        for (name, getter) in fields {
+            let prev = gen
+                .gen
+                .exports
+                .fields
+                .insert(name, ("wasmtime::component::Func".to_string(), getter));
+            assert!(prev.is_none());
+        }
+
+        for func in iface.functions.iter() {
+            let prev = mem::take(&mut gen.src);
+            gen.define_rust_guest_export(None, func);
+            let func = mem::replace(&mut gen.src, prev);
+            gen.gen.exports.funcs.push(func.to_string());
+        }
+
+        let src = gen.src;
+        self.src.push_str(&src);
+    }
+
+    fn finish(&mut self, world: &World) -> String {
+        let camel = world.name.to_upper_camel_case();
+        uwriteln!(self.src, "pub struct {camel} {{");
+        for (name, (ty, _)) in self.exports.fields.iter() {
+            uwriteln!(self.src, "{name}: {ty},");
+        }
+        self.src.push_str("}\n");
+
+        let (async_, async__, send, await_) = if self.opts.async_ {
+            ("async", "_async", ":Send", ".await")
+        } else {
+            ("", "", "", "")
+        };
+
+        uwriteln!(self.src, "const _: () = {{");
+        uwriteln!(self.src, "use wasmtime::component::__internal::anyhow;");
+
+        uwriteln!(
+            self.src,
+            "
+                impl {camel} {{
+                    /// Instantiates the provided `module` using the specified
+                    /// parameters, wrapping up the result in a structure that
+                    /// translates between wasm and the host.
+                    pub {async_} fn instantiate{async__}<T {send}>(
+                        mut store: impl wasmtime::AsContextMut<Data = T>,
+                        component: &wasmtime::component::Component,
+                        linker: &wasmtime::component::Linker<T>,
+                    ) -> anyhow::Result<(Self, wasmtime::component::Instance)> {{
+                        let instance = linker.instantiate{async__}(&mut store, component){await_}?;
+                        Ok((Self::new(store, &instance)?, instance))
+                    }}
+
+                    /// Low-level creation wrapper for wrapping up the exports
+                    /// of the `instance` provided in this structure of wasm
+                    /// exports.
+                    ///
+                    /// This function will extract exports from the `instance`
+                    /// defined within `store` and wrap them all up in the
+                    /// returned structure which can be used to interact with
+                    /// the wasm module.
+                    pub fn new(
+                        mut store: impl wasmtime::AsContextMut,
+                        instance: &wasmtime::component::Instance,
+                    ) -> anyhow::Result<Self> {{
+                        let mut store = store.as_context_mut();
+                        let mut exports = instance.exports(&mut store);
+                        let mut __exports = exports.root();
+            ",
+        );
+        for (name, (_, get)) in self.exports.fields.iter() {
+            uwriteln!(self.src, "let {name} = {get};");
+        }
+        uwriteln!(self.src, "Ok({camel} {{");
+        for (name, _) in self.exports.fields.iter() {
+            uwriteln!(self.src, "{name},");
+        }
+        uwriteln!(self.src, "}})");
+        uwriteln!(self.src, "}}"); // close `fn new`
+
+        for func in self.exports.funcs.iter() {
+            self.src.push_str(func);
+        }
+
+        uwriteln!(self.src, "}}"); // close `impl {camel}`
+
+        uwriteln!(self.src, "}};"); // close `const _: () = ...
+
+        let mut src = mem::take(&mut self.src);
+        if self.opts.rustfmt {
+            let mut child = Command::new("rustfmt")
+                .arg("--edition=2018")
+                .stdin(Stdio::piped())
+                .stdout(Stdio::piped())
+                .spawn()
+                .expect("failed to spawn `rustfmt`");
+            child
+                .stdin
+                .take()
+                .unwrap()
+                .write_all(src.as_bytes())
+                .unwrap();
+            src.as_mut_string().truncate(0);
+            child
+                .stdout
+                .take()
+                .unwrap()
+                .read_to_string(src.as_mut_string())
+                .unwrap();
+            let status = child.wait().unwrap();
+            assert!(status.success());
+        }
+
+        src.into()
+    }
+}
+
+struct InterfaceGenerator<'a> {
+    src: Source,
+    gen: &'a mut Wasmtime,
+    iface: &'a Interface,
+    default_param_mode: TypeMode,
+    types: Types,
+}
+
+impl<'a> InterfaceGenerator<'a> {
+    fn new(
+        gen: &'a mut Wasmtime,
+        iface: &'a Interface,
+        default_param_mode: TypeMode,
+    ) -> InterfaceGenerator<'a> {
+        let mut types = Types::default();
+        types.analyze(iface);
+        InterfaceGenerator {
+            src: Source::default(),
+            gen,
+            iface,
+            types,
+            default_param_mode,
+        }
+    }
+
+    fn types(&mut self) {
+        for (id, ty) in self.iface.types.iter() {
+            let name = match &ty.name {
+                Some(name) => name,
+                None => continue,
+            };
+            match &ty.kind {
+                TypeDefKind::Record(record) => self.type_record(id, name, record, &ty.docs),
+                TypeDefKind::Flags(flags) => self.type_flags(id, name, flags, &ty.docs),
+                TypeDefKind::Tuple(tuple) => self.type_tuple(id, name, tuple, &ty.docs),
+                TypeDefKind::Enum(enum_) => self.type_enum(id, name, enum_, &ty.docs),
+                TypeDefKind::Variant(variant) => self.type_variant(id, name, variant, &ty.docs),
+                TypeDefKind::Option(t) => self.type_option(id, name, t, &ty.docs),
+                TypeDefKind::Result(r) => self.type_result(id, name, r, &ty.docs),
+                TypeDefKind::Union(u) => self.type_union(id, name, u, &ty.docs),
+                TypeDefKind::List(t) => self.type_list(id, name, t, &ty.docs),
+                TypeDefKind::Type(t) => self.type_alias(id, name, t, &ty.docs),
+                TypeDefKind::Future(_) => todo!("generate for future"),
+                TypeDefKind::Stream(_) => todo!("generate for stream"),
+            }
+        }
+    }
+
+    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) {
+            let lt = self.lifetime_for(&info, mode);
+            self.rustdoc(docs);
+
+            self.push_str("#[derive(wasmtime::component::ComponentType)]\n");
+            if lt.is_none() {
+                self.push_str("#[derive(wasmtime::component::Lift)]\n");
+            }
+            self.push_str("#[derive(wasmtime::component::Lower)]\n");
+            self.push_str("#[component(record)]\n");
+
+            if !info.has_list {
+                self.push_str("#[derive(Copy, Clone)]\n");
+            } else {
+                self.push_str("#[derive(Clone)]\n");
+            }
+            self.push_str(&format!("pub struct {}", name));
+            self.print_generics(lt);
+            self.push_str(" {\n");
+            for field in record.fields.iter() {
+                self.rustdoc(&field.docs);
+                self.push_str(&format!("#[component(name = \"{}\")]\n", field.name));
+                self.push_str("pub ");
+                self.push_str(&to_rust_ident(&field.name));
+                self.push_str(": ");
+                self.print_ty(&field.ty, mode);
+                self.push_str(",\n");
+            }
+            self.push_str("}\n");
+
+            self.push_str("impl");
+            self.print_generics(lt);
+            self.push_str(" core::fmt::Debug for ");
+            self.push_str(&name);
+            self.print_generics(lt);
+            self.push_str(" {\n");
+            self.push_str(
+                "fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n",
+            );
+            self.push_str(&format!("f.debug_struct(\"{}\")", name));
+            for field in record.fields.iter() {
+                self.push_str(&format!(
+                    ".field(\"{}\", &self.{})",
+                    field.name,
+                    to_rust_ident(&field.name)
+                ));
+            }
+            self.push_str(".finish()\n");
+            self.push_str("}\n");
+            self.push_str("}\n");
+
+            if info.error {
+                self.push_str("impl");
+                self.print_generics(lt);
+                self.push_str(" core::fmt::Display for ");
+                self.push_str(&name);
+                self.print_generics(lt);
+                self.push_str(" {\n");
+                self.push_str(
+                    "fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n",
+                );
+                self.push_str("write!(f, \"{:?}\", self)\n");
+                self.push_str("}\n");
+                self.push_str("}\n");
+                self.push_str("impl std::error::Error for ");
+                self.push_str(&name);
+                self.push_str("{}\n");
+            }
+        }
+    }
+
+    fn type_tuple(&mut self, id: TypeId, _name: &str, tuple: &Tuple, docs: &Docs) {
+        let info = self.info(id);
+        for (name, mode) in self.modes_of(id) {
+            let lt = self.lifetime_for(&info, mode);
+            self.rustdoc(docs);
+            self.push_str(&format!("pub type {}", name));
+            self.print_generics(lt);
+            self.push_str(" = (");
+            for ty in tuple.types.iter() {
+                self.print_ty(ty, mode);
+                self.push_str(",");
+            }
+            self.push_str(");\n");
+        }
+    }
+
+    fn type_flags(&mut self, _id: TypeId, name: &str, flags: &Flags, docs: &Docs) {
+        self.rustdoc(docs);
+        self.src.push_str("wasmtime::component::flags!(\n");
+        self.src
+            .push_str(&format!("{} {{\n", name.to_upper_camel_case()));
+        for flag in flags.flags.iter() {
+            // TODO wasmtime-component-macro doesnt support docs for flags rn
+            uwrite!(
+                self.src,
+                "#[component(name=\"{}\")] const {};\n",
+                flag.name,
+                flag.name.to_shouty_snake_case()
+            );
+        }
+        self.src.push_str("}\n");
+        self.src.push_str(");\n\n");
+    }
+
+    fn type_variant(&mut self, id: TypeId, _name: &str, variant: &Variant, docs: &Docs) {
+        self.print_rust_enum(
+            id,
+            variant.cases.iter().map(|c| {
+                (
+                    c.name.to_upper_camel_case(),
+                    Some(c.name.clone()),
+                    &c.docs,
+                    c.ty.as_ref(),
+                )
+            }),
+            docs,
+            "variant",
+        );
+    }
+
+    fn type_union(&mut self, id: TypeId, _name: &str, union: &Union, docs: &Docs) {
+        self.print_rust_enum(
+            id,
+            std::iter::zip(self.union_case_names(union), &union.cases)
+                .map(|(name, case)| (name, None, &case.docs, Some(&case.ty))),
+            docs,
+            "union",
+        );
+    }
+
+    fn type_option(&mut self, id: TypeId, _name: &str, payload: &Type, docs: &Docs) {
+        let info = self.info(id);
+
+        for (name, mode) in self.modes_of(id) {
+            self.rustdoc(docs);
+            let lt = self.lifetime_for(&info, mode);
+            self.push_str(&format!("pub type {}", name));
+            self.print_generics(lt);
+            self.push_str("= Option<");
+            self.print_ty(payload, mode);
+            self.push_str(">;\n");
+        }
+    }
+
+    fn print_rust_enum<'b>(
+        &mut self,
+        id: TypeId,
+        cases: impl IntoIterator<Item = (String, Option<String>, &'b Docs, Option<&'b Type>)> + Clone,
+        docs: &Docs,
+        derive_component: &str,
+    ) where
+        Self: Sized,
+    {
+        let info = self.info(id);
+
+        for (name, mode) in self.modes_of(id) {
+            let name = name.to_upper_camel_case();
+
+            self.rustdoc(docs);
+            let lt = self.lifetime_for(&info, mode);
+            self.push_str("#[derive(wasmtime::component::ComponentType)]\n");
+            if lt.is_none() {
+                self.push_str("#[derive(wasmtime::component::Lift)]\n");
+            }
+            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 {
+                self.push_str("#[derive(Clone)]\n");
+            }
+            self.push_str(&format!("pub enum {name}"));
+            self.print_generics(lt);
+            self.push_str("{\n");
+            for (case_name, component_name, docs, payload) in cases.clone() {
+                self.rustdoc(docs);
+                if let Some(n) = component_name {
+                    self.push_str(&format!("#[component(name = \"{}\")] ", n));
+                }
+                self.push_str(&case_name);
+                if let Some(ty) = payload {
+                    self.push_str("(");
+                    self.print_ty(ty, mode);
+                    self.push_str(")")
+                }
+                self.push_str(",\n");
+            }
+            self.push_str("}\n");
+
+            self.print_rust_enum_debug(
+                id,
+                mode,
+                &name,
+                cases
+                    .clone()
+                    .into_iter()
+                    .map(|(name, _attr, _docs, ty)| (name, ty)),
+            );
+
+            if info.error {
+                self.push_str("impl");
+                self.print_generics(lt);
+                self.push_str(" core::fmt::Display for ");
+                self.push_str(&name);
+                self.print_generics(lt);
+                self.push_str(" {\n");
+                self.push_str(
+                    "fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n",
+                );
+                self.push_str("write!(f, \"{:?}\", self)");
+                self.push_str("}\n");
+                self.push_str("}\n");
+                self.push_str("\n");
+
+                self.push_str("impl");
+                self.print_generics(lt);
+                self.push_str(" std::error::Error for ");
+                self.push_str(&name);
+                self.print_generics(lt);
+                self.push_str(" {}\n");
+            }
+        }
+    }
+
+    fn print_rust_enum_debug<'b>(
+        &mut self,
+        id: TypeId,
+        mode: TypeMode,
+        name: &str,
+        cases: impl IntoIterator<Item = (String, Option<&'b Type>)>,
+    ) where
+        Self: Sized,
+    {
+        let info = self.info(id);
+        let lt = self.lifetime_for(&info, mode);
+        self.push_str("impl");
+        self.print_generics(lt);
+        self.push_str(" core::fmt::Debug for ");
+        self.push_str(name);
+        self.print_generics(lt);
+        self.push_str(" {\n");
+        self.push_str("fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n");
+        self.push_str("match self {\n");
+        for (case_name, payload) in cases {
+            self.push_str(name);
+            self.push_str("::");
+            self.push_str(&case_name);
+            if payload.is_some() {
+                self.push_str("(e)");
+            }
+            self.push_str(" => {\n");
+            self.push_str(&format!("f.debug_tuple(\"{}::{}\")", name, case_name));
+            if payload.is_some() {
+                self.push_str(".field(e)");
+            }
+            self.push_str(".finish()\n");
+            self.push_str("}\n");
+        }
+        self.push_str("}\n");
+        self.push_str("}\n");
+        self.push_str("}\n");
+    }
+
+    fn type_result(&mut self, id: TypeId, _name: &str, result: &Result_, docs: &Docs) {
+        let info = self.info(id);
+
+        for (name, mode) in self.modes_of(id) {
+            self.rustdoc(docs);
+            let lt = self.lifetime_for(&info, mode);
+            self.push_str(&format!("pub type {}", name));
+            self.print_generics(lt);
+            self.push_str("= Result<");
+            self.print_optional_ty(result.ok.as_ref(), mode);
+            self.push_str(",");
+            self.print_optional_ty(result.err.as_ref(), mode);
+            self.push_str(">;\n");
+        }
+    }
+
+    fn type_enum(&mut self, id: TypeId, name: &str, enum_: &Enum, docs: &Docs) {
+        let info = self.info(id);
+
+        let name = name.to_upper_camel_case();
+        self.rustdoc(docs);
+        self.push_str("#[derive(wasmtime::component::ComponentType)]\n");
+        self.push_str("#[derive(wasmtime::component::Lift)]\n");
+        self.push_str("#[derive(wasmtime::component::Lower)]\n");
+        self.push_str("#[component(enum)]\n");
+        self.push_str("#[derive(Clone, Copy, PartialEq, Eq)]\n");
+        self.push_str(&format!("pub enum {} {{\n", name.to_upper_camel_case()));
+        for case in enum_.cases.iter() {
+            self.rustdoc(&case.docs);
+            self.push_str(&format!("#[component(name = \"{}\")]", case.name));
+            self.push_str(&case.name.to_upper_camel_case());
+            self.push_str(",\n");
+        }
+        self.push_str("}\n");
+
+        // Auto-synthesize an implementation of the standard `Error` trait for
+        // error-looking types based on their name.
+        if info.error {
+            self.push_str("impl ");
+            self.push_str(&name);
+            self.push_str("{\n");
+
+            self.push_str("pub fn name(&self) -> &'static str {\n");
+            self.push_str("match self {\n");
+            for case in enum_.cases.iter() {
+                self.push_str(&name);
+                self.push_str("::");
+                self.push_str(&case.name.to_upper_camel_case());
+                self.push_str(" => \"");
+                self.push_str(case.name.as_str());
+                self.push_str("\",\n");
+            }
+            self.push_str("}\n");
+            self.push_str("}\n");
+
+            self.push_str("pub fn message(&self) -> &'static str {\n");
+            self.push_str("match self {\n");
+            for case in enum_.cases.iter() {
+                self.push_str(&name);
+                self.push_str("::");
+                self.push_str(&case.name.to_upper_camel_case());
+                self.push_str(" => \"");
+                if let Some(contents) = &case.docs.contents {
+                    self.push_str(contents.trim());
+                }
+                self.push_str("\",\n");
+            }
+            self.push_str("}\n");
+            self.push_str("}\n");
+
+            self.push_str("}\n");
+
+            self.push_str("impl core::fmt::Debug for ");
+            self.push_str(&name);
+            self.push_str(
+                "{\nfn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n",
+            );
+            self.push_str("f.debug_struct(\"");
+            self.push_str(&name);
+            self.push_str("\")\n");
+            self.push_str(".field(\"code\", &(*self as i32))\n");
+            self.push_str(".field(\"name\", &self.name())\n");
+            self.push_str(".field(\"message\", &self.message())\n");
+            self.push_str(".finish()\n");
+            self.push_str("}\n");
+            self.push_str("}\n");
+
+            self.push_str("impl core::fmt::Display for ");
+            self.push_str(&name);
+            self.push_str(
+                "{\nfn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n",
+            );
+            self.push_str("write!(f, \"{} (error {})\", self.name(), *self as i32)");
+            self.push_str("}\n");
+            self.push_str("}\n");
+            self.push_str("\n");
+            self.push_str("impl std::error::Error for ");
+            self.push_str(&name);
+            self.push_str("{}\n");
+        } else {
+            self.print_rust_enum_debug(
+                id,
+                TypeMode::Owned,
+                &name,
+                enum_
+                    .cases
+                    .iter()
+                    .map(|c| (c.name.to_upper_camel_case(), None)),
+            )
+        }
+    }
+
+    fn type_alias(&mut self, id: TypeId, _name: &str, ty: &Type, docs: &Docs) {
+        let info = self.info(id);
+        for (name, mode) in self.modes_of(id) {
+            self.rustdoc(docs);
+            self.push_str(&format!("pub type {}", name));
+            let lt = self.lifetime_for(&info, mode);
+            self.print_generics(lt);
+            self.push_str(" = ");
+            self.print_ty(ty, mode);
+            self.push_str(";\n");
+        }
+    }
+
+    fn type_list(&mut self, id: TypeId, _name: &str, ty: &Type, docs: &Docs) {
+        let info = self.info(id);
+        for (name, mode) in self.modes_of(id) {
+            let lt = self.lifetime_for(&info, mode);
+            self.rustdoc(docs);
+            self.push_str(&format!("pub type {}", name));
+            self.print_generics(lt);
+            self.push_str(" = ");
+            self.print_list(ty, mode);
+            self.push_str(";\n");
+        }
+    }
+
+    fn print_result_ty(&mut self, results: &Results, mode: TypeMode) {
+        match results {
+            Results::Named(rs) => match rs.len() {
+                0 => self.push_str("()"),
+                1 => self.print_ty(&rs[0].1, mode),
+                _ => {
+                    self.push_str("(");
+                    for (i, (_, ty)) in rs.iter().enumerate() {
+                        if i > 0 {
+                            self.push_str(", ")
+                        }
+                        self.print_ty(ty, mode)
+                    }
+                    self.push_str(")");
+                }
+            },
+            Results::Anon(ty) => self.print_ty(ty, mode),
+        }
+    }
+
+    fn special_case_host_error(&self, results: &Results) -> Option<&Result_> {
+        // We only support the Error case when
+        // a function has just one result, which is itself a `result<a, e>`, and the
+        // `e` is *not* a primitive (i.e. defined in std) type.
+        let mut i = results.iter_types();
+        if i.len() == 1 {
+            match i.next().unwrap() {
+                Type::Id(id) => match &self.iface.types[*id].kind {
+                    TypeDefKind::Result(r) => match r.err {
+                        Some(Type::Id(_)) => Some(&r),
+                        _ => None,
+                    },
+                    _ => None,
+                },
+                _ => None,
+            }
+        } else {
+            None
+        }
+    }
+
+    fn generate_add_to_linker(&mut self, name: &str) {
+        let camel = name.to_upper_camel_case();
+
+        if self.gen.opts.async_ {
+            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 {camel}: Sized {{");
+        for func in self.iface.functions.iter() {
+            self.rustdoc(&func.docs);
+
+            if self.gen.opts.async_ {
+                self.push_str("async ");
+            }
+            self.push_str("fn ");
+            self.push_str(&to_rust_ident(&func.name));
+            self.push_str("(&mut self, ");
+            for (name, param) in func.params.iter() {
+                let name = to_rust_ident(name);
+                self.push_str(&name);
+                self.push_str(": ");
+                self.print_ty(param, TypeMode::Owned);
+                self.push_str(",");
+            }
+            self.push_str(")");
+            self.push_str(" -> ");
+
+            if let Some(r) = self.special_case_host_error(&func.results).cloned() {
+                // Functions which have a single result `result<ok,err>` get special
+                // cased to use the host_wasmtime_rust::Error<err>, making it possible
+                // for them to trap or use `?` to propogate their errors
+                self.push_str("wasmtime::component::Result<");
+                if let Some(ok) = r.ok {
+                    self.print_ty(&ok, TypeMode::Owned);
+                } else {
+                    self.push_str("()");
+                }
+                self.push_str(",");
+                if let Some(err) = r.err {
+                    self.print_ty(&err, TypeMode::Owned);
+                } else {
+                    self.push_str("()");
+                }
+                self.push_str(">");
+            } else {
+                // All other functions get their return values wrapped in an anyhow::Result.
+                // Returning the anyhow::Error case can be used to trap.
+                self.push_str("anyhow::Result<");
+                self.print_result_ty(&func.results, TypeMode::Owned);
+                self.push_str(">");
+            }
+
+            self.push_str(";\n");
+        }
+        uwriteln!(self.src, "}}");
+
+        let where_clause = if self.gen.opts.async_ {
+            format!("T: Send, U: {camel} + Send")
+        } else {
+            format!("U: {camel}")
+        };
+        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,
+                ) -> anyhow::Result<()>
+                    where {where_clause},
+                {{
+            "
+        );
+        uwriteln!(self.src, "let mut inst = linker.instance(\"{name}\")?;");
+        for func in self.iface.functions.iter() {
+            uwrite!(
+                self.src,
+                "inst.{}(\"{}\", ",
+                if self.gen.opts.async_ {
+                    "func_wrap_async"
+                } else {
+                    "func_wrap"
+                },
+                func.name
+            );
+            self.generate_guest_import_closure(func);
+            uwriteln!(self.src, ")?;")
+        }
+        uwriteln!(self.src, "Ok(())");
+        uwriteln!(self.src, "}}");
+    }
+
+    fn generate_guest_import_closure(&mut self, 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() {
+            // 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.src.push_str(", ");
+        }
+        self.src.push_str(") |");
+        if self.gen.opts.async_ {
+            self.src.push_str(" Box::new(async move { \n");
+        } else {
+            self.src.push_str(" { \n");
+        }
+
+        if self.gen.opts.tracing {
+            self.src.push_str(&format!(
+                "
+                   let span = tracing::span!(
+                       tracing::Level::TRACE,
+                       \"wit-bindgen guest import\",
+                       module = \"{}\",
+                       function = \"{}\",
+                   );
+                   let _enter = span.enter();
+               ",
+                self.iface.name, func.name,
+            ));
+        }
+
+        self.src.push_str("let host = get(caller.data_mut());\n");
+
+        uwrite!(self.src, "let r = host.{}(", func.name.to_snake_case());
+        for (i, _) in func.params.iter().enumerate() {
+            uwrite!(self.src, "arg{},", i);
+        }
+        if self.gen.opts.async_ {
+            uwrite!(self.src, ").await;\n");
+        } else {
+            uwrite!(self.src, ");\n");
+        }
+
+        if self.special_case_host_error(&func.results).is_some() {
+            uwrite!(
+                self.src,
+                "match r {{
+                    Ok(a) => Ok((Ok(a),)),
+                    Err(e) => match e.downcast() {{
+                        Ok(api_error) => Ok((Err(api_error),)),
+                        Err(anyhow_error) => Err(anyhow_error),
+                    }}
+                }}"
+            );
+        } else if func.results.iter_types().len() == 1 {
+            uwrite!(self.src, "Ok((r?,))\n");
+        } else {
+            uwrite!(self.src, "r\n");
+        }
+
+        if self.gen.opts.async_ {
+            // Need to close Box::new and async block
+            self.src.push_str("})");
+        } else {
+            self.src.push_str("}");
+        }
+    }
+
+    fn extract_typed_functions(&mut self) -> Vec<(String, String)> {
+        let prev = mem::take(&mut self.src);
+        let mut ret = Vec::new();
+        for func in self.iface.functions.iter() {
+            let snake = func.name.to_snake_case();
+            uwrite!(self.src, "*__exports.typed_func::<(");
+            for (_, ty) in func.params.iter() {
+                self.print_ty(ty, TypeMode::AllBorrowed("'_"));
+                self.push_str(", ");
+            }
+            self.src.push_str("), (");
+            for ty in func.results.iter_types() {
+                self.print_ty(ty, TypeMode::Owned);
+                self.push_str(", ");
+            }
+            self.src.push_str(")>(\"");
+            self.src.push_str(&func.name);
+            self.src.push_str("\")?.func()");
+
+            ret.push((snake, mem::take(&mut self.src).to_string()));
+        }
+        self.src = prev;
+        return ret;
+    }
+
+    fn define_rust_guest_export(&mut self, ns: Option<&str>, func: &Function) {
+        let (async_, async__, await_) = if self.gen.opts.async_ {
+            ("async", "_async", ".await")
+        } else {
+            ("", "", "")
+        };
+
+        self.rustdoc(&func.docs);
+        uwrite!(
+            self.src,
+            "pub {async_} fn {}<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(") -> anyhow::Result<");
+        self.print_result_ty(&func.results, TypeMode::Owned);
+
+        if self.gen.opts.async_ {
+            self.src
+                .push_str("> where <S as wasmtime::AsContext>::Data: Send {\n");
+        } else {
+            self.src.push_str("> {\n");
+        }
+
+        if self.gen.opts.tracing {
+            self.src.push_str(&format!(
+                "
+                   let span = tracing::span!(
+                       tracing::Level::TRACE,
+                       \"wit-bindgen guest export\",
+                       module = \"{}\",
+                       function = \"{}\",
+                   );
+                   let _enter = span.enter();
+               ",
+                ns.unwrap_or("default"),
+                func.name,
+            ));
+        }
+
+        self.src.push_str("let callee = unsafe {\n");
+        self.src.push_str("wasmtime::component::TypedFunc::<(");
+        for (_, ty) in func.params.iter() {
+            self.print_ty(ty, TypeMode::AllBorrowed("'_"));
+            self.push_str(", ");
+        }
+        self.src.push_str("), (");
+        for ty in func.results.iter_types() {
+            self.print_ty(ty, TypeMode::Owned);
+            self.push_str(", ");
+        }
+        uwriteln!(
+            self.src,
+            ")>::new_unchecked(self.{})",
+            func.name.to_snake_case()
+        );
+        self.src.push_str("};\n");
+        self.src.push_str("let (");
+        for (i, _) in func.results.iter_types().enumerate() {
+            uwrite!(self.src, "ret{},", i);
+        }
+        uwrite!(
+            self.src,
+            ") = callee.call{async__}(store.as_context_mut(), ("
+        );
+        for (i, _) in func.params.iter().enumerate() {
+            uwrite!(self.src, "arg{}, ", i);
+        }
+        uwriteln!(self.src, ")){await_}?;");
+
+        uwriteln!(
+            self.src,
+            "callee.post_return{async__}(store.as_context_mut()){await_}?;"
+        );
+
+        self.src.push_str("Ok(");
+        if func.results.iter_types().len() == 1 {
+            self.src.push_str("ret0");
+        } else {
+            self.src.push_str("(");
+            for (i, _) in func.results.iter_types().enumerate() {
+                uwrite!(self.src, "ret{},", i);
+            }
+            self.src.push_str(")");
+        }
+        self.src.push_str(")\n");
+
+        // End function body
+        self.src.push_str("}\n");
+    }
+
+    fn generate_from_error_impls(&mut self) {
+        for (id, ty) in self.iface.types.iter() {
+            if ty.name.is_none() {
+                continue;
+            }
+            let info = self.info(id);
+            if info.error {
+                for (name, mode) in self.modes_of(id) {
+                    let name = name.to_upper_camel_case();
+                    if self.lifetime_for(&info, mode).is_some() {
+                        continue;
+                    }
+                    self.push_str("impl From<");
+                    self.push_str(&name);
+                    self.push_str("> for wasmtime::component::Error<");
+                    self.push_str(&name);
+                    self.push_str("> {\n");
+                    self.push_str("fn from(e: ");
+                    self.push_str(&name);
+                    self.push_str(") -> wasmtime::component::Error::< ");
+                    self.push_str(&name);
+                    self.push_str("> {\n");
+                    self.push_str("wasmtime::component::Error::new(e)\n");
+                    self.push_str("}\n");
+                    self.push_str("}\n");
+                }
+            }
+        }
+    }
+
+    fn rustdoc(&mut self, docs: &Docs) {
+        let docs = match &docs.contents {
+            Some(docs) => docs,
+            None => return,
+        };
+        for line in docs.trim().lines() {
+            self.push_str("/// ");
+            self.push_str(line);
+            self.push_str("\n");
+        }
+    }
+}
+
+impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> {
+    fn iface(&self) -> &'a Interface {
+        self.iface
+    }
+
+    fn default_param_mode(&self) -> TypeMode {
+        self.default_param_mode
+    }
+
+    fn push_str(&mut self, s: &str) {
+        self.src.push_str(s);
+    }
+
+    fn info(&self, ty: TypeId) -> TypeInfo {
+        self.types.get(ty)
+    }
+}
diff --git a/crates/wit-bindgen/src/rust.rs b/crates/wit-bindgen/src/rust.rs
new file mode 100644
index 000000000..d79147835
--- /dev/null
+++ b/crates/wit-bindgen/src/rust.rs
@@ -0,0 +1,412 @@
+use crate::types::TypeInfo;
+use heck::*;
+use std::collections::HashMap;
+use std::fmt::Write;
+use wit_parser::*;
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum TypeMode {
+    Owned,
+    AllBorrowed(&'static str),
+}
+
+pub trait RustGenerator<'a> {
+    fn iface(&self) -> &'a Interface;
+
+    fn push_str(&mut self, s: &str);
+    fn info(&self, ty: TypeId) -> TypeInfo;
+    fn default_param_mode(&self) -> TypeMode;
+
+    fn print_ty(&mut self, ty: &Type, mode: TypeMode) {
+        match ty {
+            Type::Id(t) => self.print_tyid(*t, mode),
+            Type::Bool => self.push_str("bool"),
+            Type::U8 => self.push_str("u8"),
+            Type::U16 => self.push_str("u16"),
+            Type::U32 => self.push_str("u32"),
+            Type::U64 => self.push_str("u64"),
+            Type::S8 => self.push_str("i8"),
+            Type::S16 => self.push_str("i16"),
+            Type::S32 => self.push_str("i32"),
+            Type::S64 => self.push_str("i64"),
+            Type::Float32 => self.push_str("f32"),
+            Type::Float64 => self.push_str("f64"),
+            Type::Char => self.push_str("char"),
+            Type::String => match mode {
+                TypeMode::AllBorrowed(lt) => {
+                    self.push_str("&");
+                    if lt != "'_" {
+                        self.push_str(lt);
+                        self.push_str(" ");
+                    }
+                    self.push_str("str");
+                }
+                TypeMode::Owned => self.push_str("String"),
+            },
+        }
+    }
+
+    fn print_optional_ty(&mut self, ty: Option<&Type>, mode: TypeMode) {
+        match ty {
+            Some(ty) => self.print_ty(ty, mode),
+            None => self.push_str("()"),
+        }
+    }
+
+    fn print_tyid(&mut self, id: TypeId, mode: TypeMode) {
+        let info = self.info(id);
+        let lt = self.lifetime_for(&info, mode);
+        let ty = &self.iface().types[id];
+        if ty.name.is_some() {
+            let name = if lt.is_some() {
+                self.param_name(id)
+            } else {
+                self.result_name(id)
+            };
+            self.push_str(&name);
+
+            // If the type recursively owns data and it's a
+            // variant/record/list, then we need to place the
+            // lifetime parameter on the type as well.
+            if info.has_list && needs_generics(self.iface(), &ty.kind) {
+                self.print_generics(lt);
+            }
+
+            return;
+
+            fn needs_generics(iface: &Interface, ty: &TypeDefKind) -> bool {
+                match ty {
+                    TypeDefKind::Variant(_)
+                    | TypeDefKind::Record(_)
+                    | TypeDefKind::Option(_)
+                    | TypeDefKind::Result(_)
+                    | TypeDefKind::Future(_)
+                    | TypeDefKind::Stream(_)
+                    | TypeDefKind::List(_)
+                    | TypeDefKind::Flags(_)
+                    | TypeDefKind::Enum(_)
+                    | TypeDefKind::Tuple(_)
+                    | TypeDefKind::Union(_) => true,
+                    TypeDefKind::Type(Type::Id(t)) => needs_generics(iface, &iface.types[*t].kind),
+                    TypeDefKind::Type(Type::String) => true,
+                    TypeDefKind::Type(_) => false,
+                }
+            }
+        }
+
+        match &ty.kind {
+            TypeDefKind::List(t) => self.print_list(t, mode),
+
+            TypeDefKind::Option(t) => {
+                self.push_str("Option<");
+                self.print_ty(t, mode);
+                self.push_str(">");
+            }
+
+            TypeDefKind::Result(r) => {
+                self.push_str("Result<");
+                self.print_optional_ty(r.ok.as_ref(), mode);
+                self.push_str(",");
+                self.print_optional_ty(r.err.as_ref(), mode);
+                self.push_str(">");
+            }
+
+            TypeDefKind::Variant(_) => panic!("unsupported anonymous variant"),
+
+            // Tuple-like records are mapped directly to Rust tuples of
+            // types. Note the trailing comma after each member to
+            // appropriately handle 1-tuples.
+            TypeDefKind::Tuple(t) => {
+                self.push_str("(");
+                for ty in t.types.iter() {
+                    self.print_ty(ty, mode);
+                    self.push_str(",");
+                }
+                self.push_str(")");
+            }
+            TypeDefKind::Record(_) => {
+                panic!("unsupported anonymous type reference: record")
+            }
+            TypeDefKind::Flags(_) => {
+                panic!("unsupported anonymous type reference: flags")
+            }
+            TypeDefKind::Enum(_) => {
+                panic!("unsupported anonymous type reference: enum")
+            }
+            TypeDefKind::Union(_) => {
+                panic!("unsupported anonymous type reference: union")
+            }
+            TypeDefKind::Future(ty) => {
+                self.push_str("Future<");
+                self.print_optional_ty(ty.as_ref(), mode);
+                self.push_str(">");
+            }
+            TypeDefKind::Stream(stream) => {
+                self.push_str("Stream<");
+                self.print_optional_ty(stream.element.as_ref(), mode);
+                self.push_str(",");
+                self.print_optional_ty(stream.end.as_ref(), mode);
+                self.push_str(">");
+            }
+
+            TypeDefKind::Type(t) => self.print_ty(t, mode),
+        }
+    }
+
+    fn print_list(&mut self, ty: &Type, mode: TypeMode) {
+        match mode {
+            TypeMode::AllBorrowed(lt) => {
+                self.push_str("&");
+                if lt != "'_" {
+                    self.push_str(lt);
+                    self.push_str(" ");
+                }
+                self.push_str("[");
+                self.print_ty(ty, mode);
+                self.push_str("]");
+            }
+            TypeMode::Owned => {
+                self.push_str("Vec<");
+                self.print_ty(ty, mode);
+                self.push_str(">");
+            }
+        }
+    }
+
+    fn print_generics(&mut self, lifetime: Option<&str>) {
+        if lifetime.is_none() {
+            return;
+        }
+        self.push_str("<");
+        if let Some(lt) = lifetime {
+            self.push_str(lt);
+            self.push_str(",");
+        }
+        self.push_str(">");
+    }
+
+    fn modes_of(&self, ty: TypeId) -> Vec<(String, TypeMode)> {
+        let info = self.info(ty);
+        let mut result = Vec::new();
+        if info.param {
+            result.push((self.param_name(ty), self.default_param_mode()));
+        }
+        if info.result && (!info.param || self.uses_two_names(&info)) {
+            result.push((self.result_name(ty), TypeMode::Owned));
+        }
+        return 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.iface().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::Union(_) => out.push_str("Union"),
+                    },
+                }
+            }
+        }
+    }
+
+    fn write_optional_name(&self, ty: Option<&Type>, out: &mut String) {
+        match ty {
+            Some(ty) => self.write_name(ty, out),
+            None => out.push_str("()"),
+        }
+    }
+
+    /// Returns the names for the cases of the passed union.
+    fn union_case_names(&self, union: &Union) -> Vec<String> {
+        enum UsedState<'a> {
+            /// This name has been used once before.
+            ///
+            /// Contains a reference to the name given to the first usage so that a suffix can be added to it.
+            Once(&'a mut String),
+            /// This name has already been used multiple times.
+            ///
+            /// Contains the number of times this has already been used.
+            Multiple(usize),
+        }
+
+        // A `Vec` of the names we're assigning each of the union's cases in order.
+        let mut case_names = vec![String::new(); union.cases.len()];
+        // A map from case names to their `UsedState`.
+        let mut used = HashMap::new();
+        for (case, name) in union.cases.iter().zip(case_names.iter_mut()) {
+            self.write_name(&case.ty, name);
+
+            match used.get_mut(name.as_str()) {
+                None => {
+                    // Initialise this name's `UsedState`, with a mutable reference to this name
+                    // in case we have to add a suffix to it later.
+                    used.insert(name.clone(), UsedState::Once(name));
+                    // Since this is the first (and potentially only) usage of this name,
+                    // we don't need to add a suffix here.
+                }
+                Some(state) => match state {
+                    UsedState::Multiple(n) => {
+                        // Add a suffix of the index of this usage.
+                        write!(name, "{n}").unwrap();
+                        // Add one to the number of times this type has been used.
+                        *n += 1;
+                    }
+                    UsedState::Once(first) => {
+                        // Add a suffix of 0 to the first usage.
+                        first.push('0');
+                        // We now get a suffix of 1.
+                        name.push('1');
+                        // Then update the state.
+                        *state = UsedState::Multiple(2);
+                    }
+                },
+            }
+        }
+
+        case_names
+    }
+
+    fn param_name(&self, ty: TypeId) -> String {
+        let info = self.info(ty);
+        let name = self.iface().types[ty]
+            .name
+            .as_ref()
+            .unwrap()
+            .to_upper_camel_case();
+        if self.uses_two_names(&info) {
+            format!("{}Param", name)
+        } else {
+            name
+        }
+    }
+
+    fn result_name(&self, ty: TypeId) -> String {
+        let info = self.info(ty);
+        let name = self.iface().types[ty]
+            .name
+            .as_ref()
+            .unwrap()
+            .to_upper_camel_case();
+        if self.uses_two_names(&info) {
+            format!("{}Result", name)
+        } else {
+            name
+        }
+    }
+
+    fn uses_two_names(&self, info: &TypeInfo) -> bool {
+        info.has_list
+            && info.param
+            && info.result
+            && match self.default_param_mode() {
+                TypeMode::AllBorrowed(_) => true,
+                TypeMode::Owned => false,
+            }
+    }
+
+    fn lifetime_for(&self, info: &TypeInfo, mode: TypeMode) -> Option<&'static str> {
+        match mode {
+            TypeMode::AllBorrowed(s) if info.has_list => Some(s),
+            _ => None,
+        }
+    }
+}
+
+pub fn to_rust_ident(name: &str) -> String {
+    match name {
+        // Escape Rust keywords.
+        // Source: https://doc.rust-lang.org/reference/keywords.html
+        "as" => "as_".into(),
+        "break" => "break_".into(),
+        "const" => "const_".into(),
+        "continue" => "continue_".into(),
+        "crate" => "crate_".into(),
+        "else" => "else_".into(),
+        "enum" => "enum_".into(),
+        "extern" => "extern_".into(),
+        "false" => "false_".into(),
+        "fn" => "fn_".into(),
+        "for" => "for_".into(),
+        "if" => "if_".into(),
+        "impl" => "impl_".into(),
+        "in" => "in_".into(),
+        "let" => "let_".into(),
+        "loop" => "loop_".into(),
+        "match" => "match_".into(),
+        "mod" => "mod_".into(),
+        "move" => "move_".into(),
+        "mut" => "mut_".into(),
+        "pub" => "pub_".into(),
+        "ref" => "ref_".into(),
+        "return" => "return_".into(),
+        "self" => "self_".into(),
+        "static" => "static_".into(),
+        "struct" => "struct_".into(),
+        "super" => "super_".into(),
+        "trait" => "trait_".into(),
+        "true" => "true_".into(),
+        "type" => "type_".into(),
+        "unsafe" => "unsafe_".into(),
+        "use" => "use_".into(),
+        "where" => "where_".into(),
+        "while" => "while_".into(),
+        "async" => "async_".into(),
+        "await" => "await_".into(),
+        "dyn" => "dyn_".into(),
+        "abstract" => "abstract_".into(),
+        "become" => "become_".into(),
+        "box" => "box_".into(),
+        "do" => "do_".into(),
+        "final" => "final_".into(),
+        "macro" => "macro_".into(),
+        "override" => "override_".into(),
+        "priv" => "priv_".into(),
+        "typeof" => "typeof_".into(),
+        "unsized" => "unsized_".into(),
+        "virtual" => "virtual_".into(),
+        "yield" => "yield_".into(),
+        "try" => "try_".into(),
+        s => s.to_snake_case(),
+    }
+}
diff --git a/crates/wit-bindgen/src/source.rs b/crates/wit-bindgen/src/source.rs
new file mode 100644
index 000000000..f7099f49e
--- /dev/null
+++ b/crates/wit-bindgen/src/source.rs
@@ -0,0 +1,130 @@
+use std::fmt::{self, Write};
+use std::ops::Deref;
+
+/// Helper structure to maintain indentation automatically when printing.
+#[derive(Default)]
+pub struct Source {
+    s: String,
+    indent: usize,
+}
+
+impl Source {
+    pub fn push_str(&mut self, src: &str) {
+        let lines = src.lines().collect::<Vec<_>>();
+        for (i, line) in lines.iter().enumerate() {
+            let trimmed = line.trim();
+            if trimmed.starts_with('}') && self.s.ends_with("  ") {
+                self.s.pop();
+                self.s.pop();
+            }
+            self.s.push_str(if lines.len() == 1 {
+                line
+            } else {
+                line.trim_start()
+            });
+            if trimmed.ends_with('{') {
+                self.indent += 1;
+            }
+            if trimmed.starts_with('}') {
+                // Note that a `saturating_sub` is used here to prevent a panic
+                // here in the case of invalid code being generated in debug
+                // mode. It's typically easier to debug those issues through
+                // looking at the source code rather than getting a panic.
+                self.indent = self.indent.saturating_sub(1);
+            }
+            if i != lines.len() - 1 || src.ends_with('\n') {
+                self.newline();
+            }
+        }
+    }
+
+    pub fn indent(&mut self, amt: usize) {
+        self.indent += amt;
+    }
+
+    pub fn deindent(&mut self, amt: usize) {
+        self.indent -= amt;
+    }
+
+    fn newline(&mut self) {
+        self.s.push('\n');
+        for _ in 0..self.indent {
+            self.s.push_str("  ");
+        }
+    }
+
+    pub fn as_mut_string(&mut self) -> &mut String {
+        &mut self.s
+    }
+}
+
+impl Write for Source {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        self.push_str(s);
+        Ok(())
+    }
+}
+
+impl Deref for Source {
+    type Target = str;
+    fn deref(&self) -> &str {
+        &self.s
+    }
+}
+
+impl From<Source> for String {
+    fn from(s: Source) -> String {
+        s.s
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Source;
+
+    #[test]
+    fn simple_append() {
+        let mut s = Source::default();
+        s.push_str("x");
+        assert_eq!(s.s, "x");
+        s.push_str("y");
+        assert_eq!(s.s, "xy");
+        s.push_str("z ");
+        assert_eq!(s.s, "xyz ");
+        s.push_str(" a ");
+        assert_eq!(s.s, "xyz  a ");
+        s.push_str("\na");
+        assert_eq!(s.s, "xyz  a \na");
+    }
+
+    #[test]
+    fn newline_remap() {
+        let mut s = Source::default();
+        s.push_str("function() {\n");
+        s.push_str("y\n");
+        s.push_str("}\n");
+        assert_eq!(s.s, "function() {\n  y\n}\n");
+    }
+
+    #[test]
+    fn if_else() {
+        let mut s = Source::default();
+        s.push_str("if() {\n");
+        s.push_str("y\n");
+        s.push_str("} else if () {\n");
+        s.push_str("z\n");
+        s.push_str("}\n");
+        assert_eq!(s.s, "if() {\n  y\n} else if () {\n  z\n}\n");
+    }
+
+    #[test]
+    fn trim_ws() {
+        let mut s = Source::default();
+        s.push_str(
+            "function() {
+                x
+        }",
+        );
+        assert_eq!(s.s, "function() {\n  x\n}");
+    }
+}
diff --git a/crates/wit-bindgen/src/types.rs b/crates/wit-bindgen/src/types.rs
new file mode 100644
index 000000000..3709987f3
--- /dev/null
+++ b/crates/wit-bindgen/src/types.rs
@@ -0,0 +1,207 @@
+use std::collections::HashMap;
+use wit_parser::*;
+
+#[derive(Default)]
+pub struct Types {
+    type_info: HashMap<TypeId, TypeInfo>,
+}
+
+#[derive(Default, Clone, Copy, Debug, PartialEq)]
+pub struct TypeInfo {
+    /// Whether or not this type is ever used (transitively) within the
+    /// parameter of a function.
+    pub param: bool,
+
+    /// Whether or not this type is ever used (transitively) within the
+    /// result of a function.
+    pub result: bool,
+
+    /// Whether or not this type is ever used (transitively) within the
+    /// error case in the result of a function.
+    pub error: bool,
+
+    /// Whether or not this type (transitively) has a list.
+    pub has_list: bool,
+}
+
+impl std::ops::BitOrAssign for TypeInfo {
+    fn bitor_assign(&mut self, rhs: Self) {
+        self.param |= rhs.param;
+        self.result |= rhs.result;
+        self.error |= rhs.error;
+        self.has_list |= rhs.has_list;
+    }
+}
+
+impl Types {
+    pub fn analyze(&mut self, iface: &Interface) {
+        for (t, _) in iface.types.iter() {
+            self.type_id_info(iface, t);
+        }
+        for f in iface.functions.iter() {
+            for (_, ty) in f.params.iter() {
+                self.set_param_result_ty(
+                    iface,
+                    ty,
+                    TypeInfo {
+                        param: true,
+                        ..TypeInfo::default()
+                    },
+                );
+            }
+            for ty in f.results.iter_types() {
+                self.set_param_result_ty(
+                    iface,
+                    ty,
+                    TypeInfo {
+                        result: true,
+                        ..TypeInfo::default()
+                    },
+                );
+            }
+        }
+    }
+
+    pub fn get(&self, id: TypeId) -> TypeInfo {
+        self.type_info[&id]
+    }
+
+    fn type_id_info(&mut self, iface: &Interface, ty: TypeId) -> TypeInfo {
+        if let Some(info) = self.type_info.get(&ty) {
+            return *info;
+        }
+        let mut info = TypeInfo::default();
+        match &iface.types[ty].kind {
+            TypeDefKind::Record(r) => {
+                for field in r.fields.iter() {
+                    info |= self.type_info(iface, &field.ty);
+                }
+            }
+            TypeDefKind::Tuple(t) => {
+                for ty in t.types.iter() {
+                    info |= self.type_info(iface, ty);
+                }
+            }
+            TypeDefKind::Flags(_) => {}
+            TypeDefKind::Enum(_) => {}
+            TypeDefKind::Variant(v) => {
+                for case in v.cases.iter() {
+                    info |= self.optional_type_info(iface, case.ty.as_ref());
+                }
+            }
+            TypeDefKind::List(ty) => {
+                info = self.type_info(iface, ty);
+                info.has_list = true;
+            }
+            TypeDefKind::Type(ty) => {
+                info = self.type_info(iface, ty);
+            }
+            TypeDefKind::Option(ty) => {
+                info = self.type_info(iface, ty);
+            }
+            TypeDefKind::Result(r) => {
+                info = self.optional_type_info(iface, r.ok.as_ref());
+                info |= self.optional_type_info(iface, r.err.as_ref());
+            }
+            TypeDefKind::Union(u) => {
+                for case in u.cases.iter() {
+                    info |= self.type_info(iface, &case.ty);
+                }
+            }
+            TypeDefKind::Future(ty) => {
+                info = self.optional_type_info(iface, ty.as_ref());
+            }
+            TypeDefKind::Stream(stream) => {
+                info = self.optional_type_info(iface, stream.element.as_ref());
+                info |= self.optional_type_info(iface, stream.end.as_ref());
+            }
+        }
+        self.type_info.insert(ty, info);
+        info
+    }
+
+    fn type_info(&mut self, iface: &Interface, ty: &Type) -> TypeInfo {
+        let mut info = TypeInfo::default();
+        match ty {
+            Type::String => info.has_list = true,
+            Type::Id(id) => return self.type_id_info(iface, *id),
+            _ => {}
+        }
+        info
+    }
+
+    fn optional_type_info(&mut self, iface: &Interface, ty: Option<&Type>) -> TypeInfo {
+        match ty {
+            Some(ty) => self.type_info(iface, ty),
+            None => TypeInfo::default(),
+        }
+    }
+
+    fn set_param_result_id(&mut self, iface: &Interface, ty: TypeId, info: TypeInfo) {
+        match &iface.types[ty].kind {
+            TypeDefKind::Record(r) => {
+                for field in r.fields.iter() {
+                    self.set_param_result_ty(iface, &field.ty, info)
+                }
+            }
+            TypeDefKind::Tuple(t) => {
+                for ty in t.types.iter() {
+                    self.set_param_result_ty(iface, ty, info)
+                }
+            }
+            TypeDefKind::Flags(_) => {}
+            TypeDefKind::Enum(_) => {}
+            TypeDefKind::Variant(v) => {
+                for case in v.cases.iter() {
+                    self.set_param_result_optional_ty(iface, case.ty.as_ref(), info)
+                }
+            }
+            TypeDefKind::List(ty) | TypeDefKind::Type(ty) | TypeDefKind::Option(ty) => {
+                self.set_param_result_ty(iface, ty, info)
+            }
+            TypeDefKind::Result(r) => {
+                self.set_param_result_optional_ty(iface, r.ok.as_ref(), info);
+                let mut info2 = info;
+                info2.error = info.result;
+                self.set_param_result_optional_ty(iface, r.err.as_ref(), info2);
+            }
+            TypeDefKind::Union(u) => {
+                for case in u.cases.iter() {
+                    self.set_param_result_ty(iface, &case.ty, info)
+                }
+            }
+            TypeDefKind::Future(ty) => self.set_param_result_optional_ty(iface, ty.as_ref(), info),
+            TypeDefKind::Stream(stream) => {
+                self.set_param_result_optional_ty(iface, stream.element.as_ref(), info);
+                self.set_param_result_optional_ty(iface, stream.end.as_ref(), info);
+            }
+        }
+    }
+
+    fn set_param_result_ty(&mut self, iface: &Interface, ty: &Type, info: TypeInfo) {
+        match ty {
+            Type::Id(id) => {
+                self.type_id_info(iface, *id);
+                let cur = self.type_info.get_mut(id).unwrap();
+                let prev = *cur;
+                *cur |= info;
+                if prev != *cur {
+                    self.set_param_result_id(iface, *id, info);
+                }
+            }
+            _ => {}
+        }
+    }
+
+    fn set_param_result_optional_ty(
+        &mut self,
+        iface: &Interface,
+        ty: Option<&Type>,
+        info: TypeInfo,
+    ) {
+        match ty {
+            Some(ty) => self.set_param_result_ty(iface, ty, info),
+            None => (),
+        }
+    }
+}
diff --git a/scripts/publish.rs b/scripts/publish.rs
index 77556074a..1de6f94be 100644
--- a/scripts/publish.rs
+++ b/scripts/publish.rs
@@ -47,6 +47,7 @@ const CRATES_TO_PUBLISH: &[&str] = &[
     // wasmtime
     "wasmtime-asm-macros",
     "wasmtime-component-util",
+    "wasmtime-wit-bindgen",
     "wasmtime-component-macro",
     "wasmtime-jit-debug",
     "wasmtime-fiber",
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index e7e799b31..51b300ac4 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -392,6 +392,16 @@ a few `unsafe` blocks related to utf-8 validation which are locally verifiable
 as correct and otherwise this crate is good to go.
 """
 
+[[audits.pulldown-cmark]]
+who = "Alex Crichton <alex@alexcrichton.com>"
+criteria = "safe-to-deploy"
+version = "0.8.0"
+notes = """
+This crate has `unsafe` blocks and they're all related to SIMD-acceleration and
+are otherwise not doing other `unsafe` operations. Additionally the crate does
+not do anything other than markdown rendering as is expected.
+"""
+
 [[audits.regalloc2]]
 who = "Jamey Sharp <jsharp@fastly.com>"
 criteria = "safe-to-deploy"
@@ -484,6 +494,15 @@ intended to multiplex across the internal representation of a tinyvec,
 presumably. This trivially doesn't contain anything bad.
 """
 
+[[audits.unicase]]
+who = "Alex Crichton <alex@alexcrichton.com>"
+criteria = "safe-to-deploy"
+version = "2.6.0"
+notes = """
+This crate contains no `unsafe` code and no unnecessary use of the standard
+library.
+"""
+
 [[audits.unicode-bidi]]
 who = "Alex Crichton <alex@alexcrichton.com>"
 criteria = "safe-to-deploy"
@@ -967,3 +986,9 @@ criteria = "safe-to-deploy"
 version = "0.34.0"
 notes = "I am the author of this crate."
 
+[[audits.wit-parser]]
+who = "Alex Crichton <alex@alexcrichton.com>"
+criteria = "safe-to-deploy"
+version = "0.3.0"
+notes = "The Bytecode Alliance is the author of this crate."
+
diff --git a/supply-chain/config.toml b/supply-chain/config.toml
index 0c6dd1646..c59b1fa86 100644
--- a/supply-chain/config.toml
+++ b/supply-chain/config.toml
@@ -338,10 +338,6 @@ criteria = "safe-to-deploy"
 version = "0.10.0"
 criteria = "safe-to-deploy"
 
-[[exemptions.half]]
-version = "1.8.2"
-criteria = "safe-to-run"
-
 [[exemptions.hermit-abi]]
 version = "0.1.19"
 criteria = "safe-to-deploy"
@@ -738,10 +734,6 @@ criteria = "safe-to-deploy"
 version = "1.0.137"
 criteria = "safe-to-deploy"
 
-[[exemptions.serde_cbor]]
-version = "0.11.2"
-criteria = "safe-to-run"
-
 [[exemptions.serde_derive]]
 version = "1.0.137"
 criteria = "safe-to-deploy"
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index c89595997..1cfb9fa28 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -169,6 +169,16 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
 criteria = "safe-to-deploy"
 delta = "0.2.6 -> 0.2.7"
 
+[[audits.mozilla.audits.half]]
+who = "John M. Schanck <jschanck@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "1.8.2"
+notes = """
+This crate contains unsafe code for bitwise casts to/from binary16 floating-point
+format. I've reviewed these and found no issues. There are no uses of ambient
+capabilities.
+"""
+
 [[audits.mozilla.audits.hashbrown]]
 who = "Mike Hommey <mh+mozilla@glandium.org>"
 criteria = "safe-to-deploy"
@@ -288,6 +298,16 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
 criteria = "safe-to-deploy"
 delta = "1.0.143 -> 1.0.144"
 
+[[audits.mozilla.audits.serde_cbor]]
+who = "R. Martinho Fernandes <bugs@rmf.io>"
+criteria = "safe-to-deploy"
+version = "0.11.1"
+
+[[audits.mozilla.audits.serde_cbor]]
+who = "John M. Schanck <jschanck@mozilla.com>"
+criteria = "safe-to-deploy"
+delta = "0.11.1 -> 0.11.2"
+
 [[audits.mozilla.audits.serde_derive]]
 who = "Mike Hommey <mh+mozilla@glandium.org>"
 criteria = "safe-to-deploy"


Test patch:
diff --git a/cranelift/filetests/filetests/isa/riscv64/constants.clif b/cranelift/filetests/filetests/isa/riscv64/constants.clif
index 1b4af0bdf..43190c6c7 100644
--- a/cranelift/filetests/filetests/isa/riscv64/constants.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/constants.clif
@@ -50,10 +50,7 @@ block0:
 }
 
 ; block0:
-;   auipc t1,0
-;   ld a0,12(t1)
-;   j 12
-;   .8byte 0xffff0000
+;   auipc a0,0; ld a0,12(a0); j 12; .8byte 0xffff0000
 ;   ret
 
 function %f() -> i64 {
@@ -63,10 +60,7 @@ block0:
 }
 
 ; block0:
-;   auipc t1,0
-;   ld a0,12(t1)
-;   j 12
-;   .8byte 0xffff00000000
+;   auipc a0,0; ld a0,12(a0); j 12; .8byte 0xffff00000000
 ;   ret
 
 function %f() -> i64 {
@@ -76,10 +70,7 @@ block0:
 }
 
 ; block0:
-;   auipc t1,0
-;   ld a0,12(t1)
-;   j 12
-;   .8byte 0xffff000000000000
+;   auipc a0,0; ld a0,12(a0); j 12; .8byte 0xffff000000000000
 ;   ret
 
 function %f() -> i64 {
@@ -109,10 +100,7 @@ block0:
 }
 
 ; block0:
-;   auipc t1,0
-;   ld a0,12(t1)
-;   j 12
-;   .8byte 0xffffffff0000ffff
+;   auipc a0,0; ld a0,12(a0); j 12; .8byte 0xffffffff0000ffff
 ;   ret
 
 function %f() -> i64 {
@@ -122,10 +110,7 @@ block0:
 }
 
 ; block0:
-;   auipc t1,0
-;   ld a0,12(t1)
-;   j 12
-;   .8byte 0xffff0000ffffffff
+;   auipc a0,0; ld a0,12(a0); j 12; .8byte 0xffff0000ffffffff
 ;   ret
 
 function %f() -> i64 {
@@ -135,10 +120,7 @@ block0:
 }
 
 ; block0:
-;   auipc t1,0
-;   ld a0,12(t1)
-;   j 12
-;   .8byte 0xffffffffffff
+;   auipc a0,0; ld a0,12(a0); j 12; .8byte 0xffffffffffff
 ;   ret
 
 function %f() -> i64 {
@@ -148,10 +130,7 @@ block0:
 }
 
 ; block0:
-;   auipc t1,0
-;   ld a0,12(t1)
-;   j 12
-;   .8byte 0xf34bf0a31212003a
+;   auipc a0,0; ld a0,12(a0); j 12; .8byte 0xf34bf0a31212003a
 ;   ret
 
 function %f() -> i64 {
@@ -161,10 +140,7 @@ block0:
 }
 
 ; block0:
-;   auipc t1,0
-;   ld a0,12(t1)
-;   j 12
-;   .8byte 0x12e900001ef40000
+;   auipc a0,0; ld a0,12(a0); j 12; .8byte 0x12e900001ef40000
 ;   ret
 
 function %f() -> i64 {
@@ -174,10 +150,7 @@ block0:
 }
 
 ; block0:
-;   auipc t1,0
-;   ld a0,12(t1)
-;   j 12
-;   .8byte 0x12e9ffff1ef4ffff
+;   auipc a0,0; ld a0,12(a0); j 12; .8byte 0x12e9ffff1ef4ffff
 ;   ret
 
 function %f() -> i32 {
@@ -197,10 +170,7 @@ block0:
 }
 
 ; block0:
-;   auipc t1,0
-;   ld a0,12(t1)
-;   j 12
-;   .8byte 0xfffffff7
+;   auipc a0,0; ld a0,12(a0); j 12; .8byte 0xfffffff7
 ;   ret
 
 function %f() -> i64 {
@@ -210,10 +180,7 @@ block0:
 }
 
 ; block0:
-;   auipc t1,0
-;   ld a0,12(t1)
-;   j 12
-;   .8byte 0xfffffff7
+;   auipc a0,0; ld a0,12(a0); j 12; .8byte 0xfffffff7
 ;   ret
 
 function %f() -> i64 {
@@ -233,11 +200,8 @@ block0:
 }
 
 ; block0:
-;   auipc t2,0
-;   ld t2,12(t2)
-;   j 12
-;   .8byte 0x3ff0000000000000
-;   fmv.d.x fa0,t2
+;   auipc t1,0; ld t1,12(t1); j 12; .8byte 0x3ff0000000000000
+;   fmv.d.x fa0,t1
 ;   ret
 
 function %f() -> f32 {
@@ -258,11 +222,8 @@ block0:
 }
 
 ; block0:
-;   auipc t2,0
-;   ld t2,12(t2)
-;   j 12
-;   .8byte 0x4049000000000000
-;   fmv.d.x fa0,t2
+;   auipc t1,0; ld t1,12(t1); j 12; .8byte 0x4049000000000000
+;   fmv.d.x fa0,t1
 ;   ret
 
 function %f() -> f32 {
@@ -305,11 +266,8 @@ block0:
 }
 
 ; block0:
-;   auipc t2,0
-;   ld t2,12(t2)
-;   j 12
-;   .8byte 0xc030000000000000
-;   fmv.d.x fa0,t2
+;   auipc t1,0; ld t1,12(t1); j 12; .8byte 0xc030000000000000
+;   fmv.d.x fa0,t1
 ;   ret
 
 function %f() -> f32 {
@@ -319,10 +277,7 @@ block0:
 }
 
 ; block0:
-;   auipc t2,0
-;   lwu t2,12(t2)
-;   j 8
-;   .4byte 0xc1800000
-;   fmv.w.x fa0,t2
+;   auipc t1,0; ld t1,12(t1); j 8; .4byte 0xc1800000
+;   fmv.w.x fa0,t1
 ;   ret
 
diff --git a/crates/component-macro/test-helpers/Cargo.toml b/crates/component-macro/test-helpers/Cargo.toml
new file mode 100644
index 000000000..e38882320
--- /dev/null
+++ b/crates/component-macro/test-helpers/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "component-macro-test-helpers"
+version = "0.0.0"
+edition.workspace = true
+publish = false
+license = "Apache-2.0 WITH LLVM-exception"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2 = "1.0"
+quote = "1.0"
diff --git a/crates/component-macro/test-helpers/src/lib.rs b/crates/component-macro/test-helpers/src/lib.rs
new file mode 100644
index 000000000..8b4e7059b
--- /dev/null
+++ b/crates/component-macro/test-helpers/src/lib.rs
@@ -0,0 +1,23 @@
+use proc_macro::TokenStream;
+use proc_macro2::{Ident, Span};
+use quote::quote;
+
+#[proc_macro]
+pub fn foreach(input: TokenStream) -> TokenStream {
+    let input = proc_macro2::TokenStream::from(input);
+    let mut cwd = std::env::current_dir().unwrap();
+    cwd.push("crates/component-macro/tests/codegen");
+    let mut result = Vec::new();
+    for f in cwd.read_dir().unwrap() {
+        let f = f.unwrap().path();
+        if f.extension().and_then(|s| s.to_str()) == Some("wit") {
+            let name = f.file_stem().unwrap().to_str().unwrap();
+            let name = Ident::new(&name.replace("-", "_"), Span::call_site());
+            let path = f.to_str().unwrap();
+            result.push(quote! {
+                #input!(#name #path);
+            });
+        }
+    }
+    (quote!( #(#result)*)).into()
+}
diff --git a/crates/component-macro/tests/codegen.rs b/crates/component-macro/tests/codegen.rs
new file mode 100644
index 000000000..10aee99ef
--- /dev/null
+++ b/crates/component-macro/tests/codegen.rs
@@ -0,0 +1,24 @@
+macro_rules! gentest {
+    ($id:ident $path:tt) => {
+        mod $id {
+            mod normal {
+                wasmtime::component::bindgen!($path);
+            }
+            mod async_ {
+                wasmtime::component::bindgen!({
+                    path: $path,
+                    async: true,
+                });
+            }
+            mod tracing {
+                wasmtime::component::bindgen!({
+                    path: $path,
+                    tracing: true,
+                });
+            }
+        }
+        // ...
+    };
+}
+
+component_macro_test_helpers::foreach!(gentest);
diff --git a/crates/component-macro/tests/codegen/char.wit b/crates/component-macro/tests/codegen/char.wit
new file mode 100644
index 000000000..85787e840
--- /dev/null
+++ b/crates/component-macro/tests/codegen/char.wit
@@ -0,0 +1,12 @@
+interface chars {
+  /// A function that accepts a character
+  take-char: func(x: char)
+  /// A function that returns a character
+  return-char: func() -> char
+}
+
+world the-world {
+  import imports: chars
+  export exports: chars
+  default export chars
+}
diff --git a/crates/component-macro/tests/codegen/conventions.wit b/crates/component-macro/tests/codegen/conventions.wit
new file mode 100644
index 000000000..23c472c18
--- /dev/null
+++ b/crates/component-macro/tests/codegen/conventions.wit
@@ -0,0 +1,39 @@
+// hello 🐱 world
+
+interface conventions {
+  kebab-case: func()
+
+  record ludicrous-speed {
+    how-fast-are-you-going: u32,
+    i-am-going-extremely-slow: u64,
+  }
+
+  foo: func(x: ludicrous-speed)
+  %function-with-dashes: func()
+  %function-with-no-weird-characters: func()
+
+  apple: func()
+  apple-pear: func()
+  apple-pear-grape: func()
+  a0: func()
+
+  // Comment out identifiers that collide when mapped to snake_case, for now; see
+  // https://github.com/WebAssembly/component-model/issues/118
+  //APPLE: func()
+  //APPLE-pear-GRAPE: func()
+  //apple-PEAR-grape: func()
+
+  is-XML: func()
+
+  %explicit: func()
+  %explicit-kebab: func()
+
+  // Identifiers with the same name as keywords are quoted.
+  %bool: func()
+}
+
+world the-world {
+  import imports: conventions
+  export exports: conventions
+  default export conventions
+}
diff --git a/crates/component-macro/tests/codegen/empty.wit b/crates/component-macro/tests/codegen/empty.wit
new file mode 100644
index 000000000..48c1b8597
--- /dev/null
+++ b/crates/component-macro/tests/codegen/empty.wit
@@ -0,0 +1,1 @@
+world empty {}
diff --git a/crates/component-macro/tests/codegen/flags.wit b/crates/component-macro/tests/codegen/flags.wit
new file mode 100644
index 000000000..d92111ff9
--- /dev/null
+++ b/crates/component-macro/tests/codegen/flags.wit
@@ -0,0 +1,54 @@
+interface flegs {
+  flags flag1 {
+    b0,
+  }
+
+  flags flag2 {
+    b0, b1,
+  }
+
+  flags flag4 {
+    b0, b1, b2, b3,
+  }
+
+  flags flag8 {
+    b0, b1, b2, b3, b4, b5, b6, b7,
+  }
+
+  flags flag16 {
+    b0, b1, b2, b3, b4, b5, b6, b7,
+    b8, b9, b10, b11, b12, b13, b14, b15,
+  }
+
+  flags flag32 {
+    b0, b1, b2, b3, b4, b5, b6, b7,
+    b8, b9, b10, b11, b12, b13, b14, b15,
+    b16, b17, b18, b19, b20, b21, b22, b23,
+    b24, b25, b26, b27, b28, b29, b30, b31,
+  }
+
+  flags flag64 {
+    b0, b1, b2, b3, b4, b5, b6, b7,
+    b8, b9, b10, b11, b12, b13, b14, b15,
+    b16, b17, b18, b19, b20, b21, b22, b23,
+    b24, b25, b26, b27, b28, b29, b30, b31,
+    b32, b33, b34, b35, b36, b37, b38, b39,
+    b40, b41, b42, b43, b44, b45, b46, b47,
+    b48, b49, b50, b51, b52, b53, b54, b55,
+    b56, b57, b58, b59, b60, b61, b62, b63,
+  }
+
+  roundtrip-flag1: func(x: flag1) -> flag1
+  roundtrip-flag2: func(x: flag2) -> flag2
+  roundtrip-flag4: func(x: flag4) -> flag4
+  roundtrip-flag8: func(x: flag8) -> flag8
+  roundtrip-flag16: func(x: flag16) -> flag16
+  roundtrip-flag32: func(x: flag32) -> flag32
+  roundtrip-flag64: func(x: flag64) -> flag64
+}
+
+world the-flags {
+  import import-flags: flegs
+  export export-flags: flegs
+  default export flegs
+}
diff --git a/crates/component-macro/tests/codegen/floats.wit b/crates/component-macro/tests/codegen/floats.wit
new file mode 100644
index 000000000..274770cc1
--- /dev/null
+++ b/crates/component-macro/tests/codegen/floats.wit
@@ -0,0 +1,12 @@
+interface floats {
+  float32-param: func(x: float32)
+  float64-param: func(x: float64)
+  float32-result: func() -> float32
+  float64-result: func() -> float64
+}
+
+world the-world {
+  import imports: floats
+  export exports: floats
+  default export floats
+}
diff --git a/crates/component-macro/tests/codegen/integers.wit b/crates/component-macro/tests/codegen/integers.wit
new file mode 100644
index 000000000..c5e7f096c
--- /dev/null
+++ b/crates/component-macro/tests/codegen/integers.wit
@@ -0,0 +1,39 @@
+interface integers {
+  a1: func(x: u8)
+  a2: func(x: s8)
+  a3: func(x: u16)
+  a4: func(x: s16)
+  a5: func(x: u32)
+  a6: func(x: s32)
+  a7: func(x: u64)
+  a8: func(x: s64)
+
+  a9: func(
+    p1: u8,
+    p2: s8,
+    p3: u16,
+    p4: s16,
+    p5: u32,
+    p6: s32,
+    p7: u64,
+    p8: s64,
+  )
+
+
+  r1: func() -> u8
+  r2: func() -> s8
+  r3: func() -> u16
+  r4: func() -> s16
+  r5: func() -> u32
+  r6: func() -> s32
+  r7: func() -> u64
+  r8: func() -> s64
+
+  pair-ret: func() -> tuple<s64, u8>
+}
+
+world the-world {
+  import imports: integers
+  export exports: integers
+  default export integers
+}
diff --git a/crates/component-macro/tests/codegen/lists.wit b/crates/component-macro/tests/codegen/lists.wit
new file mode 100644
index 000000000..cb5b60435
--- /dev/null
+++ b/crates/component-macro/tests/codegen/lists.wit
@@ -0,0 +1,84 @@
+interface lists {
+  list-u8-param: func(x: list<u8>)
+  list-u16-param: func(x: list<u16>)
+  list-u32-param: func(x: list<u32>)
+  list-u64-param: func(x: list<u64>)
+  list-s8-param: func(x: list<s8>)
+  list-s16-param: func(x: list<s16>)
+  list-s32-param: func(x: list<s32>)
+  list-s64-param: func(x: list<s64>)
+  list-float32-param: func(x: list<float32>)
+  list-float64-param: func(x: list<float64>)
+
+  list-u8-ret: func() -> list<u8>
+  list-u16-ret: func() -> list<u16>
+  list-u32-ret: func() -> list<u32>
+  list-u64-ret: func() -> list<u64>
+  list-s8-ret: func() -> list<s8>
+  list-s16-ret: func() -> list<s16>
+  list-s32-ret: func() -> list<s32>
+  list-s64-ret: func() -> list<s64>
+  list-float32-ret: func() -> list<float32>
+  list-float64-ret: func() -> list<float64>
+
+  tuple-list: func(x: list<tuple<u8, s8>>) -> list<tuple<s64, u32>>
+  string-list-arg: func(a: list<string>)
+  string-list-ret: func() -> list<string>
+  tuple-string-list: func(x: list<tuple<u8, string>>) -> list<tuple<string, u8>>
+  string-list: func(x: list<string>) -> list<string>
+
+  record some-record {
+    x: string,
+    y: other-record,
+    z: list<other-record>,
+    c1: u32,
+    c2: u64,
+    c3: s32,
+    c4: s64,
+  }
+  record other-record {
+    a1: u32,
+    a2: u64,
+    a3: s32,
+    a4: s64,
+    b: string,
+    c: list<u8>,
+  }
+  record-list: func(x: list<some-record>) -> list<other-record>
+  record-list-reverse: func(x: list<other-record>) -> list<some-record>
+
+  variant some-variant {
+    a(string),
+    b,
+    c(u32),
+    d(list<other-variant>),
+  }
+  variant other-variant {
+    a,
+    b(u32),
+    c(string),
+  }
+  variant-list: func(x: list<some-variant>) -> list<other-variant>
+
+  type load-store-all-sizes = list<tuple<
+    string,
+    u8,
+    s8,
+    u16,
+    s16,
+    u32,
+    s32,
+    u64,
+    s64,
+    float32,
+    float64,
+    char,
+  >>
+  load-store-everything: func(a: load-store-all-sizes) -> load-store-all-sizes
+}
+
+world the-lists {
+  import import-lists: lists
+  export export-lists: lists
+  default export lists
+}
diff --git a/crates/component-macro/tests/codegen/many-arguments.wit b/crates/component-macro/tests/codegen/many-arguments.wit
new file mode 100644
index 000000000..b7915ad36
--- /dev/null
+++ b/crates/component-macro/tests/codegen/many-arguments.wit
@@ -0,0 +1,51 @@
+interface manyarg {
+  many-args: func(
+    a1: u64,
+    a2: u64,
+    a3: u64,
+    a4: u64,
+    a5: u64,
+    a6: u64,
+    a7: u64,
+    a8: u64,
+    a9: u64,
+    a10: u64,
+    a11: u64,
+    a12: u64,
+    a13: u64,
+    a14: u64,
+    a15: u64,
+    a16: u64,
+  )
+
+  record big-struct {
+    a1: string,
+    a2: string,
+    a3: string,
+    a4: string,
+    a5: string,
+    a6: string,
+    a7: string,
+    a8: string,
+    a9: string,
+    a10: string,
+    a11: string,
+    a12: string,
+    a13: string,
+    a14: string,
+    a15: string,
+    a16: string,
+    a17: string,
+    a18: string,
+    a19: string,
+    a20: string,
+  }
+
+  big-argument: func(x: big-struct)
+}
+
+world the-world {
+  import imports: manyarg
+  export exports: manyarg
+  default export manyarg
+}
diff --git a/crates/component-macro/tests/codegen/multi-return.wit b/crates/component-macro/tests/codegen/multi-return.wit
new file mode 100644
index 000000000..4239e1efe
--- /dev/null
+++ b/crates/component-macro/tests/codegen/multi-return.wit
@@ -0,0 +1,13 @@
+interface multi-return {
+  mra: func()
+  mrb: func() -> ()
+  mrc: func() -> u32
+  mrd: func() -> (a: u32)
+  mre: func() -> (a: u32, b: float32)
+}
+
+world the-world {
+  import imports: multi-return
+  export exports: multi-return
+  default export multi-return
+}
diff --git a/crates/component-macro/tests/codegen/records.wit b/crates/component-macro/tests/codegen/records.wit
new file mode 100644
index 000000000..6d8f1712e
--- /dev/null
+++ b/crates/component-macro/tests/codegen/records.wit
@@ -0,0 +1,60 @@
+interface records {
+  tuple-arg: func(x: tuple<char, u32>)
+  tuple-result: func() -> tuple<char, u32>
+
+  record empty {}
+
+  empty-arg: func(x: empty)
+  empty-result: func() -> empty
+
+  /// A record containing two scalar fields
+  /// that both have the same type
+  record scalars {
+      /// The first field, named a
+      a: u32,
+      /// The second field, named b
+      b: u32,
+  }
+
+  scalar-arg: func(x: scalars)
+  scalar-result: func() -> scalars
+
+  /// A record that is really just flags
+  /// All of the fields are bool
+  record really-flags {
+      a: bool,
+      b: bool,
+      c: bool,
+      d: bool,
+      e: bool,
+      f: bool,
+      g: bool,
+      h: bool,
+      i: bool,
+  }
+
+  flags-arg: func(x: really-flags)
+  flags-result: func() -> really-flags
+
+  record aggregates {
+      a: scalars,
+      b: u32,
+      c: empty,
+      d: string,
+      e: really-flags,
+  }
+
+  aggregate-arg: func(x: aggregates)
+  aggregate-result: func() -> aggregates
+
+  type tuple-typedef = tuple<s32>
+  type int-typedef = s32
+  type tuple-typedef2 = tuple<int-typedef>
+  typedef-inout: func(e: tuple-typedef2) -> s32
+}
+
+world the-world {
+  import imports: records
+  export exports: records
+  default export records
+}
diff --git a/crates/component-macro/tests/codegen/simple-functions.wit b/crates/component-macro/tests/codegen/simple-functions.wit
new file mode 100644
index 000000000..0f25e6c86
--- /dev/null
+++ b/crates/component-macro/tests/codegen/simple-functions.wit
@@ -0,0 +1,16 @@
+interface simple {
+  f1: func()
+  f2: func(a: u32)
+  f3: func(a: u32, b: u32)
+
+  f4: func() -> u32
+  f5: func() -> tuple<u32, u32>
+
+  f6: func(a: u32, b: u32, c: u32) -> tuple<u32, u32, u32>
+}
+
+world the-world {
+  import imports: simple
+  export exports: simple
+  default export simple
+}
diff --git a/crates/component-macro/tests/codegen/simple-lists.wit b/crates/component-macro/tests/codegen/simple-lists.wit
new file mode 100644
index 000000000..416c94107
--- /dev/null
+++ b/crates/component-macro/tests/codegen/simple-lists.wit
@@ -0,0 +1,13 @@
+interface simple-lists {
+  simple-list1: func(l: list<u32>)
+  simple-list2: func() -> list<u32>
+  // TODO: reenable this when smw implements this
+  // simple-list3: func(a: list<u32>, b: list<u32>) -> tuple<list<u32>, list<u32>>
+  simple-list4: func(l: list<list<u32>>) -> list<list<u32>>
+}
+
+world my-world {
+  import imports: simple-lists
+  export exports: simple-lists
+  default export simple-lists
+}
diff --git a/crates/component-macro/tests/codegen/small-anonymous.wit b/crates/component-macro/tests/codegen/small-anonymous.wit
new file mode 100644
index 000000000..8f7f69007
--- /dev/null
+++ b/crates/component-macro/tests/codegen/small-anonymous.wit
@@ -0,0 +1,14 @@
+interface anon {
+  enum error {
+    success,
+    failure,
+  }
+
+  option-test: func() -> result<option<string>, error>
+}
+
+world the-world {
+  import imports: anon
+  export exports: anon
+  default export anon
+}
diff --git a/crates/component-macro/tests/codegen/smoke-default.wit b/crates/component-macro/tests/codegen/smoke-default.wit
new file mode 100644
index 000000000..e1560ac55
--- /dev/null
+++ b/crates/component-macro/tests/codegen/smoke-default.wit
@@ -0,0 +1,5 @@
+world the-world {
+  default export interface {
+    y: func()
+  }
+}
diff --git a/crates/component-macro/tests/codegen/smoke-export.wit b/crates/component-macro/tests/codegen/smoke-export.wit
new file mode 100644
index 000000000..84500ce18
--- /dev/null
+++ b/crates/component-macro/tests/codegen/smoke-export.wit
@@ -0,0 +1,5 @@
+world the-world {
+  export the-name: interface {
+    y: func()
+  }
+}
diff --git a/crates/component-macro/tests/codegen/smoke.wit b/crates/component-macro/tests/codegen/smoke.wit
new file mode 100644
index 000000000..eddf74392
--- /dev/null
+++ b/crates/component-macro/tests/codegen/smoke.wit
@@ -0,0 +1,5 @@
+world the-world {
+  import imports: interface {
+    y: func()
+  }
+}
diff --git a/crates/component-macro/tests/codegen/strings.wit b/crates/component-macro/tests/codegen/strings.wit
new file mode 100644
index 000000000..a7511c920
--- /dev/null
+++ b/crates/component-macro/tests/codegen/strings.wit
@@ -0,0 +1,11 @@
+interface strings {
+  a: func(x: string)
+  b: func() -> string
+  c: func(a: string, b: string) -> string
+}
+
+world the-world {
+  import imports: strings
+  export exports: strings
+  default export strings
+}
diff --git a/crates/component-macro/tests/codegen/unions.wit b/crates/component-macro/tests/codegen/unions.wit
new file mode 100644
index 000000000..ea2d191c6
--- /dev/null
+++ b/crates/component-macro/tests/codegen/unions.wit
@@ -0,0 +1,65 @@
+interface unions {
+  /// A union of all of the integral types
+  union all-integers {
+      /// Bool is equivalent to a 1 bit integer
+      /// and is treated that way in some languages
+      bool,
+      u8, u16, u32, u64,
+      s8, s16, s32, s64
+  }
+  union all-floats {
+      float32, float64
+  }
+  union all-text {
+      char, string
+  }
+
+  // Returns the same case as the input but with 1 added
+  add-one-integer: func(num: all-integers) -> all-integers
+  // Returns the same case as the input but with 1 added
+  add-one-float: func(num: all-floats) -> all-floats
+  // Returns the same case as the input but with the first character replaced
+  replace-first-char: func(text: all-text, letter: char) -> all-text
+
+  // Returns the index of the case provided
+  identify-integer: func(num: all-integers) -> u8
+  // Returns the index of the case provided
+  identify-float: func(num: all-floats) -> u8
+  // Returns the index of the case provided
+  identify-text: func(text: all-text) -> u8
+
+  union duplicated-s32 {
+      /// The first s32
+      s32,
+      /// The second s32
+      s32,
+      /// The third s32
+      s32
+  }
+
+  // Returns the same case as the input but with 1 added
+  add-one-duplicated: func(num: duplicated-s32) -> duplicated-s32
+
+  // Returns the index of the case provided
+  identify-duplicated: func(num: duplicated-s32) -> u8
+
+  /// A type containing numeric types that are distinct in most languages
+  union distinguishable-num {
+      /// A Floating Point Number
+      float64,
+      /// A Signed Integer
+      s64
+  }
+
+  // Returns the same case as the input but with 1 added
+  add-one-distinguishable-num: func(num: distinguishable-num) -> distinguishable-num
+
+  // Returns the index of the case provided
+  identify-distinguishable-num: func(num: distinguishable-num) -> u8
+}
+
+world the-unions {
+  import import-unions: unions
+  export export-unions: unions
+  default export unions
+}
diff --git a/crates/component-macro/tests/codegen/variants.wit b/crates/component-macro/tests/codegen/variants.wit
new file mode 100644
index 000000000..288ee19f3
--- /dev/null
+++ b/crates/component-macro/tests/codegen/variants.wit
@@ -0,0 +1,146 @@
+interface variants {
+  enum e1 {
+      a,
+  }
+
+  e1-arg: func(x: e1)
+  e1-result: func() -> e1
+
+  union u1 {
+      u32,
+      float32,
+  }
+
+  u1-arg: func(x: u1)
+  u1-result: func() -> u1
+
+  record empty {}
+
+  variant v1 {
+      a,
+      b(u1),
+      c(e1),
+      d(string),
+      e(empty),
+      f,
+      g(u32),
+  }
+
+  v1-arg: func(x: v1)
+  v1-result: func() -> v1
+
+  bool-arg: func(x: bool)
+  bool-result: func() -> bool
+
+  option-arg: func(
+    a: option<bool>,
+    b: option<tuple<>>,
+    c: option<u32>,
+    d: option<e1>,
+    e: option<float32>,
+    f: option<u1>,
+    g: option<option<bool>>,
+  )
+  option-result: func() -> tuple<
+    option<bool>,
+    option<tuple<>>,
+    option<u32>,
+    option<e1>,
+    option<float32>,
+    option<u1>,
+    option<option<bool>>,
+  >
+
+  variant casts1 {
+    a(s32),
+    b(float32),
+  }
+
+  variant casts2 {
+    a(float64),
+    b(float32),
+  }
+
+  variant casts3 {
+    a(float64),
+    b(u64),
+  }
+
+  variant casts4 {
+    a(u32),
+    b(s64),
+  }
+
+  variant casts5 {
+    a(float32),
+    b(s64),
+  }
+
+  variant casts6 {
+    a(tuple<float32, u32>),
+    b(tuple<u32, u32>),
+  }
+
+  casts: func(
+    a: casts1,
+    b: casts2,
+    c: casts3,
+    d: casts4,
+    e: casts5,
+    f: casts6,
+  ) -> tuple<
+    casts1,
+    casts2,
+    casts3,
+    casts4,
+    casts5,
+    casts6,
+  >
+
+  result-arg: func(
+    a: result,
+    b: result<_, e1>,
+    c: result<e1>,
+    d: result<tuple<>, tuple<>>,
+    e: result<u32, v1>,
+    f: result<string, list<u8>>,
+  )
+  result-result: func() -> tuple<
+    result,
+    result<_, e1>,
+    result<e1>,
+    result<tuple<>, tuple<>>,
+    result<u32, v1>,
+    result<string, list<u8>>,
+  >
+
+  enum my-errno {
+    bad1,
+    bad2,
+  }
+
+  return-result-sugar: func() -> result<s32, my-errno>
+  return-result-sugar2: func() -> result<_, my-errno>
+  return-result-sugar3: func() -> result<my-errno, my-errno>
+  return-result-sugar4: func() -> result<tuple<s32, u32>, my-errno>
+  return-option-sugar: func() -> option<s32>
+  return-option-sugar2: func() -> option<my-errno>
+
+  result-simple: func() -> result<u32, s32>
+
+  record is-clone {
+    v1: v1,
+  }
+
+  is-clone-arg: func(a: is-clone)
+  is-clone-return: func() -> is-clone
+
+  return-named-option: func() -> (a: option<u8>)
+  return-named-result: func() -> (a: result<u8, my-errno>)
+}
+
+world my-world {
+  import imports: variants
+  export exports: variants
+  default export variants
+}
diff --git a/tests/all/component_model.rs b/tests/all/component_model.rs
index 43b42d4ff..fba2739a3 100644
--- a/tests/all/component_model.rs
+++ b/tests/all/component_model.rs
@@ -7,6 +7,7 @@ use wasmtime_component_util::REALLOC_AND_FREE;
 
 mod aot;
 mod r#async;
+mod bindgen;
 mod dynamic;
 mod func;
 mod import;
diff --git a/tests/all/component_model/bindgen.rs b/tests/all/component_model/bindgen.rs
new file mode 100644
index 000000000..40f5ec42c
--- /dev/null
+++ b/tests/all/component_model/bindgen.rs
@@ -0,0 +1,117 @@
+use super::engine;
+use anyhow::Result;
+use wasmtime::{
+    component::{Component, Linker},
+    Store,
+};
+
+mod no_imports {
+    use super::*;
+
+    wasmtime::component::bindgen!({
+        inline: "
+            world no-imports {
+                export foo: interface {
+                    foo: func()
+                }
+
+                default export interface {
+                    bar: func()
+                }
+            }
+        ",
+    });
+
+    #[test]
+    fn run() -> Result<()> {
+        let engine = engine();
+
+        let component = Component::new(
+            &engine,
+            r#"
+                (component
+                    (core module $m
+                        (func (export ""))
+                    )
+                    (core instance $i (instantiate $m))
+
+                    (func $f (export "bar") (canon lift (core func $i "")))
+
+                    (instance $i (export "foo" (func $f)))
+                    (export "foo" (instance $i))
+                )
+            "#,
+        )?;
+
+        let linker = Linker::new(&engine);
+        let mut store = Store::new(&engine, ());
+        let (no_imports, _) = NoImports::instantiate(&mut store, &component, &linker)?;
+        no_imports.bar(&mut store)?;
+        no_imports.foo().foo(&mut store)?;
+        Ok(())
+    }
+}
+
+mod one_import {
+    use super::*;
+
+    wasmtime::component::bindgen!({
+        inline: "
+            world one-import {
+                import foo: interface {
+                    foo: func()
+                }
+
+                default export interface {
+                    bar: func()
+                }
+            }
+        ",
+    });
+
+    #[test]
+    fn run() -> Result<()> {
+        let engine = engine();
+
+        let component = Component::new(
+            &engine,
+            r#"
+                (component
+                    (import "foo" (instance $i
+                        (export "foo" (func))
+                    ))
+                    (core module $m
+                        (import "" "" (func))
+                        (export "" (func 0))
+                    )
+                    (core func $f (canon lower (func $i "foo")))
+                    (core instance $i (instantiate $m
+                        (with "" (instance (export "" (func $f))))
+                    ))
+
+                    (func $f (export "bar") (canon lift (core func $i "")))
+                )
+            "#,
+        )?;
+
+        #[derive(Default)]
+        struct MyImports {
+            hit: bool,
+        }
+
+        impl foo::Foo for MyImports {
+            fn foo(&mut self) -> Result<()> {
+                self.hit = true;
+                Ok(())
+            }
+        }
+
+        let mut linker = Linker::new(&engine);
+        foo::add_to_linker(&mut linker, |f: &mut MyImports| f)?;
+        let mut store = Store::new(&engine, MyImports::default());
+        let (one_import, _) = OneImport::instantiate(&mut store, &component, &linker)?;
+        one_import.bar(&mut store)?;
+        assert!(store.data().hit);
+        Ok(())
+    }
+}