diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3e5bebcd61..df414bbca5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -92,6 +92,13 @@ jobs: # want this to break. - os: ubuntu-latest rust: nightly-2024-02-12 + # test that if `RUST_BACKTRACE=1` is set in the environment that all + # tests with blessed error messages still pass. + - os: ubuntu-latest + rust: default + env: + RUST_BACKTRACE: 1 + env: ${{ matrix.env || fromJSON('{}') }} steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/release-process.yml b/.github/workflows/release-process.yml index d6b9b060b3..a9f9f91477 100644 --- a/.github/workflows/release-process.yml +++ b/.github/workflows/release-process.yml @@ -16,6 +16,10 @@ on: required: false default: 'bump' +permissions: + contents: write + pull-requests: write + jobs: release_process: name: Run the release process diff --git a/Cargo.lock b/Cargo.lock index 844cea6e2f..212235a66a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,7 +295,7 @@ checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" name = "component" version = "0.0.0" dependencies = [ - "wasmprinter 0.216.0", + "wasmprinter 0.217.0", "wat", "wit-bindgen-rt", ] @@ -1593,7 +1593,7 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-compose" -version = "0.216.0" +version = "0.217.0" dependencies = [ "anyhow", "glob", @@ -1607,11 +1607,11 @@ dependencies = [ "serde_derive", "serde_yaml", "smallvec", - "wasm-encoder 0.216.0", - "wasmparser 0.216.0", - "wasmprinter 0.216.0", + "wasm-encoder 0.217.0", + "wasmparser 0.217.0", + "wasmprinter 0.217.0", "wat", - "wit-component 0.216.0", + "wit-component 0.217.0", ] [[package]] @@ -1635,12 +1635,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.216.0" +version = "0.217.0" dependencies = [ "anyhow", "leb128", "tempfile", - "wasmparser 0.216.0", + "wasmparser 0.217.0", ] [[package]] @@ -1661,7 +1661,7 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.216.0" +version = "0.217.0" dependencies = [ "anyhow", "clap", @@ -1670,14 +1670,14 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.216.0", - "wasmparser 0.216.0", + "wasm-encoder 0.217.0", + "wasmparser 0.217.0", "wat", ] [[package]] name = "wasm-mutate" -version = "0.216.0" +version = "0.217.0" dependencies = [ "anyhow", "clap", @@ -1686,9 +1686,9 @@ dependencies = [ "log", "rand", "thiserror", - "wasm-encoder 0.216.0", - "wasmparser 0.216.0", - "wasmprinter 0.216.0", + "wasm-encoder 0.217.0", + "wasmparser 0.217.0", + "wasmprinter 0.217.0", "wat", ] @@ -1705,14 +1705,14 @@ dependencies = [ "num_cpus", "rand", "wasm-mutate", - "wasmparser 0.216.0", - "wasmprinter 0.216.0", + "wasmparser 0.217.0", + "wasmprinter 0.217.0", "wasmtime", ] [[package]] name = "wasm-shrink" -version = "0.216.0" +version = "0.217.0" dependencies = [ "anyhow", "blake3", @@ -1721,14 +1721,14 @@ dependencies = [ "log", "rand", "wasm-mutate", - "wasmparser 0.216.0", - "wasmprinter 0.216.0", + "wasmparser 0.217.0", + "wasmprinter 0.217.0", "wat", ] [[package]] name = "wasm-smith" -version = "0.216.0" +version = "0.217.0" dependencies = [ "anyhow", "arbitrary", @@ -1741,15 +1741,15 @@ dependencies = [ "rand", "serde", "serde_derive", - "wasm-encoder 0.216.0", - "wasmparser 0.216.0", - "wasmprinter 0.216.0", + "wasm-encoder 0.217.0", + "wasmparser 0.217.0", + "wasmprinter 0.217.0", "wat", ] [[package]] name = "wasm-tools" -version = "1.216.0" +version = "1.217.0" dependencies = [ "addr2line", "anyhow", @@ -1774,19 +1774,19 @@ dependencies = [ "tempfile", "termcolor", "wasm-compose", - "wasm-encoder 0.216.0", - "wasm-metadata 0.216.0", + "wasm-encoder 0.217.0", + "wasm-metadata 0.217.0", "wasm-mutate", "wasm-shrink", "wasm-smith", - "wasmparser 0.216.0", - "wasmprinter 0.216.0", + "wasmparser 0.217.0", + "wasmprinter 0.217.0", "wast", "wat", - "wit-component 0.216.0", + "wit-component 0.217.0", "wit-encoder", - "wit-parser 0.216.0", - "wit-smith 0.216.0", + "wit-parser 0.217.0", + "wit-smith", ] [[package]] @@ -1797,8 +1797,8 @@ dependencies = [ "wasm-mutate", "wasm-shrink", "wasm-smith", - "wasmparser 0.216.0", - "wasmprinter 0.216.0", + "wasmparser 0.217.0", + "wasmprinter 0.217.0", "wast", "wat", ] @@ -1813,31 +1813,30 @@ dependencies = [ "libfuzzer-sys", "log", "tempfile", - "wasm-encoder 0.216.0", + "wasm-encoder 0.217.0", "wasm-mutate", "wasm-smith", - "wasmparser 0.216.0", - "wasmprinter 0.216.0", + "wasmparser 0.217.0", + "wasmprinter 0.217.0", "wasmtime", "wast", "wat", "wit-component 0.214.0", - "wit-component 0.216.0", + "wit-component 0.217.0", "wit-parser 0.214.0", - "wit-parser 0.216.0", - "wit-smith 0.214.0", - "wit-smith 0.216.0", + "wit-parser 0.217.0", + "wit-smith", ] [[package]] name = "wasm-wave" -version = "0.216.0" +version = "0.217.0" dependencies = [ "anyhow", "indexmap 2.4.0", "logos", "thiserror", - "wit-parser 0.216.0", + "wit-parser 0.217.0", ] [[package]] @@ -1869,7 +1868,7 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.216.0" +version = "0.217.0" dependencies = [ "ahash", "anyhow", @@ -1883,7 +1882,7 @@ dependencies = [ "rayon", "semver", "serde", - "wasm-encoder 0.216.0", + "wasm-encoder 0.217.0", "wast", "wat", ] @@ -1901,14 +1900,14 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.216.0" +version = "0.217.0" dependencies = [ "anyhow", "diff", "rayon", "tempfile", "termcolor", - "wasmparser 0.216.0", + "wasmparser 0.217.0", "wast", "wat", ] @@ -2110,7 +2109,7 @@ dependencies = [ [[package]] name = "wast" -version = "216.0.0" +version = "217.0.0" dependencies = [ "anyhow", "bumpalo", @@ -2120,14 +2119,14 @@ dependencies = [ "memchr", "rand", "unicode-width", - "wasm-encoder 0.216.0", - "wasmparser 0.216.0", + "wasm-encoder 0.217.0", + "wasmparser 0.217.0", "wat", ] [[package]] name = "wat" -version = "1.216.0" +version = "1.217.0" dependencies = [ "wast", ] @@ -2358,7 +2357,7 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.216.0" +version = "0.217.0" dependencies = [ "anyhow", "bitflags", @@ -2371,26 +2370,26 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.216.0", - "wasm-metadata 0.216.0", - "wasmparser 0.216.0", - "wasmprinter 0.216.0", + "wasm-encoder 0.217.0", + "wasm-metadata 0.217.0", + "wasmparser 0.217.0", + "wasmprinter 0.217.0", "wasmtime", "wast", "wat", - "wit-parser 0.216.0", + "wit-parser 0.217.0", ] [[package]] name = "wit-encoder" -version = "0.216.0" +version = "0.217.0" dependencies = [ "id-arena", "indoc", "pretty_assertions", "semver", "serde", - "wit-parser 0.216.0", + "wit-parser 0.217.0", ] [[package]] @@ -2431,7 +2430,7 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.216.0" +version = "0.217.0" dependencies = [ "anyhow", "env_logger", @@ -2445,9 +2444,9 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.216.0", + "wasmparser 0.217.0", "wat", - "wit-parser 0.216.0", + "wit-parser 0.217.0", ] [[package]] @@ -2458,35 +2457,21 @@ dependencies = [ "env_logger", "libfuzzer-sys", "log", - "wasmprinter 0.216.0", - "wit-parser 0.216.0", + "wasmprinter 0.217.0", + "wit-parser 0.217.0", ] [[package]] name = "wit-smith" -version = "0.214.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cf816019a376a912cafb103677a8d28e3cba86b7671b754281e75f4909e129" -dependencies = [ - "arbitrary", - "indexmap 2.4.0", - "log", - "semver", - "wit-component 0.214.0", - "wit-parser 0.214.0", -] - -[[package]] -name = "wit-smith" -version = "0.216.0" +version = "0.217.0" dependencies = [ "arbitrary", "clap", "indexmap 2.4.0", "log", "semver", - "wit-component 0.216.0", - "wit-parser 0.216.0", + "wit-component 0.217.0", + "wit-parser 0.217.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f722bf0158..0e24742a42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-tools" -version = "1.216.0" +version = "1.217.0" authors = ["The Wasmtime Project Developers"] edition.workspace = true description = "CLI tools for interoperating with WebAssembly files" @@ -60,7 +60,7 @@ manual_strip = 'warn' [workspace.package] edition = '2021' license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" -version = "0.216.0" +version = "0.217.0" # Current policy for wasm-tools is the same as Wasmtime which is that this # number can be no larger than the current stable release of Rust minus 2. rust-version = "1.76.0" @@ -96,20 +96,20 @@ indoc = "2.0.5" gimli = "0.30.0" id-arena = "2" -wasm-compose = { version = "0.216.0", path = "crates/wasm-compose" } -wasm-encoder = { version = "0.216.0", path = "crates/wasm-encoder" } -wasm-metadata = { version = "0.216.0", path = "crates/wasm-metadata" } -wasm-mutate = { version = "0.216.0", path = "crates/wasm-mutate" } -wasm-shrink = { version = "0.216.0", path = "crates/wasm-shrink" } -wasm-smith = { version = "0.216.0", path = "crates/wasm-smith" } -wasmparser = { version = "0.216.0", path = "crates/wasmparser", default-features = false, features = ['std'] } -wasmprinter = { version = "0.216.0", path = "crates/wasmprinter" } -wast = { version = "216.0.0", path = "crates/wast" } -wat = { version = "1.216.0", path = "crates/wat" } -wit-component = { version = "0.216.0", path = "crates/wit-component" } -wit-encoder = { version = "0.216.0", path = "crates/wit-encoder" } -wit-parser = { version = "0.216.0", path = "crates/wit-parser" } -wit-smith = { version = "0.216.0", path = "crates/wit-smith" } +wasm-compose = { version = "0.217.0", path = "crates/wasm-compose" } +wasm-encoder = { version = "0.217.0", path = "crates/wasm-encoder" } +wasm-metadata = { version = "0.217.0", path = "crates/wasm-metadata" } +wasm-mutate = { version = "0.217.0", path = "crates/wasm-mutate" } +wasm-shrink = { version = "0.217.0", path = "crates/wasm-shrink" } +wasm-smith = { version = "0.217.0", path = "crates/wasm-smith" } +wasmparser = { version = "0.217.0", path = "crates/wasmparser", default-features = false, features = ['std'] } +wasmprinter = { version = "0.217.0", path = "crates/wasmprinter" } +wast = { version = "217.0.0", path = "crates/wast" } +wat = { version = "1.217.0", path = "crates/wat" } +wit-component = { version = "0.217.0", path = "crates/wit-component" } +wit-encoder = { version = "0.217.0", path = "crates/wit-encoder" } +wit-parser = { version = "0.217.0", path = "crates/wit-parser" } +wit-smith = { version = "0.217.0", path = "crates/wit-smith" } [dependencies] anyhow = { workspace = true } diff --git a/crates/wasm-encoder/src/component/exports.rs b/crates/wasm-encoder/src/component/exports.rs index e4fcfee055..4d988e574b 100644 --- a/crates/wasm-encoder/src/component/exports.rs +++ b/crates/wasm-encoder/src/component/exports.rs @@ -93,8 +93,7 @@ impl ComponentExportSection { index: u32, ty: Option, ) -> &mut Self { - crate::component::imports::push_extern_name_byte(&mut self.bytes, name); - name.encode(&mut self.bytes); + crate::encode_component_export_name(&mut self.bytes, name); kind.encode(&mut self.bytes); index.encode(&mut self.bytes); match ty { @@ -122,3 +121,9 @@ impl ComponentSection for ComponentExportSection { ComponentSectionId::Export.into() } } + +/// For more information on this see `encode_component_import_name`. +pub(crate) fn encode_component_export_name(bytes: &mut Vec, name: &str) { + bytes.push(0x00); + name.encode(bytes); +} diff --git a/crates/wasm-encoder/src/component/imports.rs b/crates/wasm-encoder/src/component/imports.rs index 6337a0625d..b818a17814 100644 --- a/crates/wasm-encoder/src/component/imports.rs +++ b/crates/wasm-encoder/src/component/imports.rs @@ -131,8 +131,7 @@ impl ComponentImportSection { /// Define an import in the component import section. pub fn import(&mut self, name: &str, ty: ComponentTypeRef) -> &mut Self { - push_extern_name_byte(&mut self.bytes, name); - name.encode(&mut self.bytes); + encode_component_import_name(&mut self.bytes, name); ty.encode(&mut self.bytes); self.num_added += 1; self @@ -155,21 +154,16 @@ impl ComponentSection for ComponentImportSection { /// discriminated with a leading byte indicating what kind of import they are. /// After that PR though names are always prefixed with a 0x00 byte. /// -/// This function is a compatibility shim for the time being while this change -/// is being rolled out. That PR is technically a spec-breaking change relative -/// to prior but we want old tooling to continue to work with new modules. To -/// handle that names that look like IDs, `a:b/c`, get an 0x01 prefix instead of -/// the spec-defined 0x00 prefix. That makes components produced by current -/// versions of this crate compatible with older parsers. +/// On 2023-10-28 in bytecodealliance/wasm-tools#1262 was landed to start +/// transitioning to "always lead with 0x00". That updated the validator/parser +/// to accept either 0x00 or 0x01 but the encoder wasn't updated at the time. /// -/// Note that wasmparser has a similar case where it parses either 0x01 or 0x00. -/// That means that the selection of a byte here doesn't actually matter for -/// wasmparser's own validation. Eventually this will go away and an 0x00 byte -/// will always be emitted to align with the spec. -pub(crate) fn push_extern_name_byte(bytes: &mut Vec, name: &str) { - if name.contains(':') { - bytes.push(0x01); - } else { - bytes.push(0x00); - } +/// On 2024-09-03 in bytecodealliance/wasm-tools#TODO this encoder was updated +/// to always emit 0x00 as a leading byte. +/// +/// This function corresponds with the `importname'` production in the +/// specification. +pub(crate) fn encode_component_import_name(bytes: &mut Vec, name: &str) { + bytes.push(0x00); + name.encode(bytes); } diff --git a/crates/wasm-encoder/src/component/instances.rs b/crates/wasm-encoder/src/component/instances.rs index e0877d1661..fd3fc3c3af 100644 --- a/crates/wasm-encoder/src/component/instances.rs +++ b/crates/wasm-encoder/src/component/instances.rs @@ -177,8 +177,7 @@ impl ComponentInstanceSection { self.bytes.push(0x01); exports.len().encode(&mut self.bytes); for (name, kind, index) in exports { - crate::push_extern_name_byte(&mut self.bytes, name); - name.encode(&mut self.bytes); + crate::encode_component_export_name(&mut self.bytes, name); kind.encode(&mut self.bytes); index.encode(&mut self.bytes); } diff --git a/crates/wasm-encoder/src/component/types.rs b/crates/wasm-encoder/src/component/types.rs index 006b9a33e2..aa79d69c94 100644 --- a/crates/wasm-encoder/src/component/types.rs +++ b/crates/wasm-encoder/src/component/types.rs @@ -20,7 +20,7 @@ impl ModuleType { /// Defines an import in this module type. pub fn import(&mut self, module: &str, name: &str, ty: EntityType) -> &mut Self { - crate::component::imports::push_extern_name_byte(&mut self.bytes, name); + self.bytes.push(0x00); module.encode(&mut self.bytes); name.encode(&mut self.bytes); ty.encode(&mut self.bytes); @@ -252,8 +252,7 @@ impl ComponentType { /// Defines an import in this component type. pub fn import(&mut self, name: &str, ty: ComponentTypeRef) -> &mut Self { self.bytes.push(0x03); - crate::component::imports::push_extern_name_byte(&mut self.bytes, name); - name.encode(&mut self.bytes); + crate::encode_component_import_name(&mut self.bytes, name); ty.encode(&mut self.bytes); self.num_added += 1; match ty { @@ -267,8 +266,7 @@ impl ComponentType { /// Defines an export in this component type. pub fn export(&mut self, name: &str, ty: ComponentTypeRef) -> &mut Self { self.bytes.push(0x04); - crate::component::imports::push_extern_name_byte(&mut self.bytes, name); - name.encode(&mut self.bytes); + crate::encode_component_export_name(&mut self.bytes, name); ty.encode(&mut self.bytes); self.num_added += 1; match ty { diff --git a/crates/wasm-smith/src/lib.rs b/crates/wasm-smith/src/lib.rs index 59d4ddb640..d1669853f1 100644 --- a/crates/wasm-smith/src/lib.rs +++ b/crates/wasm-smith/src/lib.rs @@ -69,8 +69,8 @@ use wasm_encoder::MemoryType; pub use config::InternalOptionalConfig; pub(crate) fn page_size(mem: &MemoryType) -> u32 { - const DEFAULT_WASM_PAGE_SIZE: u32 = 65_536; - mem.page_size_log2.unwrap_or(DEFAULT_WASM_PAGE_SIZE) + const DEFAULT_WASM_PAGE_SIZE_LOG2: u32 = 16; + 1 << mem.page_size_log2.unwrap_or(DEFAULT_WASM_PAGE_SIZE_LOG2) } /// Do something an arbitrary number of times. diff --git a/crates/wasm-smith/tests/common/mod.rs b/crates/wasm-smith/tests/common/mod.rs index 858a226f33..1d03a740a6 100644 --- a/crates/wasm-smith/tests/common/mod.rs +++ b/crates/wasm-smith/tests/common/mod.rs @@ -2,7 +2,7 @@ use wasm_smith::Config; use wasmparser::{types::Types, Validator, WasmFeatures}; pub fn parser_features_from_config(config: &Config) -> WasmFeatures { - let mut features = WasmFeatures::MUTABLE_GLOBAL | WasmFeatures::wasm1(); + let mut features = WasmFeatures::MUTABLE_GLOBAL | WasmFeatures::WASM1; features.set( WasmFeatures::SATURATING_FLOAT_TO_INT, config.saturating_float_to_int_enabled, diff --git a/crates/wasmparser/benches/benchmark.rs b/crates/wasmparser/benches/benchmark.rs index e5cdd4b495..8ec6e45612 100644 --- a/crates/wasmparser/benches/benchmark.rs +++ b/crates/wasmparser/benches/benchmark.rs @@ -58,7 +58,8 @@ fn collect_test_files(path: &Path, list: &mut Vec) -> Result<()> }; for directive in wast.directives { match directive { - wast::WastDirective::Wat(mut module) => { + wast::WastDirective::Module(mut module) + | wast::WastDirective::ModuleDefinition(mut module) => { let wasm = module.encode()?; list.push(BenchmarkInput::new(path.clone(), wasm)); } diff --git a/crates/wasmparser/src/features.rs b/crates/wasmparser/src/features.rs index 67eabcd5c5..e66b350004 100644 --- a/crates/wasmparser/src/features.rs +++ b/crates/wasmparser/src/features.rs @@ -231,26 +231,39 @@ define_wasm_features! { } impl WasmFeatures { - /// Returns the feature set associated with the 1.0 version of the + /// The feature set associated with the 1.0 version of the /// WebAssembly specification or the "MVP" feature set. #[cfg(feature = "features")] - pub fn wasm1() -> WasmFeatures { - WasmFeatures::FLOATS | WasmFeatures::GC_TYPES - } + pub const WASM1: WasmFeatures = WasmFeatures::FLOATS.union(WasmFeatures::GC_TYPES); - /// Returns the feature set associated with the 2.0 version of the + /// The feature set associated with the 2.0 version of the /// WebAssembly specification. #[cfg(feature = "features")] - pub fn wasm2() -> WasmFeatures { - WasmFeatures::wasm1() - | WasmFeatures::BULK_MEMORY - | WasmFeatures::REFERENCE_TYPES - | WasmFeatures::SIGN_EXTENSION - | WasmFeatures::MUTABLE_GLOBAL - | WasmFeatures::SATURATING_FLOAT_TO_INT - | WasmFeatures::MULTI_VALUE - | WasmFeatures::SIMD - } + pub const WASM2: WasmFeatures = WasmFeatures::WASM1 + .union(WasmFeatures::BULK_MEMORY) + .union(WasmFeatures::REFERENCE_TYPES) + .union(WasmFeatures::SIGN_EXTENSION) + .union(WasmFeatures::MUTABLE_GLOBAL) + .union(WasmFeatures::SATURATING_FLOAT_TO_INT) + .union(WasmFeatures::MULTI_VALUE) + .union(WasmFeatures::SIMD); + + /// The feature set associated with the 3.0 version of the + /// WebAssembly specification. + /// + /// Note that as of the time of this writing the 3.0 version of the + /// specification is not yet published. The precise set of features set + /// here may change as that continues to evolve. + #[cfg(feature = "features")] + pub const WASM3: WasmFeatures = WasmFeatures::WASM2 + .union(WasmFeatures::GC) + .union(WasmFeatures::TAIL_CALL) + .union(WasmFeatures::EXTENDED_CONST) + .union(WasmFeatures::FUNCTION_REFERENCES) + .union(WasmFeatures::MULTI_MEMORY) + .union(WasmFeatures::RELAXED_SIMD) + .union(WasmFeatures::THREADS) + .union(WasmFeatures::EXCEPTIONS); } #[cfg(feature = "features")] diff --git a/crates/wasmparser/src/readers/component/imports.rs b/crates/wasmparser/src/readers/component/imports.rs index f62c109310..bd14428d85 100644 --- a/crates/wasmparser/src/readers/component/imports.rs +++ b/crates/wasmparser/src/readers/component/imports.rs @@ -117,12 +117,27 @@ pub struct ComponentImportName<'a>(pub &'a str); impl<'a> FromReader<'a> for ComponentImportName<'a> { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { match reader.read_u8()? { + // This is the spec-required byte as of this time. 0x00 => {} - // Historically export names used a discriminator byte of 0x01 to - // indicate an "interface" of the form `a:b/c` but nowadays that's - // inferred from string syntax. Ignore 0-vs-1 to continue to parse - // older binaries. Eventually this will go away. + + // Prior to WebAssembly/component-model#263 export names used a + // discriminator byte of 0x01 to indicate an "interface" of the + // form `a:b/c` but nowadays that's inferred from string syntax. + // Ignore 0-vs-1 to continue to parse older binaries. Eventually + // this will go away. + // + // This logic to ignore 0x01 was landed on 2023-10-28 in + // bytecodealliance/wasm-tools#1262 and the encoder at the time + // still emitted 0x01 to have better compatibility with prior + // validators. + // + // On 2024-09-03 in bytecodealliance/wasm-tools#TODO the encoder + // was updated to always emit 0x00 as a leading byte. After enough + // time has passed this case may be able to be removed. When + // removing this it's probably best to do it with a `WasmFeatures` + // flag first to ensure there's an opt-in way of fixing things. 0x01 => {} + x => return reader.invalid_leading_byte(x, "import name"), } Ok(ComponentImportName(reader.read_string()?)) diff --git a/crates/wasmparser/src/readers/core/init.rs b/crates/wasmparser/src/readers/core/init.rs index fe61eb0fa0..f14e31051a 100644 --- a/crates/wasmparser/src/readers/core/init.rs +++ b/crates/wasmparser/src/readers/core/init.rs @@ -32,7 +32,7 @@ impl Eq for ConstExpr<'_> {} impl<'a> ConstExpr<'a> { /// Constructs a new `ConstExpr` from the given data and offset. - pub fn new(reader: BinaryReader<'a>) -> ConstExpr { + pub fn new(reader: BinaryReader<'a>) -> ConstExpr<'a> { ConstExpr { reader } } diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 405a03c573..d29f896d74 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -2,7 +2,7 @@ use super::{ check_max, - core::Module, + core::{InternRecGroup, Module}, types::{ AliasableResourceId, ComponentCoreInstanceTypeId, ComponentDefinedTypeId, ComponentFuncType, ComponentFuncTypeId, ComponentInstanceType, ComponentInstanceTypeId, @@ -261,12 +261,18 @@ impl ComponentState { offset: usize, check_limit: bool, ) -> Result<()> { - let id = match ty { + let current = components.last_mut().unwrap(); + if check_limit { + check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; + } + match ty { crate::CoreType::Sub(sub) => { - let (_is_new, group_id) = - types.intern_canonical_rec_group(RecGroup::implicit(offset, sub)); - let id = types[group_id].start; - ComponentCoreTypeId::Sub(id) + current.canonicalize_and_intern_rec_group( + features, + types, + RecGroup::implicit(offset, sub), + offset, + )?; } crate::CoreType::Module(decls) => { let mod_ty = Self::create_module_type( @@ -276,16 +282,11 @@ impl ComponentState { types, offset, )?; - let id = types.push_ty(mod_ty); - ComponentCoreTypeId::Module(id) + let id = ComponentCoreTypeId::Module(types.push_ty(mod_ty)); + components.last_mut().unwrap().core_types.push(id); } - }; - - let current = components.last_mut().unwrap(); - if check_limit { - check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; } - current.core_types.push(id); + Ok(()) } @@ -3036,6 +3037,25 @@ impl ComponentState { } } +impl InternRecGroup for ComponentState { + fn add_type_id(&mut self, id: CoreTypeId) { + self.core_types.push(ComponentCoreTypeId::Sub(id)); + } + + fn type_id_at(&self, idx: u32, offset: usize) -> Result { + match self.core_type_at(idx, offset)? { + ComponentCoreTypeId::Sub(id) => Ok(id), + ComponentCoreTypeId::Module(_) => { + bail!(offset, "type index {idx} is a module type, not a sub type"); + } + } + } + + fn types_len(&self) -> u32 { + u32::try_from(self.core_types.len()).unwrap() + } +} + impl ComponentNameContext { /// Registers that the resource `id` is named `name` within this context. fn register(&mut self, name: &str, id: AliasableResourceId) { diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs index 197921a284..891daf5c39 100644 --- a/crates/wasmparser/src/validator/core.rs +++ b/crates/wasmparser/src/validator/core.rs @@ -2,19 +2,19 @@ //! mod canonical; +pub(crate) use canonical::InternRecGroup; -use self::{arc::MaybeOwned, canonical::canonicalize_and_intern_rec_group}; +use self::arc::MaybeOwned; use super::{ check_max, combine_type_sizes, operators::{ty_to_str, OperatorValidator, OperatorValidatorAllocations}, types::{CoreTypeId, EntityType, RecGroupId, TypeAlloc, TypeList}, }; use crate::{ - limits::*, validator::types::TypeIdentifier, BinaryReaderError, CompositeType, ConstExpr, - ContType, Data, DataKind, Element, ElementKind, ExternalKind, FuncType, Global, GlobalType, - HeapType, MemoryType, PackedIndex, RecGroup, RefType, Result, StorageType, SubType, Table, - TableInit, TableType, TagType, TypeRef, UnpackedIndex, ValType, VisitOperator, WasmFeatures, - WasmModuleResources, + limits::*, BinaryReaderError, ConstExpr, ContType, Data, DataKind, Element, ElementKind, + ExternalKind, FuncType, Global, GlobalType, HeapType, MemoryType, RecGroup, RefType, Result, + SubType, Table, TableInit, TableType, TagType, TypeRef, UnpackedIndex, ValType, VisitOperator, + WasmFeatures, WasmModuleResources, }; use crate::{prelude::*, CompositeInnerType}; use alloc::sync::Arc; @@ -552,21 +552,6 @@ pub(crate) struct Module { } impl Module { - /// Get the `CoreTypeId` of the type at the given packed index. - pub(crate) fn at_packed_index( - &self, - types: &TypeList, - rec_group: RecGroupId, - index: PackedIndex, - offset: usize, - ) -> Result { - match index.unpack() { - UnpackedIndex::Id(id) => Ok(id), - UnpackedIndex::Module(idx) => self.type_id_at(idx, offset), - UnpackedIndex::RecGroup(idx) => types.rec_group_local_id(rec_group, idx, offset), - } - } - pub fn add_types( &mut self, rec_group: RecGroup, @@ -575,14 +560,6 @@ impl Module { offset: usize, check_limit: bool, ) -> Result<()> { - debug_assert!(rec_group.is_explicit_rec_group() || rec_group.types().len() == 1); - if rec_group.is_explicit_rec_group() && !features.gc() { - bail!( - offset, - "rec group usage requires `gc` proposal to be enabled" - ); - } - if check_limit { check_max( self.types.len(), @@ -592,187 +569,7 @@ impl Module { offset, )?; } - - let (is_new, rec_group_id) = - canonicalize_and_intern_rec_group(features, types, self, rec_group, offset)?; - - let range = &types[rec_group_id]; - let start = range.start.index(); - let end = range.end.index(); - - for i in start..end { - let i = u32::try_from(i).unwrap(); - let id = CoreTypeId::from_index(i); - debug_assert!(types.get(id).is_some()); - self.types.push(id); - if is_new { - self.check_subtype(rec_group_id, id, features, types, offset)?; - } - } - - Ok(()) - } - - fn check_subtype( - &mut self, - rec_group: RecGroupId, - id: CoreTypeId, - features: &WasmFeatures, - types: &mut TypeAlloc, - offset: usize, - ) -> Result<()> { - let ty = &types[id]; - if !features.gc() && (!ty.is_final || ty.supertype_idx.is_some()) { - bail!(offset, "gc proposal must be enabled to use subtypes"); - } - - self.check_composite_type(&ty.composite_type, features, &types, offset)?; - - let depth = if let Some(supertype_index) = ty.supertype_idx { - debug_assert!(supertype_index.is_canonical()); - let sup_id = self.at_packed_index(types, rec_group, supertype_index, offset)?; - if types[sup_id].is_final { - bail!(offset, "sub type cannot have a final super type"); - } - if !types.matches(id, sup_id) { - bail!(offset, "sub type must match super type"); - } - let depth = types.get_subtyping_depth(sup_id) + 1; - if usize::from(depth) > crate::limits::MAX_WASM_SUBTYPING_DEPTH { - bail!( - offset, - "sub type hierarchy too deep: found depth {}, cannot exceed depth {}", - depth, - crate::limits::MAX_WASM_SUBTYPING_DEPTH, - ); - } - depth - } else { - 0 - }; - types.set_subtyping_depth(id, depth); - - Ok(()) - } - - fn check_composite_type( - &mut self, - ty: &CompositeType, - features: &WasmFeatures, - types: &TypeList, - offset: usize, - ) -> Result<()> { - let check = |ty: &ValType, shared: bool| { - features - .check_value_type(*ty) - .map_err(|e| BinaryReaderError::new(e, offset))?; - if shared && !types.valtype_is_shared(*ty) { - return Err(BinaryReaderError::new( - "shared composite type must contain shared types", - offset, - )); - // The other cases are fine: - // - both shared or unshared: good to go - // - the func type is unshared, `ty` is shared: though - // odd, we _can_ in fact use shared values in - // unshared composite types (e.g., functions). - } - Ok(()) - }; - if !features.shared_everything_threads() && ty.shared { - return Err(BinaryReaderError::new( - "shared composite types require the shared-everything-threads proposal", - offset, - )); - } - match &ty.inner { - CompositeInnerType::Func(t) => { - for vt in t.params().iter().chain(t.results()) { - check(vt, ty.shared)?; - } - if t.results().len() > 1 && !features.multi_value() { - return Err(BinaryReaderError::new( - "func type returns multiple values but the multi-value feature is not enabled", - offset, - )); - } - } - CompositeInnerType::Cont(ct) => { - // Check that the type index points to a valid function type. - match ct.0 { - UnpackedIndex::Module(idx) => { - if (idx as usize) >= self.types.len() { - return Err(BinaryReaderError::new("invalid type index", offset)); - // TODO(dhil): tidy up error message. - } - if self.func_type_at(idx, types, offset).is_err() { - return Err(BinaryReaderError::new( - format!("non-function type {}", idx), - offset, - )); - } - } - UnpackedIndex::RecGroup(_) => unreachable!(), - UnpackedIndex::Id(id) => { - // NOTE(dhil): This is a bit dodgy... it'd be - // better to check wellformedness before - // canonicalisation. - if self - .func_type_at(id.index().try_into().unwrap(), types, offset) - .is_err() - { - return Err(BinaryReaderError::new( - format!("non-function type {}", id.index()), - offset, - )); - } - } - } - } - CompositeInnerType::Array(t) => { - if !features.gc() { - bail!( - offset, - "array indexed types not supported without the gc feature", - ); - } - if !features.gc_types() { - bail!( - offset, - "cannot define array types when gc types are disabled", - ); - } - match &t.0.element_type { - StorageType::I8 | StorageType::I16 => { - // Note: scalar types are always `shared`. - } - StorageType::Val(value_type) => check(value_type, ty.shared)?, - }; - } - CompositeInnerType::Struct(t) => { - if !features.gc() { - bail!( - offset, - "struct indexed types not supported without the gc feature", - ); - } - if !features.gc_types() { - bail!( - offset, - "cannot define struct types when gc types are disabled", - ); - } - for ft in t.fields.iter() { - match &ft.element_type { - StorageType::I8 | StorageType::I16 => { - // Note: scalar types are always `shared`. - } - StorageType::Val(value_type) => check(value_type, ty.shared)?, - } - } - } - } - Ok(()) + self.canonicalize_and_intern_rec_group(features, types, rec_group, offset) } pub fn add_import( @@ -891,13 +688,6 @@ impl Module { Ok(()) } - pub fn type_id_at(&self, idx: u32, offset: usize) -> Result { - self.types - .get(idx as usize) - .copied() - .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) - } - fn sub_type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a SubType> { let id = self.type_id_at(idx, offset)?; Ok(&types[id]) @@ -1308,6 +1098,23 @@ impl Module { } } +impl InternRecGroup for Module { + fn add_type_id(&mut self, id: CoreTypeId) { + self.types.push(id); + } + + fn type_id_at(&self, idx: u32, offset: usize) -> Result { + self.types + .get(idx as usize) + .copied() + .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) + } + + fn types_len(&self) -> u32 { + u32::try_from(self.types.len()).unwrap() + } +} + impl Default for Module { fn default() -> Self { Self { diff --git a/crates/wasmparser/src/validator/core/canonical.rs b/crates/wasmparser/src/validator/core/canonical.rs index 01358d9986..a444b3af2d 100644 --- a/crates/wasmparser/src/validator/core/canonical.rs +++ b/crates/wasmparser/src/validator/core/canonical.rs @@ -67,26 +67,219 @@ //! perform additional expensive checks to see if the types match or not //! (since the whole point of canonicalization is to avoid that!). -use super::{Module, RecGroupId, TypeAlloc}; +use super::{RecGroupId, TypeAlloc, TypeList}; use crate::{ types::{CoreTypeId, TypeIdentifier}, - PackedIndex, RecGroup, Result, UnpackedIndex, WasmFeatures, + BinaryReaderError, CompositeInnerType, CompositeType, PackedIndex, RecGroup, Result, + StorageType, UnpackedIndex, ValType, WasmFeatures, }; -/// Canonicalize the rec group and return its id and whether it is a new group -/// (we added its types to the `TypeAlloc`) or not (we deduplicated it with an -/// existing canonical rec group). -pub(crate) fn canonicalize_and_intern_rec_group( - features: &WasmFeatures, - types: &mut TypeAlloc, - module: &Module, - mut rec_group: RecGroup, - offset: usize, -) -> Result<(bool, RecGroupId)> { - TypeCanonicalizer::new(module, offset) - .with_features(features) - .canonicalize_rec_group(&mut rec_group)?; - Ok(types.intern_canonical_rec_group(rec_group)) +pub(crate) trait InternRecGroup { + fn add_type_id(&mut self, id: CoreTypeId); + fn type_id_at(&self, idx: u32, offset: usize) -> Result; + fn types_len(&self) -> u32; + + /// Canonicalize the rec group and return its id and whether it is a new group + /// (we added its types to the `TypeAlloc`) or not (we deduplicated it with an + /// existing canonical rec group). + fn canonicalize_and_intern_rec_group( + &mut self, + features: &WasmFeatures, + types: &mut TypeAlloc, + mut rec_group: RecGroup, + offset: usize, + ) -> Result<()> + where + Self: Sized, + { + debug_assert!(rec_group.is_explicit_rec_group() || rec_group.types().len() == 1); + if rec_group.is_explicit_rec_group() && !features.gc() { + bail!( + offset, + "rec group usage requires `gc` proposal to be enabled" + ); + } + TypeCanonicalizer::new(self, offset) + .with_features(features) + .canonicalize_rec_group(&mut rec_group)?; + let (is_new, rec_group_id) = types.intern_canonical_rec_group(rec_group); + let range = &types[rec_group_id]; + let start = range.start.index(); + let end = range.end.index(); + + for i in start..end { + let i = u32::try_from(i).unwrap(); + let id = CoreTypeId::from_index(i); + debug_assert!(types.get(id).is_some()); + self.add_type_id(id); + if is_new { + self.check_subtype(rec_group_id, id, features, types, offset)?; + } + } + + Ok(()) + } + + fn check_subtype( + &mut self, + rec_group: RecGroupId, + id: CoreTypeId, + features: &WasmFeatures, + types: &mut TypeAlloc, + offset: usize, + ) -> Result<()> { + let ty = &types[id]; + if !features.gc() && (!ty.is_final || ty.supertype_idx.is_some()) { + bail!(offset, "gc proposal must be enabled to use subtypes"); + } + + self.check_composite_type(&ty.composite_type, features, &types, offset)?; + + let depth = if let Some(supertype_index) = ty.supertype_idx { + debug_assert!(supertype_index.is_canonical()); + let sup_id = self.at_packed_index(types, rec_group, supertype_index, offset)?; + if types[sup_id].is_final { + bail!(offset, "sub type cannot have a final super type"); + } + if !types.matches(id, sup_id) { + bail!(offset, "sub type must match super type"); + } + let depth = types.get_subtyping_depth(sup_id) + 1; + if usize::from(depth) > crate::limits::MAX_WASM_SUBTYPING_DEPTH { + bail!( + offset, + "sub type hierarchy too deep: found depth {}, cannot exceed depth {}", + depth, + crate::limits::MAX_WASM_SUBTYPING_DEPTH, + ); + } + depth + } else { + 0 + }; + types.set_subtyping_depth(id, depth); + + Ok(()) + } + + fn check_composite_type( + &mut self, + ty: &CompositeType, + features: &WasmFeatures, + types: &TypeList, + offset: usize, + ) -> Result<()> { + let check = |ty: &ValType, shared: bool| { + features + .check_value_type(*ty) + .map_err(|e| BinaryReaderError::new(e, offset))?; + if shared && !types.valtype_is_shared(*ty) { + return Err(BinaryReaderError::new( + "shared composite type must contain shared types", + offset, + )); + // The other cases are fine: + // - both shared or unshared: good to go + // - the func type is unshared, `ty` is shared: though + // odd, we _can_ in fact use shared values in + // unshared composite types (e.g., functions). + } + Ok(()) + }; + if !features.shared_everything_threads() && ty.shared { + return Err(BinaryReaderError::new( + "shared composite types require the shared-everything-threads proposal", + offset, + )); + } + match &ty.inner { + CompositeInnerType::Func(t) => { + for vt in t.params().iter().chain(t.results()) { + check(vt, ty.shared)?; + } + if t.results().len() > 1 && !features.multi_value() { + return Err(BinaryReaderError::new( + "func type returns multiple values but the multi-value feature is not enabled", + offset, + )); + } + } + CompositeInnerType::Array(t) => { + if !features.gc() { + bail!( + offset, + "array indexed types not supported without the gc feature", + ); + } + if !features.gc_types() { + bail!( + offset, + "cannot define array types when gc types are disabled", + ); + } + match &t.0.element_type { + StorageType::I8 | StorageType::I16 => { + // Note: scalar types are always `shared`. + } + StorageType::Val(value_type) => check(value_type, ty.shared)?, + }; + } + CompositeInnerType::Struct(t) => { + if !features.gc() { + bail!( + offset, + "struct indexed types not supported without the gc feature", + ); + } + if !features.gc_types() { + bail!( + offset, + "cannot define struct types when gc types are disabled", + ); + } + for ft in t.fields.iter() { + match &ft.element_type { + StorageType::I8 | StorageType::I16 => { + // Note: scalar types are always `shared`. + } + StorageType::Val(value_type) => check(value_type, ty.shared)?, + } + } + } + CompositeInnerType::Cont(ct) => { + // Check that the type index points to a valid function type. + let id = match ct.0 { + UnpackedIndex::Id(id) => id, + UnpackedIndex::Module(idx) => self.type_id_at(idx, offset)?, + UnpackedIndex::RecGroup(_) => unreachable!(), + }; + match types[id].composite_type.inner { + CompositeInnerType::Func(_) => (), + _ => { + return Err(BinaryReaderError::new( + crate::validator::format!("non-function type {}", id.index()), + offset, + )) + } + } + } + } + Ok(()) + } + + fn at_packed_index( + &self, + types: &TypeList, + rec_group: RecGroupId, + index: PackedIndex, + offset: usize, + ) -> Result { + match index.unpack() { + UnpackedIndex::Id(id) => Ok(id), + UnpackedIndex::Module(idx) => self.type_id_at(idx, offset), + UnpackedIndex::RecGroup(idx) => types.rec_group_local_id(rec_group, idx, offset), + } + } } /// The kind of canonicalization we are doing. @@ -105,7 +298,7 @@ enum CanonicalizationMode { } pub(crate) struct TypeCanonicalizer<'a> { - module: &'a Module, + module: &'a dyn InternRecGroup, features: Option<&'a WasmFeatures>, rec_group_start: u32, rec_group_len: u32, @@ -115,7 +308,7 @@ pub(crate) struct TypeCanonicalizer<'a> { } impl<'a> TypeCanonicalizer<'a> { - pub fn new(module: &'a Module, offset: usize) -> Self { + pub fn new(module: &'a dyn InternRecGroup, offset: usize) -> Self { // These defaults will work for when we are canonicalizing types from // outside of a rec group definition, forcing all `PackedIndex`es to be // canonicalized to `CoreTypeId`s. @@ -147,7 +340,7 @@ impl<'a> TypeCanonicalizer<'a> { // Re-initialize these fields so that we properly canonicalize // intra-rec-group type references into indices into the rec group // rather than as `CoreTypeId`s. - self.rec_group_start = u32::try_from(self.module.types.len()).unwrap(); + self.rec_group_start = self.module.types_len(); self.rec_group_len = u32::try_from(rec_group.types().len()).unwrap(); for (rec_group_local_index, ty) in rec_group.types_mut().enumerate() { @@ -168,7 +361,7 @@ impl<'a> TypeCanonicalizer<'a> { fn canonicalize_type_index(&self, ty: &mut PackedIndex) -> Result<()> { match ty.unpack() { - UnpackedIndex::Id(_) => return Ok(()), + UnpackedIndex::Id(_) => Ok(()), UnpackedIndex::Module(index) => { if index < self.rec_group_start || self.mode == CanonicalizationMode::OnlyIds { let id = self.module.type_id_at(index, self.offset)?; diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs index 813e2a6292..53348b6a77 100644 --- a/crates/wasmparser/src/validator/operators.rs +++ b/crates/wasmparser/src/validator/operators.rs @@ -169,23 +169,31 @@ pub struct OperatorValidatorAllocations { /// Type storage within the validator. /// -/// This is used to manage the operand stack and notably isn't just `ValType` -/// to handle unreachable code and the "bottom" type. +/// When managing the operand stack in unreachable code, the validator may not +/// fully know an operand's type. this unknown state is known as the `bottom` +/// type in the WebAssembly specification. Validating further instructions may +/// give us more information; either partial (`PartialRef`) or fully known. #[derive(Debug, Copy, Clone)] enum MaybeType { - /// A "bottom" type which represents unconstrained unreachable code. There - /// are no constraints on what this type may be. - Bot, - /// A "bottom" type for specifically heap types. + /// The operand has no available type information due to unreachable code. /// - /// This type is known to be a reference type, optionally the specific - /// abstract heap type listed. This type can be interpeted as either - /// `shared` or not-`shared`. Additionally it can be either nullable or - /// not. Currently no further refinements are required for wasm - /// instructions today, but this may grow in the future. - HeapBot(Option), - /// A known type with the type `T`. - Type(T), + /// This state represents "unknown" and corresponds to the `bottom` type in + /// the WebAssembly specification. There are no constraints on what this + /// type may be and it can match any other type during validation. + Bottom, + /// The operand is known to be a reference and we may know its abstract + /// type. + /// + /// This state is not fully `Known`, however, because its type can be + /// interpreted as either: + /// - `shared` or not-`shared` + /// - nullable or not nullable + /// + /// No further refinements are required for WebAssembly instructions today + /// but this may grow in the future. + UnknownRef(Option), + /// The operand is known to have type `T`. + Known(T), } // The validator is pretty performance-sensitive and `MaybeType` is the main @@ -198,8 +206,8 @@ const _: () = { impl core::fmt::Display for MaybeType { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - MaybeType::Bot => write!(f, "bot"), - MaybeType::HeapBot(ty) => { + MaybeType::Bottom => write!(f, "bot"), + MaybeType::UnknownRef(ty) => { write!(f, "(ref shared? ")?; match ty { Some(ty) => write!(f, "{}bot", ty.as_str(true))?, @@ -207,14 +215,14 @@ impl core::fmt::Display for MaybeType { } write!(f, ")") } - MaybeType::Type(ty) => core::fmt::Display::fmt(ty, f), + MaybeType::Known(ty) => core::fmt::Display::fmt(ty, f), } } } impl From for MaybeType { fn from(ty: ValType) -> MaybeType { - MaybeType::Type(ty) + MaybeType::Known(ty) } } @@ -227,9 +235,9 @@ impl From for MaybeType { impl From> for MaybeType { fn from(ty: MaybeType) -> MaybeType { match ty { - MaybeType::Bot => MaybeType::Bot, - MaybeType::HeapBot(ty) => MaybeType::HeapBot(ty), - MaybeType::Type(t) => MaybeType::Type(t.into()), + MaybeType::Bottom => MaybeType::Bottom, + MaybeType::UnknownRef(ty) => MaybeType::UnknownRef(ty), + MaybeType::Known(t) => MaybeType::Known(t.into()), } } } @@ -237,17 +245,17 @@ impl From> for MaybeType { impl MaybeType { fn as_non_null(&self) -> MaybeType { match self { - MaybeType::Bot => MaybeType::Bot, - MaybeType::HeapBot(ty) => MaybeType::HeapBot(*ty), - MaybeType::Type(ty) => MaybeType::Type(ty.as_non_null()), + MaybeType::Bottom => MaybeType::Bottom, + MaybeType::UnknownRef(ty) => MaybeType::UnknownRef(*ty), + MaybeType::Known(ty) => MaybeType::Known(ty.as_non_null()), } } fn is_maybe_shared(&self, resources: &impl WasmModuleResources) -> Option { match self { - MaybeType::Bot => None, - MaybeType::HeapBot(_) => None, - MaybeType::Type(ty) => Some(resources.is_shared(*ty)), + MaybeType::Bottom => None, + MaybeType::UnknownRef(_) => None, + MaybeType::Known(ty) => Some(resources.is_shared(*ty)), } } } @@ -396,8 +404,8 @@ impl OperatorValidator { /// A `depth` of 0 will refer to the last operand on the stack. pub fn peek_operand_at(&self, depth: usize) -> Option> { Some(match self.operands.iter().rev().nth(depth)? { - MaybeType::Type(t) => Some(*t), - MaybeType::Bot | MaybeType::HeapBot(..) => None, + MaybeType::Known(t) => Some(*t), + MaybeType::Bottom | MaybeType::UnknownRef(..) => None, }) } @@ -496,7 +504,7 @@ where if cfg!(debug_assertions) { match maybe_ty { - MaybeType::Type(ValType::Ref(r)) => match r.heap_type() { + MaybeType::Known(ValType::Ref(r)) => match r.heap_type() { HeapType::Concrete(index) => { debug_assert!( matches!(index, UnpackedIndex::Id(_)), @@ -588,15 +596,15 @@ where // pop it. If we shouldn't have popped it then it's passed to the slow // path to get pushed back onto the stack. let popped = match self.operands.pop() { - Some(MaybeType::Type(actual_ty)) => { + Some(MaybeType::Known(actual_ty)) => { if Some(actual_ty) == expected { if let Some(control) = self.control.last() { if self.operands.len() >= control.height { - return Ok(MaybeType::Type(actual_ty)); + return Ok(MaybeType::Known(actual_ty)); } } } - Some(MaybeType::Type(actual_ty)) + Some(MaybeType::Known(actual_ty)) } other => other, }; @@ -619,7 +627,7 @@ where None => return Err(self.err_beyond_end(self.offset)), }; let actual = if self.operands.len() == control.height && control.unreachable { - MaybeType::Bot + MaybeType::Bottom } else { if self.operands.len() == control.height { let desc = match expected { @@ -637,13 +645,13 @@ where if let Some(expected) = expected { match (actual, expected) { // The bottom type matches all expectations - (MaybeType::Bot, _) => {} + (MaybeType::Bottom, _) => {} // The "heap bottom" type only matches other references types, // but not any integer types. Note that if the heap bottom is // known to have a specific abstract heap type then a subtype // check is performed against hte expected type. - (MaybeType::HeapBot(actual_ty), ValType::Ref(expected)) => { + (MaybeType::UnknownRef(actual_ty), ValType::Ref(expected)) => { if let Some(actual) = actual_ty { let expected_shared = self.resources.is_shared(expected); let actual = RefType::new( @@ -667,7 +675,7 @@ where // Use the `is_subtype` predicate to test if a found type matches // the expectation. - (MaybeType::Type(actual), expected) => { + (MaybeType::Known(actual), expected) => { if !self.resources.is_subtype(actual, expected) { bail!( self.offset, @@ -680,7 +688,7 @@ where // A "heap bottom" type cannot match any numeric types. ( - MaybeType::HeapBot(..), + MaybeType::UnknownRef(..), ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128, ) => { bail!( @@ -697,10 +705,10 @@ where /// Pop a reference type from the operand stack. fn pop_ref(&mut self, expected: Option) -> Result> { match self.pop_operand(expected.map(|t| t.into()))? { - MaybeType::Bot => Ok(MaybeType::HeapBot(None)), - MaybeType::HeapBot(ty) => Ok(MaybeType::HeapBot(ty)), - MaybeType::Type(ValType::Ref(rt)) => Ok(MaybeType::Type(rt)), - MaybeType::Type(ty) => bail!( + MaybeType::Bottom => Ok(MaybeType::UnknownRef(None)), + MaybeType::UnknownRef(ty) => Ok(MaybeType::UnknownRef(ty)), + MaybeType::Known(ValType::Ref(rt)) => Ok(MaybeType::Known(rt)), + MaybeType::Known(ty) => bail!( self.offset, "type mismatch: expected ref but found {}", ty_to_str(ty) @@ -715,9 +723,9 @@ where /// saving extra lookups for concrete types. fn pop_maybe_shared_ref(&mut self, expected: AbstractHeapType) -> Result> { let actual = match self.pop_ref(None)? { - MaybeType::Bot => return Ok(MaybeType::Bot), - MaybeType::HeapBot(None) => return Ok(MaybeType::HeapBot(None)), - MaybeType::HeapBot(Some(actual)) => { + MaybeType::Bottom => return Ok(MaybeType::Bottom), + MaybeType::UnknownRef(None) => return Ok(MaybeType::UnknownRef(None)), + MaybeType::UnknownRef(Some(actual)) => { if !actual.is_subtype_of(expected) { bail!( self.offset, @@ -726,9 +734,9 @@ where actual.as_str(false), ) } - return Ok(MaybeType::HeapBot(Some(actual))); + return Ok(MaybeType::UnknownRef(Some(actual))); } - MaybeType::Type(ty) => ty, + MaybeType::Known(ty) => ty, }; // Change our expectation based on whether we're dealing with an actual // shared or unshared type. @@ -751,7 +759,7 @@ where "type mismatch: expected subtype of {expected}, found {actual}", ) } - Ok(MaybeType::Type(actual)) + Ok(MaybeType::Known(actual)) } /// Fetches the type for the local at `idx`, returning an error if it's out @@ -1217,66 +1225,66 @@ where Ok(()) } - fn cont_type_of_heap_type(&self, hty: HeapType) -> Result { - match hty { - HeapType::Concrete(unpacked_index) => { - match UnpackedIndex::as_core_type_id(&unpacked_index) { - None => Err(format_err!( - self.offset, - "cannot convert heap type to continuation type. Has the heap type ({:?}) been canonicalised?", hty)), - Some(id) => self.cont_type_at(id), - } - } - _ => Err(format_err!( - self.offset, - "cannot convert heap type to continuation type. The heap type was ({:?})", - hty - )), - } - } - - fn func_repr_cont_type_of_heap_type(&self, hty: HeapType) -> Result<&'resources FuncType> { - match hty { - HeapType::Concrete(unpacked_index) => self.func_repr_cont_type_at(unpacked_index), - _ => Err(format_err!( - self.offset, - "cannot convert heap type to function type. The heap type was ({:?})", - hty - )), - } - } - - fn cont_type_at(&self, id: crate::types::CoreTypeId) -> Result { - self.resources.cont_type_at(id).ok_or_else(|| { - format_err!( - self.offset, - "non-continuation type {}", - crate::validator::types::TypeIdentifier::index(&id) - ) - }) - } + // fn cont_type_of_heap_type(&self, hty: HeapType) -> Result { + // match hty { + // HeapType::Concrete(unpacked_index) => { + // match UnpackedIndex::as_core_type_id(&unpacked_index) { + // None => Err(format_err!( + // self.offset, + // "cannot convert heap type to continuation type. Has the heap type ({:?}) been canonicalised?", hty)), + // Some(id) => self.cont_type_at(id), + // } + // } + // _ => Err(format_err!( + // self.offset, + // "cannot convert heap type to continuation type. The heap type was ({:?})", + // hty + // )), + // } + // } + + // fn func_repr_cont_type_of_heap_type(&self, hty: HeapType) -> Result<&'resources FuncType> { + // match hty { + // HeapType::Concrete(unpacked_index) => self.func_repr_cont_type_at(unpacked_index), + // _ => Err(format_err!( + // self.offset, + // "cannot convert heap type to function type. The heap type was ({:?})", + // hty + // )), + // } + // } + + // fn cont_type_at(&self, id: crate::types::CoreTypeId) -> Result { + // self.resources.cont_type_at(id).ok_or_else(|| { + // format_err!( + // self.offset, + // "non-continuation type {}", + // crate::validator::types::TypeIdentifier::index(&id) + // ) + // }) + // } /// Retrieves the function type representation of the continuation /// type stored at the given type index. - fn func_repr_cont_type_at(&self, at: UnpackedIndex) -> Result<&'resources FuncType> { - match at { - UnpackedIndex::Id(id) => { - let ct = self.cont_type_at(id)?; - let unpacked_index = ct.0; - self.resources - .func_type_at_id(UnpackedIndex::as_core_type_id(&unpacked_index).unwrap()) - .ok_or_else(|| { - format_err!( - self.offset, - "unknown function type: type index ({:?}) out of bounds", - id - ) - }) - } - UnpackedIndex::RecGroup(_) => todo!(), - UnpackedIndex::Module(_) => unreachable!(), - } - } + // fn func_repr_cont_type_at(&self, at: UnpackedIndex) -> Result<&'resources FuncType> { + // match at { + // UnpackedIndex::Id(id) => { + // let ct = self.cont_type_at(id)?; + // let unpacked_index = ct.0; + // self.resources + // .func_type_at_id(UnpackedIndex::as_core_type_id(&unpacked_index).unwrap()) + // .ok_or_else(|| { + // format_err!( + // self.offset, + // "unknown function type: type index ({:?}) out of bounds", + // id + // ) + // }) + // } + // UnpackedIndex::RecGroup(_) => todo!(), + // UnpackedIndex::Module(_) => unreachable!(), + // } + // } /// Common helper for `ref.test` and `ref.cast` downcasting/checking /// instructions. Returns the given `heap_type` as a `ValType`. @@ -1475,6 +1483,31 @@ where } } + fn cont_type_at(&self, at: u32) -> Result<&ContType> { + let sub_ty = self.sub_type_at(at)?; + if let CompositeInnerType::Cont(cont_ty) = &sub_ty.composite_type.inner { + if self.inner.shared && !sub_ty.composite_type.shared { + bail!( + self.offset, + "shared functions cannot access unshared functions", + ); + } + Ok(cont_ty) + } else { + bail!(self.offset, "non-continuation type {at}") + } + } + + fn func_repr_cont_type_at(&self, at: u32) -> Result<&'resources FuncType> { + let cont_ty = self.cont_type_at(at)?; + Ok(self + .resources + .func_type_at_id( + UnpackedIndex::as_core_type_id(&cont_ty.0).expect("expected core type id"), + ) + .unwrap()) + } + fn tag_at(&self, at: u32) -> Result<&'resources FuncType> { self.resources .tag_at(at) @@ -1576,6 +1609,41 @@ where let tagtype = self.tag_at(tag)?; let block = self.jump(relative_depth)?; + // Retrieve the continuation reference type (i.e. (cont $ft)). + match self.label_types(block.0, block.1)?.last() { + Some(ValType::Ref(rt)) if rt.is_concrete_type_ref() => { + let z = rt.type_index().unwrap().unpack().as_core_type_id().expect("expected canonicalised index"); + if let Some(cont_ty) = self.resources.cont_type_at(z) { + let y = cont_ty.0.as_core_type_id().expect("expected canonicalised index"); + if let Some(ctft2) = self.resources.func_type_at_id(y) { + // Now we must check that (ts2' -> ts2) <: $ft + // This method should be exposed by resources to make this correct + for (&tagty, &ct2ty) in tagtype.results().iter().zip(ctft2.params()) { + // Note: according to spec we should check for equality here + if !self.resources.is_subtype(ct2ty, tagty) { + bail!(self.offset, "type mismatch in continuation type") // TODO(dhil): tidy up + } + } + for (&ctty, &ct2ty) in ctft.results().iter().zip(ctft2.results()) { + // Note: according to spec we should check for equality here + if !self.resources.is_subtype(ctty, ct2ty) { + bail!(self.offset, "type mismatch in continuation type") // TODO(dhil): tidy up + } + } + } else { + bail!(self.offset, "non-function type {}", crate::validator::types::TypeIdentifier::index(&y)) + } + } else { + bail!(self.offset, "non-continuation type {}", crate::validator::types::TypeIdentifier::index(&z)) + } + } + Some(ty) => { + bail!(self.offset, "type mismatch: {}", ty_to_str(ty)) + } + _ => bail!(self.offset, + "type mismatch: instruction requires continuation reference type but label has none") + } + // label_types(offset, block.0, block.1) := ts1''* (ref null? (cont $ft)) if tagtype.params().len() != self.label_types(block.0, block.1)?.len() - 1 { bail!( @@ -1583,6 +1651,7 @@ where "type mismatch between label type and tag type length" ) // TODO(dhil): tidy up } + let labeltys = self .label_types(block.0, block.1)? .take(tagtype.params().len()); @@ -1594,36 +1663,17 @@ where // TODO(dhil): tidy up } } - - // Retrieve the continuation reference type (i.e. (cont $ft)). - match self.label_types(block.0, block.1)?.last() { - Some(ValType::Ref(rt)) if rt.is_concrete_type_ref() => { - let z = rt.type_index().unwrap().unpack(); - let ctft2 = self.func_repr_cont_type_at(z)?; - // Now we must check that (ts2' -> ts2) <: $ft - // This method should be exposed by resources to make this correct - for (&tagty, &ct2ty) in tagtype.results().iter().zip(ctft2.params()) { - // Note: according to spec we should check for equality here - if !self.resources.is_subtype(ct2ty, tagty) { - bail!(self.offset, "type mismatch in continuation type") // TODO(dhil): tidy up - } - } - for (&ctty, &ct2ty) in ctft.results().iter().zip(ctft2.results()) { - // Note: according to spec we should check for equality here - if !self.resources.is_subtype(ctty, ct2ty) { - bail!(self.offset, "type mismatch in continuation type") // TODO(dhil): tidy up - } - } - } - Some(ty) => { - bail!(self.offset, "type mismatch: {}", ty_to_str(ty)) - } - _ => bail!(self.offset, - "type mismatch: instruction requires continuation reference type but label has none") - } } Ok(()) } + + fn check_data_segment(&self, data_index: u32) -> Result<()> { + match self.resources.data_count() { + None => bail!(self.offset, "data count section required"), + Some(count) if data_index < count => Ok(()), + Some(_) => bail!(self.offset, "unknown data segment {data_index}"), + } + } } pub fn ty_to_str(ty: ValType) -> &'static str { @@ -1846,12 +1896,8 @@ where for ty in ty.clone().params().iter().rev() { self.pop_operand(Some(*ty))?; } - if ty.results().len() > 0 { - bail!( - self.offset, - "result type expected to be empty for exception" - ); - } + // this should be validated when the tag was defined in the module + debug_assert!(ty.results().is_empty()); self.unreachable()?; Ok(()) } @@ -1973,10 +2019,10 @@ where let ty = match (ty1, ty2) { // All heap-related types aren't allowed with the `select` // instruction - (MaybeType::HeapBot(..), _) - | (_, MaybeType::HeapBot(..)) - | (MaybeType::Type(ValType::Ref(_)), _) - | (_, MaybeType::Type(ValType::Ref(_))) => { + (MaybeType::UnknownRef(..), _) + | (_, MaybeType::UnknownRef(..)) + | (MaybeType::Known(ValType::Ref(_)), _) + | (_, MaybeType::Known(ValType::Ref(_))) => { bail!( self.offset, "type mismatch: select only takes integral types" @@ -1985,11 +2031,11 @@ where // If one operand is the "bottom" type then whatever the other // operand is is the result of the `select` - (MaybeType::Bot, t) | (t, MaybeType::Bot) => t, + (MaybeType::Bottom, t) | (t, MaybeType::Bottom) => t, // Otherwise these are two integral types and they must match for // `select` to typecheck. - (t @ MaybeType::Type(t1), MaybeType::Type(t2)) => { + (t @ MaybeType::Known(t1), MaybeType::Known(t2)) => { if t1 != t2 { bail!( self.offset, @@ -3910,22 +3956,14 @@ where } fn visit_memory_init(&mut self, segment: u32, mem: u32) -> Self::Output { let ty = self.check_memory_index(mem)?; - match self.resources.data_count() { - None => bail!(self.offset, "data count section required"), - Some(count) if segment < count => {} - Some(_) => bail!(self.offset, "unknown data segment {}", segment), - } + self.check_data_segment(segment)?; self.pop_operand(Some(ValType::I32))?; self.pop_operand(Some(ValType::I32))?; self.pop_operand(Some(ty))?; Ok(()) } fn visit_data_drop(&mut self, segment: u32) -> Self::Output { - match self.resources.data_count() { - None => bail!(self.offset, "data count section required"), - Some(count) if segment < count => {} - Some(_) => bail!(self.offset, "unknown data segment {}", segment), - } + self.check_data_segment(segment)?; Ok(()) } fn visit_memory_copy(&mut self, dst: u32, src: u32) -> Self::Output { @@ -3973,13 +4011,7 @@ where Ok(()) } fn visit_elem_drop(&mut self, segment: u32) -> Self::Output { - if segment >= self.resources.element_count() { - bail!( - self.offset, - "unknown elem segment {}: segment index out of bounds", - segment - ); - } + self.element_type_at(segment)?; Ok(()) } fn visit_table_copy(&mut self, dst_table: u32, src_table: u32) -> Self::Output { @@ -4073,28 +4105,17 @@ where // Typed continuations operators. fn visit_cont_new(&mut self, type_index: u32) -> Self::Output { - let unpacked_index = UnpackedIndex::Module(type_index); - let mut c_hty = HeapType::Concrete(unpacked_index); - self.resources.check_heap_type(&mut c_hty, self.offset)?; - let f_idx = self.cont_type_of_heap_type(c_hty)?.0; - let f_hty = f_idx.pack().expect("hty should be previously validated"); - let expected_rt = RefType::concrete(true, f_hty); - self.pop_operand(Some(ValType::Ref(expected_rt)))?; - let result = - ValType::Ref(RefType::new(false, c_hty).expect("hty should be previously validated")); - self.push_operand(result)?; + let cont_ty = self.cont_type_at(type_index)?; + let rt = RefType::concrete(true, cont_ty.0.pack().expect("type index too large")); + self.pop_ref(Some(rt))?; + self.push_concrete_ref(false, type_index)?; Ok(()) } fn visit_cont_bind(&mut self, src_index: u32, dst_index: u32) -> Self::Output { - let src_unpacked_index = UnpackedIndex::Module(src_index); - let mut src_hty = HeapType::Concrete(src_unpacked_index); - self.resources.check_heap_type(&mut src_hty, self.offset)?; - let src_cont = self.func_repr_cont_type_of_heap_type(src_hty)?; - - let dst_unpacked_index = UnpackedIndex::Module(dst_index); - let mut dst_hty = HeapType::Concrete(dst_unpacked_index); - self.resources.check_heap_type(&mut dst_hty, self.offset)?; - let dst_cont = self.func_repr_cont_type_of_heap_type(dst_hty)?; + self.cont_type_at(src_index)?; + self.cont_type_at(dst_index)?; + let src_cont = self.func_repr_cont_type_at(src_index)?; + let dst_cont = self.func_repr_cont_type_at(dst_index)?; // Verify that the source domain is at least as large as the // target domain. @@ -4126,21 +4147,21 @@ where } // Check that the continuation is available on the stack. - match self.pop_ref(None)? { - MaybeType::Bot => {} // bot case - MaybeType::HeapBot(_) => {} - MaybeType::Type(rt) => { - let expected = ValType::Ref( - RefType::new(false, src_hty).expect("hty should be previously validated"), - ); - if !self.resources.is_subtype(expected, ValType::Ref(rt)) { - bail!( - self.offset, - "type mismatch: instruction requires {} but stack has {}", - ty_to_str(expected), - ty_to_str(ValType::Ref(rt)) - ); - } + match self.pop_concrete_ref(true, src_index)? { + MaybeType::Bottom => {} // bot case + MaybeType::UnknownRef(_) => {} // TODO(dhil): check that the abstract heap type is a continuation. + MaybeType::Known(_rt) => { + // let expected = ValType::Ref( + // RefType::concrete(false, ).expect("hty should be previously validated"), + // ); + // if !self.resources.is_subtype(expected, ValType::Ref(rt)) { + // bail!( + // self.offset, + // "type mismatch: instruction requires {} but stack has {}", + // ty_to_str(expected), + // ty_to_str(ValType::Ref(rt)) + // ); + // } // Check that the prefix is available on the stack. for &ty in src_prefix.rev() { @@ -4150,11 +4171,7 @@ where } // Construct the result type. - let result = - ValType::Ref(RefType::new(false, dst_hty).expect("hty should be previously validated")); - - // Push the continuation reference. - self.push_operand(result)?; + self.push_concrete_ref(false, dst_index)?; Ok(()) } @@ -4169,30 +4186,20 @@ where Ok(()) } fn visit_resume(&mut self, type_index: u32, resumetable: ResumeTable) -> Self::Output { - let unpacked_index = UnpackedIndex::Module(type_index); - let mut hty = HeapType::Concrete(unpacked_index); - self.resources.check_heap_type(&mut hty, self.offset)?; - let _ = self.cont_type_of_heap_type(hty); + self.cont_type_at(type_index)?; + self.pop_concrete_ref(true, type_index)?; + let ctft = self.func_repr_cont_type_at(type_index)?; + // ft := ts1 -> ts2 + self.check_resume_table(resumetable, ctft)?; - let expected = RefType::new(true, hty).expect("hty should be previously validated"); - match self.pop_ref(Some(expected))? { - MaybeType::Bot => {} - MaybeType::HeapBot(_) => {} - MaybeType::Type(_) => { - let ctft = self.func_repr_cont_type_of_heap_type(hty)?; - // ft := ts1 -> ts2 - self.check_resume_table(resumetable, ctft)?; - - // Check that ts1 are available on the stack. - for &ty in ctft.params().iter().rev() { - self.pop_operand(Some(ty))?; - } + // Check that ts1 are available on the stack. + for &ty in ctft.params().iter().rev() { + self.pop_operand(Some(ty))?; + } - // Make ts2 available on the stack. - for &ty in ctft.results() { - self.push_operand(ty)?; - } - } + // Make ts2 available on the stack. + for &ty in ctft.results() { + self.push_operand(ty)?; } Ok(()) } @@ -4202,32 +4209,24 @@ where tag_index: u32, resumetable: ResumeTable, ) -> Self::Output { - let unpacked_index = UnpackedIndex::Module(type_index); - let mut hty = HeapType::Concrete(unpacked_index); - self.resources.check_heap_type(&mut hty, self.offset)?; - let _ = self.cont_type_of_heap_type(hty); - let expected = RefType::new(true, hty).expect("hty should be previously validated"); - match self.pop_ref(Some(expected))? { - MaybeType::Bot => {} - MaybeType::HeapBot(_) => {} - MaybeType::Type(_) => { - // ft := ts1 -> ts2 - let ctft = self.func_repr_cont_type_of_heap_type(hty)?; - self.check_resume_table(resumetable, &ctft)?; - - // tagtype := ts1' -> [] - let tagtype = &self.tag_at(tag_index)?; - - // Check that ts1' are available on the stack. - for &tagty in tagtype.params().iter().rev() { - self.pop_operand(Some(tagty))?; - } + self.cont_type_at(type_index)?; + self.pop_concrete_ref(true, type_index)?; + let ctft = self.func_repr_cont_type_at(type_index)?; - // Make ts2 available on the stack. - for &ty in ctft.results() { - self.push_operand(ty)?; - } - } + // tagtype := ts1' -> [] + let tagtype = &self.tag_at(tag_index)?; + + // Validate the resume table. + self.check_resume_table(resumetable, &ctft)?; + + // Check that ts1' are available on the stack. + for &tagty in tagtype.params().iter().rev() { + self.pop_operand(Some(tagty))?; + } + + // Make ts2 available on the stack. + for &ty in ctft.results() { + self.push_operand(ty)?; } Ok(()) @@ -4546,11 +4545,7 @@ where "type mismatch: array.new_data can only create arrays with numeric and vector elements" ), } - match self.resources.data_count() { - None => bail!(self.offset, "data count section required"), - Some(count) if data_index < count => {} - Some(_) => bail!(self.offset, "unknown data segment {}", data_index), - } + self.check_data_segment(data_index)?; self.pop_operand(Some(ValType::I32))?; self.pop_operand(Some(ValType::I32))?; self.push_concrete_ref(false, type_index) @@ -4743,11 +4738,7 @@ where "invalid array.init_data: array type is not numeric or vector" ), } - match self.resources.data_count() { - None => bail!(self.offset, "data count section required"), - Some(count) if array_data_index < count => {} - Some(_) => bail!(self.offset, "unknown data segment {}", array_data_index), - } + self.check_data_segment(array_data_index)?; self.pop_operand(Some(ValType::I32))?; self.pop_operand(Some(ValType::I32))?; self.pop_operand(Some(ValType::I32))?; @@ -4850,34 +4841,34 @@ where } fn visit_any_convert_extern(&mut self) -> Self::Output { let any_ref = match self.pop_maybe_shared_ref(AbstractHeapType::Extern)? { - MaybeType::Bot | MaybeType::HeapBot(_) => { - MaybeType::HeapBot(Some(AbstractHeapType::Any)) + MaybeType::Bottom | MaybeType::UnknownRef(_) => { + MaybeType::UnknownRef(Some(AbstractHeapType::Any)) } - MaybeType::Type(ty) => { + MaybeType::Known(ty) => { let shared = self.resources.is_shared(ty); let heap_type = HeapType::Abstract { shared, ty: AbstractHeapType::Any, }; let any_ref = RefType::new(ty.is_nullable(), heap_type).unwrap(); - MaybeType::Type(any_ref) + MaybeType::Known(any_ref) } }; self.push_operand(any_ref) } fn visit_extern_convert_any(&mut self) -> Self::Output { let extern_ref = match self.pop_maybe_shared_ref(AbstractHeapType::Any)? { - MaybeType::Bot | MaybeType::HeapBot(_) => { - MaybeType::HeapBot(Some(AbstractHeapType::Extern)) + MaybeType::Bottom | MaybeType::UnknownRef(_) => { + MaybeType::UnknownRef(Some(AbstractHeapType::Extern)) } - MaybeType::Type(ty) => { + MaybeType::Known(ty) => { let shared = self.resources.is_shared(ty); let heap_type = HeapType::Abstract { shared, ty: AbstractHeapType::Extern, }; let extern_ref = RefType::new(ty.is_nullable(), heap_type).unwrap(); - MaybeType::Type(extern_ref) + MaybeType::Known(extern_ref) } }; self.push_operand(extern_ref) diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index b7c4d69784..86165a1f99 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -1176,7 +1176,11 @@ impl Printer<'_, '_> { let p = 1_u64 .checked_shl(p) .ok_or_else(|| anyhow!("left shift overflow").context("invalid page size"))?; - write!(self.result, "(pagesize {p:#x})")?; + + self.result.write_str(" ")?; + self.start_group("pagesize ")?; + write!(self.result, "{p:#x}")?; + self.end_group()?; } Ok(()) } diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml index 2ea999c3d2..c9b949c3a9 100644 --- a/crates/wast/Cargo.toml +++ b/crates/wast/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wast" -version = "216.0.0" +version = "217.0.0" authors = ["Alex Crichton "] edition.workspace = true license.workspace = true diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs index bbcc98d195..f63af04222 100644 --- a/crates/wast/src/component/binary.rs +++ b/crates/wast/src/component/binary.rs @@ -609,27 +609,27 @@ impl From> for wasm_encoder::HeapType { fn from(r: core::HeapType<'_>) -> Self { use wasm_encoder::AbstractHeapType::*; match r { - core::HeapType::Abstract { shared, ty } => match ty { - core::AbstractHeapType::Func => Self::Abstract { shared, ty: Func }, - core::AbstractHeapType::Extern => Self::Abstract { shared, ty: Extern }, - core::AbstractHeapType::Exn | core::AbstractHeapType::NoExn => { - todo!("encoding of exceptions proposal types not yet implemented") - } - core::AbstractHeapType::Any - | core::AbstractHeapType::Eq - | core::AbstractHeapType::Struct - | core::AbstractHeapType::Array - | core::AbstractHeapType::NoFunc - | core::AbstractHeapType::NoExtern - | core::AbstractHeapType::None - | core::AbstractHeapType::I31 => { - todo!("encoding of GC proposal types not yet implemented") - } - core::AbstractHeapType::Cont | core::AbstractHeapType::NoCont => { - todo!("encoding of typed continuations proposal types not yet implemented") - // TODO(dhil): revisit later. - } - }, + core::HeapType::Abstract { shared, ty } => { + let ty = match ty { + core::AbstractHeapType::Func => Func, + core::AbstractHeapType::Extern => Extern, + core::AbstractHeapType::Exn => Exn, + core::AbstractHeapType::NoExn => NoExn, + core::AbstractHeapType::Any => Any, + core::AbstractHeapType::Eq => Eq, + core::AbstractHeapType::Struct => Struct, + core::AbstractHeapType::Array => Array, + core::AbstractHeapType::NoFunc => NoFunc, + core::AbstractHeapType::NoExtern => NoExtern, + core::AbstractHeapType::None => None, + core::AbstractHeapType::I31 => I31, + core::AbstractHeapType::Cont | core::AbstractHeapType::NoCont => { + todo!("encoding of typed continuations proposal types not yet implemented") + // TODO(dhil): revisit later. + } + }; + Self::Abstract { shared, ty } + } core::HeapType::Concrete(Index::Num(i, _)) => Self::Concrete(i), core::HeapType::Concrete(_) => panic!("unresolved index"), } diff --git a/crates/wast/src/component/component.rs b/crates/wast/src/component/component.rs index 92814db0e7..36c823190f 100644 --- a/crates/wast/src/component/component.rs +++ b/crates/wast/src/component/component.rs @@ -103,16 +103,11 @@ impl<'a> Component<'a> { } Ok(()) } -} - -impl<'a> Parse<'a> for Component<'a> { - fn parse(parser: Parser<'a>) -> Result { - let _r = parser.register_annotation("custom"); - let _r = parser.register_annotation("producers"); - let _r = parser.register_annotation("name"); - let _r = parser.register_annotation("metadata.code.branch_hint"); - let span = parser.parse::()?.0; + pub(crate) fn parse_without_component_keyword( + component_keyword_span: Span, + parser: Parser<'a>, + ) -> Result { let id = parser.parse()?; let name = parser.parse()?; @@ -127,7 +122,7 @@ impl<'a> Parse<'a> for Component<'a> { ComponentKind::Text(ComponentField::parse_remaining(parser)?) }; Ok(Component { - span, + span: component_keyword_span, id, name, kind, @@ -135,6 +130,15 @@ impl<'a> Parse<'a> for Component<'a> { } } +impl<'a> Parse<'a> for Component<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.with_standard_annotations_registered(|parser| { + let span = parser.parse::()?.0; + Component::parse_without_component_keyword(span, parser) + }) + } +} + /// A listing of all possible fields that can make up a WebAssembly component. #[allow(missing_docs)] #[derive(Debug)] @@ -157,7 +161,7 @@ pub enum ComponentField<'a> { } impl<'a> ComponentField<'a> { - fn parse_remaining(parser: Parser<'a>) -> Result> { + fn parse_remaining(parser: Parser<'a>) -> Result>> { let mut fields = Vec::new(); while !parser.is_empty() { fields.push(parser.parens(ComponentField::parse)?); diff --git a/crates/wast/src/component/import.rs b/crates/wast/src/component/import.rs index 6689f4a765..81c8e5e406 100644 --- a/crates/wast/src/component/import.rs +++ b/crates/wast/src/component/import.rs @@ -80,30 +80,31 @@ impl<'a> Parse<'a> for ItemSigNoName<'a> { fn parse_item_sig<'a>(parser: Parser<'a>, name: bool) -> Result> { let mut l = parser.lookahead1(); - let (span, parse_kind): (_, fn(Parser<'a>) -> Result) = if l.peek::()? { - let span = parser.parse::()?.0; - parser.parse::()?; - (span, |parser| Ok(ItemSigKind::CoreModule(parser.parse()?))) - } else if l.peek::()? { - let span = parser.parse::()?.0; - (span, |parser| Ok(ItemSigKind::Func(parser.parse()?))) - } else if l.peek::()? { - let span = parser.parse::()?.0; - (span, |parser| Ok(ItemSigKind::Component(parser.parse()?))) - } else if l.peek::()? { - let span = parser.parse::()?.0; - (span, |parser| Ok(ItemSigKind::Instance(parser.parse()?))) - } else if l.peek::()? { - let span = parser.parse::()?.0; - (span, |parser| Ok(ItemSigKind::Value(parser.parse()?))) - } else if l.peek::()? { - let span = parser.parse::()?.0; - (span, |parser| { - Ok(ItemSigKind::Type(parser.parens(|parser| parser.parse())?)) - }) - } else { - return Err(l.error()); - }; + let (span, parse_kind): (_, fn(Parser<'a>) -> Result>) = + if l.peek::()? { + let span = parser.parse::()?.0; + parser.parse::()?; + (span, |parser| Ok(ItemSigKind::CoreModule(parser.parse()?))) + } else if l.peek::()? { + let span = parser.parse::()?.0; + (span, |parser| Ok(ItemSigKind::Func(parser.parse()?))) + } else if l.peek::()? { + let span = parser.parse::()?.0; + (span, |parser| Ok(ItemSigKind::Component(parser.parse()?))) + } else if l.peek::()? { + let span = parser.parse::()?.0; + (span, |parser| Ok(ItemSigKind::Instance(parser.parse()?))) + } else if l.peek::()? { + let span = parser.parse::()?.0; + (span, |parser| Ok(ItemSigKind::Value(parser.parse()?))) + } else if l.peek::()? { + let span = parser.parse::()?.0; + (span, |parser| { + Ok(ItemSigKind::Type(parser.parens(|parser| parser.parse())?)) + }) + } else { + return Err(l.error()); + }; Ok(ItemSig { span, id: if name { parser.parse()? } else { None }, diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs index ac072c5bf1..1dfd260ad8 100644 --- a/crates/wast/src/component/resolve.rs +++ b/crates/wast/src/component/resolve.rs @@ -1,5 +1,5 @@ use crate::component::*; -use crate::core::{self, ValType}; +use crate::core::{self, resolve::ResolveCoreType, ValType}; use crate::kw; use crate::names::Namespace; use crate::token::Span; @@ -475,7 +475,7 @@ impl<'a> Resolver<'a> { fn core_ty(&mut self, field: &mut CoreType<'a>) -> Result<(), Error> { match &mut field.def { - CoreTypeDef::Def(_) => {} + CoreTypeDef::Def(ty) => self.stack.last_mut().unwrap().resolve_type_def(ty)?, CoreTypeDef::Module(t) => { self.stack.push(ComponentState::new(field.id)); self.module_type(t)?; @@ -766,7 +766,7 @@ impl<'a> Resolver<'a> { } impl<'a> ComponentState<'a> { - fn resolve(&mut self, ns: Ns, idx: &mut Index<'a>) -> Result { + fn resolve(&self, ns: Ns, idx: &mut Index<'a>) -> Result { match ns { Ns::CoreFunc => self.core_funcs.resolve(idx, "core func"), Ns::CoreGlobal => self.core_globals.resolve(idx, "core global"), @@ -868,6 +868,12 @@ impl<'a> ComponentState<'a> { } } +impl<'a> ResolveCoreType<'a> for ComponentState<'a> { + fn resolve_type_name(&self, name: &mut Index<'a>) -> Result { + self.resolve(Ns::CoreType, name) + } +} + #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] enum Ns { CoreFunc, diff --git a/crates/wast/src/core/binary.rs b/crates/wast/src/core/binary.rs index 65e31bbeb2..9f9f5e6a44 100644 --- a/crates/wast/src/core/binary.rs +++ b/crates/wast/src/core/binary.rs @@ -6,6 +6,7 @@ use crate::Wat; use std::marker; #[cfg(feature = "dwarf")] use std::path::Path; +use wasm_encoder::SectionId; /// Options that can be specified when encoding a component or a module to /// customize what the final binary looks like. @@ -135,33 +136,35 @@ pub(crate) fn encode( } let mut e = Encoder { - wasm: Vec::new(), + wasm: wasm_encoder::Module::new(), tmp: Vec::new(), customs: &customs, }; - e.wasm.extend(b"\0asm"); - e.wasm.extend(b"\x01\0\0\0"); e.custom_sections(BeforeFirst); - e.section_list(1, Type, &types); - e.section_list(2, Import, &imports); + e.section_list(SectionId::Type, Type, &types); + e.section_list(SectionId::Import, Import, &imports); let functys = funcs.iter().map(|f| &f.ty).collect::>(); - e.section_list(3, Func, &functys); - e.section_list(4, Table, &tables); - e.section_list(5, Memory, &memories); - e.section_list(13, Tag, &tags); - e.section_list(6, Global, &globals); - e.section_list(7, Export, &exports); + e.section_list(SectionId::Function, Func, &functys); + e.section_list(SectionId::Table, Table, &tables); + e.section_list(SectionId::Memory, Memory, &memories); + e.section_list(SectionId::Tag, Tag, &tags); + e.section_list(SectionId::Global, Global, &globals); + e.section_list(SectionId::Export, Export, &exports); e.custom_sections(Before(Start)); if let Some(start) = start.get(0) { - e.section(8, start); + e.wasm.section(&wasm_encoder::StartSection { + function_index: start.unwrap_u32(), + }); } e.custom_sections(After(Start)); - e.section_list(9, Elem, &elem); + e.section_list(SectionId::Element, Elem, &elem); if needs_data_count(&funcs) { - e.section(12, &data.len()); + e.wasm.section(&wasm_encoder::DataCountSection { + count: data.len().try_into().unwrap(), + }); } // Prepare to and emit the code section. This is where DWARF may optionally @@ -175,17 +178,17 @@ pub(crate) fn encode( let mut dwarf = dwarf::Dwarf::new(num_import_funcs, opts, &names, &types); e.code_section(&funcs, num_import_funcs, dwarf.as_mut()); - e.section_list(11, Data, &data); + e.section_list(SectionId::Data, Data, &data); if !names.is_empty() { - e.section(0, &("name", names)); + e.custom_section("name", &names); } e.custom_sections(AfterLast); if let Some(dwarf) = &mut dwarf { dwarf.emit(&mut e); } - return e.wasm; + return e.wasm.finish(); fn needs_data_count(funcs: &[&crate::core::Func<'_>]) -> bool { funcs @@ -200,38 +203,44 @@ pub(crate) fn encode( } struct Encoder<'a> { - wasm: Vec, + wasm: wasm_encoder::Module, tmp: Vec, customs: &'a [&'a Custom<'a>], } impl Encoder<'_> { - fn section(&mut self, id: u8, section: &dyn Encode) { + fn section(&mut self, id: SectionId, section: &dyn Encode) { self.tmp.truncate(0); section.encode(&mut self.tmp); - self.wasm.push(id); - self.tmp.encode(&mut self.wasm); + self.wasm.section(&wasm_encoder::RawSection { + id: id as u8, + data: &self.tmp, + }); } fn custom_sections(&mut self, place: CustomPlace) { for entry in self.customs.iter() { if entry.place() == place { - let mut data = Vec::new(); - entry.encode(&mut data); - self.custom_section(entry.name(), &data); + self.custom_section(entry.name(), entry); } } } - fn custom_section(&mut self, name: &str, data: &[u8]) { + fn custom_section(&mut self, name: &str, data: &dyn Encode) { self.tmp.truncate(0); - name.encode(&mut self.tmp); - self.tmp.extend_from_slice(data); - self.wasm.push(0); - self.tmp.encode(&mut self.wasm); - } - - fn section_list(&mut self, id: u8, anchor: CustomPlaceAnchor, list: &[impl Encode]) { + data.encode(&mut self.tmp); + self.wasm.section(&wasm_encoder::CustomSection { + name: name.into(), + data: (&self.tmp).into(), + }); + } + + fn section_list( + &mut self, + id: wasm_encoder::SectionId, + anchor: CustomPlaceAnchor, + list: &[impl Encode], + ) { self.custom_sections(CustomPlace::Before(anchor)); if !list.is_empty() { self.section(id, &list) @@ -276,12 +285,14 @@ impl Encoder<'_> { // Branch hints section has to be inserted before the Code section // Insert the section only if we have some hints if !branch_hints.is_empty() { - self.section(0, &("metadata.code.branch_hint", branch_hints)); + self.custom_section("metadata.code.branch_hint", &branch_hints); } // Finally, insert the Code section from the tmp buffer - self.wasm.push(10); - code_section.encode(&mut self.wasm); + self.wasm.section(&wasm_encoder::RawSection { + id: wasm_encoder::SectionId::Code as u8, + data: &code_section, + }); if let Some(dwarf) = &mut dwarf { dwarf.set_code_section_size(code_section.len()); @@ -554,8 +565,14 @@ impl Encode for TypeUse<'_, T> { impl Encode for Index<'_> { fn encode(&self, e: &mut Vec) { + self.unwrap_u32().encode(e) + } +} + +impl Index<'_> { + fn unwrap_u32(&self) -> u32 { match self { - Index::Num(n, _) => n.encode(e), + Index::Num(n, _) => *n, Index::Id(n) => panic!("unresolved index in emission: {:?}", n), } } diff --git a/crates/wast/src/core/binary/dwarf.rs b/crates/wast/src/core/binary/dwarf.rs index b6aa086ef9..b080f255c3 100644 --- a/crates/wast/src/core/binary/dwarf.rs +++ b/crates/wast/src/core/binary/dwarf.rs @@ -432,7 +432,10 @@ impl<'a> Dwarf<'a> { sections .for_each(|id, writer| { if !writer.bytes.is_empty() { - dst.custom_section(id.name(), &writer.bytes); + dst.wasm.section(&wasm_encoder::CustomSection { + name: id.name().into(), + data: (&writer.bytes).into(), + }); } Ok::<_, std::convert::Infallible>(()) }) diff --git a/crates/wast/src/core/expr.rs b/crates/wast/src/core/expr.rs index d023415800..f36eaaedcd 100644 --- a/crates/wast/src/core/expr.rs +++ b/crates/wast/src/core/expr.rs @@ -162,7 +162,7 @@ enum If<'a> { } impl<'a> ExpressionParser<'a> { - fn new(parser: Parser<'a>) -> ExpressionParser { + fn new(parser: Parser<'a>) -> ExpressionParser<'a> { ExpressionParser { raw_instrs: Vec::new(), stack: Vec::new(), diff --git a/crates/wast/src/core/module.rs b/crates/wast/src/core/module.rs index bba4a8dd95..e80719630b 100644 --- a/crates/wast/src/core/module.rs +++ b/crates/wast/src/core/module.rs @@ -103,17 +103,11 @@ impl<'a> Module<'a> { } Ok(()) } -} -impl<'a> Parse<'a> for Module<'a> { - fn parse(parser: Parser<'a>) -> Result { - let _r = parser.register_annotation("custom"); - let _r = parser.register_annotation("producers"); - let _r = parser.register_annotation("name"); - let _r = parser.register_annotation("dylink.0"); - let _r = parser.register_annotation("metadata.code.branch_hint"); - - let span = parser.parse::()?.0; + pub(crate) fn parse_without_module_keyword( + module_keyword_span: Span, + parser: Parser<'a>, + ) -> Result { let id = parser.parse()?; let name = parser.parse()?; @@ -128,7 +122,7 @@ impl<'a> Parse<'a> for Module<'a> { ModuleKind::Text(ModuleField::parse_remaining(parser)?) }; Ok(Module { - span, + span: module_keyword_span, id, name, kind, @@ -136,6 +130,15 @@ impl<'a> Parse<'a> for Module<'a> { } } +impl<'a> Parse<'a> for Module<'a> { + fn parse(parser: Parser<'a>) -> Result { + parser.with_standard_annotations_registered(|parser| { + let span = parser.parse::()?.0; + Self::parse_without_module_keyword(span, parser) + }) + } +} + /// A listing of all possible fields that can make up a WebAssembly module. #[allow(missing_docs)] #[derive(Debug)] @@ -156,7 +159,7 @@ pub enum ModuleField<'a> { } impl<'a> ModuleField<'a> { - pub(crate) fn parse_remaining(parser: Parser<'a>) -> Result> { + pub(crate) fn parse_remaining(parser: Parser<'a>) -> Result>> { let mut fields = Vec::new(); while !parser.is_empty() { fields.push(parser.parens(ModuleField::parse)?); diff --git a/crates/wast/src/core/resolve/mod.rs b/crates/wast/src/core/resolve/mod.rs index 7b3ba2b1e3..a5bcf623db 100644 --- a/crates/wast/src/core/resolve/mod.rs +++ b/crates/wast/src/core/resolve/mod.rs @@ -6,6 +6,8 @@ mod deinline_import_export; mod names; pub(crate) mod types; +pub(crate) use names::ResolveCoreType; + #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub enum Ns { Func, diff --git a/crates/wast/src/core/resolve/names.rs b/crates/wast/src/core/resolve/names.rs index 0361bfff62..07d7971516 100644 --- a/crates/wast/src/core/resolve/names.rs +++ b/crates/wast/src/core/resolve/names.rs @@ -119,23 +119,6 @@ impl<'a> Resolver<'a> { Ok(()) } - fn resolve_type(&self, ty: &mut Type<'a>) -> Result<(), Error> { - match &mut ty.def.kind { - InnerTypeKind::Func(func) => func.resolve(self)?, - InnerTypeKind::Struct(struct_) => { - for field in &mut struct_.fields { - self.resolve_storagetype(&mut field.ty)?; - } - } - InnerTypeKind::Cont(cont) => self.resolve_continuationtype(cont)?, - InnerTypeKind::Array(array) => self.resolve_storagetype(&mut array.ty)?, - } - if let Some(parent) = &mut ty.parent { - self.resolve(parent, Ns::Type)?; - } - Ok(()) - } - fn resolve_field(&self, field: &mut ModuleField<'a>) -> Result<(), Error> { match field { ModuleField::Import(i) => { @@ -280,41 +263,6 @@ impl<'a> Resolver<'a> { } } - fn resolve_valtype(&self, ty: &mut ValType<'a>) -> Result<(), Error> { - match ty { - ValType::Ref(ty) => self.resolve_heaptype(&mut ty.heap)?, - _ => {} - } - Ok(()) - } - - fn resolve_reftype(&self, ty: &mut RefType<'a>) -> Result<(), Error> { - self.resolve_heaptype(&mut ty.heap) - } - - fn resolve_heaptype(&self, ty: &mut HeapType<'a>) -> Result<(), Error> { - match ty { - HeapType::Concrete(i) => { - self.resolve(i, Ns::Type)?; - } - _ => {} - } - Ok(()) - } - - fn resolve_storagetype(&self, ty: &mut StorageType<'a>) -> Result<(), Error> { - match ty { - StorageType::Val(ty) => self.resolve_valtype(ty)?, - _ => {} - } - Ok(()) - } - - fn resolve_continuationtype(&self, ty: &mut ContinuationType<'a>) -> Result<(), Error> { - self.resolve(&mut ty.idx, Ns::Type)?; - Ok(()) - } - fn resolve_item_sig(&self, item: &mut ItemSig<'a>) -> Result<(), Error> { match &mut item.kind { ItemKind::Func(t) | ItemKind::Tag(TagType::Exception(t)) => { @@ -807,13 +755,82 @@ impl<'a> TypeReference<'a> for FunctionType<'a> { } fn resolve(&mut self, cx: &Resolver<'a>) -> Result<(), Error> { + cx.resolve_type_func(self) + } +} + +pub(crate) trait ResolveCoreType<'a> { + fn resolve_type_name(&self, name: &mut Index<'a>) -> Result; + + fn resolve_type(&self, ty: &mut Type<'a>) -> Result<(), Error> { + self.resolve_type_def(&mut ty.def)?; + if let Some(parent) = &mut ty.parent { + self.resolve_type_name(parent)?; + } + Ok(()) + } + + fn resolve_type_def(&self, ty: &mut TypeDef<'a>) -> Result<(), Error> { + match &mut ty.kind { + InnerTypeKind::Func(func) => self.resolve_type_func(func), + InnerTypeKind::Struct(struct_) => { + for field in &mut struct_.fields { + self.resolve_storagetype(&mut field.ty)?; + } + Ok(()) + } + InnerTypeKind::Array(array) => self.resolve_storagetype(&mut array.ty), + InnerTypeKind::Cont(cont) => self.resolve_continuationtype(cont), + } + } + + fn resolve_type_func(&self, ty: &mut FunctionType<'a>) -> Result<(), Error> { // Resolve the (ref T) value types in the final function type - for param in self.params.iter_mut() { - cx.resolve_valtype(&mut param.2)?; + for param in ty.params.iter_mut() { + self.resolve_valtype(&mut param.2)?; } - for result in self.results.iter_mut() { - cx.resolve_valtype(result)?; + for result in ty.results.iter_mut() { + self.resolve_valtype(result)?; } Ok(()) } + + fn resolve_valtype(&self, ty: &mut ValType<'a>) -> Result<(), Error> { + match ty { + ValType::Ref(ty) => self.resolve_reftype(ty), + ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => Ok(()), + } + } + + fn resolve_reftype(&self, ty: &mut RefType<'a>) -> Result<(), Error> { + self.resolve_heaptype(&mut ty.heap) + } + + fn resolve_heaptype(&self, ty: &mut HeapType<'a>) -> Result<(), Error> { + match ty { + HeapType::Concrete(i) => { + self.resolve_type_name(i)?; + } + HeapType::Abstract { .. } => {} + } + Ok(()) + } + + fn resolve_storagetype(&self, ty: &mut StorageType<'a>) -> Result<(), Error> { + match ty { + StorageType::Val(ty) => self.resolve_valtype(ty), + StorageType::I8 | StorageType::I16 => Ok(()), + } + } + + fn resolve_continuationtype(&self, ty: &mut ContinuationType<'a>) -> Result<(), Error> { + self.resolve_type_name(&mut ty.idx)?; + Ok(()) + } +} + +impl<'a> ResolveCoreType<'a> for Resolver<'a> { + fn resolve_type_name(&self, name: &mut Index<'a>) -> Result { + self.resolve(name, Ns::Type) + } } diff --git a/crates/wast/src/lexer.rs b/crates/wast/src/lexer.rs index 0fd9a72d64..c1a04deb8f 100644 --- a/crates/wast/src/lexer.rs +++ b/crates/wast/src/lexer.rs @@ -61,9 +61,10 @@ pub struct Token { pub len: u32, } -const _: () = { +#[test] +fn token_is_not_too_big() { assert!(std::mem::size_of::() <= std::mem::size_of::() * 2); -}; +} /// Classification of what was parsed from the input stream. /// diff --git a/crates/wast/src/lib.rs b/crates/wast/src/lib.rs index 535866ce68..3fc0e12ebd 100644 --- a/crates/wast/src/lib.rs +++ b/crates/wast/src/lib.rs @@ -551,6 +551,7 @@ pub mod kw { custom_keyword!(import_info = "import-info"); custom_keyword!(thread); custom_keyword!(wait); + custom_keyword!(definition); } /// Common annotations used to parse WebAssembly text files. diff --git a/crates/wast/src/parser.rs b/crates/wast/src/parser.rs index 305abe9eaf..8af058338b 100644 --- a/crates/wast/src/parser.rs +++ b/crates/wast/src/parser.rs @@ -993,6 +993,19 @@ impl<'a> Parser<'a> { pub(crate) fn track_instr_spans(&self) -> bool { self.buf.track_instr_spans } + + #[cfg(feature = "wasm-module")] + pub(crate) fn with_standard_annotations_registered( + self, + f: impl FnOnce(Self) -> Result, + ) -> Result { + let _r = self.register_annotation("custom"); + let _r = self.register_annotation("producers"); + let _r = self.register_annotation("name"); + let _r = self.register_annotation("dylink.0"); + let _r = self.register_annotation("metadata.code.branch_hint"); + f(self) + } } impl<'a> Cursor<'a> { diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index 34b0b3786d..b57f487789 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -20,17 +20,19 @@ impl<'a> Parse<'a> for Wast<'a> { fn parse(parser: Parser<'a>) -> Result { let mut directives = Vec::new(); - // If it looks like a directive token is in the stream then we parse a - // bunch of directives, otherwise assume this is an inline module. - if parser.peek2::()? { - while !parser.is_empty() { - directives.push(parser.parens(|p| p.parse())?); + parser.with_standard_annotations_registered(|parser| { + // If it looks like a directive token is in the stream then we parse a + // bunch of directives, otherwise assume this is an inline module. + if parser.peek2::()? { + while !parser.is_empty() { + directives.push(parser.parens(|p| p.parse())?); + } + } else { + let module = parser.parse::()?; + directives.push(WastDirective::Module(QuoteWat::Wat(module))); } - } else { - let module = parser.parse::()?; - directives.push(WastDirective::Wat(QuoteWat::Wat(module))); - } - Ok(Wast { directives }) + Ok(Wast { directives }) + }) } } @@ -56,72 +58,110 @@ impl Peek for WastDirectiveToken { /// The different kinds of directives found in a `*.wast` file. /// -/// It's not entirely clear to me what all of these are per se, but they're only -/// really interesting to test harnesses mostly. +/// +/// Some more information about these various branches can be found at +/// . #[allow(missing_docs)] #[derive(Debug)] pub enum WastDirective<'a> { - Wat(QuoteWat<'a>), + /// The provided module is defined, validated, and then instantiated. + Module(QuoteWat<'a>), + + /// The provided module is defined and validated. + /// + /// This module is not instantiated automatically. + ModuleDefinition(QuoteWat<'a>), + + /// The named module is instantiated under the instance name provided. + ModuleInstance { + span: Span, + instance: Option>, + module: Option>, + }, + + /// Asserts the module cannot be decoded with the given error. AssertMalformed { span: Span, module: QuoteWat<'a>, message: &'a str, }, + + /// Asserts the module cannot be validated with the given error. AssertInvalid { span: Span, module: QuoteWat<'a>, message: &'a str, }, + + /// Registers the `module` instance with the given `name` to be available + /// for importing in future module instances. Register { span: Span, name: &'a str, module: Option>, }, + + /// Invokes the specified export. Invoke(WastInvoke<'a>), + + /// The invocation provided should trap with the specified error. AssertTrap { span: Span, exec: WastExecute<'a>, message: &'a str, }, + + /// The invocation provided should succeed with the specified results. AssertReturn { span: Span, exec: WastExecute<'a>, results: Vec>, }, + + /// The invocation provided should exhaust system resources (e.g. stack + /// overflow). AssertExhaustion { span: Span, call: WastInvoke<'a>, message: &'a str, }, + + /// The provided module should fail to link when instantiation is attempted. AssertUnlinkable { span: Span, module: Wat<'a>, message: &'a str, }, - AssertException { - span: Span, - exec: WastExecute<'a>, - }, + + /// The invocation should fail to handle a suspension. AssertSuspension { span: Span, exec: WastExecute<'a>, message: &'a str, }, + + /// The invocation provided should throw an exception. + AssertException { span: Span, exec: WastExecute<'a> }, + + /// Creates a new system thread which executes the given commands. Thread(WastThread<'a>), - Wait { - span: Span, - thread: Id<'a>, - }, + + /// Waits for the specified thread to exit. + Wait { span: Span, thread: Id<'a> }, } impl WastDirective<'_> { /// Returns the location in the source that this directive was defined at pub fn span(&self) -> Span { match self { - WastDirective::Wat(QuoteWat::Wat(w)) => w.span(), - WastDirective::Wat(QuoteWat::QuoteModule(span, _)) => *span, - WastDirective::Wat(QuoteWat::QuoteComponent(span, _)) => *span, - WastDirective::AssertMalformed { span, .. } + WastDirective::Module(QuoteWat::Wat(w)) + | WastDirective::ModuleDefinition(QuoteWat::Wat(w)) => w.span(), + WastDirective::Module(QuoteWat::QuoteModule(span, _)) + | WastDirective::ModuleDefinition(QuoteWat::QuoteModule(span, _)) => *span, + WastDirective::Module(QuoteWat::QuoteComponent(span, _)) + | WastDirective::ModuleDefinition(QuoteWat::QuoteComponent(span, _)) => *span, + WastDirective::ModuleInstance { span, .. } + | WastDirective::AssertMalformed { span, .. } | WastDirective::Register { span, .. } | WastDirective::AssertTrap { span, .. } | WastDirective::AssertReturn { span, .. } @@ -141,7 +181,7 @@ impl<'a> Parse<'a> for WastDirective<'a> { fn parse(parser: Parser<'a>) -> Result { let mut l = parser.lookahead1(); if l.peek::()? || l.peek::()? { - Ok(WastDirective::Wat(parser.parse()?)) + parse_wast_module(parser) } else if l.peek::()? { let span = parser.parse::()?.0; Ok(WastDirective::AssertMalformed { @@ -309,6 +349,52 @@ impl<'a> Parse<'a> for WastInvoke<'a> { } } +fn parse_wast_module<'a>(parser: Parser<'a>) -> Result> { + if parser.peek2::()? { + QuoteWat::parse(parser).map(WastDirective::Module) + } else if parser.peek2::()? { + fn parse_module(span: Span, parser: Parser<'_>) -> Result> { + Ok(Wat::Module( + crate::core::Module::parse_without_module_keyword(span, parser)?, + )) + } + fn parse_component(span: Span, parser: Parser<'_>) -> Result> { + Ok(Wat::Component( + crate::component::Component::parse_without_component_keyword(span, parser)?, + )) + } + let (span, ctor) = if parser.peek::()? { + ( + parser.parse::()?.0, + parse_component as fn(_, _) -> _, + ) + } else { + ( + parser.parse::()?.0, + parse_module as fn(_, _) -> _, + ) + }; + parser.parse::()?; + Ok(WastDirective::ModuleDefinition(QuoteWat::Wat(ctor( + span, parser, + )?))) + } else if parser.peek2::()? { + let span = if parser.peek::()? { + parser.parse::()?.0 + } else { + parser.parse::()?.0 + }; + parser.parse::()?; + Ok(WastDirective::ModuleInstance { + span, + instance: parser.parse()?, + module: parser.parse()?, + }) + } else { + QuoteWat::parse(parser).map(WastDirective::Module) + } +} + #[allow(missing_docs)] #[derive(Debug)] pub enum QuoteWat<'a> { @@ -317,7 +403,7 @@ pub enum QuoteWat<'a> { QuoteComponent(Span, Vec<(Span, &'a [u8])>), } -impl QuoteWat<'_> { +impl<'a> QuoteWat<'a> { /// Encodes this module to bytes, either by encoding the module directly or /// parsing the contents and then encoding it. pub fn encode(&mut self) -> Result, Error> { @@ -355,6 +441,15 @@ impl QuoteWat<'_> { Ok(QuoteWatTest::Text(ret)) } + /// Returns the identifier, if registered, for this module. + pub fn name(&self) -> Option> { + match self { + QuoteWat::Wat(Wat::Module(m)) => m.id, + QuoteWat::Wat(Wat::Component(m)) => m.id, + QuoteWat::QuoteModule(..) | QuoteWat::QuoteComponent(..) => None, + } + } + /// Returns the defining span of this module. pub fn span(&self) -> Span { match self { diff --git a/crates/wast/src/wat.rs b/crates/wast/src/wat.rs index 3673c849f1..cf4b532b78 100644 --- a/crates/wast/src/wat.rs +++ b/crates/wast/src/wat.rs @@ -45,24 +45,22 @@ impl<'a> Parse<'a> for Wat<'a> { return Err(parser.error("expected at least one module field")); } - let _r = parser.register_annotation("custom"); - let _r = parser.register_annotation("producers"); - let _r = parser.register_annotation("name"); - let _r = parser.register_annotation("metadata.code.branch_hint"); - let wat = if parser.peek2::()? { - Wat::Module(parser.parens(|parser| parser.parse())?) - } else if parser.peek2::()? { - Wat::Component(parser.parens(|parser| parser.parse())?) - } else { - let fields = ModuleField::parse_remaining(parser)?; - Wat::Module(Module { - span: Span { offset: 0 }, - id: None, - name: None, - kind: ModuleKind::Text(fields), - }) - }; - wat.validate(parser)?; - Ok(wat) + parser.with_standard_annotations_registered(|parser| { + let wat = if parser.peek2::()? { + Wat::Module(parser.parens(|parser| parser.parse())?) + } else if parser.peek2::()? { + Wat::Component(parser.parens(|parser| parser.parse())?) + } else { + let fields = ModuleField::parse_remaining(parser)?; + Wat::Module(Module { + span: Span { offset: 0 }, + id: None, + name: None, + kind: ModuleKind::Text(fields), + }) + }; + wat.validate(parser)?; + Ok(wat) + }) } } diff --git a/crates/wat/Cargo.toml b/crates/wat/Cargo.toml index a064af2f24..db8ea337fd 100644 --- a/crates/wat/Cargo.toml +++ b/crates/wat/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wat" -version = "1.216.0" +version = "1.217.0" authors = ["Alex Crichton "] edition.workspace = true license.workspace = true diff --git a/crates/wit-component/tests/components.rs b/crates/wit-component/tests/components.rs index 8f2951f927..3684b8ec33 100644 --- a/crates/wit-component/tests/components.rs +++ b/crates/wit-component/tests/components.rs @@ -147,7 +147,7 @@ fn run_test(path: &Path) -> Result<()> { if !test_case.starts_with("error-") { return Err(err); } - assert_output(&format!("{err:?}"), &error_path)?; + assert_output(&format!("{err:#}"), &error_path)?; return Ok(()); } }; diff --git a/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt b/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt index 904d1aef51..72264c4a30 100644 --- a/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt +++ b/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt @@ -1,5 +1 @@ -failed to decode world from module - -Caused by: - 0: module was not valid - 1: type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []` \ No newline at end of file +failed to decode world from module: module was not valid: type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []` \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-empty-module-import/error.txt b/crates/wit-component/tests/components/error-empty-module-import/error.txt index 75f7337bf2..f68bb1b668 100644 --- a/crates/wit-component/tests/components/error-empty-module-import/error.txt +++ b/crates/wit-component/tests/components/error-empty-module-import/error.txt @@ -1,5 +1 @@ -failed to decode world from module - -Caused by: - 0: module was not valid - 1: no top-level imported function `foo` specified \ No newline at end of file +failed to decode world from module: module was not valid: no top-level imported function `foo` specified \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt b/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt index 0588ebcf0d..3b1c6afaab 100644 --- a/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt +++ b/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt @@ -1,6 +1 @@ -failed to decode world from module - -Caused by: - 0: module was not valid - 1: failed to validate exported interface `foo` - 2: type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []` \ No newline at end of file +failed to decode world from module: module was not valid: failed to validate exported interface `foo`: type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []` \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-import-resource-rep/error.txt b/crates/wit-component/tests/components/error-import-resource-rep/error.txt index f9f291d9f4..7dd176e603 100644 --- a/crates/wit-component/tests/components/error-import-resource-rep/error.txt +++ b/crates/wit-component/tests/components/error-import-resource-rep/error.txt @@ -1,5 +1 @@ -failed to decode world from module - -Caused by: - 0: module was not valid - 1: no top-level imported function `[resource-rep]a` specified \ No newline at end of file +failed to decode world from module: module was not valid: no top-level imported function `[resource-rep]a` specified \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt b/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt index e76b70fea7..d7eaac43fa 100644 --- a/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt +++ b/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt @@ -1,5 +1 @@ -failed to decode world from module - -Caused by: - 0: module was not valid - 1: type mismatch for function `[resource-drop]a`: expected `[I32] -> []` but found `[I32] -> [I32]` \ No newline at end of file +failed to decode world from module: module was not valid: type mismatch for function `[resource-drop]a`: expected `[I32] -> []` but found `[I32] -> [I32]` \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt b/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt index 57dae3991b..41ba5e7720 100644 --- a/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt +++ b/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt @@ -1,6 +1 @@ -failed to decode world from module - -Caused by: - 0: module was not valid - 1: failed to validate import interface `foo` - 2: type mismatch for function `bar`: expected `[I32, I32] -> []` but found `[] -> []` \ No newline at end of file +failed to decode world from module: module was not valid: failed to validate import interface `foo`: type mismatch for function `bar`: expected `[I32, I32] -> []` but found `[] -> []` \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-invalid-module-import/error.txt b/crates/wit-component/tests/components/error-invalid-module-import/error.txt index 4a54c8216d..4c5195ec6c 100644 --- a/crates/wit-component/tests/components/error-invalid-module-import/error.txt +++ b/crates/wit-component/tests/components/error-invalid-module-import/error.txt @@ -1,5 +1 @@ -failed to decode world from module - -Caused by: - 0: module was not valid - 1: module is only allowed to import functions \ No newline at end of file +failed to decode world from module: module was not valid: module is only allowed to import functions \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-missing-default-export/error.txt b/crates/wit-component/tests/components/error-missing-default-export/error.txt index 1dc1b01f63..cd0b78a041 100644 --- a/crates/wit-component/tests/components/error-missing-default-export/error.txt +++ b/crates/wit-component/tests/components/error-missing-default-export/error.txt @@ -1,5 +1 @@ -failed to decode world from module - -Caused by: - 0: module was not valid - 1: module does not export required function `a` \ No newline at end of file +failed to decode world from module: module was not valid: module does not export required function `a` \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-missing-export/error.txt b/crates/wit-component/tests/components/error-missing-export/error.txt index 6d4148bd72..0829150ee5 100644 --- a/crates/wit-component/tests/components/error-missing-export/error.txt +++ b/crates/wit-component/tests/components/error-missing-export/error.txt @@ -1,6 +1 @@ -failed to decode world from module - -Caused by: - 0: module was not valid - 1: failed to validate exported interface `foo` - 2: module does not export required function `foo#a` \ No newline at end of file +failed to decode world from module: module was not valid: failed to validate exported interface `foo`: module does not export required function `foo#a` \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-missing-import-func/error.txt b/crates/wit-component/tests/components/error-missing-import-func/error.txt index 388a6087bb..b6f4f0dd5d 100644 --- a/crates/wit-component/tests/components/error-missing-import-func/error.txt +++ b/crates/wit-component/tests/components/error-missing-import-func/error.txt @@ -1,5 +1 @@ -failed to decode world from module - -Caused by: - 0: module was not valid - 1: module requires an import interface named `foo` \ No newline at end of file +failed to decode world from module: module was not valid: module requires an import interface named `foo` \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-missing-import/error.txt b/crates/wit-component/tests/components/error-missing-import/error.txt index 388a6087bb..b6f4f0dd5d 100644 --- a/crates/wit-component/tests/components/error-missing-import/error.txt +++ b/crates/wit-component/tests/components/error-missing-import/error.txt @@ -1,5 +1 @@ -failed to decode world from module - -Caused by: - 0: module was not valid - 1: module requires an import interface named `foo` \ No newline at end of file +failed to decode world from module: module was not valid: module requires an import interface named `foo` \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-missing-module-metadata/error.txt b/crates/wit-component/tests/components/error-missing-module-metadata/error.txt index 16f7797650..a256db38dc 100644 --- a/crates/wit-component/tests/components/error-missing-module-metadata/error.txt +++ b/crates/wit-component/tests/components/error-missing-module-metadata/error.txt @@ -1,4 +1 @@ -failed to register indirect shims for main module - -Caused by: - missing component metadata for import of `new::log` \ No newline at end of file +failed to register indirect shims for main module: missing component metadata for import of `new::log` \ No newline at end of file diff --git a/crates/wit-component/tests/merge.rs b/crates/wit-component/tests/merge.rs index f3b3918bd6..f7f72f2f65 100644 --- a/crates/wit-component/tests/merge.rs +++ b/crates/wit-component/tests/merge.rs @@ -52,7 +52,7 @@ fn merging() -> Result<()> { } Err(e) => { assert!(test_case.starts_with("bad-"), "failed to merge with {e:?}"); - assert_output(&path.join("error.txt"), &format!("{e:?}"))?; + assert_output(&path.join("error.txt"), &format!("{e:#}"))?; } } } diff --git a/crates/wit-component/tests/merge/bad-interface1/error.txt b/crates/wit-component/tests/merge/bad-interface1/error.txt index 951bb365d3..0558df13d3 100644 --- a/crates/wit-component/tests/merge/bad-interface1/error.txt +++ b/crates/wit-component/tests/merge/bad-interface1/error.txt @@ -1,5 +1 @@ -failed to merge package `foo:foo` into existing copy - -Caused by: - 0: failed to merge interface `a` - 1: expected type `a` to be present \ No newline at end of file +failed to merge package `foo:foo` into existing copy: failed to merge interface `a`: expected type `a` to be present \ No newline at end of file diff --git a/crates/wit-component/tests/merge/bad-interface2/error.txt b/crates/wit-component/tests/merge/bad-interface2/error.txt index bbb5f232b2..0b3f674bf8 100644 --- a/crates/wit-component/tests/merge/bad-interface2/error.txt +++ b/crates/wit-component/tests/merge/bad-interface2/error.txt @@ -1,5 +1 @@ -failed to merge package `foo:foo` into existing copy - -Caused by: - 0: failed to merge interface `a` - 1: expected function `a` to be present \ No newline at end of file +failed to merge package `foo:foo` into existing copy: failed to merge interface `a`: expected function `a` to be present \ No newline at end of file diff --git a/crates/wit-component/tests/merge/bad-world1/error.txt b/crates/wit-component/tests/merge/bad-world1/error.txt index f04e2ed0c2..059b1e3daf 100644 --- a/crates/wit-component/tests/merge/bad-world1/error.txt +++ b/crates/wit-component/tests/merge/bad-world1/error.txt @@ -1,5 +1 @@ -failed to merge package `foo:foo` into existing copy - -Caused by: - 0: failed to merge world `foo` - 1: import `b` not found in target world \ No newline at end of file +failed to merge package `foo:foo` into existing copy: failed to merge world `foo`: import `b` not found in target world \ No newline at end of file diff --git a/crates/wit-component/tests/merge/bad-world2/error.txt b/crates/wit-component/tests/merge/bad-world2/error.txt index bca74e7b04..5f636c1394 100644 --- a/crates/wit-component/tests/merge/bad-world2/error.txt +++ b/crates/wit-component/tests/merge/bad-world2/error.txt @@ -1,5 +1 @@ -failed to merge package `foo:foo` into existing copy - -Caused by: - 0: failed to merge world `foo` - 1: world contains different number of imports than expected \ No newline at end of file +failed to merge package `foo:foo` into existing copy: failed to merge world `foo`: world contains different number of imports than expected \ No newline at end of file diff --git a/crates/wit-component/tests/merge/bad-world3/error.txt b/crates/wit-component/tests/merge/bad-world3/error.txt index daf6860fff..1311233fa0 100644 --- a/crates/wit-component/tests/merge/bad-world3/error.txt +++ b/crates/wit-component/tests/merge/bad-world3/error.txt @@ -1,5 +1 @@ -failed to merge package `foo:foo` into existing copy - -Caused by: - 0: failed to merge world `foo` - 1: world contains different number of exports than expected \ No newline at end of file +failed to merge package `foo:foo` into existing copy: failed to merge world `foo`: world contains different number of exports than expected \ No newline at end of file diff --git a/crates/wit-component/tests/merge/bad-world4/error.txt b/crates/wit-component/tests/merge/bad-world4/error.txt index 5cf21c9247..9b95070316 100644 --- a/crates/wit-component/tests/merge/bad-world4/error.txt +++ b/crates/wit-component/tests/merge/bad-world4/error.txt @@ -1,5 +1 @@ -failed to merge package `foo:foo` into existing copy - -Caused by: - 0: failed to merge world `foo` - 1: export `b` not found in target world \ No newline at end of file +failed to merge package `foo:foo` into existing copy: failed to merge world `foo`: export `b` not found in target world \ No newline at end of file diff --git a/crates/wit-component/tests/merge/bad-world5/error.txt b/crates/wit-component/tests/merge/bad-world5/error.txt index 9eb40e7551..38c83bb951 100644 --- a/crates/wit-component/tests/merge/bad-world5/error.txt +++ b/crates/wit-component/tests/merge/bad-world5/error.txt @@ -1,5 +1 @@ -failed to merge package `foo:foo` into existing copy - -Caused by: - 0: failed to merge world `foo` - 1: import `a` not found in target world \ No newline at end of file +failed to merge package `foo:foo` into existing copy: failed to merge world `foo`: import `a` not found in target world \ No newline at end of file diff --git a/crates/wit-component/tests/targets.rs b/crates/wit-component/tests/targets.rs index 7bbd96ff52..ff257285c1 100644 --- a/crates/wit-component/tests/targets.rs +++ b/crates/wit-component/tests/targets.rs @@ -50,7 +50,7 @@ fn targets() -> Result<()> { } Err(e) => { assert!(test_case.starts_with("error-"), "{e:?}"); - assert_output(&path.join("error.txt"), &format!("{e:?}"))?; + assert_output(&path.join("error.txt"), &format!("{e:#}"))?; } } } diff --git a/crates/wit-component/tests/targets/error-missing-export/error.txt b/crates/wit-component/tests/targets/error-missing-export/error.txt index ebfccba10d..5751a1062e 100644 --- a/crates/wit-component/tests/targets/error-missing-export/error.txt +++ b/crates/wit-component/tests/targets/error-missing-export/error.txt @@ -1,5 +1,2 @@ -failed to validate encoded bytes - -Caused by: - type mismatch for import `foobar` - missing export named `test:foo/bar` (at offset 0x1d5) \ No newline at end of file +failed to validate encoded bytes: type mismatch for import `foobar` +missing export named `test:foo/bar` (at offset 0x1d5) \ No newline at end of file diff --git a/crates/wit-component/tests/targets/error-missing-import/error.txt b/crates/wit-component/tests/targets/error-missing-import/error.txt index 3a55fb02c5..d5d32ff920 100644 --- a/crates/wit-component/tests/targets/error-missing-import/error.txt +++ b/crates/wit-component/tests/targets/error-missing-import/error.txt @@ -1,5 +1,2 @@ -failed to validate encoded bytes - -Caused by: - type mismatch for import `foobar` - missing import named `test:foo/foo` (at offset 0x175) \ No newline at end of file +failed to validate encoded bytes: type mismatch for import `foobar` +missing import named `test:foo/foo` (at offset 0x175) \ No newline at end of file diff --git a/crates/wit-encoder/src/enum_.rs b/crates/wit-encoder/src/enum_.rs index 6132ebb609..63757b470b 100644 --- a/crates/wit-encoder/src/enum_.rs +++ b/crates/wit-encoder/src/enum_.rs @@ -65,7 +65,7 @@ impl EnumCase { } } - pub fn name(&mut self) -> &Ident { + pub fn name(&self) -> &Ident { &self.name } diff --git a/crates/wit-encoder/src/record.rs b/crates/wit-encoder/src/record.rs index 6b2dca0a06..7bc505082b 100644 --- a/crates/wit-encoder/src/record.rs +++ b/crates/wit-encoder/src/record.rs @@ -28,7 +28,8 @@ impl Record { #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub struct Field { pub(crate) name: Ident, - pub(crate) ty: Type, + #[cfg_attr(feature = "serde", serde(rename = "type"))] + pub(crate) type_: Type, pub(crate) docs: Option, } @@ -36,12 +37,12 @@ impl Field { pub fn new(name: impl Into, ty: Type) -> Self { Self { name: name.into(), - ty, + type_: ty, docs: None, } } - pub fn name(&mut self) -> &Ident { + pub fn name(&self) -> &Ident { &self.name } @@ -57,12 +58,16 @@ impl Field { &self.docs } - pub fn ty(&self) -> &Type { - &self.ty + pub fn type_(&self) -> &Type { + &self.type_ } - pub fn ty_mut(&mut self) -> &mut Type { - &mut self.ty + pub fn type_mut(&mut self) -> &mut Type { + &mut self.type_ + } + + pub fn set_type(&mut self, type_: impl Into) { + self.type_ = type_.into(); } } diff --git a/crates/wit-encoder/src/resource.rs b/crates/wit-encoder/src/resource.rs index e59e941760..cab2ff6a44 100644 --- a/crates/wit-encoder/src/resource.rs +++ b/crates/wit-encoder/src/resource.rs @@ -108,6 +108,22 @@ impl ResourceFunc { } } + pub fn results(&self) -> Option<&Results> { + match &self.kind { + ResourceFuncKind::Method(_, results) => Some(results), + ResourceFuncKind::Static(_, results) => Some(results), + ResourceFuncKind::Constructor => None, + } + } + + pub fn results_mut(&mut self) -> Option<&mut Results> { + match &mut self.kind { + ResourceFuncKind::Method(_, results) => Some(results), + ResourceFuncKind::Static(_, results) => Some(results), + ResourceFuncKind::Constructor => None, + } + } + pub fn set_docs(&mut self, docs: Option>) { self.docs = docs.map(|d| d.into()); } diff --git a/crates/wit-encoder/src/result.rs b/crates/wit-encoder/src/result.rs index ee8ff11909..e7436e3ad3 100644 --- a/crates/wit-encoder/src/result.rs +++ b/crates/wit-encoder/src/result.rs @@ -17,12 +17,24 @@ impl Result_ { err: None, } } + pub fn get_ok(&self) -> &Option { + &self.ok + } + pub fn get_ok_mut(&mut self) -> &mut Option { + &mut self.ok + } pub fn err(type_: Type) -> Self { Self { ok: None, err: Some(type_), } } + pub fn get_err(&self) -> &Option { + &self.err + } + pub fn get_err_mut(&mut self) -> &mut Option { + &mut self.err + } pub fn both(ok: Type, err: Type) -> Self { Self { ok: Some(ok), diff --git a/crates/wit-encoder/src/ty.rs b/crates/wit-encoder/src/ty.rs index 434868a536..62bf852f16 100644 --- a/crates/wit-encoder/src/ty.rs +++ b/crates/wit-encoder/src/ty.rs @@ -117,7 +117,8 @@ impl Display for Type { #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub struct VariantCase { name: Ident, - ty: Option, + #[cfg_attr(feature = "serde", serde(rename = "type"))] + type_: Option, docs: Option, } @@ -125,7 +126,7 @@ impl VariantCase { pub fn empty(name: impl Into) -> Self { Self { name: name.into(), - ty: None, + type_: None, docs: None, } } @@ -133,17 +134,29 @@ impl VariantCase { pub fn value(name: impl Into, ty: Type) -> Self { Self { name: name.into(), - ty: Some(ty), + type_: Some(ty), docs: None, } } + pub fn set_name(&mut self, name: impl Into) { + self.name = name.into(); + } + + pub fn name(&self) -> &Ident { + &self.name + } + + pub fn name_mut(&mut self) -> &mut Ident { + &mut self.name + } + pub fn type_(&self) -> Option<&Type> { - self.ty.as_ref() + self.type_.as_ref() } pub fn type_mut(&mut self) -> &mut Option { - &mut self.ty + &mut self.type_ } pub fn set_docs(&mut self, docs: Option>) { @@ -348,7 +361,7 @@ impl Render for TypeDef { if let Some(docs) = &field.docs { docs.render(f, &opts)?; } - write!(f, "{}{}: {},\n", opts.spaces(), field.name, field.ty)?; + write!(f, "{}{}: {},\n", opts.spaces(), field.name, field.type_)?; } write!(f, "{}}}\n", opts.spaces())?; } @@ -408,7 +421,7 @@ impl Render for TypeDef { if let Some(docs) = &case.docs { docs.render(f, &opts)?; } - match &case.ty { + match &case.type_ { Some(type_) => { write!(f, "{}{}({}),\n", opts.spaces(), case.name, type_)?; } diff --git a/crates/wit-parser/tests/all.rs b/crates/wit-parser/tests/all.rs index 6c7af1f75b..ec9d4be941 100644 --- a/crates/wit-parser/tests/all.rs +++ b/crates/wit-parser/tests/all.rs @@ -87,7 +87,7 @@ impl Runner { "some generic platform-agnostic error message", ); } - format!("{:?}", e) + format!("{:#}", e) } } } else { diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-gate3.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-gate3.wit.result index e8e4df1dfe..181a55f67e 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-gate3.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-gate3.wit.result @@ -1,8 +1,5 @@ -this type is not gated by a feature but its interface is gated by a feature - -Caused by: - found a reference to a interface which is excluded due to its feature not being activated - --> tests/ui/parse-fail/bad-gate3.wit:5:8 - | - 5 | type a = u32; - | ^ \ No newline at end of file +this type is not gated by a feature but its interface is gated by a feature: found a reference to a interface which is excluded due to its feature not being activated + --> tests/ui/parse-fail/bad-gate3.wit:5:8 + | + 5 | type a = u32; + | ^ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-gate4.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-gate4.wit.result index 8b47145a4a..956ba063aa 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-gate4.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-gate4.wit.result @@ -1,8 +1,5 @@ -failed to update function `[constructor]a` - -Caused by: - found a reference to a type which is excluded due to its feature not being activated - --> tests/ui/parse-fail/bad-gate4.wit:6:5 - | - 6 | constructor(); - | ^---------- \ No newline at end of file +failed to update function `[constructor]a`: found a reference to a type which is excluded due to its feature not being activated + --> tests/ui/parse-fail/bad-gate4.wit:6:5 + | + 6 | constructor(); + | ^---------- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-gate5.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-gate5.wit.result index df063dd334..6c36fb3f80 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-gate5.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-gate5.wit.result @@ -1,8 +1,5 @@ -failed to update function `[static]a.x` - -Caused by: - found a reference to a type which is excluded due to its feature not being activated - --> tests/ui/parse-fail/bad-gate5.wit:9:5 - | - 9 | x: static func(); - | ^ \ No newline at end of file +failed to update function `[static]a.x`: found a reference to a type which is excluded due to its feature not being activated + --> tests/ui/parse-fail/bad-gate5.wit:9:5 + | + 9 | x: static func(); + | ^ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg1.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg1.wit.result index e366b72989..ddc7c7c307 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg1.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg1.wit.result @@ -1,9 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg1] - -Caused by: - 0: failed to parse package: tests/ui/parse-fail/bad-pkg1 - 1: interface or world `nonexistent` not found in package - --> tests/ui/parse-fail/bad-pkg1/root.wit:4:7 - | - 4 | use nonexistent.{}; - | ^---------- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg1]: failed to parse package: tests/ui/parse-fail/bad-pkg1: interface or world `nonexistent` not found in package + --> tests/ui/parse-fail/bad-pkg1/root.wit:4:7 + | + 4 | use nonexistent.{}; + | ^---------- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg2.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg2.wit.result index 3827d5db46..235a806d21 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg2.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg2.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg2] - -Caused by: - interface not found in package - --> tests/ui/parse-fail/bad-pkg2/root.wit:4:15 - | - 4 | use foo:bar/nonexistent.{}; - | ^---------- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg2]: interface not found in package + --> tests/ui/parse-fail/bad-pkg2/root.wit:4:15 + | + 4 | use foo:bar/nonexistent.{}; + | ^---------- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg3.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg3.wit.result index f5d61a284e..d3d2102d7b 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg3.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg3.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg3] - -Caused by: - interface not found in package - --> tests/ui/parse-fail/bad-pkg3/root.wit:4:15 - | - 4 | use foo:bar/baz.{}; - | ^-- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg3]: interface not found in package + --> tests/ui/parse-fail/bad-pkg3/root.wit:4:15 + | + 4 | use foo:bar/baz.{}; + | ^-- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg4.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg4.wit.result index dac51efa32..e4522856ea 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg4.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg4.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg4] - -Caused by: - type `a-name` not defined in interface - --> tests/ui/parse-fail/bad-pkg4/root.wit:3:20 - | - 3 | use foo:bar/baz.{a-name}; - | ^----- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg4]: type `a-name` not defined in interface + --> tests/ui/parse-fail/bad-pkg4/root.wit:3:20 + | + 3 | use foo:bar/baz.{a-name}; + | ^----- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg5.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg5.wit.result index 2d37060f97..0251000c35 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg5.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg5.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg5] - -Caused by: - type `nonexistent` not defined in interface - --> tests/ui/parse-fail/bad-pkg5/root.wit:3:20 - | - 3 | use foo:bar/baz.{nonexistent}; - | ^---------- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg5]: type `nonexistent` not defined in interface + --> tests/ui/parse-fail/bad-pkg5/root.wit:3:20 + | + 3 | use foo:bar/baz.{nonexistent}; + | ^---------- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg6.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg6.wit.result index c0fdc9d90e..56b5c85e8f 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg6.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg6.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg6] - -Caused by: - package not found - --> tests/ui/parse-fail/bad-pkg6/root.wit:3:7 - | - 3 | use foo:bar/baz.{}; - | ^------ \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg6]: package not found + --> tests/ui/parse-fail/bad-pkg6/root.wit:3:7 + | + 3 | use foo:bar/baz.{}; + | ^------ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-resource15.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-resource15.wit.result index c26467f896..1ed0a4f50b 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-resource15.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-resource15.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-resource15] - -Caused by: - type used in a handle must be a resource - --> tests/ui/parse-fail/bad-resource15/foo.wit:6:16 - | - 6 | type t = own; - | ^ \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-resource15]: type used in a handle must be a resource + --> tests/ui/parse-fail/bad-resource15/foo.wit:6:16 + | + 6 | type t = own; + | ^ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/conflicting-package.wit.result b/crates/wit-parser/tests/ui/parse-fail/conflicting-package.wit.result index bffa1fc4f1..dc9dcee416 100644 --- a/crates/wit-parser/tests/ui/parse-fail/conflicting-package.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/conflicting-package.wit.result @@ -1,10 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/conflicting-package] - -Caused by: - 0: failed to parse package: tests/ui/parse-fail/conflicting-package - 1: failed to start resolving path: tests/ui/parse-fail/conflicting-package/b.wit - 2: package identifier `foo:b` does not match previous package name of `foo:a` - --> tests/ui/parse-fail/conflicting-package/b.wit:1:9 - | - 1 | package foo:b; - | ^---- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/conflicting-package]: failed to parse package: tests/ui/parse-fail/conflicting-package: failed to start resolving path: tests/ui/parse-fail/conflicting-package/b.wit: package identifier `foo:b` does not match previous package name of `foo:a` + --> tests/ui/parse-fail/conflicting-package/b.wit:1:9 + | + 1 | package foo:b; + | ^---- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/duplicate-interface2.wit.result b/crates/wit-parser/tests/ui/parse-fail/duplicate-interface2.wit.result index 81735c7684..8eb038bf4b 100644 --- a/crates/wit-parser/tests/ui/parse-fail/duplicate-interface2.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/duplicate-interface2.wit.result @@ -1,9 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/duplicate-interface2] - -Caused by: - 0: failed to parse package: tests/ui/parse-fail/duplicate-interface2 - 1: duplicate item named `foo` - --> tests/ui/parse-fail/duplicate-interface2/foo2.wit:3:11 - | - 3 | interface foo {} - | ^-- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/duplicate-interface2]: failed to parse package: tests/ui/parse-fail/duplicate-interface2: duplicate item named `foo` + --> tests/ui/parse-fail/duplicate-interface2/foo2.wit:3:11 + | + 3 | interface foo {} + | ^-- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/include-foreign.wit.result b/crates/wit-parser/tests/ui/parse-fail/include-foreign.wit.result index 00257283a7..495d331006 100644 --- a/crates/wit-parser/tests/ui/parse-fail/include-foreign.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/include-foreign.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/include-foreign] - -Caused by: - world not found in package - --> tests/ui/parse-fail/include-foreign/root.wit:4:19 - | - 4 | include foo:bar/bar; - | ^-- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/include-foreign]: world not found in package + --> tests/ui/parse-fail/include-foreign/root.wit:4:19 + | + 4 | include foo:bar/bar; + | ^-- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/multi-file-missing-delimiter.wit.result b/crates/wit-parser/tests/ui/parse-fail/multi-file-missing-delimiter.wit.result index 45400e6389..8182a1a49c 100644 --- a/crates/wit-parser/tests/ui/parse-fail/multi-file-missing-delimiter.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/multi-file-missing-delimiter.wit.result @@ -1,9 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multi-file-missing-delimiter] - -Caused by: - 0: failed to parse package: tests/ui/parse-fail/multi-file-missing-delimiter - 1: expected `type`, `resource` or `func`, found eof - --> tests/ui/parse-fail/multi-file-missing-delimiter/observe.wit:6:1 - | - 6 | - | ^ \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multi-file-missing-delimiter]: failed to parse package: tests/ui/parse-fail/multi-file-missing-delimiter: expected `type`, `resource` or `func`, found eof + --> tests/ui/parse-fail/multi-file-missing-delimiter/observe.wit:6:1 + | + 6 | + | ^ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/multi-package-deps-share-nest.wit.result b/crates/wit-parser/tests/ui/parse-fail/multi-package-deps-share-nest.wit.result index 31c994e78b..053a98b163 100644 --- a/crates/wit-parser/tests/ui/parse-fail/multi-package-deps-share-nest.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/multi-package-deps-share-nest.wit.result @@ -1,6 +1,3 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multi-package-deps-share-nest] - -Caused by: - package foo:shared is defined in two different locations: - * tests/ui/parse-fail/multi-package-deps-share-nest/deps/dep2/types.wit:3:9 - * tests/ui/parse-fail/multi-package-deps-share-nest/deps/dep1/types.wit:3:9 \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multi-package-deps-share-nest]: package foo:shared is defined in two different locations: +* tests/ui/parse-fail/multi-package-deps-share-nest/deps/dep2/types.wit:3:9 +* tests/ui/parse-fail/multi-package-deps-share-nest/deps/dep1/types.wit:3:9 \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/multiple-package-docs.wit.result b/crates/wit-parser/tests/ui/parse-fail/multiple-package-docs.wit.result index 4ce4423875..fd70371f39 100644 --- a/crates/wit-parser/tests/ui/parse-fail/multiple-package-docs.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/multiple-package-docs.wit.result @@ -1,10 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multiple-package-docs] - -Caused by: - 0: failed to parse package: tests/ui/parse-fail/multiple-package-docs - 1: failed to start resolving path: tests/ui/parse-fail/multiple-package-docs/b.wit - 2: found doc comments on multiple 'package' items - --> tests/ui/parse-fail/multiple-package-docs/b.wit:1:1 - | - 1 | /// Multiple package docs, B - | ^--------------------------- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multiple-package-docs]: failed to parse package: tests/ui/parse-fail/multiple-package-docs: failed to start resolving path: tests/ui/parse-fail/multiple-package-docs/b.wit: found doc comments on multiple 'package' items + --> tests/ui/parse-fail/multiple-package-docs/b.wit:1:1 + | + 1 | /// Multiple package docs, B + | ^--------------------------- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/no-access-to-sibling-use.wit.result b/crates/wit-parser/tests/ui/parse-fail/no-access-to-sibling-use.wit.result index 72830f2be4..91c398043d 100644 --- a/crates/wit-parser/tests/ui/parse-fail/no-access-to-sibling-use.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/no-access-to-sibling-use.wit.result @@ -1,9 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/no-access-to-sibling-use] - -Caused by: - 0: failed to parse package: tests/ui/parse-fail/no-access-to-sibling-use - 1: interface or world `bar-renamed` does not exist - --> tests/ui/parse-fail/no-access-to-sibling-use/foo.wit:3:5 - | - 3 | use bar-renamed; - | ^---------- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/no-access-to-sibling-use]: failed to parse package: tests/ui/parse-fail/no-access-to-sibling-use: interface or world `bar-renamed` does not exist + --> tests/ui/parse-fail/no-access-to-sibling-use/foo.wit:3:5 + | + 3 | use bar-renamed; + | ^---------- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/non-existance-world-include.wit.result b/crates/wit-parser/tests/ui/parse-fail/non-existance-world-include.wit.result index 13a112277d..f7c853c8be 100644 --- a/crates/wit-parser/tests/ui/parse-fail/non-existance-world-include.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/non-existance-world-include.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/non-existance-world-include] - -Caused by: - world not found in package - --> tests/ui/parse-fail/non-existance-world-include/root.wit:4:19 - | - 4 | include foo:baz/non-existance; - | ^------------ \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/non-existance-world-include]: world not found in package + --> tests/ui/parse-fail/non-existance-world-include/root.wit:4:19 + | + 4 | include foo:baz/non-existance; + | ^------------ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/pkg-cycle.wit.result b/crates/wit-parser/tests/ui/parse-fail/pkg-cycle.wit.result index f64970931d..250e158141 100644 --- a/crates/wit-parser/tests/ui/parse-fail/pkg-cycle.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/pkg-cycle.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/pkg-cycle] - -Caused by: - package depends on itself - --> tests/ui/parse-fail/pkg-cycle/deps/a1/root.wit:3:7 - | - 3 | use foo:a1/foo.{}; - | ^----- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/pkg-cycle]: package depends on itself + --> tests/ui/parse-fail/pkg-cycle/deps/a1/root.wit:3:7 + | + 3 | use foo:a1/foo.{}; + | ^----- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/pkg-cycle2.wit.result b/crates/wit-parser/tests/ui/parse-fail/pkg-cycle2.wit.result index 53054871c2..ec10e18c27 100644 --- a/crates/wit-parser/tests/ui/parse-fail/pkg-cycle2.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/pkg-cycle2.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/pkg-cycle2] - -Caused by: - package depends on itself - --> tests/ui/parse-fail/pkg-cycle2/deps/a1/root.wit:3:7 - | - 3 | use foo:a2/foo.{}; - | ^----- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/pkg-cycle2]: package depends on itself + --> tests/ui/parse-fail/pkg-cycle2/deps/a1/root.wit:3:7 + | + 3 | use foo:a2/foo.{}; + | ^----- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/resources-multiple-returns-borrow.wit.result b/crates/wit-parser/tests/ui/parse-fail/resources-multiple-returns-borrow.wit.result index 2094237fa4..ba5d4f7ff4 100644 --- a/crates/wit-parser/tests/ui/parse-fail/resources-multiple-returns-borrow.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/resources-multiple-returns-borrow.wit.result @@ -1,8 +1,5 @@ -failed to update function `[method]r1.f1` - -Caused by: - function returns a type which contains a `borrow` which is not supported - --> tests/ui/parse-fail/resources-multiple-returns-borrow.wit:7:5 - | - 7 | f1: func() -> (a: s32, handle: borrow); - | ^- \ No newline at end of file +failed to update function `[method]r1.f1`: function returns a type which contains a `borrow` which is not supported + --> tests/ui/parse-fail/resources-multiple-returns-borrow.wit:7:5 + | + 7 | f1: func() -> (a: s32, handle: borrow); + | ^- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/resources-return-borrow.wit.result b/crates/wit-parser/tests/ui/parse-fail/resources-return-borrow.wit.result index 79de329242..b6a7361b67 100644 --- a/crates/wit-parser/tests/ui/parse-fail/resources-return-borrow.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/resources-return-borrow.wit.result @@ -1,8 +1,5 @@ -failed to update function `[method]r1.f1` - -Caused by: - function returns a type which contains a `borrow` which is not supported - --> tests/ui/parse-fail/resources-return-borrow.wit:7:5 - | - 7 | f1: func() -> borrow; - | ^- \ No newline at end of file +failed to update function `[method]r1.f1`: function returns a type which contains a `borrow` which is not supported + --> tests/ui/parse-fail/resources-return-borrow.wit:7:5 + | + 7 | f1: func() -> borrow; + | ^- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/return-borrow1.wit.result b/crates/wit-parser/tests/ui/parse-fail/return-borrow1.wit.result index 0a0468dd60..a4ddb59f1a 100644 --- a/crates/wit-parser/tests/ui/parse-fail/return-borrow1.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/return-borrow1.wit.result @@ -1,8 +1,5 @@ -failed to update function `x` - -Caused by: - function returns a type which contains a `borrow` which is not supported - --> tests/ui/parse-fail/return-borrow1.wit:6:3 - | - 6 | x: func() -> borrow; - | ^ \ No newline at end of file +failed to update function `x`: function returns a type which contains a `borrow` which is not supported + --> tests/ui/parse-fail/return-borrow1.wit:6:3 + | + 6 | x: func() -> borrow; + | ^ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/return-borrow2.wit.result b/crates/wit-parser/tests/ui/parse-fail/return-borrow2.wit.result index b46631bb96..01c7f6dad1 100644 --- a/crates/wit-parser/tests/ui/parse-fail/return-borrow2.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/return-borrow2.wit.result @@ -1,8 +1,5 @@ -failed to update function `[method]y.x` - -Caused by: - function returns a type which contains a `borrow` which is not supported - --> tests/ui/parse-fail/return-borrow2.wit:5:5 - | - 5 | x: func() -> borrow; - | ^ \ No newline at end of file +failed to update function `[method]y.x`: function returns a type which contains a `borrow` which is not supported + --> tests/ui/parse-fail/return-borrow2.wit:5:5 + | + 5 | x: func() -> borrow; + | ^ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/return-borrow6.wit.result b/crates/wit-parser/tests/ui/parse-fail/return-borrow6.wit.result index 13d61fba84..e1264b574d 100644 --- a/crates/wit-parser/tests/ui/parse-fail/return-borrow6.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/return-borrow6.wit.result @@ -1,8 +1,5 @@ -failed to update function `x` - -Caused by: - function returns a type which contains a `borrow` which is not supported - --> tests/ui/parse-fail/return-borrow6.wit:6:3 - | - 6 | x: func() -> tuple>; - | ^ \ No newline at end of file +failed to update function `x`: function returns a type which contains a `borrow` which is not supported + --> tests/ui/parse-fail/return-borrow6.wit:6:3 + | + 6 | x: func() -> tuple>; + | ^ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/return-borrow7.wit.result b/crates/wit-parser/tests/ui/parse-fail/return-borrow7.wit.result index 3caaf3feae..f2175ae6d6 100644 --- a/crates/wit-parser/tests/ui/parse-fail/return-borrow7.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/return-borrow7.wit.result @@ -1,8 +1,5 @@ -failed to update function `x` - -Caused by: - function returns a type which contains a `borrow` which is not supported - --> tests/ui/parse-fail/return-borrow7.wit:10:3 - | - 10 | x: func() -> y2; - | ^ \ No newline at end of file +failed to update function `x`: function returns a type which contains a `borrow` which is not supported + --> tests/ui/parse-fail/return-borrow7.wit:10:3 + | + 10 | x: func() -> y2; + | ^ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/return-borrow8.wit.result b/crates/wit-parser/tests/ui/parse-fail/return-borrow8.wit.result index 9fd1f02711..3ace7b8be7 100644 --- a/crates/wit-parser/tests/ui/parse-fail/return-borrow8.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/return-borrow8.wit.result @@ -1,9 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/return-borrow8] - -Caused by: - 0: failed to update function `x` - 1: function returns a type which contains a `borrow` which is not supported - --> tests/ui/parse-fail/return-borrow8/foo.wit:6:3 - | - 6 | x: func() -> r; - | ^ \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/return-borrow8]: failed to update function `x`: function returns a type which contains a `borrow` which is not supported + --> tests/ui/parse-fail/return-borrow8/foo.wit:6:3 + | + 6 | x: func() -> r; + | ^ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/type-and-resource-same-name.wit.result b/crates/wit-parser/tests/ui/parse-fail/type-and-resource-same-name.wit.result index d86ebfb39c..782921c6f2 100644 --- a/crates/wit-parser/tests/ui/parse-fail/type-and-resource-same-name.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/type-and-resource-same-name.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/type-and-resource-same-name] - -Caused by: - type used in a handle must be a resource - --> tests/ui/parse-fail/type-and-resource-same-name/foo.wit:7:20 - | - 7 | type t2 = borrow; - | ^ \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/type-and-resource-same-name]: type used in a handle must be a resource + --> tests/ui/parse-fail/type-and-resource-same-name/foo.wit:7:20 + | + 7 | type t2 = borrow; + | ^ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/unresolved-use10.wit.result b/crates/wit-parser/tests/ui/parse-fail/unresolved-use10.wit.result index 75343de663..8d36b21d13 100644 --- a/crates/wit-parser/tests/ui/parse-fail/unresolved-use10.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/unresolved-use10.wit.result @@ -1,9 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/unresolved-use10] - -Caused by: - 0: failed to parse package: tests/ui/parse-fail/unresolved-use10 - 1: name `thing` is not defined - --> tests/ui/parse-fail/unresolved-use10/bar.wit:4:12 - | - 4 | use foo.{thing}; - | ^---- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/unresolved-use10]: failed to parse package: tests/ui/parse-fail/unresolved-use10: name `thing` is not defined + --> tests/ui/parse-fail/unresolved-use10/bar.wit:4:12 + | + 4 | use foo.{thing}; + | ^---- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/use-and-include-world.wit.result b/crates/wit-parser/tests/ui/parse-fail/use-and-include-world.wit.result index 0d4fcd91ff..e61a285a51 100644 --- a/crates/wit-parser/tests/ui/parse-fail/use-and-include-world.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/use-and-include-world.wit.result @@ -1,9 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/use-and-include-world] - -Caused by: - 0: failed to parse package: tests/ui/parse-fail/use-and-include-world - 1: name `bar` is defined as an interface, not a world - --> tests/ui/parse-fail/use-and-include-world/root.wit:6:21 - | - 6 | include foo:baz/bar; - | ^-- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/use-and-include-world]: failed to parse package: tests/ui/parse-fail/use-and-include-world: name `bar` is defined as an interface, not a world + --> tests/ui/parse-fail/use-and-include-world/root.wit:6:21 + | + 6 | include foo:baz/bar; + | ^-- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/use-world.wit.result b/crates/wit-parser/tests/ui/parse-fail/use-world.wit.result index df56488b01..a85972579b 100644 --- a/crates/wit-parser/tests/ui/parse-fail/use-world.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/use-world.wit.result @@ -1,8 +1,5 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/use-world] - -Caused by: - interface not found in package - --> tests/ui/parse-fail/use-world/root.wit:3:13 - | - 3 | use foo:baz/bar; - | ^-- \ No newline at end of file +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/use-world]: interface not found in package + --> tests/ui/parse-fail/use-world/root.wit:3:13 + | + 3 | use foo:baz/bar; + | ^-- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/very-nested-packages.wit.result b/crates/wit-parser/tests/ui/parse-fail/very-nested-packages.wit.result index eaf0e67081..3109de9582 100644 --- a/crates/wit-parser/tests/ui/parse-fail/very-nested-packages.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/very-nested-packages.wit.result @@ -1,8 +1,5 @@ -failed to handle nested package in: tests/ui/parse-fail/very-nested-packages.wit - -Caused by: - nested packages must be placed at the top-level - --> tests/ui/parse-fail/very-nested-packages.wit:4:11 - | - 4 | package a:c2 { - | ^--- \ No newline at end of file +failed to handle nested package in: tests/ui/parse-fail/very-nested-packages.wit: nested packages must be placed at the top-level + --> tests/ui/parse-fail/very-nested-packages.wit:4:11 + | + 4 | package a:c2 { + | ^--- \ No newline at end of file diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 7bcf6d52d1..31ebb4cd14 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -37,10 +37,6 @@ version = '0.214.0' package = 'wit-component' version = '0.214.0' -[dependencies.wit-smith-old] -package = 'wit-smith' -version = '0.214.0' - [lib] test = false doctest = false diff --git a/fuzz/src/wit64.rs b/fuzz/src/wit64.rs index 594156bfbb..b6e8af8c07 100644 --- a/fuzz/src/wit64.rs +++ b/fuzz/src/wit64.rs @@ -6,7 +6,7 @@ use wit_parser as wit_parser_new; pub fn run(u: &mut Unstructured<'_>) -> Result<()> { let wasm = u.arbitrary().and_then(|config| { log::debug!("config: {config:#?}"); - wit_smith_old::smith(&config, u) + wit_smith::smith(&config, u) })?; write_file("doc.wasm", &wasm); let r1 = wit_component_old::decode(&wasm).unwrap(); diff --git a/src/bin/wasm-tools/json_from_wast.rs b/src/bin/wasm-tools/json_from_wast.rs index 958ddd4946..520b25df09 100644 --- a/src/bin/wasm-tools/json_from_wast.rs +++ b/src/bin/wasm-tools/json_from_wast.rs @@ -6,7 +6,7 @@ use wast::lexer::Lexer; use wast::parser::{self, ParseBuffer}; use wast::token::{Span, F32, F64}; use wast::{ - QuoteWat, QuoteWatTest, Wast, WastArg, WastDirective, WastExecute, WastInvoke, WastRet, Wat, + QuoteWat, QuoteWatTest, Wast, WastArg, WastDirective, WastExecute, WastInvoke, WastRet, }; /// Convert a `*.wast` WebAssembly spec test into a `*.json` file and `*.wasm` @@ -130,7 +130,7 @@ impl<'a> JsonBuilder<'a> { fn directive(&mut self, directive: WastDirective<'a>) -> Result> { let line = self.lineno(directive.span()); let command = match directive { - WastDirective::Wat(module) => { + WastDirective::Module(module) => { let (name, _module_type, filename) = self.emit_file(module)?; json::Command::Module { line, @@ -138,6 +138,21 @@ impl<'a> JsonBuilder<'a> { filename, } } + WastDirective::ModuleDefinition(module) => { + let (name, _module_type, filename) = self.emit_file(module)?; + json::Command::ModuleDefinition { + line, + name: name.map(|s| self.module_name(s)), + filename, + } + } + WastDirective::ModuleInstance { + instance, module, .. + } => json::Command::ModuleInstance { + line, + instance: instance.map(|s| self.module_name(s.name())), + module: module.map(|s| self.module_name(s.name())), + }, WastDirective::AssertMalformed { span: _, module, @@ -286,12 +301,7 @@ impl<'a> JsonBuilder<'a> { &mut self, mut module: QuoteWat<'a>, ) -> Result<(Option<&'a str>, &'a str, String)> { - let name = match &module { - QuoteWat::Wat(Wat::Module(m)) => m.id, - QuoteWat::Wat(Wat::Component(m)) => m.id, - QuoteWat::QuoteModule(..) | QuoteWat::QuoteComponent(..) => None, - }; - let name = name.map(|i| i.name()); + let name = module.name().map(|i| i.name()); let (contents, module_type, ext) = match module.to_test()? { QuoteWatTest::Text(s) => (s, "text", "wat"), QuoteWatTest::Binary(s) => (s, "binary", "wasm"), @@ -571,6 +581,19 @@ mod json { name: Option, filename: String, }, + ModuleDefinition { + line: u32, + #[serde(skip_serializing_if = "Option::is_none")] + name: Option, + filename: String, + }, + ModuleInstance { + line: u32, + #[serde(skip_serializing_if = "Option::is_none")] + instance: Option, + #[serde(skip_serializing_if = "Option::is_none")] + module: Option, + }, AssertMalformed { line: u32, filename: String, diff --git a/src/bin/wasm-tools/validate.rs b/src/bin/wasm-tools/validate.rs index 7283c4da0b..a065665df9 100644 --- a/src/bin/wasm-tools/validate.rs +++ b/src/bin/wasm-tools/validate.rs @@ -1,5 +1,5 @@ use addr2line::LookupResult; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use bitflags::Flags; use rayon::prelude::*; use std::fmt::Write; @@ -183,53 +183,76 @@ impl Opts { fn parse_features(arg: &str) -> Result { let mut ret = WasmFeatures::default(); - fn flag_name(flag: &bitflags::Flag) -> String { - flag.name().to_lowercase().replace('_', "-") + const GROUPS: &[(&str, WasmFeatures)] = &[ + ("mvp", WasmFeatures::WASM1), + ("wasm1", WasmFeatures::WASM1), + ("wasm2", WasmFeatures::WASM2), + ("wasm3", WasmFeatures::WASM3), + ]; + + enum Action { + ChangeAll, + Group(WasmFeatures), + Modify(WasmFeatures), + } + + fn actions() -> impl Iterator { + WasmFeatures::FLAGS + .iter() + .map(|f| (f.name(), Action::Modify(*f.value()))) + .chain( + GROUPS + .iter() + .map(|(name, features)| (*name, Action::Group(*features))), + ) + .chain([("all", Action::ChangeAll)]) + } + + fn flag_name(name: &str) -> String { + name.to_lowercase().replace('_', "-") } - for part in arg.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) { + 'outer: for part in arg.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) { let (enable, part) = if let Some(part) = part.strip_prefix("-") { (false, part) } else { (true, part) }; - match part { - "all" => { - for flag in WasmFeatures::FLAGS.iter() { - ret.set(*flag.value(), enable); - } + for (name, action) in actions() { + if part != flag_name(name) { + continue; } - "wasm1" | "mvp" => { - if !enable { - bail!("cannot disable `{part}`, it can only be enabled"); + match action { + Action::ChangeAll => { + for flag in WasmFeatures::FLAGS.iter() { + ret.set(*flag.value(), enable); + } } - ret = WasmFeatures::wasm1(); - } - "wasm2" => { - if !enable { - bail!("cannot disable `{part}`, it can only be enabled"); + Action::Modify(feature) => { + ret.set(feature, enable); + } + Action::Group(features) => { + if !enable { + bail!("cannot disable `{part}`, it can only be enabled"); + } + ret = features; } - ret = WasmFeatures::wasm2(); } + continue 'outer; + } - name => { - let flag = WasmFeatures::FLAGS - .iter() - .find(|f| flag_name(f) == name) - .ok_or_else(|| { - anyhow!( - "unknown feature `{}`\nValid features: {}", - name, - WasmFeatures::FLAGS - .iter() - .map(flag_name) - .collect::>() - .join(", "), - ) - })?; - ret.set(*flag.value(), enable); + let mut error = format!("unknown feature `{part}`\n"); + error.push_str("Valid features: "); + let mut first = true; + for (name, _) in actions() { + if first { + first = false; + } else { + error.push_str(", "); } + error.push_str(&flag_name(name)); } + bail!("{error}") } Ok(ret) diff --git a/tests/cli/validate-unknown-features.wat.stderr b/tests/cli/validate-unknown-features.wat.stderr index 3f7989123c..ce072e0c75 100644 --- a/tests/cli/validate-unknown-features.wat.stderr +++ b/tests/cli/validate-unknown-features.wat.stderr @@ -1,4 +1,4 @@ error: invalid value 'unknown' for '--features ': unknown feature `unknown` -Valid features: mutable-global, saturating-float-to-int, sign-extension, reference-types, multi-value, bulk-memory, simd, relaxed-simd, threads, shared-everything-threads, tail-call, floats, multi-memory, exceptions, memory64, extended-const, component-model, function-references, memory-control, gc, custom-page-sizes, component-model-values, component-model-nested-names, component-model-more-flags, component-model-multiple-returns, legacy-exceptions, gc-types, typed-continuations +Valid features: mutable-global, saturating-float-to-int, sign-extension, reference-types, multi-value, bulk-memory, simd, relaxed-simd, threads, shared-everything-threads, tail-call, floats, multi-memory, exceptions, memory64, extended-const, component-model, function-references, memory-control, gc, custom-page-sizes, component-model-values, component-model-nested-names, component-model-more-flags, component-model-multiple-returns, legacy-exceptions, gc-types, typed-continuations, mvp, wasm1, wasm2, wasm3, all For more information, try '--help'. diff --git a/tests/local/code-after-end.wast b/tests/local/code-after-end.wast new file mode 100644 index 0000000000..5656e5e94f --- /dev/null +++ b/tests/local/code-after-end.wast @@ -0,0 +1,34 @@ +(assert_invalid + (module + (func end)) + "operators remaining after end of function") + +(assert_invalid + (module + (func end block)) + "operators remaining after end of function") + +(assert_invalid + (module + (func end i32.add)) + "operators remaining after end of function") + +(assert_invalid + (module + (func end unreachable)) + "operators remaining after end of function") + +(assert_invalid + (module + (func end br 0)) + "operators remaining after end of function") + +(assert_invalid + (module + (func end return)) + "operators remaining after end of function") + +(assert_invalid + (module + (func end return_call 0)) + "operators remaining after end of function") diff --git a/tests/local/component-model/import.wast b/tests/local/component-model/import.wast index a04823cf55..b12eca5e53 100644 --- a/tests/local/component-model/import.wast +++ b/tests/local/component-model/import.wast @@ -341,3 +341,24 @@ (assert_invalid (component (import "integrity=" (func))) "unrecognized hash algorithm") + +;; Prior to WebAssembly/component-model#263 this was a valid component. +;; Specifically the 0x01 prefix byte on the import was valid. Nowadays that's +;; not valid in the spec but it's accepted for backwards compatibility. This +;; tests is here to ensure such compatibility. In the future this test should +;; be changed to `(assert_invalid ...)` +(component binary + "\00asm" "\0d\00\01\00" ;; component header + + "\07\05" ;; type section, 5 bytes large + "\01" ;; 1 count + "\40" ;; function + "\00" ;; parameters, 0 count + "\01\00" ;; results, named, 0 count + + "\0a\06" ;; import section, 6 bytes large + "\01" ;; 1 count + "\01" ;; prefix byte of 0x01 (invalid by the spec nowadays) + "\01a" ;; name = "a" + "\01\00" ;; type = func ($type 0) +) diff --git a/tests/local/component-model/types.wast b/tests/local/component-model/types.wast index 34115c942f..0323cab8e7 100644 --- a/tests/local/component-model/types.wast +++ b/tests/local/component-model/types.wast @@ -357,3 +357,45 @@ )) ) "cannot have more than 32 flags") + +;; test components with non-mvp types +(component + ;; all abstract heap types work + (core type (func (param (ref any)))) + (core type (func (param (ref func)))) + (core type (func (param (ref extern)))) + (core type (func (param (ref exn)))) + (core type (func (param (ref noexn)))) + (core type (func (param (ref eq)))) + (core type (func (param (ref struct)))) + (core type (func (param (ref array)))) + (core type (func (param (ref nofunc)))) + (core type (func (param (ref noextern)))) + (core type (func (param (ref none)))) + (core type (func (param (ref i31)))) + + ;; some shorthands work + (core type (func (param anyref))) + (core type (func (param eqref))) + + ;; simd types work + (core type (func (param v128))) + + ;; types-pointing-to-types works + (core type $t (func)) + (core type (func (param (ref $t)))) + +) + +(assert_invalid + (component + (core type $t (module)) + (core type (func (param (ref $t)))) + ) + "type index 0 is a module type") + +(assert_invalid + (component + (core type (func (param (ref 100)))) + ) + "type index out of bounds") diff --git a/tests/local/duplicate.wast b/tests/local/duplicate.wast index 621a837083..c01aa35bae 100644 --- a/tests/local/duplicate.wast +++ b/tests/local/duplicate.wast @@ -1,58 +1,58 @@ (assert_malformed (module quote "(func $foo)" "(func $foo)") - "duplicate identifier") + "duplicate func identifier") (assert_malformed (module quote "(import \"\" \"\" (func $foo))" "(func $foo)") - "duplicate identifier") + "duplicate func identifier") (assert_malformed (module quote "(import \"\" \"\" (func $foo))" "(import \"\" \"\" (func $foo))") - "duplicate identifier") + "duplicate func identifier") (assert_malformed (module quote "(global $foo i32 (i32.const 0))" "(global $foo i32 (i32.const 0))") - "duplicate identifier") + "duplicate global identifier") (assert_malformed (module quote "(import \"\" \"\" (global $foo i32))" "(global $foo i32 (i32.const 0))") - "duplicate identifier") + "duplicate global identifier") (assert_malformed (module quote "(import \"\" \"\" (global $foo i32))" "(import \"\" \"\" (global $foo i32))") - "duplicate identifier") + "duplicate global identifier") (assert_malformed (module quote "(memory $foo 1)" "(memory $foo 1)") - "duplicate identifier") + "duplicate memory identifier") (assert_malformed (module quote "(import \"\" \"\" (memory $foo 1))" "(memory $foo 1)") - "duplicate identifier") + "duplicate memory identifier") (assert_malformed (module quote "(import \"\" \"\" (memory $foo 1))" "(import \"\" \"\" (memory $foo 1))") - "duplicate identifier") + "duplicate memory identifier") (assert_malformed (module quote "(table $foo 1 funcref)" "(table $foo 1 funcref)") - "duplicate identifier") + "duplicate table identifier") (assert_malformed (module quote "(import \"\" \"\" (table $foo 1 funcref))" "(table $foo 1 funcref)") - "duplicate identifier") + "duplicate table identifier") (assert_malformed (module quote "(import \"\" \"\" (table $foo 1 funcref))" "(import \"\" \"\" (table $foo 1 funcref))") - "duplicate identifier") + "duplicate table identifier") (assert_malformed (module quote "(func (param $foo i32) (param $foo i32))") - "duplicate identifier") + "duplicate local identifier") (assert_malformed (module quote "(func (param $foo i32) (local $foo i32))") - "duplicate identifier") + "duplicate local identifier") (assert_malformed (module quote "(func (local $foo i32) (local $foo i32))") - "duplicate identifier") + "duplicate local identifier") diff --git a/tests/local/exnref/try-table.wast b/tests/local/exnref/try-table.wast new file mode 100644 index 0000000000..f416a8b93c --- /dev/null +++ b/tests/local/exnref/try-table.wast @@ -0,0 +1,10 @@ +(assert_invalid + (module + (func + block $l (result anyref) + try_table (catch_all_ref $l) + end + end + ) + ) + "type mismatch: catch_all_ref label must a subtype of (ref exn)") diff --git a/tests/local/function-references/return-call.wast b/tests/local/function-references/return-call.wast index c1c56079ad..a5eb4e7592 100644 --- a/tests/local/function-references/return-call.wast +++ b/tests/local/function-references/return-call.wast @@ -33,5 +33,5 @@ return_call_ref $ty ) ) - "type mismatch: current function requires result type [i32] but callee returns [i32 i32]" + "type mismatch: expected (ref null $type), found funcref" ) diff --git a/tests/local/gc/invalid.wast b/tests/local/gc/invalid.wast index 3f3342b5aa..20f7727459 100644 --- a/tests/local/gc/invalid.wast +++ b/tests/local/gc/invalid.wast @@ -29,3 +29,292 @@ ) "type mismatch: expected structref, found anyref" ) + +(assert_invalid + (module + (type $t (func)) + (func struct.new $t drop)) + "expected struct type at index 0, found (func ...)") + +(assert_invalid + (module + (type $t (struct)) + (func struct.get $t 100)) + "unknown field: field index out of bounds") + +(assert_invalid + (module + (func struct.get 200 100)) + "unknown type: type index out of bounds") + +(assert_invalid + (module + (type $t (struct)) + (func array.new $t drop)) + "expected array type at index 0, found (struct ...)") + +(assert_invalid + (module + (func array.new 100)) + "unknown type: type index out of bounds") + +(assert_invalid + (module + (type $t (struct)) + (func (type $t))) + "type index 0 is not a function type") + +(assert_invalid + (module + (type $t (struct)) + (func block (type $t) end)) + "expected func type at index 0, found (struct ...)") + +(assert_invalid + (module + (func + block $l + unreachable + br_on_non_null $l + end + ) + ) + "type mismatch: br_on_non_null target has no label types") + +(assert_invalid + (module + (func + block $l (result i32) + unreachable + br_on_non_null $l + end + ) + ) + "type mismatch: br_on_non_null target does not end with heap type") + +(assert_invalid + (module + (type $t (struct (field (ref func)))) + (func + struct.new_default $t + ) + ) + "invalid `struct.new_default`: (ref func) field is not defaultable") + +(assert_invalid + (module + (type $t (struct (field $f i32))) + (func + unreachable + struct.get_u $t $f + ) + ) + "cannot use struct.get_u with non-packed storage types") + +(assert_invalid + (module + (type $t (array (ref func))) + (func + array.new_default $t + ) + ) + "invalid `array.new_default`: (ref func) field is not defaultable") + +(assert_invalid + (module + (type $t (array (ref func))) + (func + i32.const 0 + i32.const 0 + array.new_data $t $d + drop + ) + (data $d "xxx") + ) + "array.new_data can only create arrays with numeric and vector elements") + +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" ;; module header + + "\01\07" ;; type section, 7 bytes + "\02" ;; 2 types + "\5e\7f\00" ;; (type (array i32)) + "\60\00\00" ;; (type (func)) + + "\03\02" ;; func section, 2 bytes + "\01" ;; 1 func + "\01" ;; type 1 + + "\0a\0d" ;; code section, 13 bytes + "\01" ;; 1 count + "\0b" ;; 11-byte function + "\00" ;; no locals + "\41\00" ;; i32.const 0 + "\41\00" ;; i32.const 0 + "\fb\09\00\00" ;; array.new_data 0 0 + "\1a" ;; drop + "\0b" ;; end + + "\0b\06" ;; data section, 6 bytes + "\01" ;; 1 count + "\01" ;; passive + "\03xxx" ;; 3 bytes of data "xxx" + ) + "data count section required") + +;; slightly modified version of the above with a data count section +(module binary + "\00asm" "\01\00\00\00" ;; module header + + "\01\07" ;; type section, 7 bytes + "\02" ;; 2 types + "\5e\7f\00" ;; (type (array i32)) + "\60\00\00" ;; (type (func)) + + "\03\02" ;; func section, 2 bytes + "\01" ;; 1 func + "\01" ;; type 1 + + "\0c\01" ;; data count section, 1 byte + "\01" ;; 1 data + + "\0a\0d" ;; code section, 13 bytes + "\01" ;; 1 count + "\0b" ;; 11-byte function + "\00" ;; no locals + "\41\00" ;; i32.const 0 + "\41\00" ;; i32.const 0 + "\fb\09\00\00" ;; array.new_data 0 0 + "\1a" ;; drop + "\0b" ;; end + + "\0b\06" ;; data section, 6 bytes + "\01" ;; 1 count + "\01" ;; passive + "\03xxx" ;; 3 bytes of data "xxx" +) + +(assert_invalid + (module + (type $t (array i8)) + (func + i32.const 0 + i32.const 0 + array.new_data $t 100 + drop + ) + ) + "unknown data segment 100") + +(assert_invalid + (module + (type $t (array i8)) + (func + i32.const 0 + i32.const 0 + array.new_elem $t $e + drop + ) + (elem $e funcref) + ) + "type mismatch: array.new_elem can only create arrays with reference elements") + +(assert_invalid + (module + (type $t (array (ref any))) + (func + i32.const 0 + i32.const 0 + array.new_elem $t $e + drop + ) + (elem $e funcref) + ) + "invalid array.new_elem instruction: element segment 0 type mismatch: expected (ref any), found funcref") + +(assert_invalid + (module + (type $t1 (array (mut i8))) + (type $t2 (array (mut i16))) + (func + unreachable + array.copy $t1 $t2 + ) + ) + "array types do not match: expected i8, found i16") + +(assert_invalid + (module + (type $t1 (array (mut i16))) + (type $t2 (array (mut i8))) + (func + unreachable + array.copy $t1 $t2 + ) + ) + "array types do not match: expected i16, found i8") + +(assert_invalid + (module + (type $t1 (array (mut i32))) + (type $t2 (array (mut i64))) + (func + unreachable + array.copy $t1 $t2 + ) + ) + "array types do not match: expected i32, found i64") + +(assert_invalid + (module + (type $t1 (array (mut i32))) + (type $t2 (array (mut i8))) + (func + unreachable + array.copy $t1 $t2 + ) + ) + "array types do not match: expected i32, found i8") + +(assert_invalid + (module + (type $t1 (array (mut i8))) + (type $t2 (array (mut i32))) + (func + unreachable + array.copy $t1 $t2 + ) + ) + "array types do not match: expected i8, found i32") + +(module + (type $t1 (array (mut i16))) + (type $t2 (array (mut i16))) + (func + unreachable + array.copy $t1 $t2 + ) +) + +(assert_invalid + (module + (type $t1 (array (mut i8))) + (type $t2 (array (mut i32))) + (func + block + unreachable + br_on_cast_fail 0 anyref anyref + end + ) + ) + "type mismatch: expected a reference type, found nothing") + +(assert_invalid + (module + (table 1 externref) + (func + i32.const 0 + call_indirect + )) + "indirect calls must go through a table with type <= funcref") diff --git a/tests/local/instance.wast b/tests/local/instance.wast new file mode 100644 index 0000000000..b88025d699 --- /dev/null +++ b/tests/local/instance.wast @@ -0,0 +1,170 @@ +;; Instantiation is generative + +(module definition $M + (global (export "glob") (mut i32) (i32.const 0)) + (table (export "tab") 10 funcref (ref.null func)) + (memory (export "mem") 1) + (tag (export "tag")) +) + +(module instance $I1 $M) +(module instance $I2 $M) +(register "I1" $I1) +(register "I2" $I2) + +(module + (import "I1" "glob" (global $glob1 (mut i32))) + (import "I2" "glob" (global $glob2 (mut i32))) + (import "I1" "tab" (table $tab1 10 funcref)) + (import "I2" "tab" (table $tab2 10 funcref)) + (import "I1" "mem" (memory $mem1 1)) + (import "I2" "mem" (memory $mem2 1)) + (import "I1" "tag" (tag $tag1)) + (import "I2" "tag" (tag $tag2)) + + (func $f) + (elem declare func $f) + + (func (export "glob") (result i32) + (global.set $glob1 (i32.const 1)) + (global.get $glob2) + ) + (func (export "tab") (result funcref) + (table.set $tab1 (i32.const 0) (ref.func $f)) + (table.get $tab2 (i32.const 0)) + ) + (func (export "mem") (result i32) + (i32.store $mem1 (i32.const 0) (i32.const 1)) + (i32.load $mem2 (i32.const 0)) + ) + (func (export "tag") (result i32) + (block $on_tag1 + (block $on_other + (try_table (catch $tag1 $on_tag1) (catch_all $on_other) + (throw $tag2) + ) + (unreachable) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) +) + +(assert_return (invoke "glob") (i32.const 0)) +(assert_return (invoke "tab") (ref.null)) +(assert_return (invoke "mem") (i32.const 0)) +(assert_return (invoke "tag") (i32.const 0)) + + +;; Import is not generative + +(module + (import "I1" "glob" (global $glob1 (mut i32))) + (import "I1" "glob" (global $glob2 (mut i32))) + (import "I1" "tab" (table $tab1 10 funcref)) + (import "I1" "tab" (table $tab2 10 funcref)) + (import "I1" "mem" (memory $mem1 1)) + (import "I1" "mem" (memory $mem2 1)) + (import "I1" "tag" (tag $tag1)) + (import "I1" "tag" (tag $tag2)) + + (func $f) + (elem declare func $f) + + (func (export "glob") (result i32) + (global.set $glob1 (i32.const 1)) + (global.get $glob2) + ) + (func (export "tab") (result funcref) + (table.set $tab1 (i32.const 0) (ref.func $f)) + (table.get $tab2 (i32.const 0)) + ) + (func (export "mem") (result i32) + (i32.store $mem1 (i32.const 0) (i32.const 1)) + (i32.load $mem2 (i32.const 0)) + ) + (func (export "tag") (result i32) + (block $on_tag1 + (block $on_other + (try_table (catch $tag1 $on_tag1) (catch_all $on_other) + (throw $tag2) + ) + (unreachable) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) +) + +(assert_return (invoke "glob") (i32.const 1)) +(assert_return (invoke "tab") (ref.func)) +(assert_return (invoke "mem") (i32.const 1)) +(assert_return (invoke "tag") (i32.const 1)) + + +;; Export is not generative + +(module definition $N + (global $glob (mut i32) (i32.const 0)) + (table $tab 10 funcref (ref.null func)) + (memory $mem 1) + (tag $tag) + + (export "glob1" (global $glob)) + (export "glob2" (global $glob)) + (export "tab1" (table $tab)) + (export "tab2" (table $tab)) + (export "mem1" (memory $mem)) + (export "mem2" (memory $mem)) + (export "tag1" (tag $tag)) + (export "tag2" (tag $tag)) +) + +(module instance $I $N) +(register "I" $I) + +(module + (import "I" "glob1" (global $glob1 (mut i32))) + (import "I" "glob2" (global $glob2 (mut i32))) + (import "I" "tab1" (table $tab1 10 funcref)) + (import "I" "tab2" (table $tab2 10 funcref)) + (import "I" "mem1" (memory $mem1 1)) + (import "I" "mem2" (memory $mem2 1)) + (import "I" "tag1" (tag $tag1)) + (import "I" "tag2" (tag $tag2)) + + (func $f) + (elem declare func $f) + + (func (export "glob") (result i32) + (global.set $glob1 (i32.const 1)) + (global.get $glob2) + ) + (func (export "tab") (result funcref) + (table.set $tab1 (i32.const 0) (ref.func $f)) + (table.get $tab2 (i32.const 0)) + ) + (func (export "mem") (result i32) + (i32.store $mem1 (i32.const 0) (i32.const 1)) + (i32.load $mem2 (i32.const 0)) + ) + (func (export "tag") (result i32) + (block $on_tag1 + (block $on_other + (try_table (catch $tag1 $on_tag1) (catch_all $on_other) + (throw $tag2) + ) + (unreachable) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) +) + +(assert_return (invoke "glob") (i32.const 1)) +(assert_return (invoke "tab") (ref.func)) +(assert_return (invoke "mem") (i32.const 1)) +(assert_return (invoke "tag") (i32.const 1)) diff --git a/tests/local/invalid.wast b/tests/local/invalid.wast new file mode 100644 index 0000000000..f793557676 --- /dev/null +++ b/tests/local/invalid.wast @@ -0,0 +1,10 @@ +(assert_invalid + (module + (table 1 funcref) + (func table.init 0 100)) + "unknown elem segment") + +(assert_invalid + (module + (func else)) + "else found outside of an `if` block") diff --git a/tests/local/invalid/empty-br-table.wast b/tests/local/invalid/empty-br-table.wast index f73082dfa4..1b95fa2020 100644 --- a/tests/local/invalid/empty-br-table.wast +++ b/tests/local/invalid/empty-br-table.wast @@ -5,8 +5,8 @@ "\01\04\01\60\00\00" ;; type section, 1 type, (func) "\03\02\01\00" ;; func section, 1 function, type 0 - "\0a\0d\01" ;; code section - "\0b" ;; function size + "\0a\0e\01" ;; code section + "\0c" ;; function size "\00" ;; no locals "\02\40" ;; block "\41\01" ;; i32.const 1 diff --git a/tests/local/legacy-exceptions/throw.wast b/tests/local/legacy-exceptions/throw.wast index 2ab9f2f330..d36e849164 100644 --- a/tests/local/legacy-exceptions/throw.wast +++ b/tests/local/legacy-exceptions/throw.wast @@ -51,6 +51,6 @@ (assert_invalid (module (func (throw 0))) "unknown tag 0") (assert_invalid (module (tag (param i32)) (func (throw 0))) - "type mismatch: instruction requires [i32] but stack has []") + "type mismatch") (assert_invalid (module (tag (param i32)) (func (i64.const 5) (throw 0))) - "type mismatch: instruction requires [i32] but stack has [i64]") + "type mismatch") diff --git a/tests/local/legacy-exceptions/try_catch.wast b/tests/local/legacy-exceptions/try_catch.wast index 8819e82487..7d93a36335 100644 --- a/tests/local/legacy-exceptions/try_catch.wast +++ b/tests/local/legacy-exceptions/try_catch.wast @@ -295,15 +295,15 @@ ;; ) (assert_invalid (module (func (result i32) try (result i32) end)) - "type mismatch: instruction requires [i32] but stack has []") + "type mismatch") (assert_invalid (module (func (result i32) try (result i32) i64.const 42 end)) - "type mismatch: instruction requires [i32] but stack has [i64]") + "type mismatch") (assert_invalid (module (tag) (func try catch 0 i32.const 42 end)) - "type mismatch: block requires [] but stack has [i32]") + "type mismatch") (assert_invalid (module (tag (param i64)) (func (result i32) try (result i32) i32.const 42 catch 0 end)) - "type mismatch: instruction requires [i32] but stack has [i64]") + "type mismatch") (assert_invalid (module (func try catch_all i32.const 32 end)) - "type mismatch: block requires [] but stack has [i32]") + "type mismatch") diff --git a/tests/local/missing-features/component-model/types.wast b/tests/local/missing-features/component-model/types.wast new file mode 100644 index 0000000000..b4d4772d3d --- /dev/null +++ b/tests/local/missing-features/component-model/types.wast @@ -0,0 +1,18 @@ +(assert_invalid + (component + (core type (func (param v128))) + ) + "SIMD support is not enabled") + +(assert_invalid + (component + (core type (func (param (ref 0)))) + ) + "unknown type 0: type index out of bounds") + +(assert_invalid + (component + (core type (func)) + (core type (func (param (ref 0)))) + ) + "reference types support is not enabled") diff --git a/tests/local/missing-features/gc/shared-any.wast b/tests/local/missing-features/gc/shared-any.wast new file mode 100644 index 0000000000..ae1de285fe --- /dev/null +++ b/tests/local/missing-features/gc/shared-any.wast @@ -0,0 +1,8 @@ +(assert_invalid + (module + (func + ref.null (shared any) + drop + ) + ) + "shared reference types require the shared-everything-threads proposal") diff --git a/tests/local/shared-everything-threads/arrays.wast b/tests/local/shared-everything-threads/arrays.wast index b545b4458d..2d91dde5d2 100644 --- a/tests/local/shared-everything-threads/arrays.wast +++ b/tests/local/shared-everything-threads/arrays.wast @@ -849,3 +849,46 @@ ) "invalid type" ) + +(assert_invalid + (module + (type $s (shared (array f32))) + (func + unreachable + array.atomic.get seq_cst $s + drop + )) + "invalid type: `array.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`" +) + +(assert_invalid + (module + (type $s (shared (array (ref (shared func))))) + (func + unreachable + array.atomic.get seq_cst $s + drop + )) + "invalid type: `array.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`" +) + +(assert_invalid + (module + (type $s (shared (array i8))) + (func + unreachable + array.atomic.get seq_cst $s + drop + )) + "cannot use array.get with packed storage types" +) + +(assert_invalid + (module + (type $s (shared (array (mut (ref (shared extern)))))) + (func + unreachable + array.atomic.set seq_cst $s + )) + "invalid type: `array.atomic.set` only allows `i8`, `i16`, `i32`, `i64` and subtypes of `anyref`" +) diff --git a/tests/local/shared-everything-threads/globals.wast b/tests/local/shared-everything-threads/globals.wast index 1066fcf406..857a0bda7a 100644 --- a/tests/local/shared-everything-threads/globals.wast +++ b/tests/local/shared-everything-threads/globals.wast @@ -367,3 +367,63 @@ global.atomic.rmw.cmpxchg acq_rel $b) ) +(assert_invalid + (module + (global $g (mut funcref) (ref.null func)) + (func + ref.null func + global.atomic.rmw.xor acq_rel $g + drop + ) + ) + "invalid type: `global.atomic.rmw.*` only allows `i32` and `i64`") + +(assert_invalid + (module + (func + global.atomic.rmw.xor acq_rel 200 + ) + ) + "global index out of bounds") + +(assert_invalid + (module + (global $g (mut funcref) (ref.null func)) + (func + ref.null func + global.atomic.rmw.xchg acq_rel $g + drop + ) + ) + "invalid type: `global.atomic.rmw.xchg` only allows `i32`, `i64` and subtypes of `anyref`") + +(module + (global $g (mut eqref) (ref.null eq)) + (func + ref.null eq + global.atomic.rmw.xchg acq_rel $g + drop + ) +) + +(assert_invalid + (module + (global $g (mut anyref) (ref.null any)) + (func + ref.null any + ref.null any + global.atomic.rmw.cmpxchg acq_rel $g + drop + ) + ) + "invalid type: `global.atomic.rmw.cmpxchg` only allows `i32`, `i64` and subtypes of `eqref`") + +(module + (global $g (mut eqref) (ref.null eq)) + (func + ref.null eq + ref.null eq + global.atomic.rmw.cmpxchg acq_rel $g + drop + ) +) diff --git a/tests/local/shared-everything-threads/structs.wast b/tests/local/shared-everything-threads/structs.wast index 240e1f87ff..da112f7d7c 100644 --- a/tests/local/shared-everything-threads/structs.wast +++ b/tests/local/shared-everything-threads/structs.wast @@ -527,3 +527,46 @@ ) "invalid type" ) + +(assert_invalid + (module + (type $s (shared (struct (field $f f32)))) + (func + unreachable + struct.atomic.get seq_cst $s $f + drop + )) + "invalid type: `struct.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`" +) + +(assert_invalid + (module + (type $s (shared (struct (field $f (ref (shared func)))))) + (func + unreachable + struct.atomic.get seq_cst $s $f + drop + )) + "invalid type: `struct.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`" +) + +(assert_invalid + (module + (type $s (shared (struct (field $f i8)))) + (func + unreachable + struct.atomic.get seq_cst $s $f + drop + )) + "can only use struct `get` with non-packed storage types" +) + +(assert_invalid + (module + (type $s (shared (struct (field $f (mut (ref (shared extern))))))) + (func + unreachable + struct.atomic.set seq_cst $s $f + )) + "invalid type: `struct.atomic.set` only allows `i8`, `i16`, `i32`, `i64` and subtypes of `anyref`" +) diff --git a/tests/local/shared-everything-threads/tables.wast b/tests/local/shared-everything-threads/tables.wast index b733ec4c2b..d293a29203 100644 --- a/tests/local/shared-everything-threads/tables.wast +++ b/tests/local/shared-everything-threads/tables.wast @@ -30,7 +30,7 @@ ;; would need to lookahead multiple tokens. (assert_malformed (module quote "(table shared i64 (ref null (shared func)) (elem (ref.null (shared func))))") - "unexpected token") + "expected a u64") (assert_invalid (module (table (import "spectest" "table_ref") shared 0 funcref)) @@ -172,3 +172,35 @@ local.get $z table.atomic.rmw.cmpxchg seq_cst $a)) "invalid type") + +(assert_invalid + (module + (table 1 funcref) + (func + i32.const 0 + table.atomic.get seq_cst 0 + ) + ) + "invalid type: `table.atomic.get` only allows subtypes of `anyref`") + +(assert_invalid + (module + (table 1 funcref) + (func + i32.const 0 + ref.null func + table.atomic.set seq_cst 0 + ) + ) + "invalid type: `table.atomic.set` only allows subtypes of `anyref`") + +(assert_invalid + (module + (table 1 funcref) + (func + i32.const 0 + ref.null func + table.atomic.rmw.xchg seq_cst 0 + ) + ) + "invalid type: `table.atomic.rmw.xchg` only allows subtypes of `anyref`") diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index 92424b76a5..cc530e30fa 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -32,6 +32,7 @@ use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::sync::Arc; use wasm_encoder::reencode::{Reencode, ReencodeComponent, RoundtripReencoder}; use wasmparser::*; +use wast::component::{Component, ComponentKind}; use wast::core::{Module, ModuleKind}; use wast::lexer::Lexer; use wast::parser::ParseBuffer; @@ -286,7 +287,7 @@ impl TestState { fn test_wast_directive(&self, test: &Path, directive: WastDirective, idx: usize) -> Result<()> { match directive { - WastDirective::Wat(mut module) => { + WastDirective::Module(mut module) | WastDirective::ModuleDefinition(mut module) => { let actual = module.encode()?; self.bump_ntests(); // testing encode @@ -302,8 +303,11 @@ impl TestState { QuoteWat::Wat(Wat::Module(Module { kind: ModuleKind::Binary(_), .. + })) + | QuoteWat::Wat(Wat::Component(Component { + kind: ComponentKind::Binary(_), + .. })) => false, - _ => true, }; @@ -355,7 +359,7 @@ impl TestState { message, ), Err(e) => { - if error_matches(&format!("{:?}", e), message) { + if error_matches(test, &format!("{:?}", e), message) { self.bump_ntests(); return Ok(()); } @@ -372,7 +376,8 @@ impl TestState { // This test suite doesn't actually execute any wasm code, so ignore // all of these assertions. - WastDirective::Register { .. } + WastDirective::ModuleInstance { .. } + | WastDirective::Register { .. } | WastDirective::Invoke(_) | WastDirective::AssertTrap { .. } | WastDirective::AssertReturn { .. } @@ -600,16 +605,9 @@ impl TestState { for part in test.iter().filter_map(|t| t.to_str()) { match part { "testsuite" => { - features = WasmFeatures::wasm2(); - features |= WasmFeatures::TAIL_CALL; - features |= WasmFeatures::EXTENDED_CONST; - - // NB: when these proposals are merged upstream in the spec - // repo then this should be removed. Currently this hasn't - // happened so this is required to get tests passing for - // when these proposals are enabled by default. - features.remove(WasmFeatures::MULTI_MEMORY); - features.remove(WasmFeatures::THREADS); + features = WasmFeatures::WASM2 + | WasmFeatures::TAIL_CALL + | WasmFeatures::EXTENDED_CONST; } "missing-features" => { features = @@ -626,14 +624,7 @@ impl TestState { "exception-handling" => features.insert(WasmFeatures::EXCEPTIONS), "legacy-exceptions" => features.insert(WasmFeatures::LEGACY_EXCEPTIONS), "tail-call" => features.insert(WasmFeatures::TAIL_CALL), - "memory64" => features.insert( - WasmFeatures::MEMORY64 - | WasmFeatures::GC - | WasmFeatures::REFERENCE_TYPES - | WasmFeatures::MULTI_MEMORY - | WasmFeatures::FUNCTION_REFERENCES - | WasmFeatures::EXCEPTIONS, - ), + "memory64" => features.insert(WasmFeatures::MEMORY64 | WasmFeatures::WASM3), "component-model" => features.insert(WasmFeatures::COMPONENT_MODEL), "shared-everything-threads" => { features.insert(WasmFeatures::COMPONENT_MODEL); @@ -671,10 +662,17 @@ impl TestState { } } -fn error_matches(error: &str, message: &str) -> bool { +fn error_matches(test: &Path, error: &str, message: &str) -> bool { if error.contains(message) { return true; } + // we are in control over all tsets in `tests/local/*` so all the error + // messages there should exactly match the `assert_invalid` or such. No need + // for fuzzy matching on error messages. + if test.starts_with("tests/local") { + return false; + } + if message == "unknown operator" || message == "unexpected token" || message == "wrong number of lane literals" diff --git a/tests/snapshots/local/code-after-end.wast.json b/tests/snapshots/local/code-after-end.wast.json new file mode 100644 index 0000000000..4dce353aba --- /dev/null +++ b/tests/snapshots/local/code-after-end.wast.json @@ -0,0 +1,54 @@ +{ + "source_filename": "tests/local/code-after-end.wast", + "commands": [ + { + "type": "assert_invalid", + "line": 2, + "filename": "code-after-end.0.wasm", + "text": "operators remaining after end of function", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 7, + "filename": "code-after-end.1.wasm", + "text": "operators remaining after end of function", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 12, + "filename": "code-after-end.2.wasm", + "text": "operators remaining after end of function", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 17, + "filename": "code-after-end.3.wasm", + "text": "operators remaining after end of function", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 22, + "filename": "code-after-end.4.wasm", + "text": "operators remaining after end of function", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 27, + "filename": "code-after-end.5.wasm", + "text": "operators remaining after end of function", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 32, + "filename": "code-after-end.6.wasm", + "text": "operators remaining after end of function", + "module_type": "binary" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/local/component-model/import.wast.json b/tests/snapshots/local/component-model/import.wast.json index ad84643248..4b291d20ba 100644 --- a/tests/snapshots/local/component-model/import.wast.json +++ b/tests/snapshots/local/component-model/import.wast.json @@ -509,6 +509,11 @@ "filename": "import.74.wasm", "text": "unrecognized hash algorithm", "module_type": "binary" + }, + { + "type": "module", + "line": 350, + "filename": "import.75.wasm" } ] } \ No newline at end of file diff --git a/tests/snapshots/local/component-model/import.wast/75.print b/tests/snapshots/local/component-model/import.wast/75.print new file mode 100644 index 0000000000..e601555176 --- /dev/null +++ b/tests/snapshots/local/component-model/import.wast/75.print @@ -0,0 +1,4 @@ +(component + (type (;0;) (func)) + (import "a" (func (;0;) (type 0))) +) diff --git a/tests/snapshots/local/component-model/types.wast.json b/tests/snapshots/local/component-model/types.wast.json index 8279f094ca..a606256226 100644 --- a/tests/snapshots/local/component-model/types.wast.json +++ b/tests/snapshots/local/component-model/types.wast.json @@ -283,6 +283,25 @@ "filename": "types.41.wasm", "text": "cannot have more than 32 flags", "module_type": "binary" + }, + { + "type": "module", + "line": 362, + "filename": "types.42.wasm" + }, + { + "type": "assert_invalid", + "line": 391, + "filename": "types.43.wasm", + "text": "type index 0 is a module type", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 398, + "filename": "types.44.wasm", + "text": "type index out of bounds", + "module_type": "binary" } ] } \ No newline at end of file diff --git a/tests/snapshots/local/component-model/types.wast/42.print b/tests/snapshots/local/component-model/types.wast/42.print new file mode 100644 index 0000000000..233982b2c2 --- /dev/null +++ b/tests/snapshots/local/component-model/types.wast/42.print @@ -0,0 +1,19 @@ +(component + (core type (;0;) (func (param (ref any)))) + (core type (;1;) (func (param (ref func)))) + (core type (;2;) (func (param (ref extern)))) + (core type (;3;) (func (param (ref exn)))) + (core type (;4;) (func (param (ref noexn)))) + (core type (;5;) (func (param (ref eq)))) + (core type (;6;) (func (param (ref struct)))) + (core type (;7;) (func (param (ref array)))) + (core type (;8;) (func (param (ref nofunc)))) + (core type (;9;) (func (param (ref noextern)))) + (core type (;10;) (func (param (ref none)))) + (core type (;11;) (func (param (ref i31)))) + (core type (;12;) (func (param anyref))) + (core type (;13;) (func (param eqref))) + (core type (;14;) (func (param v128))) + (core type $t (;15;) (func)) + (core type (;16;) (func (param (ref $t)))) +) diff --git a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/0.print b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/0.print index bdb1afc777..01f8bab139 100644 --- a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/0.print +++ b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/0.print @@ -1,3 +1,3 @@ (module - (memory (;0;) 1(pagesize 0x1)) + (memory (;0;) 1 (pagesize 0x1)) ) diff --git a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/1.print b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/1.print index c263f1833f..a86edfbcef 100644 --- a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/1.print +++ b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/1.print @@ -1,3 +1,3 @@ (module - (memory (;0;) 1(pagesize 0x10000)) + (memory (;0;) 1 (pagesize 0x10000)) ) diff --git a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/19.print b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/19.print index 2f377e5a28..9d393fba79 100644 --- a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/19.print +++ b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/19.print @@ -4,6 +4,6 @@ local.get 0 memory.grow ) - (memory (;0;) 0(pagesize 0x10000)) + (memory (;0;) 0 (pagesize 0x10000)) (export "grow" (func 0)) ) diff --git a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/2.print b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/2.print index b28b44796e..fccfed30d0 100644 --- a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/2.print +++ b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/2.print @@ -1,3 +1,3 @@ (module - (memory (;0;) 1 2(pagesize 0x1)) + (memory (;0;) 1 2 (pagesize 0x1)) ) diff --git a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/23.print b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/23.print index 1c3b7326d8..c247b5f533 100644 --- a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/23.print +++ b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/23.print @@ -21,8 +21,8 @@ local.get 0 i32.load8_u ) - (memory $small (;0;) 10(pagesize 0x1)) - (memory $large (;1;) 1(pagesize 0x10000)) + (memory $small (;0;) 10 (pagesize 0x1)) + (memory $large (;1;) 1 (pagesize 0x10000)) (export "copy-small-to-large" (func 0)) (export "copy-large-to-small" (func 1)) (export "load8-small" (func 2)) diff --git a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/3.print b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/3.print index afb7ad5c56..f8915ffd73 100644 --- a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/3.print +++ b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/3.print @@ -1,3 +1,3 @@ (module - (memory (;0;) 1 2(pagesize 0x10000)) + (memory (;0;) 1 2 (pagesize 0x10000)) ) diff --git a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/4.print b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/4.print index 388265c290..71c30c5fdc 100644 --- a/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/4.print +++ b/tests/snapshots/local/custom-page-sizes/custom-page-sizes.wast/4.print @@ -18,7 +18,7 @@ local.get 1 i32.store ) - (memory (;0;) 0(pagesize 0x1)) + (memory (;0;) 0 (pagesize 0x1)) (export "size" (func 0)) (export "grow" (func 1)) (export "load" (func 2)) diff --git a/tests/snapshots/local/duplicate.wast.json b/tests/snapshots/local/duplicate.wast.json index 96a0173dce..40d6bedef2 100644 --- a/tests/snapshots/local/duplicate.wast.json +++ b/tests/snapshots/local/duplicate.wast.json @@ -5,105 +5,105 @@ "type": "assert_malformed", "line": 1, "filename": "duplicate.0.wat", - "text": "duplicate identifier", + "text": "duplicate func identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 5, "filename": "duplicate.1.wat", - "text": "duplicate identifier", + "text": "duplicate func identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 9, "filename": "duplicate.2.wat", - "text": "duplicate identifier", + "text": "duplicate func identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 14, "filename": "duplicate.3.wat", - "text": "duplicate identifier", + "text": "duplicate global identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 18, "filename": "duplicate.4.wat", - "text": "duplicate identifier", + "text": "duplicate global identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 22, "filename": "duplicate.5.wat", - "text": "duplicate identifier", + "text": "duplicate global identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 27, "filename": "duplicate.6.wat", - "text": "duplicate identifier", + "text": "duplicate memory identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 31, "filename": "duplicate.7.wat", - "text": "duplicate identifier", + "text": "duplicate memory identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 35, "filename": "duplicate.8.wat", - "text": "duplicate identifier", + "text": "duplicate memory identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 40, "filename": "duplicate.9.wat", - "text": "duplicate identifier", + "text": "duplicate table identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 44, "filename": "duplicate.10.wat", - "text": "duplicate identifier", + "text": "duplicate table identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 48, "filename": "duplicate.11.wat", - "text": "duplicate identifier", + "text": "duplicate table identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 53, "filename": "duplicate.12.wat", - "text": "duplicate identifier", + "text": "duplicate local identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 55, "filename": "duplicate.13.wat", - "text": "duplicate identifier", + "text": "duplicate local identifier", "module_type": "text" }, { "type": "assert_malformed", "line": 57, "filename": "duplicate.14.wat", - "text": "duplicate identifier", + "text": "duplicate local identifier", "module_type": "text" } ] diff --git a/tests/snapshots/local/exnref/try-table.wast.json b/tests/snapshots/local/exnref/try-table.wast.json new file mode 100644 index 0000000000..79f3b810df --- /dev/null +++ b/tests/snapshots/local/exnref/try-table.wast.json @@ -0,0 +1,12 @@ +{ + "source_filename": "tests/local/exnref/try-table.wast", + "commands": [ + { + "type": "assert_invalid", + "line": 2, + "filename": "try-table.0.wasm", + "text": "type mismatch: catch_all_ref label must a subtype of (ref exn)", + "module_type": "binary" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/local/function-references/return-call.wast.json b/tests/snapshots/local/function-references/return-call.wast.json index 7ea31ec638..6c7d32a464 100644 --- a/tests/snapshots/local/function-references/return-call.wast.json +++ b/tests/snapshots/local/function-references/return-call.wast.json @@ -19,7 +19,7 @@ "type": "assert_invalid", "line": 29, "filename": "return-call.2.wasm", - "text": "type mismatch: current function requires result type [i32] but callee returns [i32 i32]", + "text": "type mismatch: expected (ref null $type), found funcref", "module_type": "binary" } ] diff --git a/tests/snapshots/local/gc/invalid.wast.json b/tests/snapshots/local/gc/invalid.wast.json index 715407e667..1ffe11ce2f 100644 --- a/tests/snapshots/local/gc/invalid.wast.json +++ b/tests/snapshots/local/gc/invalid.wast.json @@ -14,6 +14,184 @@ "filename": "invalid.1.wasm", "text": "type mismatch: expected structref, found anyref", "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 34, + "filename": "invalid.2.wasm", + "text": "expected struct type at index 0, found (func ...)", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 40, + "filename": "invalid.3.wasm", + "text": "unknown field: field index out of bounds", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 46, + "filename": "invalid.4.wasm", + "text": "unknown type: type index out of bounds", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 51, + "filename": "invalid.5.wasm", + "text": "expected array type at index 0, found (struct ...)", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 57, + "filename": "invalid.6.wasm", + "text": "unknown type: type index out of bounds", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 62, + "filename": "invalid.7.wasm", + "text": "type index 0 is not a function type", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 68, + "filename": "invalid.8.wasm", + "text": "expected func type at index 0, found (struct ...)", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 74, + "filename": "invalid.9.wasm", + "text": "type mismatch: br_on_non_null target has no label types", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 85, + "filename": "invalid.10.wasm", + "text": "type mismatch: br_on_non_null target does not end with heap type", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 96, + "filename": "invalid.11.wasm", + "text": "invalid `struct.new_default`: (ref func) field is not defaultable", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 105, + "filename": "invalid.12.wasm", + "text": "cannot use struct.get_u with non-packed storage types", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 115, + "filename": "invalid.13.wasm", + "text": "invalid `array.new_default`: (ref func) field is not defaultable", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 124, + "filename": "invalid.14.wasm", + "text": "array.new_data can only create arrays with numeric and vector elements", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 137, + "filename": "invalid.15.wasm", + "text": "data count section required", + "module_type": "binary" + }, + { + "type": "module", + "line": 167, + "filename": "invalid.16.wasm" + }, + { + "type": "assert_invalid", + "line": 199, + "filename": "invalid.17.wasm", + "text": "unknown data segment 100", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 211, + "filename": "invalid.18.wasm", + "text": "type mismatch: array.new_elem can only create arrays with reference elements", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 224, + "filename": "invalid.19.wasm", + "text": "invalid array.new_elem instruction: element segment 0 type mismatch: expected (ref any), found funcref", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 237, + "filename": "invalid.20.wasm", + "text": "array types do not match: expected i8, found i16", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 248, + "filename": "invalid.21.wasm", + "text": "array types do not match: expected i16, found i8", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 259, + "filename": "invalid.22.wasm", + "text": "array types do not match: expected i32, found i64", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 270, + "filename": "invalid.23.wasm", + "text": "array types do not match: expected i32, found i8", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 281, + "filename": "invalid.24.wasm", + "text": "array types do not match: expected i8, found i32", + "module_type": "binary" + }, + { + "type": "module", + "line": 291, + "filename": "invalid.25.wasm" + }, + { + "type": "assert_invalid", + "line": 301, + "filename": "invalid.26.wasm", + "text": "type mismatch: expected a reference type, found nothing", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 314, + "filename": "invalid.27.wasm", + "text": "indirect calls must go through a table with type <= funcref", + "module_type": "binary" } ] } \ No newline at end of file diff --git a/tests/snapshots/local/gc/invalid.wast/16.print b/tests/snapshots/local/gc/invalid.wast/16.print new file mode 100644 index 0000000000..bea3b7bd00 --- /dev/null +++ b/tests/snapshots/local/gc/invalid.wast/16.print @@ -0,0 +1,11 @@ +(module + (type (;0;) (array i32)) + (type (;1;) (func)) + (func (;0;) (type 1) + i32.const 0 + i32.const 0 + array.new_data 0 0 + drop + ) + (data (;0;) "xxx") +) diff --git a/tests/snapshots/local/gc/invalid.wast/25.print b/tests/snapshots/local/gc/invalid.wast/25.print new file mode 100644 index 0000000000..bfb5305a6a --- /dev/null +++ b/tests/snapshots/local/gc/invalid.wast/25.print @@ -0,0 +1,9 @@ +(module + (type $t1 (;0;) (array (mut i16))) + (type $t2 (;1;) (array (mut i16))) + (type (;2;) (func)) + (func (;0;) (type 2) + unreachable + array.copy $t1 $t2 + ) +) diff --git a/tests/snapshots/local/instance.wast.json b/tests/snapshots/local/instance.wast.json new file mode 100644 index 0000000000..0533572429 --- /dev/null +++ b/tests/snapshots/local/instance.wast.json @@ -0,0 +1,245 @@ +{ + "source_filename": "tests/local/instance.wast", + "commands": [ + { + "type": "module_definition", + "line": 3, + "name": "M", + "filename": "instance.0.wasm" + }, + { + "type": "module_instance", + "line": 10, + "instance": "I1", + "module": "M" + }, + { + "type": "module_instance", + "line": 11, + "instance": "I2", + "module": "M" + }, + { + "type": "register", + "line": 12, + "name": "I1", + "as": "I1" + }, + { + "type": "register", + "line": 13, + "name": "I2", + "as": "I2" + }, + { + "type": "module", + "line": 15, + "filename": "instance.1.wasm" + }, + { + "type": "assert_return", + "line": 54, + "action": { + "type": "invoke", + "field": "glob", + "args": [] + }, + "expected": [ + { + "type": "i32", + "value": "0" + } + ] + }, + { + "type": "assert_return", + "line": 55, + "action": { + "type": "invoke", + "field": "tab", + "args": [] + }, + "expected": [ + { + "type": "refnull" + } + ] + }, + { + "type": "assert_return", + "line": 56, + "action": { + "type": "invoke", + "field": "mem", + "args": [] + }, + "expected": [ + { + "type": "i32", + "value": "0" + } + ] + }, + { + "type": "assert_return", + "line": 57, + "action": { + "type": "invoke", + "field": "tag", + "args": [] + }, + "expected": [ + { + "type": "i32", + "value": "0" + } + ] + }, + { + "type": "module", + "line": 62, + "filename": "instance.2.wasm" + }, + { + "type": "assert_return", + "line": 101, + "action": { + "type": "invoke", + "field": "glob", + "args": [] + }, + "expected": [ + { + "type": "i32", + "value": "1" + } + ] + }, + { + "type": "assert_return", + "line": 102, + "action": { + "type": "invoke", + "field": "tab", + "args": [] + }, + "expected": [ + { + "type": "funcref" + } + ] + }, + { + "type": "assert_return", + "line": 103, + "action": { + "type": "invoke", + "field": "mem", + "args": [] + }, + "expected": [ + { + "type": "i32", + "value": "1" + } + ] + }, + { + "type": "assert_return", + "line": 104, + "action": { + "type": "invoke", + "field": "tag", + "args": [] + }, + "expected": [ + { + "type": "i32", + "value": "1" + } + ] + }, + { + "type": "module_definition", + "line": 109, + "name": "N", + "filename": "instance.3.wasm" + }, + { + "type": "module_instance", + "line": 125, + "instance": "I", + "module": "N" + }, + { + "type": "register", + "line": 126, + "name": "I", + "as": "I" + }, + { + "type": "module", + "line": 128, + "filename": "instance.4.wasm" + }, + { + "type": "assert_return", + "line": 167, + "action": { + "type": "invoke", + "field": "glob", + "args": [] + }, + "expected": [ + { + "type": "i32", + "value": "1" + } + ] + }, + { + "type": "assert_return", + "line": 168, + "action": { + "type": "invoke", + "field": "tab", + "args": [] + }, + "expected": [ + { + "type": "funcref" + } + ] + }, + { + "type": "assert_return", + "line": 169, + "action": { + "type": "invoke", + "field": "mem", + "args": [] + }, + "expected": [ + { + "type": "i32", + "value": "1" + } + ] + }, + { + "type": "assert_return", + "line": 170, + "action": { + "type": "invoke", + "field": "tag", + "args": [] + }, + "expected": [ + { + "type": "i32", + "value": "1" + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/local/instance.wast/0.print b/tests/snapshots/local/instance.wast/0.print new file mode 100644 index 0000000000..5ef681d317 --- /dev/null +++ b/tests/snapshots/local/instance.wast/0.print @@ -0,0 +1,11 @@ +(module $M + (type (;0;) (func)) + (table (;0;) 10 funcref ref.null func) + (memory (;0;) 1) + (tag (;0;) (type 0)) + (global (;0;) (mut i32) i32.const 0) + (export "glob" (global 0)) + (export "tab" (table 0)) + (export "mem" (memory 0)) + (export "tag" (tag 0)) +) diff --git a/tests/snapshots/local/instance.wast/10.print b/tests/snapshots/local/instance.wast/10.print new file mode 100644 index 0000000000..e847b035f5 --- /dev/null +++ b/tests/snapshots/local/instance.wast/10.print @@ -0,0 +1,52 @@ +(module + (type (;0;) (func)) + (type (;1;) (func (result i32))) + (type (;2;) (func (result funcref))) + (import "I1" "glob" (global $glob1 (;0;) (mut i32))) + (import "I1" "glob" (global $glob2 (;1;) (mut i32))) + (import "I1" "tab" (table $tab1 (;0;) 10 funcref)) + (import "I1" "tab" (table $tab2 (;1;) 10 funcref)) + (import "I1" "mem" (memory $mem1 (;0;) 1)) + (import "I1" "mem" (memory $mem2 (;1;) 1)) + (import "I1" "tag" (tag $tag1 (;0;) (type 0))) + (import "I1" "tag" (tag $tag2 (;1;) (type 0))) + (func $f (;0;) (type 0)) + (func (;1;) (type 1) (result i32) + i32.const 1 + global.set $glob1 + global.get $glob2 + ) + (func (;2;) (type 2) (result funcref) + i32.const 0 + ref.func $f + table.set $tab1 + i32.const 0 + table.get $tab2 + ) + (func (;3;) (type 1) (result i32) + i32.const 0 + i32.const 1 + i32.store + i32.const 0 + i32.load $mem2 + ) + (func (;4;) (type 1) (result i32) + block $on_tag1 + block $on_other + try_table (catch $tag1 $on_tag1) (catch_all $on_other) ;; label = @3 + throw $tag2 + end + unreachable + end + i32.const 0 + return + end + i32.const 1 + return + ) + (export "glob" (func 1)) + (export "tab" (func 2)) + (export "mem" (func 3)) + (export "tag" (func 4)) + (elem (;0;) declare func $f) +) diff --git a/tests/snapshots/local/instance.wast/15.print b/tests/snapshots/local/instance.wast/15.print new file mode 100644 index 0000000000..db1c4225d9 --- /dev/null +++ b/tests/snapshots/local/instance.wast/15.print @@ -0,0 +1,15 @@ +(module $N + (type (;0;) (func)) + (table $tab (;0;) 10 funcref ref.null func) + (memory $mem (;0;) 1) + (tag $tag (;0;) (type 0)) + (global $glob (;0;) (mut i32) i32.const 0) + (export "glob1" (global $glob)) + (export "glob2" (global $glob)) + (export "tab1" (table $tab)) + (export "tab2" (table $tab)) + (export "mem1" (memory $mem)) + (export "mem2" (memory $mem)) + (export "tag1" (tag 0)) + (export "tag2" (tag 0)) +) diff --git a/tests/snapshots/local/instance.wast/18.print b/tests/snapshots/local/instance.wast/18.print new file mode 100644 index 0000000000..7b9f2a497c --- /dev/null +++ b/tests/snapshots/local/instance.wast/18.print @@ -0,0 +1,52 @@ +(module + (type (;0;) (func)) + (type (;1;) (func (result i32))) + (type (;2;) (func (result funcref))) + (import "I" "glob1" (global $glob1 (;0;) (mut i32))) + (import "I" "glob2" (global $glob2 (;1;) (mut i32))) + (import "I" "tab1" (table $tab1 (;0;) 10 funcref)) + (import "I" "tab2" (table $tab2 (;1;) 10 funcref)) + (import "I" "mem1" (memory $mem1 (;0;) 1)) + (import "I" "mem2" (memory $mem2 (;1;) 1)) + (import "I" "tag1" (tag $tag1 (;0;) (type 0))) + (import "I" "tag2" (tag $tag2 (;1;) (type 0))) + (func $f (;0;) (type 0)) + (func (;1;) (type 1) (result i32) + i32.const 1 + global.set $glob1 + global.get $glob2 + ) + (func (;2;) (type 2) (result funcref) + i32.const 0 + ref.func $f + table.set $tab1 + i32.const 0 + table.get $tab2 + ) + (func (;3;) (type 1) (result i32) + i32.const 0 + i32.const 1 + i32.store + i32.const 0 + i32.load $mem2 + ) + (func (;4;) (type 1) (result i32) + block $on_tag1 + block $on_other + try_table (catch $tag1 $on_tag1) (catch_all $on_other) ;; label = @3 + throw $tag2 + end + unreachable + end + i32.const 0 + return + end + i32.const 1 + return + ) + (export "glob" (func 1)) + (export "tab" (func 2)) + (export "mem" (func 3)) + (export "tag" (func 4)) + (elem (;0;) declare func $f) +) diff --git a/tests/snapshots/local/instance.wast/5.print b/tests/snapshots/local/instance.wast/5.print new file mode 100644 index 0000000000..fe723dcfab --- /dev/null +++ b/tests/snapshots/local/instance.wast/5.print @@ -0,0 +1,52 @@ +(module + (type (;0;) (func)) + (type (;1;) (func (result i32))) + (type (;2;) (func (result funcref))) + (import "I1" "glob" (global $glob1 (;0;) (mut i32))) + (import "I2" "glob" (global $glob2 (;1;) (mut i32))) + (import "I1" "tab" (table $tab1 (;0;) 10 funcref)) + (import "I2" "tab" (table $tab2 (;1;) 10 funcref)) + (import "I1" "mem" (memory $mem1 (;0;) 1)) + (import "I2" "mem" (memory $mem2 (;1;) 1)) + (import "I1" "tag" (tag $tag1 (;0;) (type 0))) + (import "I2" "tag" (tag $tag2 (;1;) (type 0))) + (func $f (;0;) (type 0)) + (func (;1;) (type 1) (result i32) + i32.const 1 + global.set $glob1 + global.get $glob2 + ) + (func (;2;) (type 2) (result funcref) + i32.const 0 + ref.func $f + table.set $tab1 + i32.const 0 + table.get $tab2 + ) + (func (;3;) (type 1) (result i32) + i32.const 0 + i32.const 1 + i32.store + i32.const 0 + i32.load $mem2 + ) + (func (;4;) (type 1) (result i32) + block $on_tag1 + block $on_other + try_table (catch $tag1 $on_tag1) (catch_all $on_other) ;; label = @3 + throw $tag2 + end + unreachable + end + i32.const 0 + return + end + i32.const 1 + return + ) + (export "glob" (func 1)) + (export "tab" (func 2)) + (export "mem" (func 3)) + (export "tag" (func 4)) + (elem (;0;) declare func $f) +) diff --git a/tests/snapshots/local/invalid.wast.json b/tests/snapshots/local/invalid.wast.json new file mode 100644 index 0000000000..8f286c330d --- /dev/null +++ b/tests/snapshots/local/invalid.wast.json @@ -0,0 +1,19 @@ +{ + "source_filename": "tests/local/invalid.wast", + "commands": [ + { + "type": "assert_invalid", + "line": 2, + "filename": "invalid.0.wasm", + "text": "unknown elem segment", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 8, + "filename": "invalid.1.wasm", + "text": "else found outside of an `if` block", + "module_type": "binary" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/local/legacy-exceptions/throw.wast.json b/tests/snapshots/local/legacy-exceptions/throw.wast.json index bbffce788a..19bd9185bf 100644 --- a/tests/snapshots/local/legacy-exceptions/throw.wast.json +++ b/tests/snapshots/local/legacy-exceptions/throw.wast.json @@ -117,14 +117,14 @@ "type": "assert_invalid", "line": 53, "filename": "throw.2.wasm", - "text": "type mismatch: instruction requires [i32] but stack has []", + "text": "type mismatch", "module_type": "binary" }, { "type": "assert_invalid", "line": 55, "filename": "throw.3.wasm", - "text": "type mismatch: instruction requires [i32] but stack has [i64]", + "text": "type mismatch", "module_type": "binary" } ] diff --git a/tests/snapshots/local/legacy-exceptions/try_catch.wast.json b/tests/snapshots/local/legacy-exceptions/try_catch.wast.json index 8b256d189b..eafa83c339 100644 --- a/tests/snapshots/local/legacy-exceptions/try_catch.wast.json +++ b/tests/snapshots/local/legacy-exceptions/try_catch.wast.json @@ -564,35 +564,35 @@ "type": "assert_invalid", "line": 297, "filename": "try_catch.3.wasm", - "text": "type mismatch: instruction requires [i32] but stack has []", + "text": "type mismatch", "module_type": "binary" }, { "type": "assert_invalid", "line": 299, "filename": "try_catch.4.wasm", - "text": "type mismatch: instruction requires [i32] but stack has [i64]", + "text": "type mismatch", "module_type": "binary" }, { "type": "assert_invalid", "line": 301, "filename": "try_catch.5.wasm", - "text": "type mismatch: block requires [] but stack has [i32]", + "text": "type mismatch", "module_type": "binary" }, { "type": "assert_invalid", "line": 303, "filename": "try_catch.6.wasm", - "text": "type mismatch: instruction requires [i32] but stack has [i64]", + "text": "type mismatch", "module_type": "binary" }, { "type": "assert_invalid", "line": 308, "filename": "try_catch.7.wasm", - "text": "type mismatch: block requires [] but stack has [i32]", + "text": "type mismatch", "module_type": "binary" } ] diff --git a/tests/snapshots/local/missing-features/component-model/types.wast.json b/tests/snapshots/local/missing-features/component-model/types.wast.json new file mode 100644 index 0000000000..876d023287 --- /dev/null +++ b/tests/snapshots/local/missing-features/component-model/types.wast.json @@ -0,0 +1,26 @@ +{ + "source_filename": "tests/local/missing-features/component-model/types.wast", + "commands": [ + { + "type": "assert_invalid", + "line": 2, + "filename": "types.0.wasm", + "text": "SIMD support is not enabled", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 8, + "filename": "types.1.wasm", + "text": "unknown type 0: type index out of bounds", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 14, + "filename": "types.2.wasm", + "text": "reference types support is not enabled", + "module_type": "binary" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/local/missing-features/gc/shared-any.wast.json b/tests/snapshots/local/missing-features/gc/shared-any.wast.json new file mode 100644 index 0000000000..c4f2700273 --- /dev/null +++ b/tests/snapshots/local/missing-features/gc/shared-any.wast.json @@ -0,0 +1,12 @@ +{ + "source_filename": "tests/local/missing-features/gc/shared-any.wast", + "commands": [ + { + "type": "assert_invalid", + "line": 2, + "filename": "shared-any.0.wasm", + "text": "shared reference types require the shared-everything-threads proposal", + "module_type": "binary" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/local/shared-everything-threads/arrays.wast.json b/tests/snapshots/local/shared-everything-threads/arrays.wast.json index fb61bec7ac..b4c2426fbd 100644 --- a/tests/snapshots/local/shared-everything-threads/arrays.wast.json +++ b/tests/snapshots/local/shared-everything-threads/arrays.wast.json @@ -487,6 +487,34 @@ "filename": "arrays.86.wasm", "text": "invalid type", "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 854, + "filename": "arrays.87.wasm", + "text": "invalid type: `array.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 865, + "filename": "arrays.88.wasm", + "text": "invalid type: `array.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 876, + "filename": "arrays.89.wasm", + "text": "cannot use array.get with packed storage types", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 887, + "filename": "arrays.90.wasm", + "text": "invalid type: `array.atomic.set` only allows `i8`, `i16`, `i32`, `i64` and subtypes of `anyref`", + "module_type": "binary" } ] } \ No newline at end of file diff --git a/tests/snapshots/local/shared-everything-threads/globals.wast.json b/tests/snapshots/local/shared-everything-threads/globals.wast.json index d28c04dff0..65879d88d1 100644 --- a/tests/snapshots/local/shared-everything-threads/globals.wast.json +++ b/tests/snapshots/local/shared-everything-threads/globals.wast.json @@ -167,6 +167,44 @@ "type": "module", "line": 277, "filename": "globals.24.wasm" + }, + { + "type": "assert_invalid", + "line": 371, + "filename": "globals.25.wasm", + "text": "invalid type: `global.atomic.rmw.*` only allows `i32` and `i64`", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 382, + "filename": "globals.26.wasm", + "text": "global index out of bounds", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 390, + "filename": "globals.27.wasm", + "text": "invalid type: `global.atomic.rmw.xchg` only allows `i32`, `i64` and subtypes of `anyref`", + "module_type": "binary" + }, + { + "type": "module", + "line": 400, + "filename": "globals.28.wasm" + }, + { + "type": "assert_invalid", + "line": 410, + "filename": "globals.29.wasm", + "text": "invalid type: `global.atomic.rmw.cmpxchg` only allows `i32`, `i64` and subtypes of `eqref`", + "module_type": "binary" + }, + { + "type": "module", + "line": 421, + "filename": "globals.30.wasm" } ] } \ No newline at end of file diff --git a/tests/snapshots/local/shared-everything-threads/globals.wast/28.print b/tests/snapshots/local/shared-everything-threads/globals.wast/28.print new file mode 100644 index 0000000000..6cd361a78a --- /dev/null +++ b/tests/snapshots/local/shared-everything-threads/globals.wast/28.print @@ -0,0 +1,9 @@ +(module + (type (;0;) (func)) + (func (;0;) (type 0) + ref.null eq + global.atomic.rmw.xchg acq_rel $g + drop + ) + (global $g (;0;) (mut eqref) ref.null eq) +) diff --git a/tests/snapshots/local/shared-everything-threads/globals.wast/30.print b/tests/snapshots/local/shared-everything-threads/globals.wast/30.print new file mode 100644 index 0000000000..c97befa30b --- /dev/null +++ b/tests/snapshots/local/shared-everything-threads/globals.wast/30.print @@ -0,0 +1,10 @@ +(module + (type (;0;) (func)) + (func (;0;) (type 0) + ref.null eq + ref.null eq + global.atomic.rmw.cmpxchg acq_rel $g + drop + ) + (global $g (;0;) (mut eqref) ref.null eq) +) diff --git a/tests/snapshots/local/shared-everything-threads/structs.wast.json b/tests/snapshots/local/shared-everything-threads/structs.wast.json index 0a68ac7d6d..be1a6db1a9 100644 --- a/tests/snapshots/local/shared-everything-threads/structs.wast.json +++ b/tests/snapshots/local/shared-everything-threads/structs.wast.json @@ -212,6 +212,34 @@ "filename": "structs.31.wasm", "text": "invalid type", "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 532, + "filename": "structs.32.wasm", + "text": "invalid type: `struct.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 543, + "filename": "structs.33.wasm", + "text": "invalid type: `struct.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 554, + "filename": "structs.34.wasm", + "text": "can only use struct `get` with non-packed storage types", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 565, + "filename": "structs.35.wasm", + "text": "invalid type: `struct.atomic.set` only allows `i8`, `i16`, `i32`, `i64` and subtypes of `anyref`", + "module_type": "binary" } ] } \ No newline at end of file diff --git a/tests/snapshots/local/shared-everything-threads/tables.wast.json b/tests/snapshots/local/shared-everything-threads/tables.wast.json index 14f91dacca..595de4263c 100644 --- a/tests/snapshots/local/shared-everything-threads/tables.wast.json +++ b/tests/snapshots/local/shared-everything-threads/tables.wast.json @@ -29,7 +29,7 @@ "type": "assert_malformed", "line": 32, "filename": "tables.4.wat", - "text": "unexpected token", + "text": "expected a u64", "module_type": "text" }, { @@ -62,6 +62,27 @@ "filename": "tables.9.wasm", "text": "invalid type", "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 177, + "filename": "tables.10.wasm", + "text": "invalid type: `table.atomic.get` only allows subtypes of `anyref`", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 187, + "filename": "tables.11.wasm", + "text": "invalid type: `table.atomic.set` only allows subtypes of `anyref`", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 198, + "filename": "tables.12.wasm", + "text": "invalid type: `table.atomic.rmw.xchg` only allows subtypes of `anyref`", + "module_type": "binary" } ] } \ No newline at end of file