Skip to content
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

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
39 changes: 37 additions & 2 deletions crates/wit-component/src/encoding/wit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,15 @@ impl InterfaceEncoder<'_> {
}
}

fn encode_instance(&mut self, interface: InterfaceId) -> Result<u32> {
fn encode_instance_contents(&mut self, interface: InterfaceId) -> Result<InstanceType> {
self.push_instance();
let iface = &self.resolve.interfaces[interface];
let mut type_order = IndexSet::new();
for (_, id) in iface.types.iter() {
let ty = &self.resolve.types[*id];
if let TypeOwner::Interface(iface_id) = ty.owner {
self.interface = Some(iface_id);
}
Comment on lines +280 to +283
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might be best done a little differently than updating self.interface here. What I'm worried about is that this updates self.interface but doesn't restore it what it previously was.

Looking throughout this code though push_instance and pop_instance might be a good place to hook into? I think it should be ok to move most modifications of self.interface into those functions where push takes an InterfaceId and sets self.interface and then pop restores the previous value saved during push.

self.encode_valtype(self.resolve, &Type::Id(*id))?;
type_order.insert(*id);
}
Expand Down Expand Up @@ -311,14 +315,45 @@ impl InterfaceEncoder<'_> {
.unwrap()
.export(name, ComponentTypeRef::Func(ty));
}
let instance = self.pop_instance();
let mut instance = self.pop_instance();
for nest in &iface.nested {
let ty = instance.ty();
ty.instance(&self.encode_instance_contents(nest.id)?);
instance.export(
&self.interface_path(nest.id),
ComponentTypeRef::Instance(instance.type_count() - 1),
);
}

Ok(instance)
}

fn encode_instance(&mut self, interface: InterfaceId) -> Result<u32> {
let instance = self.encode_instance_contents(interface)?;
let idx = self.outer.type_count();
self.outer.ty().instance(&instance);
self.import_map.insert(interface, self.instances);
self.instances += 1;
Ok(idx)
}

