diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs index 617996da3b..3808542c89 100644 --- a/crates/wit-parser/src/resolve.rs +++ b/crates/wit-parser/src/resolve.rs @@ -3174,11 +3174,55 @@ impl Remap { let pkg = &resolve.packages[pkgid]; let span = &unresolved.interface_spans[unresolved_iface_id.index()]; - let iface_id = pkg - .interfaces - .get(interface) - .copied() - .ok_or_else(|| Error::new(span.span, "interface not found in package"))?; + + // This closure examines the unresolved worlds' imports and exports and returns true + // if all of the references to unresolved_iface_id are disabled by feature gating. + // If any enabled references exist, or no references (gated or otherwise) exist, return false. + let is_gated = |resolve: &Resolve| -> Result { + let mut found_gated_ref = false; + + // Search for unresolved_iface_id in the set of all unresolved worlds' imports and exports + for (_, world) in &unresolved.worlds { + let world_is_enabled = + resolve.include_stability(&world.stability, &pkgid, None)?; + + for (_, item) in world.imports.iter().chain(world.exports.iter()) { + if let WorldItem::Interface { id, stability } = item { + // Found unresolved_iface_id within the set of worlds' imports and exports + if *id == unresolved_iface_id { + let item_is_enabled = + resolve.include_stability(stability, &pkgid, None)?; + + // A reference to unresolved_iface_id exists, and it is not disabled by feature-gating, so return Ok(false) + if world_is_enabled && item_is_enabled { + return Ok(false); + } + + found_gated_ref = true; + } + } + } + } + + Ok(found_gated_ref) + }; + + let iface_id = match pkg.interfaces.get(interface) { + Some(id) => *id, + None => { + if is_gated(resolve)? { + // The interface's references are disabled by feature-gating so use + // 'None' as a placeholder as this reference will not be used + assert_eq!(self.interfaces.len(), unresolved_iface_id.index()); + self.interfaces.push(None); + continue; + } else { + // Enabled references to the absent interface must be present, so fail + bail!(Error::new(span.span, "interface not found in package")); + } + } + }; + assert_eq!(self.interfaces.len(), unresolved_iface_id.index()); self.interfaces.push(Some(iface_id)); } diff --git a/crates/wit-parser/tests/ui/foreign-deps-gated.wit b/crates/wit-parser/tests/ui/foreign-deps-gated.wit new file mode 100644 index 0000000000..25bbaf6334 --- /dev/null +++ b/crates/wit-parser/tests/ui/foreign-deps-gated.wit @@ -0,0 +1,17 @@ +package a:b1; + +world the-world { + include a:b2/the-world; +} + +package a:b2 { + world the-world { + @unstable(feature = disabled) + import a:b3/thing; + } +} + +package a:b3 { + @unstable(feature = disabled) + interface thing {} +} diff --git a/crates/wit-parser/tests/ui/foreign-deps-gated.wit.json b/crates/wit-parser/tests/ui/foreign-deps-gated.wit.json new file mode 100644 index 0000000000..58e43977ea --- /dev/null +++ b/crates/wit-parser/tests/ui/foreign-deps-gated.wit.json @@ -0,0 +1,39 @@ +{ + "worlds": [ + { + "name": "the-world", + "imports": {}, + "exports": {}, + "package": 1 + }, + { + "name": "the-world", + "imports": {}, + "exports": {}, + "package": 2 + } + ], + "interfaces": [], + "types": [], + "packages": [ + { + "name": "a:b3", + "interfaces": {}, + "worlds": {} + }, + { + "name": "a:b2", + "interfaces": {}, + "worlds": { + "the-world": 0 + } + }, + { + "name": "a:b1", + "interfaces": {}, + "worlds": { + "the-world": 1 + } + } + ] +} \ No newline at end of file