Repository: bytecodealliance/wasmtime
Pull request #8074 (2024-03-09 01:32:09)

Add release notes for 19.0.0
<!--
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/.github/workflows/main.yml b/.github/workflows/main.yml
index e97968826..dd165d671 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -212,7 +212,7 @@ jobs:
         submodules: true
     - uses: ./.github/actions/install-rust
       with:
-        toolchain: nightly-2024-02-22
+        toolchain: nightly-2024-03-11
 
     # Build C API documentation
     - run: curl -L https://sourceforge.net/projects/doxygen/files/rel-1.9.3/doxygen-1.9.3.linux.bin.tar.gz/download | tar xzf -
@@ -397,7 +397,7 @@ jobs:
     # happen upstream. This is periodically updated through a PR.
     - uses: ./.github/actions/install-rust
       with:
-        toolchain: nightly-2024-02-22
+        toolchain: nightly-2024-03-11
 
     # Ensure that fuzzers sitll build.
     #
@@ -723,7 +723,7 @@ jobs:
         submodules: true
     - uses: ./.github/actions/install-rust
       with:
-        toolchain: nightly-2024-02-22
+        toolchain: nightly-2024-03-11
     - run: rustup component add rust-src miri
     - uses: actions/cache@v4
       with:
diff --git a/Cargo.lock b/Cargo.lock
index ff529f58a..1af5217a0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4201,29 +4201,36 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen"
-version = "0.20.0"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39e6058f3ef320faece2d48ab105a278e94bac47df03b04af69c205173e0afcc"
+checksum = "288f992ea30e6b5c531b52cdd5f3be81c148554b09ea416f058d16556ba92c27"
 dependencies = [
  "bitflags 2.4.1",
+ "wit-bindgen-rt",
  "wit-bindgen-rust-macro",
 ]
 
 [[package]]
 name = "wit-bindgen-core"
-version = "0.20.0"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11a8abb3fed5dee9be41aef6609fe2bc4a9d10f82af24fe9b843e885d51ef638"
+checksum = "e85e72719ffbccf279359ad071497e47eb0675fe22106dea4ed2d8a7fcb60ba4"
 dependencies = [
  "anyhow",
  "wit-parser",
 ]
 
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb8738270f32a2d6739973cbbb7c1b6dd8959ce515578a6e19165853272ee64"
+
 [[package]]
 name = "wit-bindgen-rust"
-version = "0.20.0"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7c63da73f9dc6b68c6aea864b3e277ddb056ec49bd3e41b2b76bc2165d769a9"
+checksum = "d8a39a15d1ae2077688213611209849cad40e9e5cccf6e61951a425850677ff3"
 dependencies = [
  "anyhow",
  "heck",
@@ -4235,9 +4242,9 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen-rust-macro"
-version = "0.20.0"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2adddfe43ab23342715b15225a32b29010f8af91ad9cab7a51a46c5006aea08e"
+checksum = "d376d3ae5850526dfd00d937faea0d81a06fa18f7ac1e26f386d760f241a8f4b"
 dependencies = [
  "anyhow",
  "proc-macro2",
diff --git a/Cargo.toml b/Cargo.toml
index 9a8dcd502..c7431f823 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -135,7 +135,7 @@ authors = ["The Wasmtime Project Developers"]
 edition = "2021"
 # Wasmtime's current policy is that this number can be no larger than the
 # current stable release of Rust minus 2.
-rust-version = "1.73.0"
+rust-version = "1.74.0"
 
 [workspace.lints.rust]
 # Turn on some lints which are otherwise allow-by-default in rustc.
@@ -228,7 +228,7 @@ io-lifetimes = { version = "2.0.3", default-features = false }
 io-extras = "0.18.1"
 rustix = "0.38.31"
 # wit-bindgen:
-wit-bindgen = { version = "0.20.0", default-features = false }
+wit-bindgen = { version = "0.22.0", default-features = false }
 
 # wasm-tools family:
 wasmparser = "0.201.0"
diff --git a/RELEASES.md b/RELEASES.md
index 93af3bd3f..30e58bfdc 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -16,8 +16,98 @@ Unreleased.
 
 ### Added
 
+* C API bindings for the sampling-based profiler in Wasmtime have been added.
+  [#7854](https://github.com/bytecodealliance/wasmtime/pull/7854)
+
+* Add the WasiP1Ctx to ease the use of `wasmtime-wasi` in place of `wasi-common`
+  [#8053](https://github.com/bytecodealliance/wasmtime/pull/8053)
+
+* The Winch compiler backend is now feature-complete for x64. Note that minor
+  issues and fuzz bugs are still being addressed, but now's a good time to test
+  if you're interested.
+  [#7894](https://github.com/bytecodealliance/wasmtime/pull/7894)
+  [#7909](https://github.com/bytecodealliance/wasmtime/pull/7909)
+  [#7927](https://github.com/bytecodealliance/wasmtime/pull/7927)
+  [#7932](https://github.com/bytecodealliance/wasmtime/pull/7932)
+  [#7949](https://github.com/bytecodealliance/wasmtime/pull/7949)
+  [#7974](https://github.com/bytecodealliance/wasmtime/pull/7974)
+  [#8001](https://github.com/bytecodealliance/wasmtime/pull/8001)
+
+* The typed function references proposal to WebAssembly is now fully
+  implemented.
+  [#7943](https://github.com/bytecodealliance/wasmtime/pull/7943)
+
+* The `component::Linker` type is now "semver aware" in that it enables loading
+  components referring to past or future versions of interfaces so long as the
+  types are compatible.
+  [#7994](https://github.com/bytecodealliance/wasmtime/pull/7994)
+
+* Wasmtime can now be built for "custom platforms" which is intended for
+  bare-metal builds.
+  [#7995](https://github.com/bytecodealliance/wasmtime/pull/7995)
+
+* The `wasmtime-wasi-nn` crate now has a backend based on WinML.
+  [#7807](https://github.com/bytecodealliance/wasmtime/pull/7807)
+
+* The `wasmtime` CLI now has flags for configuring limits of the pooling
+  allocator.
+  [#8027](https://github.com/bytecodealliance/wasmtime/pull/8027)
+
+
 ### Changed
 
+* The `wasmtime serve` command no longer binds its port with `SO_REUSEADDR`
+  meaning that if it is invoked twice only one will succeed.
+  [#7863](https://github.com/bytecodealliance/wasmtime/pull/7863)
+
+* The sampling-based profiler in Wasmtime now takes an explicit argument of
+  the time delta between samples.
+  [#7873](https://github.com/bytecodealliance/wasmtime/pull/7873)
+
+* Many accessors for type information now require an `&Engine` argument to be
+  specified in preparation for an implementation of wasm GC.
+  [#7892](https://github.com/bytecodealliance/wasmtime/pull/7892)
+
+* The `host` header is now forbidden in wasi-http.
+  [#7905](https://github.com/bytecodealliance/wasmtime/pull/7905)
+
+* Stronger type-checks are now performed for host-owned resources when
+  interacting with the component model to help catch mistakes earlier.
+  [#7902](https://github.com/bytecodealliance/wasmtime/pull/7902)
+
+* Demangling Rust and C/C++ symbols in WebAssembly modules is now done by
+  default in the C API.
+  [#7962](https://github.com/bytecodealliance/wasmtime/pull/7962)
+
+* Preview2-based APIs are now located at the root of the `wasmtime_wasi` crate
+  instead of a submodule.
+  [#7933](https://github.com/bytecodealliance/wasmtime/pull/7933)
+
+* Components now reserve index 0 for handle tables to match the component model
+  specification.
+  [#7661](https://github.com/bytecodealliance/wasmtime/pull/7661)
+
+* Support for `externref` has been moved behind a new `gc` Cargo feature. This
+  will also gate support for wasm gc in the future.
+  [#7975](https://github.com/bytecodealliance/wasmtime/pull/7975)
+
+* Require the `WASMTIME_WASI_CONFIG_PREOPEN_SOCKET_ALLOW` environment variable
+  to bet set to allow the use of `wasi_config_preopen_socket` via the c api, as
+  it will be deprecated in `20.0.0`.
+  [#8053](https://github.com/bytecodealliance/wasmtime/pull/8053)
+
+### Fixed
+
+* WIT interface names that are Rust keywords now correctly generate bindings.
+  [#7790](https://github.com/bytecodealliance/wasmtime/pull/7790)
+
+* PKRU state is now restored across await points.
+  [#7789](https://github.com/bytecodealliance/wasmtime/pull/7789)
+
+* Wasmtime now correctly supports `global.get` in all constant expressions
+  within a module.
+  [#7996](https://github.com/bytecodealliance/wasmtime/pull/7996)
+
 --------------------------------------------------------------------------------
 
 ## 18.0.2
diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs
index dcd7e947b..440840c4d 100644
--- a/cranelift/codegen/src/isa/aarch64/abi.rs
+++ b/cranelift/codegen/src/isa/aarch64/abi.rs
@@ -110,7 +110,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
     where
         I: IntoIterator<Item = &'a ir::AbiParam>,
     {
-        if call_conv == isa::CallConv::Tail {
+        if matches!(call_conv, isa::CallConv::Tail | isa::CallConv::Winch) {
             return compute_arg_locs_tail(params, add_ret_area_ptr, args);
         }
 
diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
index d6027c074..af7e42fc5 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
@@ -867,12 +867,18 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
             collector.reg_clobbers(info.clobbers);
         }
         &Inst::CallInd { ref info, .. } => {
-            if info.callee_callconv == CallConv::Tail {
-                // TODO(https://github.com/bytecodealliance/regalloc2/issues/145):
-                // This shouldn't be a fixed register constraint.
-                collector.reg_fixed_use(info.rn, xreg(1));
-            } else {
-                collector.reg_use(info.rn);
+            match info.callee_callconv {
+                CallConv::Tail => {
+                    // TODO(https://github.com/bytecodealliance/regalloc2/issues/145):
+                    // This shouldn't be a fixed register constraint.
+                    collector.reg_fixed_use(info.rn, xreg(1))
+                }
+                CallConv::Winch => {
+                    // TODO(https://github.com/bytecodealliance/regalloc2/issues/145):
+                    // This shouldn't be a fixed register constraint.
+                    collector.reg_fixed_use(info.rn, xreg(1))
+                }
+                _ => collector.reg_use(info.rn),
             }
             for u in &info.uses {
                 collector.reg_fixed_use(u.vreg, u.preg);
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index 3ebb6ece0..c970888d7 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -2370,6 +2370,11 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
                     // This shouldn't be a fixed register constraint.
                     collector.reg_fixed_use(*reg, regs::r15())
                 }
+                RegMem::Reg { reg } if info.callee_conv == CallConv::Winch => {
+                    // TODO(https://github.com/bytecodealliance/regalloc2/issues/145):
+                    // This shouldn't be a fixed register constraint.
+                    collector.reg_fixed_use(*reg, regs::r15())
+                }
                 _ => dest.get_operands(collector),
             }
             for u in &info.uses {
diff --git a/cranelift/codegen/src/settings.rs b/cranelift/codegen/src/settings.rs
index 9c5220336..26ae6bc3e 100644
--- a/cranelift/codegen/src/settings.rs
+++ b/cranelift/codegen/src/settings.rs
@@ -231,21 +231,10 @@ fn parse_bool_value(value: &str) -> SetResult<bool> {
 fn parse_enum_value(value: &str, choices: &[&str]) -> SetResult<u8> {
     match choices.iter().position(|&tag| tag == value) {
         Some(idx) => Ok(idx as u8),
-        None => {
-            // TODO: Use `join` instead of this code, once
-            // https://github.com/rust-lang/rust/issues/27747 is resolved.
-            let mut all_choices = String::new();
-            let mut first = true;
-            for choice in choices {
-                if first {
-                    first = false
-                } else {
-                    all_choices += ", ";
-                }
-                all_choices += choice;
-            }
-            Err(SetError::BadValue(format!("any among {}", all_choices)))
-        }
+        None => Err(SetError::BadValue(format!(
+            "any among {}",
+            choices.join(", ")
+        ))),
     }
 }
 
diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs
index 489b5707e..eecf00cf9 100644
--- a/crates/environ/src/component/types.rs
+++ b/crates/environ/src/component/types.rs
@@ -1007,7 +1007,7 @@ pub enum TypeDef {
     ComponentInstance(TypeComponentInstanceIndex),
     /// A component function, not to be confused with a core wasm function.
     ComponentFunc(TypeFuncIndex),
-    /// An interface type.
+    /// An type in an interface.
     Interface(InterfaceType),
     /// A core wasm module and its type.
     Module(TypeModuleIndex),
@@ -1020,6 +1020,21 @@ pub enum TypeDef {
     Resource(TypeResourceTableIndex),
 }
 
+impl TypeDef {
+    /// A human readable description of what kind of type definition this is.
+    pub fn desc(&self) -> &str {
+        match self {
+            TypeDef::Component(_) => "component",
+            TypeDef::ComponentInstance(_) => "instance",
+            TypeDef::ComponentFunc(_) => "function",
+            TypeDef::Interface(_) => "type",
+            TypeDef::Module(_) => "core module",
+            TypeDef::CoreFunc(_) => "core function",
+            TypeDef::Resource(_) => "resource",
+        }
+    }
+}
+
 // NB: Note that maps below are stored as an `IndexMap` now but the order
 // typically does not matter. As a minor implementation detail we want the
 // serialization of this type to always be deterministic and using `IndexMap`
diff --git a/crates/wasmtime/src/runtime/component/component.rs b/crates/wasmtime/src/runtime/component/component.rs
index af242d85d..25444ef6a 100644
--- a/crates/wasmtime/src/runtime/component/component.rs
+++ b/crates/wasmtime/src/runtime/component/component.rs
@@ -1,3 +1,5 @@
+use crate::component::matching::InstanceType;
+use crate::component::types;
 use crate::{
     code::CodeObject, code_memory::CodeMemory, instantiate::MmapVecWrapper,
     type_registry::TypeCollection, Engine, Module, ResourcesRequired,
@@ -284,6 +286,141 @@ impl Component {
         Component::from_parts(engine, code, None)
     }
 
+    /// Returns the type of this component as a [`types::Component`].
+    ///
+    /// This method enables runtime introspection of the type of a component
+    /// before instantiation, if necessary.
+    ///
+    /// ## Component types and Resources
+    ///
+    /// An important point to note here is that the precise type of imports and
+    /// exports of a component change when it is instantiated with respect to
+    /// resources. For example a [`Component`] represents an un-instantiated
+    /// component meaning that its imported resources are represeted as abstract
+    /// resource types. These abstract types are not equal to any other
+    /// component's types.
+    ///
+    /// For example:
+    ///
+    /// ```
+    /// # use wasmtime::Engine;
+    /// # use wasmtime::component::Component;
+    /// # use wasmtime::component::types::ComponentItem;
+    /// # fn main() -> wasmtime::Result<()> {
+    /// # let engine = Engine::default();
+    /// let a = Component::new(&engine, r#"
+    ///     (component (import "x" (type (sub resource))))
+    /// "#)?;
+    /// let b = Component::new(&engine, r#"
+    ///     (component (import "x" (type (sub resource))))
+    /// "#)?;
+    ///
+    /// let (_, a_ty) = a.component_type().imports(&engine).next().unwrap();
+    /// let (_, b_ty) = b.component_type().imports(&engine).next().unwrap();
+    ///
+    /// let a_ty = match a_ty {
+    ///     ComponentItem::Resource(ty) => ty,
+    ///     _ => unreachable!(),
+    /// };
+    /// let b_ty = match b_ty {
+    ///     ComponentItem::Resource(ty) => ty,
+    ///     _ => unreachable!(),
+    /// };
+    /// assert!(a_ty != b_ty);
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// Additionally, however, these abstract types are "substituted" during
+    /// instantiation meaning that a component type will appear to have changed
+    /// once it is instantiated.
+    ///
+    /// ```
+    /// # use wasmtime::{Engine, Store};
+    /// # use wasmtime::component::{Component, Linker, ResourceType};
+    /// # use wasmtime::component::types::ComponentItem;
+    /// # fn main() -> wasmtime::Result<()> {
+    /// # let engine = Engine::default();
+    /// // Here this component imports a resource and then exports it as-is
+    /// // which means that the export is equal to the import.
+    /// let a = Component::new(&engine, r#"
+    ///     (component
+    ///         (import "x" (type $x (sub resource)))
+    ///         (export "x" (type $x))
+    ///     )
+    /// "#)?;
+    ///
+    /// let (_, import) = a.component_type().imports(&engine).next().unwrap();
+    /// let (_, export) = a.component_type().exports(&engine).next().unwrap();
+    ///
+    /// let import = match import {
+    ///     ComponentItem::Resource(ty) => ty,
+    ///     _ => unreachable!(),
+    /// };
+    /// let export = match export {
+    ///     ComponentItem::Resource(ty) => ty,
+    ///     _ => unreachable!(),
+    /// };
+    /// assert_eq!(import, export);
+    ///
+    /// // However after instantiation the resource type "changes"
+    /// let mut store = Store::new(&engine, ());
+    /// let mut linker = Linker::new(&engine);
+    /// linker.root().resource("x", ResourceType::host::<()>(), |_, _| Ok(()))?;
+    /// let instance = linker.instantiate(&mut store, &a)?;
+    /// let instance_ty = instance.exports(&mut store).root().resource("x").unwrap();
+    ///
+    /// // Here `instance_ty` is not the same as either `import` or `export`,
+    /// // but it is equal to what we provided as an import.
+    /// assert!(instance_ty != import);
+    /// assert!(instance_ty != export);
+    /// assert!(instance_ty == ResourceType::host::<()>());
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// Finally, each instantiation of an exported resource from a component is
+    /// considered "fresh" for all instantiations meaning that different
+    /// instantiations will have different exported resource types:
+    ///
+    /// ```
+    /// # use wasmtime::{Engine, Store};
+    /// # use wasmtime::component::{Component, Linker};
+    /// # fn main() -> wasmtime::Result<()> {
+    /// # let engine = Engine::default();
+    /// let a = Component::new(&engine, r#"
+    ///     (component
+    ///         (type $x (resource (rep i32)))
+    ///         (export "x" (type $x))
+    ///     )
+    /// "#)?;
+    ///
+    /// let mut store = Store::new(&engine, ());
+    /// let linker = Linker::new(&engine);
+    /// let instance1 = linker.instantiate(&mut store, &a)?;
+    /// let instance2 = linker.instantiate(&mut store, &a)?;
+    ///
+    /// let x1 = instance1.exports(&mut store).root().resource("x").unwrap();
+    /// let x2 = instance2.exports(&mut store).root().resource("x").unwrap();
+    ///
+    /// // Despite these two resources being the same export of the same
+    /// // component they come from two different instances meaning that their
+    /// // types will be unique.
+    /// assert!(x1 != x2);
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn component_type(&self) -> types::Component {
+        let resources = Arc::new(PrimaryMap::new());
+        types::Component::from(
+            self.inner.ty,
+            &InstanceType {
+                types: self.types(),
+                resources: &resources,
+            },
+        )
+    }
+
     /// Final assembly step for a component from its in-memory representation.
     ///
     /// If the `artifacts` are specified as `None` here then they will be
diff --git a/crates/wasmtime/src/runtime/component/linker.rs b/crates/wasmtime/src/runtime/component/linker.rs
index 00bcb0a52..5c3847082 100644
--- a/crates/wasmtime/src/runtime/component/linker.rs
+++ b/crates/wasmtime/src/runtime/component/linker.rs
@@ -208,7 +208,7 @@ impl<T> Linker<T> {
         for (_idx, (name, ty)) in env_component.import_types.iter() {
             let import = self.map.get(name, &self.strings);
             cx.definition(ty, import)
-                .with_context(|| format!("import `{name}` has the wrong type"))?;
+                .with_context(|| format!("component imports {desc} `{name}`, but a matching implementation was not found in the linker", desc = ty.desc()))?;
         }
         Ok(cx)
     }
diff --git a/crates/wasmtime/src/runtime/component/matching.rs b/crates/wasmtime/src/runtime/component/matching.rs
index 7c47d98c9..8fb2c02c7 100644
--- a/crates/wasmtime/src/runtime/component/matching.rs
+++ b/crates/wasmtime/src/runtime/component/matching.rs
@@ -36,19 +36,27 @@ impl TypeChecker<'_> {
         match *expected {
             TypeDef::Module(t) => match actual {
                 Some(Definition::Module(actual)) => self.module(&self.types[t], actual),
-                _ => bail!("expected module found {}", desc(actual)),
+                Some(actual) => bail!("expected module found {}", actual.desc()),
+                None => bail!("module implementation is missing"),
             },
             TypeDef::ComponentInstance(t) => match actual {
                 Some(Definition::Instance(actual)) => self.instance(&self.types[t], Some(actual)),
                 None => self.instance(&self.types[t], None),
-                _ => bail!("expected instance found {}", desc(actual)),
+                Some(actual) => bail!("expected instance found {}", actual.desc()),
             },
             TypeDef::ComponentFunc(t) => match actual {
                 Some(Definition::Func(actual)) => self.func(t, actual),
-                _ => bail!("expected func found {}", desc(actual)),
+                Some(actual) => bail!("expected function found {}", actual.desc()),
+                None => bail!("function implementation is missing"),
+            },
+            TypeDef::Component(_) => match actual {
+                Some(actual) => bail!("expected component found {}", actual.desc()),
+                None => bail!("component implementation is missing"),
+            },
+            TypeDef::Interface(_) => match actual {
+                Some(actual) => bail!("expected type found {}", actual.desc()),
+                None => bail!("type implementation is missing"),
             },
-            TypeDef::Component(_) => bail!("expected component found {}", desc(actual)),
-            TypeDef::Interface(_) => bail!("expected type found {}", desc(actual)),
 
             TypeDef::Resource(i) => {
                 let i = self.types[i].ty;
@@ -63,7 +71,8 @@ impl TypeChecker<'_> {
                     // Wasmtime API.
                     None if self.imported_resources.get(i).is_some() => return Ok(()),
 
-                    _ => bail!("expected resource found {}", desc(actual)),
+                    Some(actual) => bail!("expected resource found {}", actual.desc()),
+                    None => bail!("resource implementation is missing"),
                 };
 
                 match self.imported_resources.get(i) {
@@ -165,13 +174,6 @@ impl TypeChecker<'_> {
     }
 }
 
-fn desc(def: Option<&Definition>) -> &'static str {
-    match def {
-        Some(def) => def.desc(),
-        None => "nothing",
-    }
-}
-
 impl Definition {
     fn desc(&self) -> &'static str {
         match self {
@@ -193,7 +195,10 @@ impl<'a> InstanceType<'a> {
 
     pub fn resource_type(&self, index: TypeResourceTableIndex) -> ResourceType {
         let index = self.types[index].ty;
-        self.resources[index]
+        self.resources
+            .get(index)
+            .copied()
+            .unwrap_or_else(|| ResourceType::uninstantiated(&self.types, index))
     }
 }
 
diff --git a/docs/cli-options.md b/docs/cli-options.md
index d6ffe74ad..6cf534380 100644
--- a/docs/cli-options.md
+++ b/docs/cli-options.md
@@ -52,6 +52,29 @@ an exported function of the module to run.
 $ wasmtime run foo.wasm --invoke initialize
 ```
 
+## `serve`
+
+The `serve` subcommand runs a WebAssembly component in the `wasi:http/proxy`
+world via the WASI HTTP API, which is available since Wasmtime 18.0.0. The goal
+of this world is to support sending and receiving HTTP requests.
+
+The `serve` command takes one positional argument which is the name of the
+component to run:
+
+```sh
+$ wasmtime serve foo.wasm
+```
+
+Furthermore, an address can be specified via:
+
+```sh
+$ wasmtime serve --addr=0.0.0.0:8081 foo.wasm
+```
+
+At the time of writing, the `wasi:http/proxy` world is still experimental and
+requires setup of some `wit` dependencies. For more information, see
+the [hello-wasi-http](https://github.com/sunfishcode/hello-wasi-http/) example.
+
 ## `wast`
 
 The `wast` command executes a `*.wast` file which is the test format for the
diff --git a/src/commands/serve.rs b/src/commands/serve.rs
index 85e2a40c5..e59f91839 100644
--- a/src/commands/serve.rs
+++ b/src/commands/serve.rs
@@ -233,6 +233,22 @@ impl ServeCommand {
         use hyper::server::conn::http1;
 
         let mut config = self.run.common.config(None)?;
+        match self.run.common.opts.pooling_allocator {
+            // If explicitly enabled on the CLI then the pooling allocator was
+            // already configured in the `config` method above. If the allocator
+            // is explicitly disabled, then we don't want it. In both cases do
+            // nothing.
+            Some(true) | Some(false) => {}
+
+            // Otherwise though if not explicitly specified then always enable
+            // the pooling allocator. The `wasmtime serve` use case is
+            // tailor-made for pooling allocation and there's no downside to
+            // enabling it.
+            None => {
+                let cfg = wasmtime::PoolingAllocationConfig::default();
+                config.allocation_strategy(wasmtime::InstanceAllocationStrategy::Pooling(cfg));
+            }
+        }
         config.wasm_component_model(true);
         config.async_support(true);
 
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 5f6db2f7b..f37438171 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -3739,6 +3739,12 @@ user-id = 73222 # wasmtime-publish
 start = "2024-02-23"
 end = "2025-02-23"
 
+[[trusted.wit-bindgen-rt]]
+criteria = "safe-to-deploy"
+user-id = 73222 # wasmtime-publish
+start = "2024-03-04"
+end = "2025-03-11"
+
 [[trusted.wit-bindgen-rust]]
 criteria = "safe-to-deploy"
 user-id = 73222 # wasmtime-publish
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 3a2bea9a3..622fa6e93 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -1886,6 +1886,12 @@ when = "2024-02-29"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen]]
+version = "0.22.0"
+when = "2024-03-11"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-core]]
 version = "0.17.0"
 when = "2024-02-05"
@@ -1911,6 +1917,18 @@ when = "2024-02-29"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-core]]
+version = "0.22.0"
+when = "2024-03-11"
+user-id = 73222
+user-login = "wasmtime-publish"
+
+[[publisher.wit-bindgen-rt]]
+version = "0.22.0"
+when = "2024-03-11"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-rust]]
 version = "0.17.0"
 when = "2024-02-05"
@@ -1936,6 +1954,12 @@ when = "2024-02-29"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-rust]]
+version = "0.22.0"
+when = "2024-03-11"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-rust-macro]]
 version = "0.17.0"
 when = "2024-02-05"
@@ -1961,6 +1985,12 @@ when = "2024-02-29"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-rust-macro]]
+version = "0.22.0"
+when = "2024-03-11"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-component]]
 version = "0.20.1"
 when = "2024-01-29"


Test patch:
diff --git a/cranelift/filetests/filetests/isa/aarch64/winch.clif b/cranelift/filetests/filetests/isa/aarch64/winch.clif
index a872dab3e..4168e3a7d 100644
--- a/cranelift/filetests/filetests/isa/aarch64/winch.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/winch.clif
@@ -28,10 +28,10 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   mov fp, sp
 ;   sub sp, sp, #16
 ; block0:
-;   str x0, [sp]
-;   load_ext_name x7, TestCase(%g)+0
-;   blr x7
-;   ldr x0, [sp]
+;   str x2, [sp]
+;   load_ext_name x1, TestCase(%g)+0
+;   blr x1
+;   ldr x2, [sp]
 ;   add sp, sp, #16
 ;   ldp fp, lr, [sp], #16
 ;   ret
@@ -42,13 +42,13 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   mov x29, sp
 ;   sub sp, sp, #0x10
 ; block1: ; offset 0xc
-;   stur x0, [sp]
-;   ldr x7, #0x18
+;   stur x2, [sp]
+;   ldr x1, #0x18
 ;   b #0x20
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   blr x7
-;   ldur x0, [sp]
+;   blr x1
+;   ldur x2, [sp]
 ;   add sp, sp, #0x10
 ;   ldp x29, x30, [sp], #0x10
 ;   ret
@@ -77,8 +77,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   sub sp, sp, #16
 ; block0:
 ;   str x0, [sp]
-;   load_ext_name x7, TestCase(%g)+0
-;   blr x7
+;   load_ext_name x1, TestCase(%g)+0
+;   blr x1
 ;   ldr x0, [sp]
 ;   add sp, sp, #16
 ;   ldp d8, d9, [sp], #16
@@ -109,11 +109,11 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   sub sp, sp, #0x10
 ; block1: ; offset 0x30
 ;   stur x0, [sp]
-;   ldr x7, #0x3c
+;   ldr x1, #0x3c
 ;   b #0x44
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   blr x7
+;   blr x1
 ;   ldur x0, [sp]
 ;   add sp, sp, #0x10
 ;   ldp d8, d9, [sp], #0x10
@@ -141,11 +141,11 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
 ; block0:
-;   mov x6, x0
-;   mov x0, x5
-;   mov x5, x6
-;   load_ext_name x8, TestCase(%g)+0
-;   blr x8
+;   mov x0, x2
+;   mov x2, x7
+;   mov x7, x0
+;   load_ext_name x1, TestCase(%g)+0
+;   blr x1
 ;   ldp fp, lr, [sp], #16
 ;   ret
 ;
@@ -154,14 +154,14 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
 ; block1: ; offset 0x8
-;   mov x6, x0
-;   mov x0, x5
-;   mov x5, x6
-;   ldr x8, #0x1c
+;   mov x0, x2
+;   mov x2, x7
+;   mov x7, x0
+;   ldr x1, #0x1c
 ;   b #0x24
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   blr x8
+;   blr x1
 ;   ldp x29, x30, [sp], #0x10
 ;   ret
 
@@ -187,11 +187,15 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   stp d10, d11, [sp, #-16]!
 ;   stp d8, d9, [sp, #-16]!
 ; block0:
-;   mov x6, x0
-;   mov x0, x5
-;   mov x5, x6
-;   load_ext_name x8, TestCase(%g)+0
-;   blr x8
+;   mov x7, x0
+;   mov x6, x4
+;   mov x4, x2
+;   mov x2, x5
+;   mov x5, x3
+;   mov x3, x1
+;   load_ext_name x1, TestCase(%g)+0
+;   blr x1
+;   mov x0, x2
 ;   ldp d8, d9, [sp], #16
 ;   ldp d10, d11, [sp], #16
 ;   ldp d12, d13, [sp], #16
@@ -218,14 +222,99 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   stp d10, d11, [sp, #-0x10]!
 ;   stp d8, d9, [sp, #-0x10]!
 ; block1: ; offset 0x2c
-;   mov x6, x0
-;   mov x0, x5
-;   mov x5, x6
-;   ldr x8, #0x40
-;   b #0x48
+;   mov x7, x0
+;   mov x6, x4
+;   mov x4, x2
+;   mov x2, x5
+;   mov x5, x3
+;   mov x3, x1
+;   ldr x1, #0x4c
+;   b #0x54
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   blr x8
+;   blr x1
+;   mov x0, x2
+;   ldp d8, d9, [sp], #0x10
+;   ldp d10, d11, [sp], #0x10
+;   ldp d12, d13, [sp], #0x10
+;   ldp d14, d15, [sp], #0x10
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
+;   ldp x29, x30, [sp], #0x10
+;   ret
+
+function u1:0() system_v {
+    sig0 = () winch
+    fn0 = u2:0 sig0
+
+block0:
+    v5 = func_addr.i64 fn0
+    call_indirect sig0, v5()
+    call_indirect sig0, v5()
+    return
+}
+
+; VCode:
+;   stp fp, lr, [sp, #-16]!
+;   mov fp, sp
+;   stp x27, x28, [sp, #-16]!
+;   stp x25, x26, [sp, #-16]!
+;   stp x23, x24, [sp, #-16]!
+;   stp x21, x22, [sp, #-16]!
+;   stp x19, x20, [sp, #-16]!
+;   stp d14, d15, [sp, #-16]!
+;   stp d12, d13, [sp, #-16]!
+;   stp d10, d11, [sp, #-16]!
+;   stp d8, d9, [sp, #-16]!
+;   sub sp, sp, #16
+; block0:
+;   load_ext_name x1, User(userextname0)+0
+;   str x1, [sp]
+;   ldr x1, [sp]
+;   blr x1
+;   ldr x1, [sp]
+;   blr x1
+;   add sp, sp, #16
+;   ldp d8, d9, [sp], #16
+;   ldp d10, d11, [sp], #16
+;   ldp d12, d13, [sp], #16
+;   ldp d14, d15, [sp], #16
+;   ldp x19, x20, [sp], #16
+;   ldp x21, x22, [sp], #16
+;   ldp x23, x24, [sp], #16
+;   ldp x25, x26, [sp], #16
+;   ldp x27, x28, [sp], #16
+;   ldp fp, lr, [sp], #16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   stp x29, x30, [sp, #-0x10]!
+;   mov x29, sp
+;   stp x27, x28, [sp, #-0x10]!
+;   stp x25, x26, [sp, #-0x10]!
+;   stp x23, x24, [sp, #-0x10]!
+;   stp x21, x22, [sp, #-0x10]!
+;   stp x19, x20, [sp, #-0x10]!
+;   stp d14, d15, [sp, #-0x10]!
+;   stp d12, d13, [sp, #-0x10]!
+;   stp d10, d11, [sp, #-0x10]!
+;   stp d8, d9, [sp, #-0x10]!
+;   sub sp, sp, #0x10
+; block1: ; offset 0x30
+;   ldr x1, #0x38
+;   b #0x40
+;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 u2:0 0
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   stur x1, [sp]
+;   ldur x1, [sp]
+;   blr x1
+;   ldur x1, [sp]
+;   blr x1
+;   add sp, sp, #0x10
 ;   ldp d8, d9, [sp], #0x10
 ;   ldp d10, d11, [sp], #0x10
 ;   ldp d12, d13, [sp], #0x10
diff --git a/cranelift/filetests/filetests/isa/x64/winch.clif b/cranelift/filetests/filetests/isa/x64/winch.clif
index 876d6c90f..e1845d9f9 100644
--- a/cranelift/filetests/filetests/isa/x64/winch.clif
+++ b/cranelift/filetests/filetests/isa/x64/winch.clif
@@ -38,8 +38,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   subq    %rsp, $16, %rsp
 ; block0:
 ;   movq    %rdi, rsp(0 + virtual offset)
-;   load_ext_name %g+0, %r10
-;   call    *%r10
+;   load_ext_name %g+0, %r15
+;   call    *%r15
 ;   movq    rsp(0 + virtual offset), %rax
 ;   addq    %rsp, $16, %rsp
 ;   movq    %rbp, %rsp
@@ -53,8 +53,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   subq $0x10, %rsp
 ; block1: ; offset 0x8
 ;   movq %rdi, (%rsp)
-;   movabsq $0, %r10 ; reloc_external Abs8 %g 0
-;   callq *%r10
+;   movabsq $0, %r15 ; reloc_external Abs8 %g 0
+;   callq *%r15
 ;   movq (%rsp), %rax
 ;   addq $0x10, %rsp
 ;   movq %rbp, %rsp
@@ -81,8 +81,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq    %r15, 48(%rsp)
 ; block0:
 ;   movq    %rdi, rsp(0 + virtual offset)
-;   load_ext_name %g+0, %r10
-;   call    *%r10
+;   load_ext_name %g+0, %r15
+;   call    *%r15
 ;   movq    rsp(0 + virtual offset), %rax
 ;   movq    16(%rsp), %rbx
 ;   movq    24(%rsp), %r12
@@ -106,8 +106,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq %r15, 0x30(%rsp)
 ; block1: ; offset 0x21
 ;   movq %rdi, (%rsp)
-;   movabsq $0, %r10 ; reloc_external Abs8 %g 0
-;   callq *%r10
+;   movabsq $0, %r15 ; reloc_external Abs8 %g 0
+;   callq *%r15
 ;   movq (%rsp), %rax
 ;   movq 0x10(%rsp), %rbx
 ;   movq 0x18(%rsp), %r12
@@ -135,8 +135,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq    %rdi, %rax
 ;   movq    %r9, %rdi
 ;   movq    %rax, %r9
-;   load_ext_name %g+0, %r11
-;   call    *%r11
+;   load_ext_name %g+0, %r15
+;   call    *%r15
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -149,8 +149,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq %rdi, %rax
 ;   movq %r9, %rdi
 ;   movq %rax, %r9
-;   movabsq $0, %r11 ; reloc_external Abs8 %g 0
-;   callq *%r11
+;   movabsq $0, %r15 ; reloc_external Abs8 %g 0
+;   callq *%r15
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
@@ -177,8 +177,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq    %rdi, %rax
 ;   movq    %r9, %rdi
 ;   movq    %rax, %r9
-;   load_ext_name %g+0, %r11
-;   call    *%r11
+;   load_ext_name %g+0, %r15
+;   call    *%r15
 ;   movq    0(%rsp), %rbx
 ;   movq    8(%rsp), %r12
 ;   movq    16(%rsp), %r13
@@ -203,8 +203,8 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   movq %rdi, %rax
 ;   movq %r9, %rdi
 ;   movq %rax, %r9
-;   movabsq $0, %r11 ; reloc_external Abs8 %g 0
-;   callq *%r11
+;   movabsq $0, %r15 ; reloc_external Abs8 %g 0
+;   callq *%r15
 ;   movq (%rsp), %rbx
 ;   movq 8(%rsp), %r12
 ;   movq 0x10(%rsp), %r13
@@ -215,3 +215,67 @@ block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
 ;   popq %rbp
 ;   retq
 
+function u1:0() system_v {
+    sig0 = () winch
+    fn0 = u2:0 sig0
+
+block0:
+    v5 = func_addr.i64 fn0
+    call_indirect sig0, v5()
+    call_indirect sig0, v5()
+    return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+;   subq    %rsp, $64, %rsp
+;   movq    %rbx, 16(%rsp)
+;   movq    %r12, 24(%rsp)
+;   movq    %r13, 32(%rsp)
+;   movq    %r14, 40(%rsp)
+;   movq    %r15, 48(%rsp)
+; block0:
+;   load_ext_name userextname0+0, %r15
+;   movq    %r15, rsp(0 + virtual offset)
+;   movq    rsp(0 + virtual offset), %r15
+;   call    *%r15
+;   movq    rsp(0 + virtual offset), %r15
+;   call    *%r15
+;   movq    16(%rsp), %rbx
+;   movq    24(%rsp), %r12
+;   movq    32(%rsp), %r13
+;   movq    40(%rsp), %r14
+;   movq    48(%rsp), %r15
+;   addq    %rsp, $64, %rsp
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+;   subq $0x40, %rsp
+;   movq %rbx, 0x10(%rsp)
+;   movq %r12, 0x18(%rsp)
+;   movq %r13, 0x20(%rsp)
+;   movq %r14, 0x28(%rsp)
+;   movq %r15, 0x30(%rsp)
+; block1: ; offset 0x21
+;   movabsq $0, %r15 ; reloc_external Abs8 u2:0 0
+;   movq %r15, (%rsp)
+;   movq (%rsp), %r15
+;   callq *%r15
+;   movq (%rsp), %r15
+;   callq *%r15
+;   movq 0x10(%rsp), %rbx
+;   movq 0x18(%rsp), %r12
+;   movq 0x20(%rsp), %r13
+;   movq 0x28(%rsp), %r14
+;   movq 0x30(%rsp), %r15
+;   addq $0x40, %rsp
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/tests/all/component_model/aot.rs b/tests/all/component_model/aot.rs
index 5fc5c8890..b94e8f085 100644
--- a/tests/all/component_model/aot.rs
+++ b/tests/all/component_model/aot.rs
@@ -1,5 +1,6 @@
 use anyhow::Result;
-use wasmtime::component::{Component, Linker};
+use wasmtime::component::types::ComponentItem;
+use wasmtime::component::{Component, Linker, Type};
 use wasmtime::{Module, Precompiled, Store};
 
 #[test]
@@ -133,3 +134,33 @@ fn detect_precompiled() -> Result<()> {
     );
     Ok(())
 }
+
+#[test]
+fn reflect_resource_import() -> Result<()> {
+    let engine = super::engine();
+    let c = Component::new(
+        &engine,
+        r#"
+        (component
+            (import "x" (type $x (sub resource)))
+            (import "y" (func (result (own $x))))
+        )
+        "#,
+    )?;
+    let ty = c.component_type();
+    let mut imports = ty.imports(&engine);
+    let (_, x) = imports.next().unwrap();
+    let (_, y) = imports.next().unwrap();
+    let x = match x {
+        ComponentItem::Resource(t) => t,
+        _ => unreachable!(),
+    };
+    let y = match y {
+        ComponentItem::ComponentFunc(t) => t,
+        _ => unreachable!(),
+    };
+    let result = y.results().next().unwrap();
+    assert_eq!(result, Type::Own(x));
+
+    Ok(())
+}
diff --git a/tests/misc_testsuite/component-model/linking.wast b/tests/misc_testsuite/component-model/linking.wast
index 7cf4aad7d..966926f08 100644
--- a/tests/misc_testsuite/component-model/linking.wast
+++ b/tests/misc_testsuite/component-model/linking.wast
@@ -2,7 +2,7 @@
   (component
     (import "undefined-name" (core module))
   )
-  "expected module found nothing")
+  "was not found")
 (component $i)
 (component
   (import "i" (instance))
@@ -12,7 +12,7 @@
   "expected module found instance")
 (assert_unlinkable
   (component (import "i" (func)))
-  "expected func found instance")
+  "expected function found instance")
 (assert_unlinkable
   (component (import "i" (instance (export "x" (func)))))
-  "expected func found nothing")
+  "was not found")
diff --git a/tests/misc_testsuite/component-model/resources.wast b/tests/misc_testsuite/component-model/resources.wast
index 351ed85b3..c2868f838 100644
--- a/tests/misc_testsuite/component-model/resources.wast
+++ b/tests/misc_testsuite/component-model/resources.wast
@@ -167,7 +167,7 @@
       (export "missing" (type (sub resource)))
     ))
   )
-  "expected resource found nothing")
+  "was not found")
 (assert_unlinkable
   (component
     (import "host" (instance