From 41a75aad861427307608e04ac0f6011afa305d14 Mon Sep 17 00:00:00 2001 From: Yi LIU Date: Thu, 12 Feb 2026 20:00:02 +0800 Subject: [PATCH] fix: support named fixed-length list types in code generation The `define_type` function dispatches to `type_fixed_length_list` on the `InterfaceGenerator` trait for named fixed-length list types like `type my-array = list`. This makes `type_fixed_length_list` a required trait method (no default) and adds implementations in all backends: - Rust: generates `pub type Name = [T; N];` - Go: generates `type Name = [N]Type` - Markdown: delegates to `type_alias` - Moonbit: no-op (maps to `FixedArray[T]` natively) - C, C++, C#: explicit `todo!()` (not yet supported) Also adds a shared codegen test in `tests/codegen/named-fixed-length-list.wit` with expected failures for backends that don't yet support this. --- crates/c/src/lib.rs | 11 +++++++++++ crates/core/src/lib.rs | 5 ++++- crates/cpp/src/lib.rs | 11 +++++++++++ crates/csharp/src/interface.rs | 11 +++++++++++ crates/go/src/lib.rs | 7 +++++++ crates/markdown/src/lib.rs | 11 +++++++++++ crates/moonbit/src/lib.rs | 11 +++++++++++ crates/rust/src/interface.rs | 18 ++++++++++++++++++ crates/test/src/c.rs | 4 ++-- crates/test/src/cpp.rs | 4 ++-- crates/test/src/csharp.rs | 1 + crates/test/src/go.rs | 4 +++- crates/test/src/rust.rs | 5 +++++ tests/codegen/named-fixed-length-list.wit | 12 ++++++++++++ 14 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 tests/codegen/named-fixed-length-list.wit diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 1e40e41be..2d53d23dd 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -1735,6 +1735,17 @@ void __wasm_export_{ns}_{snake}_dtor({ns}_{snake}_t* arg) {{ self.finish_typedef_struct(id); } + fn type_fixed_length_list( + &mut self, + _id: TypeId, + _name: &str, + _ty: &Type, + _size: u32, + _docs: &Docs, + ) { + todo!("named fixed-length list types are not yet supported in the C backend") + } + fn type_future(&mut self, id: TypeId, _name: &str, _ty: &Option, docs: &Docs) { self.src.h_defs("\n"); self.docs(docs, SourceType::HDefs); diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index c0d118a6f..15a2ad824 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -165,6 +165,7 @@ pub trait InterfaceGenerator<'a> { fn type_enum(&mut self, id: TypeId, name: &str, enum_: &Enum, docs: &Docs); fn type_alias(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); fn type_list(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); + fn type_fixed_length_list(&mut self, id: TypeId, name: &str, ty: &Type, size: u32, docs: &Docs); fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); fn type_future(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs); fn type_stream(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs); @@ -199,7 +200,9 @@ where TypeDefKind::Future(t) => generator.type_future(id, name, t, &ty.docs), TypeDefKind::Stream(t) => generator.type_stream(id, name, t, &ty.docs), TypeDefKind::Handle(_) => panic!("handle types do not require definition"), - TypeDefKind::FixedLengthList(..) => todo!(), + TypeDefKind::FixedLengthList(t, size) => { + generator.type_fixed_length_list(id, name, t, *size, &ty.docs) + } TypeDefKind::Map(..) => todo!(), TypeDefKind::Unknown => unreachable!(), } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 183609237..271c4fe30 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2221,6 +2221,17 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> // nothing to do here } + fn type_fixed_length_list( + &mut self, + _id: TypeId, + _name: &str, + _ty: &wit_bindgen_core::wit_parser::Type, + _size: u32, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + todo!("named fixed-length list types are not yet supported in the C++ backend") + } + fn type_builtin( &mut self, _id: TypeId, diff --git a/crates/csharp/src/interface.rs b/crates/csharp/src/interface.rs index adf5d383b..87b94ef1f 100644 --- a/crates/csharp/src/interface.rs +++ b/crates/csharp/src/interface.rs @@ -1543,6 +1543,17 @@ impl<'a> CoreInterfaceGenerator<'a> for InterfaceGenerator<'a> { self.type_name(&Type::Id(id)); } + fn type_fixed_length_list( + &mut self, + _id: TypeId, + _name: &str, + _ty: &Type, + _size: u32, + _docs: &Docs, + ) { + todo!("named fixed-length list types are not yet supported in the C# backend") + } + fn type_builtin(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { unimplemented!(); } diff --git a/crates/go/src/lib.rs b/crates/go/src/lib.rs index 2c61536d3..cef5d3869 100644 --- a/crates/go/src/lib.rs +++ b/crates/go/src/lib.rs @@ -2873,6 +2873,13 @@ const ( uwriteln!(self.src, "{docs}type {name} = []{ty}"); } + fn type_fixed_length_list(&mut self, _: TypeId, name: &str, ty: &Type, size: u32, docs: &Docs) { + let name = name.to_upper_camel_case(); + let ty = self.type_name(self.resolve, *ty); + let docs = format_docs(docs); + uwriteln!(self.src, "{docs}type {name} = [{size}]{ty}"); + } + fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { _ = (id, name, ty, docs); todo!() diff --git a/crates/markdown/src/lib.rs b/crates/markdown/src/lib.rs index 8e05696d0..cfc2ed3d5 100644 --- a/crates/markdown/src/lib.rs +++ b/crates/markdown/src/lib.rs @@ -650,6 +650,17 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { self.type_alias(id, name, &Type::Id(id), docs); } + fn type_fixed_length_list( + &mut self, + id: TypeId, + name: &str, + _ty: &Type, + _size: u32, + docs: &Docs, + ) { + self.type_alias(id, name, &Type::Id(id), docs); + } + fn type_future(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { _ = (id, name, ty, docs); todo!() diff --git a/crates/moonbit/src/lib.rs b/crates/moonbit/src/lib.rs index 656b0fea3..3f0d13b0a 100644 --- a/crates/moonbit/src/lib.rs +++ b/crates/moonbit/src/lib.rs @@ -1319,6 +1319,17 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { // Not needed. They will become `Array[T]` or `FixedArray[T]` in Moonbit } + fn type_fixed_length_list( + &mut self, + _id: TypeId, + _name: &str, + _ty: &Type, + _size: u32, + _docs: &Docs, + ) { + // Not needed. They will become `FixedArray[T]` in Moonbit + } + fn type_future(&mut self, _id: TypeId, _name: &str, _ty: &Option, _docs: &Docs) { unimplemented!() // Not needed } diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 8ebc42aca..7903d41c8 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -2889,6 +2889,24 @@ impl<'a> {camel}Borrow<'a>{{ } } + fn type_fixed_length_list( + &mut self, + id: TypeId, + _name: &str, + ty: &Type, + size: u32, + docs: &Docs, + ) { + for (name, mode) in self.modes_of(id) { + self.rustdoc(docs); + self.push_str(&format!("pub type {name}")); + self.print_generics(mode.lifetime); + self.push_str(" = ["); + self.print_ty(ty, mode); + self.push_str(&format!("; {size}];\n")); + } + } + fn type_future(&mut self, _id: TypeId, name: &str, ty: &Option, docs: &Docs) { let async_support = self.r#gen.async_support_path(); let mode = TypeMode { diff --git a/crates/test/src/c.rs b/crates/test/src/c.rs index 3c42aa624..a34f2fc55 100644 --- a/crates/test/src/c.rs +++ b/crates/test/src/c.rs @@ -52,11 +52,11 @@ impl LanguageMethods for C { fn should_fail_verify( &self, - _name: &str, + name: &str, config: &crate::config::WitConfig, _args: &[String], ) -> bool { - config.error_context + config.error_context || name.starts_with("named-fixed-length-list.wit") } fn codegen_test_variants(&self) -> &[(&str, &[&str])] { diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index ff10eeb12..39223fd19 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -41,11 +41,11 @@ impl LanguageMethods for Cpp { fn should_fail_verify( &self, - _name: &str, + name: &str, config: &crate::config::WitConfig, _args: &[String], ) -> bool { - config.async_ + config.async_ || name == "named-fixed-length-list.wit" } fn prepare(&self, runner: &mut Runner) -> anyhow::Result<()> { diff --git a/crates/test/src/csharp.rs b/crates/test/src/csharp.rs index a26eddac7..e5a287388 100644 --- a/crates/test/src/csharp.rs +++ b/crates/test/src/csharp.rs @@ -52,6 +52,7 @@ impl LanguageMethods for Csharp { | "issue-1432.wit" | "issue-1433.wit" | "future-same-type-different-names.wit" + | "named-fixed-length-list.wit" ) } diff --git a/crates/test/src/go.rs b/crates/test/src/go.rs index 0f63bb0b4..ef1bf0b58 100644 --- a/crates/test/src/go.rs +++ b/crates/test/src/go.rs @@ -27,7 +27,9 @@ impl LanguageMethods for Go { // patch](https://github.com/dicej/go/commit/40fc123d5bce6448fc4e4601fd33bad4250b36a5). // Once we upstream something equivalent, we can remove the ` || name == // "async-trait-function.wit"` here. - config.error_context || name == "async-trait-function.wit" + config.error_context + || name == "async-trait-function.wit" + || name == "named-fixed-length-list.wit" } fn default_bindgen_args_for_codegen(&self) -> &[&str] { diff --git a/crates/test/src/rust.rs b/crates/test/src/rust.rs index d7d3fa389..56ab21b0c 100644 --- a/crates/test/src/rust.rs +++ b/crates/test/src/rust.rs @@ -76,6 +76,11 @@ impl LanguageMethods for Rust { return true; } + // Named fixed-length lists don't work with async yet. + if name == "named-fixed-length-list.wit-async" { + return true; + } + false } diff --git a/tests/codegen/named-fixed-length-list.wit b/tests/codegen/named-fixed-length-list.wit new file mode 100644 index 000000000..395291c25 --- /dev/null +++ b/tests/codegen/named-fixed-length-list.wit @@ -0,0 +1,12 @@ +package test:named-fll; + +interface types { + type my-array = list; + type byte-buf = list; + use-my-array: func(a: my-array) -> my-array; +} + +world test { + import types; + export types; +}