fn interface_path(&self, iface: InterfaceId) -> String {
let iface = &self.resolve.interfaces[iface];
let pkg_id = iface.package.unwrap();
let pkg = &self.resolve.packages[pkg_id];
if let Some(v) = &pkg.name.version {
format!(
"{}:{}/{}@{}",
pkg.name.namespace,
pkg.name.name,
iface.name.as_ref().unwrap(),
v.to_string()
)
} else {
format!("{}/{}", pkg.name.to_string(), iface.name.as_ref().unwrap())
}
}
Comment on lines +340 to +355
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this I think Resolve::id_of might be what you want here? (in that I think it's equivalent to this function)


fn push_instance(&mut self) {
assert!(self.ty.is_none());
assert!(self.saved_types.is_none());
Expand Down
7 changes: 7 additions & 0 deletions crates/wit-component/src/printing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ impl WitPrinter {
fn print_interface(&mut self, resolve: &Resolve, id: InterfaceId) -> Result<()> {
let prev_items = mem::replace(&mut self.any_items, false);
let interface = &resolve.interfaces[id];
for nest in &interface.nested {
self.print_stability(&nest.stability);
self.print_docs(&nest.docs);
self.output.push_str("nest ");
self.print_path_to_interface(resolve, nest.id, interface.package.unwrap())?;
self.output.push_str(";\n");
}

let mut resource_funcs = HashMap::new();
let mut freestanding = Vec::new();
Expand Down
23 changes: 23 additions & 0 deletions crates/wit-component/tests/interfaces/multi-package-nest.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
(component
(type (;0;)
(component
(type (;0;)
(instance
(type (;0;)
(instance
(type (;0;) string)
(export (;1;) "t" (type (eq 0)))
)
)
(export (;0;) "foo:other/i" (instance (type 0)))
)
)
(export (;0;) "foo:bar/i1" (instance (type 0)))
)
)
(export (;1;) "i1" (type 0))
(@custom "package-docs" "\00{}")
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
11 changes: 11 additions & 0 deletions crates/wit-component/tests/interfaces/multi-package-nest.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package foo:bar;

package foo:other {
interface i {
type t = string;
}
}

interface i1 {
nest foo:other/i;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package foo:bar;

interface i1 {
nest foo:other/i;
}

48 changes: 48 additions & 0 deletions crates/wit-component/tests/interfaces/nested.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
(component
(type (;0;)
(component
(type (;0;)
(instance
(type (;0;) (record (field "foo" string)))
(export (;1;) "my-record" (type (eq 0)))
(type (;2;) (func (result string)))
(export (;0;) "hello" (func (type 2)))
(type (;3;)
(instance
(type (;0;) (record (field "foo" string)))
(export (;1;) "nest-record" (type (eq 0)))
(type (;2;) (func (result 1)))
(export (;0;) "goodbye" (func (type 2)))
(type (;3;)
(instance
(type (;0;) (record (field "foo" string)))
(export (;1;) "deep-record" (type (eq 0)))
(type (;2;) string)
(export (;3;) "anything" (type (eq 2)))
)
)
(export (;0;) "foo:nestnest/deep" (instance (type 3)))
)
)
(export (;0;) "foo:nestee/[email protected]" (instance (type 3)))
(type (;4;)
(instance
(export (;0;) "foo" (type (sub resource)))
(type (;1;) (borrow 0))
(type (;2;) (option string))
(type (;3;) (func (param "self" 1) (result 2)))
(export (;0;) "[method]foo.bar" (func (type 3)))
)
)
(export (;1;) "foo:nestee/[email protected]" (instance (type 4)))
)
)
(export (;0;) "foo:thing/[email protected]" (instance (type 0)))
)
)
(export (;1;) "something" (type 0))
(@custom "package-docs" "\01{\22interfaces\22:{\22something\22:{\22types\22:{\22my-record\22:{\22stability\22:{\22stable\22:{\22since\22:\221.0.0\22}}}},\22nested\22:{\22things\22:{\22docs\22:{\22contents\22:\22nesting can be documented\22},\22stability\22:\22unknown\22},\22more\22:{\22docs\22:{\22contents\22:null},\22stability\22:{\22stable\22:{\22since\22:\221.0.0\22}}}}}}}")
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
17 changes: 17 additions & 0 deletions crates/wit-component/tests/interfaces/nested/deps/nestee.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package foo:[email protected];

interface things {
//nesting can be documented
@since(version = 1.0.0)
nest foo:nestnest/deep;
record nest-record {
foo: string
}
goodbye: func() -> nest-record;
}

interface more {
resource foo {
bar: func() -> option<string>;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package foo:nestnest;

interface deep {
record deep-record {
foo: string
}
type anything = string;
}
14 changes: 14 additions & 0 deletions crates/wit-component/tests/interfaces/nested/nested.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package foo:[email protected];

interface something {
//nesting can be documented
nest foo:nestee/[email protected];
@since(version = 1.0.0)
nest foo:nestee/[email protected];
@since(version = 1.0.0)
record my-record {
foo: string
}

hello: func() -> string;
}
15 changes: 15 additions & 0 deletions crates/wit-component/tests/interfaces/nested/thing.wit.print
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package foo:[email protected];

interface something {
/// nesting can be documented
nest foo:nestee/[email protected];
@since(version = 1.0.0)
nest foo:nestee/[email protected];
@since(version = 1.0.0)
record my-record {
foo: string,
}

hello: func() -> string;
}

30 changes: 30 additions & 0 deletions crates/wit-parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ impl<'a> DeclList<'a> {
Some(&u.names),
WorldOrInterface::Interface,
)?,
InterfaceItem::Nest(n) => {
f(Some(&i.name), &n.from, None, WorldOrInterface::Interface)?
}
_ => {}
}
}
Expand Down Expand Up @@ -549,6 +552,30 @@ enum InterfaceItem<'a> {
TypeDef(TypeDef<'a>),
Func(NamedFunc<'a>),
Use(Use<'a>),
Nest(Nest<'a>),
}

struct Nest<'a> {
docs: Docs<'a>,
attributes: Vec<Attribute<'a>>,
from: UsePath<'a>,
}

impl<'a> Nest<'a> {
fn parse(
tokens: &mut Tokenizer<'a>,
docs: Docs<'a>,
attributes: Vec<Attribute<'a>>,
) -> Result<Self> {
tokens.eat(Token::Nest)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you'll want to expect here instead of eat (as eat has a boolean return)

let path = UsePath::parse(tokens)?;
tokens.expect_semicolon()?;
Ok(Self {
docs,
attributes,
from: path,
})
}
}

struct Use<'a> {
Expand Down Expand Up @@ -987,6 +1014,9 @@ impl<'a> InterfaceItem<'a> {
NamedFunc::parse(tokens, docs, attributes).map(InterfaceItem::Func)
}
Some((_span, Token::Use)) => Use::parse(tokens, attributes).map(InterfaceItem::Use),
Some((_span, Token::Nest)) => {
Nest::parse(tokens, docs, attributes).map(InterfaceItem::Nest)
}
other => Err(err_expected(tokens, "`type`, `resource` or `func`", other).into()),
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/wit-parser/src/ast/lex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub enum Token {
As,
From_,
Static,
Nest,
Interface,
Tuple,
Import,
Expand Down Expand Up @@ -302,6 +303,7 @@ impl<'a> Tokenizer<'a> {
"as" => As,
"from" => From_,
"static" => Static,
"nest" => Nest,
"interface" => Interface,
"tuple" => Tuple,
"world" => World,
Expand Down Expand Up @@ -563,6 +565,7 @@ impl Token {
As => "keyword `as`",
From_ => "keyword `from`",
Static => "keyword `static`",
Nest => "keyword `nest`",
Interface => "keyword `interface`",
Tuple => "keyword `tuple`",
Import => "keyword `import`",
Expand Down
19 changes: 16 additions & 3 deletions crates/wit-parser/src/ast/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ impl<'a> Resolver<'a> {
});
self.interfaces.alloc(Interface {
name: None,
nested: Vec::new(),
types: IndexMap::new(),
docs: Docs::default(),
stability: Default::default(),
Expand Down Expand Up @@ -792,7 +793,7 @@ impl<'a> Resolver<'a> {
fields.iter().filter_map(|i| match i {
ast::InterfaceItem::Use(u) => Some(TypeItem::Use(u)),
ast::InterfaceItem::TypeDef(t) => Some(TypeItem::Def(t)),
ast::InterfaceItem::Func(_) => None,
ast::InterfaceItem::Func(_) | ast::InterfaceItem::Nest(_) => None,
}),
)?;

Expand Down Expand Up @@ -839,6 +840,18 @@ impl<'a> Resolver<'a> {
}
}
ast::InterfaceItem::TypeDef(_) => {}
ast::InterfaceItem::Nest(n) => {
let (item, name, span) = self.resolve_ast_item_path(&n.from)?;
let nested_id = self.extract_iface_from_item(&item, &name, span)?;

let stability = self.stability(&n.attributes)?;
let docs = self.docs(&n.docs);
self.interfaces[interface_id].nested.push(crate::Nest {
id: nested_id,
docs,
stability,
});
}
}
}
for func in funcs {
Expand Down Expand Up @@ -1369,7 +1382,7 @@ impl<'a> Resolver<'a> {
Type::Id(*id)
}

fn docs(&mut self, doc: &super::Docs<'_>) -> Docs {
fn docs(&self, doc: &super::Docs<'_>) -> Docs {
let mut docs = vec![];
for doc in doc.docs.iter() {
if let Some(doc) = doc.strip_prefix("/**") {
Expand Down Expand Up @@ -1402,7 +1415,7 @@ impl<'a> Resolver<'a> {
Docs { contents }
}

fn stability(&mut self, attrs: &[ast::Attribute<'_>]) -> Result<Stability> {
fn stability(&self, attrs: &[ast::Attribute<'_>]) -> Result<Stability> {
match attrs {
[] => Ok(Stability::Unknown),

Expand Down
Loading