Skip to content

Commit 5acb1b1

Browse files
authored
Generate nominal interface/type ids in wit-component (#2516)
* Generate nominal interface/type ids in `wit-component` This commit is a long-overdue change in the componentization process in `wit-component` to ensure that when a component is generated there are unique `TypeId` and `InterfaceId` entries corresponding to all types/interfaces which map to the final component. This is distinct from the structure of WIT where the same `InterfaceId`, for example, can be both imported and exported. When generating a component this means that two distinct interfaces are imported/exported. Previously `wit-component` has had a lot of very carefully filled out and handled maps and such to ensure everything works, and this should make things significantly easier because the possibility over overlap is now nonexistent. * Leverage nominal types/interfaces in encoding This commit builds on the previous commit to simplify internal structures within the encoding process of a component. Notably there's no longer any need to have separate maps for imports/exports and instead everything is located in a single set of maps. Not major changes, but this is hoped to unlock future changes and additionally pave the way to simplifying some internals in the future.
1 parent 39103bf commit 5acb1b1

13 files changed

Lines changed: 141 additions & 111 deletions

File tree

crates/wit-component/src/encoding.rs

Lines changed: 79 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -399,17 +399,15 @@ pub struct EncodingState<'a> {
399399
/// A map of adapter module instances and the index of their instance.
400400
adapter_instances: IndexMap<&'a str, u32>,
401401

402-
/// Imported instances and what index they were imported as.
403-
imported_instances: IndexMap<InterfaceId, u32>,
402+
/// Imported/exported instances and what index they were imported as.
403+
instances: IndexMap<InterfaceId, u32>,
404404
imported_funcs: IndexMap<String, u32>,
405-
exported_instances: IndexMap<InterfaceId, u32>,
406405

407406
/// Maps used when translating types to the component model binary format.
408407
/// Note that imports and exports are stored in separate maps since they
409408
/// need fresh hierarchies of types in case the same interface is both
410409
/// imported and exported.
411-
import_type_encoding_maps: TypeEncodingMaps<'a>,
412-
export_type_encoding_maps: TypeEncodingMaps<'a>,
410+
type_encoding_maps: TypeEncodingMaps<'a>,
413411

414412
/// Cache of items that have been aliased from core instances.
415413
///
@@ -566,7 +564,7 @@ impl<'a> EncodingState<'a> {
566564
let instance_idx = self
567565
.component
568566
.import(name, ComponentTypeRef::Instance(instance_type_idx));
569-
let prev = self.imported_instances.insert(interface_id, instance_idx);
567+
let prev = self.instances.insert(interface_id, instance_idx);
570568
assert!(prev.is_none());
571569
Ok(())
572570
}
@@ -600,18 +598,10 @@ impl<'a> EncodingState<'a> {
600598
Ok(())
601599
}
602600

603-
fn alias_imported_type(&mut self, interface: InterfaceId, id: TypeId) -> u32 {
601+
fn alias_instance_type_export(&mut self, interface: InterfaceId, id: TypeId) -> u32 {
604602
let ty = &self.info.encoder.metadata.resolve.types[id];
605603
let name = ty.name.as_ref().expect("type must have a name");
606-
let instance = self.imported_instances[&interface];
607-
self.component
608-
.alias_export(instance, name, ComponentExportKind::Type)
609-
}
610-
611-
fn alias_exported_type(&mut self, interface: InterfaceId, id: TypeId) -> u32 {
612-
let ty = &self.info.encoder.metadata.resolve.types[id];
613-
let name = ty.name.as_ref().expect("type must have a name");
614-
let instance = self.exported_instances[&interface];
604+
let instance = self.instances[&interface];
615605
self.component
616606
.alias_export(instance, name, ComponentExportKind::Type)
617607
}
@@ -674,9 +664,9 @@ impl<'a> EncodingState<'a> {
674664
// If this resource is owned by a world then it's a top-level
675665
// resource which means it must have already been translated so
676666
// it's available for lookup in `import_type_map`.
677-
TypeOwner::World(_) => self.import_type_encoding_maps.id_to_index[&id],
667+
TypeOwner::World(_) => self.type_encoding_maps.id_to_index[&id],
678668
TypeOwner::Interface(i) => {
679-
let instance = self.imported_instances[&i];
669+
let instance = self.instances[&i];
680670
let name = ty.name.as_ref().expect("resources must be named");
681671
self.component
682672
.alias_export(instance, name, ComponentExportKind::Type)
@@ -886,17 +876,7 @@ impl<'a> EncodingState<'a> {
886876
nested.type_encoding_maps.def_to_index.clear();
887877
for (name, idx) in nested.imports.drain(..) {
888878
let id = reverse_map[&idx];
889-
let owner = match resolve.types[id].owner {
890-
TypeOwner::Interface(id) => id,
891-
_ => unreachable!(),
892-
};
893-
let idx = if owner == export || exports_used.contains(&owner) {
894-
log::trace!("consulting exports for {id:?}");
895-
nested.state.export_type_encoding_maps.id_to_index[&id]
896-
} else {
897-
log::trace!("consulting imports for {id:?}");
898-
nested.state.import_type_encoding_maps.id_to_index[&id]
899-
};
879+
let idx = nested.state.type_encoding_maps.id_to_index[&id];
900880
imports.push((name, ComponentExportKind::Type, idx))
901881
}
902882

@@ -966,7 +946,7 @@ impl<'a> EncodingState<'a> {
966946
instance_index,
967947
None,
968948
);
969-
let prev = self.exported_instances.insert(export, idx);
949+
let prev = self.instances.insert(export, idx);
970950
assert!(prev.is_none());
971951

972952
// After everything is all said and done remove all the type information
@@ -977,8 +957,8 @@ impl<'a> EncodingState<'a> {
977957
// necessary via aliases from the exported instance which is the new
978958
// source of truth for all these types.
979959
for (_name, id) in resolve.interfaces[export].types.iter() {
980-
self.export_type_encoding_maps.id_to_index.remove(id);
981-
self.export_type_encoding_maps
960+
self.type_encoding_maps.id_to_index.remove(id);
961+
self.type_encoding_maps
982962
.def_to_index
983963
.remove(&resolve.types[*id].kind);
984964
}
@@ -1296,7 +1276,7 @@ impl<'a> EncodingState<'a> {
12961276
let ((name, _), _) = interface.lowerings.get_index(*index).unwrap();
12971277
let func_index = match &interface.interface {
12981278
Some(interface_id) => {
1299-
let instance_index = self.imported_instances[interface_id];
1279+
let instance_index = self.instances[interface_id];
13001280
self.component.alias_export(
13011281
instance_index,
13021282
name,
@@ -1602,7 +1582,7 @@ impl<'a> EncodingState<'a> {
16021582
dtor,
16031583
);
16041584
let prev = self
1605-
.export_type_encoding_maps
1585+
.type_encoding_maps
16061586
.id_to_index
16071587
.insert(*ty, resource_idx);
16081588
assert!(prev.is_none());
@@ -1759,19 +1739,19 @@ impl<'a> EncodingState<'a> {
17591739
Import::ExportedResourceDrop(_key, id) => {
17601740
let index = self
17611741
.component
1762-
.resource_drop(self.export_type_encoding_maps.id_to_index[id]);
1742+
.resource_drop(self.type_encoding_maps.id_to_index[id]);
17631743
Ok((ExportKind::Func, index))
17641744
}
17651745
Import::ExportedResourceRep(_key, id) => {
17661746
let index = self
17671747
.component
1768-
.resource_rep(self.export_type_encoding_maps.id_to_index[id]);
1748+
.resource_rep(self.type_encoding_maps.id_to_index[id]);
17691749
Ok((ExportKind::Func, index))
17701750
}
17711751
Import::ExportedResourceNew(_key, id) => {
17721752
let index = self
17731753
.component
1774-
.resource_new(self.export_type_encoding_maps.id_to_index[id]);
1754+
.resource_new(self.type_encoding_maps.id_to_index[id]);
17751755
Ok((ExportKind::Func, index))
17761756
}
17771757

@@ -2078,7 +2058,7 @@ impl<'a> EncodingState<'a> {
20782058
Lowering::Direct => {
20792059
let func_index = match &import.interface {
20802060
Some(interface) => {
2081-
let instance_index = self.imported_instances[interface];
2061+
let instance_index = self.instances[interface];
20822062
self.component
20832063
.alias_export(instance_index, name, ComponentExportKind::Func)
20842064
}
@@ -3336,6 +3316,8 @@ impl ComponentEncoder {
33363316
.merge_world_imports_based_on_semver(self.metadata.world)?;
33373317
}
33383318

3319+
self.finalize_resolve_with_nominal_ids();
3320+
33393321
let world = ComponentWorld::new(self).context("failed to decode world from module")?;
33403322
let mut state = EncodingState {
33413323
component: ComponentBuilder::default(),
@@ -3346,11 +3328,9 @@ impl ComponentEncoder {
33463328
fixups_module_index: None,
33473329
adapter_modules: IndexMap::new(),
33483330
adapter_instances: IndexMap::new(),
3349-
import_type_encoding_maps: Default::default(),
3350-
export_type_encoding_maps: Default::default(),
3351-
imported_instances: Default::default(),
3331+
type_encoding_maps: Default::default(),
3332+
instances: Default::default(),
33523333
imported_funcs: Default::default(),
3353-
exported_instances: Default::default(),
33543334
aliased_core_items: Default::default(),
33553335
info: &world,
33563336
export_task_initialization_wrappers: HashMap::new(),
@@ -3376,6 +3356,63 @@ impl ComponentEncoder {
33763356

33773357
Ok(bytes)
33783358
}
3359+
3360+
/// Call the `generate_nominal_type_ids` method on the `Resolve` that we're
3361+
/// using, adjusting any preexisting keys/pointers as necessary.
3362+
///
3363+
/// This is the final step after merging all known `Resolve`s together
3364+
/// before a component is actually created. By creating a unique
3365+
/// `InterfaceId` for all interfaces it makes the generation process easier
3366+
/// since there's no need to fret about whether an `InterfaceId` is an
3367+
/// import or an export for example.
3368+
fn finalize_resolve_with_nominal_ids(&mut self) {
3369+
// Before calling `generate_nominal_type_ids` we need to handle the fact
3370+
// that the exports of the world are going to be rewritten. The only
3371+
// pointers we have into those are the exports of the main module and
3372+
// adapters. To handle this, before we generate nominal ids, indices of
3373+
// exports are saved here on the stack to get restored later on.
3374+
// Effectively we're clearing out the exports and rebuilding them later.
3375+
let world = &self.metadata.resolve.worlds[self.metadata.world];
3376+
let main_module_exports = self
3377+
.main_module_exports
3378+
.iter()
3379+
.map(|i| world.exports.get_index_of(i).unwrap())
3380+
.collect::<Vec<_>>();
3381+
let adapter_exports = self
3382+
.adapters
3383+
.values()
3384+
.map(|adapter| {
3385+
adapter
3386+
.required_exports
3387+
.iter()
3388+
.map(|i| world.exports.get_index_of(i).unwrap())
3389+
.collect::<Vec<_>>()
3390+
})
3391+
.collect::<Vec<_>>();
3392+
3393+
// With everything saved this will modify `Resolve` to ensure there's a
3394+
// nominal identifier for all interfaces (e.g. not both simultaneously
3395+
// imported and exported).
3396+
self.metadata
3397+
.resolve
3398+
.generate_nominal_type_ids(self.metadata.world);
3399+
3400+
// Rebuild the sets of exports now that the world's exports have been
3401+
// clobbered.
3402+
self.main_module_exports.clear();
3403+
let world = &self.metadata.resolve.worlds[self.metadata.world];
3404+
for index in main_module_exports {
3405+
let (key, _) = world.exports.get_index(index).unwrap();
3406+
self.main_module_exports.insert(key.clone());
3407+
}
3408+
for (exports, adapter) in adapter_exports.into_iter().zip(self.adapters.values_mut()) {
3409+
adapter.required_exports.clear();
3410+
for index in exports {
3411+
let (key, _) = world.exports.get_index(index).unwrap();
3412+
adapter.required_exports.insert(key.clone());
3413+
}
3414+
}
3415+
}
33793416
}
33803417

33813418
impl ComponentWorld<'_> {

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

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -474,22 +474,10 @@ impl<'a> ValtypeEncoder<'a> for RootTypeEncoder<'_, 'a> {
474474
.import(name, ComponentTypeRef::Type(TypeBounds::SubResource))
475475
}
476476
fn import_type(&mut self, interface: InterfaceId, id: TypeId) -> u32 {
477-
if !self.import_types {
478-
if let Some(cur) = self.interface {
479-
let set = &self.state.info.exports_used[&cur];
480-
if set.contains(&interface) {
481-
return self.state.alias_exported_type(interface, id);
482-
}
483-
}
484-
}
485-
self.state.alias_imported_type(interface, id)
477+
self.state.alias_instance_type_export(interface, id)
486478
}
487479
fn type_encoding_maps(&mut self) -> &mut TypeEncodingMaps<'a> {
488-
if self.import_types {
489-
&mut self.state.import_type_encoding_maps
490-
} else {
491-
&mut self.state.export_type_encoding_maps
492-
}
480+
&mut self.state.type_encoding_maps
493481
}
494482
}
495483

@@ -528,7 +516,7 @@ impl<'a> ValtypeEncoder<'a> for InstanceTypeEncoder<'_, 'a> {
528516
fn import_type(&mut self, interface: InterfaceId, id: TypeId) -> u32 {
529517
self.ty.alias(Alias::Outer {
530518
count: 1,
531-
index: self.state.alias_imported_type(interface, id),
519+
index: self.state.alias_instance_type_export(interface, id),
532520
kind: ComponentOuterAliasKind::Type,
533521
});
534522
self.ty.type_count() - 1

crates/wit-component/src/validation.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,10 +2397,27 @@ impl NameMangling for Legacy {
23972397
.interfaces
23982398
.get(name.interface().as_str())
23992399
{
2400+
// If the interface from the package is directly in `items` then
2401+
// return that.
24002402
let key = WorldKey::Interface(*id);
24012403
if items.contains_key(&key) {
24022404
return Ok((key, *id));
24032405
}
2406+
2407+
// .. otherwise see if any interface in `items` is a clone of
2408+
// the package's interface. This means it's created by
2409+
// `generate_nominal_type_ids` and is used to match up exports
2410+
// to their nominal clone since the original is no longer
2411+
// exported.
2412+
for k in items.keys() {
2413+
let i = match *k {
2414+
WorldKey::Interface(id) => id,
2415+
WorldKey::Name(_) => continue,
2416+
};
2417+
if resolve.interfaces[i].clone_of == Some(*id) {
2418+
return Ok((WorldKey::Interface(i), i));
2419+
}
2420+
}
24042421
}
24052422
}
24062423

crates/wit-component/tests/components/async-builtins/component.wat

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,8 @@
239239
(alias core export $main "[async-lift-stackful]foo" (core func $"[async-lift-stackful]foo" (;33;)))
240240
(func $foo (;0;) (type 0) (canon lift (core func $"[async-lift-stackful]foo") (memory $memory) (realloc $realloc) string-encoding=utf8 async))
241241
(export $"#func1 foo" (@name "foo") (;1;) "foo" (func $foo))
242-
(type (;1;) (func (param "s" string) (result string)))
243242
(alias core export $main "[async-lift-stackful]foo:foo/bar#foo" (core func $"[async-lift-stackful]foo:foo/bar#foo" (;34;)))
244-
(func $"#func2 foo" (@name "foo") (;2;) (type 1) (canon lift (core func $"[async-lift-stackful]foo:foo/bar#foo") (memory $memory) (realloc $realloc) string-encoding=utf8 async))
243+
(func $"#func2 foo" (@name "foo") (;2;) (type 0) (canon lift (core func $"[async-lift-stackful]foo:foo/bar#foo") (memory $memory) (realloc $realloc) string-encoding=utf8 async))
245244
(component $foo:foo/bar-shim-component (;0;)
246245
(type (;0;) (func (param "s" string) (result string)))
247246
(import "import-func-foo" (func (;0;) (type 0)))

crates/wit-component/tests/components/async-export-with-callback/component.wat

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,9 @@
3838
(alias core export $main "[callback][async-lift]foo" (core func $"[callback][async-lift]foo" (;2;)))
3939
(func $foo (;0;) (type 0) (canon lift (core func $"[async-lift]foo") (memory $memory) (realloc $cabi_realloc) string-encoding=utf8 async (callback $"[callback][async-lift]foo")))
4040
(export $"#func1 foo" (@name "foo") (;1;) "foo" (func $foo))
41-
(type (;1;) (func (param "s" string) (result string)))
4241
(alias core export $main "[async-lift]foo:foo/bar#foo" (core func $"[async-lift]foo:foo/bar#foo" (;3;)))
4342
(alias core export $main "[callback][async-lift]foo:foo/bar#foo" (core func $"[callback][async-lift]foo:foo/bar#foo" (;4;)))
44-
(func $"#func2 foo" (@name "foo") (;2;) (type 1) (canon lift (core func $"[async-lift]foo:foo/bar#foo") (memory $memory) (realloc $cabi_realloc) string-encoding=utf8 async (callback $"[callback][async-lift]foo:foo/bar#foo")))
43+
(func $"#func2 foo" (@name "foo") (;2;) (type 0) (canon lift (core func $"[async-lift]foo:foo/bar#foo") (memory $memory) (realloc $cabi_realloc) string-encoding=utf8 async (callback $"[callback][async-lift]foo:foo/bar#foo")))
4544
(component $foo:foo/bar-shim-component (;0;)
4645
(type (;0;) (func (param "s" string) (result string)))
4746
(import "import-func-foo" (func (;0;) (type 0)))

crates/wit-component/tests/components/async-export/component.wat

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,14 @@
4040
(alias core export $main "cabi_realloc" (core func $cabi_realloc (;1;)))
4141
(func $foo (;0;) (type 3) (canon lift (core func $"[async-lift-stackful]foo") (memory $memory) (realloc $cabi_realloc) string-encoding=utf8 async))
4242
(export $"#func1 foo" (@name "foo") (;1;) "foo" (func $foo))
43-
(type (;4;) (func (param "s" string) (result string)))
4443
(alias core export $main "[async-lift-stackful]foo:foo/bar#foo" (core func $"[async-lift-stackful]foo:foo/bar#foo" (;2;)))
45-
(func $"#func2 foo" (@name "foo") (;2;) (type 4) (canon lift (core func $"[async-lift-stackful]foo:foo/bar#foo") (memory $memory) (realloc $cabi_realloc) string-encoding=utf8 async))
46-
(type (;5;) (func (result string)))
44+
(func $"#func2 foo" (@name "foo") (;2;) (type 3) (canon lift (core func $"[async-lift-stackful]foo:foo/bar#foo") (memory $memory) (realloc $cabi_realloc) string-encoding=utf8 async))
45+
(type (;4;) (func (result string)))
4746
(alias core export $main "[async-lift-stackful]foo:foo/bar#get-string" (core func $"[async-lift-stackful]foo:foo/bar#get-string" (;3;)))
48-
(func $get-string (;3;) (type 5) (canon lift (core func $"[async-lift-stackful]foo:foo/bar#get-string") (memory $memory) string-encoding=utf8 async))
49-
(type (;6;) (func (result 2)))
47+
(func $get-string (;3;) (type 4) (canon lift (core func $"[async-lift-stackful]foo:foo/bar#get-string") (memory $memory) string-encoding=utf8 async))
48+
(type (;5;) (func (result 2)))
5049
(alias core export $main "[async-lift-stackful]foo:foo/bar#get-big" (core func $"[async-lift-stackful]foo:foo/bar#get-big" (;4;)))
51-
(func $get-big (;4;) (type 6) (canon lift (core func $"[async-lift-stackful]foo:foo/bar#get-big") (memory $memory) async))
50+
(func $get-big (;4;) (type 5) (canon lift (core func $"[async-lift-stackful]foo:foo/bar#get-big") (memory $memory) async))
5251
(component $foo:foo/bar-shim-component (;0;)
5352
(type (;0;) (func (param "s" string) (result string)))
5453
(import "import-func-foo" (func (;0;) (type 0)))

0 commit comments

Comments
 (0)