Repository: bytecodealliance/wasm-tools
Pull request #1989 (2025-01-28 18:30:05)

update `task.return` to match spec
As of https://github.com/WebAssembly/component-model/pull/431, `task.return` takes component-level result types rather than a core function type representing the flattening of those types.  This updates `wasm-tools` to match.


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 58949b2d..55d3a1ef 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -70,37 +70,37 @@ version = "0.224.0"
 rust-version = "1.76.0"
 
 [workspace.dependencies]
+ahash = { version = "0.8.11", default-features = false }
 anyhow = "1.0.58"
 arbitrary = "1.1.0"
+bitflags = "2.5.0"
 clap = { version = "4.0.0", features = ["derive"] }
 clap_complete = "4.4.7"
 comfy-table = { version = "7.1.3", default-features = false }
 criterion = { version = "0.5.1", default-features = false }
 env_logger = "0.11"
+gimli = "0.30.0"
+hashbrown = { version = "0.15.2", default-features = false, features = ['default-hasher'] }
+id-arena = "2"
 indexmap = { version = "2.7.0", default-features = false }
+indoc = "2.0.5"
 leb128fmt = { version = "0.1.0", default-features = false }
 libfuzzer-sys = "0.4.0"
+libtest-mimic = "0.7.0"
 log = "0.4.17"
 num_cpus = "1.13"
+pretty_assertions = "1.3.0"
 rand = { version = "0.8.4", features = ["small_rng"] }
 rayon = "1.3"
+semver = { version = "1.0.0", default-features = false }
 serde = { version = "1.0.166", default-features = false, features = ['alloc'] }
 serde_derive = "1.0.166"
 serde_json = { version = "1" }
-spdx = "0.10.1"
-wasmtime = { version = "28.0.0", default-features = false, features = ['cranelift', 'component-model', 'runtime', 'gc-drc'] }
-url = "2.0.0"
-pretty_assertions = "1.3.0"
-semver = { version = "1.0.0", default-features = false }
 smallvec = "1.11.1"
-libtest-mimic = "0.7.0"
-bitflags = "2.5.0"
-hashbrown = { version = "0.15.2", default-features = false, features = ['default-hasher'] }
-ahash = { version = "0.8.11", default-features = false }
+spdx = "0.10.1"
 termcolor = "1.2.0"
-indoc = "2.0.5"
-gimli = "0.30.0"
-id-arena = "2"
+url = "2.0.0"
+wasmtime = { version = "28.0.0", default-features = false, features = ['cranelift', 'component-model', 'runtime', 'gc-drc'] }
 
 wasm-compose = { version = "0.224.0", path = "crates/wasm-compose" }
 wasm-encoder = { version = "0.224.0", path = "crates/wasm-encoder", default-features = false }
@@ -119,19 +119,19 @@ wit-smith = { version = "0.224.0", path = "crates/wit-smith" }
 
 [dependencies]
 anyhow = { workspace = true }
-env_logger = { workspace = true }
-log = { workspace = true }
 clap = { workspace = true, features = ['wrap_help'] }
 clap_complete = { workspace = true, optional = true }
 comfy-table = { workspace = true }
+env_logger = { workspace = true }
+log = { workspace = true }
 tempfile = "3.2.0"
-wat = { workspace = true, features = ['dwarf', 'component-model'] }
 termcolor = { workspace = true }
+wat = { workspace = true, features = ['dwarf', 'component-model'] }
 
 # Dependencies of `validate`
-wasmparser = { workspace = true, optional = true, features = ['std', 'component-model', 'simd'] }
-rayon = { workspace = true, optional = true }
 bitflags = { workspace = true, optional = true }
+rayon = { workspace = true, optional = true }
+wasmparser = { workspace = true, optional = true, features = ['std', 'component-model', 'simd'] }
 
 # Dependencies of `print`
 wasmprinter = { workspace = true, features = ['component-model'] }
@@ -150,21 +150,21 @@ wasm-shrink = { workspace = true, features = ["clap"], optional = true }
 wasm-mutate = { workspace = true, features = ["clap"], optional = true }
 
 # Dependencies of `strip`
-wasm-encoder = { workspace = true, features = ["std"], optional = true }
 regex = { version = "1.6.0", optional = true }
+wasm-encoder = { workspace = true, features = ["std"], optional = true }
 
 # Dependencies of `compose`
 wasm-compose = { workspace = true, optional = true }
 
 # Dependencies of `demangle`
-rustc-demangle = { version = "0.1.21", optional = true }
 cpp_demangle = { version = "0.4.0", optional = true }
+rustc-demangle = { version = "0.1.21", optional = true }
 
 # Dependencies of `component`
+wast = { workspace = true, optional = true }
 wit-component = { workspace = true, optional = true, features = ['dummy-module', 'wat', 'semver-check'] }
 wit-encoder = { workspace = true, optional = true }
 wit-parser = { workspace = true, optional = true, features = ['decoding', 'wat', 'serde'] }
-wast = { workspace = true, optional = true }
 
 # Dependencies of `metadata`
 wasm-metadata = { workspace = true, features = ["clap"], optional = true }
@@ -180,12 +180,12 @@ gimli = { workspace = true, optional = true }
 is_executable = { version = "1.0.1", optional = true }
 
 [dev-dependencies]
+indexmap = { workspace = true }
+libtest-mimic = { workspace = true }
+pretty_assertions = { workspace = true }
 serde_json = "1.0"
 tempfile = "3.1"
 wast = { workspace = true }
-pretty_assertions = { workspace = true }
-libtest-mimic = { workspace = true }
-indexmap = { workspace = true }
 
 [[test]]
 name = "cli"
