Skip to content

Commit 4cac3fc

Browse files
wit-bindgen: generate only one StreamPayload or FuturePayload implementation per type alias set
WIT 'use' statements translate to Rust type aliases in the generated Rust bindings. Since these aliases may be located at different module paths, creating a StreamPayload or FuturePayload implementation for more than one of these paths will cause the Rust compiler to complain about conflicting trait implementations for the same type. This commit allows only one StreamPayload or FuturePayload implementation to be generated for a WIT type which will get transpiled into a Rust type alias, and it records mappings of the WIT type to the canonical module path to the type alias, which allows equivalent type aliases to reuse the implementation. Fixes issue 1432
1 parent 88533c1 commit 4cac3fc

File tree

7 files changed

+123
-3
lines changed

7 files changed

+123
-3
lines changed

crates/rust/src/bindgen.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,13 +427,31 @@ impl Bindgen for FunctionBindgen<'_, '_> {
427427
Instruction::FutureLift { payload, .. } => {
428428
let async_support = self.r#gen.r#gen.async_support_path();
429429
let op = &operands[0];
430-
let name = payload
430+
let mut name = payload
431431
.as_ref()
432432
.map(|ty| {
433433
self.r#gen
434434
.type_name_owned_with_id(ty, Identifier::StreamOrFuturePayload)
435435
})
436436
.unwrap_or_else(|| "()".into());
437+
438+
// If the payload type corresponds to a Rust type alias,
439+
// use the path to the canonical type alias for which the
440+
// single FuturePayload implementation was generated, as the
441+
// key to future_payloads
442+
if let Some(Type::Id(id)) = payload {
443+
let dealiased_id = dealias(resolve, *id);
444+
if dealiased_id != *id {
445+
name = self
446+
.r#gen
447+
.r#gen
448+
.aliased_future_payloads
449+
.get(&dealiased_id)
450+
.unwrap()
451+
.clone();
452+
}
453+
};
454+
437455
let ordinal = self
438456
.r#gen
439457
.r#gen
@@ -455,13 +473,31 @@ impl Bindgen for FunctionBindgen<'_, '_> {
455473
Instruction::StreamLift { payload, .. } => {
456474
let async_support = self.r#gen.r#gen.async_support_path();
457475
let op = &operands[0];
458-
let name = payload
476+
let mut name = payload
459477
.as_ref()
460478
.map(|ty| {
461479
self.r#gen
462480
.type_name_owned_with_id(ty, Identifier::StreamOrFuturePayload)
463481
})
464482
.unwrap_or_else(|| "()".into());
483+
484+
// If the payload type corresponds to a Rust type alias,
485+
// use the path to the canonical type alias for which the
486+
// single StreamPayload implementation was generated, as the
487+
// key to stream_payloads
488+
if let Some(Type::Id(id)) = payload {
489+
let dealiased_id = dealias(resolve, *id);
490+
if dealiased_id != *id {
491+
name = self
492+
.r#gen
493+
.r#gen
494+
.aliased_stream_payloads
495+
.get(&dealiased_id)
496+
.unwrap()
497+
.clone();
498+
}
499+
};
500+
465501
let ordinal = self
466502
.r#gen
467503
.r#gen

crates/rust/src/interface.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,27 @@ macro_rules! {macro_name} {{
528528
} else {
529529
"()".into()
530530
};
531+
532+
let dealiased_id = match payload_type {
533+
Some(Type::Id(id)) => Some(dealias(self.resolve, *id)),
534+
_ => None,
535+
};
536+
537+
// If the payload type corresponds to a Rust type alias,
538+
// only one StreamPayload/FuturePayload implementation should be
539+
// generated, therefore if one already exists, return
540+
if let Some(dealiased_id) = dealiased_id
541+
&& payload_type != Some(&Type::Id(dealiased_id))
542+
{
543+
let alias_map = match payload_for {
544+
PayloadFor::Future => &mut self.r#gen.aliased_future_payloads,
545+
PayloadFor::Stream => &mut self.r#gen.aliased_stream_payloads,
546+
};
547+
if alias_map.contains_key(&dealiased_id) {
548+
return;
549+
}
550+
}
551+
531552
let map = match payload_for {
532553
PayloadFor::Future => &mut self.r#gen.future_payloads,
533554
PayloadFor::Stream => &mut self.r#gen.stream_payloads,
@@ -682,7 +703,21 @@ pub mod vtable{ordinal} {{
682703
PayloadFor::Future => &mut self.r#gen.future_payloads,
683704
PayloadFor::Stream => &mut self.r#gen.stream_payloads,
684705
};
685-
map.insert(name, code);
706+
map.insert(name.clone(), code);
707+
708+
// If the payload type corresponds to a Rust type alias,
709+
// record the module path/type name under which the
710+
// StreamPayload/FuturePayload implementation was generated,
711+
// so equivalent types can reuse the implementation
712+
if let Some(dealiased_id) = dealiased_id
713+
&& payload_type != Some(&Type::Id(dealiased_id))
714+
{
715+
let alias_map = match payload_for {
716+
PayloadFor::Future => &mut self.r#gen.aliased_future_payloads,
717+
PayloadFor::Stream => &mut self.r#gen.aliased_stream_payloads,
718+
};
719+
alias_map.insert(dealiased_id, name);
720+
}
686721
}
687722

688723
fn generate_guest_import(&mut self, func: &Function, interface: Option<&WorldKey>) {

crates/rust/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ pub struct RustWasm {
5353

5454
future_payloads: IndexMap<String, String>,
5555
stream_payloads: IndexMap<String, String>,
56+
57+
// Stores the canonical type names with module paths for WIT types which generate Rust type aliases
58+
// (and should therefore only generate a single StreamPayload or FuturePayload implementation)
59+
//
60+
// The values of these maps are used as keys to the future_payloads, and stream_payloads maps
61+
aliased_future_payloads: IndexMap<TypeId, String>,
62+
aliased_stream_payloads: IndexMap<TypeId, String>,
5663
}
5764

5865
#[derive(Default)]

crates/test/src/cpp.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ impl LanguageMethods for Cpp {
4949
"async-trait-function.wit"
5050
| "error-context.wit"
5151
| "futures.wit"
52+
| "import-export-future.wit"
53+
| "import-export-stream.wit"
5254
| "resources-with-futures.wit"
5355
| "resources-with-streams.wit"
5456
| "streams.wit" => true,

crates/test/src/csharp.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ impl LanguageMethods for Csharp {
4949
| "streams.wit"
5050
| "error-context.wit"
5151
| "resource-fallible-constructor.wit"
52+
| "import-export-future.wit"
53+
| "import-export-stream.wit"
5254
)
5355
}
5456

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ async = true
2+
3+
package a:b;
4+
5+
world w {
6+
import i;
7+
export i;
8+
}
9+
10+
interface i {
11+
use t.{r};
12+
f: func(p: future<r>);
13+
}
14+
15+
interface t {
16+
record r {
17+
x: u32,
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ async = true
2+
3+
package a:b;
4+
5+
world w {
6+
import i;
7+
export i;
8+
}
9+
10+
interface i {
11+
use t.{r};
12+
f: func(p: stream<r>);
13+
}
14+
15+
interface t {
16+
record r {
17+
x: u32,
18+
}
19+
}

0 commit comments

Comments
 (0)