-
Notifications
You must be signed in to change notification settings - Fork 227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nested interfaces in wit #1624
base: main
Are you sure you want to change the base?
Nested interfaces in wit #1624
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done a bit of a cursory review to start off with but I think that this still has a few missing pieces. For example src/resolve.rs
in the wit-parser
care will need to be updated to handle this new features of interfaces in a number of locations.
At a high level though I think it would be best to propose this feature on the component-model repository first. There's a number of high-level questions about how to design this feature that I think should be thought through such as:
- Is the syntax
nest a:b/c;
what everyone agrees on? - Should
nest a;
be allowed? - Should
nest a { /* ... */ }
be allowed? - How will code generators for guest langauges map this?
Those are at least the questions off the top of my head which I think would be best to settle first before finishing up the implementation here. A PR to the component model repository would also help flesh out the wasm-encoding of this WIT construct.
let id = parse_id(tokens)?; | ||
tokens.expect(Token::Colon)?; | ||
// `foo:bar/baz@1.0` | ||
let namespace = id; | ||
let pkg_name = parse_id(tokens)?; | ||
tokens.expect(Token::Slash)?; | ||
let name = parse_id(tokens)?; | ||
let version = parse_opt_version(tokens)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this is a duplication of UseName
's internal parsing, but could UseName
be used here instead? That would enable consistently being able to refer to both foreign and local interfaces with nest
decl_list | ||
.for_each_nest(|nest: &Nest| { | ||
let deps = foreign_deps | ||
.entry(nest.id.package_name()) | ||
.or_insert_with(|| { | ||
self.foreign_dep_spans.push(nest.id.span); | ||
IndexMap::new() | ||
}); | ||
let id = *deps.entry(nest.name.name).or_insert_with(|| { | ||
log::trace!( | ||
"creating an interface for foreign dep: {}/{}", | ||
nest.id.package_name(), | ||
nest.name.name | ||
); | ||
|
||
AstItem::Interface(self.alloc_interface(nest.name.span)) | ||
}); | ||
match id { | ||
AstItem::Interface(id) => foreign_interfaces.insert(id), | ||
AstItem::World(_) => unreachable!(), | ||
}; | ||
Ok(()) | ||
}) | ||
.unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think ideally if nest used a UsePath
then thiscould go away, along with the for_each_nest
method?
for nest_item in i.items.iter() { | ||
if let InterfaceItem::Nest(n) = nest_item { | ||
if package_items.insert(n.name.name, n.name.span).is_some() { | ||
bail!(Error::new( | ||
n.name.span, | ||
format!("duplicate item named `{}`", n.name.name), | ||
)) | ||
} | ||
let prev = decl_list_ns.insert(n.name.name, ()); | ||
assert!(prev.is_none()); | ||
let prev = order.insert(n.name.name, Vec::new()); | ||
assert!(prev.is_none()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this is quite right because this seems like it would be an interface
-level validation rather than a package-level validation which I think this loop is doing.
let id = self.alloc_interface(package_items[name]); | ||
self.interfaces[id].name = Some(name.to_string()); | ||
for item in &i.items { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this loop and processing nest
items in general would be best done during the elaboration of an interface to uniformly handle it next to other interface
constructs
pub package_name: PackageName, | ||
pub iface_name: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think both of these fields can be elided in favor of looking them up through the id
here
self.print_stability(&item.1.stability); | ||
self.print_docs(&item.1.docs); | ||
self.output.push_str("nest "); | ||
self.print_name(item.0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that this is quite right because this should print nest a:b/c;
or similar so I think an interface id needs to be printed here rather than just the string name
Sounds good! I can work on opening something up over there. |
This PR enables the ability to describe nested interfaces in wit via the
nest
keyword.This would indicate that that the instance associated with baz would export the instance associated with interface
qux
from packageother:pkg
.As I understand it, most use cases most immediately benefit from nesting interfaces from foreign packages, so I started with that, though we could certainly extend this PR or have follow ups to support any combo of locally defined/inlined/anonymous interfaces being nested.
This issue points out that wit currently is not able to express nested instances that can be expressed in wat/binary, so wit-bindgen can't be used by language toolchains to generate that binary. This syntax can be leveraged so that the unlocked dependency syntax referenced in the linked issue can specify the exports it expects in the imports it's describing.