diff --git a/crates/wasm-compose/Cargo.toml b/crates/wasm-compose/Cargo.toml
index 30ed8954..50e25014 100644
--- a/crates/wasm-compose/Cargo.toml
+++ b/crates/wasm-compose/Cargo.toml
@@ -15,19 +15,19 @@ rust-version.workspace = true
 workspace = true
 
 [dependencies]
-wat = { workspace = true }
-wasm-encoder = { workspace = true, features = ['std', 'wasmparser', 'component-model'] }
-wasmparser = { workspace = true, features = ['std', 'validate', 'component-model', 'features'] }
-indexmap = { workspace = true, features = ["serde"] }
 anyhow = { workspace = true }
+heck = "0.4.0"
+im-rc = "15.1.0"
+indexmap = { workspace = true, features = ["serde"] }
+log = { workspace = true }
+petgraph = "0.6.2"
 serde = { workspace = true }
 serde_derive = { workspace = true }
-petgraph = "0.6.2"
-log = { workspace = true }
 serde_yaml = "0.9.22"
 smallvec = "1.10.0"
-heck = "0.4.0"
-im-rc = "15.1.0"
+wasm-encoder = { workspace = true, features = ['std', 'wasmparser', 'component-model'] }
+wasmparser = { workspace = true, features = ['std', 'validate', 'component-model', 'features'] }
+wat = { workspace = true }
 
 [dev-dependencies]
 glob = "0.3.0"
diff --git a/crates/wasm-compose/example/middleware/Cargo.toml b/crates/wasm-compose/example/middleware/Cargo.toml
index a8270bea..7d9bbb7a 100644
--- a/crates/wasm-compose/example/middleware/Cargo.toml
+++ b/crates/wasm-compose/example/middleware/Cargo.toml
@@ -6,8 +6,8 @@ publish = false
 rust-version.workspace = true
 
 [dependencies]
-flate2 = "1.0.24"
 cargo-component-bindings = "0.6.0"
+flate2 = "1.0.24"
 
 [lib]
 crate-type = ["cdylib"]
diff --git a/crates/wasm-compose/example/server/Cargo.toml b/crates/wasm-compose/example/server/Cargo.toml
index 10ab9b3d..9e90cddc 100644
--- a/crates/wasm-compose/example/server/Cargo.toml
+++ b/crates/wasm-compose/example/server/Cargo.toml
@@ -6,14 +6,14 @@ publish = false
 rust-version.workspace = true
 
 [dependencies]
+anyhow = "1.0.72"
 async-std = { version = "1.12.0", features = ["attributes"] }
+async-trait = "0.1.74"
 clap = { version = "4.3.19", features = ["derive"] }
 driftwood = "0.0.7"
 tide = "0.16.0"
+wasi-cap-std-sync = { version = "16.0" }
 wasmtime = { version = "16.0", features = ["component-model"] }
 wasmtime-wasi = { version = "16.0" }
-wasi-cap-std-sync = { version = "16.0" }
-anyhow = "1.0.72"
-async-trait = "0.1.74"
 
 [workspace]
diff --git a/crates/wasm-encoder/src/component/builder.rs b/crates/wasm-encoder/src/component/builder.rs
index fedff5fa..8c78b40d 100644
--- a/crates/wasm-encoder/src/component/builder.rs
+++ b/crates/wasm-encoder/src/component/builder.rs
@@ -392,7 +392,7 @@ impl ComponentBuilder {
     }
 
     /// Declares a new `task.return` intrinsic.
-    pub fn task_return(&mut self, ty: u32) -> u32 {
+    pub fn task_return(&mut self, ty: Option<impl Into<ComponentValType>>) -> u32 {
         self.canonical_functions().task_return(ty);
         inc(&mut self.core_funcs)
     }
diff --git a/crates/wasm-encoder/src/component/canonicals.rs b/crates/wasm-encoder/src/component/canonicals.rs
index e8517551..e1ea1783 100644
--- a/crates/wasm-encoder/src/component/canonicals.rs
+++ b/crates/wasm-encoder/src/component/canonicals.rs
@@ -1,4 +1,4 @@
-use crate::{encode_section, ComponentSection, ComponentSectionId, Encode};
+use crate::{encode_section, ComponentSection, ComponentSectionId, ComponentValType, Encode};
 use alloc::vec::Vec;
 
 /// Represents options for canonical function definitions.
@@ -188,9 +188,15 @@ impl CanonicalFunctionSection {
     /// Defines a function which returns a result to the caller of a lifted
     /// export function.  This allows the callee to continue executing after
     /// returning a result.
-    pub fn task_return(&mut self, ty: u32) -> &mut Self {
+    pub fn task_return(&mut self, ty: Option<impl Into<ComponentValType>>) -> &mut Self {
         self.bytes.push(0x09);
-        ty.encode(&mut self.bytes);
+        if let Some(ty) = ty {
+            self.bytes.push(0x00);
+            ty.into().encode(&mut self.bytes);
+        } else {
+            self.bytes.push(0x01);
+            0_usize.encode(&mut self.bytes);
+        }
         self.num_added += 1;
         self
     }
diff --git a/crates/wasm-encoder/src/reencode/component.rs b/crates/wasm-encoder/src/reencode/component.rs
index c4b1d789..c19f4da3 100644
--- a/crates/wasm-encoder/src/reencode/component.rs
+++ b/crates/wasm-encoder/src/reencode/component.rs
@@ -970,8 +970,8 @@ pub mod component_utils {
             wasmparser::CanonicalFunction::TaskBackpressure => {
                 section.task_backpressure();
             }
-            wasmparser::CanonicalFunction::TaskReturn { type_index } => {
-                section.task_return(reencoder.type_index(type_index));
+            wasmparser::CanonicalFunction::TaskReturn { result } => {
+                section.task_return(result.map(|ty| reencoder.component_val_type(ty)));
             }
             wasmparser::CanonicalFunction::TaskWait { async_, memory } => {
                 section.task_wait(async_, reencoder.memory_index(memory));
diff --git a/crates/wasm-metadata/Cargo.toml b/crates/wasm-metadata/Cargo.toml
index f3593bb0..11eb0b07 100644
--- a/crates/wasm-metadata/Cargo.toml
+++ b/crates/wasm-metadata/Cargo.toml
@@ -11,13 +11,13 @@ rust-version.workspace = true
 workspace = true
 
 [dependencies]
-clap = { workspace = true, optional = true }
 anyhow = { workspace = true }
-wasmparser = { workspace = true, features = ['std', 'component-model', 'hash-collections'] }
-wasm-encoder = { workspace = true, features = ['std', 'component-model'] }
+clap = { workspace = true, optional = true }
 indexmap = { workspace = true, features = ["serde"] }
 serde = { workspace = true }
 serde_derive = { workspace = true }
 serde_json = { workspace = true }
 spdx = { workspace = true }
 url = { workspace = true }
+wasm-encoder = { workspace = true, features = ['std', 'component-model'] }
+wasmparser = { workspace = true, features = ['std', 'component-model', 'hash-collections'] }
diff --git a/crates/wasm-mutate-stats/Cargo.toml b/crates/wasm-mutate-stats/Cargo.toml
index d77079a1..2c475e71 100644
--- a/crates/wasm-mutate-stats/Cargo.toml
+++ b/crates/wasm-mutate-stats/Cargo.toml
@@ -10,16 +10,16 @@ workspace = true
 [dependencies]
 anyhow = { workspace = true }
 arbitrary = { workspace = true }
+clap = { workspace = true }
+env_logger = { workspace = true }
+itertools = "0.13.0"
+log = { workspace = true }
 num_cpus = { workspace = true }
 rand = { workspace = true }
 wasm-mutate = { workspace = true }
-wasmprinter = { workspace = true }
 wasmparser = { workspace = true, features = ['std', 'validate'] }
+wasmprinter = { workspace = true }
 wasmtime = { workspace = true }
-env_logger = { workspace = true }
-itertools = "0.13.0"
-clap = { workspace = true }
-log = { workspace = true }
 
 [lib]
 doctest = false
diff --git a/crates/wasm-mutate/Cargo.toml b/crates/wasm-mutate/Cargo.toml
index 5ec4b598..74ab4739 100644
--- a/crates/wasm-mutate/Cargo.toml
+++ b/crates/wasm-mutate/Cargo.toml
@@ -12,16 +12,16 @@ workspace = true
 
 [dependencies]
 clap = { workspace = true, optional = true }
+egg = "0.6.0"
+log = { workspace = true }
+rand = { workspace = true }
 thiserror = "1.0.28"
-wasmparser = { workspace = true, features = ['std', 'simd'] }
 wasm-encoder = { workspace = true, features = ['std', 'wasmparser'] }
-rand = { workspace = true }
-log = { workspace = true }
-egg = "0.6.0"
+wasmparser = { workspace = true, features = ['std', 'simd'] }
 
 [dev-dependencies]
 anyhow = { workspace = true }
-wat = { workspace = true }
-wasmprinter = { workspace = true }
 env_logger = { workspace = true }
 wasmparser = { workspace = true, features = ['std', 'validate', 'features', 'simd'] }
+wasmprinter = { workspace = true }
+wat = { workspace = true }
diff --git a/crates/wasm-shrink/Cargo.toml b/crates/wasm-shrink/Cargo.toml
index 26a73f85..c8312d52 100644
--- a/crates/wasm-shrink/Cargo.toml
+++ b/crates/wasm-shrink/Cargo.toml
@@ -17,9 +17,9 @@ workspace = true
 [dependencies]
 anyhow = { workspace = true }
 blake3 = "1.2.0"
+clap = { workspace = true, optional = true }
 log = { workspace = true }
 rand = { workspace = true }
-clap = { workspace = true, optional = true }
 wasm-mutate = { workspace = true }
 wasmparser = { workspace = true, features = ['std', 'validate', 'features'] }
 
diff --git a/crates/wasmparser/Cargo.toml b/crates/wasmparser/Cargo.toml
index 8378daba..275b6e28 100644
--- a/crates/wasmparser/Cargo.toml
+++ b/crates/wasmparser/Cargo.toml
@@ -21,21 +21,21 @@ all-features = true
 
 [dependencies]
 bitflags = "2.4.1"
+hashbrown = { workspace = true, optional = true }
 indexmap = { workspace = true, optional = true }
 semver = { workspace = true, optional = true }
-hashbrown = { workspace = true, optional = true }
 serde = { workspace = true, optional = true }
 
 [dev-dependencies]
 anyhow = { workspace = true }
 criterion = { workspace = true }
-wat = { path = "../wat" }
-wast = { path = "../wast" }
-rayon = { workspace = true }
-once_cell = "1.13.0"
-wasm-encoder = { path = "../wasm-encoder" }
 env_logger.workspace = true
 log.workspace = true
+once_cell = "1.13.0"
+rayon = { workspace = true }
+wasm-encoder = { path = "../wasm-encoder" }
+wast = { path = "../wast" }
+wat = { path = "../wat" }
 
 [[bench]]
 name = "benchmark"
@@ -57,7 +57,7 @@ hash-collections = [
   'dep:hashbrown',
   'dep:indexmap',
 ]
-# Tells the `wasmparser` crate to prefer using its built-in btree-based collections 
+# Tells the `wasmparser` crate to prefer using its built-in btree-based collections
 # even if `hash-collections` is enabled.
 prefer-btree-collections = []
 
diff --git a/crates/wasmparser/src/readers/component/canonicals.rs b/crates/wasmparser/src/readers/component/canonicals.rs
index d2ebe33a..6fe9f165 100644
--- a/crates/wasmparser/src/readers/component/canonicals.rs
+++ b/crates/wasmparser/src/readers/component/canonicals.rs
@@ -1,6 +1,8 @@
 use crate::limits::MAX_WASM_CANONICAL_OPTIONS;
 use crate::prelude::*;
-use crate::{BinaryReader, FromReader, Result, SectionLimited};
+use crate::{
+    BinaryReader, BinaryReaderError, ComponentValType, FromReader, Result, SectionLimited,
+};
 
 /// Represents options for component functions.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -80,10 +82,8 @@ pub enum CanonicalFunction {
     /// function.  This allows the callee to continue executing after returning
     /// a result.
     TaskReturn {
-        /// Core function type whose parameters represent the flattened
-        /// representation of the component-level results to be returned by the
-        /// currently executing task.
-        type_index: u32,
+        /// The result type, if any.
+        result: Option<ComponentValType>,
     },
     /// A function which waits for at least one outstanding async
     /// task/stream/future to make progress, returning the first such event.
@@ -275,7 +275,20 @@ impl<'a> FromReader<'a> for CanonicalFunction {
             0x06 => CanonicalFunction::ThreadHwConcurrency,
             0x08 => CanonicalFunction::TaskBackpressure,
             0x09 => CanonicalFunction::TaskReturn {
-                type_index: reader.read()?,
+                result: match reader.read_u8()? {
+                    0x00 => Some(reader.read()?),
+                    0x01 => {
+                        if reader.read_u8()? == 0 {
+                            None
+                        } else {
+                            return Err(BinaryReaderError::new(
+                                "named results not allowed for `task.return` intrinsic",
+                                reader.original_position() - 2,
+                            ));
+                        }
+                    }
+                    x => return reader.invalid_leading_byte(x, "`task.return` result"),
+                },
             },
             0x0a => CanonicalFunction::TaskWait {
                 async_: reader.read()?,
diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs
index 182ade9c..86dc227d 100644
--- a/crates/wasmparser/src/validator.rs
+++ b/crates/wasmparser/src/validator.rs
@@ -1313,8 +1313,8 @@ impl Validator {
                     crate::CanonicalFunction::TaskBackpressure => {
                         current.task_backpressure(types, offset, features)
                     }
-                    crate::CanonicalFunction::TaskReturn { type_index } => {
-                        current.task_return(type_index, types, offset, features)
+                    crate::CanonicalFunction::TaskReturn { result } => {
+                        current.task_return(&result, types, offset, features)
                     }
                     crate::CanonicalFunction::TaskWait { async_, memory } => {
                         current.task_wait(async_, memory, types, offset, features)
diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs
index 4133bf4e..1ccd1a5e 100644
--- a/crates/wasmparser/src/validator/component.rs
+++ b/crates/wasmparser/src/validator/component.rs
@@ -19,9 +19,9 @@ use crate::prelude::*;
 use crate::validator::names::{ComponentName, ComponentNameKind, KebabStr, KebabString};
 use crate::{
     BinaryReaderError, CanonicalOption, ComponentExportName, ComponentExternalKind,
-    ComponentOuterAliasKind, ComponentTypeRef, CompositeInnerType, CompositeType, ExternalKind,
-    FuncType, GlobalType, InstantiationArgKind, MemoryType, PackedIndex, RefType, Result, SubType,
-    TableType, TypeBounds, ValType, WasmFeatures,
+    ComponentOuterAliasKind, ComponentTypeRef, CompositeInnerType, ExternalKind, FuncType,
+    GlobalType, InstantiationArgKind, MemoryType, PackedIndex, RefType, Result, SubType, TableType,
+    TypeBounds, ValType, WasmFeatures,
 };
 use core::mem;
 
@@ -1102,7 +1102,7 @@ impl ComponentState {
 
     pub fn task_return(
         &mut self,
-        type_index: u32,
+        result: &Option<crate::ComponentValType>,
         types: &mut TypeAlloc,
         offset: usize,
         features: &WasmFeatures,
@@ -1114,20 +1114,32 @@ impl ComponentState {
             )
         }
 
-        let id = self.type_id_at(type_index, offset)?;
-        let Some(SubType {
-            composite_type:
-                CompositeType {
-                    inner: CompositeInnerType::Func(_),
-                    ..
-                },
-            ..
-        }) = types.get(id)
-        else {
-            bail!(offset, "invalid `task.return` type index");
-        };
+        let info = ComponentFuncType {
+            info: TypeInfo::new(),
+            params: result
+                .iter()
+                .map(|ty| {
+                    Ok((
+                        KebabString::new("v").unwrap(),
+                        match ty {
+                            crate::ComponentValType::Primitive(ty) => {
+                                ComponentValType::Primitive(*ty)
+                            }
+                            crate::ComponentValType::Type(index) => {
+                                ComponentValType::Type(self.defined_type_at(*index, offset)?)
+                            }
+                        },
+                    ))
+                })
+                .collect::<Result<_>>()?,
+            results: Box::new([]),
+        }
+        .lower(types, Abi::LiftSync);
 
-        self.core_funcs.push(id);
+        assert!(info.results.iter().next().is_none());
+
+        self.core_funcs
+            .push(types.intern_func_type(FuncType::new(info.params.iter(), []), offset));
         Ok(())
     }
 
diff --git a/crates/wasmprinter/Cargo.toml b/crates/wasmprinter/Cargo.toml
index de8f3807..b868ce15 100644
--- a/crates/wasmprinter/Cargo.toml
+++ b/crates/wasmprinter/Cargo.toml
@@ -18,8 +18,8 @@ workspace = true
 
 [dependencies]
 anyhow = { workspace = true }
-wasmparser = { workspace = true, features = ['std', 'simd'] }
 termcolor = { workspace = true }
+wasmparser = { workspace = true, features = ['std', 'simd'] }
 
 [dev-dependencies]
 wat = { path = "../wat" }
diff --git a/crates/wasmprinter/src/component.rs b/crates/wasmprinter/src/component.rs
index 24a2cbfd..626a0c1b 100644
--- a/crates/wasmprinter/src/component.rs
+++ b/crates/wasmprinter/src/component.rs
@@ -914,9 +914,15 @@ impl Printer<'_, '_> {
                 CanonicalFunction::TaskBackpressure => {
                     self.print_intrinsic(state, "canon task.backpressure", &|_, _| Ok(()))?;
                 }
-                CanonicalFunction::TaskReturn { type_index } => {
-                    self.print_intrinsic(state, "canon task.return ", &|me, state| {
-                        me.print_idx(&state.component.type_names, type_index)
+                CanonicalFunction::TaskReturn { result } => {
+                    self.print_intrinsic(state, "canon task.return", &|me, state| {
+                        if let Some(ty) = result {
+                            me.result.write_str(" ")?;
+                            me.start_group("result ")?;
+                            me.print_component_val_type(state, &ty)?;
+                            me.end_group()?;
+                        }
+                        Ok(())
                     })?;
                 }
                 CanonicalFunction::TaskWait { async_, memory } => {
diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml
index 3e087c2f..54fbe360 100644
--- a/crates/wast/Cargo.toml
+++ b/crates/wast/Cargo.toml
@@ -20,19 +20,19 @@ all-features = true
 workspace = true
 
 [dependencies]
+bumpalo = "3.14.0"
+gimli = { workspace = true, optional = true }
 leb128fmt = { workspace = true }
-unicode-width = "0.2.0"
 memchr = "2.4.1"
+unicode-width = "0.2.0"
 wasm-encoder = { workspace = true, features = ['std'] }
-bumpalo = "3.14.0"
-gimli = { workspace = true, optional = true }
 
 [dev-dependencies]
 anyhow = { workspace = true }
 libtest-mimic = { workspace = true }
+rand = { workspace = true }
 wasmparser = { path = "../wasmparser" }
 wat = { path = "../wat" }
-rand = { workspace = true }
 
 [features]
 default = ['wasm-module', 'component-model']
diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs
index 2bf5e773..5e2cd299 100644
--- a/crates/wast/src/component/binary.rs
+++ b/crates/wast/src/component/binary.rs
@@ -363,7 +363,11 @@ impl<'a> Encoder<'a> {
             }
             CanonicalFuncKind::TaskReturn(info) => {
                 self.core_func_names.push(name);
-                self.funcs.task_return(info.ty.into());
+                self.funcs.task_return(
+                    info.result
+                        .as_ref()
+                        .map(|ty| wasm_encoder::ComponentValType::from(ty)),
+                );
             }
             CanonicalFuncKind::TaskWait(info) => {
                 self.core_func_names.push(name);
diff --git a/crates/wast/src/component/func.rs b/crates/wast/src/component/func.rs
index 49a42eda..8d68065e 100644
--- a/crates/wast/src/component/func.rs
+++ b/crates/wast/src/component/func.rs
@@ -521,17 +521,24 @@ impl<'a> Parse<'a> for CanonThreadHwConcurrency {
 /// Information relating to the `task.return` intrinsic.
 #[derive(Debug)]
 pub struct CanonTaskReturn<'a> {
-    /// The core function type representing the signature of this intrinsic.
-    pub ty: Index<'a>,
+    /// The type of the result which may be returned with this intrinsic.
+    pub result: Option<ComponentValType<'a>>,
 }
 
 impl<'a> Parse<'a> for CanonTaskReturn<'a> {
     fn parse(parser: Parser<'a>) -> Result<Self> {
         parser.parse::<kw::task_return>()?;
 
-        Ok(Self {
-            ty: parser.parse()?,
-        })
+        let result = if parser.peek2::<kw::result>()? {
+            Some(parser.parens(|p| {
+                p.parse::<kw::result>()?.0;
+                p.parse()
+            })?)
+        } else {
+            None
+        };
+
+        Ok(Self { result })
     }
 }
 
diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs
index c782a2eb..ddec257c 100644
--- a/crates/wast/src/component/resolve.rs
+++ b/crates/wast/src/component/resolve.rs
@@ -392,7 +392,9 @@ impl<'a> Resolver<'a> {
             | CanonicalFuncKind::SubtaskDrop
             | CanonicalFuncKind::ErrorContextDrop => {}
             CanonicalFuncKind::TaskReturn(info) => {
-                self.resolve_ns(&mut info.ty, Ns::CoreType)?;
+                if let Some(ty) = &mut info.result {
+                    self.component_val_type(ty)?;
+                }
             }
             CanonicalFuncKind::TaskWait(info) => {
                 self.core_item_ref(&mut info.memory)?;
diff --git a/crates/wit-component/Cargo.toml b/crates/wit-component/Cargo.toml
index 7697acff..e630ab17 100644
--- a/crates/wit-component/Cargo.toml
+++ b/crates/wit-component/Cargo.toml
@@ -20,28 +20,28 @@ workspace = true
 all-features = true
 
 [dependencies]
-wasmparser = { workspace = true, features = ['std', 'component-model', 'simd'] }
-wasm-encoder = { workspace = true, features = ['std', 'wasmparser'] }
-wasm-metadata = { workspace = true }
-wit-parser = { workspace = true, features = ['decoding', 'serde'] }
 anyhow = { workspace = true }
-log = "0.4.17"
 bitflags = "2.3.3"
 indexmap = { workspace = true }
-wast = { workspace = true, optional = true }
-wat = { workspace = true, optional = true }
+log = "0.4.17"
 serde = { workspace = true }
 serde_derive = { workspace = true }
 serde_json = { workspace = true }
+wasm-encoder = { workspace = true, features = ['std', 'wasmparser'] }
+wasm-metadata = { workspace = true }
+wasmparser = { workspace = true, features = ['std', 'component-model', 'simd'] }
+wast = { workspace = true, optional = true }
+wat = { workspace = true, optional = true }
+wit-parser = { workspace = true, features = ['decoding', 'serde'] }
 
 [dev-dependencies]
-wasmparser = { workspace = true, features = ['std', 'component-model', 'features'] }
-wasmprinter = { workspace = true, features = ['component-model'] }
+env_logger = { workspace = true }
 glob = "0.3.0"
+libtest-mimic = { workspace = true }
 pretty_assertions = "1.3.0"
-env_logger = { workspace = true }
+wasmparser = { workspace = true, features = ['std', 'component-model', 'features'] }
+wasmprinter = { workspace = true, features = ['component-model'] }
 wat = { workspace = true, features = ['component-model'] }
-libtest-mimic = { workspace = true }
 
 [target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
 wasmtime = { workspace = true }
diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs
index 75852bc1..0f433b93 100644
--- a/crates/wit-component/src/encoding.rs
+++ b/crates/wit-component/src/encoding.rs
@@ -84,8 +84,8 @@ use wasm_encoder::*;
 use wasmparser::Validator;
 use wit_parser::{
     abi::{AbiVariant, WasmSignature, WasmType},
-    Docs, Function, FunctionKind, InterfaceId, LiveTypes, Resolve, Results, Stability, Type,
-    TypeDefKind, TypeId, TypeOwner, WorldItem, WorldKey,
+    Function, FunctionKind, InterfaceId, LiveTypes, Resolve, Results, Stability, Type, TypeDefKind,
+    TypeId, TypeOwner, WorldItem, WorldKey,
 };
 
 const INDIRECT_TABLE_NAME: &str = "$imports";
@@ -1718,38 +1718,22 @@ impl<'a> EncodingState<'a> {
                     AbiVariant::GuestImport,
                 )
             }
-            Import::ExportedTaskReturn(function) => {
-                let signature = resolve.wasm_signature(
-                    AbiVariant::GuestImport,
-                    &Function {
-                        name: String::new(),
-                        kind: FunctionKind::Freestanding,
-                        params: match &function.results {
-                            Results::Named(params) => params.clone(),
-                            Results::Anon(ty) => vec![("v".to_string(), *ty)],
-                        },
-                        results: Results::Named(Vec::new()),
-                        docs: Docs::default(),
-                        stability: Stability::Unknown,
-                    },
-                );
-                let (type_index, encoder) = self.component.core_type();
-                encoder.core().function(
-                    signature.params.into_iter().map(into_val_type),
-                    signature.results.into_iter().map(into_val_type),
-                );
-
-                let index = self.component.task_return(type_index);
-                return Ok((ExportKind::Func, index));
+            Import::ExportedTaskReturn(interface, function) => {
+                let mut encoder = self.root_export_type_encoder(*interface);
 
-                fn into_val_type(ty: WasmType) -> ValType {
-                    match ty {
-                        WasmType::I32 | WasmType::Pointer | WasmType::Length => ValType::I32,
-                        WasmType::I64 | WasmType::PointerOrI64 => ValType::I64,
-                        WasmType::F32 => ValType::F32,
-                        WasmType::F64 => ValType::F64,
+                let result = match &function.results {
+                    Results::Named(rs) => {
+                        if rs.is_empty() {
+                            None
+                        } else {
+                            bail!("named results not supported for `task.return` intrinsic")
+                        }
                     }
-                }
+                    Results::Anon(ty) => Some(encoder.encode_valtype(resolve, ty)?),
+                };
+
+                let index = self.component.task_return(result);
+                return Ok((ExportKind::Func, index));
             }
             Import::TaskBackpressure => {
                 let index = self.component.task_backpressure();
diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs
index 203e5567..b5ed70b9 100644
--- a/crates/wit-component/src/validation.rs
+++ b/crates/wit-component/src/validation.rs
@@ -243,7 +243,7 @@ pub enum Import {
     /// As of this writing, only async-lifted exports use `task.return`, but the
     /// plan is to also support it for sync-lifted exports in the future as
     /// well.
-    ExportedTaskReturn(Function),
+    ExportedTaskReturn(Option<InterfaceId>, Function),
 
     /// A `canon task.backpressure` intrinsic.
     ///
@@ -551,7 +551,7 @@ impl ImportMap {
                 // it's associated with in general.  Instead, the host will
                 // compare it with the expected type at runtime and trap if
                 // necessary.
-                Some(Import::ExportedTaskReturn(func))
+                Some(Import::ExportedTaskReturn(interface_id, func))
             } else {
                 None
             })
diff --git a/crates/wit-encoder/Cargo.toml b/crates/wit-encoder/Cargo.toml
index cae10c59..33c80066 100644
--- a/crates/wit-encoder/Cargo.toml
+++ b/crates/wit-encoder/Cargo.toml
@@ -26,11 +26,11 @@ serde = ["dep:serde", "semver/serde"]
 from-parser = ["wit-parser", "id-arena"]
 
 [dependencies]
-semver = { workspace = true }
+id-arena = { workspace = true, optional = true }
 pretty_assertions = { workspace = true }
+semver = { workspace = true }
 serde = { workspace = true, optional = true, features = ["derive"] }
 wit-parser = { workspace = true, optional = true }
-id-arena = { workspace = true, optional = true }
 
 [dev-dependencies]
 anyhow = { workspace = true }
diff --git a/crates/wit-parser/Cargo.toml b/crates/wit-parser/Cargo.toml
index 596c3689..426f52c1 100644
--- a/crates/wit-parser/Cargo.toml
+++ b/crates/wit-parser/Cargo.toml
@@ -17,16 +17,16 @@ rust-version.workspace = true
 workspace = true
 
 [dependencies]
-id-arena = { workspace = true }
 anyhow = { workspace = true }
+id-arena = { workspace = true }
 indexmap = { workspace = true, features = ['std'] }
-unicode-xid = "0.2.2"
 log = { workspace = true }
 semver = { workspace = true }
 serde = { workspace = true, optional = true }
 serde_derive = { workspace = true, optional = true }
-wasmparser = { workspace = true, optional = true, features = ['std', 'validate', 'component-model', 'features'] }
 serde_json = { workspace = true, optional = true }
+unicode-xid = "0.2.2"
+wasmparser = { workspace = true, optional = true, features = ['std', 'validate', 'component-model', 'features'] }
 wat = { workspace = true, optional = true }
 
 [features]
@@ -46,10 +46,10 @@ wat = ['decoding', 'dep:wat']
 
 [dev-dependencies]
 env_logger = { workspace = true }
+libtest-mimic = { workspace = true }
 pretty_assertions = { workspace = true }
 serde_json = { workspace = true }
 wit-parser = { path = '.', features = ['serde', 'wat'] }
-libtest-mimic = { workspace = true }
 
 [[test]]
 name = "all"
diff --git a/crates/wit-smith/Cargo.toml b/crates/wit-smith/Cargo.toml
index 09d8e9c6..ae634451 100644
--- a/crates/wit-smith/Cargo.toml
+++ b/crates/wit-smith/Cargo.toml
@@ -13,9 +13,9 @@ workspace = true
 
 [dependencies]
 arbitrary = { workspace = true, features = ["derive"] }
-wit-component = { workspace = true }
-wit-parser = { workspace = true }
 clap = { workspace = true, optional = true }
 indexmap = { workspace = true }
 log = { workspace = true }
 semver = { workspace = true }
+wit-component = { workspace = true }
+wit-parser = { workspace = true }
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
index d6a60c31..658c2015 100644
--- a/fuzz/Cargo.toml
+++ b/fuzz/Cargo.toml
@@ -17,6 +17,7 @@ env_logger = { workspace = true }
 libfuzzer-sys = { workspace = true }
 log = { workspace = true }
 tempfile = "3.0"
+wasm-encoder = { workspace = true }
 wasm-mutate = { workspace = true }
 wasm-smith = { workspace = true, features = ['component-model', 'wasmparser'] }
 wasmparser = { workspace = true, features = ['std', 'features'] }
@@ -24,10 +25,9 @@ wasmprinter = { workspace = true, features = ['component-model'] }
 wasmtime = { workspace = true, optional = true }
 wast = { workspace = true }
 wat = { workspace = true }
+wit-component = { workspace = true, features = ['dummy-module'] }
 wit-parser = { workspace = true }
 wit-smith = { workspace = true }
-wit-component = { workspace = true, features = ['dummy-module'] }
-wasm-encoder = { workspace = true }
 
 [dependencies.wit-parser-old]
 package = 'wit-parser'
diff --git a/playground/component/Cargo.toml b/playground/component/Cargo.toml
index 6d3993ff..9a91d341 100644
--- a/playground/component/Cargo.toml
+++ b/playground/component/Cargo.toml
@@ -4,9 +4,9 @@ publish = false
 edition.workspace = true
 
 [dependencies]
-wit-bindgen-rt = { version = "0.26.0", features = ["bitflags"] }
-wat = { workspace = true }
 wasmprinter = { workspace = true }
+wat = { workspace = true }
+wit-bindgen-rt = { version = "0.26.0", features = ["bitflags"] }
 
 [lib]
 crate-type = ["cdylib"]


Test patch:
diff --git a/crates/wit-component/tests/components/async-builtins/component.wat b/crates/wit-component/tests/components/async-builtins/component.wat
index dcb9a56c..b6b937a2 100644
--- a/crates/wit-component/tests/components/async-builtins/component.wat
+++ b/crates/wit-component/tests/components/async-builtins/component.wat
@@ -104,13 +104,11 @@
     (export "[error-context-debug-message;encoding=utf8;realloc=cabi_realloc]" (func 6))
     (export "[error-context-drop]" (func 7))
   )
-  (core type (;0;) (func (param i32 i32)))
-  (core func (;8;) (canon task.return 0))
+  (core func (;8;) (canon task.return (result string)))
   (core instance (;2;)
     (export "[task-return]foo" (func 8))
   )
-  (core type (;1;) (func (param i32 i32)))
-  (core func (;9;) (canon task.return 1))
+  (core func (;9;) (canon task.return (result string)))
   (core instance (;3;)
     (export "[task-return]foo" (func 9))
   )
diff --git a/tests/local/component-model-async/task-builtins.wast b/tests/local/component-model-async/task-builtins.wast
index 6115b5d6..68c702d2 100644
--- a/tests/local/component-model-async/task-builtins.wast
+++ b/tests/local/component-model-async/task-builtins.wast
@@ -24,8 +24,7 @@
   (core module $m
     (import "" "task.return" (func $task-return (param i32)))
   )
-  (core type $task-return-type (func (param i32)))
-  (core func $task-return (canon task.return $task-return-type))
+  (core func $task-return (canon task.return (result u32)))
   (core instance $i (instantiate $m (with "" (instance (export "task.return" (func $task-return))))))
 )
 
diff --git a/tests/local/missing-features/component-model/async.wast b/tests/local/missing-features/component-model/async.wast
index 06c93f9d..5e4ea0f8 100644
--- a/tests/local/missing-features/component-model/async.wast
+++ b/tests/local/missing-features/component-model/async.wast
@@ -46,8 +46,7 @@
     (core module $m
       (import "" "task.return" (func $task-return (param i32)))
     )
-    (core type $task-return-type (func (param i32)))
-    (core func $task-return (canon task.return $task-return-type))
+    (core func $task-return (canon task.return (result u32)))
     (core instance $i (instantiate $m (with "" (instance (export "task.return" (func $task-return))))))
   )
   "`task.return` requires the component model async feature"
diff --git a/tests/snapshots/local/component-model-async/task-builtins.wast.json b/tests/snapshots/local/component-model-async/task-builtins.wast.json
index abc6d5cb..6001d1e3 100644
--- a/tests/snapshots/local/component-model-async/task-builtins.wast.json
+++ b/tests/snapshots/local/component-model-async/task-builtins.wast.json
@@ -22,52 +22,52 @@
     },
     {
       "type": "module",
-      "line": 33,
+      "line": 32,
       "filename": "task-builtins.3.wasm",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 45,
+      "line": 44,
       "filename": "task-builtins.4.wasm",
       "module_type": "binary",
       "text": "type mismatch for export `task.wait` of module instantiation argument ``"
     },
     {
       "type": "module",
-      "line": 58,
+      "line": 57,
       "filename": "task-builtins.5.wasm",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 70,
+      "line": 69,
       "filename": "task-builtins.6.wasm",
       "module_type": "binary",
       "text": "type mismatch for export `task.poll` of module instantiation argument ``"
     },
     {
       "type": "module",
-      "line": 83,
+      "line": 82,
       "filename": "task-builtins.7.wasm",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 93,
+      "line": 92,
       "filename": "task-builtins.8.wasm",
       "module_type": "binary",
       "text": "type mismatch for export `task.yield` of module instantiation argument ``"
     },
     {
       "type": "module",
-      "line": 104,
+      "line": 103,
       "filename": "task-builtins.9.wasm",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 114,
+      "line": 113,
       "filename": "task-builtins.10.wasm",
       "module_type": "binary",
       "text": "type mismatch for export `subtask.drop` of module instantiation argument ``"
diff --git a/tests/snapshots/local/component-model-async/task-builtins.wast/2.print b/tests/snapshots/local/component-model-async/task-builtins.wast/2.print
index 0209f7a7..84b3158d 100644
--- a/tests/snapshots/local/component-model-async/task-builtins.wast/2.print
+++ b/tests/snapshots/local/component-model-async/task-builtins.wast/2.print
@@ -3,8 +3,7 @@
     (type (;0;) (func (param i32)))
     (import "" "task.return" (func $task-return (;0;) (type 0)))
   )
-  (core type $task-return-type (;0;) (func (param i32)))
-  (core func $task-return (;0;) (canon task.return 0))
+  (core func $task-return (;0;) (canon task.return (result u32)))
   (core instance (;0;)
     (export "task.return" (func $task-return))
   )
diff --git a/tests/snapshots/local/missing-features/component-model/async.wast.json b/tests/snapshots/local/missing-features/component-model/async.wast.json
index 6aabbc4f..a02f9dfa 100644
--- a/tests/snapshots/local/missing-features/component-model/async.wast.json
+++ b/tests/snapshots/local/missing-features/component-model/async.wast.json
@@ -31,147 +31,147 @@
     },
     {
       "type": "assert_invalid",
-      "line": 58,
+      "line": 57,
       "filename": "async.4.wasm",
       "module_type": "binary",
       "text": "`task.wait` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 72,
+      "line": 71,
       "filename": "async.5.wasm",
       "module_type": "binary",
       "text": "`task.poll` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 86,
+      "line": 85,
       "filename": "async.6.wasm",
       "module_type": "binary",
       "text": "`task.yield` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 98,
+      "line": 97,
       "filename": "async.7.wasm",
       "module_type": "binary",
       "text": "`subtask.drop` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 110,
+      "line": 109,
       "filename": "async.8.wasm",
       "module_type": "binary",
       "text": "`stream.new` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 123,
+      "line": 122,
       "filename": "async.9.wasm",
       "module_type": "binary",
       "text": "`stream.read` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 138,
+      "line": 137,
       "filename": "async.10.wasm",
       "module_type": "binary",
       "text": "`stream.write` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 153,
+      "line": 152,
       "filename": "async.11.wasm",
       "module_type": "binary",
       "text": "`stream.cancel-read` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 166,
+      "line": 165,
       "filename": "async.12.wasm",
       "module_type": "binary",
       "text": "`stream.cancel-write` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 179,
+      "line": 178,
       "filename": "async.13.wasm",
       "module_type": "binary",
       "text": "`stream.close-readable` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 192,
+      "line": 191,
       "filename": "async.14.wasm",
       "module_type": "binary",
       "text": "`stream.close-writable` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 205,
+      "line": 204,
       "filename": "async.15.wasm",
       "module_type": "binary",
       "text": "`future.new` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 218,
+      "line": 217,
       "filename": "async.16.wasm",
       "module_type": "binary",
       "text": "`future.read` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 233,
+      "line": 232,
       "filename": "async.17.wasm",
       "module_type": "binary",
       "text": "`future.write` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 248,
+      "line": 247,
       "filename": "async.18.wasm",
       "module_type": "binary",
       "text": "`future.cancel-read` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 261,
+      "line": 260,
       "filename": "async.19.wasm",
       "module_type": "binary",
       "text": "`future.cancel-write` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 274,
+      "line": 273,
       "filename": "async.20.wasm",
       "module_type": "binary",
       "text": "`future.close-readable` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 287,
+      "line": 286,
       "filename": "async.21.wasm",
       "module_type": "binary",
       "text": "`future.close-writable` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 300,
+      "line": 299,
       "filename": "async.22.wasm",
       "module_type": "binary",
       "text": "`error-context.new` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 314,
+      "line": 313,
       "filename": "async.23.wasm",
       "module_type": "binary",
       "text": "`error-context.debug-message` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 331,
+      "line": 330,
       "filename": "async.24.wasm",
       "module_type": "binary",
       "text": "`error-context.drop` requires the component model async feature"