From 992bc5199866ebb267ef44d49d86f598733b3f5a Mon Sep 17 00:00:00 2001 From: Yi LIU Date: Thu, 12 Feb 2026 19:53:54 +0800 Subject: [PATCH] fix: merge TypeInfo for structurally equal types in bindgen When `collect_equal_types` unions structurally identical types from different interfaces, the TypeInfo (borrowed/owned/error flags) was not being merged. This caused incorrect code generation when a type was imported in one interface and exported in another - only one side's flags would be visible, leading to missing borrowed or owned type variants. Add a post-pass in `collect_equal_types` that merges TypeInfo across all members of each equivalence class, so the representative carries the union of all usage flags. --- crates/core/src/types.rs | 15 +++++++++++ crates/rust/tests/codegen.rs | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs index 3927835af..9769d35ce 100644 --- a/crates/core/src/types.rs +++ b/crates/core/src/types.rs @@ -109,6 +109,21 @@ impl Types { } } } + + // Merge TypeInfo for structurally equal types so that all members + // of an equivalence class carry the union of all usage flags + // (borrowed, owned, etc.). + let mut merged: HashMap = HashMap::new(); + for (&id, &info) in &self.type_info { + let rep = self.equal_types.find(id); + *merged.entry(rep).or_default() |= info; + } + for (&id, info) in &mut self.type_info { + let rep = self.equal_types.find(id); + if let Some(&merged_info) = merged.get(&rep) { + *info = merged_info; + } + } } fn type_info_func(&mut self, resolve: &Resolve, func: &Function, import: bool) { diff --git a/crates/rust/tests/codegen.rs b/crates/rust/tests/codegen.rs index 44ddf31d8..322f04fbc 100644 --- a/crates/rust/tests/codegen.rs +++ b/crates/rust/tests/codegen.rs @@ -142,6 +142,54 @@ mod newtyped_list { } } } + +#[test] +fn merge_structurally_equal_types() { + let wit = r#" + package test:merge-equal; + + interface importer { + record payload { + data: list, + tag: string, + } + consume: func(p: payload); + } + + interface exporter { + record payload { + data: list, + tag: string, + } + produce: func() -> payload; + } + + world test { + import importer; + export exporter; + } + "#; + + let mut resolve = wit_bindgen_core::wit_parser::Resolve::default(); + let pkg = resolve.push_str("test.wit", wit).unwrap(); + let world = resolve.select_world(&[pkg], None).unwrap(); + + let opts = wit_bindgen_rust::Opts { + generate_all: true, + merge_structurally_equal_types: Some(Some(true)), + ownership: wit_bindgen_rust::Ownership::Borrowing { + duplicate_if_necessary: true, + }, + ..Default::default() + }; + + let mut generator = opts.build(); + let mut files = wit_bindgen_core::Files::default(); + // Should not panic and should generate valid code with merged types + use wit_bindgen_core::WorldGenerator; + generator.generate(&mut resolve, world, &mut files).unwrap(); +} + #[allow(unused, reason = "testing codegen, not functionality")] mod retyped_list { use std::ops::Deref;