Skip to content

Commit b3a57ad

Browse files
committed
[wit-component] Add implements encoding support to wit-component
Update component encoding to use `name_world_key_with_item` at sites that produce component-level extern names, so that `implements` imports and exports are encoded as `[implements=<I>]L` in the binary format. Five call sites are changed from `name_world_key` to the implements-aware variant: import_map key construction, component export names, ImportedResourceDrop lookups, and both direct and indirect InterfaceFunc lookups.
1 parent f4950af commit b3a57ad

File tree

7 files changed

+251
-24
lines changed

7 files changed

+251
-24
lines changed

crates/wit-component/src/encoding.rs

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ pub struct EncodingState<'a> {
401401

402402
/// Imported instances and what index they were imported as.
403403
imported_instances: IndexMap<InterfaceId, u32>,
404+
imported_instances_by_name: HashMap<String, u32>,
404405
imported_funcs: IndexMap<String, u32>,
405406
exported_instances: IndexMap<InterfaceId, u32>,
406407

@@ -456,6 +457,18 @@ impl<'a> EncodingState<'a> {
456457
}
457458
}
458459

460+
fn world_import_name(&self, key: &WorldKey) -> String {
461+
let resolve = &self.info.encoder.metadata.resolve;
462+
let world = &resolve.worlds[self.info.encoder.metadata.world];
463+
resolve.name_world_key_with_item(key, &world.imports[key])
464+
}
465+
466+
fn world_export_name(&self, key: &WorldKey) -> String {
467+
let resolve = &self.info.encoder.metadata.resolve;
468+
let world = &resolve.worlds[self.info.encoder.metadata.world];
469+
resolve.name_world_key_with_item(key, &world.exports[key])
470+
}
471+
459472
fn root_import_type_encoder(
460473
&mut self,
461474
interface: Option<InterfaceId>,
@@ -566,8 +579,11 @@ impl<'a> EncodingState<'a> {
566579
let instance_idx = self
567580
.component
568581
.import(name, ComponentTypeRef::Instance(instance_type_idx));
569-
let prev = self.imported_instances.insert(interface_id, instance_idx);
570-
assert!(prev.is_none());
582+
self.imported_instances
583+
.entry(interface_id)
584+
.or_insert(instance_idx);
585+
self.imported_instances_by_name
586+
.insert(name.to_string(), instance_idx);
571587
Ok(())
572588
}
573589

@@ -704,9 +720,9 @@ impl<'a> EncodingState<'a> {
704720
let prev = world_func_core_names.insert(name, core_name);
705721
assert!(prev.is_none());
706722
}
707-
Export::InterfaceFunc(_, id, name, _) => {
723+
Export::InterfaceFunc(key, _id, name, _) => {
708724
let prev = interface_func_core_names
709-
.entry(id)
725+
.entry(key)
710726
.or_insert(IndexMap::new())
711727
.insert(name.as_str(), core_name);
712728
assert!(prev.is_none());
@@ -731,7 +747,7 @@ impl<'a> EncodingState<'a> {
731747
let world = &resolve.worlds[self.info.encoder.metadata.world];
732748

733749
for export_name in exports {
734-
let export_string = resolve.name_world_key(export_name);
750+
let export_string = self.world_export_name(export_name);
735751
match &world.exports[export_name] {
736752
WorldItem::Function(func) => {
737753
let ty = self
@@ -743,7 +759,7 @@ impl<'a> EncodingState<'a> {
743759
.export(&export_string, ComponentExportKind::Func, idx, None);
744760
}
745761
WorldItem::Interface { id, .. } => {
746-
let core_names = interface_func_core_names.get(id);
762+
let core_names = interface_func_core_names.get(export_name);
747763
self.encode_interface_export(
748764
&export_string,
749765
module,
@@ -966,8 +982,7 @@ impl<'a> EncodingState<'a> {
966982
instance_index,
967983
None,
968984
);
969-
let prev = self.exported_instances.insert(export, idx);
970-
assert!(prev.is_none());
985+
self.exported_instances.entry(export).or_insert(idx);
971986

972987
// After everything is all said and done remove all the type information
973988
// about type exports of this interface. Any entries in the map
@@ -1292,11 +1307,12 @@ impl<'a> EncodingState<'a> {
12921307
realloc,
12931308
encoding,
12941309
} => {
1295-
let interface = &self.info.import_map[interface];
1296-
let ((name, _), _) = interface.lowerings.get_index(*index).unwrap();
1297-
let func_index = match &interface.interface {
1298-
Some(interface_id) => {
1299-
let instance_index = self.imported_instances[interface_id];
1310+
let import = &self.info.import_map[interface];
1311+
let ((name, _), _) = import.lowerings.get_index(*index).unwrap();
1312+
let func_index = match interface {
1313+
Some(iface_key) => {
1314+
let instance_index =
1315+
self.imported_instances_by_name[iface_key.as_str()];
13001316
self.component.alias_export(
13011317
instance_index,
13021318
name,
@@ -1309,7 +1325,7 @@ impl<'a> EncodingState<'a> {
13091325
let realloc = self
13101326
.info
13111327
.exports_for(*realloc)
1312-
.import_realloc_for(interface.interface, name)
1328+
.import_realloc_for(import.interface, name)
13131329
.map(|name| {
13141330
let instance = self.instance_for(*realloc);
13151331
self.core_alias_export(
@@ -1785,7 +1801,7 @@ impl<'a> EncodingState<'a> {
17851801
self.materialize_wit_import(
17861802
shims,
17871803
for_module,
1788-
iface.map(|_| resolve.name_world_key(key)),
1804+
iface.map(|_| self.world_import_name(key)),
17891805
&format!("{name}_drop"),
17901806
key,
17911807
AbiVariant::GuestImport,
@@ -1955,7 +1971,7 @@ impl<'a> EncodingState<'a> {
19551971
Import::InterfaceFunc(key, _, name, abi) => self.materialize_wit_import(
19561972
shims,
19571973
for_module,
1958-
Some(resolve.name_world_key(key)),
1974+
Some(self.world_import_name(key)),
19591975
name,
19601976
key,
19611977
*abi,
@@ -2072,9 +2088,9 @@ impl<'a> EncodingState<'a> {
20722088
// All direct lowerings can be `canon lower`'d here immediately
20732089
// and passed as arguments.
20742090
Lowering::Direct => {
2075-
let func_index = match &import.interface {
2076-
Some(interface) => {
2077-
let instance_index = self.imported_instances[interface];
2091+
let func_index = match &interface_key {
2092+
Some(iface_key) => {
2093+
let instance_index = self.imported_instances_by_name[iface_key.as_str()];
20782094
self.component
20792095
.alias_export(instance_index, name, ComponentExportKind::Func)
20802096
}
@@ -2849,14 +2865,15 @@ impl<'a> Shims<'a> {
28492865
// metadata out of this `match` to the loop below to figure that
28502866
// out.
28512867
Import::InterfaceFunc(key, _, name, abi) => {
2868+
let wit_world = &resolve.worlds[world.encoder.metadata.world];
28522869
self.append_indirect_wit_func(
28532870
world,
28542871
for_module,
28552872
module,
28562873
field,
28572874
key,
28582875
name,
2859-
Some(resolve.name_world_key(key)),
2876+
Some(resolve.name_world_key_with_item(key, &wit_world.imports[key])),
28602877
*abi,
28612878
)?;
28622879
}
@@ -3343,6 +3360,7 @@ impl ComponentEncoder {
33433360
import_type_encoding_maps: Default::default(),
33443361
export_type_encoding_maps: Default::default(),
33453362
imported_instances: Default::default(),
3363+
imported_instances_by_name: Default::default(),
33463364
imported_funcs: Default::default(),
33473365
exported_instances: Default::default(),
33483366
aliased_core_items: Default::default(),

crates/wit-component/src/encoding/wit.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub fn encode_world(resolve: &Resolve, world_id: WorldId) -> Result<ComponentTyp
7373

7474
// Encode the imports
7575
for (name, import) in world.imports.iter() {
76-
let name = resolve.name_world_key(name);
76+
let name = resolve.name_world_key_with_item(name, import);
7777
log::trace!("encoding import {name}");
7878
let ty = match import {
7979
WorldItem::Interface { id, .. } => {
@@ -98,7 +98,7 @@ pub fn encode_world(resolve: &Resolve, world_id: WorldId) -> Result<ComponentTyp
9898
}
9999
// Encode the exports
100100
for (name, export) in world.exports.iter() {
101-
let name = resolve.name_world_key(name);
101+
let name = resolve.name_world_key_with_item(name, export);
102102
log::trace!("encoding export {name}");
103103
let ty = match export {
104104
WorldItem::Interface { id, .. } => {

crates/wit-component/src/encoding/world.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ impl<'a> ComponentWorld<'a> {
248248
item: &WorldItem,
249249
required: &Required<'_>,
250250
) -> Result<()> {
251-
let name = resolve.name_world_key(name);
251+
let name = resolve.name_world_key_with_item(name, item);
252252
log::trace!("register import `{name}`");
253253
let import_map_key = match item {
254254
WorldItem::Function(_) | WorldItem::Type { .. } => None,
@@ -314,7 +314,10 @@ impl<'a> ComponentWorld<'a> {
314314
// set. This means that only types not defined by referenced exports
315315
// will get added here.
316316
for (name, item) in resolve.worlds[world].exports.iter() {
317-
log::trace!("add live world export `{}`", resolve.name_world_key(name));
317+
log::trace!(
318+
"add live world export `{}`",
319+
resolve.name_world_key_with_item(name, item)
320+
);
318321
let id = match item {
319322
WorldItem::Interface { id, .. } => id,
320323
WorldItem::Function(_) | WorldItem::Type { .. } => {
@@ -453,6 +456,10 @@ impl<'a> ComponentWorld<'a> {
453456
WorldItem::Interface { id, .. } => *id,
454457
WorldItem::Type { .. } => unreachable!(),
455458
};
459+
// Skip already-processed interfaces (implements duplicates).
460+
if self.exports_used.contains_key(&id) {
461+
continue;
462+
}
456463
let mut set = HashSet::new();
457464

458465
for other in resolve.interface_direct_deps(id) {

crates/wit-component/src/printing.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,17 @@ impl<O: Output> WitPrinter<O> {
468468
match name {
469469
WorldKey::Name(name) => {
470470
match item {
471+
WorldItem::Interface {
472+
id,
473+
implements: Some(_),
474+
..
475+
} => {
476+
// `import label: use-path;` syntax
477+
self.print_name_type(name, TypeKind::Other);
478+
self.output.str(": ");
479+
self.print_path_to_interface(resolve, *id, cur_pkg)?;
480+
self.output.semicolon();
481+
}
471482
WorldItem::Interface { id, .. } => {
472483
self.print_name_type(name, TypeKind::Other);
473484
self.output.str(": ");
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
(component
2+
(type (;0;)
3+
(component
4+
(type (;0;)
5+
(instance
6+
(type (;0;) (option string))
7+
(type (;1;) (func (param "key" string) (result 0)))
8+
(export (;0;) "get" (func (type 1)))
9+
(type (;2;) (func (param "key" string) (param "value" string)))
10+
(export (;1;) "set" (func (type 2)))
11+
)
12+
)
13+
(export (;0;) "foo:foo/store" (instance (type 0)))
14+
)
15+
)
16+
(export (;1;) "store" (type 0))
17+
(type (;2;)
18+
(component
19+
(type (;0;)
20+
(instance
21+
(type (;0;) (func (param "msg" string)))
22+
(export (;0;) "log" (func (type 0)))
23+
)
24+
)
25+
(export (;0;) "foo:foo/logger" (instance (type 0)))
26+
)
27+
)
28+
(export (;3;) "logger" (type 2))
29+
(type (;4;)
30+
(component
31+
(type (;0;)
32+
(component
33+
(type (;0;)
34+
(instance
35+
(type (;0;) (option string))
36+
(type (;1;) (func (param "key" string) (result 0)))
37+
(export (;0;) "get" (func (type 1)))
38+
(type (;2;) (func (param "key" string) (param "value" string)))
39+
(export (;1;) "set" (func (type 2)))
40+
)
41+
)
42+
(import "[implements=<foo:foo/store>]primary" (instance (;0;) (type 0)))
43+
(type (;1;)
44+
(instance
45+
(type (;0;) (option string))
46+
(type (;1;) (func (param "key" string) (result 0)))
47+
(export (;0;) "get" (func (type 1)))
48+
(type (;2;) (func (param "key" string) (param "value" string)))
49+
(export (;1;) "set" (func (type 2)))
50+
)
51+
)
52+
(import "[implements=<foo:foo/store>]backup" (instance (;1;) (type 1)))
53+
(type (;2;)
54+
(instance
55+
(type (;0;) (func (param "msg" string)))
56+
(export (;0;) "log" (func (type 0)))
57+
)
58+
)
59+
(import "foo:foo/logger" (instance (;2;) (type 2)))
60+
)
61+
)
62+
(export (;0;) "foo:foo/multi-import" (component (type 0)))
63+
)
64+
)
65+
(export (;5;) "multi-import" (type 4))
66+
(type (;6;)
67+
(component
68+
(type (;0;)
69+
(component
70+
(type (;0;)
71+
(instance
72+
(type (;0;) (option string))
73+
(type (;1;) (func (param "key" string) (result 0)))
74+
(export (;0;) "get" (func (type 1)))
75+
(type (;2;) (func (param "key" string) (param "value" string)))
76+
(export (;1;) "set" (func (type 2)))
77+
)
78+
)
79+
(export (;0;) "[implements=<foo:foo/store>]primary" (instance (type 0)))
80+
(type (;1;)
81+
(instance
82+
(type (;0;) (option string))
83+
(type (;1;) (func (param "key" string) (result 0)))
84+
(export (;0;) "get" (func (type 1)))
85+
(type (;2;) (func (param "key" string) (param "value" string)))
86+
(export (;1;) "set" (func (type 2)))
87+
)
88+
)
89+
(export (;1;) "[implements=<foo:foo/store>]backup" (instance (type 1)))
90+
)
91+
)
92+
(export (;0;) "foo:foo/multi-export" (component (type 0)))
93+
)
94+
)
95+
(export (;7;) "multi-export" (type 6))
96+
(type (;8;)
97+
(component
98+
(type (;0;)
99+
(component
100+
(type (;0;)
101+
(instance
102+
(type (;0;) (option string))
103+
(type (;1;) (func (param "key" string) (result 0)))
104+
(export (;0;) "get" (func (type 1)))
105+
(type (;2;) (func (param "key" string) (param "value" string)))
106+
(export (;1;) "set" (func (type 2)))
107+
)
108+
)
109+
(import "foo:foo/store" (instance (;0;) (type 0)))
110+
(type (;1;)
111+
(instance
112+
(type (;0;) (option string))
113+
(type (;1;) (func (param "key" string) (result 0)))
114+
(export (;0;) "get" (func (type 1)))
115+
(type (;2;) (func (param "key" string) (param "value" string)))
116+
(export (;1;) "set" (func (type 2)))
117+
)
118+
)
119+
(import "[implements=<foo:foo/store>]cache" (instance (;1;) (type 1)))
120+
)
121+
)
122+
(export (;0;) "foo:foo/mixed" (component (type 0)))
123+
)
124+
)
125+
(export (;9;) "mixed" (type 8))
126+
(@custom "package-docs" "\01{\22worlds\22:{\22multi-import\22:{\22docs\22:\22A world that imports the same interface multiple times under different\5cnplain names using the `implements` syntax.\22},\22multi-export\22:{\22docs\22:\22A world that exports the same interface multiple times.\22},\22mixed\22:{\22docs\22:\22A world with both implements imports and a normal interface import,\5cntesting that elaboration adds the dependency correctly.\22}}}")
127+
(@producers
128+
(processed-by "wit-component" "$CARGO_PKG_VERSION")
129+
)
130+
)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package foo:foo;
2+
3+
interface store {
4+
get: func(key: string) -> option<string>;
5+
set: func(key: string, value: string);
6+
}
7+
8+
interface logger {
9+
log: func(msg: string);
10+
}
11+
12+
/// A world that imports the same interface multiple times under different
13+
/// plain names using the `implements` syntax.
14+
world multi-import {
15+
import primary: store;
16+
import backup: store;
17+
import logger;
18+
}
19+
20+
/// A world that exports the same interface multiple times.
21+
world multi-export {
22+
export primary: store;
23+
export backup: store;
24+
}
25+
26+
/// A world with both implements imports and a normal interface import,
27+
/// testing that elaboration adds the dependency correctly.
28+
world mixed {
29+
import store;
30+
import cache: store;
31+
}

0 commit comments

Comments
 (0)