Skip to content

Commit 85d10eb

Browse files
authored
fix(rust): no entry found for key (#1547)
* fix(rust): no entry found for key Fixes #1544 * fix(csharp): add issue-1544.wit fail list The test issue-1544.wit uses async features (futures) which C# doesn't support yet.
1 parent 9be80b5 commit 85d10eb

File tree

3 files changed

+72
-22
lines changed

3 files changed

+72
-22
lines changed

crates/rust/src/interface.rs

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -523,19 +523,19 @@ macro_rules! {macro_name} {{
523523
func_name: &str,
524524
payload_type: Option<&Type>,
525525
) {
526-
let payload_type = match payload_type {
527-
// Rust requires one-impl-per-type, so any `id` here is transformed
528-
// into its canonical representation using
529-
// `get_representative_type`. This ensures that type aliases, uses,
530-
// etc, all get canonicalized to the exact same ID regardless of
531-
// type structure.
532-
//
533-
// Note that `get_representative_type` maps ids-to-ids which is 95%
534-
// of what we want, but this additionally goes one layer further to
535-
// see if the final id is actually itself a typedef, which would
536-
// always be to a primitive, and then uses the primitive type
537-
// instead of the typedef to canonicalize with other streams/futures
538-
// using the primitive type.
526+
// Rust requires one-impl-per-type, so any `id` here is transformed
527+
// into its canonical representation using
528+
// `get_representative_type`. This ensures that type aliases, uses,
529+
// etc, all get canonicalized to the exact same ID regardless of
530+
// type structure. This canonical key is used for deduplication only.
531+
//
532+
// Note that `get_representative_type` maps ids-to-ids which is 95%
533+
// of what we want, but this additionally goes one layer further to
534+
// see if the final id is actually itself a typedef, which would
535+
// always be to a primitive, and then uses the primitive type
536+
// instead of the typedef to canonicalize with other streams/futures
537+
// using the primitive type.
538+
let canonical_payload = match payload_type {
539539
Some(Type::Id(id)) => {
540540
let id = self.r#gen.types.get_representative_type(*id);
541541
match self.resolve.types[id].kind {
@@ -545,20 +545,39 @@ macro_rules! {macro_name} {{
545545
}
546546
other => other.copied(),
547547
};
548+
{
549+
let map = match payload_for {
550+
PayloadFor::Future => &self.r#gen.future_payloads,
551+
PayloadFor::Stream => &self.r#gen.stream_payloads,
552+
};
553+
if map.contains_key(&canonical_payload) {
554+
return;
555+
}
556+
}
557+
558+
// Use the original (non-canonicalized) type for generating the
559+
// type name and code. The canonical representative may belong to
560+
// an interface that hasn't been processed yet (when world import
561+
// order differs from WIT definition order), which would cause
562+
// `path_to_interface` to panic. Since structurally equal types
563+
// resolve to the same Rust type, it doesn't matter which alias
564+
// path we use in the generated `impl`.
565+
let payload_type = match payload_type {
566+
Some(Type::Id(id)) => match self.resolve.types[*id].kind {
567+
TypeDefKind::Type(t) => Some(t),
568+
_ => Some(Type::Id(*id)),
569+
},
570+
other => other.copied(),
571+
};
548572
let payload_type = payload_type.as_ref();
549573
let name = match payload_type {
550574
Some(payload_type) => self.type_name_owned(payload_type),
551575
None => "()".into(),
552576
};
553-
let map = match payload_for {
554-
PayloadFor::Future => &mut self.r#gen.future_payloads,
555-
PayloadFor::Stream => &mut self.r#gen.stream_payloads,
577+
let ordinal = match payload_for {
578+
PayloadFor::Future => self.r#gen.future_payloads.len(),
579+
PayloadFor::Stream => self.r#gen.stream_payloads.len(),
556580
};
557-
558-
if map.contains_key(&payload_type.copied()) {
559-
return;
560-
}
561-
let ordinal = map.len();
562581
let async_support = self.r#gen.async_support_path();
563582
let (size, align) = if let Some(payload_type) = payload_type {
564583
(
@@ -704,7 +723,7 @@ pub mod vtable{ordinal} {{
704723
PayloadFor::Future => &mut self.r#gen.future_payloads,
705724
PayloadFor::Stream => &mut self.r#gen.stream_payloads,
706725
};
707-
map.insert(payload_type.copied(), code);
726+
map.insert(canonical_payload, code);
708727
}
709728

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

crates/test/src/csharp.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ impl LanguageMethods for Csharp {
5151
| "import-export-stream.wit"
5252
| "issue-1432.wit"
5353
| "issue-1433.wit"
54+
| "issue-1544.wit"
5455
| "future-same-type-different-names.wit"
5556
| "named-fixed-length-list.wit"
5657
)

tests/codegen/issue-1544.wit

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//@ async = true
2+
3+
// Regression: `generate_payloads` panics when `get_representative_type`
4+
// canonicalizes a payload type to one owned by an interface that hasn't
5+
// been imported yet (because world import order differs from WIT
6+
// definition order which determines type IDs).
7+
8+
package test:repro;
9+
10+
interface types {
11+
type t = u32;
12+
}
13+
14+
// Defined first (smaller type IDs), imported second.
15+
interface a {
16+
use types.{t};
17+
f: func() -> future<result<_, t>>;
18+
}
19+
20+
// Defined second (larger type IDs), imported first.
21+
interface b {
22+
use types.{t};
23+
f: func() -> future<result<_, t>>;
24+
}
25+
26+
world w {
27+
import types;
28+
import b;
29+
import a;
30+
}

0 commit comments

Comments
 (0)