From f632eb508efb117aca3417acb78ba8586e7d5ada Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 7 Jan 2025 08:36:35 -0800 Subject: [PATCH 1/5] Fix mismatched tuple ABIs in Rust This fixes the mistaken assumption that the tuple ABI in Rust is the same as the component model ABI and generates different code for lifting/lowering lists. Closes #1112 --- crates/core/src/types.rs | 5 +++++ crates/rust/src/bindgen.rs | 7 ++++++- tests/runtime/lists.rs | 4 ++++ tests/runtime/lists/wasm.c | 25 +++++++++++++++++++++++++ tests/runtime/lists/wasm.rs | 5 +++++ tests/runtime/lists/world.wit | 1 + 6 files changed, 46 insertions(+), 1 deletion(-) diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs index a0862f5e8..5aaa71607 100644 --- a/crates/core/src/types.rs +++ b/crates/core/src/types.rs @@ -30,6 +30,9 @@ pub struct TypeInfo { /// Whether this type (transitively) has a list (or string). pub has_list: bool, + /// Whether this type (transitively) has a tuple. + pub has_tuple: bool, + /// Whether this type (transitively) has a resource (or handle). pub has_resource: bool, @@ -46,6 +49,7 @@ impl std::ops::BitOrAssign for TypeInfo { self.owned |= rhs.owned; self.error |= rhs.error; self.has_list |= rhs.has_list; + self.has_tuple |= rhs.has_tuple; self.has_resource |= rhs.has_resource; self.has_borrow_handle |= rhs.has_borrow_handle; self.has_own_handle |= rhs.has_own_handle; @@ -171,6 +175,7 @@ impl Types { for ty in t.types.iter() { info |= self.type_info(resolve, ty); } + info.has_tuple = true; } TypeDefKind::Flags(_) => {} TypeDefKind::Enum(_) => {} diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 1e9dc9ff1..e0b21f4dc 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -292,7 +292,12 @@ impl Bindgen for FunctionBindgen<'_, '_> { return false; } match ty { - Type::Id(id) => !self.gen.gen.types.get(*id).has_resource, + // Note that tuples in Rust are not ABI-compatible with component + // model tuples, so those are exempted here from canonical lists. + Type::Id(id) => { + let info = self.gen.gen.types.get(*id); + !info.has_resource && !info.has_tuple + } _ => true, } } diff --git a/tests/runtime/lists.rs b/tests/runtime/lists.rs index 114ff1724..f5761ccfc 100644 --- a/tests/runtime/lists.rs +++ b/tests/runtime/lists.rs @@ -47,6 +47,10 @@ impl test::lists::test::Host for MyImports { assert_eq!(ptr[1][0], "baz"); } + fn list_param5(&mut self, ptr: Vec<(u8, u32, u8)>) { + assert_eq!(ptr, [(1, 2, 3), (4, 5, 6)]); + } + fn list_result(&mut self) -> Vec { vec![1, 2, 3, 4, 5] } diff --git a/tests/runtime/lists/wasm.c b/tests/runtime/lists/wasm.c index 253dc586d..7787984a1 100644 --- a/tests/runtime/lists/wasm.c +++ b/tests/runtime/lists/wasm.c @@ -78,6 +78,20 @@ void exports_lists_test_imports() { test_lists_test_list_param4(&a); } + { + lists_tuple3_u8_u32_u8_t data[2]; + data[0].f0 = 1; + data[0].f1 = 2; + data[0].f2 = 3; + data[1].f0 = 4; + data[1].f1 = 5; + data[1].f2 = 6; + lists_list_tuple3_u8_u32_u8_t a; + a.len = 2; + a.ptr = data; + test_lists_test_list_param5(&a); + } + { lists_list_u8_t a; test_lists_test_list_result(&a); @@ -301,6 +315,17 @@ void exports_test_lists_test_list_param4(lists_list_list_string_t *a) { lists_list_list_string_free(a); } +void exports_test_lists_test_list_param5(lists_list_tuple3_u8_u32_u8_t *a) { + assert(a->len == 2); + assert(a->ptr[0].f0 == 1); + assert(a->ptr[0].f1 == 2); + assert(a->ptr[0].f2 == 3); + assert(a->ptr[1].f0 == 4); + assert(a->ptr[1].f1 == 5); + assert(a->ptr[1].f2 == 6); + lists_list_tuple3_u8_u32_u8_free(a); +} + void exports_test_lists_test_list_result(lists_list_u8_t *ret0) { ret0->ptr = (uint8_t *) malloc(5); ret0->len = 5; diff --git a/tests/runtime/lists/wasm.rs b/tests/runtime/lists/wasm.rs index 8107eab29..d13eb5c58 100644 --- a/tests/runtime/lists/wasm.rs +++ b/tests/runtime/lists/wasm.rs @@ -28,6 +28,7 @@ impl Guest for Component { vec!["foo".to_owned(), "bar".to_owned()], vec!["baz".to_owned()], ]); + list_param5(&[(1, 2, 3), (4, 5, 6)]); assert_eq!(list_result(), [1, 2, 3, 4, 5]); assert_eq!(list_result2(), "hello!"); assert_eq!(list_result3(), ["hello,", "world!"]); @@ -109,6 +110,10 @@ impl exports::test::lists::test::Guest for Component { assert_eq!(ptr[1][0], "baz"); } + fn list_param5(ptr: Vec<(u8, u32, u8)>) { + assert_eq!(ptr, [(1, 2, 3), (4, 5, 6)]); + } + fn list_result() -> Vec { vec![1, 2, 3, 4, 5] } diff --git a/tests/runtime/lists/world.wit b/tests/runtime/lists/world.wit index 3be347a41..4e5730875 100644 --- a/tests/runtime/lists/world.wit +++ b/tests/runtime/lists/world.wit @@ -10,6 +10,7 @@ interface test { list-param2: func(a: string); list-param3: func(a: list); list-param4: func(a: list>); + list-param5: func(a: list>); list-result: func() -> list; list-result2: func() -> string; list-result3: func() -> list; From 3f2933ad25460b922c73ef1a208168ea24cc33c0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 7 Jan 2025 08:45:23 -0800 Subject: [PATCH 2/5] Attempt a java test --- .../runtime/lists/wit_exports_test_lists_TestImpl.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/runtime/lists/wit_exports_test_lists_TestImpl.java b/tests/runtime/lists/wit_exports_test_lists_TestImpl.java index 492760aa8..5020d8bce 100644 --- a/tests/runtime/lists/wit_exports_test_lists_TestImpl.java +++ b/tests/runtime/lists/wit_exports_test_lists_TestImpl.java @@ -52,6 +52,16 @@ public static void listParam4(ArrayList> a) { expect(a.get(1).get(0).equals("baz")); } + public static void listParam5(ArrayList> a) { + expect(a.size() == 2); + expect(a.get(0).f0 == 1); + expect(a.get(0).f1 == 2); + expect(a.get(0).f2 == 3); + expect(a.get(1).f0 == 4); + expect(a.get(1).f1 == 5); + expect(a.get(1).f2 == 6); + } + public static byte[] listResult() { return new byte[] { (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5 }; } From 5a609394466b165c8848b045837bb6e39ae3c809 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 7 Jan 2025 08:48:46 -0800 Subject: [PATCH 3/5] Add a go test --- tests/runtime/lists/wasm.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/runtime/lists/wasm.go b/tests/runtime/lists/wasm.go index d4f96a15b..119b3d0f2 100644 --- a/tests/runtime/lists/wasm.go +++ b/tests/runtime/lists/wasm.go @@ -200,6 +200,18 @@ func (i ListImpl) ListParam4(a [][]string) { } } +func (i ListImpl) ListParam5(a []ExportsTestListsTestTuple3U8U32U8T) { + if len(a) != 2 { + panic("ListParam5") + } + if a[0].F0 != 1 || a[0].F1 != 2 || a[0].F2 != 3 { + panic("ListParam5") + } + if a[1].F0 != 4 || a[1].F1 != 5 || a[1].F2 != 6 { + panic("ListParam5") + } +} + func (i ListImpl) ListResult() []uint8 { return []uint8{1, 2, 3, 4, 5} } From 8cdba26854019ebbca4608b46752c987c3f1b1bb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 7 Jan 2025 08:59:16 -0800 Subject: [PATCH 4/5] Fix java test --- tests/runtime/lists/wit_exports_test_lists_TestImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/runtime/lists/wit_exports_test_lists_TestImpl.java b/tests/runtime/lists/wit_exports_test_lists_TestImpl.java index 5020d8bce..58ad90677 100644 --- a/tests/runtime/lists/wit_exports_test_lists_TestImpl.java +++ b/tests/runtime/lists/wit_exports_test_lists_TestImpl.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import wit.worlds.Lists.Tuple2; +import wit.worlds.Lists.Tuple3; public class TestImpl { public static void emptyListParam(byte[] a) { @@ -52,7 +53,7 @@ public static void listParam4(ArrayList> a) { expect(a.get(1).get(0).equals("baz")); } - public static void listParam5(ArrayList> a) { + public static void listParam5(ArrayList> a) { expect(a.size() == 2); expect(a.get(0).f0 == 1); expect(a.get(0).f1 == 2); From 05d3b136491ca08b22478247d0fd2ac2eac5af42 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 7 Jan 2025 10:06:36 -0800 Subject: [PATCH 5/5] Attempt to write C# --- tests/runtime/lists/wasm.cs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/runtime/lists/wasm.cs b/tests/runtime/lists/wasm.cs index adefc9577..fd929c7ba 100644 --- a/tests/runtime/lists/wasm.cs +++ b/tests/runtime/lists/wasm.cs @@ -18,7 +18,7 @@ public static void TestImports() TestInterop.EmptyListParam(new byte[0]); TestInterop.EmptyStringParam(""); - + { byte[] result = TestInterop.EmptyListResult(); Debug.Assert(result.Length == 0); @@ -62,7 +62,7 @@ public static void TestImports() Console.WriteLine(result); Debug.Assert(result == "hello!"); } - + { List result = TestInterop.ListResult3(); Debug.Assert(result.Count() == 2); @@ -103,7 +103,7 @@ public static void TestImports() Debug.Assert(u.Length == 2, $"u.Length {u.Length}"); Debug.Assert(u[0] == ushort.MinValue, $"u[0] == {u[0]}"); Debug.Assert(u[1] == ushort.MaxValue, $"u[1] == {u[1]}"); - + Debug.Assert(s.Length == 2); Console.WriteLine(s[0]); Console.WriteLine(s[1]); @@ -112,7 +112,7 @@ public static void TestImports() { var (u, s) = TestInterop.ListMinmax32( - new uint[] { uint.MinValue, uint.MaxValue }, + new uint[] { uint.MinValue, uint.MaxValue }, new int[] { int.MinValue, int.MaxValue } ); @@ -130,7 +130,7 @@ public static void TestImports() Debug.Assert(s.Length == 2 && s[0] == long.MinValue && s[1] == long.MaxValue); } - + { var (u, s) = TestInterop.ListMinmaxFloat( new float[] { @@ -220,6 +220,17 @@ public static void ListParam4(List> a) Debug.Assert(a[1][0].Equals("baz")); } + public static void ListParam5(List<(byte, uint, byte)> a) + { + Debug.Assert(a.Count() == 2); + Debug.Assert(a[0].Item1 == 1); + Debug.Assert(a[0].Item2 == 2); + Debug.Assert(a[0].Item3 == 3); + Debug.Assert(a[1].Item1 == 4); + Debug.Assert(a[1].Item2 == 5); + Debug.Assert(a[1].Item3 == 6); + } + public static byte[] ListResult() { return new byte[] { (byte)1, (byte)2, (byte)3, (byte)4, (byte)5 };