Repository: bytecodealliance/wasmtime
Pull request #8061 (2024-03-07 18:59:18)

Move dtor information from `ResourceAny` into a `Store`
This commit moves the internals of the `ResourceAny` type related to destructors into the auxiliary data within a `Store`. This avoids the need to carry around pointers with `ResourceAny` and additionally makes it easier to work with.

As part of this commit this also updates the behavior of `ResourceAny::try_from_resource` to no longer need an `InstancePre` and type information. This was required originally to get `ResourceAny::resource_drop` working to drop the host resource. In retrospect I'm not sure if this was the best goal to achieve because `Resource<T>` already has no destructor support and one of the more common use cases for the conversion is to simply turn around and give it back to a component where a component already has enough destructor information.

In the end this should make both `ResourceAny` and `Resource<T>` pretty close to "just an index" which is easier to reason about when working with resources than carrying additional pointers.

Closes https://github.com/bytecodealliance/wasmtime/issues/7714

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->


alexcrichton: cc @rvolosatovs as this is touching on the work of https://github.com/bytecodealliance/wasmtime/pull/7688
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 5b7d43e2b..ff529f58a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1735,6 +1735,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "mach2"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "matchers"
 version = "0.1.0"
@@ -3643,7 +3652,7 @@ dependencies = [
  "indexmap 2.0.0",
  "libc",
  "log",
- "mach",
+ "mach2",
  "memfd",
  "memoffset",
  "once_cell",
diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h
index cb092ade7..a9051d7c9 100644
--- a/crates/c-api/include/wasmtime.h
+++ b/crates/c-api/include/wasmtime.h
@@ -193,6 +193,7 @@
 #include <wasmtime/memory.h>
 #include <wasmtime/module.h>
 #include <wasmtime/profiling.h>
+#include <wasmtime/sharedmemory.h>
 #include <wasmtime/store.h>
 #include <wasmtime/table.h>
 #include <wasmtime/trap.h>
diff --git a/crates/c-api/include/wasmtime/extern.h b/crates/c-api/include/wasmtime/extern.h
index 427c5ba7f..7cb058b7d 100644
--- a/crates/c-api/include/wasmtime/extern.h
+++ b/crates/c-api/include/wasmtime/extern.h
@@ -85,6 +85,11 @@ typedef uint8_t wasmtime_extern_kind_t;
 /// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
 /// memory
 #define WASMTIME_EXTERN_MEMORY 3
+/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
+/// shared memory
+#define WASMTIME_EXTERN_SHAREDMEMORY 4
+
+struct wasmtime_sharedmemory;
 
 /**
  * \typedef wasmtime_extern_union_t
@@ -105,6 +110,8 @@ typedef union wasmtime_extern_union {
   wasmtime_table_t table;
   /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MEMORY
   wasmtime_memory_t memory;
+  /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_SHAREDMEMORY
+  struct wasmtime_sharedmemory *sharedmemory;
 } wasmtime_extern_union_t;
 
 /**
diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h
index c70423495..da07db30b 100644
--- a/crates/c-api/include/wasmtime/linker.h
+++ b/crates/c-api/include/wasmtime/linker.h
@@ -42,6 +42,15 @@ typedef struct wasmtime_linker wasmtime_linker_t;
  */
 WASM_API_EXTERN wasmtime_linker_t *wasmtime_linker_new(wasm_engine_t *engine);
 
+/**
+ * \brief Clones existing linker.
+ *
+ * This function does not take ownership of the linker argument, and the caller
+ * is expected to delete the returned linker.
+ */
+WASM_API_EXTERN wasmtime_linker_t *
+wasmtime_linker_clone(wasmtime_linker_t *linker);
+
 /**
  * \brief Deletes a linker
  */
diff --git a/crates/c-api/include/wasmtime/memory.h b/crates/c-api/include/wasmtime/memory.h
index a22b93c97..53e82fcc6 100644
--- a/crates/c-api/include/wasmtime/memory.h
+++ b/crates/c-api/include/wasmtime/memory.h
@@ -54,6 +54,11 @@ WASM_API_EXTERN bool wasmtime_memorytype_maximum(const wasm_memorytype_t *ty,
  */
 WASM_API_EXTERN bool wasmtime_memorytype_is64(const wasm_memorytype_t *ty);
 
+/**
+ * \brief Returns whether this type of memory represents a shared memory.
+ */
+WASM_API_EXTERN bool wasmtime_memorytype_isshared(const wasm_memorytype_t *ty);
+
 /**
  * \brief Creates a new WebAssembly linear memory
  *
diff --git a/crates/c-api/include/wasmtime/sharedmemory.h b/crates/c-api/include/wasmtime/sharedmemory.h
new file mode 100644
index 000000000..0c68d7403
--- /dev/null
+++ b/crates/c-api/include/wasmtime/sharedmemory.h
@@ -0,0 +1,115 @@
+/**
+ * \file wasmtime/sharedmemory.h
+ *
+ * Wasmtime API for interacting with wasm shared memories.
+ */
+
+#ifndef WASMTIME_SHAREDMEMORY_H
+#define WASMTIME_SHAREDMEMORY_H
+
+#include <wasm.h>
+#include <wasmtime/error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Interface for shared memories.
+ *
+ * For more information see the Rust documentation at:
+ * https://docs.wasmtime.dev/api/wasmtime/struct.SharedMemory.html
+ */
+typedef struct wasmtime_sharedmemory wasmtime_sharedmemory_t;
+
+/**
+ * \brief Creates a new WebAssembly shared linear memory
+ *
+ * \param engine engine that created shared memory is associated with
+ * \param ty the type of the memory to create
+ * \param ret where to store the returned memory
+ *
+ * If an error happens when creating the memory it's returned and owned by the
+ * caller. If an error happens then `ret` is not filled in.
+ */
+WASM_API_EXTERN wasmtime_error_t *
+wasmtime_sharedmemory_new(const wasm_engine_t *engine,
+                          const wasm_memorytype_t *ty,
+                          wasmtime_sharedmemory_t **ret);
+
+/**
+ * \brief Deletes shared linear memory
+ *
+ * \param memory memory to be deleted
+ */
+WASM_API_EXTERN void
+wasmtime_sharedmemory_delete(wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Clones shared linear memory
+ *
+ * \param memory memory to be cloned
+ *
+ * This function makes shallow clone, ie. copy of reference counted
+ * memory handle.
+ */
+WASM_API_EXTERN wasmtime_sharedmemory_t *
+wasmtime_sharedmemory_clone(const wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Moves shared linear memory into wasmtime_extern_t object
+ *
+ * \param memory memory to be moved
+ * \param out where to store resulting `wasmtime_extern_t`
+ *
+ * This function moves ownership of `memory` into resulting `wasmtime_extern_t`.
+ */
+WASM_API_EXTERN void
+wasmtime_sharedmemory_into_extern(wasmtime_sharedmemory_t *memory,
+                                  wasmtime_extern_t *out);
+
+/**
+ * \brief Returns the type of the shared memory specified
+ */
+WASM_API_EXTERN wasm_memorytype_t *
+wasmtime_sharedmemory_type(const wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Returns the base pointer in memory where
+          the shared linear memory starts.
+ */
+WASM_API_EXTERN uint8_t *
+wasmtime_sharedmemory_data(const wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Returns the byte length of this shared linear memory.
+ */
+WASM_API_EXTERN size_t
+wasmtime_sharedmemory_data_size(const wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Returns the length, in WebAssembly pages, of this shared linear memory
+ */
+WASM_API_EXTERN uint64_t
+wasmtime_sharedmemory_size(const wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Attempts to grow the specified shared memory by `delta` pages.
+ *
+ * \param memory the memory to grow
+ * \param delta the number of pages to grow by
+ * \param prev_size where to store the previous size of memory
+ *
+ * If memory cannot be grown then `prev_size` is left unchanged and an error is
+ * returned. Otherwise `prev_size` is set to the previous size of the memory, in
+ * WebAssembly pages, and `NULL` is returned.
+ */
+WASM_API_EXTERN wasmtime_error_t *
+wasmtime_sharedmemory_grow(const wasmtime_sharedmemory_t *memory,
+                           uint64_t delta, uint64_t *prev_size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WASMTIME_SHAREDMEMORY_H
diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs
index b97ba1f19..3fbf1d0cd 100644
--- a/crates/c-api/src/extern.rs
+++ b/crates/c-api/src/extern.rs
@@ -3,7 +3,7 @@ use crate::{
     CStoreContext, StoreRef,
 };
 use std::mem::ManuallyDrop;
-use wasmtime::{Extern, Func, Global, Memory, Table};
+use wasmtime::{Extern, Func, Global, Memory, SharedMemory, Table};
 
 #[derive(Clone)]
 pub struct wasm_extern_t {
@@ -82,6 +82,7 @@ pub const WASMTIME_EXTERN_FUNC: wasmtime_extern_kind_t = 0;
 pub const WASMTIME_EXTERN_GLOBAL: wasmtime_extern_kind_t = 1;
 pub const WASMTIME_EXTERN_TABLE: wasmtime_extern_kind_t = 2;
 pub const WASMTIME_EXTERN_MEMORY: wasmtime_extern_kind_t = 3;
+pub const WASMTIME_EXTERN_SHAREDMEMORY: wasmtime_extern_kind_t = 4;
 
 #[repr(C)]
 pub union wasmtime_extern_union {
@@ -89,6 +90,17 @@ pub union wasmtime_extern_union {
     pub table: Table,
     pub global: Global,
     pub memory: Memory,
+    pub sharedmemory: ManuallyDrop<Box<SharedMemory>>,
+}
+
+impl Drop for wasmtime_extern_t {
+    fn drop(&mut self) {
+        if self.kind == WASMTIME_EXTERN_SHAREDMEMORY {
+            unsafe {
+                ManuallyDrop::drop(&mut self.of.sharedmemory);
+            }
+        }
+    }
 }
 
 impl wasmtime_extern_t {
@@ -98,7 +110,8 @@ impl wasmtime_extern_t {
             WASMTIME_EXTERN_GLOBAL => Extern::Global(self.of.global),
             WASMTIME_EXTERN_TABLE => Extern::Table(self.of.table),
             WASMTIME_EXTERN_MEMORY => Extern::Memory(self.of.memory),
-            other => panic!("unknown wasm_extern_kind_t: {}", other),
+            WASMTIME_EXTERN_SHAREDMEMORY => Extern::SharedMemory((**self.of.sharedmemory).clone()),
+            other => panic!("unknown wasmtime_extern_kind_t: {}", other),
         }
     }
 }
@@ -122,7 +135,12 @@ impl From<Extern> for wasmtime_extern_t {
                 kind: WASMTIME_EXTERN_MEMORY,
                 of: wasmtime_extern_union { memory },
             },
-            Extern::SharedMemory(_memory) => todo!(),
+            Extern::SharedMemory(sharedmemory) => wasmtime_extern_t {
+                kind: WASMTIME_EXTERN_SHAREDMEMORY,
+                of: wasmtime_extern_union {
+                    sharedmemory: ManuallyDrop::new(Box::new(sharedmemory)),
+                },
+            },
         }
     }
 }
diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs
index a8f2bca41..fbf3ab287 100644
--- a/crates/c-api/src/lib.rs
+++ b/crates/c-api/src/lib.rs
@@ -29,6 +29,7 @@ mod module;
 #[cfg(feature = "profiling")]
 mod profiling;
 mod r#ref;
+mod sharedmemory;
 mod store;
 mod table;
 mod trap;
diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs
index 4ef4aaecf..0c2fd8a61 100644
--- a/crates/c-api/src/linker.rs
+++ b/crates/c-api/src/linker.rs
@@ -21,6 +21,13 @@ pub extern "C" fn wasmtime_linker_new(engine: &wasm_engine_t) -> Box<wasmtime_li
     })
 }
 
+#[no_mangle]
+pub extern "C" fn wasmtime_linker_clone(linker: &wasmtime_linker_t) -> Box<wasmtime_linker_t> {
+    Box::new(wasmtime_linker_t {
+        linker: linker.linker.clone(),
+    })
+}
+
 #[no_mangle]
 pub extern "C" fn wasmtime_linker_allow_shadowing(
     linker: &mut wasmtime_linker_t,
diff --git a/crates/c-api/src/sharedmemory.rs b/crates/c-api/src/sharedmemory.rs
new file mode 100644
index 000000000..1521373c4
--- /dev/null
+++ b/crates/c-api/src/sharedmemory.rs
@@ -0,0 +1,68 @@
+use crate::{handle_result, wasm_engine_t, wasm_memorytype_t, wasmtime_error_t, wasmtime_extern_t};
+use std::cell::UnsafeCell;
+use std::mem::MaybeUninit;
+use wasmtime::{Extern, SharedMemory};
+
+type wasmtime_sharedmemory_t = SharedMemory;
+
+wasmtime_c_api_macros::declare_own!(wasmtime_sharedmemory_t);
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_new(
+    engine: &wasm_engine_t,
+    ty: &wasm_memorytype_t,
+    ret: &mut *mut wasmtime_sharedmemory_t,
+) -> Option<Box<wasmtime_error_t>> {
+    handle_result(
+        SharedMemory::new(&engine.engine, ty.ty().ty.clone()),
+        |mem| *ret = Box::<wasmtime_sharedmemory_t>::into_raw(Box::new(mem)),
+    )
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_clone(
+    mem: &wasmtime_sharedmemory_t,
+) -> Box<wasmtime_sharedmemory_t> {
+    Box::new(mem.clone())
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_into_extern(
+    mem: Box<wasmtime_sharedmemory_t>,
+    ext: &mut MaybeUninit<wasmtime_extern_t>,
+) {
+    crate::initialize(ext, Extern::from(*mem).into());
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_type(
+    mem: &wasmtime_sharedmemory_t,
+) -> Box<wasm_memorytype_t> {
+    Box::new(wasm_memorytype_t::new(mem.ty()))
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_data(
+    mem: &wasmtime_sharedmemory_t,
+) -> *const UnsafeCell<u8> {
+    mem.data().as_ptr()
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_data_size(mem: &wasmtime_sharedmemory_t) -> usize {
+    mem.data().len()
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_size(mem: &wasmtime_sharedmemory_t) -> u64 {
+    mem.size()
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_grow(
+    mem: &wasmtime_sharedmemory_t,
+    delta: u64,
+    prev_size: &mut u64,
+) -> Option<Box<wasmtime_error_t>> {
+    handle_result(mem.grow(delta), |prev| *prev_size = prev)
+}
diff --git a/crates/c-api/src/types/memory.rs b/crates/c-api/src/types/memory.rs
index 034bea862..bcdb933d7 100644
--- a/crates/c-api/src/types/memory.rs
+++ b/crates/c-api/src/types/memory.rs
@@ -108,6 +108,11 @@ pub extern "C" fn wasmtime_memorytype_is64(mt: &wasm_memorytype_t) -> bool {
     mt.ty().ty.is_64()
 }
 
+#[no_mangle]
+pub extern "C" fn wasmtime_memorytype_isshared(mt: &wasm_memorytype_t) -> bool {
+    mt.ty().ty.is_shared()
+}
+
 #[no_mangle]
 pub extern "C" fn wasm_memorytype_as_externtype(ty: &wasm_memorytype_t) -> &wasm_externtype_t {
     &ty.ext
diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs
index 7061eacc0..091bf5816 100644
--- a/crates/c-api/src/wasi.rs
+++ b/crates/c-api/src/wasi.rs
@@ -292,6 +292,14 @@ pub unsafe extern "C" fn wasi_config_preopen_socket(
     fd_num: u32,
     host_port: *const c_char,
 ) -> bool {
+    const VAR: &str = "WASMTIME_WASI_CONFIG_PREOPEN_SOCKET_ALLOW";
+    if std::env::var(VAR).is_err() {
+        panic!(
+            "wasmtime c-api: wasi_config_preopen_socket will be deprecated in the \
+            wasmtime 20.0.0 release. set {VAR} to enable temporarily"
+        );
+    }
+
     let address = match cstr_to_str(host_port) {
         Some(s) => s,
         None => return false,
diff --git a/crates/component-macro/src/component.rs b/crates/component-macro/src/component.rs
index 0f49ef07a..00d5ba83e 100644
--- a/crates/component-macro/src/component.rs
+++ b/crates/component-macro/src/component.rs
@@ -447,7 +447,7 @@ impl Expander for LiftExpander {
             if let Some(ty) = ty {
                 let payload_ty = match style {
                     VariantStyle::Variant => {
-                        quote!(ty.cases[#index].ty.unwrap_or_else(#internal::bad_type_info))
+                        quote!(ty.cases[#index].unwrap_or_else(#internal::bad_type_info))
                     }
                     VariantStyle::Enum => unreachable!(),
                 };
@@ -626,7 +626,7 @@ impl Expander for LowerExpander {
             if ty.is_some() {
                 let ty = match style {
                     VariantStyle::Variant => {
-                        quote!(ty.cases[#index].ty.unwrap_or_else(#internal::bad_type_info))
+                        quote!(ty.cases[#index].unwrap_or_else(#internal::bad_type_info))
                     }
                     VariantStyle::Enum => unreachable!(),
                 };
diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs
index f873d86e3..489b5707e 100644
--- a/crates/environ/src/component/types.rs
+++ b/crates/environ/src/component/types.rs
@@ -8,7 +8,7 @@ use cranelift_entity::EntityRef;
 use indexmap::{IndexMap, IndexSet};
 use serde_derive::{Deserialize, Serialize};
 use std::collections::HashMap;
-use std::hash::Hash;
+use std::hash::{Hash, Hasher};
 use std::ops::Index;
 use wasmparser::names::KebabString;
 use wasmparser::types;
@@ -757,19 +757,20 @@ impl ComponentTypesBuilder {
                 if case.refines.is_some() {
                     bail!("refines is not supported at this time");
                 }
-                Ok(VariantCase {
-                    name: name.to_string(),
-                    ty: match &case.ty.as_ref() {
+                Ok((
+                    name.to_string(),
+                    match &case.ty.as_ref() {
                         Some(ty) => Some(self.valtype(types, ty)?),
                         None => None,
                     },
-                })
+                ))
             })
-            .collect::<Result<Box<[_]>>>()?;
-        let (info, abi) = VariantInfo::new(cases.iter().map(|c| {
-            c.ty.as_ref()
-                .map(|ty| self.component_types.canonical_abi(ty))
-        }));
+            .collect::<Result<IndexMap<_, _>>>()?;
+        let (info, abi) = VariantInfo::new(
+            cases
+                .iter()
+                .map(|(_, c)| c.as_ref().map(|ty| self.component_types.canonical_abi(ty))),
+        );
         Ok(self.add_variant_type(TypeVariant { cases, abi, info }))
     }
 
@@ -804,7 +805,10 @@ impl ComponentTypesBuilder {
     }
 
     fn enum_type(&mut self, variants: &IndexSet<KebabString>) -> TypeEnumIndex {
-        let names = variants.iter().map(|s| s.to_string()).collect::<Box<[_]>>();
+        let names = variants
+            .iter()
+            .map(|s| s.to_string())
+            .collect::<IndexSet<_>>();
         let (info, abi) = VariantInfo::new(names.iter().map(|_| None));
         self.add_enum_type(TypeEnum { names, abi, info })
     }
@@ -1485,24 +1489,23 @@ pub struct RecordField {
 /// Variants are close to Rust `enum` declarations where a value is one of many
 /// cases and each case has a unique name and an optional payload associated
 /// with it.
-#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
+#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
 pub struct TypeVariant {
     /// The list of cases that this variant can take.
-    pub cases: Box<[VariantCase]>,
+    pub cases: IndexMap<String, Option<InterfaceType>>,
     /// Byte information about this type in the canonical ABI.
     pub abi: CanonicalAbiInfo,
     /// Byte information about this variant type.
     pub info: VariantInfo,
 }
 
-/// One case of a `variant` type which contains the name of the variant as well
-/// as the payload.
-#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
-pub struct VariantCase {
-    /// Name of the variant, unique amongst all cases in a variant.
-    pub name: String,
-    /// Optional type associated with this payload.
-    pub ty: Option<InterfaceType>,
+impl Hash for TypeVariant {
+    fn hash<H: Hasher>(&self, h: &mut H) {
+        let TypeVariant { cases, abi, info } = self;
+        cases.as_slice().hash(h);
+        abi.hash(h);
+        info.hash(h);
+    }
 }
 
 /// Shape of a "tuple" type in interface types.
@@ -1521,29 +1524,46 @@ pub struct TypeTuple {
 ///
 /// This can be thought of as a record-of-bools, although the representation is
 /// more efficient as bitflags.
-#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
+#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
 pub struct TypeFlags {
     /// The names of all flags, all of which are unique.
-    pub names: Box<[String]>,
+    pub names: IndexSet<String>,
     /// Byte information about this type in the canonical ABI.
     pub abi: CanonicalAbiInfo,
 }
 
+impl Hash for TypeFlags {
+    fn hash<H: Hasher>(&self, h: &mut H) {
+        let TypeFlags { names, abi } = self;
+        names.as_slice().hash(h);
+        abi.hash(h);
+    }
+}
+
 /// Shape of an "enum" type in interface types, not to be confused with a Rust
 /// `enum` type.
 ///
 /// In interface types enums are simply a bag of names, and can be seen as a
 /// variant where all payloads are `Unit`.
-#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
+#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
 pub struct TypeEnum {
     /// The names of this enum, all of which are unique.
-    pub names: Box<[String]>,
+    pub names: IndexSet<String>,
     /// Byte information about this type in the canonical ABI.
     pub abi: CanonicalAbiInfo,
     /// Byte information about this variant type.
     pub info: VariantInfo,
 }
 
+impl Hash for TypeEnum {
+    fn hash<H: Hasher>(&self, h: &mut H) {
+        let TypeEnum { names, abi, info } = self;
+        names.as_slice().hash(h);
+        abi.hash(h);
+        info.hash(h);
+    }
+}
+
 /// Shape of an "option" interface type.
 #[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
 pub struct TypeOption {
@@ -1909,7 +1929,7 @@ impl TypeInformation {
         self.build_variant(
             ty.cases
                 .iter()
-                .map(|c| c.ty.as_ref().map(|ty| types.type_information(ty))),
+                .map(|(_, c)| c.as_ref().map(|ty| types.type_information(ty))),
         )
     }
 
diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs
index 5936de013..f85a60c6d 100644
--- a/crates/environ/src/fact/trampoline.rs
+++ b/crates/environ/src/fact/trampoline.rs
@@ -2179,25 +2179,29 @@ impl Compiler<'_, '_> {
             _ => panic!("expected a variant"),
         };
 
-        let src_info = variant_info(self.types, src_ty.cases.iter().map(|c| c.ty.as_ref()));
-        let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|c| c.ty.as_ref()));
+        let src_info = variant_info(self.types, src_ty.cases.iter().map(|(_, c)| c.as_ref()));
+        let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|(_, c)| c.as_ref()));
 
-        let iter = src_ty.cases.iter().enumerate().map(|(src_i, src_case)| {
-            let dst_i = dst_ty
-                .cases
-                .iter()
-                .position(|c| c.name == src_case.name)
-                .unwrap();
-            let dst_case = &dst_ty.cases[dst_i];
-            let src_i = u32::try_from(src_i).unwrap();
-            let dst_i = u32::try_from(dst_i).unwrap();
-            VariantCase {
-                src_i,
-                src_ty: src_case.ty.as_ref(),
-                dst_i,
-                dst_ty: dst_case.ty.as_ref(),
-            }
-        });
+        let iter = src_ty
+            .cases
+            .iter()
+            .enumerate()
+            .map(|(src_i, (src_case, src_case_ty))| {
+                let dst_i = dst_ty
+                    .cases
+                    .iter()
+                    .position(|(c, _)| c == src_case)
+                    .unwrap();
+                let dst_case_ty = &dst_ty.cases[dst_i];
+                let src_i = u32::try_from(src_i).unwrap();
+                let dst_i = u32::try_from(dst_i).unwrap();
+                VariantCase {
+                    src_i,
+                    src_ty: src_case_ty.as_ref(),
+                    dst_i,
+                    dst_ty: dst_case_ty.as_ref(),
+                }
+            });
         self.convert_variant(src, &src_info, dst, &dst_info, iter);
     }
 
diff --git a/crates/fuzzing/src/generators/component_types.rs b/crates/fuzzing/src/generators/component_types.rs
index 80b2838b1..37e34e83e 100644
--- a/crates/fuzzing/src/generators/component_types.rs
+++ b/crates/fuzzing/src/generators/component_types.rs
@@ -46,77 +46,67 @@ pub fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrar
                 Ok(ControlFlow::Continue(()))
             })?;
 
-            list.new_val(values.into()).unwrap()
+            Val::List(values.into())
         }
-        Type::Record(record) => record
-            .new_val(
-                record
-                    .fields()
-                    .map(|field| Ok((field.name, arbitrary_val(&field.ty, input)?)))
-                    .collect::<arbitrary::Result<Vec<_>>>()?,
-            )
-            .unwrap(),
-        Type::Tuple(tuple) => tuple
-            .new_val(
-                tuple
-                    .types()
-                    .map(|ty| arbitrary_val(&ty, input))
-                    .collect::<arbitrary::Result<_>>()?,
-            )
-            .unwrap(),
+        Type::Record(record) => Val::Record(
+            record
+                .fields()
+                .map(|field| Ok((field.name.to_string(), arbitrary_val(&field.ty, input)?)))
+                .collect::<arbitrary::Result<_>>()?,
+        ),
+        Type::Tuple(tuple) => Val::Tuple(
+            tuple
+                .types()
+                .map(|ty| arbitrary_val(&ty, input))
+                .collect::<arbitrary::Result<_>>()?,
+        ),
         Type::Variant(variant) => {
             let cases = variant.cases().collect::<Vec<_>>();
             let case = input.choose(&cases)?;
             let payload = match &case.ty {
-                Some(ty) => Some(arbitrary_val(ty, input)?),
+                Some(ty) => Some(Box::new(arbitrary_val(ty, input)?)),
                 None => None,
             };
-            variant.new_val(case.name, payload).unwrap()
+            Val::Variant(case.name.to_string(), payload)
         }
         Type::Enum(en) => {
             let names = en.names().collect::<Vec<_>>();
             let name = input.choose(&names)?;
-            en.new_val(name).unwrap()
+            Val::Enum(name.to_string())
         }
         Type::Option(option) => {
             let discriminant = input.int_in_range(0..=1)?;
-            option
-                .new_val(match discriminant {
-                    0 => None,
-                    1 => Some(arbitrary_val(&option.ty(), input)?),
-                    _ => unreachable!(),
-                })
-                .unwrap()
+            Val::Option(match discriminant {
+                0 => None,
+                1 => Some(Box::new(arbitrary_val(&option.ty(), input)?)),
+                _ => unreachable!(),
+            })
         }
         Type::Result(result) => {
             let discriminant = input.int_in_range(0..=1)?;
-            result
-                .new_val(match discriminant {
-                    0 => Ok(match result.ok() {
-                        Some(ty) => Some(arbitrary_val(&ty, input)?),
-                        None => None,
-                    }),
-                    1 => Err(match result.err() {
-                        Some(ty) => Some(arbitrary_val(&ty, input)?),
-                        None => None,
-                    }),
-                    _ => unreachable!(),
-                })
-                .unwrap()
+            Val::Result(match discriminant {
+                0 => Ok(match result.ok() {
+                    Some(ty) => Some(Box::new(arbitrary_val(&ty, input)?)),
+                    None => None,
+                }),
+                1 => Err(match result.err() {
+                    Some(ty) => Some(Box::new(arbitrary_val(&ty, input)?)),
+                    None => None,
+                }),
+                _ => unreachable!(),
+            })
         }
-        Type::Flags(flags) => flags
-            .new_val(
-                &flags
-                    .names()
-                    .filter_map(|name| {
-                        input
-                            .arbitrary()
-                            .map(|p| if p { Some(name) } else { None })
-                            .transpose()
-                    })
-                    .collect::<arbitrary::Result<Box<[_]>>>()?,
-            )
-            .unwrap(),
+        Type::Flags(flags) => Val::Flags(
+            flags
+                .names()
+                .filter_map(|name| {
+                    input
+                        .arbitrary()
+                        .map(|p| if p { Some(name.to_string()) } else { None })
+                        .transpose()
+                })
+                .collect::<arbitrary::Result<_>>()?,
+        ),
 
         // Resources aren't fuzzed at this time.
         Type::Own(_) | Type::Borrow(_) => unreachable!(),
diff --git a/crates/runtime/Cargo.toml b/crates/runtime/Cargo.toml
index 364668577..17196cfa0 100644
--- a/crates/runtime/Cargo.toml
+++ b/crates/runtime/Cargo.toml
@@ -35,7 +35,7 @@ wasm-encoder = { workspace = true }
 memfd = "0.6.2"
 
 [target.'cfg(target_os = "macos")'.dependencies]
-mach = "0.3.2"
+mach2 = "0.4.1"
 
 [target.'cfg(unix)'.dependencies]
 rustix = { workspace = true, features = ["mm"] }
diff --git a/crates/runtime/src/sys/unix/machports.rs b/crates/runtime/src/sys/unix/machports.rs
index 7ab1276fc..3b6f8dfa5 100644
--- a/crates/runtime/src/sys/unix/machports.rs
+++ b/crates/runtime/src/sys/unix/machports.rs
@@ -35,134 +35,21 @@
 
 use crate::sys::traphandlers::wasmtime_longjmp;
 use crate::traphandlers::tls;
-use mach::exception_types::*;
-use mach::kern_return::*;
-use mach::mach_init::*;
-use mach::mach_port::*;
-use mach::message::*;
-use mach::port::*;
-use mach::thread_act::*;
-use mach::traps::*;
+use mach2::exc::*;
+use mach2::exception_types::*;
+use mach2::kern_return::*;
+use mach2::mach_init::*;
+use mach2::mach_port::*;
+use mach2::message::*;
+use mach2::ndr::*;
+use mach2::port::*;
+use mach2::thread_act::*;
+use mach2::thread_status::*;
+use mach2::traps::*;
 use std::mem;
 use std::ptr::addr_of_mut;
 use std::thread;
 
-/// Other `mach` declarations awaiting <https://github.com/fitzgen/mach/pull/64>
-/// to be merged.
-mod mach_addons {
-    #![allow(non_camel_case_types)]
-    #![allow(non_upper_case_globals)]
-    #![allow(dead_code)]
-
-    use mach::{
-        exception_types::*, kern_return::*, mach_types::*, message::*, port::*, thread_status::*,
-    };
-    use std::mem;
-
-    #[repr(C)]
-    #[derive(Copy, Clone, Debug)]
-    #[allow(dead_code)]
-    pub struct NDR_record_t {
-        mig_vers: libc::c_uchar,
-        if_vers: libc::c_uchar,
-        reserved1: libc::c_uchar,
-        mig_encoding: libc::c_uchar,
-        int_rep: libc::c_uchar,
-        char_rep: libc::c_uchar,
-        float_rep: libc::c_uchar,
-        reserved32: libc::c_uchar,
-    }
-
-    extern "C" {
-        pub static NDR_record: NDR_record_t;
-    }
-
-    // Note that this is copied from Gecko at
-    //
-    // https://searchfox.org/mozilla-central/rev/ed93119be4818da1509bbcb7b28e245853eeedd5/js/src/wasm/WasmSignalHandlers.cpp#583-601
-    //
-    // which distinctly diverges from the actual version of this in the header
-    // files provided by macOS, notably in the `code` field which uses `i64`
-    // instead of `i32`.
-    //
-    // Also note the `packed(4)` here which forcibly decreases alignment to 4 to
-    // additionally match what mach expects (apparently, I wish I had a better
-    // reference for this).
-    #[repr(C, packed(4))]
-    #[allow(dead_code)]
-    #[derive(Copy, Clone, Debug)]
-    pub struct __Request__exception_raise_t {
-        pub Head: mach_msg_header_t,
-        /* start of the kernel processed data */
-        pub msgh_body: mach_msg_body_t,
-        pub thread: mach_msg_port_descriptor_t,
-        pub task: mach_msg_port_descriptor_t,
-        /* end of the kernel processed data */
-        pub NDR: NDR_record_t,
-        pub exception: exception_type_t,
-        pub codeCnt: mach_msg_type_number_t,
-
-        // Note that this is a divergence from the C headers which use
-        // `integer_t` here for this field which is a `c_int`. That isn't
-        // actually reflecting reality apparently though because if `c_int` is
-        // used here then the structure is too small to receive a message.
-        pub code: [i64; 2],
-    }
-
-    #[repr(C)]
-    #[allow(dead_code)]
-    #[derive(Copy, Clone, Debug)]
-    pub struct __Reply__exception_raise_t {
-        pub Head: mach_msg_header_t,
-        pub NDR: NDR_record_t,
-        pub RetCode: kern_return_t,
-    }
-
-    #[repr(C)]
-    #[derive(Copy, Clone, Debug, Default, Hash, PartialOrd, PartialEq, Eq, Ord)]
-    pub struct arm_thread_state64_t {
-        pub __x: [u64; 29],
-        pub __fp: u64, // frame pointer x29
-        pub __lr: u64, // link register x30
-        pub __sp: u64, // stack pointer x31
-        pub __pc: u64,
-        pub __cpsr: u32,
-        pub __pad: u32,
-    }
-
-    impl arm_thread_state64_t {
-        pub fn count() -> mach_msg_type_number_t {
-            (mem::size_of::<Self>() / mem::size_of::<u32>()) as mach_msg_type_number_t
-        }
-    }
-
-    pub static ARM_THREAD_STATE64: thread_state_flavor_t = 6;
-
-    #[cfg(target_arch = "x86_64")]
-    pub static THREAD_STATE_NONE: thread_state_flavor_t = 13;
-    #[cfg(target_arch = "aarch64")]
-    pub static THREAD_STATE_NONE: thread_state_flavor_t = 5;
-
-    extern "C" {
-        pub fn thread_set_state(
-            target_act: thread_port_t,
-            flavor: thread_state_flavor_t,
-            new_state: thread_state_t,
-            new_stateCnt: mach_msg_type_number_t,
-        ) -> kern_return_t;
-
-        pub fn thread_set_exception_ports(
-            thread: thread_port_t,
-            exception_mask: exception_mask_t,
-            new_port: mach_port_t,
-            behavior: libc::c_uint,
-            new_flavor: thread_state_flavor_t,
-        ) -> kern_return_t;
-    }
-}
-
-use mach_addons::*;
-
 /// Process-global port that we use to route thread-level exceptions to.
 static mut WASMTIME_PORT: mach_port_name_t = MACH_PORT_NULL;
 
@@ -192,6 +79,38 @@ pub unsafe fn platform_init() {
     thread::spawn(|| handler_thread());
 }
 
+// Note that this is copied from Gecko at
+//
+// https://searchfox.org/mozilla-central/rev/ed93119be4818da1509bbcb7b28e245853eeedd5/js/src/wasm/WasmSignalHandlers.cpp#583-601
+//
+// which distinctly diverges from the actual version of this in the header
+// files provided by macOS, notably in the `code` field which uses `i64`
+// instead of `i32`.
+//
+// Also note the `packed(4)` here which forcibly decreases alignment to 4 to
+// additionally match what mach expects (apparently, I wish I had a better
+// reference for this).
+#[repr(C, packed(4))]
+#[allow(dead_code)]
+#[derive(Copy, Clone, Debug)]
+struct __Request__exception_raise_t {
+    Head: mach_msg_header_t,
+    /* start of the kernel processed data */
+    msgh_body: mach_msg_body_t,
+    thread: mach_msg_port_descriptor_t,
+    task: mach_msg_port_descriptor_t,
+    /* end of the kernel processed data */
+    NDR: NDR_record_t,
+    exception: exception_type_t,
+    codeCnt: mach_msg_type_number_t,
+
+    // Note that this is a divergence from the C headers which use
+    // `integer_t` here for this field which is a `c_int`. That isn't
+    // actually reflecting reality apparently though because if `c_int` is
+    // used here then the structure is too small to receive a message.
+    code: [i64; 2],
+}
+
 // This is largely just copied from SpiderMonkey.
 #[repr(C)]
 #[allow(dead_code)]
@@ -301,8 +220,8 @@ unsafe fn handle_exception(request: &mut ExceptionRequest) -> bool {
     // * `thread_state_count` - the size to pass to `mach_msg`.
     cfg_if::cfg_if! {
         if #[cfg(target_arch = "x86_64")] {
-            use mach::structs::x86_thread_state64_t;
-            use mach::thread_status::x86_THREAD_STATE64;
+            use mach2::structs::x86_thread_state64_t;
+            use mach2::thread_status::x86_THREAD_STATE64;
 
             type ThreadState = x86_thread_state64_t;
 
@@ -345,7 +264,7 @@ unsafe fn handle_exception(request: &mut ExceptionRequest) -> bool {
             };
             let mut thread_state = ThreadState::new();
         } else if #[cfg(target_arch = "aarch64")] {
-            type ThreadState = arm_thread_state64_t;
+            type ThreadState = mach2::structs::arm_thread_state64_t;
 
             let thread_state_flavor = ARM_THREAD_STATE64;
 
@@ -479,8 +398,8 @@ pub fn lazy_per_thread_init() {
             this_thread,
             EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC,
             WASMTIME_PORT,
-            EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
-            mach_addons::THREAD_STATE_NONE,
+            (EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES) as exception_behavior_t,
+            THREAD_STATE_NONE,
         );
         mach_port_deallocate(mach_task_self(), this_thread);
         assert_eq!(kret, KERN_SUCCESS, "failed to set thread exception port");
diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml
index 5ee1cc235..f35908fb5 100644
--- a/crates/wasi/Cargo.toml
+++ b/crates/wasi/Cargo.toml
@@ -37,7 +37,7 @@ url = { workspace = true }
 once_cell = { workspace = true }
 
 [dev-dependencies]
-tokio = { workspace = true, features = ["time", "sync", "io-std", "io-util", "rt", "rt-multi-thread", "net", "macros"] }
+tokio = { workspace = true, features = ["time", "sync", "io-std", "io-util", "rt", "rt-multi-thread", "net", "macros", "fs"] }
 test-log = { workspace = true }
 tracing-subscriber = { workspace = true }
 test-programs-artifacts = { workspace = true }
diff --git a/crates/wasi/src/ctx.rs b/crates/wasi/src/ctx.rs
index 090135a0f..4e9f3578e 100644
--- a/crates/wasi/src/ctx.rs
+++ b/crates/wasi/src/ctx.rs
@@ -1,3 +1,5 @@
+#[cfg(feature = "preview1")]
+use crate::WasiP1Ctx;
 use crate::{
     clocks::{
         host::{monotonic_clock, wall_clock},
@@ -127,6 +129,10 @@ impl WasiCtxBuilder {
         self
     }
 
+    pub fn inherit_env(&mut self) -> &mut Self {
+        self.envs(&std::env::vars().collect::<Vec<(String, String)>>())
+    }
+
     pub fn args(&mut self, args: &[impl AsRef<str>]) -> &mut Self {
         self.args.extend(args.iter().map(|a| a.as_ref().to_owned()));
         self
@@ -137,6 +143,10 @@ impl WasiCtxBuilder {
         self
     }
 
+    pub fn inherit_args(&mut self) -> &mut Self {
+        self.args(&std::env::args().collect::<Vec<String>>())
+    }
+
     pub fn preopened_dir(
         &mut self,
         dir: cap_std::fs::Dir,
@@ -273,6 +283,12 @@ impl WasiCtxBuilder {
             allowed_network_uses,
         }
     }
+
+    #[cfg(feature = "preview1")]
+    pub fn build_p1(&mut self) -> WasiP1Ctx {
+        let wasi = self.build();
+        WasiP1Ctx::new(wasi)
+    }
 }
 
 pub trait WasiView: Send {
diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs
index 225245b47..6ef80d83e 100644
--- a/crates/wasi/src/lib.rs
+++ b/crates/wasi/src/lib.rs
@@ -15,6 +15,8 @@ mod filesystem;
 mod host;
 mod ip_name_lookup;
 mod network;
+#[cfg(feature = "preview1")]
+mod p1ctx;
 pub mod pipe;
 mod poll;
 #[cfg(feature = "preview1")]
@@ -34,10 +36,13 @@ pub use self::ctx::{WasiCtx, WasiCtxBuilder, WasiView};
 pub use self::error::{I32Exit, TrappableError};
 pub use self::filesystem::{DirPerms, FilePerms, FsError, FsResult};
 pub use self::network::{Network, SocketError, SocketResult};
+#[cfg(feature = "preview1")]
+pub use self::p1ctx::WasiP1Ctx;
 pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe};
 pub use self::random::{thread_rng, Deterministic};
 pub use self::stdio::{
-    stderr, stdin, stdout, IsATTY, Stderr, Stdin, StdinStream, Stdout, StdoutStream,
+    stderr, stdin, stdout, AsyncStdinStream, AsyncStdoutStream, IsATTY, Stderr, Stdin, StdinStream,
+    Stdout, StdoutStream,
 };
 pub use self::stream::{
     HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError, StreamResult,
diff --git a/crates/wasi/src/p1ctx.rs b/crates/wasi/src/p1ctx.rs
new file mode 100644
index 000000000..990609de3
--- /dev/null
+++ b/crates/wasi/src/p1ctx.rs
@@ -0,0 +1,37 @@
+use crate::preview1::{WasiPreview1Adapter, WasiPreview1View};
+use crate::{WasiCtx, WasiView};
+use wasmtime::component::ResourceTable;
+
+pub struct WasiP1Ctx {
+    pub table: ResourceTable,
+    pub wasi: WasiCtx,
+    pub adapter: WasiPreview1Adapter,
+}
+
+impl WasiP1Ctx {
+    pub fn new(wasi: WasiCtx) -> Self {
+        Self {
+            table: ResourceTable::new(),
+            wasi,
+            adapter: WasiPreview1Adapter::new(),
+        }
+    }
+}
+
+impl WasiView for WasiP1Ctx {
+    fn table(&mut self) -> &mut ResourceTable {
+        &mut self.table
+    }
+    fn ctx(&mut self) -> &mut WasiCtx {
+        &mut self.wasi
+    }
+}
+
+impl WasiPreview1View for WasiP1Ctx {
+    fn adapter(&self) -> &WasiPreview1Adapter {
+        &self.adapter
+    }
+    fn adapter_mut(&mut self) -> &mut WasiPreview1Adapter {
+        &mut self.adapter
+    }
+}
diff --git a/crates/wasi/src/pipe.rs b/crates/wasi/src/pipe.rs
index 819455c59..3ecc9cd3b 100644
--- a/crates/wasi/src/pipe.rs
+++ b/crates/wasi/src/pipe.rs
@@ -22,9 +22,9 @@ pub struct MemoryInputPipe {
 }
 
 impl MemoryInputPipe {
-    pub fn new(bytes: Bytes) -> Self {
+    pub fn new(bytes: impl Into<Bytes>) -> Self {
         Self {
-            buffer: Arc::new(Mutex::new(bytes)),
+            buffer: Arc::new(Mutex::new(bytes.into())),
         }
     }
 
diff --git a/crates/wasi/src/preview0.rs b/crates/wasi/src/preview0.rs
index b7c8c40a7..38c0de8dd 100644
--- a/crates/wasi/src/preview0.rs
+++ b/crates/wasi/src/preview0.rs
@@ -4,16 +4,18 @@ use crate::preview1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1;
 use crate::preview1::WasiPreview1View;
 use wiggle::{GuestError, GuestPtr};
 
-pub fn add_to_linker_async<T: WasiPreview1View>(
+pub fn add_to_linker_async<T: Send, W: WasiPreview1View>(
     linker: &mut wasmtime::Linker<T>,
+    f: impl Fn(&mut T) -> &mut W + Copy + Send + Sync + 'static,
 ) -> anyhow::Result<()> {
-    wasi_unstable::add_to_linker(linker, |t| t)
+    wasi_unstable::add_to_linker(linker, f)
 }
 
-pub fn add_to_linker_sync<T: WasiPreview1View>(
+pub fn add_to_linker_sync<T: Send, W: WasiPreview1View>(
     linker: &mut wasmtime::Linker<T>,
+    f: impl Fn(&mut T) -> &mut W + Copy + Send + Sync + 'static,
 ) -> anyhow::Result<()> {
-    sync::add_wasi_unstable_to_linker(linker, |t| t)
+    sync::add_wasi_unstable_to_linker(linker, f)
 }
 
 wiggle::from_witx!({
diff --git a/crates/wasi/src/preview1.rs b/crates/wasi/src/preview1.rs
index 16a5e58ee..0147a8103 100644
--- a/crates/wasi/src/preview1.rs
+++ b/crates/wasi/src/preview1.rs
@@ -490,18 +490,19 @@ trait WasiPreview1ViewExt:
 
 impl<T: WasiPreview1View + preopens::Host> WasiPreview1ViewExt for T {}
 
-pub fn add_to_linker_async<T: WasiPreview1View>(
+pub fn add_to_linker_async<T: Send, W: WasiPreview1View>(
     linker: &mut wasmtime::Linker<T>,
+    f: impl Fn(&mut T) -> &mut W + Copy + Send + Sync + 'static,
 ) -> anyhow::Result<()> {
-    wasi_snapshot_preview1::add_to_linker(linker, |t| t)
+    crate::preview1::wasi_snapshot_preview1::add_to_linker(linker, f)
 }
 
-pub fn add_to_linker_sync<T: WasiPreview1View>(
+pub fn add_to_linker_sync<T: Send, W: WasiPreview1View>(
     linker: &mut wasmtime::Linker<T>,
+    f: impl Fn(&mut T) -> &mut W + Copy + Send + Sync + 'static,
 ) -> anyhow::Result<()> {
-    sync::add_wasi_snapshot_preview1_to_linker(linker, |t| t)
+    crate::preview1::sync::add_wasi_snapshot_preview1_to_linker(linker, f)
 }
-
 // Generate the wasi_snapshot_preview1::WasiSnapshotPreview1 trait,
 // and the module types.
 // None of the generated modules, traits, or types should be used externally
@@ -520,7 +521,7 @@ wiggle::from_witx!({
     errors: { errno => trappable Error },
 });
 
-mod sync {
+pub(crate) mod sync {
     use anyhow::Result;
     use std::future::Future;
 
diff --git a/crates/wasi/src/stdio.rs b/crates/wasi/src/stdio.rs
index 5c6b191eb..04bab3bdc 100644
--- a/crates/wasi/src/stdio.rs
+++ b/crates/wasi/src/stdio.rs
@@ -6,7 +6,11 @@ use crate::bindings::io::streams;
 use crate::pipe;
 use crate::{HostInputStream, HostOutputStream, StreamError, StreamResult, Subscribe, WasiView};
 use bytes::Bytes;
+use std::future::Future;
 use std::io::IsTerminal;
+use std::pin::Pin;
+use std::sync::{Arc, Mutex};
+use std::task::{Context, Poll};
 use wasmtime::component::Resource;
 
 /// A trait used to represent the standard input to a guest program.
@@ -54,6 +58,52 @@ impl StdinStream for pipe::ClosedInputStream {
     }
 }
 
+/// An impl of [`StdinStream`] built on top of [`crate::pipe::AsyncReadStream`].
+pub struct AsyncStdinStream(Arc<Mutex<crate::pipe::AsyncReadStream>>);
+
+impl AsyncStdinStream {
+    pub fn new(s: crate::pipe::AsyncReadStream) -> Self {
+        Self(Arc::new(Mutex::new(s)))
+    }
+}
+
+impl StdinStream for AsyncStdinStream {
+    fn stream(&self) -> Box<dyn HostInputStream> {
+        Box::new(Self(self.0.clone()))
+    }
+    fn isatty(&self) -> bool {
+        false
+    }
+}
+
+impl HostInputStream for AsyncStdinStream {
+    fn read(&mut self, size: usize) -> Result<bytes::Bytes, crate::StreamError> {
+        self.0.lock().unwrap().read(size)
+    }
+    fn skip(&mut self, size: usize) -> Result<usize, crate::StreamError> {
+        self.0.lock().unwrap().skip(size)
+    }
+}
+
+impl Subscribe for AsyncStdinStream {
+    fn ready<'a, 'b>(&'a mut self) -> Pin<Box<dyn Future<Output = ()> + Send + 'b>>
+    where
+        Self: 'b,
+        'a: 'b,
+    {
+        struct F(AsyncStdinStream);
+        impl Future for F {
+            type Output = ();
+            fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+                let mut inner = self.0 .0.lock().unwrap();
+                let mut fut = inner.ready();
+                fut.as_mut().poll(cx)
+            }
+        }
+        Box::pin(F(Self(self.0.clone())))
+    }
+}
+
 mod worker_thread_stdin;
 pub use self::worker_thread_stdin::{stdin, Stdin};
 
@@ -181,6 +231,72 @@ impl Subscribe for OutputStream {
     async fn ready(&mut self) {}
 }
 
+/// A wrapper of [`crate::pipe::AsyncWriteStream`] that implements
+/// [`StdoutStream`]. Note that the [`HostOutputStream`] impl for this is not
+/// correct when used for interleaved async IO.
+pub struct AsyncStdoutStream(Arc<Mutex<crate::pipe::AsyncWriteStream>>);
+
+impl AsyncStdoutStream {
+    pub fn new(s: crate::pipe::AsyncWriteStream) -> Self {
+        Self(Arc::new(Mutex::new(s)))
+    }
+}
+
+impl StdoutStream for AsyncStdoutStream {
+    fn stream(&self) -> Box<dyn HostOutputStream> {
+        Box::new(Self(self.0.clone()))
+    }
+    fn isatty(&self) -> bool {
+        false
+    }
+}
+
+// This implementation is known to be bogus. All check-writes and writes are
+// directed at the same underlying stream. The check-write/write protocol does
+// require the size returned by a check-write to be accepted by write, even if
+// other side-effects happen between those calls, and this implementation
+// permits another view (created by StdoutStream::stream()) of the same
+// underlying stream to accept a write which will invalidate a prior
+// check-write of another view.
+// Ultimately, the Std{in,out}Stream::stream() methods exist because many
+// different places in a linked component (which may itself contain many
+// modules) may need to access stdio without any coordination to keep those
+// accesses all using pointing to the same resource. So, we allow many
+// resources to be created. We have the reasonable expectation that programs
+// won't attempt to interleave async IO from these disparate uses of stdio.
+// If that expectation doesn't turn out to be true, and you find yourself at
+// this comment to correct it: sorry about that.
+impl HostOutputStream for AsyncStdoutStream {
+    fn check_write(&mut self) -> Result<usize, StreamError> {
+        self.0.lock().unwrap().check_write()
+    }
+    fn write(&mut self, bytes: Bytes) -> Result<(), StreamError> {
+        self.0.lock().unwrap().write(bytes)
+    }
+    fn flush(&mut self) -> Result<(), StreamError> {
+        self.0.lock().unwrap().flush()
+    }
+}
+
+impl Subscribe for AsyncStdoutStream {
+    fn ready<'a, 'b>(&'a mut self) -> Pin<Box<dyn Future<Output = ()> + Send + 'b>>
+    where
+        Self: 'b,
+        'a: 'b,
+    {
+        struct F(AsyncStdoutStream);
+        impl Future for F {
+            type Output = ();
+            fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+                let mut inner = self.0 .0.lock().unwrap();
+                let mut fut = inner.ready();
+                fut.as_mut().poll(cx)
+            }
+        }
+        Box::pin(F(Self(self.0.clone())))
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum IsATTY {
     Yes,
@@ -255,3 +371,73 @@ impl<T: WasiView> terminal_stderr::Host for T {
         }
     }
 }
+
+#[cfg(test)]
+mod test {
+    #[test]
+    fn memory_stdin_stream() {
+        // A StdinStream has the property that there are multiple
+        // HostInputStreams created, using the stream() method which are each
+        // views on the same shared state underneath. Consuming input on one
+        // stream results in consuming that input on all streams.
+        //
+        // The simplest way to measure this is to check if the MemoryInputPipe
+        // impl of StdinStream follows this property.
+
+        let pipe = super::pipe::MemoryInputPipe::new(
+            "the quick brown fox jumped over the three lazy dogs",
+        );
+
+        use super::StdinStream;
+
+        let mut view1 = pipe.stream();
+        let mut view2 = pipe.stream();
+
+        let read1 = view1.read(10).expect("read first 10 bytes");
+        assert_eq!(read1, "the quick ".as_bytes(), "first 10 bytes");
+        let read2 = view2.read(10).expect("read second 10 bytes");
+        assert_eq!(read2, "brown fox ".as_bytes(), "second 10 bytes");
+        let read3 = view1.read(10).expect("read third 10 bytes");
+        assert_eq!(read3, "jumped ove".as_bytes(), "third 10 bytes");
+        let read4 = view2.read(10).expect("read fourth 10 bytes");
+        assert_eq!(read4, "r the thre".as_bytes(), "fourth 10 bytes");
+    }
+    #[tokio::test]
+    async fn async_stdin_stream() {
+        // A StdinStream has the property that there are multiple
+        // HostInputStreams created, using the stream() method which are each
+        // views on the same shared state underneath. Consuming input on one
+        // stream results in consuming that input on all streams.
+        //
+        // AsyncStdinStream is a slightly more complex impl of StdinStream
+        // than the MemoryInputPipe above. We can create an AsyncReadStream
+        // from a file on the disk, and an AsyncStdinStream from that common
+        // stream, then check that the same property holds as above.
+
+        let dir = tempfile::tempdir().unwrap();
+        let mut path = std::path::PathBuf::from(dir.path());
+        path.push("file");
+        std::fs::write(&path, "the quick brown fox jumped over the three lazy dogs").unwrap();
+
+        let file = tokio::fs::File::open(&path)
+            .await
+            .expect("open created file");
+        let stdin_stream = super::AsyncStdinStream::new(crate::pipe::AsyncReadStream::new(file));
+
+        use super::StdinStream;
+
+        let mut view1 = stdin_stream.stream();
+        let mut view2 = stdin_stream.stream();
+
+        view1.ready().await;
+
+        let read1 = view1.read(10).expect("read first 10 bytes");
+        assert_eq!(read1, "the quick ".as_bytes(), "first 10 bytes");
+        let read2 = view2.read(10).expect("read second 10 bytes");
+        assert_eq!(read2, "brown fox ".as_bytes(), "second 10 bytes");
+        let read3 = view1.read(10).expect("read third 10 bytes");
+        assert_eq!(read3, "jumped ove".as_bytes(), "third 10 bytes");
+        let read4 = view2.read(10).expect("read fourth 10 bytes");
+        assert_eq!(read4, "r the thre".as_bytes(), "fourth 10 bytes");
+    }
+}
diff --git a/crates/wasmtime/src/runtime/component/func.rs b/crates/wasmtime/src/runtime/component/func.rs
index 2458a132c..2791da879 100644
--- a/crates/wasmtime/src/runtime/component/func.rs
+++ b/crates/wasmtime/src/runtime/component/func.rs
@@ -395,11 +395,6 @@ impl Func {
             );
         }
 
-        for (param, ty) in params.iter().zip(param_tys.iter()) {
-            ty.is_supertype_of(param)
-                .context("type mismatch with parameters")?;
-        }
-
         self.call_raw(
             store,
             params,
diff --git a/crates/wasmtime/src/runtime/component/func/host.rs b/crates/wasmtime/src/runtime/component/func/host.rs
index c00f38c83..22946854f 100644
--- a/crates/wasmtime/src/runtime/component/func/host.rs
+++ b/crates/wasmtime/src/runtime/component/func/host.rs
@@ -1,7 +1,7 @@
 use crate::component::func::{LiftContext, LowerContext, Options};
 use crate::component::matching::InstanceType;
 use crate::component::storage::slice_to_storage_mut;
-use crate::component::{ComponentNamedList, ComponentType, Lift, Lower, Type, Val};
+use crate::component::{ComponentNamedList, ComponentType, Lift, Lower, Val};
 use crate::{AsContextMut, StoreContextMut, ValRaw};
 use anyhow::{anyhow, bail, Context, Result};
 use std::any::Any;
@@ -383,10 +383,6 @@ where
     flags.set_may_leave(false);
 
     let mut cx = LowerContext::new(store, &options, types, instance);
-    let instance = cx.instance_type();
-    for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
-        Type::from(ty, &instance).is_supertype_of(val)?;
-    }
     if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
         let mut dst = storage[..cnt].iter_mut();
         for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
diff --git a/crates/wasmtime/src/runtime/component/func/options.rs b/crates/wasmtime/src/runtime/component/func/options.rs
index f08a29223..0217c31ff 100644
--- a/crates/wasmtime/src/runtime/component/func/options.rs
+++ b/crates/wasmtime/src/runtime/component/func/options.rs
@@ -338,8 +338,14 @@ impl<'a, T> LowerContext<'a, T> {
     ///
     /// Note that this is a special case for `Resource<T>`. Most of the time a
     /// host value shouldn't be lowered with a lowering context.
-    pub fn host_resource_lower_own(&mut self, rep: u32) -> Result<HostResourceIndex> {
-        self.resource_tables().host_resource_lower_own(rep)
+    pub fn host_resource_lower_own(
+        &mut self,
+        rep: u32,
+        dtor: Option<NonNull<VMFuncRef>>,
+        flags: Option<InstanceFlags>,
+    ) -> Result<HostResourceIndex> {
+        self.resource_tables()
+            .host_resource_lower_own(rep, dtor, flags)
     }
 
     /// Returns the underlying resource type for the `ty` table specified.
@@ -491,8 +497,14 @@ impl<'a> LiftContext<'a> {
 
     /// Lowers a resource into the host-owned table, returning the index it was
     /// inserted at.
-    pub fn host_resource_lower_own(&mut self, rep: u32) -> Result<HostResourceIndex> {
-        self.resource_tables().host_resource_lower_own(rep)
+    pub fn host_resource_lower_own(
+        &mut self,
+        rep: u32,
+        dtor: Option<NonNull<VMFuncRef>>,
+        flags: Option<InstanceFlags>,
+    ) -> Result<HostResourceIndex> {
+        self.resource_tables()
+            .host_resource_lower_own(rep, dtor, flags)
     }
 
     /// Lowers a resource into the host-owned table, returning the index it was
diff --git a/crates/wasmtime/src/runtime/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs
index 60bbcaeb4..3240a884a 100644
--- a/crates/wasmtime/src/runtime/component/func/typed.rs
+++ b/crates/wasmtime/src/runtime/component/func/typed.rs
@@ -1787,12 +1787,12 @@ pub fn typecheck_variant(
                 );
             }
 
-            for (case, &(name, check)) in cases.iter().zip(expected) {
-                if case.name != name {
-                    bail!("expected variant case named {name}, found {}", case.name);
+            for ((case_name, case_ty), &(name, check)) in cases.iter().zip(expected) {
+                if *case_name != name {
+                    bail!("expected variant case named {name}, found {case_name}");
                 }
 
-                match (check, &case.ty) {
+                match (check, case_ty) {
                     (Some(check), Some(ty)) => check(ty, types)
                         .with_context(|| format!("type mismatch for case {name}"))?,
                     (None, None) => {}
diff --git a/crates/wasmtime/src/runtime/component/instance.rs b/crates/wasmtime/src/runtime/component/instance.rs
index d15740995..0e2633e0e 100644
--- a/crates/wasmtime/src/runtime/component/instance.rs
+++ b/crates/wasmtime/src/runtime/component/instance.rs
@@ -1,8 +1,6 @@
 use crate::component::func::HostFunc;
 use crate::component::matching::InstanceType;
-use crate::component::{
-    Component, ComponentNamedList, Func, Lift, Lower, ResourceImportIndex, ResourceType, TypedFunc,
-};
+use crate::component::{Component, ComponentNamedList, Func, Lift, Lower, ResourceType, TypedFunc};
 use crate::instance::OwnedImports;
 use crate::linker::DefinitionType;
 use crate::store::{StoreOpaque, Stored};
@@ -525,7 +523,6 @@ impl<'a> Instantiator<'a> {
 pub struct InstancePre<T> {
     component: Component,
     imports: Arc<PrimaryMap<RuntimeImportIndex, RuntimeImport>>,
-    resource_imports: Arc<PrimaryMap<ResourceImportIndex, Option<RuntimeImportIndex>>>,
     _marker: marker::PhantomData<fn() -> T>,
 }
 
@@ -535,7 +532,6 @@ impl<T> Clone for InstancePre<T> {
         Self {
             component: self.component.clone(),
             imports: self.imports.clone(),
-            resource_imports: self.resource_imports.clone(),
             _marker: self._marker,
         }
     }
@@ -551,28 +547,14 @@ impl<T> InstancePre<T> {
     pub(crate) unsafe fn new_unchecked(
         component: Component,
         imports: PrimaryMap<RuntimeImportIndex, RuntimeImport>,
-        resource_imports: PrimaryMap<ResourceImportIndex, Option<RuntimeImportIndex>>,
     ) -> InstancePre<T> {
         InstancePre {
             component,
             imports: Arc::new(imports),
-            resource_imports: Arc::new(resource_imports),
             _marker: marker::PhantomData,
         }
     }
 
-    pub(crate) fn resource_import_index(
-        &self,
-        path: ResourceImportIndex,
-    ) -> Option<RuntimeImportIndex> {
-        *self.resource_imports.get(path)?
-    }
-
-    pub(crate) fn resource_import(&self, path: ResourceImportIndex) -> Option<&RuntimeImport> {
-        let idx = self.resource_import_index(path)?;
-        self.imports.get(idx)
-    }
-
     /// Returns the underlying component that will be instantiated.
     pub fn component(&self) -> &Component {
         &self.component
diff --git a/crates/wasmtime/src/runtime/component/linker.rs b/crates/wasmtime/src/runtime/component/linker.rs
index e41cca1c2..4b232cb11 100644
--- a/crates/wasmtime/src/runtime/component/linker.rs
+++ b/crates/wasmtime/src/runtime/component/linker.rs
@@ -16,7 +16,7 @@ use std::ops::Deref;
 use std::pin::Pin;
 use std::sync::Arc;
 use wasmtime_environ::component::TypeDef;
-use wasmtime_environ::{EntityRef, PrimaryMap};
+use wasmtime_environ::PrimaryMap;
 
 /// A type used to instantiate [`Component`]s.
 ///
@@ -64,7 +64,6 @@ pub struct Linker<T> {
     strings: Strings,
     map: NameMap,
     path: Vec<usize>,
-    resource_imports: usize,
     allow_shadowing: bool,
     _marker: marker::PhantomData<fn() -> T>,
 }
@@ -76,7 +75,6 @@ impl<T> Clone for Linker<T> {
             strings: self.strings.clone(),
             map: self.map.clone(),
             path: self.path.clone(),
-            resource_imports: self.resource_imports.clone(),
             allow_shadowing: self.allow_shadowing,
             _marker: self._marker,
         }
@@ -100,50 +98,10 @@ pub struct LinkerInstance<'a, T> {
     path_len: usize,
     strings: &'a mut Strings,
     map: &'a mut NameMap,
-    resource_imports: &'a mut usize,
     allow_shadowing: bool,
     _marker: marker::PhantomData<fn() -> T>,
 }
 
-/// Index correlating a resource definition to the import path.
-/// This is assigned by [`Linker::resource`] and may be used to associate it to
-/// [`RuntimeImportIndex`](wasmtime_environ::component::RuntimeImportIndex)
-/// at a later stage
-///
-/// [`Linker::resource`]: crate::component::LinkerInstance::resource
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-pub struct ResourceImportIndex(usize);
-
-impl EntityRef for ResourceImportIndex {
-    fn new(idx: usize) -> Self {
-        Self(idx)
-    }
-
-    fn index(self) -> usize {
-        self.0
-    }
-}
-
-impl Deref for ResourceImportIndex {
-    type Target = usize;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-impl From<usize> for ResourceImportIndex {
-    fn from(idx: usize) -> Self {
-        Self(idx)
-    }
-}
-
-impl From<ResourceImportIndex> for usize {
-    fn from(idx: ResourceImportIndex) -> Self {
-        idx.0
-    }
-}
-
 #[derive(Clone, Default)]
 pub(crate) struct NameMap {
     /// A map of interned strings to the name that they define.
@@ -184,11 +142,7 @@ pub(crate) enum Definition {
     Instance(NameMap),
     Func(Arc<HostFunc>),
     Module(Module),
-    Resource(
-        ResourceImportIndex,
-        ResourceType,
-        Arc<crate::func::HostFunc>,
-    ),
+    Resource(ResourceType, Arc<crate::func::HostFunc>),
 }
 
 impl<T> Linker<T> {
@@ -201,7 +155,6 @@ impl<T> Linker<T> {
             map: NameMap::default(),
             allow_shadowing: false,
             path: Vec::new(),
-            resource_imports: 0,
             _marker: marker::PhantomData,
         }
     }
@@ -229,7 +182,6 @@ impl<T> Linker<T> {
             path_len: 0,
             strings: &mut self.strings,
             map: &mut self.map,
-            resource_imports: &mut self.resource_imports,
             allow_shadowing: self.allow_shadowing,
             _marker: self._marker,
         }
@@ -304,7 +256,6 @@ impl<T> Linker<T> {
         // component-compile-time.
         let env_component = component.env_component();
         let mut imports = PrimaryMap::with_capacity(env_component.imports.len());
-        let mut resource_imports = PrimaryMap::from(vec![None; self.resource_imports]);
         for (idx, (import, names)) in env_component.imports.iter() {
             let (root, _) = &env_component.import_types[*import];
 
@@ -321,14 +272,11 @@ impl<T> Linker<T> {
             let import = match cur {
                 Definition::Module(m) => RuntimeImport::Module(m.clone()),
                 Definition::Func(f) => RuntimeImport::Func(f.clone()),
-                Definition::Resource(res_idx, t, dtor) => {
-                    resource_imports[*res_idx] = Some(idx);
-                    RuntimeImport::Resource {
-                        ty: t.clone(),
-                        _dtor: dtor.clone(),
-                        dtor_funcref: component.resource_drop_func_ref(dtor),
-                    }
-                }
+                Definition::Resource(t, dtor) => RuntimeImport::Resource {
+                    ty: t.clone(),
+                    _dtor: dtor.clone(),
+                    dtor_funcref: component.resource_drop_func_ref(dtor),
+                },
 
                 // This is guaranteed by the compilation process that "leaf"
                 // runtime imports are never instances.
@@ -337,7 +285,7 @@ impl<T> Linker<T> {
             let i = imports.push(import);
             assert_eq!(i, idx);
         }
-        Ok(unsafe { InstancePre::new_unchecked(component.clone(), imports, resource_imports) })
+        Ok(unsafe { InstancePre::new_unchecked(component.clone(), imports) })
     }
 
     /// Instantiates the [`Component`] provided into the `store` specified.
@@ -402,7 +350,6 @@ impl<T> LinkerInstance<'_, T> {
             path_len: self.path_len,
             strings: self.strings,
             map: self.map,
-            resource_imports: self.resource_imports,
             allow_shadowing: self.allow_shadowing,
             _marker: self._marker,
         }
@@ -581,18 +528,13 @@ impl<T> LinkerInstance<'_, T> {
         name: &str,
         ty: ResourceType,
         dtor: impl Fn(StoreContextMut<'_, T>, u32) -> Result<()> + Send + Sync + 'static,
-    ) -> Result<ResourceImportIndex> {
+    ) -> Result<()> {
         let dtor = Arc::new(crate::func::HostFunc::wrap(
             &self.engine,
             move |mut cx: crate::Caller<'_, T>, param: u32| dtor(cx.as_context_mut(), param),
         ));
-        let idx = ResourceImportIndex::new(*self.resource_imports);
-        *self.resource_imports = self
-            .resource_imports
-            .checked_add(1)
-            .context("resource import count would overflow")?;
-        self.insert(name, Definition::Resource(idx, ty, dtor))?;
-        Ok(idx)
+        self.insert(name, Definition::Resource(ty, dtor))?;
+        Ok(())
     }
 
     /// Defines a nested instance within this instance.
diff --git a/crates/wasmtime/src/runtime/component/matching.rs b/crates/wasmtime/src/runtime/component/matching.rs
index ba93a5fbf..7c47d98c9 100644
--- a/crates/wasmtime/src/runtime/component/matching.rs
+++ b/crates/wasmtime/src/runtime/component/matching.rs
@@ -53,7 +53,7 @@ impl TypeChecker<'_> {
             TypeDef::Resource(i) => {
                 let i = self.types[i].ty;
                 let actual = match actual {
-                    Some(Definition::Resource(_idx, actual, _dtor)) => actual,
+                    Some(Definition::Resource(actual, _dtor)) => actual,
 
                     // If a resource is imported yet nothing was supplied then
                     // that's only successful if the resource has itself
diff --git a/crates/wasmtime/src/runtime/component/mod.rs b/crates/wasmtime/src/runtime/component/mod.rs
index f3e40cb8e..7247b93a1 100644
--- a/crates/wasmtime/src/runtime/component/mod.rs
+++ b/crates/wasmtime/src/runtime/component/mod.rs
@@ -57,11 +57,11 @@ pub use self::func::{
     ComponentNamedList, ComponentType, Func, Lift, Lower, TypedFunc, WasmList, WasmStr,
 };
 pub use self::instance::{ExportInstance, Exports, Instance, InstancePre};
-pub use self::linker::{Linker, LinkerInstance, ResourceImportIndex};
+pub use self::linker::{Linker, LinkerInstance};
 pub use self::resource_table::{ResourceTable, ResourceTableError};
 pub use self::resources::{Resource, ResourceAny};
 pub use self::types::{ResourceType, Type};
-pub use self::values::{Enum, Flags, List, OptionVal, Record, ResultVal, Tuple, Val, Variant};
+pub use self::values::Val;
 
 pub(crate) use self::resources::HostResourceData;
 
diff --git a/crates/wasmtime/src/runtime/component/resources.rs b/crates/wasmtime/src/runtime/component/resources.rs
index 6cc1e8b89..061412eda 100644
--- a/crates/wasmtime/src/runtime/component/resources.rs
+++ b/crates/wasmtime/src/runtime/component/resources.rs
@@ -1,15 +1,14 @@
 use crate::component::func::{bad_type_info, desc, LiftContext, LowerContext};
-use crate::component::instance::RuntimeImport;
-use crate::component::linker::ResourceImportIndex;
 use crate::component::matching::InstanceType;
-use crate::component::{ComponentType, InstancePre, Lift, Lower};
+use crate::component::{ComponentType, Lift, Lower};
 use crate::store::{StoreId, StoreOpaque};
 use crate::{AsContextMut, StoreContextMut, Trap};
-use anyhow::{bail, ensure, Context, Result};
+use anyhow::{bail, ensure, Result};
 use std::any::TypeId;
 use std::fmt;
 use std::marker;
 use std::mem::MaybeUninit;
+use std::ptr::NonNull;
 use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
 use wasmtime_environ::component::{
     CanonicalAbiInfo, ComponentTypes, DefinedResourceIndex, InterfaceType, ResourceIndex,
@@ -336,7 +335,14 @@ pub struct HostResourceTables<'a> {
 #[derive(Default)]
 pub struct HostResourceData {
     cur_generation: u32,
-    generation_of_table_slot: Vec<u32>,
+    table_slot_metadata: Vec<TableSlot>,
+}
+
+#[derive(Copy, Clone)]
+struct TableSlot {
+    generation: u32,
+    flags: Option<InstanceFlags>,
+    dtor: Option<SendSyncPtr<VMFuncRef>>,
 }
 
 /// Host representation of an index into a table slot.
@@ -388,42 +394,43 @@ impl<'a> HostResourceTables<'a> {
     /// Lifts an `own` resource that resides in the host's tables at the `idx`
     /// specified into its `rep`.
     ///
-    /// This method additionally takes an `expected` type which the resource is
-    /// expected to have. All host resources are stored into a single table so
-    /// this is used to perform a runtime check to ensure that the resource
-    /// still has the same type as when it was originally inserted.
-    ///
     /// # Errors
     ///
-    /// Returns an error if `idx` doesn't point to a valid owned resource, if
-    /// `idx` can't be lifted as an `own` (e.g. it has active borrows), or if
-    /// the resource at `idx` does not have the type `expected`.
+    /// Returns an error if `idx` doesn't point to a valid owned resource, or
+    /// if `idx` can't be lifted as an `own` (e.g. it has active borrows).
     pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> {
-        let idx = self.validate_host_index(idx, true)?;
+        let (idx, _) = self.validate_host_index(idx, true)?;
         self.tables.resource_lift_own(None, idx)
     }
 
     /// See [`HostResourceTables::host_resource_lift_own`].
     pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> {
-        let idx = self.validate_host_index(idx, false)?;
+        let (idx, _) = self.validate_host_index(idx, false)?;
         self.tables.resource_lift_borrow(None, idx)
     }
 
     /// Lowers an `own` resource to be owned by the host.
     ///
     /// This returns a new index into the host's set of resource tables which
-    /// will point to the `rep` specified as well as recording that it has the
-    /// `ty` specified. The returned index is suitable for conversion into
-    /// either [`Resource`] or [`ResourceAny`].
-    pub fn host_resource_lower_own(&mut self, rep: u32) -> Result<HostResourceIndex> {
+    /// will point to the `rep` specified. The returned index is suitable for
+    /// conversion into either [`Resource`] or [`ResourceAny`].
+    ///
+    /// The `dtor` and instance `flags` are specified as well to know what
+    /// destructor to run when this resource is destroyed.
+    pub fn host_resource_lower_own(
+        &mut self,
+        rep: u32,
+        dtor: Option<NonNull<VMFuncRef>>,
+        flags: Option<InstanceFlags>,
+    ) -> Result<HostResourceIndex> {
         let idx = self.tables.resource_lower_own(None, rep)?;
-        Ok(self.new_host_index(idx))
+        Ok(self.new_host_index(idx, dtor, flags))
     }
 
     /// See [`HostResourceTables::host_resource_lower_own`].
     pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> {
         let idx = self.tables.resource_lower_borrow(None, rep)?;
-        Ok(self.new_host_index(idx))
+        Ok(self.new_host_index(idx, None, None))
     }
 
     /// Validates that `idx` is still valid for the host tables, notably
@@ -433,19 +440,20 @@ impl<'a> HostResourceTables<'a> {
     /// The `is_removal` option indicates whether or not this table access will
     /// end up removing the element from the host table. In such a situation the
     /// current generation number is incremented.
-    fn validate_host_index(&mut self, idx: HostResourceIndex, is_removal: bool) -> Result<u32> {
-        let actual = usize::try_from(idx.index()).ok().and_then(|i| {
-            self.host_resource_data
-                .generation_of_table_slot
-                .get(i)
-                .copied()
-        });
+    fn validate_host_index(
+        &mut self,
+        idx: HostResourceIndex,
+        is_removal: bool,
+    ) -> Result<(u32, Option<TableSlot>)> {
+        let actual = usize::try_from(idx.index())
+            .ok()
+            .and_then(|i| self.host_resource_data.table_slot_metadata.get(i).copied());
 
         // If `idx` is out-of-bounds then skip returning an error. In such a
         // situation the operation that this is guarding will return a more
         // precise error, such as a lift operation.
         if let Some(actual) = actual {
-            if actual != idx.gen() {
+            if actual.generation != idx.gen() {
                 bail!("host-owned resource is being used with the wrong type");
             }
         }
@@ -457,7 +465,7 @@ impl<'a> HostResourceTables<'a> {
             self.host_resource_data.cur_generation += 1;
         }
 
-        Ok(idx.index())
+        Ok((idx.index(), actual))
     }
 
     /// Creates a new `HostResourceIndex` which will point to the raw table
@@ -465,24 +473,37 @@ impl<'a> HostResourceTables<'a> {
     ///
     /// This will register metadata necessary to track the current generation
     /// in the returned `HostResourceIndex` as well.
-    fn new_host_index(&mut self, idx: u32) -> HostResourceIndex {
-        let list = &mut self.host_resource_data.generation_of_table_slot;
-        let gen = self.host_resource_data.cur_generation;
+    fn new_host_index(
+        &mut self,
+        idx: u32,
+        dtor: Option<NonNull<VMFuncRef>>,
+        flags: Option<InstanceFlags>,
+    ) -> HostResourceIndex {
+        let list = &mut self.host_resource_data.table_slot_metadata;
+        let info = TableSlot {
+            generation: self.host_resource_data.cur_generation,
+            flags,
+            dtor: dtor.map(SendSyncPtr::new),
+        };
         match list.get_mut(idx as usize) {
-            Some(slot) => *slot = gen,
+            Some(slot) => *slot = info,
             None => {
                 // Resource handles start at 1, not zero, so push two elements
                 // for the first resource handle.
                 if list.is_empty() {
                     assert_eq!(idx, 1);
-                    list.push(0);
+                    list.push(TableSlot {
+                        generation: 0,
+                        flags: None,
+                        dtor: None,
+                    });
                 }
                 assert_eq!(idx as usize, list.len());
-                list.push(gen);
+                list.push(info);
             }
         }
 
-        HostResourceIndex::new(idx, gen)
+        HostResourceIndex::new(idx, info.generation)
     }
 
     /// Drops a host-owned resource from host tables.
@@ -497,9 +518,12 @@ impl<'a> HostResourceTables<'a> {
     /// Returns an error if `idx` doesn't point to a valid resource, points to
     /// an `own` with active borrows, or if it doesn't have the type `expected`
     /// in the host tables.
-    pub fn host_resource_drop(&mut self, idx: HostResourceIndex) -> Result<Option<u32>> {
-        let idx = self.validate_host_index(idx, true)?;
-        self.tables.resource_drop(None, idx)
+    fn host_resource_drop(&mut self, idx: HostResourceIndex) -> Result<Option<(u32, TableSlot)>> {
+        let (idx, slot) = self.validate_host_index(idx, true)?;
+        match self.tables.resource_drop(None, idx)? {
+            Some(rep) => Ok(Some((rep, slot.unwrap()))),
+            None => Ok(None),
+        }
     }
 
     /// Lowers an `own` resource into the guest, converting the `rep` specified
@@ -663,8 +687,11 @@ where
                     // state.
                     //
                     // Afterwards this is the same as the `idx` case below.
+                    //
+                    // Note that flags/dtor are passed as `None` here since
+                    // `Resource<T>` doesn't offer destruction support.
                     ResourceState::NotInTable => {
-                        let idx = cx.host_resource_lower_own(self.rep)?;
+                        let idx = cx.host_resource_lower_own(self.rep, None, None)?;
                         let prev = self.state.swap(ResourceState::Index(idx));
                         assert_eq!(prev, ResourceState::NotInTable);
                         cx.host_resource_lift_borrow(idx)?
@@ -737,14 +764,10 @@ where
         mut store: impl AsContextMut,
     ) -> Result<Self> {
         let store = store.as_context_mut();
-        let store_id = store.0.id();
         let mut tables = HostResourceTables::new_host(store.0);
-        let ResourceAny { idx, ty, own_state } = resource;
+        let ResourceAny { idx, ty, owned } = resource;
         ensure!(ty == ResourceType::host::<T>(), "resource type mismatch");
-        let (state, rep) = if let Some(OwnState { store, dtor, flags }) = own_state {
-            assert_eq!(store_id, store, "wrong store used to convert resource");
-            assert!(dtor.is_some(), "destructor must be set");
-            assert!(flags.is_none(), "flags must not be set");
+        let (state, rep) = if owned {
             let rep = tables.host_resource_lift_own(idx)?;
             (AtomicResourceState::NOT_IN_TABLE, rep)
         } else {
@@ -759,13 +782,8 @@ where
     }
 
     /// See [`ResourceAny::try_from_resource`]
-    pub fn try_into_resource_any<U>(
-        self,
-        store: impl AsContextMut,
-        instance: &InstancePre<U>,
-        idx: ResourceImportIndex,
-    ) -> Result<ResourceAny> {
-        ResourceAny::try_from_resource(self, store, instance, idx)
+    pub fn try_into_resource_any(self, store: impl AsContextMut) -> Result<ResourceAny> {
+        ResourceAny::try_from_resource(self, store)
     }
 }
 
@@ -865,14 +883,7 @@ impl<T> fmt::Debug for Resource<T> {
 pub struct ResourceAny {
     idx: HostResourceIndex,
     ty: ResourceType,
-    own_state: Option<OwnState>,
-}
-
-#[derive(Copy, Clone)]
-struct OwnState {
-    store: StoreId,
-    flags: Option<InstanceFlags>,
-    dtor: Option<SendSyncPtr<VMFuncRef>>,
+    owned: bool,
 }
 
 impl ResourceAny {
@@ -880,63 +891,38 @@ impl ResourceAny {
     ///
     /// * `resource` is the resource to convert.
     /// * `store` is the store to place the returned resource into.
-    /// * `instance_pre` is the instance from where `idx` below was derived.
-    /// * `idx` is the [`ResourceImportIndex`] returned by [`Linker::resource`].
     ///
-    /// [`Linker::resource`]: crate::component::LinkerInstance::resource
+    /// The returned `ResourceAny` will not have a destructor attached to it
+    /// meaning that if `resource_drop` is called then it will not invoked a
+    /// host-defined destructor. This is similar to how `Resource<T>` does not
+    /// have a destructor associated with it.
     ///
     /// # Errors
     ///
-    /// This method will return an error if `idx` isn't valid for
-    /// `instance_pre` or if `resource` is not of the correct type for the
-    /// `idx` import.
-    pub fn try_from_resource<T: 'static, U>(
+    /// This method will return an error if `resource` has already been "taken"
+    /// and has ownership transferred elsewhere which can happen in situations
+    /// such as when it's already lowered into a component.
+    pub fn try_from_resource<T: 'static>(
         resource: Resource<T>,
         mut store: impl AsContextMut,
-        instance_pre: &InstancePre<U>,
-        idx: ResourceImportIndex,
     ) -> Result<Self> {
         let Resource { rep, state, .. } = resource;
         let store = store.as_context_mut();
-        let import = instance_pre
-            .resource_import(idx)
-            .context("import not found")?;
-        let RuntimeImport::Resource {
-            ty, dtor_funcref, ..
-        } = import
-        else {
-            bail!("import is not a resource")
-        };
-        ensure!(*ty == ResourceType::host::<T>(), "resource type mismatch");
 
         let mut tables = HostResourceTables::new_host(store.0);
-        let (idx, own_state) = match state.get() {
-            ResourceState::Borrow => (tables.host_resource_lower_borrow(rep)?, None),
+        let (idx, owned) = match state.get() {
+            ResourceState::Borrow => (tables.host_resource_lower_borrow(rep)?, false),
             ResourceState::NotInTable => {
-                let idx = tables.host_resource_lower_own(rep)?;
-                (
-                    idx,
-                    Some(OwnState {
-                        dtor: Some(dtor_funcref.into()),
-                        flags: None,
-                        store: store.0.id(),
-                    }),
-                )
+                let idx = tables.host_resource_lower_own(rep, None, None)?;
+                (idx, true)
             }
             ResourceState::Taken => bail!("host resource already consumed"),
-            ResourceState::Index(idx) => (
-                idx,
-                Some(OwnState {
-                    dtor: Some(dtor_funcref.into()),
-                    flags: None,
-                    store: store.0.id(),
-                }),
-            ),
+            ResourceState::Index(idx) => (idx, true),
         };
         Ok(Self {
             idx,
-            ty: *ty,
-            own_state,
+            ty: ResourceType::host::<T>(),
+            owned,
         })
     }
 
@@ -960,7 +946,7 @@ impl ResourceAny {
     /// Returns whether this is an owned resource, and if not it's a borrowed
     /// resource.
     pub fn owned(&self) -> bool {
-        self.own_state.is_some()
+        self.owned
     }
 
     /// Destroy this resource and release any state associated with it.
@@ -1002,33 +988,25 @@ impl ResourceAny {
         //
         // This could fail if the index is invalid or if this is removing an
         // `Own` entry which is currently being borrowed.
-        let rep = HostResourceTables::new_host(store.0).host_resource_drop(self.idx)?;
+        let pair = HostResourceTables::new_host(store.0).host_resource_drop(self.idx)?;
 
-        let (rep, state) = match (rep, &self.own_state) {
-            (Some(rep), Some(state)) => (rep, state),
+        let (rep, slot) = match (pair, self.owned) {
+            (Some(pair), true) => pair,
 
             // A `borrow` was removed from the table and no further
             // destruction, e.g. the destructor, is required so we're done.
-            (None, None) => return Ok(()),
+            (None, false) => return Ok(()),
 
             _ => unreachable!(),
         };
 
-        // Double-check that accessing the raw pointers on `state` are safe due
-        // to the presence of `store` above.
-        assert_eq!(
-            store.0.id(),
-            state.store,
-            "wrong store used to destroy resource"
-        );
-
         // Implement the reentrance check required by the canonical ABI. Note
         // that this happens whether or not a destructor is present.
         //
         // Note that this should be safe because the raw pointer access in
         // `flags` is valid due to `store` being the owner of the flags and
         // flags are never destroyed within the store.
-        if let Some(flags) = state.flags {
+        if let Some(flags) = slot.flags {
             unsafe {
                 if !flags.may_enter() {
                     bail!(Trap::CannotEnterComponent);
@@ -1036,7 +1014,7 @@ impl ResourceAny {
             }
         }
 
-        let dtor = match state.dtor {
+        let dtor = match slot.dtor {
             Some(dtor) => dtor.as_non_null(),
             None => return Ok(()),
         };
@@ -1075,15 +1053,11 @@ impl ResourceAny {
             InterfaceType::Own(t) => {
                 let ty = cx.resource_type(t);
                 let (rep, dtor, flags) = cx.guest_resource_lift_own(t, index)?;
-                let idx = cx.host_resource_lower_own(rep)?;
+                let idx = cx.host_resource_lower_own(rep, dtor, flags)?;
                 Ok(ResourceAny {
                     idx,
                     ty,
-                    own_state: Some(OwnState {
-                        dtor: dtor.map(SendSyncPtr::new),
-                        flags,
-                        store: cx.store_id(),
-                    }),
+                    owned: true,
                 })
             }
             InterfaceType::Borrow(t) => {
@@ -1093,7 +1067,7 @@ impl ResourceAny {
                 Ok(ResourceAny {
                     idx,
                     ty,
-                    own_state: None,
+                    owned: false,
                 })
             }
             _ => bad_type_info(),
@@ -1147,23 +1121,3 @@ unsafe impl Lift for ResourceAny {
         ResourceAny::lift_from_index(cx, ty, index)
     }
 }
-
-impl fmt::Debug for OwnState {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("OwnState")
-            .field("store", &self.store)
-            .finish()
-    }
-}
-
-// This is a loose definition for `Val` primarily so it doesn't need to be
-// strictly 100% correct, and equality of resources is a bit iffy anyway, so
-// ignore equality here and only factor in the indices and other metadata in
-// `ResourceAny`.
-impl PartialEq for OwnState {
-    fn eq(&self, _other: &OwnState) -> bool {
-        true
-    }
-}
-
-impl Eq for OwnState {}
diff --git a/crates/wasmtime/src/runtime/component/types.rs b/crates/wasmtime/src/runtime/component/types.rs
index 06b29d47e..b40780b5d 100644
--- a/crates/wasmtime/src/runtime/component/types.rs
+++ b/crates/wasmtime/src/runtime/component/types.rs
@@ -1,18 +1,15 @@
 //! This module defines the `Type` type, representing the dynamic form of a component interface type.
 
 use crate::component::matching::InstanceType;
-use crate::component::values::{self, Val};
 use crate::{Engine, ExternType, FuncType};
-use anyhow::{anyhow, Result};
 use std::fmt;
-use std::mem;
 use std::ops::Deref;
 use std::sync::Arc;
 use wasmtime_environ::component::{
-    CanonicalAbiInfo, ComponentTypes, InterfaceType, ResourceIndex, TypeComponentIndex,
-    TypeComponentInstanceIndex, TypeDef, TypeEnumIndex, TypeFlagsIndex, TypeFuncIndex,
-    TypeListIndex, TypeModuleIndex, TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex,
-    TypeResultIndex, TypeTupleIndex, TypeVariantIndex,
+    ComponentTypes, InterfaceType, ResourceIndex, TypeComponentIndex, TypeComponentInstanceIndex,
+    TypeDef, TypeEnumIndex, TypeFlagsIndex, TypeFuncIndex, TypeListIndex, TypeModuleIndex,
+    TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeTupleIndex,
+    TypeVariantIndex,
 };
 use wasmtime_environ::PrimaryMap;
 
@@ -183,18 +180,21 @@ impl TypeChecker<'_> {
         if a.cases.len() != b.cases.len() {
             return false;
         }
-        a.cases.iter().zip(b.cases.iter()).all(|(a_case, b_case)| {
-            if a_case.name != b_case.name {
-                return false;
-            }
-            match (a_case.ty, b_case.ty) {
-                (Some(a_case_ty), Some(b_case_ty)) => {
-                    self.interface_types_equal(a_case_ty, b_case_ty)
+        a.cases
+            .iter()
+            .zip(b.cases.iter())
+            .all(|((a_name, a_ty), (b_name, b_ty))| {
+                if a_name != b_name {
+                    return false;
                 }
-                (None, None) => true,
-                _ => false,
-            }
-        })
+                match (a_ty, b_ty) {
+                    (Some(a_case_ty), Some(b_case_ty)) => {
+                        self.interface_types_equal(*a_case_ty, *b_case_ty)
+                    }
+                    (None, None) => true,
+                    _ => false,
+                }
+            })
     }
 
     fn results_equal(&self, r1: TypeResultIndex, r2: TypeResultIndex) -> bool {
@@ -259,11 +259,6 @@ impl PartialEq for List {
 impl Eq for List {}
 
 impl List {
-    /// Instantiate this type with the specified `values`.
-    pub fn new_val(&self, values: Box<[Val]>) -> Result<Val> {
-        Ok(Val::List(values::List::new(self, values)?))
-    }
-
     pub(crate) fn from(index: TypeListIndex, ty: &InstanceType<'_>) -> Self {
         List(Handle::new(index, ty))
     }
@@ -288,11 +283,6 @@ pub struct Field<'a> {
 pub struct Record(Handle<TypeRecordIndex>);
 
 impl Record {
-    /// Instantiate this type with the specified `values`.
-    pub fn new_val<'a>(&self, values: impl IntoIterator<Item = (&'a str, Val)>) -> Result<Val> {
-        Ok(Val::Record(values::Record::new(self, values)?))
-    }
-
     pub(crate) fn from(index: TypeRecordIndex, ty: &InstanceType<'_>) -> Self {
         Record(Handle::new(index, ty))
     }
@@ -319,11 +309,6 @@ impl Eq for Record {}
 pub struct Tuple(Handle<TypeTupleIndex>);
 
 impl Tuple {
-    /// Instantiate this type ith the specified `values`.
-    pub fn new_val(&self, values: Box<[Val]>) -> Result<Val> {
-        Ok(Val::Tuple(values::Tuple::new(self, values)?))
-    }
-
     pub(crate) fn from(index: TypeTupleIndex, ty: &InstanceType<'_>) -> Self {
         Tuple(Handle::new(index, ty))
     }
@@ -358,24 +343,19 @@ pub struct Case<'a> {
 pub struct Variant(Handle<TypeVariantIndex>);
 
 impl Variant {
-    /// Instantiate this type with the specified case `name` and `value`.
-    pub fn new_val(&self, name: &str, value: Option<Val>) -> Result<Val> {
-        Ok(Val::Variant(values::Variant::new(self, name, value)?))
-    }
-
     pub(crate) fn from(index: TypeVariantIndex, ty: &InstanceType<'_>) -> Self {
         Variant(Handle::new(index, ty))
     }
 
     /// Retrieve the cases of this `variant` in declaration order.
     pub fn cases(&self) -> impl ExactSizeIterator<Item = Case> {
-        self.0.types[self.0.index].cases.iter().map(|case| Case {
-            name: &case.name,
-            ty: case
-                .ty
-                .as_ref()
-                .map(|ty| Type::from(ty, &self.0.instance())),
-        })
+        self.0.types[self.0.index]
+            .cases
+            .iter()
+            .map(|(name, ty)| Case {
+                name: name,
+                ty: ty.as_ref().map(|ty| Type::from(ty, &self.0.instance())),
+            })
     }
 }
 
@@ -392,11 +372,6 @@ impl Eq for Variant {}
 pub struct Enum(Handle<TypeEnumIndex>);
 
 impl Enum {
-    /// Instantiate this type with the specified case `name`.
-    pub fn new_val(&self, name: &str) -> Result<Val> {
-        Ok(Val::Enum(values::Enum::new(self, name)?))
-    }
-
     pub(crate) fn from(index: TypeEnumIndex, ty: &InstanceType<'_>) -> Self {
         Enum(Handle::new(index, ty))
     }
@@ -423,11 +398,6 @@ impl Eq for Enum {}
 pub struct OptionType(Handle<TypeOptionIndex>);
 
 impl OptionType {
-    /// Instantiate this type with the specified `value`.
-    pub fn new_val(&self, value: Option<Val>) -> Result<Val> {
-        Ok(Val::Option(values::OptionVal::new(self, value)?))
-    }
-
     pub(crate) fn from(index: TypeOptionIndex, ty: &InstanceType<'_>) -> Self {
         OptionType(Handle::new(index, ty))
     }
@@ -451,11 +421,6 @@ impl Eq for OptionType {}
 pub struct ResultType(Handle<TypeResultIndex>);
 
 impl ResultType {
-    /// Instantiate this type with the specified `value`.
-    pub fn new_val(&self, value: Result<Option<Val>, Option<Val>>) -> Result<Val> {
-        Ok(Val::Result(values::ResultVal::new(self, value)?))
-    }
-
     pub(crate) fn from(index: TypeResultIndex, ty: &InstanceType<'_>) -> Self {
         ResultType(Handle::new(index, ty))
     }
@@ -490,11 +455,6 @@ impl Eq for ResultType {}
 pub struct Flags(Handle<TypeFlagsIndex>);
 
 impl Flags {
-    /// Instantiate this type with the specified flag `names`.
-    pub fn new_val(&self, names: &[&str]) -> Result<Val> {
-        Ok(Val::Flags(values::Flags::new(self, names)?))
-    }
-
     pub(crate) fn from(index: TypeFlagsIndex, ty: &InstanceType<'_>) -> Self {
         Flags(Handle::new(index, ty))
     }
@@ -506,10 +466,6 @@ impl Flags {
             .iter()
             .map(|name| name.deref())
     }
-
-    pub(crate) fn canonical_abi(&self) -> &CanonicalAbiInfo {
-        &self.0.types[self.0.index].abi
-    }
 }
 
 impl PartialEq for Flags {
@@ -678,31 +634,6 @@ impl Type {
         }
     }
 
-    /// Checks whether type of `value` is a subtype of `self`.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error in case of a type mismatch
-    pub(crate) fn is_supertype_of(&self, value: &Val) -> Result<()> {
-        let other = &value.ty();
-        if self == other
-            || matches!((self, other), (Self::Borrow(s), Self::Own(other)) if s == other)
-        {
-            Ok(())
-        } else if mem::discriminant(self) != mem::discriminant(other) {
-            Err(anyhow!(
-                "type mismatch: expected {}, got {}",
-                self.desc(),
-                other.desc()
-            ))
-        } else {
-            Err(anyhow!(
-                "type mismatch for {}, possibly due to mixing distinct composite types",
-                self.desc()
-            ))
-        }
-    }
-
     /// Convert the specified `InterfaceType` to a `Type`.
     pub(crate) fn from(ty: &InterfaceType, instance: &InstanceType<'_>) -> Self {
         match ty {
diff --git a/crates/wasmtime/src/runtime/component/values.rs b/crates/wasmtime/src/runtime/component/values.rs
index 52d08d120..f6bfd1893 100644
--- a/crates/wasmtime/src/runtime/component/values.rs
+++ b/crates/wasmtime/src/runtime/component/values.rs
@@ -1,640 +1,14 @@
-use crate::component::func::{bad_type_info, Lift, LiftContext, Lower, LowerContext};
-use crate::component::types::{self, Type};
+use crate::component::func::{desc, Lift, LiftContext, Lower, LowerContext};
 use crate::component::ResourceAny;
 use crate::ValRaw;
-use anyhow::{anyhow, bail, Context, Error, Result};
-use std::collections::HashMap;
-use std::fmt;
-use std::iter;
+use anyhow::{anyhow, bail, Result};
 use std::mem::MaybeUninit;
-use std::ops::Deref;
 use wasmtime_component_util::{DiscriminantSize, FlagsSize};
 use wasmtime_environ::component::{
-    CanonicalAbiInfo, ComponentTypes, InterfaceType, TypeListIndex, VariantInfo,
+    CanonicalAbiInfo, InterfaceType, TypeEnum, TypeFlags, TypeListIndex, TypeOption, TypeResult,
+    TypeVariant, VariantInfo,
 };
 
-/// Dynamic representation of `list<T>` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that `Vec<T>` is also supported for component model lists when
-/// using [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct List {
-    ty: types::List,
-    values: Box<[Val]>,
-}
-
-impl List {
-    /// Creates a new list with the specified type with the specified `values`.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if any value in `values` does not have the element
-    /// type of `ty` specified.
-    pub fn new(ty: &types::List, values: Box<[Val]>) -> Result<Self> {
-        let element_type = ty.ty();
-        for (index, value) in values.iter().enumerate() {
-            element_type
-                .is_supertype_of(value)
-                .with_context(|| format!("type mismatch for element {index} of list"))?;
-        }
-
-        Ok(Self {
-            ty: ty.clone(),
-            values,
-        })
-    }
-
-    /// Returns the corresponding type of this list
-    pub fn ty(&self) -> &types::List {
-        &self.ty
-    }
-}
-
-impl Deref for List {
-    type Target = [Val];
-
-    fn deref(&self) -> &[Val] {
-        &self.values
-    }
-}
-
-impl fmt::Debug for List {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut f = f.debug_list();
-        for val in self.iter() {
-            f.entry(val);
-        }
-        f.finish()
-    }
-}
-
-/// Dynamic representation of `record` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that records can also be mapped to Rust `struct`s through the
-/// [`#[derive(ComponentType)]`](macro@crate::component::ComponentType) macro
-/// and using [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct Record {
-    ty: types::Record,
-    values: Box<[Val]>,
-}
-
-impl Record {
-    /// Creates a new record with the specified `ty` and the `values` for fields.
-    ///
-    /// Note that the `values` specified for fields must list fields in the
-    /// same order that the `ty` lists them in.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if any of the `values` specified are in the wrong order
-    /// field-wise or have the wrong type relative to what's specified in `ty`.
-    pub fn new<'a>(
-        ty: &types::Record,
-        values: impl IntoIterator<Item = (&'a str, Val)>,
-    ) -> Result<Self> {
-        let mut fields = ty.fields();
-        let expected_len = fields.len();
-        let mut iter = values.into_iter();
-        let mut values = Vec::with_capacity(expected_len);
-        loop {
-            match (fields.next(), iter.next()) {
-                (Some(field), Some((name, value))) => {
-                    if name == field.name {
-                        field
-                            .ty
-                            .is_supertype_of(&value)
-                            .with_context(|| format!("type mismatch for field {name} of record"))?;
-
-                        values.push(value);
-                    } else {
-                        bail!("field name mismatch: expected {}; got {name}", field.name)
-                    }
-                }
-                (None, Some((_, value))) => values.push(value),
-                _ => break,
-            }
-        }
-
-        if values.len() != expected_len {
-            bail!("expected {} value(s); got {}", expected_len, values.len());
-        }
-
-        Ok(Self {
-            ty: ty.clone(),
-            values: values.into(),
-        })
-    }
-
-    /// Returns the corresponding type of this record.
-    pub fn ty(&self) -> &types::Record {
-        &self.ty
-    }
-
-    /// Returns the list of fields that this struct contains with their name
-    /// and value.
-    pub fn fields(&self) -> impl ExactSizeIterator<Item = (&str, &Val)> {
-        assert_eq!(self.values.len(), self.ty.fields().len());
-        self.ty
-            .fields()
-            .zip(self.values.iter())
-            .map(|(ty, val)| (ty.name, val))
-    }
-}
-
-impl fmt::Debug for Record {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut f = f.debug_struct("Record");
-        for (name, val) in self.fields() {
-            f.field(name, val);
-        }
-        f.finish()
-    }
-}
-
-/// Dynamic representation of `tuple<...>` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that tuples are also mapped to Rust tuples when using
-/// [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct Tuple {
-    ty: types::Tuple,
-    values: Box<[Val]>,
-}
-
-impl Tuple {
-    /// Creates a new tuple with the `ty` specified and `values` for each
-    /// entry.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if `values` does not match the `ty` specified, such as
-    /// if it has the wrong length or if any field has the wrong type.
-    pub fn new(ty: &types::Tuple, values: Box<[Val]>) -> Result<Self> {
-        if values.len() != ty.types().len() {
-            bail!(
-                "expected {} value(s); got {}",
-                ty.types().len(),
-                values.len()
-            );
-        }
-
-        for (index, (value, ty)) in values.iter().zip(ty.types()).enumerate() {
-            ty.is_supertype_of(value)
-                .with_context(|| format!("type mismatch for field {index} of tuple"))?;
-        }
-
-        Ok(Self {
-            ty: ty.clone(),
-            values,
-        })
-    }
-
-    /// Returns the type of this tuple.
-    pub fn ty(&self) -> &types::Tuple {
-        &self.ty
-    }
-
-    /// Returns the list of values that this tuple contains.
-    pub fn values(&self) -> &[Val] {
-        &self.values
-    }
-}
-
-impl fmt::Debug for Tuple {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut tuple = f.debug_tuple("");
-        for val in self.values() {
-            tuple.field(val);
-        }
-        tuple.finish()
-    }
-}
-
-/// Dynamic representation of `variant` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that variants can also be mapped to Rust `enum`s through the
-/// [`#[derive(ComponentType)]`](macro@crate::component::ComponentType) macro
-/// and using [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct Variant {
-    ty: types::Variant,
-    discriminant: u32,
-    value: Option<Box<Val>>,
-}
-
-impl Variant {
-    /// Creates a new variant with the specified `ty` and the case `name` and
-    /// `value` provided.
-    ///
-    /// This will create a instance of a variant with the case `name` which has
-    /// the optional payload of `value`.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if `name` doesn't match any case of `ty` or if
-    /// `value` is of the wrong type. Note that `ty` must match the payload
-    /// type exactly, in that if the case for `name` doesn't have a type then
-    /// `value` must be `None`.
-    pub fn new(ty: &types::Variant, name: &str, value: Option<Val>) -> Result<Self> {
-        let (discriminant, case_type) = ty
-            .cases()
-            .enumerate()
-            .find_map(|(index, case)| {
-                if case.name == name {
-                    Some((index, case.ty))
-                } else {
-                    None
-                }
-            })
-            .ok_or_else(|| anyhow!("unknown variant case: {name}"))?;
-
-        typecheck_payload(name, case_type.as_ref(), value.as_ref())?;
-
-        Ok(Self {
-            ty: ty.clone(),
-            discriminant: u32::try_from(discriminant)?,
-            value: value.map(Box::new),
-        })
-    }
-
-    /// Returns the type of this variant.
-    pub fn ty(&self) -> &types::Variant {
-        &self.ty
-    }
-
-    /// Returns name of the discriminant of this value within the variant type.
-    pub fn discriminant(&self) -> &str {
-        self.ty
-            .cases()
-            .nth(self.discriminant as usize)
-            .unwrap()
-            .name
-    }
-
-    /// Returns the payload value for this variant.
-    pub fn payload(&self) -> Option<&Val> {
-        self.value.as_deref()
-    }
-
-    fn as_generic<'a>(
-        &'a self,
-        types: &'a ComponentTypes,
-        ty: InterfaceType,
-    ) -> GenericVariant<'a> {
-        let ty = match ty {
-            InterfaceType::Variant(i) => &types[i],
-            _ => bad_type_info(),
-        };
-        GenericVariant {
-            discriminant: self.discriminant,
-            abi: &ty.abi,
-            info: &ty.info,
-            payload: self
-                .value
-                .as_deref()
-                .zip(ty.cases[self.discriminant as usize].ty),
-        }
-    }
-}
-
-fn typecheck_payload(name: &str, case_type: Option<&Type>, value: Option<&Val>) -> Result<()> {
-    match (case_type, value) {
-        (Some(expected), Some(actual)) => expected
-            .is_supertype_of(&actual)
-            .with_context(|| format!("type mismatch for case {name} of variant")),
-        (None, None) => Ok(()),
-        (Some(_), None) => bail!("expected a payload for case `{name}`"),
-        (None, Some(_)) => bail!("did not expect payload for case `{name}`"),
-    }
-}
-
-impl fmt::Debug for Variant {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_tuple(self.discriminant())
-            .field(&self.payload())
-            .finish()
-    }
-}
-
-/// Dynamic representation of `enum` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that enums can also be mapped to Rust `enum`s through the
-/// [`#[derive(ComponentType)]`](macro@crate::component::ComponentType) macro
-/// and using [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct Enum {
-    ty: types::Enum,
-    discriminant: u32,
-}
-
-impl Enum {
-    /// Creates a new enum value with the `ty` specified selecting the
-    /// specified variant called `name`.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if `name` isn't listed within `ty`.
-    pub fn new(ty: &types::Enum, name: &str) -> Result<Self> {
-        let discriminant = u32::try_from(
-            ty.names()
-                .position(|n| n == name)
-                .ok_or_else(|| anyhow!("unknown enum case: {name}"))?,
-        )?;
-
-        Ok(Self {
-            ty: ty.clone(),
-            discriminant,
-        })
-    }
-
-    /// Returns the type of this value.
-    pub fn ty(&self) -> &types::Enum {
-        &self.ty
-    }
-
-    /// Returns name of this enum value.
-    pub fn discriminant(&self) -> &str {
-        self.ty.names().nth(self.discriminant as usize).unwrap()
-    }
-
-    fn as_generic<'a>(
-        &'a self,
-        types: &'a ComponentTypes,
-        ty: InterfaceType,
-    ) -> GenericVariant<'a> {
-        let ty = match ty {
-            InterfaceType::Enum(i) => &types[i],
-            _ => bad_type_info(),
-        };
-        GenericVariant {
-            discriminant: self.discriminant,
-            abi: &ty.abi,
-            info: &ty.info,
-            payload: None,
-        }
-    }
-}
-
-impl fmt::Debug for Enum {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&self.discriminant(), f)
-    }
-}
-
-/// Dynamic representation of `option<T>` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that options are also mapped to Rust `Option<T>` when using
-/// [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct OptionVal {
-    ty: types::OptionType,
-    discriminant: u32,
-    value: Option<Box<Val>>,
-}
-
-impl OptionVal {
-    /// Creates a new value with the `ty` specified holding the payload `value`.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if `value` is `Some` and the payload has the wrong
-    /// type for `ty`.
-    pub fn new(ty: &types::OptionType, value: Option<Val>) -> Result<Self> {
-        let value = value
-            .map(|value| {
-                ty.ty()
-                    .is_supertype_of(&value)
-                    .context("type mismatch for option")?;
-
-                Ok::<_, Error>(value)
-            })
-            .transpose()?;
-
-        Ok(Self {
-            ty: ty.clone(),
-            discriminant: if value.is_none() { 0 } else { 1 },
-            value: value.map(Box::new),
-        })
-    }
-
-    /// Returns the type of this value.
-    pub fn ty(&self) -> &types::OptionType {
-        &self.ty
-    }
-
-    /// Returns the optional value contained within.
-    pub fn value(&self) -> Option<&Val> {
-        self.value.as_deref()
-    }
-
-    fn as_generic<'a>(
-        &'a self,
-        types: &'a ComponentTypes,
-        ty: InterfaceType,
-    ) -> GenericVariant<'a> {
-        let ty = match ty {
-            InterfaceType::Option(i) => &types[i],
-            _ => bad_type_info(),
-        };
-        GenericVariant {
-            discriminant: self.discriminant,
-            abi: &ty.abi,
-            info: &ty.info,
-            payload: self.value.as_deref().zip(Some(ty.ty)),
-        }
-    }
-}
-
-impl fmt::Debug for OptionVal {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.value().fmt(f)
-    }
-}
-
-/// Dynamic representation of `result<T, E>` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that tuples are also mapped to Rust's `Result<T, E>` when using
-/// [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct ResultVal {
-    ty: types::ResultType,
-    discriminant: u32,
-    value: Option<Box<Val>>,
-}
-
-impl ResultVal {
-    /// Creates a new `Result` from the specified `ty` and `value`.
-    ///
-    /// Note that the `value` uses `Option<Val>` for payloads to map to the
-    /// optional payload value of the component model within each variant.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if the `value` payload has the wrong type for `ty`.
-    pub fn new(ty: &types::ResultType, value: Result<Option<Val>, Option<Val>>) -> Result<Self> {
-        Ok(Self {
-            ty: ty.clone(),
-            discriminant: if value.is_ok() { 0 } else { 1 },
-            value: match value {
-                Ok(value) => {
-                    typecheck_payload("ok", ty.ok().as_ref(), value.as_ref())?;
-                    value.map(Box::new)
-                }
-                Err(value) => {
-                    typecheck_payload("err", ty.err().as_ref(), value.as_ref())?;
-                    value.map(Box::new)
-                }
-            },
-        })
-    }
-
-    /// Returns the type of this value.
-    pub fn ty(&self) -> &types::ResultType {
-        &self.ty
-    }
-
-    /// Returns the result value contained within.
-    pub fn value(&self) -> Result<Option<&Val>, Option<&Val>> {
-        if self.discriminant == 0 {
-            Ok(self.value.as_deref())
-        } else {
-            Err(self.value.as_deref())
-        }
-    }
-
-    fn as_generic<'a>(
-        &'a self,
-        types: &'a ComponentTypes,
-        ty: InterfaceType,
-    ) -> GenericVariant<'a> {
-        let ty = match ty {
-            InterfaceType::Result(i) => &types[i],
-            _ => bad_type_info(),
-        };
-        GenericVariant {
-            discriminant: self.discriminant,
-            abi: &ty.abi,
-            info: &ty.info,
-            payload: self.value.as_deref().zip(if self.discriminant == 0 {
-                ty.ok
-            } else {
-                ty.err
-            }),
-        }
-    }
-}
-
-impl fmt::Debug for ResultVal {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.value().fmt(f)
-    }
-}
-
-/// Dynamic representation of `flags` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that flags can also be mapped to Rust structures through the
-/// [`flags!`](crate::component::flags) macro and using
-/// [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct Flags {
-    ty: types::Flags,
-    count: u32,
-    value: Box<[u32]>,
-}
-
-impl Flags {
-    /// Creates a new set of flags with the `ty` specified and the `names` as
-    /// members of the flags.
-    ///
-    /// # Errors
-    ///
-    /// An error is returned if any of `names` aren't present within the `ty`
-    /// specified.
-    pub fn new(ty: &types::Flags, names: &[&str]) -> Result<Self> {
-        let map = ty
-            .names()
-            .enumerate()
-            .map(|(index, name)| (name, index))
-            .collect::<HashMap<_, _>>();
-
-        let count = usize::from(ty.canonical_abi().flat_count.unwrap());
-        let mut values = vec![0_u32; count];
-
-        for name in names {
-            let index = map
-                .get(name)
-                .ok_or_else(|| anyhow!("unknown flag: {name}"))?;
-            values[index / 32] |= 1 << (index % 32);
-        }
-
-        Ok(Self {
-            ty: ty.clone(),
-            count: u32::try_from(map.len())?,
-            value: values.into(),
-        })
-    }
-
-    /// Returns the type of this value.
-    pub fn ty(&self) -> &types::Flags {
-        &self.ty
-    }
-
-    /// Returns an iterator over the set of names that this flags set contains.
-    pub fn flags(&self) -> impl Iterator<Item = &str> {
-        (0..self.count).filter_map(|i| {
-            let (idx, bit) = ((i / 32) as usize, i % 32);
-            if self.value[idx] & (1 << bit) != 0 {
-                Some(self.ty.names().nth(i as usize).unwrap())
-            } else {
-                None
-            }
-        })
-    }
-}
-
-impl fmt::Debug for Flags {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut set = f.debug_set();
-        for flag in self.flags() {
-            set.entry(&flag);
-        }
-        set.finish()
-    }
-}
-
 /// Represents possible runtime values which a component function can either
 /// consume or produce
 ///
@@ -647,7 +21,7 @@ impl fmt::Debug for Flags {
 /// This type is used in conjunction with [`Func::call`] for example if the
 /// signature of a component is not statically known ahead of time.
 ///
-/// # Notes on Equality
+/// # Equality and `Val`
 ///
 /// This type implements both the Rust `PartialEq` and `Eq` traits. This type
 /// additionally contains values which are not necessarily easily equated,
@@ -673,6 +47,18 @@ impl fmt::Debug for Flags {
 /// recommended to "build your own" as this equality intended for fuzzing
 /// Wasmtime may not be suitable for you.
 ///
+/// # Component model types and `Val`
+///
+/// The `Val` type here does not contain enough information to say what the
+/// component model type of a `Val` is. This is instead more of an AST of sorts.
+/// For example the `Val::Enum` only carries information about a single
+/// discriminant, not the entire enumeration or what it's a discriminant of.
+///
+/// This means that when a `Val` is passed to Wasmtime, for example as a
+/// function parameter when calling a function or as a return value from an
+/// host-defined imported function, then it must pass a type-check. Instances of
+/// `Val` are type-checked against what's required by the component itself.
+///
 /// [`Func::call`]: crate::component::Func::call
 #[derive(Debug, Clone)]
 #[allow(missing_docs)]
@@ -689,53 +75,19 @@ pub enum Val {
     Float32(f32),
     Float64(f64),
     Char(char),
-    String(Box<str>),
-    List(List),
-    Record(Record),
-    Tuple(Tuple),
-    Variant(Variant),
-    Enum(Enum),
-    Option(OptionVal),
-    Result(ResultVal),
-    Flags(Flags),
+    String(String),
+    List(Vec<Val>),
+    Record(Vec<(String, Val)>),
+    Tuple(Vec<Val>),
+    Variant(String, Option<Box<Val>>),
+    Enum(String),
+    Option(Option<Box<Val>>),
+    Result(Result<Option<Box<Val>>, Option<Box<Val>>>),
+    Flags(Vec<String>),
     Resource(ResourceAny),
 }
 
 impl Val {
-    /// Retrieve the [`Type`] of this value.
-    pub fn ty(&self) -> Type {
-        match self {
-            Val::Bool(_) => Type::Bool,
-            Val::S8(_) => Type::S8,
-            Val::U8(_) => Type::U8,
-            Val::S16(_) => Type::S16,
-            Val::U16(_) => Type::U16,
-            Val::S32(_) => Type::S32,
-            Val::U32(_) => Type::U32,
-            Val::S64(_) => Type::S64,
-            Val::U64(_) => Type::U64,
-            Val::Float32(_) => Type::Float32,
-            Val::Float64(_) => Type::Float64,
-            Val::Char(_) => Type::Char,
-            Val::String(_) => Type::String,
-            Val::List(List { ty, .. }) => Type::List(ty.clone()),
-            Val::Record(Record { ty, .. }) => Type::Record(ty.clone()),
-            Val::Tuple(Tuple { ty, .. }) => Type::Tuple(ty.clone()),
-            Val::Variant(Variant { ty, .. }) => Type::Variant(ty.clone()),
-            Val::Enum(Enum { ty, .. }) => Type::Enum(ty.clone()),
-            Val::Option(OptionVal { ty, .. }) => Type::Option(ty.clone()),
-            Val::Result(ResultVal { ty, .. }) => Type::Result(ty.clone()),
-            Val::Flags(Flags { ty, .. }) => Type::Flags(ty.clone()),
-            Val::Resource(r) => {
-                if r.owned() {
-                    Type::Own(r.ty())
-                } else {
-                    Type::Borrow(r.ty())
-                }
-            }
-        }
-    }
-
     /// Deserialize a value of this type from core Wasm stack values.
     pub(crate) fn lift(
         cx: &mut LiftContext<'_>,
@@ -758,71 +110,62 @@ impl Val {
             InterfaceType::Own(_) | InterfaceType::Borrow(_) => {
                 Val::Resource(ResourceAny::lift(cx, ty, next(src))?)
             }
-            InterfaceType::String => {
-                Val::String(Box::<str>::lift(cx, ty, &[*next(src), *next(src)])?)
-            }
+            InterfaceType::String => Val::String(<_>::lift(cx, ty, &[*next(src), *next(src)])?),
             InterfaceType::List(i) => {
                 // FIXME: needs memory64 treatment
                 let ptr = u32::lift(cx, InterfaceType::U32, next(src))? as usize;
                 let len = u32::lift(cx, InterfaceType::U32, next(src))? as usize;
                 load_list(cx, i, ptr, len)?
             }
-            InterfaceType::Record(i) => Val::Record(Record {
-                ty: types::Record::from(i, &cx.instance_type()),
-                values: cx.types[i]
+            InterfaceType::Record(i) => Val::Record(
+                cx.types[i]
                     .fields
                     .iter()
-                    .map(|field| Self::lift(cx, field.ty, src))
+                    .map(|field| {
+                        let val = Self::lift(cx, field.ty, src)?;
+                        Ok((field.name.to_string(), val))
+                    })
                     .collect::<Result<_>>()?,
-            }),
-            InterfaceType::Tuple(i) => Val::Tuple(Tuple {
-                ty: types::Tuple::from(i, &cx.instance_type()),
-                values: cx.types[i]
+            ),
+            InterfaceType::Tuple(i) => Val::Tuple(
+                cx.types[i]
                     .types
                     .iter()
                     .map(|ty| Self::lift(cx, *ty, src))
                     .collect::<Result<_>>()?,
-            }),
+            ),
             InterfaceType::Variant(i) => {
+                let vty = &cx.types[i];
                 let (discriminant, value) = lift_variant(
                     cx,
                     cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(),
-                    cx.types[i].cases.iter().map(|case| case.ty),
+                    vty.cases.values().copied(),
                     src,
                 )?;
 
-                Val::Variant(Variant {
-                    ty: types::Variant::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
-                })
+                let (k, _) = vty.cases.get_index(discriminant as usize).unwrap();
+                Val::Variant(k.clone(), value)
             }
             InterfaceType::Enum(i) => {
+                let ety = &cx.types[i];
                 let (discriminant, _) = lift_variant(
                     cx,
                     cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(),
-                    cx.types[i].names.iter().map(|_| None),
+                    ety.names.iter().map(|_| None),
                     src,
                 )?;
 
-                Val::Enum(Enum {
-                    ty: types::Enum::from(i, &cx.instance_type()),
-                    discriminant,
-                })
+                Val::Enum(ety.names[discriminant as usize].clone())
             }
             InterfaceType::Option(i) => {
-                let (discriminant, value) = lift_variant(
+                let (_discriminant, value) = lift_variant(
                     cx,
                     cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(),
                     [None, Some(cx.types[i].ty)].into_iter(),
                     src,
                 )?;
 
-                Val::Option(OptionVal {
-                    ty: types::OptionType::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
-                })
+                Val::Option(value)
             }
             InterfaceType::Result(i) => {
                 let result_ty = &cx.types[i];
@@ -833,24 +176,26 @@ impl Val {
                     src,
                 )?;
 
-                Val::Result(ResultVal {
-                    ty: types::ResultType::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
+                Val::Result(if discriminant == 0 {
+                    Ok(value)
+                } else {
+                    Err(value)
                 })
             }
             InterfaceType::Flags(i) => {
-                let count = u32::try_from(cx.types[i].names.len()).unwrap();
                 let u32_count = cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap();
-                let value = iter::repeat_with(|| u32::lift(cx, InterfaceType::U32, next(src)))
-                    .take(u32_count)
-                    .collect::<Result<_>>()?;
+                let ty = &cx.types[i];
+                let mut flags = Vec::new();
+                for i in 0..u32_count {
+                    push_flags(
+                        ty,
+                        &mut flags,
+                        (i as u32) * 32,
+                        u32::lift(cx, InterfaceType::U32, next(src))?,
+                    );
+                }
 
-                Val::Flags(Flags {
-                    ty: types::Flags::from(i, &cx.instance_type()),
-                    count,
-                    value,
-                })
+                Val::Flags(flags.into())
             }
         })
     }
@@ -870,7 +215,7 @@ impl Val {
             InterfaceType::Float32 => Val::Float32(f32::load(cx, ty, bytes)?),
             InterfaceType::Float64 => Val::Float64(f64::load(cx, ty, bytes)?),
             InterfaceType::Char => Val::Char(char::load(cx, ty, bytes)?),
-            InterfaceType::String => Val::String(<Box<str>>::load(cx, ty, bytes)?),
+            InterfaceType::String => Val::String(<_>::load(cx, ty, bytes)?),
             InterfaceType::Own(_) | InterfaceType::Borrow(_) => {
                 Val::Resource(ResourceAny::load(cx, ty, bytes)?)
             }
@@ -881,79 +226,98 @@ impl Val {
                 load_list(cx, i, ptr, len)?
             }
 
-            InterfaceType::Record(i) => Val::Record(Record {
-                ty: types::Record::from(i, &cx.instance_type()),
-                values: load_record(cx, cx.types[i].fields.iter().map(|field| field.ty), bytes)?,
-            }),
-            InterfaceType::Tuple(i) => Val::Tuple(Tuple {
-                ty: types::Tuple::from(i, &cx.instance_type()),
-                values: load_record(cx, cx.types[i].types.iter().copied(), bytes)?,
-            }),
+            InterfaceType::Record(i) => {
+                let mut offset = 0;
+                let fields = cx.types[i].fields.iter();
+                Val::Record(
+                    fields
+                        .map(|field| -> Result<(String, Val)> {
+                            let abi = cx.types.canonical_abi(&field.ty);
+                            let offset = abi.next_field32(&mut offset);
+                            let offset = usize::try_from(offset).unwrap();
+                            let size = usize::try_from(abi.size32).unwrap();
+                            Ok((
+                                field.name.to_string(),
+                                Val::load(cx, field.ty, &bytes[offset..][..size])?,
+                            ))
+                        })
+                        .collect::<Result<_>>()?,
+                )
+            }
+            InterfaceType::Tuple(i) => {
+                let types = cx.types[i].types.iter().copied();
+                let mut offset = 0;
+                Val::Tuple(
+                    types
+                        .map(|ty| {
+                            let abi = cx.types.canonical_abi(&ty);
+                            let offset = abi.next_field32(&mut offset);
+                            let offset = usize::try_from(offset).unwrap();
+                            let size = usize::try_from(abi.size32).unwrap();
+                            Val::load(cx, ty, &bytes[offset..][..size])
+                        })
+                        .collect::<Result<_>>()?,
+                )
+            }
             InterfaceType::Variant(i) => {
                 let ty = &cx.types[i];
                 let (discriminant, value) =
-                    load_variant(cx, &ty.info, ty.cases.iter().map(|case| case.ty), bytes)?;
+                    load_variant(cx, &ty.info, ty.cases.values().copied(), bytes)?;
 
-                Val::Variant(Variant {
-                    ty: types::Variant::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
-                })
+                let (k, _) = ty.cases.get_index(discriminant as usize).unwrap();
+                Val::Variant(k.clone(), value)
             }
             InterfaceType::Enum(i) => {
                 let ty = &cx.types[i];
                 let (discriminant, _) =
                     load_variant(cx, &ty.info, ty.names.iter().map(|_| None), bytes)?;
 
-                Val::Enum(Enum {
-                    ty: types::Enum::from(i, &cx.instance_type()),
-                    discriminant,
-                })
+                Val::Enum(ty.names[discriminant as usize].clone())
             }
             InterfaceType::Option(i) => {
                 let ty = &cx.types[i];
-                let (discriminant, value) =
+                let (_discriminant, value) =
                     load_variant(cx, &ty.info, [None, Some(ty.ty)].into_iter(), bytes)?;
 
-                Val::Option(OptionVal {
-                    ty: types::OptionType::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
-                })
+                Val::Option(value)
             }
             InterfaceType::Result(i) => {
                 let ty = &cx.types[i];
                 let (discriminant, value) =
                     load_variant(cx, &ty.info, [ty.ok, ty.err].into_iter(), bytes)?;
 
-                Val::Result(ResultVal {
-                    ty: types::ResultType::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
+                Val::Result(if discriminant == 0 {
+                    Ok(value)
+                } else {
+                    Err(value)
                 })
             }
-            InterfaceType::Flags(i) => Val::Flags(Flags {
-                ty: types::Flags::from(i, &cx.instance_type()),
-                count: u32::try_from(cx.types[i].names.len())?,
-                value: match FlagsSize::from_count(cx.types[i].names.len()) {
-                    FlagsSize::Size0 => Box::new([]),
+            InterfaceType::Flags(i) => {
+                let ty = &cx.types[i];
+                let mut flags = Vec::new();
+                match FlagsSize::from_count(ty.names.len()) {
+                    FlagsSize::Size0 => {}
                     FlagsSize::Size1 => {
-                        iter::once(u8::load(cx, InterfaceType::U8, bytes)?.into()).collect()
+                        let bits = u8::load(cx, InterfaceType::U8, bytes)?;
+                        push_flags(ty, &mut flags, 0, u32::from(bits));
                     }
                     FlagsSize::Size2 => {
-                        iter::once(u16::load(cx, InterfaceType::U16, bytes)?.into()).collect()
+                        let bits = u16::load(cx, InterfaceType::U16, bytes)?;
+                        push_flags(ty, &mut flags, 0, u32::from(bits));
                     }
-                    FlagsSize::Size4Plus(n) => (0..n)
-                        .map(|index| {
-                            u32::load(
+                    FlagsSize::Size4Plus(n) => {
+                        for i in 0..n {
+                            let bits = u32::load(
                                 cx,
                                 InterfaceType::U32,
-                                &bytes[usize::from(index) * 4..][..4],
-                            )
-                        })
-                        .collect::<Result<_>>()?,
-                },
-            }),
+                                &bytes[usize::from(i) * 4..][..4],
+                            )?;
+                            push_flags(ty, &mut flags, u32::from(i) * 32, bits);
+                        }
+                    }
+                }
+                Val::Flags(flags.into())
+            }
         })
     }
 
@@ -964,68 +328,107 @@ impl Val {
         ty: InterfaceType,
         dst: &mut std::slice::IterMut<'_, MaybeUninit<ValRaw>>,
     ) -> Result<()> {
-        match self {
-            Val::Bool(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::S8(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::U8(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::S16(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::U16(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::S32(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::U32(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::S64(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::U64(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::Float32(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::Float64(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::Char(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::Resource(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::String(value) => {
+        match (ty, self) {
+            (InterfaceType::Bool, Val::Bool(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::Bool, _) => unexpected(ty, self),
+            (InterfaceType::S8, Val::S8(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::S8, _) => unexpected(ty, self),
+            (InterfaceType::U8, Val::U8(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::U8, _) => unexpected(ty, self),
+            (InterfaceType::S16, Val::S16(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::S16, _) => unexpected(ty, self),
+            (InterfaceType::U16, Val::U16(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::U16, _) => unexpected(ty, self),
+            (InterfaceType::S32, Val::S32(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::S32, _) => unexpected(ty, self),
+            (InterfaceType::U32, Val::U32(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::U32, _) => unexpected(ty, self),
+            (InterfaceType::S64, Val::S64(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::S64, _) => unexpected(ty, self),
+            (InterfaceType::U64, Val::U64(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::U64, _) => unexpected(ty, self),
+            (InterfaceType::Float32, Val::Float32(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::Float32, _) => unexpected(ty, self),
+            (InterfaceType::Float64, Val::Float64(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::Float64, _) => unexpected(ty, self),
+            (InterfaceType::Char, Val::Char(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::Char, _) => unexpected(ty, self),
+            // NB: `lower` on `ResourceAny` does its own type-checking, so skip
+            // looking at it here.
+            (InterfaceType::Borrow(_) | InterfaceType::Own(_), Val::Resource(value)) => {
+                value.lower(cx, ty, next_mut(dst))
+            }
+            (InterfaceType::Borrow(_) | InterfaceType::Own(_), _) => unexpected(ty, self),
+            (InterfaceType::String, Val::String(value)) => {
                 let my_dst = &mut MaybeUninit::<[ValRaw; 2]>::uninit();
                 value.lower(cx, ty, my_dst)?;
                 let my_dst = unsafe { my_dst.assume_init() };
                 next_mut(dst).write(my_dst[0]);
                 next_mut(dst).write(my_dst[1]);
+                Ok(())
             }
-            Val::List(List { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::List(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
+            (InterfaceType::String, _) => unexpected(ty, self),
+            (InterfaceType::List(ty), Val::List(values)) => {
+                let ty = &cx.types[ty];
                 let (ptr, len) = lower_list(cx, ty.element, values)?;
                 next_mut(dst).write(ValRaw::i64(ptr as i64));
                 next_mut(dst).write(ValRaw::i64(len as i64));
+                Ok(())
             }
-            Val::Record(Record { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::Record(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
-                for (value, field) in values.iter().zip(ty.fields.iter()) {
+            (InterfaceType::List(_), _) => unexpected(ty, self),
+            (InterfaceType::Record(ty), Val::Record(values)) => {
+                let ty = &cx.types[ty];
+                if ty.fields.len() != values.len() {
+                    bail!("expected {} fields, got {}", ty.fields.len(), values.len());
+                }
+                for ((name, value), field) in values.iter().zip(ty.fields.iter()) {
+                    if *name != field.name {
+                        bail!("expected field `{}`, got `{name}`", field.name);
+                    }
                     value.lower(cx, field.ty, dst)?;
                 }
+                Ok(())
             }
-            Val::Tuple(Tuple { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::Tuple(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
+            (InterfaceType::Record(_), _) => unexpected(ty, self),
+            (InterfaceType::Tuple(ty), Val::Tuple(values)) => {
+                let ty = &cx.types[ty];
+                if ty.types.len() != values.len() {
+                    bail!("expected {} types, got {}", ty.types.len(), values.len());
+                }
                 for (value, ty) in values.iter().zip(ty.types.iter()) {
                     value.lower(cx, *ty, dst)?;
                 }
-            }
-            Val::Variant(v) => v.as_generic(cx.types, ty).lower(cx, dst)?,
-            Val::Option(v) => v.as_generic(cx.types, ty).lower(cx, dst)?,
-            Val::Result(v) => v.as_generic(cx.types, ty).lower(cx, dst)?,
-            Val::Enum(Enum { discriminant, .. }) => {
-                next_mut(dst).write(ValRaw::u32(*discriminant));
-            }
-            Val::Flags(Flags { value, .. }) => {
-                for value in value.deref() {
-                    next_mut(dst).write(ValRaw::u32(*value));
+                Ok(())
+            }
+            (InterfaceType::Tuple(_), _) => unexpected(ty, self),
+            (InterfaceType::Variant(ty), Val::Variant(n, v)) => {
+                GenericVariant::variant(&cx.types[ty], n, v)?.lower(cx, dst)
+            }
+            (InterfaceType::Variant(_), _) => unexpected(ty, self),
+            (InterfaceType::Option(ty), Val::Option(v)) => {
+                GenericVariant::option(&cx.types[ty], v).lower(cx, dst)
+            }
+            (InterfaceType::Option(_), _) => unexpected(ty, self),
+            (InterfaceType::Result(ty), Val::Result(v)) => {
+                GenericVariant::result(&cx.types[ty], v)?.lower(cx, dst)
+            }
+            (InterfaceType::Result(_), _) => unexpected(ty, self),
+            (InterfaceType::Enum(ty), Val::Enum(discriminant)) => {
+                let discriminant = get_enum_discriminant(&cx.types[ty], discriminant)?;
+                next_mut(dst).write(ValRaw::u32(discriminant));
+                Ok(())
+            }
+            (InterfaceType::Enum(_), _) => unexpected(ty, self),
+            (InterfaceType::Flags(ty), Val::Flags(value)) => {
+                let ty = &cx.types[ty];
+                let storage = flags_to_storage(ty, value)?;
+                for value in storage {
+                    next_mut(dst).write(ValRaw::u32(value));
                 }
+                Ok(())
             }
+            (InterfaceType::Flags(_), _) => unexpected(ty, self),
         }
-
-        Ok(())
     }
 
     /// Serialize this value to the heap at the specified memory location.
@@ -1037,38 +440,58 @@ impl Val {
     ) -> Result<()> {
         debug_assert!(offset % usize::try_from(cx.types.canonical_abi(&ty).align32)? == 0);
 
-        match self {
-            Val::Bool(value) => value.store(cx, ty, offset)?,
-            Val::U8(value) => value.store(cx, ty, offset)?,
-            Val::S8(value) => value.store(cx, ty, offset)?,
-            Val::U16(value) => value.store(cx, ty, offset)?,
-            Val::S16(value) => value.store(cx, ty, offset)?,
-            Val::U32(value) => value.store(cx, ty, offset)?,
-            Val::S32(value) => value.store(cx, ty, offset)?,
-            Val::U64(value) => value.store(cx, ty, offset)?,
-            Val::S64(value) => value.store(cx, ty, offset)?,
-            Val::Float32(value) => value.store(cx, ty, offset)?,
-            Val::Float64(value) => value.store(cx, ty, offset)?,
-            Val::Char(value) => value.store(cx, ty, offset)?,
-            Val::String(value) => value.store(cx, ty, offset)?,
-            Val::Resource(value) => value.store(cx, ty, offset)?,
-            Val::List(List { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::List(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
+        match (ty, self) {
+            (InterfaceType::Bool, Val::Bool(value)) => value.store(cx, ty, offset),
+            (InterfaceType::Bool, _) => unexpected(ty, self),
+            (InterfaceType::U8, Val::U8(value)) => value.store(cx, ty, offset),
+            (InterfaceType::U8, _) => unexpected(ty, self),
+            (InterfaceType::S8, Val::S8(value)) => value.store(cx, ty, offset),
+            (InterfaceType::S8, _) => unexpected(ty, self),
+            (InterfaceType::U16, Val::U16(value)) => value.store(cx, ty, offset),
+            (InterfaceType::U16, _) => unexpected(ty, self),
+            (InterfaceType::S16, Val::S16(value)) => value.store(cx, ty, offset),
+            (InterfaceType::S16, _) => unexpected(ty, self),
+            (InterfaceType::U32, Val::U32(value)) => value.store(cx, ty, offset),
+            (InterfaceType::U32, _) => unexpected(ty, self),
+            (InterfaceType::S32, Val::S32(value)) => value.store(cx, ty, offset),
+            (InterfaceType::S32, _) => unexpected(ty, self),
+            (InterfaceType::U64, Val::U64(value)) => value.store(cx, ty, offset),
+            (InterfaceType::U64, _) => unexpected(ty, self),
+            (InterfaceType::S64, Val::S64(value)) => value.store(cx, ty, offset),
+            (InterfaceType::S64, _) => unexpected(ty, self),
+            (InterfaceType::Float32, Val::Float32(value)) => value.store(cx, ty, offset),
+            (InterfaceType::Float32, _) => unexpected(ty, self),
+            (InterfaceType::Float64, Val::Float64(value)) => value.store(cx, ty, offset),
+            (InterfaceType::Float64, _) => unexpected(ty, self),
+            (InterfaceType::Char, Val::Char(value)) => value.store(cx, ty, offset),
+            (InterfaceType::Char, _) => unexpected(ty, self),
+            (InterfaceType::String, Val::String(value)) => value.store(cx, ty, offset),
+            (InterfaceType::String, _) => unexpected(ty, self),
+
+            // NB: resources do type-checking when they lower.
+            (InterfaceType::Borrow(_) | InterfaceType::Own(_), Val::Resource(value)) => {
+                value.store(cx, ty, offset)
+            }
+            (InterfaceType::Borrow(_) | InterfaceType::Own(_), _) => unexpected(ty, self),
+            (InterfaceType::List(ty), Val::List(values)) => {
+                let ty = &cx.types[ty];
                 let (ptr, len) = lower_list(cx, ty.element, values)?;
                 // FIXME: needs memory64 handling
                 *cx.get(offset + 0) = (ptr as i32).to_le_bytes();
                 *cx.get(offset + 4) = (len as i32).to_le_bytes();
+                Ok(())
             }
-            Val::Record(Record { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::Record(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
+            (InterfaceType::List(_), _) => unexpected(ty, self),
+            (InterfaceType::Record(ty), Val::Record(values)) => {
+                let ty = &cx.types[ty];
+                if ty.fields.len() != values.len() {
+                    bail!("expected {} fields, got {}", ty.fields.len(), values.len());
+                }
                 let mut offset = offset;
-                for (value, field) in values.iter().zip(ty.fields.iter()) {
+                for ((name, value), field) in values.iter().zip(ty.fields.iter()) {
+                    if *name != field.name {
+                        bail!("expected field `{}`, got `{name}`", field.name);
+                    }
                     value.store(
                         cx,
                         field.ty,
@@ -1077,12 +500,14 @@ impl Val {
                             .next_field32_size(&mut offset),
                     )?;
                 }
+                Ok(())
             }
-            Val::Tuple(Tuple { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::Tuple(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
+            (InterfaceType::Record(_), _) => unexpected(ty, self),
+            (InterfaceType::Tuple(ty), Val::Tuple(values)) => {
+                let ty = &cx.types[ty];
+                if ty.types.len() != values.len() {
+                    bail!("expected {} types, got {}", ty.types.len(), values.len());
+                }
                 let mut offset = offset;
                 for (value, ty) in values.iter().zip(ty.types.iter()) {
                     value.store(
@@ -1091,38 +516,81 @@ impl Val {
                         cx.types.canonical_abi(ty).next_field32_size(&mut offset),
                     )?;
                 }
+                Ok(())
             }
+            (InterfaceType::Tuple(_), _) => unexpected(ty, self),
 
-            Val::Variant(v) => v.as_generic(cx.types, ty).store(cx, offset)?,
-            Val::Enum(v) => v.as_generic(cx.types, ty).store(cx, offset)?,
-            Val::Option(v) => v.as_generic(cx.types, ty).store(cx, offset)?,
-            Val::Result(v) => v.as_generic(cx.types, ty).store(cx, offset)?,
+            (InterfaceType::Variant(ty), Val::Variant(n, v)) => {
+                GenericVariant::variant(&cx.types[ty], n, v)?.store(cx, offset)
+            }
+            (InterfaceType::Variant(_), _) => unexpected(ty, self),
+            (InterfaceType::Enum(ty), Val::Enum(v)) => {
+                GenericVariant::enum_(&cx.types[ty], v)?.store(cx, offset)
+            }
+            (InterfaceType::Enum(_), _) => unexpected(ty, self),
+            (InterfaceType::Option(ty), Val::Option(v)) => {
+                GenericVariant::option(&cx.types[ty], v).store(cx, offset)
+            }
+            (InterfaceType::Option(_), _) => unexpected(ty, self),
+            (InterfaceType::Result(ty), Val::Result(v)) => {
+                GenericVariant::result(&cx.types[ty], v)?.store(cx, offset)
+            }
+            (InterfaceType::Result(_), _) => unexpected(ty, self),
 
-            Val::Flags(Flags { count, value, .. }) => {
-                match FlagsSize::from_count(*count as usize) {
+            (InterfaceType::Flags(ty), Val::Flags(flags)) => {
+                let ty = &cx.types[ty];
+                let storage = flags_to_storage(ty, flags)?;
+                match FlagsSize::from_count(ty.names.len()) {
                     FlagsSize::Size0 => {}
                     FlagsSize::Size1 => {
-                        u8::try_from(value[0])
+                        u8::try_from(storage[0])
                             .unwrap()
                             .store(cx, InterfaceType::U8, offset)?
                     }
                     FlagsSize::Size2 => {
-                        u16::try_from(value[0])
+                        u16::try_from(storage[0])
                             .unwrap()
                             .store(cx, InterfaceType::U16, offset)?
                     }
                     FlagsSize::Size4Plus(_) => {
                         let mut offset = offset;
-                        for value in value.deref() {
+                        for value in storage {
                             value.store(cx, InterfaceType::U32, offset)?;
                             offset += 4;
                         }
                     }
                 }
+                Ok(())
             }
+            (InterfaceType::Flags(_), _) => unexpected(ty, self),
         }
+    }
 
-        Ok(())
+    fn desc(&self) -> &'static str {
+        match self {
+            Val::Bool(_) => "bool",
+            Val::U8(_) => "u8",
+            Val::S8(_) => "s8",
+            Val::U16(_) => "u16",
+            Val::S16(_) => "s16",
+            Val::U32(_) => "u32",
+            Val::S32(_) => "s32",
+            Val::U64(_) => "u64",
+            Val::S64(_) => "s64",
+            Val::Float32(_) => "f32",
+            Val::Float64(_) => "f64",
+            Val::Char(_) => "char",
+            Val::List(_) => "list",
+            Val::String(_) => "string",
+            Val::Record(_) => "record",
+            Val::Enum(_) => "enum",
+            Val::Variant(..) => "variant",
+            Val::Tuple(_) => "tuple",
+            Val::Option(_) => "option",
+            Val::Result(_) => "result",
+            Val::Resource(_) => "resource",
+            Val::Flags(_) => "flags",
+        }
     }
 }
 
@@ -1175,8 +643,8 @@ impl PartialEq for Val {
             (Self::Record(_), _) => false,
             (Self::Tuple(l), Self::Tuple(r)) => l == r,
             (Self::Tuple(_), _) => false,
-            (Self::Variant(l), Self::Variant(r)) => l == r,
-            (Self::Variant(_), _) => false,
+            (Self::Variant(ln, lv), Self::Variant(rn, rv)) => ln == rn && lv == rv,
+            (Self::Variant(..), _) => false,
             (Self::Enum(l), Self::Enum(r)) => l == r,
             (Self::Enum(_), _) => false,
             (Self::Option(l), Self::Option(r)) => l == r,
@@ -1201,6 +669,92 @@ struct GenericVariant<'a> {
 }
 
 impl GenericVariant<'_> {
+    fn result<'a>(
+        ty: &'a TypeResult,
+        r: &'a Result<Option<Box<Val>>, Option<Box<Val>>>,
+    ) -> Result<GenericVariant<'a>> {
+        let (discriminant, payload) = match r {
+            Ok(val) => {
+                let payload = match (val, ty.ok) {
+                    (Some(val), Some(ty)) => Some((&**val, ty)),
+                    (None, None) => None,
+                    (Some(_), None) => {
+                        bail!("payload provided to `ok` but not expected");
+                    }
+                    (None, Some(_)) => {
+                        bail!("payload expected to `ok` but not provided");
+                    }
+                };
+                (0, payload)
+            }
+            Err(val) => {
+                let payload = match (val, ty.err) {
+                    (Some(val), Some(ty)) => Some((&**val, ty)),
+                    (None, None) => None,
+                    (Some(_), None) => {
+                        bail!("payload provided to `err` but not expected");
+                    }
+                    (None, Some(_)) => {
+                        bail!("payload expected to `err` but not provided");
+                    }
+                };
+                (1, payload)
+            }
+        };
+        Ok(GenericVariant {
+            discriminant,
+            payload,
+            abi: &ty.abi,
+            info: &ty.info,
+        })
+    }
+
+    fn option<'a>(ty: &'a TypeOption, r: &'a Option<Box<Val>>) -> GenericVariant<'a> {
+        let (discriminant, payload) = match r {
+            None => (0, None),
+            Some(val) => (1, Some((&**val, ty.ty))),
+        };
+        GenericVariant {
+            discriminant,
+            payload,
+            abi: &ty.abi,
+            info: &ty.info,
+        }
+    }
+
+    fn enum_<'a>(ty: &'a TypeEnum, discriminant: &str) -> Result<GenericVariant<'a>> {
+        let discriminant = get_enum_discriminant(ty, discriminant)?;
+
+        Ok(GenericVariant {
+            discriminant,
+            payload: None,
+            abi: &ty.abi,
+            info: &ty.info,
+        })
+    }
+
+    fn variant<'a>(
+        ty: &'a TypeVariant,
+        discriminant_name: &str,
+        payload: &'a Option<Box<Val>>,
+    ) -> Result<GenericVariant<'a>> {
+        let (discriminant, payload_ty) = get_variant_discriminant(ty, discriminant_name)?;
+
+        let payload = match (payload, payload_ty) {
+            (Some(val), Some(ty)) => Some((&**val, *ty)),
+            (None, None) => None,
+            (Some(_), None) => bail!("did not expect a payload for case `{discriminant_name}`"),
+            (None, Some(_)) => bail!("expected a payload for case `{discriminant_name}`"),
+        };
+
+        Ok(GenericVariant {
+            discriminant,
+            payload,
+            abi: &ty.abi,
+            info: &ty.info,
+        })
+    }
+
     fn lower<T>(
         &self,
         cx: &mut LowerContext<'_, T>,
@@ -1266,9 +820,8 @@ fn load_list(cx: &mut LiftContext<'_>, ty: TypeListIndex, ptr: usize, len: usize
         bail!("list pointer is not aligned")
     }
 
-    Ok(Val::List(List {
-        ty: types::List::from(ty, &cx.instance_type()),
-        values: (0..len)
+    Ok(Val::List(
+        (0..len)
             .map(|index| {
                 Val::load(
                     cx,
@@ -1277,24 +830,7 @@ fn load_list(cx: &mut LiftContext<'_>, ty: TypeListIndex, ptr: usize, len: usize
                 )
             })
             .collect::<Result<_>>()?,
-    }))
-}
-
-fn load_record(
-    cx: &mut LiftContext<'_>,
-    types: impl Iterator<Item = InterfaceType>,
-    bytes: &[u8],
-) -> Result<Box<[Val]>> {
-    let mut offset = 0;
-    types
-        .map(|ty| {
-            let abi = cx.types.canonical_abi(&ty);
-            let offset = abi.next_field32(&mut offset);
-            let offset = usize::try_from(offset).unwrap();
-            let size = usize::try_from(abi.size32).unwrap();
-            Val::load(cx, ty, &bytes[offset..][..size])
-        })
-        .collect()
+    ))
 }
 
 fn load_variant(
@@ -1377,6 +913,51 @@ fn lower_list<T>(
     Ok((ptr, items.len()))
 }
 
+fn push_flags(ty: &TypeFlags, flags: &mut Vec<String>, mut offset: u32, mut bits: u32) {
+    while bits > 0 {
+        if bits & 1 != 0 {
+            flags.push(ty.names[offset as usize].clone());
+        }
+        bits >>= 1;
+        offset += 1;
+    }
+}
+
+fn flags_to_storage(ty: &TypeFlags, flags: &[String]) -> Result<Vec<u32>> {
+    let mut storage = match FlagsSize::from_count(ty.names.len()) {
+        FlagsSize::Size0 => Vec::new(),
+        FlagsSize::Size1 | FlagsSize::Size2 => vec![0],
+        FlagsSize::Size4Plus(n) => vec![0; n.into()],
+    };
+
+    for flag in flags {
+        let bit = ty
+            .names
+            .get_index_of(flag)
+            .ok_or_else(|| anyhow::anyhow!("unknown flag: `{flag}`"))?;
+        storage[bit / 32] |= 1 << (bit % 32);
+    }
+    Ok(storage)
+}
+
+fn get_enum_discriminant(ty: &TypeEnum, n: &str) -> Result<u32> {
+    ty.names
+        .get_index_of(n)
+        .ok_or_else(|| anyhow::anyhow!("enum variant name `{n}` is not valid"))
+        .map(|i| i as u32)
+}
+
+fn get_variant_discriminant<'a>(
+    ty: &'a TypeVariant,
+    name: &str,
+) -> Result<(u32, &'a Option<InterfaceType>)> {
+    let (i, _, ty) = ty
+        .cases
+        .get_full(name)
+        .ok_or_else(|| anyhow::anyhow!("unknown variant case: `{name}`"))?;
+    Ok((i as u32, ty))
+}
+
 fn next<'a>(src: &mut std::slice::Iter<'a, ValRaw>) -> &'a ValRaw {
     src.next().unwrap()
 }
@@ -1386,3 +967,12 @@ fn next_mut<'a>(
 ) -> &'a mut MaybeUninit<ValRaw> {
     dst.next().unwrap()
 }
+
+#[cold]
+fn unexpected<T>(ty: InterfaceType, val: &Val) -> Result<T> {
+    bail!(
+        "type mismatch: expected {}, found {}",
+        desc(&ty),
+        val.desc()
+    )
+}
diff --git a/crates/wast/src/component.rs b/crates/wast/src/component.rs
index 2ce11c865..34c63260d 100644
--- a/crates/wast/src/component.rs
+++ b/crates/wast/src/component.rs
@@ -1,13 +1,13 @@
 use crate::core;
-use anyhow::{anyhow, bail, Context, Result};
-use std::collections::{BTreeSet, HashMap};
+use anyhow::{bail, Context, Result};
+use std::collections::BTreeSet;
 use std::fmt::Debug;
 use wast::component::WastVal;
 use wast::core::NanPattern;
 
 pub use wasmtime::component::*;
 
-pub fn val(v: &WastVal<'_>, ty: &Type) -> Result<Val> {
+pub fn val(v: &WastVal<'_>) -> Result<Val> {
     Ok(match v {
         WastVal::Bool(b) => Val::Bool(*b),
         WastVal::U8(b) => Val::U8(*b),
@@ -22,104 +22,44 @@ pub fn val(v: &WastVal<'_>, ty: &Type) -> Result<Val> {
         WastVal::Float64(b) => Val::Float64(f64::from_bits(b.bits)),
         WastVal::Char(b) => Val::Char(*b),
         WastVal::String(s) => Val::String(s.to_string().into()),
-        WastVal::List(vals) => match ty {
-            Type::List(t) => {
-                let element = t.ty();
-                let vals = vals
-                    .iter()
-                    .map(|v| val(v, &element))
-                    .collect::<Result<Vec<_>>>()?;
-                t.new_val(vals.into())?
-            }
-            _ => bail!("expected a list value"),
-        },
-        WastVal::Record(fields) => match ty {
-            Type::Record(t) => {
-                let mut fields_by_name = HashMap::new();
-                for (name, val) in fields {
-                    let prev = fields_by_name.insert(*name, val);
-                    if prev.is_some() {
-                        bail!("field `{name}` specified twice");
-                    }
-                }
-                let mut values = Vec::new();
-                for field in t.fields() {
-                    let name = field.name;
-                    let v = fields_by_name
-                        .remove(name)
-                        .ok_or_else(|| anyhow!("field `{name}` not specified"))?;
-                    values.push((name, val(v, &field.ty)?));
-                }
-                if let Some((field, _)) = fields_by_name.iter().next() {
-                    bail!("extra field `{field}` specified");
-                }
-                t.new_val(values)?
-            }
-            _ => bail!("expected a record value"),
-        },
-        WastVal::Tuple(vals) => match ty {
-            Type::Tuple(t) => {
-                if vals.len() != t.types().len() {
-                    bail!("expected {} values got {}", t.types().len(), vals.len());
-                }
-                t.new_val(
-                    vals.iter()
-                        .zip(t.types())
-                        .map(|(v, ty)| val(v, &ty))
-                        .collect::<Result<Vec<_>>>()?
-                        .into(),
-                )?
-            }
-            _ => bail!("expected a tuple value"),
-        },
-        WastVal::Enum(name) => match ty {
-            Type::Enum(t) => t.new_val(name)?,
-            _ => bail!("expected an enum value"),
-        },
-        WastVal::Variant(name, payload) => match ty {
-            Type::Variant(t) => {
-                let case = match t.cases().find(|c| c.name == *name) {
-                    Some(case) => case,
-                    None => bail!("no case named `{}", name),
-                };
-                let payload = payload_val(case.name, payload.as_deref(), case.ty.as_ref())?;
-                t.new_val(name, payload)?
-            }
-            _ => bail!("expected a variant value"),
-        },
-        WastVal::Option(v) => match ty {
-            Type::Option(t) => {
-                let v = match v {
-                    Some(v) => Some(val(v, &t.ty())?),
-                    None => None,
-                };
-                t.new_val(v)?
-            }
-            _ => bail!("expected an option value"),
-        },
-        WastVal::Result(v) => match ty {
-            Type::Result(t) => {
-                let v = match v {
-                    Ok(v) => Ok(payload_val("ok", v.as_deref(), t.ok().as_ref())?),
-                    Err(v) => Err(payload_val("err", v.as_deref(), t.err().as_ref())?),
-                };
-                t.new_val(v)?
+        WastVal::List(vals) => {
+            let vals = vals.iter().map(|v| val(v)).collect::<Result<Vec<_>>>()?;
+            Val::List(vals.into())
+        }
+        WastVal::Record(vals) => {
+            let mut fields = Vec::new();
+            for (name, v) in vals {
+                fields.push((name.to_string(), val(v)?));
             }
-            _ => bail!("expected an expected value"),
-        },
-        WastVal::Flags(v) => match ty {
-            Type::Flags(t) => t.new_val(v)?,
-            _ => bail!("expected a flags value"),
-        },
+            Val::Record(fields.into())
+        }
+        WastVal::Tuple(vals) => Val::Tuple(
+            vals.iter()
+                .map(|v| val(v))
+                .collect::<Result<Vec<_>>>()?
+                .into(),
+        ),
+        WastVal::Enum(name) => Val::Enum(name.to_string()),
+        WastVal::Variant(name, payload) => {
+            let payload = payload_val(payload.as_deref())?;
+            Val::Variant(name.to_string(), payload)
+        }
+        WastVal::Option(v) => Val::Option(match v {
+            Some(v) => Some(Box::new(val(v)?)),
+            None => None,
+        }),
+        WastVal::Result(v) => Val::Result(match v {
+            Ok(v) => Ok(payload_val(v.as_deref())?),
+            Err(v) => Err(payload_val(v.as_deref())?),
+        }),
+        WastVal::Flags(v) => Val::Flags(v.iter().map(|s| s.to_string()).collect()),
     })
 }
 
-fn payload_val(name: &str, v: Option<&WastVal<'_>>, ty: Option<&Type>) -> Result<Option<Val>> {
-    match (v, ty) {
-        (Some(v), Some(ty)) => Ok(Some(val(v, ty)?)),
-        (None, None) => Ok(None),
-        (Some(_), None) => bail!("expected payload for case `{name}`"),
-        (None, Some(_)) => bail!("unexpected payload for case `{name}`"),
+fn payload_val(v: Option<&WastVal<'_>>) -> Result<Option<Box<Val>>> {
+    match v {
+        Some(v) => Ok(Some(Box::new(val(v)?))),
+        None => Ok(None),
     }
 }
 
@@ -192,22 +132,15 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
         },
         WastVal::Record(e) => match actual {
             Val::Record(a) => {
-                let mut fields_by_name = HashMap::new();
-                for (name, val) in e {
-                    let prev = fields_by_name.insert(*name, val);
-                    if prev.is_some() {
-                        bail!("field `{name}` specified twice");
-                    }
-                }
-                for (name, actual) in a.fields() {
-                    let expected = fields_by_name
-                        .remove(name)
-                        .ok_or_else(|| anyhow!("field `{name}` not specified"))?;
-                    match_val(expected, actual)
-                        .with_context(|| format!("failed to match field `{name}`"))?;
+                if e.len() != e.len() {
+                    bail!("mismatched number of record fields");
                 }
-                if let Some((field, _)) = fields_by_name.iter().next() {
-                    bail!("extra field `{field}` specified");
+                for ((e_name, e_val), (a_name, a_val)) in e.iter().zip(a.iter()) {
+                    if e_name != a_name {
+                        bail!("expected field `{e_name}` got `{a_name}`");
+                    }
+                    match_val(e_val, a_val)
+                        .with_context(|| format!("failed to match field `{e_name}`"))?;
                 }
                 Ok(())
             }
@@ -215,14 +148,10 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
         },
         WastVal::Tuple(e) => match actual {
             Val::Tuple(a) => {
-                if e.len() != a.values().len() {
-                    bail!(
-                        "expected {}-tuple, found {}-tuple",
-                        e.len(),
-                        a.values().len()
-                    );
+                if e.len() != a.len() {
+                    bail!("expected {}-tuple, found {}-tuple", e.len(), a.len());
                 }
-                for (i, (expected, actual)) in e.iter().zip(a.values()).enumerate() {
+                for (i, (expected, actual)) in e.iter().zip(a.iter()).enumerate() {
                     match_val(expected, actual)
                         .with_context(|| format!("failed to match tuple element {i}"))?;
                 }
@@ -231,18 +160,18 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
             _ => mismatch(expected, actual),
         },
         WastVal::Variant(name, e) => match actual {
-            Val::Variant(a) => {
-                if a.discriminant() != *name {
-                    bail!("expected discriminant `{name}` got `{}`", a.discriminant());
+            Val::Variant(discr, payload) => {
+                if *discr != *name {
+                    bail!("expected discriminant `{name}` got `{discr}`");
                 }
-                match_payload_val(name, e.as_deref(), a.payload())
+                match_payload_val(name, e.as_deref(), payload.as_deref())
             }
             _ => mismatch(expected, actual),
         },
         WastVal::Enum(name) => match actual {
             Val::Enum(a) => {
-                if a.discriminant() != *name {
-                    bail!("expected discriminant `{name}` got `{}`", a.discriminant());
+                if *a != *name {
+                    bail!("expected discriminant `{name}` got `{a}`");
                 } else {
                     Ok(())
                 }
@@ -250,7 +179,7 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
             _ => mismatch(expected, actual),
         },
         WastVal::Option(e) => match actual {
-            Val::Option(a) => match (e, a.value()) {
+            Val::Option(a) => match (e, a) {
                 (None, None) => Ok(()),
                 (Some(expected), Some(actual)) => match_val(expected, actual),
                 (None, Some(_)) => bail!("expected `none`, found `some`"),
@@ -259,18 +188,18 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
             _ => mismatch(expected, actual),
         },
         WastVal::Result(e) => match actual {
-            Val::Result(a) => match (e, a.value()) {
+            Val::Result(a) => match (e, a) {
                 (Ok(_), Err(_)) => bail!("expected `ok`, found `err`"),
                 (Err(_), Ok(_)) => bail!("expected `err`, found `ok`"),
-                (Err(e), Err(a)) => match_payload_val("err", e.as_deref(), a),
-                (Ok(e), Ok(a)) => match_payload_val("ok", e.as_deref(), a),
+                (Err(e), Err(a)) => match_payload_val("err", e.as_deref(), a.as_deref()),
+                (Ok(e), Ok(a)) => match_payload_val("ok", e.as_deref(), a.as_deref()),
             },
             _ => mismatch(expected, actual),
         },
         WastVal::Flags(e) => match actual {
             Val::Flags(a) => {
                 let expected = e.iter().copied().collect::<BTreeSet<_>>();
-                let actual = a.flags().collect::<BTreeSet<_>>();
+                let actual = a.iter().map(|s| s.as_str()).collect::<BTreeSet<_>>();
                 match_debug(&actual, &expected)
             }
             _ => mismatch(expected, actual),
diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs
index 6763bd3f8..fcb2b8d47 100644
--- a/crates/wast/src/wast.rs
+++ b/crates/wast/src/wast.rs
@@ -186,16 +186,11 @@ where
             }
             #[cfg(feature = "component-model")]
             Export::Component(func) => {
-                let params = func.params(&self.store);
-                if exec.args.len() != params.len() {
-                    bail!("mismatched number of parameters")
-                }
                 let values = exec
                     .args
                     .iter()
-                    .zip(params.iter())
-                    .map(|(v, t)| match v {
-                        WastArg::Component(v) => component::val(v, t),
+                    .map(|v| match v {
+                        WastArg::Component(v) => component::val(v),
                         WastArg::Core(_) => bail!("expected core function, found component"),
                     })
                     .collect::<Result<Vec<_>>>()?;
diff --git a/examples/wasi-async/main.rs b/examples/wasi-async/main.rs
index 87661cd24..9fea4ce8a 100644
--- a/examples/wasi-async/main.rs
+++ b/examples/wasi-async/main.rs
@@ -46,7 +46,7 @@ async fn main() -> Result<()> {
     // Add the WASI preview1 API to the linker (will be implemented in terms of
     // the preview2 API)
     let mut linker: Linker<WasiHostCtx> = Linker::new(&engine);
-    wasmtime_wasi::preview1::add_to_linker_async(&mut linker)?;
+    wasmtime_wasi::preview1::add_to_linker_async(&mut linker, |t| t)?;
 
     // Add capabilities (e.g. filesystem access) to the WASI preview2 context here.
     let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new().inherit_stdio().build();
diff --git a/src/commands/run.rs b/src/commands/run.rs
index c0c04c787..8f25ba91e 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -628,9 +628,9 @@ impl RunCommand {
                         // default-disabled in the future.
                         (Some(true), _) | (None, Some(false) | None) => {
                             if self.run.common.wasi.preview0 != Some(false) {
-                                wasmtime_wasi::preview0::add_to_linker_sync(linker)?;
+                                wasmtime_wasi::preview0::add_to_linker_sync(linker, |t| t)?;
                             }
-                            wasmtime_wasi::preview1::add_to_linker_sync(linker)?;
+                            wasmtime_wasi::preview1::add_to_linker_sync(linker, |t| t)?;
                             self.set_preview2_ctx(store)?;
                         }
                     }
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index d3551da5f..5f6db2f7b 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -1695,6 +1695,12 @@ criteria = "safe-to-deploy"
 version = "0.3.3"
 notes = "I am the author of this crate."
 
+[[audits.mach2]]
+who = "Nick Fitzgerald <fitzgen@gmail.com>"
+criteria = "safe-to-deploy"
+delta = "0.4.1 -> 0.4.2"
+notes = "It does unsafe FFI bindings, as expected. I didn't check the FFI bindings against the C headers."
+
 [[audits.matchers]]
 who = "Pat Hickey <phickey@fastly.com>"
 criteria = "safe-to-deploy"
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 14d7058cb..3a2bea9a3 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -2453,6 +2453,12 @@ criteria = "safe-to-deploy"
 version = "0.4.17"
 aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
 
+[[audits.mozilla.audits.mach2]]
+who = "Gabriele Svelto <gsvelto@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "0.4.1"
+aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
+
 [[audits.mozilla.audits.memoffset]]
 who = "Gabriele Svelto <gsvelto@mozilla.com>"
 criteria = "safe-to-deploy"


Test patch:
diff --git a/crates/wasi/tests/all/preview1.rs b/crates/wasi/tests/all/preview1.rs
index 7dd8f2db5..f0d958fdf 100644
--- a/crates/wasi/tests/all/preview1.rs
+++ b/crates/wasi/tests/all/preview1.rs
@@ -8,10 +8,10 @@ async fn run(path: &str, inherit_stdio: bool) -> Result<()> {
     let path = Path::new(path);
     let name = path.file_stem().unwrap().to_str().unwrap();
     let mut config = Config::new();
-    config.async_support(true).wasm_component_model(true);
+    config.async_support(true);
     let engine = Engine::new(&config)?;
     let mut linker = Linker::new(&engine);
-    add_to_linker_async(&mut linker)?;
+    add_to_linker_async(&mut linker, |t| t)?;
 
     let module = Module::from_file(&engine, path)?;
     let (mut store, _td) = store(&engine, name, inherit_stdio)?;
diff --git a/tests/all/component_model/dynamic.rs b/tests/all/component_model/dynamic.rs
index f83f550d8..e01742a79 100644
--- a/tests/all/component_model/dynamic.rs
+++ b/tests/all/component_model/dynamic.rs
@@ -4,7 +4,7 @@ use super::{make_echo_component, make_echo_component_with_params, Param, Type};
 use anyhow::Result;
 use component_test_util::FuncExt;
 use wasmtime::component::types::{self, Case, ComponentItem, Field};
-use wasmtime::component::{self, Component, Linker, ResourceType, Val};
+use wasmtime::component::{Component, Linker, ResourceType, Val};
 use wasmtime::{Module, Store};
 use wasmtime_component_util::REALLOC_AND_FREE;
 
@@ -102,7 +102,7 @@ fn strings() -> Result<()> {
     let component = Component::new(&engine, make_echo_component("string", 8))?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let input = Val::String(Box::from("hello, component!"));
+    let input = Val::String("hello, component!".into());
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
     assert_eq!(input, output[0]);
@@ -118,12 +118,11 @@ fn lists() -> Result<()> {
     let component = Component::new(&engine, make_echo_component("(list u32)", 8))?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let input = ty.unwrap_list().new_val(Box::new([
+    let input = Val::List(vec![
         Val::U32(32343),
         Val::U32(79023439),
         Val::U32(2084037802),
-    ]))?;
+    ]);
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
@@ -131,15 +130,14 @@ fn lists() -> Result<()> {
 
     // Sad path: type mismatch
 
-    let err = ty
-        .unwrap_list()
-        .new_val(Box::new([
-            Val::U32(32343),
-            Val::U32(79023439),
-            Val::Float32(3.14159265),
-        ]))
+    let err = Val::List(vec![
+        Val::U32(32343),
+        Val::U32(79023439),
+        Val::Float32(3.14159265),
+    ]);
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
-
     assert!(err.to_string().contains("type mismatch"), "{err}");
 
     Ok(())
@@ -175,18 +173,17 @@ fn records() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let inner_type = &ty.unwrap_record().fields().nth(2).unwrap().ty;
-    let input = ty.unwrap_record().new_val([
-        ("A", Val::U32(32343)),
-        ("B", Val::Float64(3.14159265)),
+    let input = Val::Record(vec![
+        ("A".into(), Val::U32(32343)),
+        ("B".into(), Val::Float64(3.14159265)),
         (
-            "C",
-            inner_type
-                .unwrap_record()
-                .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
+            "C".into(),
+            Val::Record(vec![
+                ("D".into(), Val::Bool(false)),
+                ("E".into(), Val::U32(2084037802)),
+            ]),
         ),
-    ])?;
+    ]);
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
@@ -194,53 +191,61 @@ fn records() -> Result<()> {
 
     // Sad path: type mismatch
 
-    let err = ty
-        .unwrap_record()
-        .new_val([
-            ("A", Val::S32(32343)),
-            ("B", Val::Float64(3.14159265)),
-            (
-                "C",
-                inner_type
-                    .unwrap_record()
-                    .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
-            ),
-        ])
+    let err = Val::Record(vec![
+        ("A".into(), Val::S32(32343)),
+        ("B".into(), Val::Float64(3.14159265)),
+        (
+            "C".into(),
+            Val::Record(vec![
+                ("D".into(), Val::Bool(false)),
+                ("E".into(), Val::U32(2084037802)),
+            ]),
+        ),
+    ]);
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
-
     assert!(err.to_string().contains("type mismatch"), "{err}");
 
     // Sad path: too many fields
 
-    let err = ty
-        .unwrap_record()
-        .new_val([
-            ("A", Val::U32(32343)),
-            ("B", Val::Float64(3.14159265)),
-            (
-                "C",
-                inner_type
-                    .unwrap_record()
-                    .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
-            ),
-            ("F", Val::Bool(true)),
-        ])
+    let err = Val::Record(vec![
+        ("A".into(), Val::U32(32343)),
+        ("B".into(), Val::Float64(3.14159265)),
+        (
+            "C".into(),
+            Val::Record(vec![
+                ("D".into(), Val::Bool(false)),
+                ("E".into(), Val::U32(2084037802)),
+            ]),
+        ),
+        ("F".into(), Val::Bool(true)),
+    ]);
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
-
     assert!(
-        err.to_string().contains("expected 3 value(s); got 4"),
+        err.to_string().contains("expected 3 fields, got 4"),
         "{err}"
     );
 
     // Sad path: too few fields
 
-    let err = ty
-        .unwrap_record()
-        .new_val([("A", Val::U32(32343)), ("B", Val::Float64(3.14159265))])
+    let err = Val::Record(vec![
+        ("A".into(), Val::U32(32343)),
+        ("B".into(), Val::Float64(3.14159265)),
+    ]);
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
-
     assert!(
-        err.to_string().contains("expected 3 value(s); got 2"),
+        err.to_string().contains("expected 3 fields, got 2"),
         "{err}"
     );
 
@@ -275,10 +280,7 @@ fn variants() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let input = ty
-        .unwrap_variant()
-        .new_val("B", Some(Val::Float64(3.14159265)))?;
+    let input = Val::Variant("B".into(), Some(Box::new(Val::Float64(3.14159265))));
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
@@ -299,28 +301,33 @@ fn variants() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let c_type = &ty.unwrap_variant().cases().nth(2).unwrap().ty.unwrap();
-    let input = ty.unwrap_variant().new_val(
-        "C",
-        Some(
-            c_type
-                .unwrap_record()
-                .new_val([("D", Val::Bool(true)), ("E", Val::U32(314159265))])?,
-        ),
-    )?;
+    let input = Val::Variant(
+        "C".into(),
+        Some(Box::new(Val::Record(vec![
+            ("D".into(), Val::Bool(true)),
+            ("E".into(), Val::U32(314159265)),
+        ]))),
+    );
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
     assert_eq!(input, output[0]);
 
     // Sad path: type mismatch
 
-    let err = ty
-        .unwrap_variant()
-        .new_val("B", Some(Val::U64(314159265)))
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = Val::Variant("B".into(), Some(Box::new(Val::U64(314159265))));
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
     assert!(err.to_string().contains("type mismatch"), "{err}");
-    let err = ty.unwrap_variant().new_val("B", None).unwrap_err();
+
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = Val::Variant("B".into(), None);
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
+        .unwrap_err();
     assert!(
         err.to_string().contains("expected a payload for case `B`"),
         "{err}"
@@ -328,12 +335,20 @@ fn variants() -> Result<()> {
 
     // Sad path: unknown case
 
-    let err = ty
-        .unwrap_variant()
-        .new_val("D", Some(Val::U64(314159265)))
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = Val::Variant("D".into(), Some(Box::new(Val::U64(314159265))));
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
     assert!(err.to_string().contains("unknown variant case"), "{err}");
-    let err = ty.unwrap_variant().new_val("D", None).unwrap_err();
+
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = Val::Variant("D".into(), None);
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
+        .unwrap_err();
     assert!(err.to_string().contains("unknown variant case"), "{err}");
 
     // Make sure we lift variants which have cases of different sizes with the correct alignment
@@ -365,17 +380,13 @@ fn variants() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let a_type = &ty.unwrap_record().fields().nth(0).unwrap().ty;
-    let input = ty.unwrap_record().new_val([
+    let input = Val::Record(vec![
         (
-            "A",
-            a_type
-                .unwrap_variant()
-                .new_val("A", Some(Val::U32(314159265)))?,
+            "A".into(),
+            Val::Variant("A".into(), Some(Box::new(Val::U32(314159265)))),
         ),
-        ("B", Val::U32(628318530)),
-    ])?;
+        ("B".into(), Val::U32(628318530)),
+    ]);
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
     assert_eq!(input, output[0]);
@@ -397,8 +408,7 @@ fn flags() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let input = ty.unwrap_flags().new_val(&["B", "D"])?;
+    let input = Val::Flags(vec!["B".into(), "D".into()]);
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
@@ -406,8 +416,10 @@ fn flags() -> Result<()> {
 
     // Sad path: unknown flags
 
-    let err = ty.unwrap_flags().new_val(&["B", "D", "F"]).unwrap_err();
-
+    let err = Val::Flags(vec!["B".into(), "D".into(), "F".into()]);
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
+        .unwrap_err();
     assert!(err.to_string().contains("unknown flag"), "{err}");
 
     Ok(())
@@ -490,62 +502,42 @@ fn everything() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let types = ty
-        .unwrap_record()
-        .fields()
-        .map(|field| field.ty)
-        .collect::<Box<[component::Type]>>();
-    let (b_type, c_type, f_type, j_type, y_type, aa_type, bb_type) = (
-        &types[1], &types[2], &types[3], &types[4], &types[13], &types[14], &types[15],
-    );
-    let f_element_type = &f_type.unwrap_list().ty();
-    let input = ty.unwrap_record().new_val([
-        ("A", Val::U32(32343)),
-        ("B", b_type.unwrap_enum().new_val("b")?),
-        (
-            "C",
-            c_type
-                .unwrap_record()
-                .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
-        ),
+    let input = Val::Record(vec![
+        ("A".into(), Val::U32(32343)),
+        ("B".into(), Val::Enum("b".to_string())),
         (
-            "F",
-            f_type.unwrap_list().new_val(Box::new([f_element_type
-                .unwrap_flags()
-                .new_val(&["G", "I"])?]))?,
+            "C".into(),
+            Val::Record(vec![
+                ("D".to_string(), Val::Bool(false)),
+                ("E".to_string(), Val::U32(2084037802)),
+            ]),
         ),
         (
-            "J",
-            j_type
-                .unwrap_variant()
-                .new_val("L", Some(Val::Float64(3.14159265)))?,
+            "F".into(),
+            Val::List(vec![Val::Flags(vec!["G".to_string(), "I".to_string()])]),
         ),
-        ("P", Val::S8(42)),
-        ("Q", Val::S16(4242)),
-        ("R", Val::S32(42424242)),
-        ("S", Val::S64(424242424242424242)),
-        ("T", Val::Float32(3.14159265)),
-        ("U", Val::Float64(3.14159265)),
-        ("V", Val::String(Box::from("wow, nice types"))),
-        ("W", Val::Char('🦀')),
         (
-            "Y",
-            y_type
-                .unwrap_tuple()
-                .new_val(Box::new([Val::U32(42), Val::U32(24)]))?,
+            "J".into(),
+            Val::Variant("L".to_string(), Some(Box::new(Val::Float64(3.14159265)))),
         ),
+        ("P".into(), Val::S8(42)),
+        ("Q".into(), Val::S16(4242)),
+        ("R".into(), Val::S32(42424242)),
+        ("S".into(), Val::S64(424242424242424242)),
+        ("T".into(), Val::Float32(3.14159265)),
+        ("U".into(), Val::Float64(3.14159265)),
+        ("V".into(), Val::String("wow, nice types".to_string())),
+        ("W".into(), Val::Char('🦀')),
+        ("Y".into(), Val::Tuple(vec![Val::U32(42), Val::U32(24)])),
         (
-            "AA",
-            aa_type.unwrap_option().new_val(Some(Val::U32(314159265)))?,
+            "AA".into(),
+            Val::Option(Some(Box::new(Val::U32(314159265)))),
         ),
         (
-            "BB",
-            bb_type
-                .unwrap_result()
-                .new_val(Ok(Some(Val::String(Box::from("no problem")))))?,
+            "BB".into(),
+            Val::Result(Ok(Some(Box::new(Val::String("no problem".to_string()))))),
         ),
-    ])?;
+    ]);
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs
index 9f8ae09f0..3b453457f 100644
--- a/tests/all/component_model/import.rs
+++ b/tests/all/component_model/import.rs
@@ -706,7 +706,7 @@ fn stack_and_heap_args_and_rets() -> Result<()> {
         .root()
         .func_new(&component, "f2", |_, args, results| {
             if let Val::Tuple(tuple) = &args[0] {
-                if let Val::String(s) = &tuple.values()[0] {
+                if let Val::String(s) = &tuple[0] {
                     assert_eq!(s.deref(), "abc");
                     results[0] = Val::U32(3);
                     Ok(())
@@ -732,7 +732,7 @@ fn stack_and_heap_args_and_rets() -> Result<()> {
         .root()
         .func_new(&component, "f4", |_, args, results| {
             if let Val::Tuple(tuple) = &args[0] {
-                if let Val::String(s) = &tuple.values()[0] {
+                if let Val::String(s) = &tuple[0] {
                     assert_eq!(s.deref(), "abc");
                     results[0] = Val::String("xyz".into());
                     Ok(())
diff --git a/tests/all/component_model/resources.rs b/tests/all/component_model/resources.rs
index bd5fa9d20..343cd4c21 100644
--- a/tests/all/component_model/resources.rs
+++ b/tests/all/component_model/resources.rs
@@ -584,7 +584,7 @@ fn dynamic_val() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    let idx = linker
+    linker
         .root()
         .resource("t1", ResourceType::host::<MyType>(), |_, _| Ok(()))?;
     let i_pre = linker.instantiate_pre(&c)?;
@@ -614,14 +614,14 @@ fn dynamic_val() -> Result<()> {
             assert_eq!(resource.rep(), 100);
             assert!(resource.owned());
 
-            let resource = resource.try_into_resource_any(&mut store, &i_pre, idx)?;
+            let resource = resource.try_into_resource_any(&mut store)?;
             assert_eq!(resource.ty(), ResourceType::host::<MyType>());
             assert!(resource.owned());
         }
         _ => unreachable!(),
     }
 
-    let t1_any = Resource::<MyType>::new_own(100).try_into_resource_any(&mut store, &i_pre, idx)?;
+    let t1_any = Resource::<MyType>::new_own(100).try_into_resource_any(&mut store)?;
     let mut results = [Val::Bool(false)];
     a.call(&mut store, &[Val::Resource(t1_any)], &mut results)?;
     a.post_return(&mut store)?;
@@ -634,7 +634,7 @@ fn dynamic_val() -> Result<()> {
             assert_eq!(resource.rep(), 100);
             assert!(resource.owned());
 
-            let resource = resource.try_into_resource_any(&mut store, &i_pre, idx)?;
+            let resource = resource.try_into_resource_any(&mut store)?;
             assert_eq!(resource.ty(), ResourceType::host::<MyType>());
             assert!(resource.owned());
         }
@@ -642,7 +642,7 @@ fn dynamic_val() -> Result<()> {
     }
 
     let t1 = Resource::<MyType>::new_own(100)
-        .try_into_resource_any(&mut store, &i_pre, idx)?
+        .try_into_resource_any(&mut store)?
         .try_into_resource(&mut store)?;
     let (t1,) = a_typed_result.call(&mut store, (t1,))?;
     a_typed_result.post_return(&mut store)?;
@@ -650,9 +650,9 @@ fn dynamic_val() -> Result<()> {
     assert!(t1.owned());
 
     let t1_any = t1
-        .try_into_resource_any(&mut store, &i_pre, idx)?
+        .try_into_resource_any(&mut store)?
         .try_into_resource::<MyType>(&mut store)?
-        .try_into_resource_any(&mut store, &i_pre, idx)?;
+        .try_into_resource_any(&mut store)?;
     let mut results = [Val::Bool(false)];
     a.call(&mut store, &[Val::Resource(t1_any)], &mut results)?;
     a.post_return(&mut store)?;
@@ -665,7 +665,7 @@ fn dynamic_val() -> Result<()> {
             assert_eq!(resource.rep(), 100);
             assert!(resource.owned());
 
-            let resource = resource.try_into_resource_any(&mut store, &i_pre, idx)?;
+            let resource = resource.try_into_resource_any(&mut store)?;
             assert_eq!(resource.ty(), ResourceType::host::<MyType>());
             assert!(resource.owned());
         }
@@ -915,7 +915,7 @@ fn can_use_own_for_borrow() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    let ty_idx = linker
+    linker
         .root()
         .resource("t", ResourceType::host::<MyType>(), |_, _| Ok(()))?;
     let i_pre = linker.instantiate_pre(&c)?;
@@ -932,8 +932,7 @@ fn can_use_own_for_borrow() -> Result<()> {
     f_typed.call(&mut store, (&resource,))?;
     f_typed.post_return(&mut store)?;
 
-    let resource =
-        Resource::<MyType>::new_own(300).try_into_resource_any(&mut store, &i_pre, ty_idx)?;
+    let resource = Resource::<MyType>::new_own(300).try_into_resource_any(&mut store)?;
     f.call(&mut store, &[Val::Resource(resource)], &mut [])?;
     f.post_return(&mut store)?;
     resource.resource_drop(&mut store)?;
diff --git a/tests/all/host_funcs.rs b/tests/all/host_funcs.rs
index bf8f4b6de..9814b6140 100644
--- a/tests/all/host_funcs.rs
+++ b/tests/all/host_funcs.rs
@@ -1,7 +1,5 @@
 use anyhow::bail;
 use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
-use wasi_common::sync::WasiCtxBuilder;
-use wasi_common::I32Exit;
 use wasmtime::*;
 
 #[test]
@@ -717,7 +715,7 @@ fn store_with_context() -> Result<()> {
 fn wasi_imports() -> Result<()> {
     let engine = Engine::default();
     let mut linker = Linker::new(&engine);
-    wasi_common::sync::add_to_linker(&mut linker, |s| s)?;
+    wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |t| t)?;
 
     let wasm = wat::parse_str(
         r#"
@@ -730,14 +728,14 @@ fn wasi_imports() -> Result<()> {
     )?;
 
     let module = Module::new(&engine, wasm)?;
-    let mut store = Store::new(&engine, WasiCtxBuilder::new().build());
+    let mut store = Store::new(&engine, wasmtime_wasi::WasiCtxBuilder::new().build_p1());
     let instance = linker.instantiate(&mut store, &module)?;
 
     let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
     let exit = start
         .call(&mut store, ())
         .unwrap_err()
-        .downcast::<I32Exit>()?;
+        .downcast::<wasmtime_wasi::I32Exit>()?;
     assert_eq!(exit.0, 123);
 
     Ok(())
diff --git a/tests/all/traps.rs b/tests/all/traps.rs
index affb2ac7c..6679704e0 100644
--- a/tests/all/traps.rs
+++ b/tests/all/traps.rs
@@ -751,13 +751,8 @@ fn parse_dwarf_info() -> Result<()> {
     let engine = Engine::new(&config)?;
     let module = Module::new(&engine, &wasm)?;
     let mut linker = Linker::new(&engine);
-    wasi_common::sync::add_to_linker(&mut linker, |s| s)?;
-    let mut store = Store::new(
-        &engine,
-        wasi_common::sync::WasiCtxBuilder::new()
-            .inherit_stdio()
-            .build(),
-    );
+    wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |t| t)?;
+    let mut store = Store::new(&engine, wasmtime_wasi::WasiCtxBuilder::new().build_p1());
     linker.module(&mut store, "", &module)?;
     let run = linker.get_default(&mut store, "")?;
     let trap = run.call(&mut store, &[], &mut []).unwrap_err();