Skip to content

wasmparser: Add Error::missing_wasm_feature - #2549

Merged
alexcrichton merged 3 commits into
bytecodealliance:mainfrom
lann:features-errors
Jun 30, 2026
Merged

alexcrichton merged 3 commits into
bytecodealliance:mainfrom
lann:features-errors

Conversation

@lann

@lann lann commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Add generated require_feature::* methods that return Errors with a new ErrorKind::WasmFeature if the named feature is disabled. Replace most feature checks with calls to these methods.

Checks in BinaryReader (and ancillary code) are gated on the 'features' feature to preserve existing behavior.

Additional internal changes:

  • Made Error fields private by adding a couple of methods
  • Renamed ErrorKind variants for clarity

@lann
lann requested a review from a team as a code owner June 29, 2026 22:48
@lann
lann requested review from alexcrichton and removed request for a team June 29, 2026 22:48
Comment on lines +98 to +99
#[cfg(feature = "features")]
crate::require_feature::compact_imports(

@lann lann Jun 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing generated BinaryReader::<feature>() methods always return true (not $default) when features is disabled which was already effectively disabling these checks.

Comment on lines -96 to +101
if rec_group.is_explicit_rec_group() && !self.features().gc() {
bail!(
if rec_group.is_explicit_rec_group() {
require_feature::gc(
*self.features(),
"rec group usage requires `gc` proposal to be enabled",
offset,
"rec group usage requires `gc` proposal to be enabled"
);
)?;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This vast majority of the changes in this PR look basically like this. I'll try to annotate the "weirdest" other ones.

Comment on lines -1548 to 1556
if !self.features.cm_threading() && immediate > 0 {
bail!(offset, "`{operation}` immediate must be zero: {immediate}")
} else if immediate > 1 {
bail!(
if immediate > 0 {
require_feature::cm_threading(
self.features,
format_args!("`{operation}` immediate must be zero: {immediate}"),
offset,
"`{operation}` immediate must be zero or one: {immediate}"
)
)?;
if immediate > 1 {
bail!(
offset,
"`{operation}` immediate must be zero or one: {immediate}"
)
}
}

@lann lann Jun 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It took a distressingly long time to convince myself this was equivalent. 😅

Comment on lines +94 to +106
/// Returns `Some` if this error is due to a disabled Wasm feature.
///
/// If the `features` crate feature is enabled, a returned [`WasmFeatures`]
/// will contain a feature that could be enabled to prevent this error.
///
/// **Note:** This is not comprehensive; `None` may be returned in some
/// cases where enabling a feature could avoid the error.
pub fn missing_wasm_feature(&self) -> Option<WasmFeatures> {
match self.inner.kind {
ErrorKind::WasmFeature(features) => Some(features),
_ => None,
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't clear to me what this should do when features is disabled. Currently it just returns the useless Some(WasmFeatures{ _priv: () }) which at least signals that some feature is missing. Alternatively it could unconditionally return None or the entire method could be gated. 🤷

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I prefer to avoid #[cfg] where possible so I think this is fine. There's nothing that can be done with the return value if this is returned in a feature-less build and that'd basically just mean "some off-by-default thing was used".

Comment on lines -97 to +123
pub(crate) fn set_message(&mut self, message: &str) {
self.inner.message = message.to_string();
pub(crate) fn set_message(&mut self, message: impl Into<String>) {
self.inner.message = message.into();
}

pub(crate) fn needed_hint(&self) -> Option<usize> {
self.inner.needed_hint
}

pub(crate) fn without_needed_hint(mut self) -> Self {
self.inner.needed_hint = None;
self

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a tangent that allows the Error fields to become private.

}
pub(crate) use foreach_wasm_feature;

#[allow(dead_code)]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three unused methods are multi_memory, cm_nested_names, and call_indirect_overlong. This does mean that those features are never reflected in missing_wasm_feature as noted in its doc comment. That could be fixed but they're all non-trivial (at least to me).

@lann
lann force-pushed the features-errors branch 2 times, most recently from c11d735 to c17e9ba Compare June 30, 2026 14:08

@alexcrichton alexcrichton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this!

Comment on lines +94 to +106
/// Returns `Some` if this error is due to a disabled Wasm feature.
///
/// If the `features` crate feature is enabled, a returned [`WasmFeatures`]
/// will contain a feature that could be enabled to prevent this error.
///
/// **Note:** This is not comprehensive; `None` may be returned in some
/// cases where enabling a feature could avoid the error.
pub fn missing_wasm_feature(&self) -> Option<WasmFeatures> {
match self.inner.kind {
ErrorKind::WasmFeature(features) => Some(features),
_ => None,
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I prefer to avoid #[cfg] where possible so I think this is fine. There's nothing that can be done with the return value if this is returned in a feature-less build and that'd basically just mean "some off-by-default thing was used".

#[doc = "Returns an error if [`WasmFeatures::"]
#[doc = stringify!($const)]
#[doc = "`] is not enabled in `features`."]
pub fn $field(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some mild bikeshedding on this:

  • This'll want to use #[inline] as otherwise it's not a candidate for inlining.
  • The msg argument is one where the ideal here is that it's very low-overhead when not used and the processing on the error path ideally happens in a "cold" function somewhere else to avoid cluttering up hot functions. For example Error::fmt is itself #[cold] so the to_string happens out-of-the-hot-function which can help reduce function sizes.

Could there be a new Error::new_display function (probably crate-local to start) which is #[cold] to avoid the format! here? Or perhaps Error::wasm_feature which takes impl Display and is #[cold]?

@lann lann Jun 30, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had Error::wasm_feature and mindlessly inlined it. 😅

Brought that back, added #[inline] here.

Comment thread crates/wasmparser/src/validator/operators.rs Outdated
Making these a little more precise before adding a new one:

Custom -> Uncategorized
Invalid -> InvalidHeapType
@lann
lann force-pushed the features-errors branch from c17e9ba to 1d10a75 Compare June 30, 2026 18:21
Add generated require_feature::* methods that return Errors with a new
ErrorKind::WasmFeature if the named feature is disabled. Replace feature
checks with calls to these methods.

Checks in BinaryReader (and ancillary code) are gated on the 'features'
feature to preserve existing behavior.
@lann
lann force-pushed the features-errors branch from 1d10a75 to 80b3b8d Compare June 30, 2026 18:24
@alexcrichton
alexcrichton added this pull request to the merge queue Jun 30, 2026
Merged via the queue into bytecodealliance:main with commit c99f362 Jun 30, 2026
37 checks passed
@lann
lann deleted the features-errors branch June 30, 2026 19:48
social4hyq pushed a commit to social4hyq/homebrew-core that referenced this pull request Sep 20, 2026
wasm-tools 1.253.0

Created-by: HarmonybrewBot
Commit-by: HarmonybrewBot
Merged-by: HarmonybrewBot
Description: Created by `brew bump`

---

Created with `brew bump-formula-pr`.<details>
  <summary>release notes</summary>
  <pre>## What's Changed
* Limit the element count of fixed-length lists by @eyupcanakman in bytecodealliance/wasm-tools#2537
* wasmparser: Rename BinaryReaderError to Error by @lann in bytecodealliance/wasm-tools#2546
* fix: signature lowering for strings and lists on modules with 64bit memory by @christoph-dfinity in bytecodealliance/wasm-tools#2544
* json-from-wast: name emitted files after the output stem by @eyupcanakman in bytecodealliance/wasm-tools#2542
* Refactor `KebabStr` to not require unsafe code by @alexcrichton in bytecodealliance/wasm-tools#2548
* Add CustomSectionReader -> BinaryReader conversion by @WorldSEnder in bytecodealliance/wasm-tools#2550
* wasmparser: Add Error::missing_wasm_feature by @lann in bytecodealliance/wasm-tools#2549
* fix: use ptr-sized type when returning via ptr indirection under memory64 by @christoph-dfinity in bytecodealliance/wasm-tools#2553
* Synchronize threading intrinsics with specification by @alexcrichton in bytecodealliance/wasm-tools#2554
* Remove `resource.drop async` by @alexcrichton in bytecodealliance/wasm-tools#2552
* Initial work for `versionsuffix` and `external-id` by @alexcrichton in bytecodealliance/wasm-tools#2555
* Make parsing errors downcastable by @PhoebeSzmucer in bytecodealliance/wasm-tools#2557
* Add `SourceMap::resolve_span` for resolving global span locations by @PhoebeSzmucer in bytecodealliance/wasm-tools#2559
* Render error by @PhoebeSzmucer in bytecodealliance/wasm-tools#2560
* Support `external-id` on more items by @alexcrichton in bytecodealliance/wasm-tools#2558
* Release wasm-tools 1.253.0 by @github-actions[bot] in bytecodealliance/wasm-tools#2561

## New Contributors
* @christoph-dfinity made their first contribution in bytecodealliance/wasm-tools#2544

**Full Changelog**: https://github.com/bytecodealliance/wasm-tools/compare/v1.252.0...v1.253.0</pre>
  <p>View the full release notes at <a href="https://github.com/bytecodealliance/wasm-tools/releases/tag/v1.253.0">https://github.com/bytecodealliance/wasm-tools/releases/tag/v1.253.0</a>.</p>
</details>
<hr>

See merge request: Harmonybrew/homebrew-core!13746
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants