diff --git a/crates/wit-parser/src/resolve/mod.rs b/crates/wit-parser/src/resolve/mod.rs
index 12900fe4d0..fea6a2218a 100644
--- a/crates/wit-parser/src/resolve/mod.rs
+++ b/crates/wit-parser/src/resolve/mod.rs
@@ -537,9 +537,43 @@ package {name} is defined in two different locations:\n\
let mut moved_interfaces = Vec::new();
for (id, mut iface) in interfaces {
let new_id = match interface_map.get(&id).copied() {
- Some(id) => {
- update_stability(&iface.stability, &mut self.interfaces[id].stability)?;
- id
+ Some(into_id) => {
+ update_stability(&iface.stability, &mut self.interfaces[into_id].stability)?;
+
+ // Add any extra types from `from`'s interface that
+ // don't exist in `into`'s interface. These types were
+ // already moved as new types above (since they weren't
+ // in `type_map`), but they still need to be registered
+ // in the target interface's `types` map.
+ for (name, from_type_id) in iface.types.iter() {
+ if self.interfaces[into_id].types.contains_key(name) {
+ continue;
+ }
+ let new_type_id = remap.map_type(*from_type_id, Default::default())?;
+ self.interfaces[into_id]
+ .types
+ .insert(name.clone(), new_type_id);
+ }
+
+ // Add any extra functions from `from`'s interface that
+ // don't exist in `into`'s interface. These need their
+ // type references remapped and spans adjusted.
+ let extra_funcs: Vec<_> = iface
+ .functions
+ .into_iter()
+ .filter(|(name, _)| {
+ !self.interfaces[into_id]
+ .functions
+ .contains_key(name.as_str())
+ })
+ .collect();
+ for (name, mut func) in extra_funcs {
+ remap.update_function(self, &mut func, Default::default())?;
+ func.adjust_spans(span_offset);
+ self.interfaces[into_id].functions.insert(name, func);
+ }
+
+ into_id
}
None => {
log::debug!("moving interface {:?}", iface.name);
@@ -676,6 +710,11 @@ package {name} is defined in two different locations:\n\
log::trace!("now have {} packages", self.packages.len());
+ // Ensure the interfaces arena is in topological order after the
+ // merge. Newly-added interfaces may have been appended after
+ // existing interfaces that now depend on them.
+ self.topologically_sort_interfaces(Some(&mut remap));
+
#[cfg(debug_assertions)]
self.assert_valid();
Ok(remap)
@@ -1486,6 +1525,149 @@ package {name} is defined in two different locations:\n\
pushed[id.index()] = true;
}
+ /// Rebuilds the interfaces arena in topological order and updates all
+ /// InterfaceId references throughout this `Resolve`.
+ ///
+ /// After a merge, newly-added interfaces may appear after existing
+ /// interfaces that now depend on them. This method re-orders the
+ /// interfaces arena so that dependencies always precede dependents
+ /// within each package.
+ ///
+ /// The optional `remap` is updated so that its interface entries reflect
+ /// the new interface IDs.
+ fn topologically_sort_interfaces(&mut self, remap: Option<&mut Remap>) {
+ // Compute a global topological order: iterate packages in topological
+ // order, and within each package topologically sort interfaces based
+ // on their `use`-type dependencies.
+ let mut order = Vec::with_capacity(self.interfaces.len());
+ let mut visited = vec![false; self.interfaces.len()];
+
+ for pkg_id in self.topological_packages() {
+ let pkg = &self.packages[pkg_id];
+ for (_, &iface_id) in pkg.interfaces.iter() {
+ self.visit_interface_topo(iface_id, pkg_id, &mut visited, &mut order);
+ }
+ }
+
+ // Also include interfaces that don't belong to any package (shouldn't
+ // normally happen, but be safe).
+ for (id, _) in self.interfaces.iter() {
+ if !visited[id.index()] {
+ order.push(id);
+ }
+ }
+
+ // Check if already in order — if so, skip the rebuild.
+ let already_sorted = order
+ .iter()
+ .zip(order.iter().skip(1))
+ .all(|(a, b)| a.index() < b.index());
+ if already_sorted {
+ return;
+ }
+
+ // Build old-to-new mapping.
+ let mut old_to_new: Vec