feat(rust): qualify named types in future/stream payload vtables#1601
Open
mattwilkinsonn wants to merge 3 commits intobytecodealliance:mainfrom
Open
feat(rust): qualify named types in future/stream payload vtables#1601mattwilkinsonn wants to merge 3 commits intobytecodealliance:mainfrom
mattwilkinsonn wants to merge 3 commits intobytecodealliance:mainfrom
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #1598.
future<result<R, _>>whereRis brought in viause t.{r}at world level generates Rust code that referencesRunqualified inside the emittedwit_future::vtableNmodule, producingerror[E0425]: cannot find type R in this scope. Same shape as #1433 (closed by #1528), but the future-of-result path wasn't covered by that fix —#1528canonicalized the outer payload type but not types nested inside composite structures.Reproducer
Minimal WIT mirroring the bug report in #1598:
Before this PR (all seven codegen-test modes fail):
error[E0425]: cannot find type R in this scope. After this PR: all seven pass.Root cause
generate_payloadincrates/rust/src/interface.rsemits code like:_rtis qualified withsuper::super::becausepath_to_root()has anIdentifier::StreamOrFuturePayloadarm that emits it. Buttype_path_with_nameonly emits the owner-qualified path when the type'sTypeOwnerisInterface(_). A world-leveluse t.{r}alias hasTypeOwner::World(_), so the qualifier is skipped and the bareRis emitted.Rlives at macro-root via the generatedpub type R = ...;— two modules up from the vtable — but there's no correspondinguse super::super::R;in the vtable module.#1528canonicalized the outer payload type so e.g.stream<r>correctly resolves through dealiasing. Forfuture<result<r, string>>the outerresultgets canonicalized but nothing dives into itsokslot to resolver.Fix
Walk the payload's type tree up-front and emit a
use <qualified path>;statement at the top of each generatedvtable{ordinal}module for every named type the payload transitively references. The printer's existing bare-identifier emission then resolves naturally inside the vtable scope without any behavioral change totype_path_with_nameorprint_tyid.Implementation:
collect_named_type_idsthat recursively walksType/TypeDefKind, collecting theTypeIdof every named type it finds. It stops at bare named aliases (TypeDefKind::Type(_)with a name) so we don't import bothuse t.{r};(the alias) andrin interfacet(its target) under the same Rust identifier — doing both would tripE0252.generate_payload, call the helper, compute each type's path viaself.type_path(id, true), prependsuper::super::when the emitted path is a bare name (the aliased-from-world case thattype_path_with_nameleaves unqualified), and format ause ...;statement for each.{type_imports}block inside thepub mod vtable{ordinal} { ... }template. Addedunused_importsto the module's existing#[allow]in case a payload happens to only reference types already in scope.Benefits over fixing the printer:
type_path_with_name/print_tyid— those remain exactly as they are, keeping the PR's blast radius contained to payload emission.future<tuple<r, s>>,stream<variant { foo(r), bar(s) }>,future<result<_, r>>, nested records, etc.) — the walker doesn't care about shape, just follows innerTypes.use X as Yrenames for free:self.type_pathreturns the locally-visible name, so the emittedusealiases to whatever the printer actually emits.Test
tests/codegen/issue-1598.witmirrors the reproducer in #1598. Run via the existing codegen harness — all seven test modes pass on this branch. Verified the test fails onmainpre-fix in all seven modes (cannot find type R in this scope), so it acts as a real regression guard. The existingtests/codegen/issue-1432.wit,issue-1433.wit, andfuture-same-type-different-names.witcontinue to pass (no regression of#1528's work).Clippy (
cargo clippy --workspace --all-targets -- -D warnings) and rustfmt (cargo fmt -- --check) both clean.Downstream
My WIP project (not public yet) hit this bug when using
future<result<turn-result, string>>in an async export withuse types.{turn-result}at world level. With this patch the project builds cleanly against 0.57.1 + patchedwit-bindgen-rust.