Skip to content

Commit a7a7254

Browse files
committed
feat: add future.forward canon builtin
Implement parsing, validation, encoding, and printing support for the ⏩-gated `future.forward` built-in proposed in WebAssembly/component-model#658: (canon future.forward $futureT (core func $f)) where `$f` has type `(func (param i32 i32))`, taking a readable future end and a writable future end. Exactly like its `stream.forward` counterpart, `future.forward` transfers both ends out of the calling instance and returns immediately with no result, so it takes neither an `async` immediate nor any `canonopt`s, since the value does not pass through the caller's linear memory. The binary encoding uses opcode 0x2e as assigned in the proposed specification change, and validation gates the built-in behind the component model async and "more async builtins" features. Assisted-by: claude:claude-fable-5
1 parent b602617 commit a7a7254

16 files changed

Lines changed: 167 additions & 0 deletions

File tree

crates/wasm-encoder/src/component/builder.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,12 @@ impl ComponentBuilder {
612612
self.core_funcs.add(Some("future.write"))
613613
}
614614

615+
/// Declares a new `future.forward` intrinsic.
616+
pub fn future_forward(&mut self, ty: u32) -> u32 {
617+
self.canonical_functions().future_forward(ty);
618+
self.core_funcs.add(Some("future.forward"))
619+
}
620+
615621
/// Declares a new `future.cancel-read` intrinsic.
616622
pub fn future_cancel_read(&mut self, ty: u32, async_: bool) -> u32 {
617623
self.canonical_functions().future_cancel_read(ty, async_);

crates/wasm-encoder/src/component/canonicals.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,16 @@ impl CanonicalFunctionSection {
381381
self
382382
}
383383

384+
/// Defines a function to forward the value of the readable end of one
385+
/// `future` to the writable end of another `future` of the specified
386+
/// type, transferring both ends out of the calling instance.
387+
pub fn future_forward(&mut self, ty: u32) -> &mut Self {
388+
self.bytes.push(0x2f);
389+
ty.encode(&mut self.bytes);
390+
self.num_added += 1;
391+
self
392+
}
393+
384394
/// Defines a function to cancel an in-progress read from a `future` of the
385395
/// specified type.
386396
pub fn future_cancel_read(&mut self, ty: u32, async_: bool) -> &mut Self {

crates/wasm-encoder/src/reencode/component.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,9 @@ pub mod component_utils {
10621062
.collect::<Result<Vec<_>, _>>()?;
10631063
section.future_write(reencoder.component_type_index(ty), options);
10641064
}
1065+
wasmparser::CanonicalFunction::FutureForward { ty } => {
1066+
section.future_forward(reencoder.component_type_index(ty));
1067+
}
10651068
wasmparser::CanonicalFunction::FutureCancelRead { ty, async_ } => {
10661069
section.future_cancel_read(reencoder.component_type_index(ty), async_);
10671070
}

crates/wasmparser/src/readers/component/canonicals.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,17 @@ pub enum CanonicalFunction {
218218
/// memory.
219219
options: Box<[CanonicalOption]>,
220220
},
221+
/// A function to forward the value of the readable end of one `future`
222+
/// to the writable end of another `future` of the same specified type,
223+
/// transferring both ends out of the calling instance.
224+
///
225+
/// 🚧 This is an experimental builtin sketched in
226+
/// <https://github.com/WebAssembly/component-model/issues/658> and not yet
227+
/// part of the Component Model specification.
228+
FutureForward {
229+
/// The `future` type to expect.
230+
ty: u32,
231+
},
221232
/// A function to cancel an in-progress read from a `future` of the
222233
/// specified type.
223234
FutureCancelRead {
@@ -402,6 +413,7 @@ impl<'a> FromReader<'a> for CanonicalFunction {
402413
ty: reader.read()?,
403414
options: read_opts(reader)?,
404415
},
416+
0x2f => CanonicalFunction::FutureForward { ty: reader.read()? },
405417
0x18 => CanonicalFunction::FutureCancelRead {
406418
ty: reader.read()?,
407419
async_: reader.read()?,

crates/wasmparser/src/validator/component.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,7 @@ impl ComponentState {
12621262
CanonicalFunction::FutureWrite { ty, options } => {
12631263
self.future_write(ty, &options, types, offset)
12641264
}
1265+
CanonicalFunction::FutureForward { ty } => self.future_forward(ty, types, offset),
12651266
CanonicalFunction::FutureCancelRead { ty, async_ } => {
12661267
self.future_cancel_read(ty, async_, types, offset)
12671268
}
@@ -1940,6 +1941,28 @@ impl ComponentState {
19401941
Ok(())
19411942
}
19421943

1944+
fn future_forward(&mut self, ty: u32, types: &mut TypeAlloc, offset: u64) -> Result<()> {
1945+
require_feature::cm_async(
1946+
self.features,
1947+
"`future.forward` requires the component model async feature",
1948+
offset,
1949+
)?;
1950+
require_feature::cm_more_async_builtins(
1951+
self.features,
1952+
"`future.forward` requires the component model more async builtins feature",
1953+
offset,
1954+
)?;
1955+
1956+
let ty = self.defined_type_at(ty, offset)?;
1957+
let ComponentDefinedType::Future { .. } = &types[ty] else {
1958+
bail!(offset, "`future.forward` requires a future type")
1959+
};
1960+
1961+
self.core_funcs
1962+
.push(types.intern_func_type(FuncType::new([ValType::I32; 2], []), offset));
1963+
Ok(())
1964+
}
1965+
19431966
fn future_cancel_read(
19441967
&mut self,
19451968
ty: u32,

crates/wasmprinter/src/component.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,11 @@ impl Printer<'_, '_> {
10981098
me.print_canonical_options(state, &options)
10991099
})?;
11001100
}
1101+
CanonicalFunction::FutureForward { ty } => {
1102+
self.print_intrinsic(state, "canon future.forward ", &|me, state| {
1103+
me.print_idx(&state.component.type_names, ty)
1104+
})?;
1105+
}
11011106
CanonicalFunction::FutureCancelRead { ty, async_ } => {
11021107
self.print_intrinsic(state, "canon future.cancel-read ", &|me, state| {
11031108
me.print_idx(&state.component.type_names, ty)?;

crates/wast/src/component/binary.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,10 @@ impl<'a> Encoder<'a> {
454454
self.funcs
455455
.future_write((&info.ty).into(), info.opts.iter().map(Into::into));
456456
}
457+
CoreFuncKind::FutureForward(info) => {
458+
self.core_func_names.push(name);
459+
self.funcs.future_forward((&info.ty).into());
460+
}
457461
CoreFuncKind::FutureCancelRead(info) => {
458462
self.core_func_names.push(name);
459463
self.funcs

crates/wast/src/component/func.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub enum CoreFuncKind<'a> {
7373
FutureNew(CanonFutureNew<'a>),
7474
FutureRead(CanonFutureRead<'a>),
7575
FutureWrite(CanonFutureWrite<'a>),
76+
FutureForward(CanonFutureForward<'a>),
7677
FutureCancelRead(CanonFutureCancelRead<'a>),
7778
FutureCancelWrite(CanonFutureCancelWrite<'a>),
7879
FutureDropReadable(CanonFutureDropReadable<'a>),
@@ -174,6 +175,8 @@ impl<'a> CoreFuncKind<'a> {
174175
Ok(CoreFuncKind::FutureRead(parser.parse()?))
175176
} else if l.peek::<kw::future_write>()? {
176177
Ok(CoreFuncKind::FutureWrite(parser.parse()?))
178+
} else if l.peek::<kw::future_forward>()? {
179+
Ok(CoreFuncKind::FutureForward(parser.parse()?))
177180
} else if l.peek::<kw::future_cancel_read>()? {
178181
Ok(CoreFuncKind::FutureCancelRead(parser.parse()?))
179182
} else if l.peek::<kw::future_cancel_write>()? {
@@ -873,6 +876,23 @@ impl<'a> Parse<'a> for CanonFutureWrite<'a> {
873876
}
874877
}
875878

879+
/// Information relating to the `future.forward` intrinsic.
880+
#[derive(Debug)]
881+
pub struct CanonFutureForward<'a> {
882+
/// The future type to forward.
883+
pub ty: ItemRef<'a, kw::r#type>,
884+
}
885+
886+
impl<'a> Parse<'a> for CanonFutureForward<'a> {
887+
fn parse(parser: Parser<'a>) -> Result<Self> {
888+
parser.parse::<kw::future_forward>()?;
889+
890+
Ok(Self {
891+
ty: parser.parse::<IndexOrRef<'_, _>>()?.0,
892+
})
893+
}
894+
}
895+
876896
/// Information relating to the `future.cancel-read` intrinsic.
877897
#[derive(Debug)]
878898
pub struct CanonFutureCancelRead<'a> {

crates/wast/src/component/resolve.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,9 @@ impl<'a> Resolver<'a> {
446446
self.component_item_ref(&mut info.ty)?;
447447
self.canon_opts(&mut info.opts)?;
448448
}
449+
CoreFuncKind::FutureForward(info) => {
450+
self.component_item_ref(&mut info.ty)?;
451+
}
449452
CoreFuncKind::FutureCancelRead(info) => {
450453
self.component_item_ref(&mut info.ty)?;
451454
}

crates/wast/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ pub mod kw {
583583
custom_keyword!(future_new = "future.new");
584584
custom_keyword!(future_read = "future.read");
585585
custom_keyword!(future_write = "future.write");
586+
custom_keyword!(future_forward = "future.forward");
586587
custom_keyword!(future_cancel_read = "future.cancel-read");
587588
custom_keyword!(future_cancel_write = "future.cancel-write");
588589
custom_keyword!(future_drop_readable = "future.drop-readable");

0 commit comments

Comments
 (0)