Repository: bytecodealliance/wasmtime
Pull request #8062 (2024-03-07 23:11:32)

Remove type information from `wasmtime::component::Val`
This commit is a large refactor of the `Val` type as used with components to remove inherent type information present currently. The `Val` type is now only an AST of what a component model value looks like and cannot fully describe the type that it is without further context. For example enums only store the case that's being used, not the full set of cases.

The motivation for this commit is to make it simpler to use and construct `Val`, especially in the face of resources. Some problems solved here are:

* With resources in play managing type information is not trivial and can often be surprising. For example if you learn the type of a function from a component and the instantiate the component twice the type information is not suitable to use with either function due to exported resources acquiring unique types on all instantiations.

* Functionally it's much easier to construct values when type information is not required as it no longer requires probing various pieces for type information here and there.

* API-wise there's far less for us to maintain as there's no need for a type-per-variant of component model types. Pieces now fit much more naturally into a `Val` shape without extra types.

* Functionally when working with `Val` there's now only one typecheck instead of two. Previously a typecheck was performed first when a `Val` was created and then again later when it was passed to wasm. Now the typecheck only happens when passed to wasm.

It's worth pointing out that `Val` as-is is a pretty inefficient representation of component model values, for example flags are stored as a list of strings. While semantically correct this is quite inefficient for most purposes other than "get something working". To that extent my goal is to, in the future, add traits that enable building a custom user-defined `Val` (of sorts), but still dynamically. This should enable embedders to opt-in to a more efficient representation that relies on contextual knowledge.

<!--
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
-->



Patch:
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/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/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/mod.rs b/crates/wasmtime/src/runtime/component/mod.rs
index f3e40cb8e..65ee5cf63 100644
--- a/crates/wasmtime/src/runtime/component/mod.rs
+++ b/crates/wasmtime/src/runtime/component/mod.rs
@@ -61,7 +61,7 @@ pub use self::linker::{Linker, LinkerInstance, ResourceImportIndex};
 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/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)?;
                         }
                     }


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/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();