Skip to content

Commit 1548953

Browse files
authored
Handle duplicate imports of the same item. (#1942)
The wasm-bindgen nightly test suite started failing recently and a bisection shows that rust-lang/rust#67363 is the cause of this issue. The problem happening here is that after that Rust PR duplicate imports from the same name/module in different parts of a Rust program may now show up as duplicate imports rather than being coalesced into one import. This was tripping up `wasm-bindgen` which, when translating from the wasm module to wasm-bindgen's IR, is unfortunately very string-based. The fix here was to detect these duplicate imports and map them all to the same item, removing the duplicate imports. Closes #1929
1 parent 91aaf88 commit 1548953

File tree

1 file changed

+64
-6
lines changed
  • crates/cli-support/src/wit

1 file changed

+64
-6
lines changed

crates/cli-support/src/wit/mod.rs

+64-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::descriptor::{Descriptor, Function};
33
use crate::descriptors::WasmBindgenDescriptorsSection;
44
use crate::intrinsic::Intrinsic;
55
use anyhow::{anyhow, bail, Error};
6-
use std::collections::HashMap;
6+
use std::collections::{HashMap, HashSet};
77
use std::str;
88
use walrus::MemoryId;
99
use walrus::{ExportId, FunctionId, ImportId, Module};
@@ -107,21 +107,47 @@ impl<'a> Context<'a> {
107107
// placeholder module name which we'll want to be sure that we've got a
108108
// location listed of what to import there for each item.
109109
let mut intrinsics = Vec::new();
110+
let mut duplicate_import_map = HashMap::new();
111+
let mut imports_to_delete = HashSet::new();
110112
for import in self.module.imports.iter() {
111113
if import.module != PLACEHOLDER_MODULE {
112114
continue;
113115
}
114-
if let walrus::ImportKind::Function(f) = import.kind {
115-
self.function_imports
116-
.insert(import.name.clone(), (import.id(), f));
117-
if let Some(intrinsic) = Intrinsic::from_symbol(&import.name) {
118-
intrinsics.push((import.id(), intrinsic));
116+
let f = match import.kind {
117+
walrus::ImportKind::Function(f) => f,
118+
_ => continue,
119+
};
120+
121+
match self.function_imports.get(&import.name) {
122+
// If this `import.name` already exists in our import map, then
123+
// we need to delete `import`. We also need to replace any
124+
// references to it with `prev_func`, so register that here to
125+
// happen later.
126+
Some((_, prev_func)) => {
127+
imports_to_delete.insert(import.id());
128+
duplicate_import_map.insert(f, *prev_func);
129+
}
130+
131+
// Otherwise this is brand new, so insert it into the map.
132+
None => {
133+
self.function_imports
134+
.insert(import.name.clone(), (import.id(), f));
119135
}
120136
}
137+
138+
// Test to see if this is an intrinsic symbol, in which case we'll
139+
// process this later.
140+
if let Some(intrinsic) = Intrinsic::from_symbol(&import.name) {
141+
intrinsics.push((import.id(), intrinsic));
142+
}
121143
}
122144
for (id, intrinsic) in intrinsics {
123145
self.bind_intrinsic(id, intrinsic)?;
124146
}
147+
for import in imports_to_delete {
148+
self.module.imports.delete(import);
149+
}
150+
self.handle_duplicate_imports(&duplicate_import_map);
125151

126152
self.inject_anyref_initialization()?;
127153

@@ -182,6 +208,38 @@ impl<'a> Context<'a> {
182208
Ok(())
183209
}
184210

211+
/// The same name function from the same module may be imported at different
212+
/// points in a program. The compiler may synthesize two `import`
213+
/// statements, both with the same module/name, to match these two function
214+
/// imports. This is handled here.
215+
///
216+
/// Almost all of our handling of directives and such is string-based (eew)
217+
/// instead of ID based due to the way the macro works right now. This means
218+
/// that we don't work well with these duplicate imports. As a result when
219+
/// we see these duplicate imports we fixup the module to ensure that only
220+
/// one import is used, deleting all the other imports. This is what's
221+
/// wanted anyway in terms of semantics.
222+
///
223+
/// The map provided here is a map where the key is a function id to replace
224+
/// and the value is what to replace it with.
225+
fn handle_duplicate_imports(&mut self, map: &HashMap<FunctionId, FunctionId>) {
226+
struct Replace<'a> {
227+
map: &'a HashMap<FunctionId, FunctionId>,
228+
}
229+
impl walrus::ir::VisitorMut for Replace<'_> {
230+
fn visit_function_id_mut(&mut self, function: &mut FunctionId) {
231+
if let Some(replacement) = self.map.get(function) {
232+
*function = *replacement;
233+
}
234+
}
235+
}
236+
let mut replace = Replace { map };
237+
for (_id, func) in self.module.funcs.iter_local_mut() {
238+
let entry = func.entry_block();
239+
walrus::ir::dfs_pre_order_mut(&mut replace, func, entry);
240+
}
241+
}
242+
185243
// Discover a function `main(i32, i32) -> i32` and, if it exists, make that function run at module start.
186244
fn discover_main(&mut self) -> Result<(), Error> {
187245
// find a `main(i32, i32) -> i32`

0 commit comments

Comments
 (0)