Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions crates/wit-parser/src/ast/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ pub struct Resolver<'a> {
/// Metadata about foreign dependencies which are not defined in this
/// package. This map is keyed by the name of the package being imported
/// from. The next level of key is the name of the interface being imported
/// from, and the final value is the assigned ID of the interface.
foreign_deps: IndexMap<PackageName, IndexMap<&'a str, AstItem>>,
/// from, and the final value is a tuple containing the assigned ID of the
/// dependency, and a Vector of the Stability attributes associated with each
/// of its imports.
foreign_deps: IndexMap<PackageName, IndexMap<&'a str, (AstItem, Vec<Stability>)>>,

/// All interfaces that are present within `self.foreign_deps`.
foreign_interfaces: HashSet<InterfaceId>,
Expand Down Expand Up @@ -233,7 +235,9 @@ impl<'a> Resolver<'a> {
(
name.clone(),
deps.iter()
.map(|(name, id)| (name.to_string(), *id))
.map(|(name, (id, stabilities))| {
(name.to_string(), (*id, stabilities.clone()))
})
.collect(),
)
})
Expand All @@ -257,18 +261,20 @@ impl<'a> Resolver<'a> {
let mut foreign_worlds = mem::take(&mut self.foreign_worlds);
for decl_list in decl_lists {
decl_list
.for_each_path(&mut |_, _attrs, path, _names, world_or_iface| {
.for_each_path(&mut |_, attrs, path, _names, world_or_iface| {
let (id, name) = match path {
ast::UsePath::Package { id, name } => (id, name),
_ => return Ok(()),
};

let stability = self.stability(attrs)?;

let deps = foreign_deps.entry(id.package_name()).or_insert_with(|| {
self.foreign_dep_spans.push(id.span);
IndexMap::new()
});
let id = *deps.entry(name.name).or_insert_with(|| {
match world_or_iface {
let (id, stabilities) = deps.entry(name.name).or_insert_with(|| {
let id = match world_or_iface {
WorldOrInterface::World => {
log::trace!(
"creating a world for foreign dep: {}/{}",
Expand All @@ -287,10 +293,13 @@ impl<'a> Resolver<'a> {
);
AstItem::Interface(self.alloc_interface(name.span))
}
}
};
(id, Vec::new())
});

let _ = match id {
stabilities.push(stability);

let _ = match *id {
AstItem::Interface(id) => foreign_interfaces.insert(id),
AstItem::World(id) => foreign_worlds.insert(id),
};
Expand Down Expand Up @@ -520,7 +529,7 @@ impl<'a> Resolver<'a> {
)
})?,
ast::UsePath::Package { id, name } => {
self.foreign_deps[&id.package_name()][name.name]
self.foreign_deps[&id.package_name()][name.name].0
}
};
(name.name, item)
Expand Down Expand Up @@ -1104,7 +1113,7 @@ impl<'a> Resolver<'a> {
}
}
ast::UsePath::Package { id, name } => Ok((
self.foreign_deps[&id.package_name()][name.name],
self.foreign_deps[&id.package_name()][name.name].0,
name.name.into(),
name.span,
)),
Expand Down
2 changes: 1 addition & 1 deletion crates/wit-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ pub struct UnresolvedPackage {
/// interface name followed by the identifier within `self.interfaces`. The
/// fields of `self.interfaces` describes the required types that are from
/// each foreign interface.
pub foreign_deps: IndexMap<PackageName, IndexMap<String, AstItem>>,
pub foreign_deps: IndexMap<PackageName, IndexMap<String, (AstItem, Vec<Stability>)>>,

/// Doc comments for this package.
pub docs: Docs,
Expand Down
73 changes: 53 additions & 20 deletions crates/wit-parser/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3083,19 +3083,19 @@ impl Remap {
let mut world_to_package = HashMap::new();
let mut interface_to_package = HashMap::new();
for (i, (pkg_name, worlds_or_ifaces)) in unresolved.foreign_deps.iter().enumerate() {
for (name, item) in worlds_or_ifaces {
for (name, (item, stabilities)) in worlds_or_ifaces {
match item {
AstItem::Interface(unresolved_interface_id) => {
let prev = interface_to_package.insert(
*unresolved_interface_id,
(pkg_name, name, unresolved.foreign_dep_spans[i]),
(pkg_name, name, unresolved.foreign_dep_spans[i], stabilities),
);
assert!(prev.is_none());
}
AstItem::World(unresolved_world_id) => {
let prev = world_to_package.insert(
*unresolved_world_id,
(pkg_name, name, unresolved.foreign_dep_spans[i]),
(pkg_name, name, unresolved.foreign_dep_spans[i], stabilities),
);
assert!(prev.is_none());
}
Expand All @@ -3106,12 +3106,12 @@ impl Remap {
// Connect all interfaces referred to in `interface_to_package`, which
// are at the front of `unresolved.interfaces`, to interfaces already
// contained within `resolve`.
self.process_foreign_interfaces(unresolved, &interface_to_package, resolve)?;
self.process_foreign_interfaces(unresolved, &interface_to_package, resolve, &pkgid)?;

// Connect all worlds referred to in `world_to_package`, which
// are at the front of `unresolved.worlds`, to worlds already
// contained within `resolve`.
self.process_foreign_worlds(unresolved, &world_to_package, resolve)?;
self.process_foreign_worlds(unresolved, &world_to_package, resolve, &pkgid)?;

// Finally, iterate over all foreign-defined types and determine
// what they map to.
Expand Down Expand Up @@ -3146,17 +3146,20 @@ impl Remap {
fn process_foreign_interfaces(
&mut self,
unresolved: &UnresolvedPackage,
interface_to_package: &HashMap<InterfaceId, (&PackageName, &String, Span)>,
interface_to_package: &HashMap<InterfaceId, (&PackageName, &String, Span, &Vec<Stability>)>,
resolve: &mut Resolve,
parent_pkg_id: &PackageId,
) -> Result<(), anyhow::Error> {
for (unresolved_iface_id, unresolved_iface) in unresolved.interfaces.iter() {
let (pkg_name, interface, span) = match interface_to_package.get(&unresolved_iface_id) {
Some(items) => *items,
// All foreign interfaces are defined first, so the first one
// which is defined in a non-foreign document means that all
// further interfaces will be non-foreign as well.
None => break,
};
let (pkg_name, interface, span, stabilities) =
match interface_to_package.get(&unresolved_iface_id) {
Some(items) => *items,
// All foreign interfaces are defined first, so the first one
// which is defined in a non-foreign document means that all
// further interfaces will be non-foreign as well.
None => break,
};

let pkgid = resolve
.package_names
.get(pkg_name)
Expand All @@ -3174,6 +3177,20 @@ impl Remap {

let pkg = &resolve.packages[pkgid];
let span = &unresolved.interface_spans[unresolved_iface_id.index()];

let mut enabled = false;
for stability in stabilities {
if resolve.include_stability(stability, parent_pkg_id, Some(span.span))? {
enabled = true;
break;
}
}

if !enabled {
self.interfaces.push(None);
continue;
}

let iface_id = pkg
.interfaces
.get(interface)
Expand All @@ -3194,16 +3211,18 @@ impl Remap {
fn process_foreign_worlds(
&mut self,
unresolved: &UnresolvedPackage,
world_to_package: &HashMap<WorldId, (&PackageName, &String, Span)>,
world_to_package: &HashMap<WorldId, (&PackageName, &String, Span, &Vec<Stability>)>,
resolve: &mut Resolve,
parent_pkg_id: &PackageId,
) -> Result<(), anyhow::Error> {
for (unresolved_world_id, _) in unresolved.worlds.iter() {
let (pkg_name, world, span) = match world_to_package.get(&unresolved_world_id) {
Some(items) => *items,
// Same as above, all worlds are foreign until we find a
// non-foreign one.
None => break,
};
let (pkg_name, world, span, stabilities) =
match world_to_package.get(&unresolved_world_id) {
Some(items) => *items,
// Same as above, all worlds are foreign until we find a
// non-foreign one.
None => break,
};

let pkgid = resolve
.package_names
Expand All @@ -3212,6 +3231,20 @@ impl Remap {
.ok_or_else(|| Error::new(span, "package not found"))?;
let pkg = &resolve.packages[pkgid];
let span = &unresolved.world_spans[unresolved_world_id.index()];

let mut enabled = false;
for stability in stabilities {
if resolve.include_stability(stability, parent_pkg_id, Some(span.span))? {
enabled = true;
break;
}
}

if !enabled {
self.worlds.push(None);
continue;
}

let world_id = pkg
.worlds
.get(world)
Expand Down
17 changes: 17 additions & 0 deletions crates/wit-parser/tests/ui/foreign-interface-dep-gated.wit
Original file line number Diff line number Diff line change
@@ -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 {}
}
39 changes: 39 additions & 0 deletions crates/wit-parser/tests/ui/foreign-interface-dep-gated.wit.json
Original file line number Diff line number Diff line change
@@ -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
}
}
]
}
17 changes: 17 additions & 0 deletions crates/wit-parser/tests/ui/foreign-world-dep-gated.wit
Original file line number Diff line number Diff line change
@@ -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/another-world;
}
}

package a:b3 {
@unstable(feature = disabled)
world another-world {}
}
39 changes: 39 additions & 0 deletions crates/wit-parser/tests/ui/foreign-world-dep-gated.wit.json
Original file line number Diff line number Diff line change
@@ -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
}
}
]
}
13 changes: 5 additions & 8 deletions tests/cli/bad-stability.wit.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
error: failed to process world item in `c`

Caused by:
0: package [a:b] contains a feature gate with a version specifier, so it must have a version
--> tests/cli/bad-stability.wit:7:14
|
7 | import d:e/[email protected];
| ^----
error: package [a:b] contains a feature gate with a version specifier, so it must have a version
--> tests/cli/bad-stability.wit:7:14
|
7 | import d:e/[email protected];
| ^----