Skip to content

Commit 49e1e61

Browse files
Merge pull request #205 from salmans/fix-nested-use-type-alias
Alias `use`'d types during composition instead of re-encoding them locally
2 parents a0d6a9b + 1f06572 commit 49e1e61

12 files changed

Lines changed: 350 additions & 24 deletions

File tree

crates/wac-graph/src/encoding.rs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,31 @@ impl<'a> TypeEncoder<'a> {
190190
Self(types)
191191
}
192192

193+
// Aliases a value type from an imported instance that exports it, rather
194+
// than re-encoding it locally: a type import must reference a named type,
195+
// not a local definition.
196+
fn alias_from_instance_export(&self, state: &mut State, ty: ValueType) -> Option<u32> {
197+
if state.current.instances.is_empty() {
198+
return None;
199+
}
200+
let ty = Type::Value(ty);
201+
let (instance, name) = self.0.interfaces().find_map(|interface| {
202+
let iid = interface.id.as_ref()?;
203+
let instance = *state.current.instances.get(iid)?;
204+
interface.exports.iter().find_map(|(name, kind)| {
205+
(*kind == ItemKind::Type(ty)).then(|| (instance, name.clone()))
206+
})
207+
})?;
208+
let index = state.current.encodable.type_count();
209+
state.current.encodable.alias(Alias::InstanceExport {
210+
instance,
211+
kind: ComponentExportKind::Type,
212+
name: &name,
213+
});
214+
state.current.type_indexes.insert(ty, index);
215+
Some(index)
216+
}
217+
193218
pub fn ty(&self, state: &mut State, ty: Type, name: Option<&str>) -> u32 {
194219
if let Some(index) = state.current.type_indexes.get(&ty) {
195220
return *index;
@@ -264,7 +289,12 @@ impl<'a> TypeEncoder<'a> {
264289
index
265290
}
266291

267-
fn use_aliases(&self, state: &mut State, uses: &'a IndexMap<String, UsedType>) {
292+
fn use_aliases(
293+
&self,
294+
state: &mut State,
295+
uses: &'a IndexMap<String, UsedType>,
296+
items: &'a IndexMap<String, ItemKind>,
297+
) {
268298
state.current.type_aliases.clear();
269299

270300
for (name, used) in uses {
@@ -286,6 +316,17 @@ impl<'a> TypeEncoder<'a> {
286316
);
287317

288318
state.current.type_aliases.insert(name.clone(), index);
319+
320+
// Record the alias under both ids that denote this used type (the
321+
// interface's export and the using entity's own item) so a later
322+
// id-keyed reference reuses it instead of re-encoding a local copy:
323+
// a type import must reference a named type, not a local definition.
324+
if let ItemKind::Type(ty) = kind {
325+
state.current.type_indexes.insert(*ty, index);
326+
}
327+
if let Some(ItemKind::Type(ty)) = items.get(name) {
328+
state.current.type_indexes.insert(*ty, index);
329+
}
289330
}
290331
}
291332

@@ -297,7 +338,7 @@ impl<'a> TypeEncoder<'a> {
297338
}
298339

299340
// Encode any required aliases
300-
self.use_aliases(state, &interface.uses);
341+
self.use_aliases(state, &interface.uses, &interface.exports);
301342
state.push(Encodable::Instance(InstanceType::default()));
302343

303344
// Otherwise, export all exports
@@ -335,7 +376,7 @@ impl<'a> TypeEncoder<'a> {
335376
self.import_deps(state, used.interface);
336377
}
337378

338-
self.use_aliases(state, &world.uses);
379+
self.use_aliases(state, &world.uses, &world.imports);
339380

340381
for (name, kind) in &world.imports {
341382
self.import(state, name, *kind);
@@ -516,6 +557,10 @@ impl<'a> TypeEncoder<'a> {
516557
return ComponentValType::Type(*index);
517558
}
518559

560+
if let Some(index) = self.alias_from_instance_export(state, ty) {
561+
return ComponentValType::Type(index);
562+
}
563+
519564
let index = match ty {
520565
ValueType::Primitive(ty) => return ComponentValType::Primitive(ty.into()),
521566
ValueType::Borrow(id) => self.borrow(state, id),

crates/wac-graph/src/graph.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,8 +1544,12 @@ impl<'a> CompositionGraphEncoder<'a> {
15441544

15451545
let mut encoded = HashMap::new();
15461546

1547-
// Next encode the imports
1548-
for (name, kind) in aggregator.imports() {
1547+
// Instances first, so a later type import can alias a type from an
1548+
// instance export instead of re-encoding it as a local definition.
1549+
let (instances, rest): (Vec<_>, Vec<_>) = aggregator
1550+
.imports()
1551+
.partition(|(_, kind)| matches!(kind, ItemKind::Instance(_)));
1552+
for (name, kind) in instances.into_iter().chain(rest) {
15491553
log::debug!("import `{name}` is being imported");
15501554
let index = self.import(state, name, aggregator.types(), kind);
15511555
encoded.insert(name, (kind.into(), index));

crates/wac-graph/tests/graphs/implicit-resource-import/encoded.wat

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,18 @@
66
)
77
(import "foo:dependency/types" (instance (;0;) (type 0)))
88
(alias export 0 "x" (type (;1;)))
9-
(import "x" (type (;2;) (eq 1)))
10-
(alias export 0 "x" (type (;3;)))
11-
(type (;4;)
9+
(type (;2;)
1210
(instance
13-
(alias outer 1 3 (type (;0;)))
11+
(alias outer 1 1 (type (;0;)))
1412
(export (;1;) "x" (type (eq 0)))
1513
(type (;2;) (own 1))
1614
(type (;3;) (func (result 2)))
1715
(export (;0;) "my-func" (func (type 3)))
1816
)
1917
)
20-
(import "foo:test-import/my-interface" (instance (;1;) (type 4)))
21-
(type (;5;)
18+
(import "foo:test-import/my-interface" (instance (;1;) (type 2)))
19+
(import "x" (type (;3;) (eq 1)))
20+
(type (;4;)
2221
(component
2322
(type (;0;)
2423
(instance
@@ -28,30 +27,30 @@
2827
(export (;0;) "foo:dependency/types" (instance (type 0)))
2928
)
3029
)
31-
(import "unlocked-dep=<test:bar>" (component (;0;) (type 5)))
30+
(import "unlocked-dep=<test:bar>" (component (;0;) (type 4)))
3231
(instance (;2;) (instantiate 0))
3332
(alias export 1 "my-func" (func (;0;)))
3433
(alias export 2 "foo:dependency/types" (instance (;3;)))
35-
(type (;6;)
34+
(type (;5;)
3635
(component
3736
(type (;0;)
3837
(instance
3938
(export (;0;) "x" (type (sub resource)))
4039
)
4140
)
4241
(import "foo:dependency/types" (instance (;0;) (type 0)))
43-
(alias outer 1 3 (type (;1;)))
42+
(alias outer 1 1 (type (;1;)))
4443
(import "x" (type (;2;) (eq 1)))
4544
(type (;3;) (own 2))
4645
(type (;4;) (func (result 3)))
4746
(import "my-func" (func (;0;) (type 4)))
4847
)
4948
)
50-
(import "unlocked-dep=<test:baz>" (component (;1;) (type 6)))
49+
(import "unlocked-dep=<test:baz>" (component (;1;) (type 5)))
5150
(instance (;4;) (instantiate 1
5251
(with "foo:dependency/types" (instance 3))
5352
(with "my-func" (func 0))
54-
(with "x" (type 2))
53+
(with "x" (type 3))
5554
)
5655
)
5756
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package test:cons;
2+
3+
world cons {
4+
import test:prod/defs;
5+
use test:prod/defs.{region};
6+
import make: func() -> region;
7+
export run: func() -> s32;
8+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package test:prod;
2+
3+
interface ext {
4+
record point {
5+
x: s32,
6+
}
7+
}
8+
9+
interface defs {
10+
use ext.{point};
11+
12+
record region {
13+
inner: point,
14+
}
15+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
(component
2+
(type (;0;)
3+
(instance
4+
(type (;0;) (record (field "x" s32)))
5+
(export (;1;) "point" (type (eq 0)))
6+
)
7+
)
8+
(import "test:prod/ext" (instance (;0;) (type 0)))
9+
(alias export 0 "point" (type (;1;)))
10+
(type (;2;)
11+
(instance
12+
(alias outer 1 1 (type (;0;)))
13+
(export (;1;) "point" (type (eq 0)))
14+
(type (;2;) (record (field "inner" 1)))
15+
(export (;3;) "region" (type (eq 2)))
16+
)
17+
)
18+
(import "test:prod/defs" (instance (;1;) (type 2)))
19+
(type (;3;) (record (field "inner" 1)))
20+
(import "region" (type (;4;) (eq 3)))
21+
(type (;5;)
22+
(component
23+
(type (;0;)
24+
(instance
25+
(type (;0;) (record (field "x" s32)))
26+
(export (;1;) "point" (type (eq 0)))
27+
)
28+
)
29+
(import "test:prod/ext" (instance (;0;) (type 0)))
30+
(alias export 0 "point" (type (;1;)))
31+
(type (;2;)
32+
(instance
33+
(alias outer 1 1 (type (;0;)))
34+
(export (;1;) "point" (type (eq 0)))
35+
(type (;2;) (record (field "inner" 1)))
36+
(export (;3;) "region" (type (eq 2)))
37+
)
38+
)
39+
(import "test:prod/defs" (instance (;1;) (type 2)))
40+
(type (;3;) (record (field "inner" 1)))
41+
(import "region" (type (;4;) (eq 3)))
42+
(type (;5;) (func (result 4)))
43+
(export (;0;) "make" (func (type 5)))
44+
(alias export 0 "point" (type (;6;)))
45+
(type (;7;)
46+
(instance
47+
(alias outer 1 6 (type (;0;)))
48+
(export (;1;) "point" (type (eq 0)))
49+
(type (;2;) (record (field "inner" 1)))
50+
(export (;3;) "region" (type (eq 2)))
51+
)
52+
)
53+
(export (;2;) "test:prod/defs" (instance (type 7)))
54+
)
55+
)
56+
(import "unlocked-dep=<test:prod>" (component (;0;) (type 5)))
57+
(instance (;2;) (instantiate 0
58+
(with "test:prod/ext" (instance 0))
59+
(with "test:prod/defs" (instance 1))
60+
(with "region" (type 4))
61+
)
62+
)
63+
(alias export 2 "test:prod/defs" (instance (;3;)))
64+
(alias export 2 "make" (func (;0;)))
65+
(type (;6;)
66+
(component
67+
(type (;0;)
68+
(instance
69+
(type (;0;) (record (field "x" s32)))
70+
(export (;1;) "point" (type (eq 0)))
71+
)
72+
)
73+
(import "test:prod/ext" (instance (;0;) (type 0)))
74+
(alias export 0 "point" (type (;1;)))
75+
(type (;2;)
76+
(instance
77+
(alias outer 1 1 (type (;0;)))
78+
(export (;1;) "point" (type (eq 0)))
79+
(type (;2;) (record (field "inner" 1)))
80+
(export (;3;) "region" (type (eq 2)))
81+
)
82+
)
83+
(import "test:prod/defs" (instance (;1;) (type 2)))
84+
(type (;3;) (record (field "inner" 1)))
85+
(import "region" (type (;4;) (eq 3)))
86+
(type (;5;) (func (result 4)))
87+
(import "make" (func (;0;) (type 5)))
88+
(type (;6;) (func (result s32)))
89+
(export (;1;) "run" (func (type 6)))
90+
)
91+
)
92+
(import "unlocked-dep=<test:cons>" (component (;1;) (type 6)))
93+
(instance (;4;) (instantiate 1
94+
(with "make" (func 0))
95+
(with "test:prod/defs" (instance 3))
96+
(with "test:prod/ext" (instance 0))
97+
(with "region" (type 4))
98+
)
99+
)
100+
(alias export 4 "run" (func (;1;)))
101+
(export (;2;) "run" (func 1))
102+
)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"packages": [
3+
{
4+
"name": "test:prod",
5+
"path": "prod.wit"
6+
},
7+
{
8+
"name": "test:cons",
9+
"path": "cons"
10+
}
11+
],
12+
"nodes": [
13+
{
14+
"type": "instantiation",
15+
"package": 0
16+
},
17+
{
18+
"type": "alias",
19+
"source": 0,
20+
"export": "test:prod/defs"
21+
},
22+
{
23+
"type": "alias",
24+
"source": 0,
25+
"export": "make"
26+
},
27+
{
28+
"type": "instantiation",
29+
"package": 1
30+
},
31+
{
32+
"type": "alias",
33+
"source": 3,
34+
"export": "run"
35+
}
36+
],
37+
"arguments": [
38+
{
39+
"source": 1,
40+
"target": 3,
41+
"name": "test:prod/defs"
42+
},
43+
{
44+
"source": 2,
45+
"target": 3,
46+
"name": "make"
47+
}
48+
],
49+
"exports": [
50+
{
51+
"node": 4,
52+
"name": "run"
53+
}
54+
]
55+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package test:prod;
2+
3+
interface ext {
4+
record point {
5+
x: s32,
6+
}
7+
}
8+
9+
interface defs {
10+
use ext.{point};
11+
12+
record region {
13+
inner: point,
14+
}
15+
}
16+
17+
world prod {
18+
import ext;
19+
export defs;
20+
use defs.{region};
21+
export make: func() -> region;
22+
}

0 commit comments

Comments
 (0)