From 312d51c835fae71208950181afdbad3878e8526b Mon Sep 17 00:00:00 2001 From: Michael Weigelt Date: Mon, 20 Apr 2026 14:21:39 +0000 Subject: [PATCH 1/7] wip --- crates/wasm-encoder/src/component/builder.rs | 29 ++++++--- .../wasm-encoder/src/component/canonicals.rs | 18 ++++-- crates/wasm-encoder/src/reencode/component.rs | 8 +-- .../src/readers/component/canonicals.rs | 32 +++++++--- crates/wasmparser/src/validator/component.rs | 46 +++++++++++-- crates/wasmprinter/src/component.rs | 16 +++-- crates/wast/src/component/binary.rs | 8 +-- crates/wast/src/component/func.rs | 12 ++-- crates/wast/src/component/resolve.rs | 2 +- crates/wit-component/src/encoding.rs | 24 +++++-- crates/wit-component/src/encoding/world.rs | 4 +- crates/wit-component/src/validation.rs | 64 ++++++++++++------- .../component-model/async/task-builtins.wast | 36 +++++++---- .../cli/component-model/memory64/context.wast | 61 ++++++++++++++++++ 14 files changed, 267 insertions(+), 93 deletions(-) create mode 100644 tests/cli/component-model/memory64/context.wast diff --git a/crates/wasm-encoder/src/component/builder.rs b/crates/wasm-encoder/src/component/builder.rs index 9a4099d7f7..8ff65c1811 100644 --- a/crates/wasm-encoder/src/component/builder.rs +++ b/crates/wasm-encoder/src/component/builder.rs @@ -506,16 +506,18 @@ impl ComponentBuilder { self.core_funcs.add(Some("task.cancel")) } - /// Declares a new `context.get` intrinsic. - pub fn context_get(&mut self, i: u32) -> u32 { - self.canonical_functions().context_get(i); - self.core_funcs.add(Some(&format!("context.get {i}"))) + /// Declares a new `context.get` intrinsic with the given value type. + pub fn context_get(&mut self, ty: ValType, i: u32) -> u32 { + self.canonical_functions().context_get(ty, i); + self.core_funcs + .add(Some(&format!("context.get {} {i}", val_type_name(ty)))) } - /// Declares a new `context.set` intrinsic. - pub fn context_set(&mut self, i: u32) -> u32 { - self.canonical_functions().context_set(i); - self.core_funcs.add(Some(&format!("context.set {i}"))) + /// Declares a new `context.set` intrinsic with the given value type. + pub fn context_set(&mut self, ty: ValType, i: u32) -> u32 { + self.canonical_functions().context_set(ty, i); + self.core_funcs + .add(Some(&format!("context.set {} {i}", val_type_name(ty)))) } /// Declares a new `thread.yield` intrinsic. @@ -832,3 +834,14 @@ impl Namespace { ret } } + +fn val_type_name(ty: ValType) -> &'static str { + match ty { + ValType::I32 => "i32", + ValType::I64 => "i64", + ValType::F32 => "f32", + ValType::F64 => "f64", + ValType::V128 => "v128", + ValType::Ref(_) => "ref", + } +} diff --git a/crates/wasm-encoder/src/component/canonicals.rs b/crates/wasm-encoder/src/component/canonicals.rs index b2fed82933..1e89d9cd62 100644 --- a/crates/wasm-encoder/src/component/canonicals.rs +++ b/crates/wasm-encoder/src/component/canonicals.rs @@ -1,4 +1,6 @@ -use crate::{ComponentSection, ComponentSectionId, ComponentValType, Encode, encode_section}; +use crate::{ + ComponentSection, ComponentSectionId, ComponentValType, Encode, ValType, encode_section, +}; use alloc::vec::Vec; /// Represents options for canonical function definitions. @@ -234,19 +236,21 @@ impl CanonicalFunctionSection { self } - /// Defines a new `context.get` intrinsic of the ith slot. - pub fn context_get(&mut self, i: u32) -> &mut Self { + /// Defines a new `context.get` intrinsic of the ith slot with the given + /// value type. + pub fn context_get(&mut self, ty: ValType, i: u32) -> &mut Self { self.bytes.push(0x0a); - self.bytes.push(0x7f); + ty.encode(&mut self.bytes); i.encode(&mut self.bytes); self.num_added += 1; self } - /// Defines a new `context.set` intrinsic of the ith slot. - pub fn context_set(&mut self, i: u32) -> &mut Self { + /// Defines a new `context.set` intrinsic of the ith slot with the given + /// value type. + pub fn context_set(&mut self, ty: ValType, i: u32) -> &mut Self { self.bytes.push(0x0b); - self.bytes.push(0x7f); + ty.encode(&mut self.bytes); i.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 464c28a20e..acb753713b 100644 --- a/crates/wasm-encoder/src/reencode/component.rs +++ b/crates/wasm-encoder/src/reencode/component.rs @@ -996,11 +996,11 @@ pub mod component_utils { wasmparser::CanonicalFunction::TaskCancel => { section.task_cancel(); } - wasmparser::CanonicalFunction::ContextGet(i) => { - section.context_get(i); + wasmparser::CanonicalFunction::ContextGet { ty, slot } => { + section.context_get(reencoder.val_type(ty)?, slot); } - wasmparser::CanonicalFunction::ContextSet(i) => { - section.context_set(i); + wasmparser::CanonicalFunction::ContextSet { ty, slot } => { + section.context_set(reencoder.val_type(ty)?, slot); } wasmparser::CanonicalFunction::ThreadYield { cancellable } => { section.thread_yield(cancellable); diff --git a/crates/wasmparser/src/readers/component/canonicals.rs b/crates/wasmparser/src/readers/component/canonicals.rs index 26df1257cd..aaa6b9c67d 100644 --- a/crates/wasmparser/src/readers/component/canonicals.rs +++ b/crates/wasmparser/src/readers/component/canonicals.rs @@ -1,6 +1,6 @@ use crate::limits::MAX_WASM_CANONICAL_OPTIONS; use crate::prelude::*; -use crate::{BinaryReader, ComponentValType, FromReader, Result, SectionLimited}; +use crate::{BinaryReader, ComponentValType, FromReader, Result, SectionLimited, ValType}; /// Represents options for component functions. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -108,9 +108,23 @@ pub enum CanonicalFunction { /// A function to acknowledge cancellation of the current task. TaskCancel, /// A `context.get` intrinsic for the `i`th slot of task-local storage. - ContextGet(u32), + ContextGet { + /// The value type of the slot. Currently only `ValType::I32` and + /// `ValType::I64` are accepted by the validator (with `I64` gated on + /// the component-model 64-bit feature). + ty: ValType, + /// The index of the task-local storage slot. + slot: u32, + }, /// A `context.set` intrinsic for the `i`th slot of task-local storage. - ContextSet(u32), + ContextSet { + /// The value type of the slot. Currently only `ValType::I32` and + /// `ValType::I64` are accepted by the validator (with `I64` gated on + /// the component-model 64-bit feature). + ty: ValType, + /// The index of the task-local storage slot. + slot: u32, + }, /// A function which yields control to the host so that other tasks are able /// to make progress, if any. ThreadYield { @@ -337,13 +351,13 @@ impl<'a> FromReader<'a> for CanonicalFunction { result: crate::read_resultlist(reader)?, options: read_opts(reader)?, }, - 0x0a => match reader.read_u8()? { - 0x7f => CanonicalFunction::ContextGet(reader.read_var_u32()?), - x => return reader.invalid_leading_byte(x, "context.get intrinsic type"), + 0x0a => CanonicalFunction::ContextGet { + ty: reader.read()?, + slot: reader.read_var_u32()?, }, - 0x0b => match reader.read_u8()? { - 0x7f => CanonicalFunction::ContextSet(reader.read_var_u32()?), - x => return reader.invalid_leading_byte(x, "context.set intrinsic type"), + 0x0b => CanonicalFunction::ContextSet { + ty: reader.read()?, + slot: reader.read_var_u32()?, }, 0x0c => CanonicalFunction::ThreadYield { cancellable: reader.read()?, diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index f323315630..4c57421dd7 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -1203,8 +1203,12 @@ impl ComponentState { self.task_return(&result, &options, types, offset) } CanonicalFunction::TaskCancel => self.task_cancel(types, offset), - CanonicalFunction::ContextGet(i) => self.context_get(i, types, offset), - CanonicalFunction::ContextSet(i) => self.context_set(i, types, offset), + CanonicalFunction::ContextGet { ty, slot } => { + self.context_get(ty, slot, types, offset) + } + CanonicalFunction::ContextSet { ty, slot } => { + self.context_set(ty, slot, types, offset) + } CanonicalFunction::ThreadYield { cancellable: _ } => self.thread_yield(types, offset), CanonicalFunction::SubtaskDrop => self.subtask_drop(types, offset), CanonicalFunction::SubtaskCancel { async_ } => { @@ -1517,34 +1521,64 @@ impl ComponentState { Ok(()) } - fn context_get(&mut self, i: u32, types: &mut TypeAlloc, offset: usize) -> Result<()> { + fn context_get( + &mut self, + ty: ValType, + i: u32, + types: &mut TypeAlloc, + offset: usize, + ) -> Result<()> { if !self.features.cm_async() { bail!( offset, "`context.get` requires the component model async feature" ) } + self.validate_context_type(ty, "context.get", offset)?; self.validate_context_immediate(i, "context.get", offset)?; self.core_funcs - .push(types.intern_func_type(FuncType::new([], [ValType::I32]), offset)); + .push(types.intern_func_type(FuncType::new([], [ty]), offset)); Ok(()) } - fn context_set(&mut self, i: u32, types: &mut TypeAlloc, offset: usize) -> Result<()> { + fn context_set( + &mut self, + ty: ValType, + i: u32, + types: &mut TypeAlloc, + offset: usize, + ) -> Result<()> { if !self.features.cm_async() { bail!( offset, "`context.set` requires the component model async feature" ) } + self.validate_context_type(ty, "context.set", offset)?; self.validate_context_immediate(i, "context.set", offset)?; self.core_funcs - .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset)); + .push(types.intern_func_type(FuncType::new([ty], []), offset)); Ok(()) } + fn validate_context_type(&self, ty: ValType, intrinsic: &str, offset: usize) -> Result<()> { + match ty { + ValType::I32 => Ok(()), + ValType::I64 => { + if !self.features.cm64() { + bail!( + offset, + "64-bit `{intrinsic}` requires the component model 64-bit feature" + ) + } + Ok(()) + } + _ => bail!(offset, "`{intrinsic}` only supports `i32` or `i64`"), + } + } + fn thread_yield(&mut self, types: &mut TypeAlloc, offset: usize) -> Result<()> { if !self.features.cm_async() { bail!( diff --git a/crates/wasmprinter/src/component.rs b/crates/wasmprinter/src/component.rs index e8a90b85f2..524760fd7a 100644 --- a/crates/wasmprinter/src/component.rs +++ b/crates/wasmprinter/src/component.rs @@ -977,15 +977,19 @@ impl Printer<'_, '_> { CanonicalFunction::TaskCancel => { self.print_intrinsic(state, "canon task.cancel", &|_, _| Ok(()))?; } - CanonicalFunction::ContextGet(i) => { - self.print_intrinsic(state, "canon context.get", &|me, _state| { - write!(me.result, " i32 {i}")?; + CanonicalFunction::ContextGet { ty, slot } => { + self.print_intrinsic(state, "canon context.get", &|me, state| { + me.result.write_str(" ")?; + me.print_valtype(state, ty)?; + write!(me.result, " {slot}")?; Ok(()) })?; } - CanonicalFunction::ContextSet(i) => { - self.print_intrinsic(state, "canon context.set", &|me, _state| { - write!(me.result, " i32 {i}")?; + CanonicalFunction::ContextSet { ty, slot } => { + self.print_intrinsic(state, "canon context.set", &|me, state| { + me.result.write_str(" ")?; + me.print_valtype(state, ty)?; + write!(me.result, " {slot}")?; Ok(()) })?; } diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs index 658f238c3f..eb90edb6c8 100644 --- a/crates/wast/src/component/binary.rs +++ b/crates/wast/src/component/binary.rs @@ -392,13 +392,13 @@ impl<'a> Encoder<'a> { self.core_func_names.push(name); self.funcs.task_cancel(); } - CoreFuncKind::ContextGet(i) => { + CoreFuncKind::ContextGet(ty, i) => { self.core_func_names.push(name); - self.funcs.context_get(*i); + self.funcs.context_get((*ty).into(), *i); } - CoreFuncKind::ContextSet(i) => { + CoreFuncKind::ContextSet(ty, i) => { self.core_func_names.push(name); - self.funcs.context_set(*i); + self.funcs.context_set((*ty).into(), *i); } CoreFuncKind::ThreadYield(info) => { self.core_func_names.push(name); diff --git a/crates/wast/src/component/func.rs b/crates/wast/src/component/func.rs index bb995117a7..17d31b5f90 100644 --- a/crates/wast/src/component/func.rs +++ b/crates/wast/src/component/func.rs @@ -58,8 +58,8 @@ pub enum CoreFuncKind<'a> { BackpressureDec, TaskReturn(CanonTaskReturn<'a>), TaskCancel, - ContextGet(u32), - ContextSet(u32), + ContextGet(crate::core::ValType<'a>, u32), + ContextSet(crate::core::ValType<'a>, u32), ThreadYield(CanonThreadYield), SubtaskDrop, SubtaskCancel(CanonSubtaskCancel), @@ -139,12 +139,12 @@ impl<'a> CoreFuncKind<'a> { Ok(CoreFuncKind::TaskCancel) } else if l.peek::()? { parser.parse::()?; - parser.parse::()?; - Ok(CoreFuncKind::ContextGet(parser.parse()?)) + let ty = parser.parse()?; + Ok(CoreFuncKind::ContextGet(ty, parser.parse()?)) } else if l.peek::()? { parser.parse::()?; - parser.parse::()?; - Ok(CoreFuncKind::ContextSet(parser.parse()?)) + let ty = parser.parse()?; + Ok(CoreFuncKind::ContextSet(ty, parser.parse()?)) } else if l.peek::()? { Ok(CoreFuncKind::ThreadYield(parser.parse()?)) } else if l.peek::()? { diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs index ca290fafe8..34ccf4726b 100644 --- a/crates/wast/src/component/resolve.rs +++ b/crates/wast/src/component/resolve.rs @@ -408,7 +408,7 @@ impl<'a> Resolver<'a> { } self.canon_opts(&mut info.opts)?; } - CoreFuncKind::ContextGet(_) | CoreFuncKind::ContextSet(_) => {} + CoreFuncKind::ContextGet(..) | CoreFuncKind::ContextSet(..) => {} CoreFuncKind::StreamNew(info) => { self.resolve_ns(&mut info.ty, Ns::Type)?; } diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index 958e1eb7df..23e16baa7d 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -116,6 +116,18 @@ fn to_val_type(ty: &WasmType) -> ValType { } } +/// Translates the `context.get`/`context.set` slot type recorded by the +/// validator into the `wasm_encoder::ValType` consumed by the component +/// builder. Only `i32` and `i64` make it past the validator today; anything +/// else is a bug. +fn context_val_type(ty: wasmparser::ValType) -> ValType { + match ty { + wasmparser::ValType::I32 => ValType::I32, + wasmparser::ValType::I64 => ValType::I64, + _ => unreachable!("context.get/set slot type should have been rejected by the validator"), + } +} + fn import_func_name(f: &Function) -> String { match f.kind { FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => { @@ -1977,12 +1989,12 @@ impl<'a> EncodingState<'a> { let index = self.component.waitable_join(); Ok((ExportKind::Func, index)) } - Import::ContextGet(n) => { - let index = self.component.context_get(*n); + Import::ContextGet { ty, slot } => { + let index = self.component.context_get(context_val_type(*ty), *slot); Ok((ExportKind::Func, index)) } - Import::ContextSet(n) => { - let index = self.component.context_set(*n); + Import::ContextSet { ty, slot } => { + let index = self.component.context_set(context_val_type(*ty), *slot); Ok((ExportKind::Func, index)) } Import::ExportedTaskCancel => { @@ -2630,8 +2642,8 @@ impl<'a> Shims<'a> { | Import::WaitableSetNew | Import::WaitableSetDrop | Import::WaitableJoin - | Import::ContextGet(_) - | Import::ContextSet(_) + | Import::ContextGet { .. } + | Import::ContextSet { .. } | Import::ThreadIndex | Import::ThreadSuspendToSuspended { .. } | Import::ThreadSuspend { .. } diff --git a/crates/wit-component/src/encoding/world.rs b/crates/wit-component/src/encoding/world.rs index 66b412e767..84cf92dd84 100644 --- a/crates/wit-component/src/encoding/world.rs +++ b/crates/wit-component/src/encoding/world.rs @@ -415,8 +415,8 @@ impl<'a> ComponentWorld<'a> { | Import::MainModuleMemory | Import::MainModuleExport { .. } | Import::Item(_) - | Import::ContextGet(_) - | Import::ContextSet(_) + | Import::ContextGet { .. } + | Import::ContextSet { .. } | Import::BackpressureInc | Import::BackpressureDec | Import::WaitableSetNew diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs index a9d3d3e664..268185eece 100644 --- a/crates/wit-component/src/validation.rs +++ b/crates/wit-component/src/validation.rs @@ -276,9 +276,19 @@ pub enum Import { ExportedTaskCancel, /// The `context.get` intrinsic for the nth slot of storage. - ContextGet(u32), + ContextGet { + /// The value type of the slot (`i32` or `i64`). + ty: ValType, + /// The index of the storage slot. + slot: u32, + }, /// The `context.set` intrinsic for the nth slot of storage. - ContextSet(u32), + ContextSet { + /// The value type of the slot (`i32` or `i64`). + ty: ValType, + /// The index of the storage slot. + slot: u32, + }, /// A `canon backpressure.inc` intrinsic. BackpressureInc, @@ -685,15 +695,15 @@ impl ImportMap { return Ok(Import::ErrorContextDebugMessage { encoding }); } - if let Some(i) = names.context_get(name) { - let expected = FuncType::new([], [ValType::I32]); + if let Some((slot_ty, slot)) = names.context_get(name) { + let expected = FuncType::new([], [slot_ty]); validate_func_sig(name, &expected, ty)?; - return Ok(Import::ContextGet(i)); + return Ok(Import::ContextGet { ty: slot_ty, slot }); } - if let Some(i) = names.context_set(name) { - let expected = FuncType::new([ValType::I32], []); + if let Some((slot_ty, slot)) = names.context_set(name) { + let expected = FuncType::new([slot_ty], []); validate_func_sig(name, &expected, ty)?; - return Ok(Import::ContextSet(i)); + return Ok(Import::ContextSet { ty: slot_ty, slot }); } if names.thread_index(name) { let expected = FuncType::new([], [ValType::I32]); @@ -1583,8 +1593,8 @@ trait NameMangling { fn error_context_new(&self, name: &str) -> Option; fn error_context_debug_message(&self, name: &str) -> Option; fn error_context_drop(&self, name: &str) -> bool; - fn context_get(&self, name: &str) -> Option; - fn context_set(&self, name: &str) -> Option; + fn context_get(&self, name: &str) -> Option<(ValType, u32)>; + fn context_set(&self, name: &str) -> Option<(ValType, u32)>; fn future_new(&self, lookup_context: &PayloadLookupContext, name: &str) -> Option; fn future_write( &self, @@ -1776,10 +1786,10 @@ impl NameMangling for Standard { fn error_context_drop(&self, _name: &str) -> bool { false } - fn context_get(&self, _name: &str) -> Option { + fn context_get(&self, _name: &str) -> Option<(ValType, u32)> { None } - fn context_set(&self, _name: &str) -> Option { + fn context_set(&self, _name: &str) -> Option<(ValType, u32)> { None } fn thread_index(&self, _name: &str) -> bool { @@ -2235,13 +2245,11 @@ impl NameMangling for Legacy { fn error_context_drop(&self, name: &str) -> bool { name == "[error-context-drop]" } - fn context_get(&self, name: &str) -> Option { - let (n, rest) = prefixed_integer(name, "[context-get-")?; - if rest.is_empty() { Some(n) } else { None } + fn context_get(&self, name: &str) -> Option<(ValType, u32)> { + parse_context_name(name, "[context-get-") } - fn context_set(&self, name: &str) -> Option { - let (n, rest) = prefixed_integer(name, "[context-set-")?; - if rest.is_empty() { Some(n) } else { None } + fn context_set(&self, name: &str) -> Option<(ValType, u32)> { + parse_context_name(name, "[context-set-") } fn thread_index(&self, name: &str) -> bool { name == "[thread-index]" @@ -2700,11 +2708,23 @@ fn prefixed_intrinsic<'a>(name: &'a str, prefix: &str) -> Option<(&'a str, &'a s Some((&suffix[..index], rest)) } -/// Matches `name` as `[${prefix}N]...`, and if found returns `(N, "...")` -fn prefixed_integer<'a>(name: &'a str, prefix: &str) -> Option<(u32, &'a str)> { +/// Parses a `[context-get-]` / `[context-set-]` style name, optionally +/// carrying a type width infix: `[context-get-i64-]`. +/// +/// Returns the value type together with the numeric slot. Additional type +/// widths can be added here by extending the match below without touching any +/// other layer of the pipeline. +fn parse_context_name(name: &str, prefix: &str) -> Option<(ValType, u32)> { let (suffix, rest) = prefixed_intrinsic(name, prefix)?; - let n = suffix.parse().ok()?; - Some((n, rest)) + if !rest.is_empty() { + return None; + } + let (ty, slot) = match suffix.split_once('-') { + Some(("i64", slot)) => (ValType::I64, slot), + _ => (ValType::I32, suffix), + }; + let slot = slot.parse().ok()?; + Some((ty, slot)) } fn get_function<'a>( diff --git a/tests/cli/component-model/async/task-builtins.wast b/tests/cli/component-model/async/task-builtins.wast index 7cc328897e..ae8cca69f3 100644 --- a/tests/cli/component-model/async/task-builtins.wast +++ b/tests/cli/component-model/async/task-builtins.wast @@ -344,29 +344,41 @@ (component (core func (canon context.set i32 100))) "immediate must be zero: 100") -(assert_malformed - (component quote - "(core func (canon context.get i64 100))") - "expected keyword `i32`") -(assert_malformed - (component quote - "(core func (canon context.set i64 100))") - "expected keyword `i32`") +;; `i64` is accepted syntactically but requires the component model 64-bit +;; feature (`cm64`), which is not enabled by this test. +(assert_invalid + (component + (core func (canon context.get i64 0))) + "64-bit `context.get` requires the component model 64-bit feature") +(assert_invalid + (component + (core func (canon context.set i64 0))) + "64-bit `context.set` requires the component model 64-bit feature") -(assert_malformed +(assert_invalid (component binary "\00asm" "\0d\00\01\00" ;; component header "\08\04" ;; canonicals section, 4 bytes "\01" ;; 1 count "\0a\7e\00") ;; context.get i64 0 - "invalid leading byte (0x7e) for context.get") -(assert_malformed + "64-bit `context.get` requires the component model 64-bit feature") +(assert_invalid (component binary "\00asm" "\0d\00\01\00" ;; component header "\08\04" ;; canonicals section, 4 bytes "\01" ;; 1 count "\0b\7e\00") ;; context.set i64 0 - "invalid leading byte (0x7e) for context.set") + "64-bit `context.set` requires the component model 64-bit feature") + +;; Other value types (e.g. `f32`) are rejected regardless of the `cm64` feature. +(assert_invalid + (component + (core func (canon context.get f32 0))) + "`context.get` only supports `i32` or `i64`") +(assert_invalid + (component + (core func (canon context.set f32 0))) + "`context.set` only supports `i32` or `i64`") ;; different forms of canonical intrinsics diff --git a/tests/cli/component-model/memory64/context.wast b/tests/cli/component-model/memory64/context.wast new file mode 100644 index 0000000000..4eb37f43f5 --- /dev/null +++ b/tests/cli/component-model/memory64/context.wast @@ -0,0 +1,61 @@ +;; RUN: wast --assert default --snapshot tests/snapshots % -f cm-async,cm64 + +;; i64 context.{get,set} slots are accepted when the component model 64-bit +;; feature is enabled. +(component + (core func $get0 (canon context.get i64 0)) + (core func $set0 (canon context.set i64 0)) + + (core module $m + (import "" "get0" (func (result i64))) + (import "" "set0" (func (param i64))) + ) + (core instance (instantiate $m + (with "" (instance + (export "get0" (func $get0)) + (export "set0" (func $set0)) + )) + )) +) + +;; Mixed i32 and i64 slots round-trip independently. +(component + (core func (canon context.get i32 0)) + (core func (canon context.set i32 0)) + (core func (canon context.get i64 0)) + (core func (canon context.set i64 0)) +) + +;; Different canon forms also accept i64. +(component + (core func (canon context.get i64 0)) + (canon context.get i64 0 (core func)) + (core func (canon context.set i64 0)) + (canon context.set i64 0 (core func)) +) + +;; Signature must match the declared slot width. +(assert_invalid + (component + (core module $m (import "" "" (func (result i32)))) + (core func $f (canon context.get i64 0)) + (core instance (instantiate $m (with "" (instance (export "" (func $f)))))) + ) + "found: (func (result i64))") + +(assert_invalid + (component + (core module $m (import "" "" (func (param i32)))) + (core func $f (canon context.set i64 0)) + (core instance (instantiate $m (with "" (instance (export "" (func $f)))))) + ) + "found: (func (param i64))") + +;; Binary form: `0x7e` type byte is i64. +(component binary + "\00asm" "\0d\00\01\00" ;; component header + "\08\07" ;; canonicals section, 7 bytes + "\02" ;; 2 entries + "\0a\7e\00" ;; context.get i64 0 + "\0b\7e\00" ;; context.set i64 0 +) From fda9c0f23d8c5cc4ffea6e1d6bfbde6c661aa8e2 Mon Sep 17 00:00:00 2001 From: Michael Weigelt Date: Fri, 24 Apr 2026 12:33:07 +0000 Subject: [PATCH 2/7] details --- crates/wasm-encoder/src/component/builder.rs | 15 ++++++--------- .../src/readers/component/canonicals.rs | 4 ++-- crates/wasmparser/src/validator/component.rs | 8 ++------ crates/wit-component/src/encoding.rs | 6 ++++-- crates/wit-component/src/validation.rs | 10 +++++----- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/crates/wasm-encoder/src/component/builder.rs b/crates/wasm-encoder/src/component/builder.rs index 8ff65c1811..adcf5bdcdc 100644 --- a/crates/wasm-encoder/src/component/builder.rs +++ b/crates/wasm-encoder/src/component/builder.rs @@ -506,18 +506,18 @@ impl ComponentBuilder { self.core_funcs.add(Some("task.cancel")) } - /// Declares a new `context.get` intrinsic with the given value type. + /// Declares a new `context.get` intrinsic. pub fn context_get(&mut self, ty: ValType, i: u32) -> u32 { self.canonical_functions().context_get(ty, i); self.core_funcs - .add(Some(&format!("context.get {} {i}", val_type_name(ty)))) + .add(Some(&format!("context.get {} {i}", type_name(ty)))) } - /// Declares a new `context.set` intrinsic with the given value type. + /// Declares a new `context.set` intrinsic. pub fn context_set(&mut self, ty: ValType, i: u32) -> u32 { self.canonical_functions().context_set(ty, i); self.core_funcs - .add(Some(&format!("context.set {} {i}", val_type_name(ty)))) + .add(Some(&format!("context.set {} {i}", type_name(ty)))) } /// Declares a new `thread.yield` intrinsic. @@ -835,13 +835,10 @@ impl Namespace { } } -fn val_type_name(ty: ValType) -> &'static str { +fn type_name(ty: ValType) -> &'static str { match ty { ValType::I32 => "i32", ValType::I64 => "i64", - ValType::F32 => "f32", - ValType::F64 => "f64", - ValType::V128 => "v128", - ValType::Ref(_) => "ref", + _ => "", } } diff --git a/crates/wasmparser/src/readers/component/canonicals.rs b/crates/wasmparser/src/readers/component/canonicals.rs index aaa6b9c67d..7876805d4b 100644 --- a/crates/wasmparser/src/readers/component/canonicals.rs +++ b/crates/wasmparser/src/readers/component/canonicals.rs @@ -109,7 +109,7 @@ pub enum CanonicalFunction { TaskCancel, /// A `context.get` intrinsic for the `i`th slot of task-local storage. ContextGet { - /// The value type of the slot. Currently only `ValType::I32` and + /// The type of the slot. Currently only `ValType::I32` and /// `ValType::I64` are accepted by the validator (with `I64` gated on /// the component-model 64-bit feature). ty: ValType, @@ -118,7 +118,7 @@ pub enum CanonicalFunction { }, /// A `context.set` intrinsic for the `i`th slot of task-local storage. ContextSet { - /// The value type of the slot. Currently only `ValType::I32` and + /// The type of the slot. Currently only `ValType::I32` and /// `ValType::I64` are accepted by the validator (with `I64` gated on /// the component-model 64-bit feature). ty: ValType, diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 4c57421dd7..6ae9da4601 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -1203,12 +1203,8 @@ impl ComponentState { self.task_return(&result, &options, types, offset) } CanonicalFunction::TaskCancel => self.task_cancel(types, offset), - CanonicalFunction::ContextGet { ty, slot } => { - self.context_get(ty, slot, types, offset) - } - CanonicalFunction::ContextSet { ty, slot } => { - self.context_set(ty, slot, types, offset) - } + CanonicalFunction::ContextGet { ty, slot } => self.context_get(ty, slot, types, offset), + CanonicalFunction::ContextSet { ty, slot } => self.context_set(ty, slot, types, offset), CanonicalFunction::ThreadYield { cancellable: _ } => self.thread_yield(types, offset), CanonicalFunction::SubtaskDrop => self.subtask_drop(types, offset), CanonicalFunction::SubtaskCancel { async_ } => { diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index 23e16baa7d..2df05eb874 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -118,13 +118,15 @@ fn to_val_type(ty: &WasmType) -> ValType { /// Translates the `context.get`/`context.set` slot type recorded by the /// validator into the `wasm_encoder::ValType` consumed by the component -/// builder. Only `i32` and `i64` make it past the validator today; anything +/// builder. Only `i32` and `i64` currently make it past the validator; anything /// else is a bug. fn context_val_type(ty: wasmparser::ValType) -> ValType { match ty { wasmparser::ValType::I32 => ValType::I32, wasmparser::ValType::I64 => ValType::I64, - _ => unreachable!("context.get/set slot type should have been rejected by the validator"), + _ => unreachable!( + "context.get/set slot type `{ty}` should have been rejected by the validator" + ), } } diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs index 268185eece..12bc4c1450 100644 --- a/crates/wit-component/src/validation.rs +++ b/crates/wit-component/src/validation.rs @@ -277,14 +277,14 @@ pub enum Import { /// The `context.get` intrinsic for the nth slot of storage. ContextGet { - /// The value type of the slot (`i32` or `i64`). + /// The type of the slot (`i32` or `i64`). ty: ValType, /// The index of the storage slot. slot: u32, }, /// The `context.set` intrinsic for the nth slot of storage. ContextSet { - /// The value type of the slot (`i32` or `i64`). + /// The type of the slot (`i32` or `i64`). ty: ValType, /// The index of the storage slot. slot: u32, @@ -2712,8 +2712,7 @@ fn prefixed_intrinsic<'a>(name: &'a str, prefix: &str) -> Option<(&'a str, &'a s /// carrying a type width infix: `[context-get-i64-]`. /// /// Returns the value type together with the numeric slot. Additional type -/// widths can be added here by extending the match below without touching any -/// other layer of the pipeline. +/// widths can be added here by extending the match below. fn parse_context_name(name: &str, prefix: &str) -> Option<(ValType, u32)> { let (suffix, rest) = prefixed_intrinsic(name, prefix)?; if !rest.is_empty() { @@ -2721,7 +2720,8 @@ fn parse_context_name(name: &str, prefix: &str) -> Option<(ValType, u32)> { } let (ty, slot) = match suffix.split_once('-') { Some(("i64", slot)) => (ValType::I64, slot), - _ => (ValType::I32, suffix), + Some(("i32", slot)) => (ValType::I32, slot), + _ => (ValType::I32, "unreachable"), }; let slot = slot.parse().ok()?; Some((ty, slot)) From 3ad01514c14b20273f310666b56f1ecc7dc81357 Mon Sep 17 00:00:00 2001 From: Michael Weigelt Date: Fri, 24 Apr 2026 13:20:58 +0000 Subject: [PATCH 3/7] tests --- .../cli/component-model/memory64/context.wast | 24 +++++--- .../async/task-builtins.wast.json | 54 +++++++++++------- .../async/task-builtins.wast/44.print | 56 +++++++++++++++++++ .../async/task-builtins.wast/45.print | 4 ++ .../memory64/context.wast.json | 51 +++++++++++++++++ .../memory64/context.wast/0.print | 18 ++++++ .../memory64/context.wast/1.print | 6 ++ .../memory64/context.wast/6.print | 4 ++ 8 files changed, 189 insertions(+), 28 deletions(-) create mode 100644 tests/snapshots/cli/component-model/async/task-builtins.wast/44.print create mode 100644 tests/snapshots/cli/component-model/async/task-builtins.wast/45.print create mode 100644 tests/snapshots/cli/component-model/memory64/context.wast.json create mode 100644 tests/snapshots/cli/component-model/memory64/context.wast/0.print create mode 100644 tests/snapshots/cli/component-model/memory64/context.wast/1.print create mode 100644 tests/snapshots/cli/component-model/memory64/context.wast/6.print diff --git a/tests/cli/component-model/memory64/context.wast b/tests/cli/component-model/memory64/context.wast index 4eb37f43f5..fd82df477c 100644 --- a/tests/cli/component-model/memory64/context.wast +++ b/tests/cli/component-model/memory64/context.wast @@ -26,14 +26,6 @@ (core func (canon context.set i64 0)) ) -;; Different canon forms also accept i64. -(component - (core func (canon context.get i64 0)) - (canon context.get i64 0 (core func)) - (core func (canon context.set i64 0)) - (canon context.set i64 0 (core func)) -) - ;; Signature must match the declared slot width. (assert_invalid (component @@ -51,6 +43,22 @@ ) "found: (func (param i64))") +(assert_invalid + (component + (core module $m (import "" "" (func (result i64)))) + (core func $f (canon context.get i32 0)) + (core instance (instantiate $m (with "" (instance (export "" (func $f)))))) + ) + "found: (func (result i32))") + +(assert_invalid + (component + (core module $m (import "" "" (func (param i64)))) + (core func $f (canon context.set i32 0)) + (core instance (instantiate $m (with "" (instance (export "" (func $f)))))) + ) + "found: (func (param i32))") + ;; Binary form: `0x7e` type byte is i64. (component binary "\00asm" "\0d\00\01\00" ;; component header diff --git a/tests/snapshots/cli/component-model/async/task-builtins.wast.json b/tests/snapshots/cli/component-model/async/task-builtins.wast.json index 195e931af7..13cfdbf05c 100644 --- a/tests/snapshots/cli/component-model/async/task-builtins.wast.json +++ b/tests/snapshots/cli/component-model/async/task-builtins.wast.json @@ -251,43 +251,57 @@ "text": "immediate must be zero: 100" }, { - "type": "assert_malformed", - "line": 348, - "filename": "task-builtins.38.wat", - "module_type": "text", - "text": "expected keyword `i32`" + "type": "assert_invalid", + "line": 350, + "filename": "task-builtins.38.wasm", + "module_type": "binary", + "text": "64-bit `context.get` requires the component model 64-bit feature" }, { - "type": "assert_malformed", - "line": 352, - "filename": "task-builtins.39.wat", - "module_type": "text", - "text": "expected keyword `i32`" + "type": "assert_invalid", + "line": 354, + "filename": "task-builtins.39.wasm", + "module_type": "binary", + "text": "64-bit `context.set` requires the component model 64-bit feature" }, { - "type": "assert_malformed", - "line": 357, + "type": "assert_invalid", + "line": 359, "filename": "task-builtins.40.wasm", "module_type": "binary", - "text": "invalid leading byte (0x7e) for context.get" + "text": "64-bit `context.get` requires the component model 64-bit feature" }, { - "type": "assert_malformed", - "line": 364, + "type": "assert_invalid", + "line": 366, "filename": "task-builtins.41.wasm", "module_type": "binary", - "text": "invalid leading byte (0x7e) for context.set" + "text": "64-bit `context.set` requires the component model 64-bit feature" }, { - "type": "module", - "line": 373, + "type": "assert_invalid", + "line": 375, "filename": "task-builtins.42.wasm", + "module_type": "binary", + "text": "`context.get` only supports `i32` or `i64`" + }, + { + "type": "assert_invalid", + "line": 379, + "filename": "task-builtins.43.wasm", + "module_type": "binary", + "text": "`context.set` only supports `i32` or `i64`" + }, + { + "type": "module", + "line": 385, + "filename": "task-builtins.44.wasm", "module_type": "binary" }, { "type": "module", - "line": 433, - "filename": "task-builtins.43.wasm", + "line": 445, + "filename": "task-builtins.45.wasm", "module_type": "binary" } ] diff --git a/tests/snapshots/cli/component-model/async/task-builtins.wast/44.print b/tests/snapshots/cli/component-model/async/task-builtins.wast/44.print new file mode 100644 index 0000000000..ec9bb3db84 --- /dev/null +++ b/tests/snapshots/cli/component-model/async/task-builtins.wast/44.print @@ -0,0 +1,56 @@ +(component + (core func (;0;) (canon backpressure.inc)) + (core func (;1;) (canon backpressure.inc)) + (core func (;2;) (canon backpressure.dec)) + (core func (;3;) (canon backpressure.dec)) + (core func (;4;) (canon task.return)) + (core func (;5;) (canon task.return)) + (core func (;6;) (canon task.cancel)) + (core func (;7;) (canon task.cancel)) + (core func (;8;) (canon subtask.drop)) + (core func (;9;) (canon subtask.drop)) + (core func (;10;) (canon subtask.cancel)) + (core func (;11;) (canon subtask.cancel)) + (core module $m (;0;) + (memory (;0;) 1) + (export "m" (memory 0)) + ) + (core instance $i (;0;) (instantiate $m)) + (alias core export $i "m" (core memory $m (;0;))) + (type $s (;0;) (stream)) + (type $f (;1;) (future)) + (core func (;12;) (canon future.new $f)) + (core func (;13;) (canon future.new $f)) + (core func (;14;) (canon stream.new $s)) + (core func (;15;) (canon stream.new $s)) + (core func (;16;) (canon future.cancel-read $f)) + (core func (;17;) (canon future.cancel-read $f)) + (core func (;18;) (canon stream.cancel-read $s)) + (core func (;19;) (canon stream.cancel-read $s)) + (core func (;20;) (canon future.cancel-write $f)) + (core func (;21;) (canon future.cancel-write $f)) + (core func (;22;) (canon stream.cancel-write $s)) + (core func (;23;) (canon stream.cancel-write $s)) + (core func (;24;) (canon future.drop-readable $f)) + (core func (;25;) (canon future.drop-readable $f)) + (core func (;26;) (canon future.drop-writable $f)) + (core func (;27;) (canon future.drop-writable $f)) + (core func (;28;) (canon stream.drop-readable $s)) + (core func (;29;) (canon stream.drop-readable $s)) + (core func (;30;) (canon stream.drop-writable $s)) + (core func (;31;) (canon stream.drop-writable $s)) + (core func (;32;) (canon future.read $f (memory $m))) + (core func (;33;) (canon future.read $f (memory $m))) + (core func (;34;) (canon future.write $f (memory $m))) + (core func (;35;) (canon future.write $f (memory $m))) + (core func (;36;) (canon stream.read $s (memory $m))) + (core func (;37;) (canon stream.read $s (memory $m))) + (core func (;38;) (canon stream.write $s (memory $m))) + (core func (;39;) (canon stream.write $s (memory $m))) + (core func (;40;) (canon context.get i32 0)) + (core func (;41;) (canon context.get i32 0)) + (core func (;42;) (canon context.set i32 0)) + (core func (;43;) (canon context.set i32 0)) + (core func (;44;) (canon thread.yield)) + (core func (;45;) (canon thread.yield)) +) diff --git a/tests/snapshots/cli/component-model/async/task-builtins.wast/45.print b/tests/snapshots/cli/component-model/async/task-builtins.wast/45.print new file mode 100644 index 0000000000..28dfb3b000 --- /dev/null +++ b/tests/snapshots/cli/component-model/async/task-builtins.wast/45.print @@ -0,0 +1,4 @@ +(component + (type (;0;) (stream u8)) + (core func (;0;) (canon task.return (result 0))) +) diff --git a/tests/snapshots/cli/component-model/memory64/context.wast.json b/tests/snapshots/cli/component-model/memory64/context.wast.json new file mode 100644 index 0000000000..e2673f2b59 --- /dev/null +++ b/tests/snapshots/cli/component-model/memory64/context.wast.json @@ -0,0 +1,51 @@ +{ + "source_filename": "tests/cli/component-model/memory64/context.wast", + "commands": [ + { + "type": "module", + "line": 5, + "filename": "context.0.wasm", + "module_type": "binary" + }, + { + "type": "module", + "line": 22, + "filename": "context.1.wasm", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 31, + "filename": "context.2.wasm", + "module_type": "binary", + "text": "found: (func (result i64))" + }, + { + "type": "assert_invalid", + "line": 39, + "filename": "context.3.wasm", + "module_type": "binary", + "text": "found: (func (param i64))" + }, + { + "type": "assert_invalid", + "line": 47, + "filename": "context.4.wasm", + "module_type": "binary", + "text": "found: (func (result i32))" + }, + { + "type": "assert_invalid", + "line": 55, + "filename": "context.5.wasm", + "module_type": "binary", + "text": "found: (func (param i32))" + }, + { + "type": "module", + "line": 63, + "filename": "context.6.wasm", + "module_type": "binary" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/cli/component-model/memory64/context.wast/0.print b/tests/snapshots/cli/component-model/memory64/context.wast/0.print new file mode 100644 index 0000000000..30493a358a --- /dev/null +++ b/tests/snapshots/cli/component-model/memory64/context.wast/0.print @@ -0,0 +1,18 @@ +(component + (core func $get0 (;0;) (canon context.get i64 0)) + (core func $set0 (;1;) (canon context.set i64 0)) + (core module $m (;0;) + (type (;0;) (func (result i64))) + (type (;1;) (func (param i64))) + (import "" "get0" (func (;0;) (type 0))) + (import "" "set0" (func (;1;) (type 1))) + ) + (core instance (;0;) + (export "get0" (func $get0)) + (export "set0" (func $set0)) + ) + (core instance (;1;) (instantiate $m + (with "" (instance 0)) + ) + ) +) diff --git a/tests/snapshots/cli/component-model/memory64/context.wast/1.print b/tests/snapshots/cli/component-model/memory64/context.wast/1.print new file mode 100644 index 0000000000..d46be4e689 --- /dev/null +++ b/tests/snapshots/cli/component-model/memory64/context.wast/1.print @@ -0,0 +1,6 @@ +(component + (core func (;0;) (canon context.get i32 0)) + (core func (;1;) (canon context.set i32 0)) + (core func (;2;) (canon context.get i64 0)) + (core func (;3;) (canon context.set i64 0)) +) diff --git a/tests/snapshots/cli/component-model/memory64/context.wast/6.print b/tests/snapshots/cli/component-model/memory64/context.wast/6.print new file mode 100644 index 0000000000..80914678a6 --- /dev/null +++ b/tests/snapshots/cli/component-model/memory64/context.wast/6.print @@ -0,0 +1,4 @@ +(component + (core func (;0;) (canon context.get i64 0)) + (core func (;1;) (canon context.set i64 0)) +) From baf0cd575fa956510c24882cc30252f64e6cc4fb Mon Sep 17 00:00:00 2001 From: Michael Weigelt Date: Mon, 27 Apr 2026 13:31:08 +0000 Subject: [PATCH 4/7] address comments --- crates/wasm-encoder/src/component/builder.rs | 14 ++------------ crates/wast/src/component/resolve.rs | 3 ++- crates/wit-component/src/encoding.rs | 18 ++---------------- 3 files changed, 6 insertions(+), 29 deletions(-) diff --git a/crates/wasm-encoder/src/component/builder.rs b/crates/wasm-encoder/src/component/builder.rs index adcf5bdcdc..c2065a249a 100644 --- a/crates/wasm-encoder/src/component/builder.rs +++ b/crates/wasm-encoder/src/component/builder.rs @@ -509,15 +509,13 @@ impl ComponentBuilder { /// Declares a new `context.get` intrinsic. pub fn context_get(&mut self, ty: ValType, i: u32) -> u32 { self.canonical_functions().context_get(ty, i); - self.core_funcs - .add(Some(&format!("context.get {} {i}", type_name(ty)))) + self.core_funcs.add(Some(&format!("context.get {i}"))) } /// Declares a new `context.set` intrinsic. pub fn context_set(&mut self, ty: ValType, i: u32) -> u32 { self.canonical_functions().context_set(ty, i); - self.core_funcs - .add(Some(&format!("context.set {} {i}", type_name(ty)))) + self.core_funcs.add(Some(&format!("context.set {i}"))) } /// Declares a new `thread.yield` intrinsic. @@ -834,11 +832,3 @@ impl Namespace { ret } } - -fn type_name(ty: ValType) -> &'static str { - match ty { - ValType::I32 => "i32", - ValType::I64 => "i64", - _ => "", - } -} diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs index 34ccf4726b..81bbbf20d7 100644 --- a/crates/wast/src/component/resolve.rs +++ b/crates/wast/src/component/resolve.rs @@ -408,7 +408,8 @@ impl<'a> Resolver<'a> { } self.canon_opts(&mut info.opts)?; } - CoreFuncKind::ContextGet(..) | CoreFuncKind::ContextSet(..) => {} + CoreFuncKind::ContextGet(..) => {} + CoreFuncKind::ContextSet(..) => {} CoreFuncKind::StreamNew(info) => { self.resolve_ns(&mut info.ty, Ns::Type)?; } diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index 2df05eb874..e16e70cb3d 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -116,20 +116,6 @@ fn to_val_type(ty: &WasmType) -> ValType { } } -/// Translates the `context.get`/`context.set` slot type recorded by the -/// validator into the `wasm_encoder::ValType` consumed by the component -/// builder. Only `i32` and `i64` currently make it past the validator; anything -/// else is a bug. -fn context_val_type(ty: wasmparser::ValType) -> ValType { - match ty { - wasmparser::ValType::I32 => ValType::I32, - wasmparser::ValType::I64 => ValType::I64, - _ => unreachable!( - "context.get/set slot type `{ty}` should have been rejected by the validator" - ), - } -} - fn import_func_name(f: &Function) -> String { match f.kind { FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => { @@ -1992,11 +1978,11 @@ impl<'a> EncodingState<'a> { Ok((ExportKind::Func, index)) } Import::ContextGet { ty, slot } => { - let index = self.component.context_get(context_val_type(*ty), *slot); + let index = self.component.context_get((*ty).try_into()?, *slot); Ok((ExportKind::Func, index)) } Import::ContextSet { ty, slot } => { - let index = self.component.context_set(context_val_type(*ty), *slot); + let index = self.component.context_set((*ty).try_into()?, *slot); Ok((ExportKind::Func, index)) } Import::ExportedTaskCancel => { From 822bec1ea01c1ff6d85775899d13518e3c81db64 Mon Sep 17 00:00:00 2001 From: Michael Weigelt Date: Mon, 27 Apr 2026 13:48:51 +0000 Subject: [PATCH 5/7] resolve ref --- crates/wast/src/component/resolve.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs index 81bbbf20d7..4f1dff6c53 100644 --- a/crates/wast/src/component/resolve.rs +++ b/crates/wast/src/component/resolve.rs @@ -408,8 +408,24 @@ impl<'a> Resolver<'a> { } self.canon_opts(&mut info.opts)?; } - CoreFuncKind::ContextGet(..) => {} - CoreFuncKind::ContextSet(..) => {} + CoreFuncKind::ContextGet(ty, _) => match ty { + ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {} + ValType::Ref(r) => match &mut r.heap { + core::HeapType::Abstract { .. } => {} + core::HeapType::Concrete(id) | core::HeapType::Exact(id) => { + self.resolve_ns(id, Ns::Type)?; + } + }, + }, + CoreFuncKind::ContextSet(ty, _) => match ty { + ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {} + ValType::Ref(r) => match &mut r.heap { + core::HeapType::Abstract { .. } => {} + core::HeapType::Concrete(id) | core::HeapType::Exact(id) => { + self.resolve_ns(id, Ns::Type)?; + } + }, + }, CoreFuncKind::StreamNew(info) => { self.resolve_ns(&mut info.ty, Ns::Type)?; } From 3697e1f10399a3c67d044bb54f03f9d613ac0c2a Mon Sep 17 00:00:00 2001 From: Michael Weigelt Date: Mon, 27 Apr 2026 15:04:24 +0000 Subject: [PATCH 6/7] factor out ref --- crates/wast/src/component/resolve.rs | 42 ++++++++++------------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs index 4f1dff6c53..cca50c4f11 100644 --- a/crates/wast/src/component/resolve.rs +++ b/crates/wast/src/component/resolve.rs @@ -408,24 +408,8 @@ impl<'a> Resolver<'a> { } self.canon_opts(&mut info.opts)?; } - CoreFuncKind::ContextGet(ty, _) => match ty { - ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {} - ValType::Ref(r) => match &mut r.heap { - core::HeapType::Abstract { .. } => {} - core::HeapType::Concrete(id) | core::HeapType::Exact(id) => { - self.resolve_ns(id, Ns::Type)?; - } - }, - }, - CoreFuncKind::ContextSet(ty, _) => match ty { - ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {} - ValType::Ref(r) => match &mut r.heap { - core::HeapType::Abstract { .. } => {} - core::HeapType::Concrete(id) | core::HeapType::Exact(id) => { - self.resolve_ns(id, Ns::Type)?; - } - }, - }, + CoreFuncKind::ContextGet(ty, _) => self.ref_type(ty)?, + CoreFuncKind::ContextSet(ty, _) => self.ref_type(ty)?, CoreFuncKind::StreamNew(info) => { self.resolve_ns(&mut info.ty, Ns::Type)?; } @@ -503,6 +487,18 @@ impl<'a> Resolver<'a> { Ok(()) } + fn ref_type(&mut self, ty: &mut ValType<'a>) -> Result<(), Error> { + Ok(match ty { + ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {} + ValType::Ref(r) => match &mut r.heap { + core::HeapType::Abstract { .. } => {} + core::HeapType::Concrete(id) | core::HeapType::Exact(id) => { + self.resolve_ns(id, Ns::Type)?; + } + }, + }) + } + fn canon_opts(&mut self, opts: &mut [CanonOpt<'a>]) -> Result<(), Error> { for opt in opts { match opt { @@ -675,15 +671,7 @@ impl<'a> Resolver<'a> { self.stack.pop(); } TypeDef::Resource(r) => { - match &mut r.rep { - ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {} - ValType::Ref(r) => match &mut r.heap { - core::HeapType::Abstract { .. } => {} - core::HeapType::Concrete(id) | core::HeapType::Exact(id) => { - self.resolve_ns(id, Ns::Type)?; - } - }, - } + self.ref_type(&mut r.rep)?; if let Some(dtor) = &mut r.dtor { self.core_item_ref(dtor)?; } From 4c5f40b7ede3f99d9e1a98982b0c933962a4a5fe Mon Sep 17 00:00:00 2001 From: Michael Weigelt Date: Mon, 27 Apr 2026 15:38:03 +0000 Subject: [PATCH 7/7] fix --- crates/wit-component/src/validation.rs | 2 +- .../async/task-builtins.wast/42.print | 56 ------------------- .../async/task-builtins.wast/43.print | 4 -- 3 files changed, 1 insertion(+), 61 deletions(-) delete mode 100644 tests/snapshots/cli/component-model/async/task-builtins.wast/42.print delete mode 100644 tests/snapshots/cli/component-model/async/task-builtins.wast/43.print diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs index 12bc4c1450..d7565b5600 100644 --- a/crates/wit-component/src/validation.rs +++ b/crates/wit-component/src/validation.rs @@ -2721,7 +2721,7 @@ fn parse_context_name(name: &str, prefix: &str) -> Option<(ValType, u32)> { let (ty, slot) = match suffix.split_once('-') { Some(("i64", slot)) => (ValType::I64, slot), Some(("i32", slot)) => (ValType::I32, slot), - _ => (ValType::I32, "unreachable"), + _ => (ValType::I32, suffix), }; let slot = slot.parse().ok()?; Some((ty, slot)) diff --git a/tests/snapshots/cli/component-model/async/task-builtins.wast/42.print b/tests/snapshots/cli/component-model/async/task-builtins.wast/42.print deleted file mode 100644 index ec9bb3db84..0000000000 --- a/tests/snapshots/cli/component-model/async/task-builtins.wast/42.print +++ /dev/null @@ -1,56 +0,0 @@ -(component - (core func (;0;) (canon backpressure.inc)) - (core func (;1;) (canon backpressure.inc)) - (core func (;2;) (canon backpressure.dec)) - (core func (;3;) (canon backpressure.dec)) - (core func (;4;) (canon task.return)) - (core func (;5;) (canon task.return)) - (core func (;6;) (canon task.cancel)) - (core func (;7;) (canon task.cancel)) - (core func (;8;) (canon subtask.drop)) - (core func (;9;) (canon subtask.drop)) - (core func (;10;) (canon subtask.cancel)) - (core func (;11;) (canon subtask.cancel)) - (core module $m (;0;) - (memory (;0;) 1) - (export "m" (memory 0)) - ) - (core instance $i (;0;) (instantiate $m)) - (alias core export $i "m" (core memory $m (;0;))) - (type $s (;0;) (stream)) - (type $f (;1;) (future)) - (core func (;12;) (canon future.new $f)) - (core func (;13;) (canon future.new $f)) - (core func (;14;) (canon stream.new $s)) - (core func (;15;) (canon stream.new $s)) - (core func (;16;) (canon future.cancel-read $f)) - (core func (;17;) (canon future.cancel-read $f)) - (core func (;18;) (canon stream.cancel-read $s)) - (core func (;19;) (canon stream.cancel-read $s)) - (core func (;20;) (canon future.cancel-write $f)) - (core func (;21;) (canon future.cancel-write $f)) - (core func (;22;) (canon stream.cancel-write $s)) - (core func (;23;) (canon stream.cancel-write $s)) - (core func (;24;) (canon future.drop-readable $f)) - (core func (;25;) (canon future.drop-readable $f)) - (core func (;26;) (canon future.drop-writable $f)) - (core func (;27;) (canon future.drop-writable $f)) - (core func (;28;) (canon stream.drop-readable $s)) - (core func (;29;) (canon stream.drop-readable $s)) - (core func (;30;) (canon stream.drop-writable $s)) - (core func (;31;) (canon stream.drop-writable $s)) - (core func (;32;) (canon future.read $f (memory $m))) - (core func (;33;) (canon future.read $f (memory $m))) - (core func (;34;) (canon future.write $f (memory $m))) - (core func (;35;) (canon future.write $f (memory $m))) - (core func (;36;) (canon stream.read $s (memory $m))) - (core func (;37;) (canon stream.read $s (memory $m))) - (core func (;38;) (canon stream.write $s (memory $m))) - (core func (;39;) (canon stream.write $s (memory $m))) - (core func (;40;) (canon context.get i32 0)) - (core func (;41;) (canon context.get i32 0)) - (core func (;42;) (canon context.set i32 0)) - (core func (;43;) (canon context.set i32 0)) - (core func (;44;) (canon thread.yield)) - (core func (;45;) (canon thread.yield)) -) diff --git a/tests/snapshots/cli/component-model/async/task-builtins.wast/43.print b/tests/snapshots/cli/component-model/async/task-builtins.wast/43.print deleted file mode 100644 index 28dfb3b000..0000000000 --- a/tests/snapshots/cli/component-model/async/task-builtins.wast/43.print +++ /dev/null @@ -1,4 +0,0 @@ -(component - (type (;0;) (stream u8)) - (core func (;0;) (canon task.return (result 0))) -)