Skip to content

Commit e7da16a

Browse files
committed
Fix merge of disjoint interfaces.
Currently, a bug exists in merging of interfaces such that if one interface exports a function that references an exported (i.e. "named") type and another interface exports a different function that references the same exported type, the merged interface would have references to function types with types that are not present in the "encoded type index" map. This results in the encoder re-encoding the same type and using that newly encoded type index instead of the correct type index to the named type. Ultimately this resulted in a validation error of the encoded output where the interface wasn't valid for import because of the function not using what should be a named value type. The fix is for the merged interface to maintain a mapping between the new type and the old types so that the "encoded type index" map is updated accordingly.
1 parent d95f15e commit e7da16a

8 files changed

Lines changed: 105 additions & 2 deletions

File tree

crates/wac-parser/src/resolution/ast.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ impl<'a> AstResolver<'a> {
445445

446446
let mut ty = Interface {
447447
id: None,
448+
remapped_types: Default::default(),
448449
uses: Default::default(),
449450
exports: Default::default(),
450451
};
@@ -483,6 +484,7 @@ impl<'a> AstResolver<'a> {
483484

484485
let mut ty = Interface {
485486
id: Some(self.id(decl.id.string)),
487+
remapped_types: Default::default(),
486488
uses: Default::default(),
487489
exports: Default::default(),
488490
};
@@ -1898,7 +1900,13 @@ impl<'a> AstResolver<'a> {
18981900
.with_context(|| format!("mismatched type for export `{name}`")),
18991901
) {
19001902
(Ok(_), Ok(_)) => {
1901-
// The two are compatible, so do nothing
1903+
// The two are compatible, check for remapped type
1904+
match (*target_kind, *source_kind) {
1905+
(ItemKind::Type(new), ItemKind::Type(old)) if new != old => {
1906+
target.remapped_types.entry(new).or_default().insert(old);
1907+
}
1908+
_ => {}
1909+
}
19021910
}
19031911
(Err(e), _) | (_, Err(e)) => {
19041912
// Neither is a subtype of the other, so error

crates/wac-parser/src/resolution/encoding.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,17 @@ impl<'a> TypeEncoder<'a> {
620620
// Otherwise, export all exports
621621
for (name, kind) in &interface.exports {
622622
match kind {
623-
ItemKind::Type(_) | ItemKind::Resource(_) => {
623+
ItemKind::Type(ty) => {
624+
let index = self.export(state, name, *kind)?;
625+
626+
// Map the encoded type index to any remapped types.
627+
if let Some(prev) = interface.remapped_types.get(ty) {
628+
for ty in prev {
629+
state.current.type_indexes.insert(*ty, index);
630+
}
631+
}
632+
}
633+
ItemKind::Resource(_) => {
624634
self.export(state, name, *kind)?;
625635
}
626636
_ => {

crates/wac-parser/src/resolution/package.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ impl<'a> TypeConverter<'a> {
364364
let instance_ty = &types[id];
365365
let id = self.definitions.interfaces.alloc(Interface {
366366
id: name.and_then(|n| n.contains(':').then(|| n.to_owned())),
367+
remapped_types: Default::default(),
367368
uses: Default::default(),
368369
exports: IndexMap::with_capacity(instance_ty.exports.len()),
369370
});

crates/wac-parser/src/resolution/types.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,15 @@ pub struct Interface {
387387
///
388388
/// This may be `None` for inline interfaces.
389389
pub id: Option<String>,
390+
/// Represents a remapping of types that may occur when an interface is merged.
391+
///
392+
/// The map is from the type present in this interface to a set of types
393+
/// originating from the merged interfaces.
394+
///
395+
/// Encoding uses this map to populate the encoded type index map for the
396+
/// original types.
397+
#[serde(skip_serializing_if = "IndexMap::is_empty")]
398+
pub remapped_types: IndexMap<Type, IndexSet<Type>>,
390399
/// A map from used interface to set of used type export indexes.
391400
#[serde(serialize_with = "serialize_id_key_map")]
392401
pub uses: IndexMap<InterfaceId, IndexSet<usize>>,
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package test:comp;
2+
3+
let a = new foo:bar {
4+
...
5+
};
6+
7+
let b = new foo:baz {
8+
...
9+
};
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
(component
2+
(type (;0;)
3+
(instance
4+
(type (;0;) (record (field "x" u32)))
5+
(export (;1;) "x" (type (eq 0)))
6+
(type (;2;) (func (param "x" 1)))
7+
(export (;0;) "y" (func (type 2)))
8+
(type (;3;) (func (param "x" 1)))
9+
(export (;1;) "z" (func (type 3)))
10+
)
11+
)
12+
(import "foo:bar/baz" (instance (;0;) (type 0)))
13+
(type (;1;)
14+
(component
15+
(type (;0;)
16+
(instance
17+
(type (;0;) (record (field "x" u32)))
18+
(export (;1;) "x" (type (eq 0)))
19+
(type (;2;) (func (param "x" 1)))
20+
(export (;0;) "y" (func (type 2)))
21+
)
22+
)
23+
(import "foo:bar/baz" (instance (;0;) (type 0)))
24+
)
25+
)
26+
(import "unlocked-dep=<foo:bar>" (component (;0;) (type 1)))
27+
(instance (;1;) (instantiate 0
28+
(with "foo:bar/baz" (instance 0))
29+
)
30+
)
31+
(type (;2;)
32+
(component
33+
(type (;0;)
34+
(instance
35+
(type (;0;) (record (field "x" u32)))
36+
(export (;1;) "x" (type (eq 0)))
37+
(type (;2;) (func (param "x" 1)))
38+
(export (;0;) "z" (func (type 2)))
39+
)
40+
)
41+
(import "foo:bar/baz" (instance (;0;) (type 0)))
42+
)
43+
)
44+
(import "unlocked-dep=<foo:baz>" (component (;1;) (type 2)))
45+
(instance (;2;) (instantiate 1
46+
(with "foo:bar/baz" (instance 0))
47+
)
48+
)
49+
(@producers
50+
(processed-by "wac-parser" "0.1.0")
51+
)
52+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(component
2+
(import "foo:bar/baz" (instance
3+
(type (record (field "x" u32)))
4+
(export "x" (type (eq 0)))
5+
(export "y" (func (param "x" 1)))
6+
))
7+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(component
2+
(import "foo:bar/baz" (instance
3+
(type (record (field "x" u32)))
4+
(export "x" (type (eq 0)))
5+
(export "z" (func (param "x" 1)))
6+
))
7+
)

0 commit comments

Comments
 (0)