From da177159cab3a6833a9b0cfa866ffbf971e1a46f Mon Sep 17 00:00:00 2001 From: Demetrius Kanios Date: Fri, 6 Mar 2026 11:39:11 -0800 Subject: [PATCH 1/6] Basic setup of `wit-bindgen-d` --- Cargo.lock | 15 + Cargo.toml | 6 +- crates/d/Cargo.toml | 33 ++ crates/d/LICENSE-APACHE | 1 + .../d/LICENSE-Apache-2.0_WITH_LLVM-exception | 1 + crates/d/LICENSE-MIT | 1 + crates/d/README.md | 17 + crates/d/src/lib.rs | 327 ++++++++++++++++++ src/bin/wit-bindgen.rs | 11 + 9 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 crates/d/Cargo.toml create mode 120000 crates/d/LICENSE-APACHE create mode 120000 crates/d/LICENSE-Apache-2.0_WITH_LLVM-exception create mode 120000 crates/d/LICENSE-MIT create mode 100644 crates/d/README.md create mode 100644 crates/d/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index afc846c84..9d7f3eed6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1496,6 +1496,7 @@ dependencies = [ "wit-bindgen-core", "wit-bindgen-cpp", "wit-bindgen-csharp", + "wit-bindgen-d", "wit-bindgen-go", "wit-bindgen-markdown", "wit-bindgen-moonbit", @@ -1544,6 +1545,20 @@ dependencies = [ "wit-parser", ] +[[package]] +name = "wit-bindgen-d" +version = "0.53.1" +dependencies = [ + "anyhow", + "clap", + "heck", + "indexmap", + "wasm-encoder 0.245.1", + "wasm-metadata 0.245.1", + "wit-bindgen-core", + "wit-component", +] + [[package]] name = "wit-bindgen-go" version = "0.53.1" diff --git a/Cargo.toml b/Cargo.toml index 86e224127..489ce7fa8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ wit-bindgen-csharp = { path = 'crates/csharp', version = '0.53.1' } wit-bindgen-markdown = { path = 'crates/markdown', version = '0.53.1' } wit-bindgen-moonbit = { path = 'crates/moonbit', version = '0.53.1' } wit-bindgen-go = { path = 'crates/go', version = '0.53.1' } +wit-bindgen-d = { path = 'crates/d', version = '0.53.1' } wit-bindgen = { path = 'crates/guest-rust', version = '0.53.1', default-features = false } wit-bindgen-test = { path = 'crates/test', version = '0.53.1' } @@ -94,6 +95,7 @@ wit-bindgen-markdown = { workspace = true, features = ['clap'], optional = true wit-bindgen-moonbit = { workspace = true, features = ['clap'], optional = true } wit-bindgen-csharp = { workspace = true, features = ['clap'], optional = true } wit-bindgen-go = { workspace = true, features = ['clap'], optional = true } +wit-bindgen-d = { workspace = true, features = ['clap'], optional = true } wit-bindgen-test = { workspace = true } wit-component = { workspace = true } wasm-encoder = { workspace = true } @@ -108,7 +110,8 @@ default = [ 'csharp', 'cpp', 'moonbit', - 'async', + 'd', + 'async' ] c = ['dep:wit-bindgen-c'] cpp = ['dep:wit-bindgen-cpp'] @@ -118,4 +121,5 @@ go = ['dep:wit-bindgen-go'] csharp = ['dep:wit-bindgen-csharp'] csharp-mono = ['csharp'] moonbit = ['dep:wit-bindgen-moonbit'] +d = ['dep:wit-bindgen-d'] async = [] diff --git a/crates/d/Cargo.toml b/crates/d/Cargo.toml new file mode 100644 index 000000000..e90a308c9 --- /dev/null +++ b/crates/d/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "wit-bindgen-d" +authors = ["Demetrius Kanios "] +version = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } +rust-version = { workspace = true } +homepage = 'https://github.com/bytecodealliance/wit-bindgen' +description = """ +D bindings generator for WIT and the component model, typically used through the +`wit-bindgen-cli` crate. +""" + +[lints] +workspace = true + +[lib] +doctest = false +test = false + +[dependencies] +wit-bindgen-core = { workspace = true } +wit-component = { workspace = true } +wasm-encoder = { workspace = true } +wasm-metadata = { workspace = true } +anyhow = { workspace = true } +heck = { workspace = true } +clap = { workspace = true, optional = true } +indexmap = { workspace = true } + +[features] +clap = ['dep:clap', 'wit-bindgen-core/clap'] diff --git a/crates/d/LICENSE-APACHE b/crates/d/LICENSE-APACHE new file mode 120000 index 000000000..1cd601d0a --- /dev/null +++ b/crates/d/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/crates/d/LICENSE-Apache-2.0_WITH_LLVM-exception b/crates/d/LICENSE-Apache-2.0_WITH_LLVM-exception new file mode 120000 index 000000000..3a28a354e --- /dev/null +++ b/crates/d/LICENSE-Apache-2.0_WITH_LLVM-exception @@ -0,0 +1 @@ +../../LICENSE-Apache-2.0_WITH_LLVM-exception \ No newline at end of file diff --git a/crates/d/LICENSE-MIT b/crates/d/LICENSE-MIT new file mode 120000 index 000000000..b2cfbdc7b --- /dev/null +++ b/crates/d/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT \ No newline at end of file diff --git a/crates/d/README.md b/crates/d/README.md new file mode 100644 index 000000000..ebb79098b --- /dev/null +++ b/crates/d/README.md @@ -0,0 +1,17 @@ +# `wit-bindgen` D Bindings Generator + +This tool generates [D](https://dlang.org) bindings for a chosen WIT world. + +## Usage + +To generate bindings with this crate, issue the `d` subcommand to `wit-bindgen`: + +```bash +$ wit-bindgen d [OPTIONS] +``` + +See the output of `wit-bindgen help d` for available options. + +------- + +TODO: Flesh out fuller docs (ownership, more usage, examples, etc.) diff --git a/crates/d/src/lib.rs b/crates/d/src/lib.rs new file mode 100644 index 000000000..162728983 --- /dev/null +++ b/crates/d/src/lib.rs @@ -0,0 +1,327 @@ +use anyhow::Result; +use heck::*; +use std::collections::{BTreeSet, HashMap}; +use std::path::PathBuf; +use wit_bindgen_core::{Files, Source, WorldGenerator, wit_parser::*}; + +#[derive(Default)] +struct D { + world_src: Source, + opts: Opts, + + cur_world_fqn: String, + interfaces: HashMap, +} + +#[derive(Default)] +struct InterfaceSource { + fqn: String, + src: Source, + imported: bool, + exported: bool, +} + +#[derive(Default, Debug, Clone)] +#[cfg_attr(feature = "clap", derive(clap::Parser))] +pub struct Opts { + /// Where to place output files + #[cfg_attr(feature = "clap", arg(skip))] + out_dir: Option, +} + +impl Opts { + pub fn build(mut self, out_dir: Option<&PathBuf>) -> Box { + let mut r = D::default(); + self.out_dir = out_dir.cloned(); + r.opts = self.clone(); + Box::new(r) + } +} + +fn get_package_fqn(id: PackageId, resolve: &Resolve) -> String { + let mut ns = String::new(); + + let pkg = &resolve.packages[id]; + ns.push_str("wit."); + ns.push_str(&pkg.name.namespace.to_snake_case()); + ns.push_str("."); + ns.push_str(&pkg.name.name.to_snake_case()); + ns.push_str("."); + let pkg_has_multiple_versions = resolve.packages.iter().any(|(_, p)| { + p.name.namespace == pkg.name.namespace + && p.name.name == pkg.name.name + && p.name.version != pkg.name.version + }); + if pkg_has_multiple_versions { + if let Some(version) = &pkg.name.version { + let version = version + .to_string() + .replace('.', "_") + .replace('-', "_") + .replace('+', "_"); + ns.push_str(&version); + ns.push_str("."); + } + } + ns +} + +fn get_interface_fqn( + interface_id: &WorldKey, + cur_world_fqn: &String, + resolve: &Resolve, + is_export: bool, +) -> String { + let mut ns = String::new(); + match interface_id { + WorldKey::Name(name) => { + ns.push_str(cur_world_fqn); + if is_export { + ns.push_str(".exports") + } else { + ns.push_str(".imports") + } + ns.push_str("."); + ns.push_str(&name.to_snake_case()) + } + WorldKey::Interface(id) => { + let iface = &resolve.interfaces[*id]; + ns.push_str(&get_package_fqn(iface.package.unwrap(), resolve)); + ns.push_str(&iface.name.as_ref().unwrap().to_snake_case()) + } + } + ns +} + +fn get_world_fqn(id: WorldId, resolve: &Resolve) -> String { + let mut ns = String::new(); + + let world = &resolve.worlds[id]; + ns.push_str(&get_package_fqn(world.package.unwrap(), resolve)); + ns.push_str(&world.name.to_snake_case()); + ns +} + +impl D { + fn prepare_interface_bindings( + &self, + id: InterfaceId, + fqn: &String, + cur_world_fqn: &String, + resolve: &Resolve, + ) -> Source { + let mut src = Source::default(); + let interface = &resolve.interfaces[id]; + + match &interface.docs.contents { + Some(docs) => src.push_str(&format!("/++\n{docs}\n+/\n")), + None => {} + } + + src.push_str(&format!("module {};\n\n", fqn)); + + let mut deps = BTreeSet::new(); + + for dep_id in resolve.interface_direct_deps(id) { + deps.insert(dep_id); + } + + for dep_id in deps { + let wrapped_dep_id = WorldKey::Interface(dep_id); + src.push_str(&format!( + "import {};\n", + get_interface_fqn(&wrapped_dep_id, cur_world_fqn, resolve, false) + )); + } + + src.push_str("\n// Type defines\n"); + + for (name, id) in &interface.types { + src.push_str(&format!("// Define type: {name}\n")); + } + + src + } +} + +impl WorldGenerator for D { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + self.cur_world_fqn = get_world_fqn(world, resolve); + + let world = &resolve.worlds[world]; + match &world.docs.contents { + Some(docs) => self.world_src.push_str(&format!("/++\n{docs}\n+/\n")), + None => {} + } + self.world_src + .push_str(&format!("module {};\n\n", self.cur_world_fqn)); + + self.world_src.push_str("// Interface imports\n"); + } + + fn import_interface( + &mut self, + resolve: &Resolve, + name: &WorldKey, + id: InterfaceId, + _files: &mut Files, + ) -> Result<()> { + let interface_src = match self.interfaces.get_mut(&id) { + Some(src) => src, + None => { + let new_fqn = get_interface_fqn(name, &self.cur_world_fqn, resolve, false); + let new_src = + self.prepare_interface_bindings(id, &new_fqn, &self.cur_world_fqn, resolve); + + let mut result = InterfaceSource::default(); + result.fqn = new_fqn; + result.src = new_src; + + self.interfaces.insert(id, result); + self.interfaces.get_mut(&id).unwrap() + } + }; + + if interface_src.imported { + return Ok(()); + } + interface_src.imported = true; + + self.world_src + .push_str(&format!("public import {}\n", &self.interfaces[&id].fqn)); + + Ok(()) + } + + fn import_types( + &mut self, + resolve: &Resolve, + world: WorldId, + types: &[(&str, TypeId)], + _files: &mut Files, + ) { + self.world_src.push_str(&format!("\n// Type imports\n")); + for (name, id) in types { + self.world_src + .push_str(&format!("// Define type: {name}\n")); + } + } + + fn import_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + self.world_src.push_str(&format!("\n// Function imports\n")); + for (name, func) in funcs { + self.world_src + .push_str(&format!("// Import function: {name}\n")); + } + } + + fn pre_export_interface(&mut self, resolve: &Resolve, files: &mut Files) -> Result<()> { + self.world_src.push_str("\n// Interface exports\n"); + self.world_src + .push_str("mixin template Exports(alias Impl) {\n"); + self.world_src.indent(1); + + Ok(()) + } + + fn export_interface( + &mut self, + resolve: &Resolve, + name: &WorldKey, + id: InterfaceId, + _files: &mut Files, + ) -> Result<()> { + let interface = &resolve.interfaces[id]; + let interface_src = match self.interfaces.get_mut(&id) { + Some(src) => src, + None => { + let new_fqn = get_interface_fqn(name, &self.cur_world_fqn, resolve, true); + let new_src = + self.prepare_interface_bindings(id, &new_fqn, &self.cur_world_fqn, resolve); + + let mut result = InterfaceSource::default(); + result.fqn = new_fqn; + result.src = new_src; + + self.interfaces.insert(id, result); + + self.interfaces.get_mut(&id).unwrap() + } + }; + + if interface_src.exported { + return Ok(()); + } + interface_src.exported = true; + + self.world_src.push_str(&format!( + "mixin imported!\"{}\".Exports!Impl;\n", + interface_src.fqn + )); + + interface_src + .src + .push_str("\nmixin template Exports(alias Impl) {\n"); + interface_src.src.indent(1); + + interface_src + .src + .push_str(&format!("// Function exports\n")); + for (name, func) in &interface.functions { + interface_src + .src + .push_str(&format!("// Export function: {name}\n")); + } + + interface_src.src.deindent(1); + interface_src.src.push_str("}\n"); + + Ok(()) + } + + fn export_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) -> Result<()> { + self.world_src.push_str(&format!("\n// Function exports\n")); + for (name, func) in funcs { + self.world_src + .push_str(&format!("// Export function: {name}\n")); + } + Ok(()) + } + + fn finish(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) -> Result<()> { + // Close out interface exports + self.world_src.deindent(1); + self.world_src.push_str("}\n"); + + let mut world_filepath = PathBuf::from_iter(get_world_fqn(id, resolve).split(".")); + world_filepath.push("package.d"); + + files.push( + world_filepath.to_str().unwrap(), + self.world_src.as_str().as_bytes(), + ); + + for (_, interface_src) in &self.interfaces { + let mut interface_filepath = PathBuf::from_iter(interface_src.fqn.split(".")); + interface_filepath.add_extension("d"); + + files.push( + interface_filepath.to_str().unwrap(), + interface_src.src.as_bytes(), + ); + } + Ok(()) + } +} diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 04e38f7f0..6f0ff8fed 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -74,6 +74,15 @@ enum Opt { args: Common, }, + /// Generates bindings for D guest modules. + #[cfg(feature = "d")] + D { + #[clap(flatten)] + opts: wit_bindgen_d::Opts, + #[clap(flatten)] + args: Common, + }, + // doc-comments are present on `wit_bindgen_test::Opts` for clap to use. Test { #[clap(flatten)] @@ -150,6 +159,8 @@ fn main() -> Result<()> { Opt::Go { opts, args } => (opts.build(), args), #[cfg(feature = "csharp")] Opt::Csharp { opts, args } => (opts.build(), args), + #[cfg(feature = "d")] + Opt::D { opts, args } => (opts.build(args.out_dir.as_ref()), args), Opt::Test { opts } => return opts.run(std::env::args_os().nth(0).unwrap().as_ref()), }; From 8e1666e8367d1aa9abcdca8ca53384a77f0a496c Mon Sep 17 00:00:00 2001 From: Demetrius Kanios Date: Mon, 9 Mar 2026 23:12:29 -0700 Subject: [PATCH 2/6] Initial support for most types. --- crates/d/src/lib.rs | 609 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 573 insertions(+), 36 deletions(-) diff --git a/crates/d/src/lib.rs b/crates/d/src/lib.rs index 162728983..7187c6ccb 100644 --- a/crates/d/src/lib.rs +++ b/crates/d/src/lib.rs @@ -1,5 +1,6 @@ use anyhow::Result; use heck::*; +use std::borrow::Cow; use std::collections::{BTreeSet, HashMap}; use std::path::PathBuf; use wit_bindgen_core::{Files, Source, WorldGenerator, wit_parser::*}; @@ -11,6 +12,8 @@ struct D { cur_world_fqn: String, interfaces: HashMap, + + cur_interface: Option, } #[derive(Default)] @@ -38,14 +41,141 @@ impl Opts { } } +fn escape_d_identifier(name: &str) -> &str { + match name { + // Escape D keywords. + // Source: https://dlang.org/spec/lex.html#keywords + "abstract" => "abstract_", + "alias" => "alias_", + "align" => "align_", + "asm" => "asm_", + "assert" => "assert_", + "auto" => "auto_", + + "body" => "body_", + "bool" => "bool_", + "break" => "break_", + "byte" => "byte_", + + "case" => "case_", + "cast" => "cast_", + "catch" => "catch_", + "cdouble" => "cdouble_", + "cent" => "cent_", + "cfloat" => "cfloat_", + "char" => "char_", + "class" => "class_", + "const" => "const_", + "continue" => "continue_", + "creal" => "creal_", + + "dchar" => "dchar_", + "debug" => "debug_", + "default" => "default_", + "delegate" => "delegate_", + "delete" => "delete_", + "deprecated" => "deprecated_", + "do" => "do_", + "double" => "double_", + + "else" => "else_", + "enum" => "enum_", + "export" => "export_", + "extern" => "extern_", + + "false" => "false_", + "final" => "final_", + "finally" => "finally_", + "float" => "float_", + "for" => "for_", + "foreach" => "foreach_", + "foreach_reverse" => "foreach_reverse_", + "function" => "function_", + + "goto" => "goto_", + + "idouble" => "idouble_", + "if" => "if_", + "ifloat" => "ifloat_", + "immutable" => "immutable_", + "import" => "import_", + "in" => "in_", + "inout" => "inout_", + "int" => "int_", + "interface" => "interface_", + "invariant" => "invariant_", + "ireal" => "ireal_", + "is" => "is_", + + "lazy" => "lazy_", + "long" => "long_", + + "macro" => "macro_", + "mixin" => "mixin_", + "module" => "module_", + + "new" => "new_", + "nothrow" => "nothrow_", + "null" => "null_", + + "out" => "out_", + "override" => "override_", + + "package" => "package_", + "pragma" => "pragma_", + "private" => "private_", + "protected" => "protected_", + "public" => "public_", + "pure" => "pure_", + + "real" => "real_", + "ref" => "ref_", + "return" => "return_", + + "scope" => "scope_", + "shared" => "shared_", + "short" => "short_", + "static" => "static_", + "struct" => "struct_", + "super" => "super_", + "switch" => "switch_", + "synchronized" => "synchronized_", + + "template" => "template_", + "this" => "this_", + "throw" => "throw_", + "true" => "true_", + "try" => "try_", + "typeid" => "typeid_", + "typeof" => "typeof_", + + "ubyte" => "ubyte_", + "ucent" => "ucent_", + "uint" => "uint_", + "ulong" => "ulong_", + "union" => "union_", + "unittest" => "unittest_", + "ushort" => "ushort_", + + "version" => "version_", + "void" => "void_", + + "wchar" => "wchar_", + "while" => "while_", + "with" => "with_", + + s => s, + } +} + fn get_package_fqn(id: PackageId, resolve: &Resolve) -> String { let mut ns = String::new(); let pkg = &resolve.packages[id]; ns.push_str("wit."); - ns.push_str(&pkg.name.namespace.to_snake_case()); + ns.push_str(escape_d_identifier(&pkg.name.namespace.to_snake_case())); ns.push_str("."); - ns.push_str(&pkg.name.name.to_snake_case()); + ns.push_str(escape_d_identifier(&pkg.name.name.to_snake_case())); ns.push_str("."); let pkg_has_multiple_versions = resolve.packages.iter().any(|(_, p)| { p.name.namespace == pkg.name.namespace @@ -82,12 +212,14 @@ fn get_interface_fqn( ns.push_str(".imports") } ns.push_str("."); - ns.push_str(&name.to_snake_case()) + ns.push_str(escape_d_identifier(&name.to_snake_case())) } WorldKey::Interface(id) => { let iface = &resolve.interfaces[*id]; ns.push_str(&get_package_fqn(iface.package.unwrap(), resolve)); - ns.push_str(&iface.name.as_ref().unwrap().to_snake_case()) + ns.push_str(escape_d_identifier( + &iface.name.as_ref().unwrap().to_snake_case(), + )) } } ns @@ -98,11 +230,42 @@ fn get_world_fqn(id: WorldId, resolve: &Resolve) -> String { let world = &resolve.worlds[id]; ns.push_str(&get_package_fqn(world.package.unwrap(), resolve)); - ns.push_str(&world.name.to_snake_case()); + ns.push_str(escape_d_identifier(&world.name.to_snake_case())); ns } impl D { + fn get_type_fqn(&self, name: &str, owner: &TypeOwner) -> String { + match owner { + TypeOwner::None => String::from(name), + TypeOwner::Interface(id) => { + format!( + "{}.{}", + self.interfaces[id].fqn, + escape_d_identifier(&name.to_upper_camel_case()) + ) + } + TypeOwner::World(_) => format!( + "{}.{}", + self.cur_world_fqn, + escape_d_identifier(&name.to_upper_camel_case()) + ), + } + } + + fn get_type_name(&self, name: &str, owner: &TypeOwner) -> String { + match &owner { + TypeOwner::Interface(id) => Some(id), + _ => None, + } + .zip(self.cur_interface.as_ref()) + .filter(|(id, cur_id)| id == cur_id) + .map_or_else( + || self.get_type_fqn(name, owner), + |_| escape_d_identifier(&name.to_upper_camel_case()).into(), + ) + } + fn prepare_interface_bindings( &self, id: InterfaceId, @@ -113,13 +276,15 @@ impl D { let mut src = Source::default(); let interface = &resolve.interfaces[id]; - match &interface.docs.contents { - Some(docs) => src.push_str(&format!("/++\n{docs}\n+/\n")), - None => {} - } + src.push_str(&format!( + "/++\n{}\n+/\n", + interface.docs.contents.as_deref().unwrap_or_default() + )); src.push_str(&format!("module {};\n\n", fqn)); + src.push_str("import wit.common;\n\n"); + let mut deps = BTreeSet::new(); for dep_id in resolve.interface_direct_deps(id) { @@ -129,7 +294,7 @@ impl D { for dep_id in deps { let wrapped_dep_id = WorldKey::Interface(dep_id); src.push_str(&format!( - "import {};\n", + "static import {};\n", get_interface_fqn(&wrapped_dep_id, cur_world_fqn, resolve, false) )); } @@ -137,25 +302,384 @@ impl D { src.push_str("\n// Type defines\n"); for (name, id) in &interface.types { - src.push_str(&format!("// Define type: {name}\n")); + let type_src = self.generate_type_declaration(name, *id, resolve); + src.append_src(&type_src); } src } + + fn generate_type_use(&self, r#type: &Type, resolve: &Resolve) -> Cow<'static, str> { + match r#type { + Type::Bool => Cow::Borrowed("bool"), + Type::U8 => Cow::Borrowed("ubyte"), + Type::U16 => Cow::Borrowed("ushort"), + Type::U32 => Cow::Borrowed("uint"), + Type::U64 => Cow::Borrowed("ulong"), + Type::S8 => Cow::Borrowed("byte"), + Type::S16 => Cow::Borrowed("short"), + Type::S32 => Cow::Borrowed("int"), + Type::S64 => Cow::Borrowed("long"), + Type::F32 => Cow::Borrowed("float"), + Type::F64 => Cow::Borrowed("double"), + Type::Char => Cow::Borrowed("dchar"), + Type::String => Cow::Borrowed("String"), + Type::ErrorContext => { + todo!("use of `error_context`!"); + } + Type::Id(id) => { + let typedef = &resolve.types[*id]; + match &typedef.owner { + TypeOwner::None => match &typedef.kind { + TypeDefKind::Handle(handle) => todo!("use of `TypeDefKind::Handle`"), + TypeDefKind::Tuple(tuple) => Cow::Owned(format!( + "Tuple!({})", + tuple + .types + .iter() + .map(|ty| self.generate_type_use(ty, resolve).into_owned()) + .collect::>() + .join(", ") + )), + TypeDefKind::Option(opt_type) => Cow::Owned(format!( + "Option!({})", + self.generate_type_use(opt_type, resolve) + )), + TypeDefKind::Result(result) => Cow::Owned(format!( + "Result!({}, {})", + match result.ok { + Some(ok_type) => self.generate_type_use(&ok_type, resolve), + None => Cow::Borrowed("void"), + }, + match result.err { + Some(err_type) => self.generate_type_use(&err_type, resolve), + None => Cow::Borrowed("void"), + } + )), + TypeDefKind::List(list_type) => Cow::Owned(format!( + "List!({})", + self.generate_type_use(list_type, resolve) + )), + TypeDefKind::Map(_, _) => todo!("use of `TypeDefKind::Map`"), + TypeDefKind::FixedLengthList(list_type, length) => Cow::Owned(format!( + "{}[{length}]", + self.generate_type_use(list_type, resolve) + )), + TypeDefKind::Future(future_type) => todo!("use of `TypeDefKind::Future`"), + TypeDefKind::Stream(stream_type) => todo!("use of `TypeDefKind::Stream`"), + TypeDefKind::Type(target_type) => { + self.generate_type_use(target_type, resolve) + } + TypeDefKind::Unknown => { + panic!("Trying to emit type use for `TypeDefKind::Unknown`?"); + } + unhandled => { + panic!( + "Encountered unexpected use of ownerless typedef: {unhandled:?}." + ); + } + }, + _ => Cow::Owned( + self.get_type_name(typedef.name.as_ref().unwrap(), &typedef.owner), + ), + } + } + } + } + + fn generate_type_declaration(&self, name: &str, id: TypeId, resolve: &Resolve) -> Source { + let mut src = Source::default(); + + let typedef = &resolve.types[id]; + + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); + + src.push_str(&format!( + "\n/++\n{}\n+/\n", + typedef.docs.contents.as_deref().unwrap_or_default() + )); + match &typedef.kind { + TypeDefKind::Record(record) => { + src.push_str(&format!("struct {escaped_name} {{\n")); + + let mut is_first = true; + for field in &record.fields { + if is_first { + is_first = false; + } else { + src.push_str("\n"); + } + + src.push_str(&format!( + "/++\n{}\n+/\n", + field.docs.contents.as_deref().unwrap_or_default() + )); + src.push_str(&format!( + "{} {};\n", + self.generate_type_use(&field.ty, resolve), + field.name.to_lower_camel_case() + )); + } + + src.push_str("}\n"); + } + TypeDefKind::Resource => { + //src.push_str(&format!("// TODO: def of resource - {name}")) + todo!("def of `TypeDefKind::Resource`"); + } + TypeDefKind::Handle(handle) => { + todo!("def of `TypeDefKind::Handle`"); + } + TypeDefKind::Flags(flags) => { + let storage_type = match flags.repr() { + FlagsRepr::U8 => "ubyte", + FlagsRepr::U16 => "ushort", + FlagsRepr::U32(1) => "uint", + FlagsRepr::U32(2) => "ulong", + repr => todo!("flags {repr:?}"), + }; + + src.push_str(&format!("enum {escaped_name}_ : {storage_type} {{\n")); + for (index, flag) in flags.flags.iter().enumerate() { + if index != 0 { + src.push_str("\n"); + } + src.push_str(&format!( + "/++\n{}\n+/\n", + flag.docs.contents.as_deref().unwrap_or_default() + )); + src.push_str(&format!( + "{} = 1 << {index},\n", + escape_d_identifier(&flag.name.to_lower_camel_case()) + )); + } + src.push_str(&format!( + "}}\n/// ditto\nalias {escaped_name} = Flags!{escaped_name}_;" + )); + } + TypeDefKind::Tuple(tuple) => src.push_str(&format!( + "alias {escaped_name} = Tuple!({});", + tuple + .types + .iter() + .map(|ty| self.generate_type_use(ty, resolve).into_owned()) + .collect::>() + .join(", ") + )), + TypeDefKind::Variant(variant) => { + let storage_type = match variant.tag() { + Int::U8 => "ubyte", + Int::U16 => "ushort", + Int::U32 => "uint", + Int::U64 => "ulong", + }; + + src.push_str(&format!("struct {escaped_name} {{\n")); + src.deindent(1); + src.push_str(&format!("@safe @nogc nothrow:\n")); + src.indent(1); + + src.push_str(&format!("enum Tag : {storage_type} {{\n")); + + let mut is_first = true; + for case in &variant.cases { + if is_first { + is_first = false; + } else { + src.push_str("\n"); + } + src.push_str(&format!( + "/++\n{}\n+/\n", + case.docs.contents.as_deref().unwrap_or_default() + )); + src.push_str(&format!( + "{},\n", + escape_d_identifier(&case.name.to_lower_camel_case()) + )); + } + + src.push_str("}\n"); + + src.deindent(1); + src.push_str(&format!("\nprivate:\n")); + src.indent(1); + + if variant.cases.iter().any(|case| case.ty.is_some()) { + src.push_str(&format!("union Storage {{\n")); + src.push_str("ubyte __zeroinit = 0;\n"); + for case in &variant.cases { + if let Some(ty) = &case.ty { + src.push_str(&format!( + "{} {};\n", + self.generate_type_use(ty, resolve), + escape_d_identifier(&case.name.to_lower_camel_case()) + )); + } + } + + src.push_str("}\n\n"); + + src.push_str("Tag _tag;\n"); + src.push_str("Storage _storage;\n\n"); + } else { + src.push_str("Tag _tag;\n\n"); + } + + src.push_str("@disable this();\n"); + src.push_str("this(Tag tag, Storage storage = Storage.init) {\n"); + src.push_str("_tag = tag;\n"); + src.push_str("_storage = storage;\n"); + src.push_str("}\n"); + + src.deindent(1); + src.push_str(&format!("\npublic:\n")); + src.indent(1); + + src.push_str("Tag tag() => _tag;\n"); + + for case in &variant.cases { + src.push_str(&format!( + "\n/++\n{}\n+/\n", + case.docs.contents.as_deref().unwrap_or_default() + )); + let upper_case_name = case.name.to_upper_camel_case(); + let escaped_upper_case_name = escape_d_identifier(&upper_case_name); + + let lower_case_name = case.name.to_lower_camel_case(); + let escaped_lower_case_name = escape_d_identifier(&lower_case_name); + + if let Some(ty) = &case.ty { + src.push_str(&format!( + "static {escaped_name} {escaped_lower_case_name}({} val) {{\n", + self.generate_type_use(ty, resolve) + )); + src.push_str("Storage storage;\n"); + src.push_str(&format!("storage.{escaped_lower_case_name} = val;\n")); + src.push_str(&format!( + "return {escaped_name}(Tag.{escaped_lower_case_name}, storage);\n" + )); + src.push_str("}\n"); + + src.push_str(&format!( + "/// ditto\n ref inout({}) get{escaped_upper_case_name}() inout return\n", + self.generate_type_use(ty, resolve) + )); + + src.push_str(&format!("in (is{escaped_upper_case_name}) ")); + src.push_str(&format!( + "do {{ return _storage.{escaped_lower_case_name}; }}\n" + )); + } else { + src.push_str(&format!( + "static {escaped_name} {escaped_lower_case_name}() => {escaped_name}(Tag.{escaped_lower_case_name});\n", + )); + } + src.push_str(&format!( + "/// ditto\nbool is{escaped_upper_case_name}() const => _tag == Tag.{escaped_lower_case_name};\n", + )); + } + + src.push_str("}\n"); + } + TypeDefKind::Enum(r#enum) => { + let storage_type = match r#enum.tag() { + Int::U8 => "ubyte", + Int::U16 => "ushort", + Int::U32 => "uint", + Int::U64 => "ulong", + }; + + src.push_str(&format!("enum {escaped_name} : {storage_type} {{\n")); + + let mut is_first = true; + for case in &r#enum.cases { + if is_first { + is_first = false; + } else { + src.push_str("\n"); + } + src.push_str(&format!( + "/++\n{}\n+/\n", + case.docs.contents.as_deref().unwrap_or_default() + )); + src.push_str(&format!( + "{},\n", + escape_d_identifier(&case.name.to_lower_camel_case()) + )); + } + + src.push_str(&format!("}}")); + } + TypeDefKind::Option(opt_type) => src.push_str(&format!( + "alias {escaped_name} = Option!({});", + self.generate_type_use(opt_type, resolve) + )), + TypeDefKind::Result(result) => src.push_str(&format!( + "alias {escaped_name} = Result!({}, {});", + match result.ok { + Some(ok_type) => self.generate_type_use(&ok_type, resolve), + None => Cow::Borrowed("void"), + }, + match result.err { + Some(err_type) => self.generate_type_use(&err_type, resolve), + None => Cow::Borrowed("void"), + } + )), + TypeDefKind::List(list_type) => src.push_str(&format!( + "alias {escaped_name} = List!({});", + self.generate_type_use(list_type, resolve) + )), + TypeDefKind::Map(_, _) => { + todo!("def of `TypeDefKind::Map`"); + } + TypeDefKind::FixedLengthList(list_type, length) => { + src.push_str(&format!( + "alias {escaped_name} = {}[{length}];", + self.generate_type_use(&list_type, resolve), + )); + } + TypeDefKind::Future(future_type) => { + todo!("def of `TypeDefKind::Future`"); + } + TypeDefKind::Stream(stream_type) => { + todo!("def of `TypeDefKind::Stream`"); + } + TypeDefKind::Type(target_type) => { + src.push_str(&format!( + "alias {escaped_name} = {};", + self.generate_type_use(&target_type, resolve), + )); + } + TypeDefKind::Unknown => { + panic!("Trying to emit type declaration for `TypeDefKind::Unknown`?"); + } + } + src.push_str("\n"); + src + } } impl WorldGenerator for D { + fn uses_nominal_type_ids(&self) -> bool { + false + } + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { self.cur_world_fqn = get_world_fqn(world, resolve); let world = &resolve.worlds[world]; - match &world.docs.contents { - Some(docs) => self.world_src.push_str(&format!("/++\n{docs}\n+/\n")), - None => {} - } + + self.world_src.push_str(&format!( + "/++\n{}\n+/\n", + world.docs.contents.as_deref().unwrap_or_default() + )); + self.world_src .push_str(&format!("module {};\n\n", self.cur_world_fqn)); + self.world_src.push_str("import wit.common;\n\n"); + self.world_src.push_str("// Interface imports\n"); } @@ -166,19 +690,27 @@ impl WorldGenerator for D { id: InterfaceId, _files: &mut Files, ) -> Result<()> { + self.cur_interface = Some(id); let interface_src = match self.interfaces.get_mut(&id) { Some(src) => src, None => { + eprintln!("Import {id:?}"); let new_fqn = get_interface_fqn(name, &self.cur_world_fqn, resolve, false); - let new_src = - self.prepare_interface_bindings(id, &new_fqn, &self.cur_world_fqn, resolve); - let mut result = InterfaceSource::default(); - result.fqn = new_fqn; - result.src = new_src; + let mut result_init = InterfaceSource::default(); + result_init.fqn = new_fqn; + self.interfaces.insert(id, result_init); + + let new_src = self.prepare_interface_bindings( + id, + &self.interfaces.get(&id).unwrap().fqn, + &self.cur_world_fqn, + resolve, + ); - self.interfaces.insert(id, result); - self.interfaces.get_mut(&id).unwrap() + let result = self.interfaces.get_mut(&id).unwrap(); + result.src = new_src; + result } }; @@ -188,8 +720,9 @@ impl WorldGenerator for D { interface_src.imported = true; self.world_src - .push_str(&format!("public import {}\n", &self.interfaces[&id].fqn)); + .push_str(&format!("public import {};\n", &self.interfaces[&id].fqn)); + self.cur_interface = None; Ok(()) } @@ -202,8 +735,8 @@ impl WorldGenerator for D { ) { self.world_src.push_str(&format!("\n// Type imports\n")); for (name, id) in types { - self.world_src - .push_str(&format!("// Define type: {name}\n")); + let type_src = self.generate_type_declaration(name, *id, resolve); + self.world_src.append_src(&type_src); } } @@ -225,7 +758,6 @@ impl WorldGenerator for D { self.world_src.push_str("\n// Interface exports\n"); self.world_src .push_str("mixin template Exports(alias Impl) {\n"); - self.world_src.indent(1); Ok(()) } @@ -237,21 +769,28 @@ impl WorldGenerator for D { id: InterfaceId, _files: &mut Files, ) -> Result<()> { + self.cur_interface = Some(id); let interface = &resolve.interfaces[id]; let interface_src = match self.interfaces.get_mut(&id) { Some(src) => src, None => { + eprintln!("Export {id:?}"); let new_fqn = get_interface_fqn(name, &self.cur_world_fqn, resolve, true); - let new_src = - self.prepare_interface_bindings(id, &new_fqn, &self.cur_world_fqn, resolve); - let mut result = InterfaceSource::default(); - result.fqn = new_fqn; - result.src = new_src; + let mut result_init = InterfaceSource::default(); + result_init.fqn = new_fqn; + self.interfaces.insert(id, result_init); - self.interfaces.insert(id, result); + let new_src = self.prepare_interface_bindings( + id, + &self.interfaces.get(&id).unwrap().fqn, + &self.cur_world_fqn, + resolve, + ); - self.interfaces.get_mut(&id).unwrap() + let result = self.interfaces.get_mut(&id).unwrap(); + result.src = new_src; + result } }; @@ -268,7 +807,6 @@ impl WorldGenerator for D { interface_src .src .push_str("\nmixin template Exports(alias Impl) {\n"); - interface_src.src.indent(1); interface_src .src @@ -279,9 +817,9 @@ impl WorldGenerator for D { .push_str(&format!("// Export function: {name}\n")); } - interface_src.src.deindent(1); interface_src.src.push_str("}\n"); + self.cur_interface = None; Ok(()) } @@ -302,7 +840,6 @@ impl WorldGenerator for D { fn finish(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) -> Result<()> { // Close out interface exports - self.world_src.deindent(1); self.world_src.push_str("}\n"); let mut world_filepath = PathBuf::from_iter(get_world_fqn(id, resolve).split(".")); From bc9d5b11551178d3064dceb0aad43fb0715d0fd0 Mon Sep 17 00:00:00 2001 From: Demetrius Kanios Date: Tue, 10 Mar 2026 16:43:36 -0700 Subject: [PATCH 3/6] Basic implementation of `wit.common` type templates. [skip ci] --- crates/d/src/lib.rs | 2 + crates/d/src/wit_common.d | 275 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 277 insertions(+) create mode 100644 crates/d/src/wit_common.d diff --git a/crates/d/src/lib.rs b/crates/d/src/lib.rs index 7187c6ccb..4d6e8cead 100644 --- a/crates/d/src/lib.rs +++ b/crates/d/src/lib.rs @@ -850,6 +850,8 @@ impl WorldGenerator for D { self.world_src.as_str().as_bytes(), ); + files.push("wit/common.d", include_bytes!("wit_common.d")); + for (_, interface_src) in &self.interfaces { let mut interface_filepath = PathBuf::from_iter(interface_src.fqn.split(".")); interface_filepath.add_extension("d"); diff --git a/crates/d/src/wit_common.d b/crates/d/src/wit_common.d new file mode 100644 index 000000000..f5d3f67f5 --- /dev/null +++ b/crates/d/src/wit_common.d @@ -0,0 +1,275 @@ +module wit.common; + +/// Thin CABI compliant wrapper over `T[]` +struct List(T) { +@safe @nogc pure nothrow: + T* ptr; + size_t length; + + this(T[] slice) @trusted { + this = slice; + } + + void opAssign(T[] slice) @trusted { + ptr = slice.ptr; + length = slice.length; + } + + alias asSlice this; + inout(T)[] asSlice() @trusted inout { + return (ptr && length) ? ptr[0..length] : null; + } +} + +// WIT ABI for string matches List, +// except list in WIT is actually List!(dchar) +// +// We assume UTF-8 data (as D native strings are UTF-8) +alias String = List!(immutable char); + +// TODO: split this file up and give Tuple a full port of the Phobos version? +/// adapted from Phobos std.typecons.Tuple +/// No support for naming members. +struct Tuple(Types...) if (is(Types)) { + Types expand; + alias expand this; +} + +/// adapted from Phobos std.bitmanip.BitFlags +struct Flags(Enum) if (is(Enum == enum)) { +@safe @nogc pure nothrow: + public alias E = Enum; + +private: + template allAreBaseEnum(T...) + { + static foreach (Ti; T) + { + static if (!is(typeof(allAreBaseEnum) == bool) && // not yet defined + !is(Ti : E)) + { + enum allAreBaseEnum = false; + } + } + static if (!is(typeof(allAreBaseEnum) == bool)) // if not yet defined + { + enum allAreBaseEnum = true; + } + } + + static if (is(E U == enum)) { + alias Base = U; + } else static assert(0); + + Base mValue; + +public: + this(E flag) + { + this = flag; + } + + this(T...)(T flags) + if (allAreBaseEnum!(T)) + { + this = flags; + } + + bool opCast(B: bool)() const + { + return mValue != 0; + } + + Base opCast(B)() const + if (is(Base : B)) + { + return mValue; + } + + auto opUnary(string op)() const + if (op == "~") + { + return WitFlags(cast(E) cast(Base) ~mValue); + } + + auto ref opAssign(T...)(T flags) + if (allAreBaseEnum!(T)) + { + mValue = 0; + foreach (E flag; flags) + { + mValue |= flag; + } + return this; + } + + auto ref opAssign(E flag) + { + mValue = flag; + return this; + } + + auto ref opOpAssign(string op: "|")(WitFlags flags) + { + mValue |= flags.mValue; + return this; + } + + auto ref opOpAssign(string op: "&")(WitFlags flags) + { + mValue &= flags.mValue; + return this; + } + + auto ref opOpAssign(string op: "|")(E flag) + { + mValue |= flag; + return this; + } + + auto ref opOpAssign(string op: "&")(E flag) + { + mValue &= flag; + return this; + } + + auto opBinary(string op)(WitFlags flags) const + if (op == "|" || op == "&") + { + WitFlags result = this; + result.opOpAssign!op(flags); + return result; + } + + auto opBinary(string op)(E flag) const + if (op == "|" || op == "&") + { + WitFlags result = this; + result.opOpAssign!op(flag); + return result; + } + + auto opBinaryRight(string op)(E flag) const + if (op == "|" || op == "&") + { + return opBinary!op(flag); + } + + bool opDispatch(string name)() const + if (__traits(hasMember, E, name)) + { + enum e = __traits(getMember, E, name); + return (mValue & e) == e; + } + + void opDispatch(string name)(bool set) + if (__traits(hasMember, E, name)) + { + enum e = __traits(getMember, E, name); + if (set) + mValue |= e; + else + mValue &= ~e; + } +} + +/// Based on Rust's Option +struct Option(T) { +private: + bool present = false; + T value; + + @disable this(); + this(bool present, T value = T.init) @safe @nogc nothrow { + this.present = present; + this.value = value; + } +public: + static Option some(T value) @safe @nogc nothrow { + return Option(true, value); + } + + static Option none() @safe @nogc nothrow { + return Option(false); + } + + bool isSome() const @safe @nogc nothrow => present; + alias isSome this; // implicit conversion to bool + + bool isNone() const @safe @nogc nothrow => !present; + + ref inout(T) unwrap() inout @safe @nogc nothrow return + in (present) do { return value; } + + T unwrapOr(T fallback) @safe @nogc nothrow => present ? value : fallback; + + T unwrapOrElse(D)(scope D fallback) + if (is(D R == return) && is(R : T) && is(D == __parameters)) + { return present ? value : fallback(); } +} + +/// Based on Rust's Result +struct Result(T, E) { +private: + bool hasError; + union Storage { + ubyte __zeroinit = 0; + static if (!is(T == void)) { + T value; + } + static if (!is(E == void)) { + E error; + } + } + Storage storage; + + @disable this(); + this(bool hasError, Storage storage) @safe @nogc nothrow { + this.hasError = hasError; + this.storage = storage; + } + +public: + static if (is(T == void)) { + static Result ok() @safe @nogc nothrow => Result(false, Storage()); + } else { + static Result ok(T value) @safe @nogc nothrow { + Storage newStorage; + newStorage.value = value; + + return Result(false, newStorage); + } + } + + static if (is(E == void)) { + static Result err() @safe @nogc nothrow => Result(true, Storage()); + } else { + static Result err(E error) @safe @nogc nothrow { + Storage newStorage; + newStorage.error = error; + + return Result(true, newStorage); + } + } + + bool isOk() const @safe @nogc nothrow => !hasError; + + bool isErr() const @safe @nogc nothrow => hasError; + alias isErr this; // implicit conversion to bool + + static if (!is(T == void)) { + ref inout(T) unwrap() inout @safe @nogc nothrow return + in (isOk) do { return storage.value; } + + T unwrapOr(T fallback) @safe @nogc nothrow => isOk ? storage.value : fallback; + + T unwrapOrElse(D)(scope D fallback) + if (is(D R == return) && is(R : T) && is(D == __parameters)) + { return isOk ? storage.value : fallback(); } + } + + static if (!is(E == void)) { + ref inout(E) unwrapErr() inout @safe @nogc nothrow return + in (isErr) do { return storage.error; } + } +} From fd719690bac280531ca1b93a87f0ee785730a46e Mon Sep 17 00:00:00 2001 From: Demetrius Kanios Date: Thu, 12 Mar 2026 16:10:09 -0700 Subject: [PATCH 4/6] Refactor type generation [skip ci] --- crates/d/src/lib.rs | 1440 +++++++++++++++++++++++-------------- crates/d/src/wit_common.d | 140 +--- 2 files changed, 900 insertions(+), 680 deletions(-) diff --git a/crates/d/src/lib.rs b/crates/d/src/lib.rs index 4d6e8cead..e39a1efaa 100644 --- a/crates/d/src/lib.rs +++ b/crates/d/src/lib.rs @@ -1,27 +1,38 @@ use anyhow::Result; use heck::*; use std::borrow::Cow; -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeSet, HashMap, HashSet}; +use std::mem::take; use std::path::PathBuf; -use wit_bindgen_core::{Files, Source, WorldGenerator, wit_parser::*}; +use wit_bindgen_core::{Direction, Types}; +use wit_bindgen_core::{Files, InterfaceGenerator, Source, WorldGenerator, wit_parser::*}; #[derive(Default)] struct D { - world_src: Source, + used_interfaces: HashSet<(WorldKey, InterfaceId)>, + + interface_imports: Vec, + interface_exports: Vec, + type_imports_src: Source, + function_imports_src: Source, + function_exports_src: Source, + opts: Opts, - cur_world_fqn: String, - interfaces: HashMap, + world_id: Option, + world_fqn: String, + interface_fqns: HashMap, cur_interface: Option, + + types: Types, } -#[derive(Default)] -struct InterfaceSource { - fqn: String, - src: Source, - imported: bool, - exported: bool, +#[derive(Default, Debug)] +struct InterfaceFQNSet { + import: Option, + export: Option, + common: Option, } #[derive(Default, Debug, Clone)] @@ -164,703 +175,1022 @@ fn escape_d_identifier(name: &str) -> &str { "while" => "while_", "with" => "with_", + // Symbols we define as part of the bindings we want to avoid creating conflicts with + "WitList" => "WitList_", + "WitString" => "WitString_", + "WitFlags" => "WitFlags_", + "Option" => "Option_", + "Result" => "Result_", + "bits" => "bits_", // part of WitFlags + s => s, } } fn get_package_fqn(id: PackageId, resolve: &Resolve) -> String { - let mut ns = String::new(); - let pkg = &resolve.packages[id]; - ns.push_str("wit."); - ns.push_str(escape_d_identifier(&pkg.name.namespace.to_snake_case())); - ns.push_str("."); - ns.push_str(escape_d_identifier(&pkg.name.name.to_snake_case())); - ns.push_str("."); let pkg_has_multiple_versions = resolve.packages.iter().any(|(_, p)| { p.name.namespace == pkg.name.namespace && p.name.name == pkg.name.name && p.name.version != pkg.name.version }); - if pkg_has_multiple_versions { - if let Some(version) = &pkg.name.version { - let version = version - .to_string() - .replace('.', "_") - .replace('-', "_") - .replace('+', "_"); - ns.push_str(&version); - ns.push_str("."); + + format!( + "wit.{}.{}{}", + escape_d_identifier(&pkg.name.namespace.to_snake_case()), + escape_d_identifier(&pkg.name.name.to_snake_case()), + if pkg_has_multiple_versions { + if let Some(version) = &pkg.name.version { + let version = version + .to_string() + .replace('.', "_") + .replace('-', "_") + .replace('+', "_"); + format!(".{version}") + } else { + String::default() + } + } else { + String::default() } - } - ns + ) } fn get_interface_fqn( interface_id: &WorldKey, - cur_world_fqn: &String, + world_fqn: &str, resolve: &Resolve, - is_export: bool, + direction: Option, ) -> String { - let mut ns = String::new(); match interface_id { WorldKey::Name(name) => { - ns.push_str(cur_world_fqn); - if is_export { - ns.push_str(".exports") - } else { - ns.push_str(".imports") - } - ns.push_str("."); - ns.push_str(escape_d_identifier(&name.to_snake_case())) + format!( + "{}.{}.{}", + world_fqn, + match direction { + None => panic!( + "Inline interfaces can only generate `import` or `export` module variant" + ), + Some(Direction::Import) => "imports", + Some(Direction::Export) => "exports", + }, + escape_d_identifier(&name.to_snake_case()) + ) } WorldKey::Interface(id) => { let iface = &resolve.interfaces[*id]; - ns.push_str(&get_package_fqn(iface.package.unwrap(), resolve)); - ns.push_str(escape_d_identifier( - &iface.name.as_ref().unwrap().to_snake_case(), - )) + + format!( + "{}.{}.{}", + get_package_fqn(iface.package.unwrap(), resolve), + escape_d_identifier(&iface.name.as_ref().unwrap().to_snake_case()), + match direction { + None => "common", + Some(Direction::Import) => "imports", + Some(Direction::Export) => "exports", + }, + ) } } - ns } fn get_world_fqn(id: WorldId, resolve: &Resolve) -> String { - let mut ns = String::new(); - let world = &resolve.worlds[id]; - ns.push_str(&get_package_fqn(world.package.unwrap(), resolve)); - ns.push_str(escape_d_identifier(&world.name.to_snake_case())); - ns + format!( + "{}.{}", + get_package_fqn(world.package.unwrap(), resolve), + escape_d_identifier(&world.name.to_snake_case()) + ) } impl D { - fn get_type_fqn(&self, name: &str, owner: &TypeOwner) -> String { - match owner { - TypeOwner::None => String::from(name), - TypeOwner::Interface(id) => { - format!( - "{}.{}", - self.interfaces[id].fqn, - escape_d_identifier(&name.to_upper_camel_case()) - ) - } - TypeOwner::World(_) => format!( - "{}.{}", - self.cur_world_fqn, - escape_d_identifier(&name.to_upper_camel_case()) - ), + fn interface<'a>( + &'a mut self, + resolve: &'a Resolve, + direction: Option, + name: Option<&'a WorldKey>, + wasm_import_module: Option<&'a str>, + ) -> DInterfaceGenerator<'a> { + let mut sizes = SizeAlign::default(); + sizes.fill(resolve); + + DInterfaceGenerator { + src: Source::default(), + fqn: "", + r#gen: self, + resolve, + interface: None, + name: name, + sizes, + direction, + + wasm_import_module, } } - fn get_type_name(&self, name: &str, owner: &TypeOwner) -> String { - match &owner { - TypeOwner::Interface(id) => Some(id), - _ => None, + fn lookup_interface_fqn(&self, id: InterfaceId, direction: Option) -> Option<&str> { + let all_fqns = &self.interface_fqns[&id]; + match direction { + None => all_fqns.common.as_deref(), + Some(Direction::Import) => all_fqns.import.as_deref(), + Some(Direction::Export) => all_fqns.export.as_deref(), + } + } +} + +impl WorldGenerator for D { + fn uses_nominal_type_ids(&self) -> bool { + false + } + + fn preprocess(&mut self, resolve: &Resolve, world_id: WorldId) { + self.world_fqn = get_world_fqn(world_id, resolve); + self.world_id = Some(world_id); + self.types.analyze(resolve); + + let world = &resolve.worlds[world_id]; + + for (name, import) in world.imports.iter() { + match import { + WorldItem::Interface { id, .. } => { + let fqns = self.interface_fqns.entry(*id).or_insert_with(|| { + let mut result = InterfaceFQNSet::default(); + + match name { + WorldKey::Interface(_) => { + result.common = + Some(get_interface_fqn(&name, &self.world_fqn, resolve, None)); + } + WorldKey::Name(_) => { + // For anonymous/inline imports, the common types are in the same file as the imports + result.common = Some(get_interface_fqn( + &name, + &self.world_fqn, + resolve, + Some(Direction::Import), + )); + } + } + + result + }); + (*fqns).import = Some(get_interface_fqn( + &name, + &self.world_fqn, + resolve, + Some(Direction::Import), + )) + } + _ => {} + } + } + + for (name, export) in world.exports.iter() { + match export { + WorldItem::Interface { id, .. } => { + let fqns = self.interface_fqns.entry(*id).or_insert_with(|| { + let mut result = InterfaceFQNSet::default(); + + match name { + WorldKey::Interface(_) => { + result.common = + Some(get_interface_fqn(&name, &self.world_fqn, resolve, None)); + } + WorldKey::Name(_) => { + // For anonymous/inline exports, the common types are in the same file as the exports + result.common = Some(get_interface_fqn( + &name, + &self.world_fqn, + resolve, + Some(Direction::Export), + )); + } + } + + result + }); + (*fqns).export = Some(get_interface_fqn( + &name, + &self.world_fqn, + resolve, + Some(Direction::Export), + )) + } + _ => {} + } } - .zip(self.cur_interface.as_ref()) - .filter(|(id, cur_id)| id == cur_id) - .map_or_else( - || self.get_type_fqn(name, owner), - |_| escape_d_identifier(&name.to_upper_camel_case()).into(), - ) } - fn prepare_interface_bindings( - &self, + fn import_interface( + &mut self, + resolve: &Resolve, + name: &WorldKey, id: InterfaceId, - fqn: &String, - cur_world_fqn: &String, + files: &mut Files, + ) -> Result<()> { + self.used_interfaces.insert((name.clone(), id)); + + self.cur_interface = Some(id); + + let fqn = self.interface_fqns[&id].import.as_ref().unwrap().clone(); + + self.interface_imports.push(fqn.clone()); + + let wasm_import_module = resolve.name_world_key(name); + let mut r#gen = self.interface( + resolve, + Some(Direction::Import), + Some(name), + Some(&wasm_import_module), + ); + r#gen.fqn = &fqn; + r#gen.interface = Some(id); + r#gen.prologue(); + + if let WorldKey::Name(_) = name { + // We have an inline interface imported in a world. + // Emit the "common" types as well + + r#gen.direction = None; + r#gen.types(id); + r#gen.direction = Some(Direction::Import); + } + + r#gen.types(id); + + let mut interface_filepath = PathBuf::from_iter(fqn.split(".")); + interface_filepath.add_extension("d"); + + files.push(interface_filepath.to_str().unwrap(), r#gen.src.as_bytes()); + + //self.interface_imports.push(interface_src.fqn.clone()); + //interface_src.src.push_str("\n// Function imports\n"); + //interface_src.src.append_src(&tmp_src); + + self.cur_interface = None; + Ok(()) + } + + fn import_types( + &mut self, resolve: &Resolve, - ) -> Source { - let mut src = Source::default(); - let interface = &resolve.interfaces[id]; + _world: WorldId, + types: &[(&str, TypeId)], + _files: &mut Files, + ) { + let mut r#gen = self.interface(resolve, Some(Direction::Import), None, Some("$root")); + for (name, id) in types.iter() { + r#gen.define_type(name, *id); + } - src.push_str(&format!( - "/++\n{}\n+/\n", - interface.docs.contents.as_deref().unwrap_or_default() - )); + let src = take(&mut r#gen.src); + self.type_imports_src.append_src(&src); + } - src.push_str(&format!("module {};\n\n", fqn)); + fn import_funcs( + &mut self, + _resolve: &Resolve, + _world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let _name = WorldKey::Name("$root".to_string()); + //let wasm_import_module = resolve.name_world_key(&name); - src.push_str("import wit.common;\n\n"); + for (name, _func) in funcs { + self.function_imports_src + .push_str(&format!("// Import function - {name}\n")); + } + } - let mut deps = BTreeSet::new(); + fn export_interface( + &mut self, + resolve: &Resolve, + name: &WorldKey, + id: InterfaceId, + files: &mut Files, + ) -> Result<()> { + self.used_interfaces.insert((name.clone(), id)); - for dep_id in resolve.interface_direct_deps(id) { - deps.insert(dep_id); + self.cur_interface = Some(id); + + let fqn = self.interface_fqns[&id].export.as_ref().unwrap().clone(); + + self.interface_exports.push(fqn.clone()); + + let wasm_import_module = resolve.name_world_key(name); + let mut r#gen = self.interface( + resolve, + Some(Direction::Export), + Some(name), + Some(&wasm_import_module), + ); + r#gen.interface = Some(id); + r#gen.prologue(); + + if let WorldKey::Name(_) = name { + // We have an inline interface exported in a world. + // Emit the "common" types as well + + r#gen.direction = None; + r#gen.types(id); + r#gen.direction = Some(Direction::Export); } - for dep_id in deps { - let wrapped_dep_id = WorldKey::Interface(dep_id); - src.push_str(&format!( - "static import {};\n", - get_interface_fqn(&wrapped_dep_id, cur_world_fqn, resolve, false) - )); + r#gen.types(id); + + let mut interface_filepath = PathBuf::from_iter(fqn.split(".")); + interface_filepath.add_extension("d"); + + files.push(interface_filepath.to_str().unwrap(), r#gen.src.as_bytes()); + + self.cur_interface = None; + Ok(()) + } + + fn export_funcs( + &mut self, + _resolve: &Resolve, + _world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) -> Result<()> { + for (name, _func) in funcs { + self.function_exports_src + .push_str(&format!("// Export function: {name}\n")); } + Ok(()) + } + + fn finish(&mut self, resolve: &Resolve, world_id: WorldId, files: &mut Files) -> Result<()> { + for (name, id) in take(&mut self.used_interfaces) { + if let WorldKey::Interface(_) = name { + let fqn = self.interface_fqns[&id].common.as_ref().unwrap().clone(); - src.push_str("\n// Type defines\n"); + let wasm_import_module = resolve.name_world_key(&name); + let mut r#gen = + self.interface(resolve, None, Some(&name), Some(&wasm_import_module)); + r#gen.interface = Some(id); + r#gen.prologue(); + r#gen.types(id); - for (name, id) in &interface.types { - let type_src = self.generate_type_declaration(name, *id, resolve); - src.append_src(&type_src); + let mut interface_filepath = PathBuf::from_iter(fqn.split(".")); + interface_filepath.add_extension("d"); + + files.push(interface_filepath.to_str().unwrap(), r#gen.src.as_bytes()); + } } - src + let mut world_src = Source::default(); + + let world = &resolve.worlds[world_id]; + + world_src.push_str(&format!( + "/++\n{}\n+/\n", + world.docs.contents.as_deref().unwrap_or_default() + )); + + world_src.push_str(&format!("module {};\n\n", self.world_fqn)); + world_src.push_str("import wit.common;\n\n"); + world_src.push_str("// Interface imports\n"); + world_src.push_str( + &self + .interface_imports + .iter() + .map(|fqn| format!("public import {fqn};")) + .collect::>() + .join("\n"), + ); + + world_src.push_str("\n\n// Type imports\n"); + world_src.append_src(&self.type_imports_src); + + world_src.push_str("\n// Function imports\n"); + world_src.append_src(&self.function_imports_src); + + world_src.push_str("\n// Interface exports\n"); + world_src.push_str( + &self + .interface_exports + .iter() + .map(|fqn| format!("public import {fqn};")) + .collect::>() + .join("\n"), + ); + + world_src.push_str("\n\nprivate alias AliasSeq(T...) = T;\n"); + world_src.push_str("template Exports(Impl...) {\n"); + world_src.push_str("// Interface exports\n"); + + world_src.push_str("alias InterfaceExports = AliasSeq!(\n"); + world_src.indent(1); + world_src.push_str( + &self + .interface_exports + .iter() + .map(|fqn| format!("{fqn}.Exports!Impl")) + .collect::>() + .join(",\n"), + ); + world_src.deindent(1); + world_src.push_str("\n);\n"); + + world_src.push_str("\n// Function exports\n"); + world_src.append_src(&self.function_exports_src); + world_src.push_str("}\n"); + + let mut world_filepath = PathBuf::from_iter(get_world_fqn(world_id, resolve).split(".")); + world_filepath.push("package.d"); + + files.push(world_filepath.to_str().unwrap(), world_src.as_bytes()); + + files.push("wit/common.d", include_bytes!("wit_common.d")); + Ok(()) } +} + +struct DInterfaceGenerator<'a> { + src: Source, + direction: Option, + r#gen: &'a mut D, + resolve: &'a Resolve, + interface: Option, + name: Option<&'a WorldKey>, + wasm_import_module: Option<&'a str>, + fqn: &'a str, + + sizes: SizeAlign, +} - fn generate_type_use(&self, r#type: &Type, resolve: &Resolve) -> Cow<'static, str> { - match r#type { +impl<'a> DInterfaceGenerator<'a> { + fn scoped_type_name(&self, id: TypeId, from_module_fqn: &str) -> String { + let ty = &self.resolve.types[id]; + + let owner_fqn = self.type_owner_fqn(&ty.owner).unwrap(); + + let upper_name = ty.name.as_ref().unwrap().to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); + + if from_module_fqn == owner_fqn { + escaped_name.into() + } else { + format!("{owner_fqn}.{escaped_name}") + } + } + fn type_name(&self, ty: &Type, from_module_fqn: &str) -> Cow<'static, str> { + match ty { Type::Bool => Cow::Borrowed("bool"), + Type::Char => Cow::Borrowed("dchar"), Type::U8 => Cow::Borrowed("ubyte"), - Type::U16 => Cow::Borrowed("ushort"), - Type::U32 => Cow::Borrowed("uint"), - Type::U64 => Cow::Borrowed("ulong"), Type::S8 => Cow::Borrowed("byte"), + Type::U16 => Cow::Borrowed("ushort"), Type::S16 => Cow::Borrowed("short"), + Type::U32 => Cow::Borrowed("uint"), Type::S32 => Cow::Borrowed("int"), + Type::U64 => Cow::Borrowed("ulong"), Type::S64 => Cow::Borrowed("long"), Type::F32 => Cow::Borrowed("float"), Type::F64 => Cow::Borrowed("double"), - Type::Char => Cow::Borrowed("dchar"), - Type::String => Cow::Borrowed("String"), - Type::ErrorContext => { - todo!("use of `error_context`!"); - } + Type::String => Cow::Borrowed("WitString"), Type::Id(id) => { - let typedef = &resolve.types[*id]; - match &typedef.owner { + let typedef = &self.resolve.types[*id]; + + match typedef.owner { TypeOwner::None => match &typedef.kind { - TypeDefKind::Handle(handle) => todo!("use of `TypeDefKind::Handle`"), - TypeDefKind::Tuple(tuple) => Cow::Owned(format!( + TypeDefKind::Record(_) => { + Cow::Owned(self.scoped_type_name(*id, from_module_fqn)) + } + TypeDefKind::Resource => { + Cow::Owned(self.scoped_type_name(*id, from_module_fqn)) + } + TypeDefKind::Handle(Handle::Own(_id)) => { + Cow::Borrowed("/* todo - type_name of `own` */") + } + TypeDefKind::Handle(Handle::Borrow(_id)) => { + Cow::Borrowed("/* todo - type_name of `borrow` */") + } + TypeDefKind::Tuple(t) => Cow::Owned(format!( "Tuple!({})", - tuple - .types + t.types .iter() - .map(|ty| self.generate_type_use(ty, resolve).into_owned()) + .map(|ty| self.type_name(ty, from_module_fqn).into_owned()) .collect::>() .join(", ") )), - TypeDefKind::Option(opt_type) => Cow::Owned(format!( - "Option!({})", - self.generate_type_use(opt_type, resolve) - )), - TypeDefKind::Result(result) => Cow::Owned(format!( + TypeDefKind::Option(o) => { + Cow::Owned(format!("Option!({})", self.type_name(o, from_module_fqn))) + } + TypeDefKind::Result(r) => Cow::Owned(format!( "Result!({}, {})", - match result.ok { - Some(ok_type) => self.generate_type_use(&ok_type, resolve), + match r.ok { + Some(ok_type) => self.type_name(&ok_type, from_module_fqn), None => Cow::Borrowed("void"), }, - match result.err { - Some(err_type) => self.generate_type_use(&err_type, resolve), + match r.err { + Some(err_type) => self.type_name(&err_type, from_module_fqn), None => Cow::Borrowed("void"), } )), - TypeDefKind::List(list_type) => Cow::Owned(format!( - "List!({})", - self.generate_type_use(list_type, resolve) + TypeDefKind::List(ty) => Cow::Owned(format!( + "WitList!({})", + self.type_name(&ty, from_module_fqn) )), - TypeDefKind::Map(_, _) => todo!("use of `TypeDefKind::Map`"), - TypeDefKind::FixedLengthList(list_type, length) => Cow::Owned(format!( - "{}[{length}]", - self.generate_type_use(list_type, resolve) - )), - TypeDefKind::Future(future_type) => todo!("use of `TypeDefKind::Future`"), - TypeDefKind::Stream(stream_type) => todo!("use of `TypeDefKind::Stream`"), - TypeDefKind::Type(target_type) => { - self.generate_type_use(target_type, resolve) + TypeDefKind::Future(_) => { + Cow::Borrowed("/* todo - type_name of `future` */") + } + TypeDefKind::Stream(_) => { + Cow::Borrowed("/* todo - type_name of `stream` */") } - TypeDefKind::Unknown => { - panic!("Trying to emit type use for `TypeDefKind::Unknown`?"); + TypeDefKind::FixedLengthList(ty, size) => { + Cow::Owned(format!("{}[{size}]", self.type_name(ty, from_module_fqn))) } + TypeDefKind::Map(_, _) => todo!(), + TypeDefKind::Unknown => unimplemented!(), unhandled => { panic!( - "Encountered unexpected use of ownerless typedef: {unhandled:?}." + "Encountered unexpected `type_name` invocation of ownerless typedef: {unhandled:?}." ); } }, - _ => Cow::Owned( - self.get_type_name(typedef.name.as_ref().unwrap(), &typedef.owner), - ), + _ => Cow::Owned(self.scoped_type_name(*id, from_module_fqn)), } } + Type::ErrorContext => todo!(), } } - fn generate_type_declaration(&self, name: &str, id: TypeId, resolve: &Resolve) -> Source { - let mut src = Source::default(); + fn type_owner_fqn(&self, owner: &TypeOwner) -> Option<&str> { + match &owner { + TypeOwner::None => None, + TypeOwner::Interface(interface_id) => match self.direction { + Some(_) => self + .r#gen + .lookup_interface_fqn(*interface_id, self.direction) + .or_else(|| self.r#gen.lookup_interface_fqn(*interface_id, None)), + None => self.r#gen.lookup_interface_fqn(*interface_id, None), + }, + TypeOwner::World(world_id) => { + if *world_id != self.r#gen.world_id.unwrap() { + panic!("Dealing with type from different world?"); + } - let typedef = &resolve.types[id]; + Some(&self.r#gen.world_fqn) + } + } + } - let upper_name = name.to_upper_camel_case(); - let escaped_name = escape_d_identifier(&upper_name); + fn prologue(&mut self) { + let id = self.interface.unwrap(); - src.push_str(&format!( - "\n/++\n{}\n+/\n", - typedef.docs.contents.as_deref().unwrap_or_default() - )); - match &typedef.kind { - TypeDefKind::Record(record) => { - src.push_str(&format!("struct {escaped_name} {{\n")); - - let mut is_first = true; - for field in &record.fields { - if is_first { - is_first = false; - } else { - src.push_str("\n"); - } + let fqn = self.r#gen.lookup_interface_fqn(id, self.direction).unwrap(); - src.push_str(&format!( - "/++\n{}\n+/\n", - field.docs.contents.as_deref().unwrap_or_default() - )); - src.push_str(&format!( - "{} {};\n", - self.generate_type_use(&field.ty, resolve), - field.name.to_lower_camel_case() - )); - } + let interface = &self.resolve.interfaces[self.interface.unwrap()]; - src.push_str("}\n"); - } - TypeDefKind::Resource => { - //src.push_str(&format!("// TODO: def of resource - {name}")) - todo!("def of `TypeDefKind::Resource`"); - } - TypeDefKind::Handle(handle) => { - todo!("def of `TypeDefKind::Handle`"); - } - TypeDefKind::Flags(flags) => { - let storage_type = match flags.repr() { - FlagsRepr::U8 => "ubyte", - FlagsRepr::U16 => "ushort", - FlagsRepr::U32(1) => "uint", - FlagsRepr::U32(2) => "ulong", - repr => todo!("flags {repr:?}"), - }; + self.src.push_str(&format!( + "/++\n{}\n+/\n", + interface.docs.contents.as_deref().unwrap_or_default() + )); - src.push_str(&format!("enum {escaped_name}_ : {storage_type} {{\n")); - for (index, flag) in flags.flags.iter().enumerate() { - if index != 0 { - src.push_str("\n"); - } - src.push_str(&format!( - "/++\n{}\n+/\n", - flag.docs.contents.as_deref().unwrap_or_default() - )); - src.push_str(&format!( - "{} = 1 << {index},\n", - escape_d_identifier(&flag.name.to_lower_camel_case()) - )); - } - src.push_str(&format!( - "}}\n/// ditto\nalias {escaped_name} = Flags!{escaped_name}_;" - )); - } - TypeDefKind::Tuple(tuple) => src.push_str(&format!( - "alias {escaped_name} = Tuple!({});", - tuple - .types - .iter() - .map(|ty| self.generate_type_use(ty, resolve).into_owned()) - .collect::>() - .join(", ") - )), - TypeDefKind::Variant(variant) => { - let storage_type = match variant.tag() { - Int::U8 => "ubyte", - Int::U16 => "ushort", - Int::U32 => "uint", - Int::U64 => "ulong", - }; + self.src.push_str(&format!("module {};\n\n", fqn)); - src.push_str(&format!("struct {escaped_name} {{\n")); - src.deindent(1); - src.push_str(&format!("@safe @nogc nothrow:\n")); - src.indent(1); + self.src.push_str("import wit.common;\n\n"); + if self.direction.is_some() + && let Some(WorldKey::Interface(_)) = self.name + { + self.src.push_str("public import "); + self.src + .push_str(self.r#gen.lookup_interface_fqn(id, None).unwrap()); + self.src.push_str(";\n\n"); + } - src.push_str(&format!("enum Tag : {storage_type} {{\n")); + let mut deps = BTreeSet::new(); - let mut is_first = true; - for case in &variant.cases { - if is_first { - is_first = false; - } else { - src.push_str("\n"); - } - src.push_str(&format!( - "/++\n{}\n+/\n", - case.docs.contents.as_deref().unwrap_or_default() - )); - src.push_str(&format!( - "{},\n", - escape_d_identifier(&case.name.to_lower_camel_case()) - )); - } + for dep_id in self.resolve.interface_direct_deps(id) { + deps.insert(dep_id); + } - src.push_str("}\n"); - - src.deindent(1); - src.push_str(&format!("\nprivate:\n")); - src.indent(1); - - if variant.cases.iter().any(|case| case.ty.is_some()) { - src.push_str(&format!("union Storage {{\n")); - src.push_str("ubyte __zeroinit = 0;\n"); - for case in &variant.cases { - if let Some(ty) = &case.ty { - src.push_str(&format!( - "{} {};\n", - self.generate_type_use(ty, resolve), - escape_d_identifier(&case.name.to_lower_camel_case()) - )); - } + for dep_id in deps { + let common_fqn = self.r#gen.lookup_interface_fqn(dep_id, None).unwrap(); + let directional_fqn = self.r#gen.lookup_interface_fqn(dep_id, self.direction); + + if let Some(WorldKey::Interface(_)) = self.name { + self.src.push_str(&format!( + "static import {};\n", + match self.direction { + Some(_) => directional_fqn.unwrap_or(common_fqn), + None => common_fqn, } + )); + } else { + self.src + .push_str(&format!("static import {};\n", common_fqn)); - src.push_str("}\n\n"); - - src.push_str("Tag _tag;\n"); - src.push_str("Storage _storage;\n\n"); - } else { - src.push_str("Tag _tag;\n\n"); - } - - src.push_str("@disable this();\n"); - src.push_str("this(Tag tag, Storage storage = Storage.init) {\n"); - src.push_str("_tag = tag;\n"); - src.push_str("_storage = storage;\n"); - src.push_str("}\n"); + if let Some(fqn) = directional_fqn { + self.src.push_str(&format!("static import {};\n", fqn)); + }; + } + } + self.src.push_str("\n"); + } - src.deindent(1); - src.push_str(&format!("\npublic:\n")); - src.indent(1); + fn type_is_direction_sensitive(&self, id: TypeId) -> bool { + let type_info = &self.r#gen.types.get(id); - src.push_str("Tag tag() => _tag;\n"); + type_info.has_resource + } +} - for case in &variant.cases { - src.push_str(&format!( - "\n/++\n{}\n+/\n", - case.docs.contents.as_deref().unwrap_or_default() - )); - let upper_case_name = case.name.to_upper_camel_case(); - let escaped_upper_case_name = escape_d_identifier(&upper_case_name); - - let lower_case_name = case.name.to_lower_camel_case(); - let escaped_lower_case_name = escape_d_identifier(&lower_case_name); - - if let Some(ty) = &case.ty { - src.push_str(&format!( - "static {escaped_name} {escaped_lower_case_name}({} val) {{\n", - self.generate_type_use(ty, resolve) - )); - src.push_str("Storage storage;\n"); - src.push_str(&format!("storage.{escaped_lower_case_name} = val;\n")); - src.push_str(&format!( - "return {escaped_name}(Tag.{escaped_lower_case_name}, storage);\n" - )); - src.push_str("}\n"); - - src.push_str(&format!( - "/// ditto\n ref inout({}) get{escaped_upper_case_name}() inout return\n", - self.generate_type_use(ty, resolve) - )); - - src.push_str(&format!("in (is{escaped_upper_case_name}) ")); - src.push_str(&format!( - "do {{ return _storage.{escaped_lower_case_name}; }}\n" - )); - } else { - src.push_str(&format!( - "static {escaped_name} {escaped_lower_case_name}() => {escaped_name}(Tag.{escaped_lower_case_name});\n", - )); - } - src.push_str(&format!( - "/// ditto\nbool is{escaped_upper_case_name}() const => _tag == Tag.{escaped_lower_case_name};\n", - )); - } +impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { + fn resolve(&self) -> &'a Resolve { + self.resolve + } - src.push_str("}\n"); + // Override `types` to filter by `self.direction` + fn types(&mut self, iface: InterfaceId) { + let iface = &self.resolve().interfaces[iface]; + for (name, id) in iface.types.iter() { + if self.direction.is_some() == self.type_is_direction_sensitive(*id) { + self.define_type(name, *id); } - TypeDefKind::Enum(r#enum) => { - let storage_type = match r#enum.tag() { - Int::U8 => "ubyte", - Int::U16 => "ushort", - Int::U32 => "uint", - Int::U64 => "ulong", - }; + } + } - src.push_str(&format!("enum {escaped_name} : {storage_type} {{\n")); + fn type_record(&mut self, id: TypeId, name: &str, record: &Record, docs: &Docs) { + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); - let mut is_first = true; - for case in &r#enum.cases { - if is_first { - is_first = false; - } else { - src.push_str("\n"); - } - src.push_str(&format!( - "/++\n{}\n+/\n", - case.docs.contents.as_deref().unwrap_or_default() - )); - src.push_str(&format!( - "{},\n", - escape_d_identifier(&case.name.to_lower_camel_case()) - )); - } + let owner_fqn = self + .type_owner_fqn(&self.resolve.types[id].owner) + .unwrap() + .to_string(); - src.push_str(&format!("}}")); - } - TypeDefKind::Option(opt_type) => src.push_str(&format!( - "alias {escaped_name} = Option!({});", - self.generate_type_use(opt_type, resolve) - )), - TypeDefKind::Result(result) => src.push_str(&format!( - "alias {escaped_name} = Result!({}, {});", - match result.ok { - Some(ok_type) => self.generate_type_use(&ok_type, resolve), - None => Cow::Borrowed("void"), - }, - match result.err { - Some(err_type) => self.generate_type_use(&err_type, resolve), - None => Cow::Borrowed("void"), - } - )), - TypeDefKind::List(list_type) => src.push_str(&format!( - "alias {escaped_name} = List!({});", - self.generate_type_use(list_type, resolve) - )), - TypeDefKind::Map(_, _) => { - todo!("def of `TypeDefKind::Map`"); - } - TypeDefKind::FixedLengthList(list_type, length) => { - src.push_str(&format!( - "alias {escaped_name} = {}[{length}];", - self.generate_type_use(&list_type, resolve), - )); - } - TypeDefKind::Future(future_type) => { - todo!("def of `TypeDefKind::Future`"); - } - TypeDefKind::Stream(stream_type) => { - todo!("def of `TypeDefKind::Stream`"); - } - TypeDefKind::Type(target_type) => { - src.push_str(&format!( - "alias {escaped_name} = {};", - self.generate_type_use(&target_type, resolve), - )); - } - TypeDefKind::Unknown => { - panic!("Trying to emit type declaration for `TypeDefKind::Unknown`?"); + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + docs.contents.as_deref().unwrap_or_default() + )); + self.src.push_str(&format!("struct {escaped_name} {{\n")); + + let mut is_first = true; + for field in &record.fields { + if is_first { + is_first = false; + } else { + self.src.push_str("\n"); } + + self.src.push_str(&format!( + "/++\n{}\n+/\n", + field.docs.contents.as_deref().unwrap_or_default() + )); + self.src.push_str(&format!( + "{} {};\n", + self.type_name(&field.ty, &owner_fqn), + field.name.to_lower_camel_case() + )); } - src.push_str("\n"); - src + + self.src.push_str("}\n"); } -} -impl WorldGenerator for D { - fn uses_nominal_type_ids(&self) -> bool { - false + fn type_resource(&mut self, _id: TypeId, name: &str, _docs: &Docs) { + self.src + .push_str(&format!("// TODO: def of `resource` - {name}\n")) + //todo!("def of `resource`") } - fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { - self.cur_world_fqn = get_world_fqn(world, resolve); + fn type_tuple(&mut self, id: TypeId, name: &str, tuple: &Tuple, docs: &Docs) { + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); - let world = &resolve.worlds[world]; + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + docs.contents.as_deref().unwrap_or_default() + )); - self.world_src.push_str(&format!( - "/++\n{}\n+/\n", - world.docs.contents.as_deref().unwrap_or_default() + let owner_fqn = self.type_owner_fqn(&self.resolve.types[id].owner).unwrap(); + self.src.push_str(&format!( + "alias {escaped_name} = Tuple!({});", + tuple + .types + .iter() + .map(|ty| self.type_name(ty, owner_fqn).into_owned()) + .collect::>() + .join(", ") )); + } - self.world_src - .push_str(&format!("module {};\n\n", self.cur_world_fqn)); + fn type_flags(&mut self, _id: TypeId, name: &str, flags: &Flags, docs: &Docs) { + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); - self.world_src.push_str("import wit.common;\n\n"); + let storage_type = match flags.repr() { + FlagsRepr::U8 => "ubyte", + FlagsRepr::U16 => "ushort", + FlagsRepr::U32(1) => "uint", + FlagsRepr::U32(2) => "ulong", + repr => todo!("flags {repr:?}"), + }; - self.world_src.push_str("// Interface imports\n"); - } + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + docs.contents.as_deref().unwrap_or_default() + )); + self.src.push_str(&format!("struct {escaped_name} {{\n")); - fn import_interface( - &mut self, - resolve: &Resolve, - name: &WorldKey, - id: InterfaceId, - _files: &mut Files, - ) -> Result<()> { - self.cur_interface = Some(id); - let interface_src = match self.interfaces.get_mut(&id) { - Some(src) => src, - None => { - eprintln!("Import {id:?}"); - let new_fqn = get_interface_fqn(name, &self.cur_world_fqn, resolve, false); - - let mut result_init = InterfaceSource::default(); - result_init.fqn = new_fqn; - self.interfaces.insert(id, result_init); - - let new_src = self.prepare_interface_bindings( - id, - &self.interfaces.get(&id).unwrap().fqn, - &self.cur_world_fqn, - resolve, - ); - - let result = self.interfaces.get_mut(&id).unwrap(); - result.src = new_src; - result + self.src + .push_str(&format!("mixin WitFlags!{storage_type};\n\n")); + + for (index, flag) in flags.flags.iter().enumerate() { + if index != 0 { + self.src.push_str("\n"); } + self.src.push_str(&format!( + "/++\n{}\n+/\n", + flag.docs.contents.as_deref().unwrap_or_default() + )); + self.src.push_str(&format!( + "enum {} = {escaped_name}[{index}];\n", + escape_d_identifier(&flag.name.to_lower_camel_case()) + )); + } + self.src.push_str(&format!("}}\n")); + } + + fn type_variant(&mut self, id: TypeId, name: &str, variant: &Variant, docs: &Docs) { + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); + + let storage_type = match variant.tag() { + Int::U8 => "ubyte", + Int::U16 => "ushort", + Int::U32 => "uint", + Int::U64 => "ulong", }; - if interface_src.imported { - return Ok(()); + let owner_fqn = self + .type_owner_fqn(&self.resolve.types[id].owner) + .unwrap() + .to_string(); + + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + docs.contents.as_deref().unwrap_or_default() + )); + self.src.push_str(&format!("struct {escaped_name} {{\n")); + self.src.deindent(1); + self.src.push_str("@safe @nogc nothrow:\n"); + self.src.indent(1); + + self.src + .push_str(&format!("enum Tag : {storage_type} {{\n")); + + let mut is_first = true; + for case in &variant.cases { + if is_first { + is_first = false; + } else { + self.src.push_str("\n"); + } + self.src.push_str(&format!( + "/++\n{}\n+/\n", + case.docs.contents.as_deref().unwrap_or_default() + )); + self.src.push_str(&format!( + "{},\n", + escape_d_identifier(&case.name.to_lower_camel_case()) + )); } - interface_src.imported = true; - self.world_src - .push_str(&format!("public import {};\n", &self.interfaces[&id].fqn)); + self.src.push_str("}\n"); - self.cur_interface = None; - Ok(()) - } + self.src.deindent(1); + self.src.push_str("\nprivate:\n"); + self.src.indent(1); - fn import_types( - &mut self, - resolve: &Resolve, - world: WorldId, - types: &[(&str, TypeId)], - _files: &mut Files, - ) { - self.world_src.push_str(&format!("\n// Type imports\n")); - for (name, id) in types { - let type_src = self.generate_type_declaration(name, *id, resolve); - self.world_src.append_src(&type_src); + if variant.cases.iter().any(|case| case.ty.is_some()) { + self.src.push_str("union Storage {\n"); + self.src.push_str("ubyte __zeroinit = 0;\n"); + for case in &variant.cases { + if let Some(ty) = &case.ty { + self.src.push_str(&format!( + "{} {};\n", + self.type_name(ty, &owner_fqn), + escape_d_identifier(&case.name.to_lower_camel_case()) + )); + } + } + + self.src.push_str("}\n\n"); + + self.src.push_str("Tag _tag;\n"); + self.src.push_str("Storage _storage;\n\n"); + } else { + self.src.push_str("Tag _tag;\n\n"); } - } - fn import_funcs( - &mut self, - resolve: &Resolve, - world: WorldId, - funcs: &[(&str, &Function)], - _files: &mut Files, - ) { - self.world_src.push_str(&format!("\n// Function imports\n")); - for (name, func) in funcs { - self.world_src - .push_str(&format!("// Import function: {name}\n")); + self.src.push_str("@disable this();\n"); + self.src + .push_str("this(Tag tag, Storage storage = Storage.init) {\n"); + self.src.push_str("_tag = tag;\n"); + self.src.push_str("_storage = storage;\n"); + self.src.push_str("}\n"); + + self.src.deindent(1); + self.src.push_str("\npublic:\n"); + self.src.indent(1); + + self.src.push_str("Tag tag() => _tag;\n"); + + for case in &variant.cases { + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + case.docs.contents.as_deref().unwrap_or_default() + )); + let upper_case_name = case.name.to_upper_camel_case(); + let escaped_upper_case_name = escape_d_identifier(&upper_case_name); + + let lower_case_name = case.name.to_lower_camel_case(); + let escaped_lower_case_name = escape_d_identifier(&lower_case_name); + + if let Some(ty) = &case.ty { + self.src.push_str(&format!( + "static {escaped_name} {escaped_lower_case_name}({} val) {{\n", + self.type_name(ty, &owner_fqn) + )); + self.src.push_str("Storage storage;\n"); + self.src + .push_str(&format!("storage.{escaped_lower_case_name} = val;\n")); + self.src.push_str(&format!( + "return {escaped_name}(Tag.{escaped_lower_case_name}, storage);\n" + )); + self.src.push_str("}\n"); + + self.src.push_str(&format!( + "/// ditto\n ref inout({}) get{escaped_upper_case_name}() inout return\n", + self.type_name(ty, &owner_fqn) + )); + + self.src + .push_str(&format!("in (is{escaped_upper_case_name}) ")); + self.src.push_str(&format!( + "do {{ return _storage.{escaped_lower_case_name}; }}\n" + )); + } else { + self.src.push_str(&format!( + "static {escaped_name} {escaped_lower_case_name}() => {escaped_name}(Tag.{escaped_lower_case_name});\n", + )); + } + self.src.push_str(&format!( + "/// ditto\nbool is{escaped_upper_case_name}() const => _tag == Tag.{escaped_lower_case_name};\n", + )); } + + self.src.push_str("}\n"); } - fn pre_export_interface(&mut self, resolve: &Resolve, files: &mut Files) -> Result<()> { - self.world_src.push_str("\n// Interface exports\n"); - self.world_src - .push_str("mixin template Exports(alias Impl) {\n"); + fn type_option(&mut self, id: TypeId, name: &str, payload: &Type, docs: &Docs) { + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); - Ok(()) + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + docs.contents.as_deref().unwrap_or_default() + )); + + let owner_fqn = self.type_owner_fqn(&self.resolve.types[id].owner).unwrap(); + self.src.push_str(&format!( + "alias {escaped_name} = Option!({});", + self.type_name(payload, owner_fqn) + )); } - fn export_interface( - &mut self, - resolve: &Resolve, - name: &WorldKey, - id: InterfaceId, - _files: &mut Files, - ) -> Result<()> { - self.cur_interface = Some(id); - let interface = &resolve.interfaces[id]; - let interface_src = match self.interfaces.get_mut(&id) { - Some(src) => src, - None => { - eprintln!("Export {id:?}"); - let new_fqn = get_interface_fqn(name, &self.cur_world_fqn, resolve, true); - - let mut result_init = InterfaceSource::default(); - result_init.fqn = new_fqn; - self.interfaces.insert(id, result_init); - - let new_src = self.prepare_interface_bindings( - id, - &self.interfaces.get(&id).unwrap().fqn, - &self.cur_world_fqn, - resolve, - ); - - let result = self.interfaces.get_mut(&id).unwrap(); - result.src = new_src; - result + fn type_result(&mut self, id: TypeId, name: &str, result: &Result_, docs: &Docs) { + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); + + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + docs.contents.as_deref().unwrap_or_default() + )); + + let owner_fqn = self.type_owner_fqn(&self.resolve.types[id].owner).unwrap(); + self.src.push_str(&format!( + "alias {escaped_name} = Result!({}, {});", + match result.ok { + Some(ok_type) => self.type_name(&ok_type, owner_fqn), + None => Cow::Borrowed("void"), + }, + match result.err { + Some(err_type) => self.type_name(&err_type, owner_fqn), + None => Cow::Borrowed("void"), } + )); + } + + fn type_enum(&mut self, _id: TypeId, name: &str, enum_: &Enum, docs: &Docs) { + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); + + let storage_type = match enum_.tag() { + Int::U8 => "ubyte", + Int::U16 => "ushort", + Int::U32 => "uint", + Int::U64 => "ulong", }; - if interface_src.exported { - return Ok(()); + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + docs.contents.as_deref().unwrap_or_default() + )); + self.src + .push_str(&format!("enum {escaped_name} : {storage_type} {{\n")); + + let mut is_first = true; + for case in &enum_.cases { + if is_first { + is_first = false; + } else { + self.src.push_str("\n"); + } + self.src.push_str(&format!( + "/++\n{}\n+/\n", + case.docs.contents.as_deref().unwrap_or_default() + )); + self.src.push_str(&format!( + "{},\n", + escape_d_identifier(&case.name.to_lower_camel_case()) + )); } - interface_src.exported = true; - self.world_src.push_str(&format!( - "mixin imported!\"{}\".Exports!Impl;\n", - interface_src.fqn - )); + self.src.push_str("}"); + } - interface_src - .src - .push_str("\nmixin template Exports(alias Impl) {\n"); + fn type_alias(&mut self, id: TypeId, name: &str, alias_ty: &Type, docs: &Docs) { + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); - interface_src - .src - .push_str(&format!("// Function exports\n")); - for (name, func) in &interface.functions { - interface_src - .src - .push_str(&format!("// Export function: {name}\n")); - } + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + docs.contents.as_deref().unwrap_or_default() + )); - interface_src.src.push_str("}\n"); + let typename = self.type_name( + alias_ty, + self.type_owner_fqn(&self.resolve.types[id].owner).unwrap(), + ); - self.cur_interface = None; - Ok(()) + self.src + .push_str(&format!("alias {escaped_name} = {typename};\n")); } - fn export_funcs( - &mut self, - resolve: &Resolve, - world: WorldId, - funcs: &[(&str, &Function)], - _files: &mut Files, - ) -> Result<()> { - self.world_src.push_str(&format!("\n// Function exports\n")); - for (name, func) in funcs { - self.world_src - .push_str(&format!("// Export function: {name}\n")); - } - Ok(()) + fn type_list(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); + + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + docs.contents.as_deref().unwrap_or_default() + )); + + let owner_fqn = self.type_owner_fqn(&self.resolve.types[id].owner).unwrap(); + self.src.push_str(&format!( + "alias {escaped_name} = WitList!({});", + self.type_name(ty, owner_fqn) + )); } - fn finish(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) -> Result<()> { - // Close out interface exports - self.world_src.push_str("}\n"); + fn type_fixed_length_list( + &mut self, + id: TypeId, + name: &str, + ty: &Type, + size: u32, + docs: &Docs, + ) { + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); - let mut world_filepath = PathBuf::from_iter(get_world_fqn(id, resolve).split(".")); - world_filepath.push("package.d"); + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + docs.contents.as_deref().unwrap_or_default() + )); - files.push( - world_filepath.to_str().unwrap(), - self.world_src.as_str().as_bytes(), - ); + let owner_fqn = self.type_owner_fqn(&self.resolve.types[id].owner).unwrap(); + self.src.push_str(&format!( + "alias {escaped_name} = {}[{size}];", + self.type_name(ty, owner_fqn) + )); + } - files.push("wit/common.d", include_bytes!("wit_common.d")); + fn type_future(&mut self, _id: TypeId, name: &str, _ty: &Option, _docs: &Docs) { + todo!("def of `future` - {name}"); + } - for (_, interface_src) in &self.interfaces { - let mut interface_filepath = PathBuf::from_iter(interface_src.fqn.split(".")); - interface_filepath.add_extension("d"); + fn type_stream(&mut self, _id: TypeId, name: &str, _ty: &Option, _docs: &Docs) { + todo!("def of `stream` - {name}"); + } - files.push( - interface_filepath.to_str().unwrap(), - interface_src.src.as_bytes(), - ); - } - Ok(()) + fn type_builtin(&mut self, _id: TypeId, name: &str, _ty: &Type, _docs: &Docs) { + todo!("def of `builtin` - {name}"); } } diff --git a/crates/d/src/wit_common.d b/crates/d/src/wit_common.d index f5d3f67f5..755caeca3 100644 --- a/crates/d/src/wit_common.d +++ b/crates/d/src/wit_common.d @@ -1,7 +1,7 @@ module wit.common; /// Thin CABI compliant wrapper over `T[]` -struct List(T) { +struct WitList(T) { @safe @nogc pure nothrow: T* ptr; size_t length; @@ -25,7 +25,7 @@ struct List(T) { // except list in WIT is actually List!(dchar) // // We assume UTF-8 data (as D native strings are UTF-8) -alias String = List!(immutable char); +alias WitString = List!(char); // TODO: split this file up and give Tuple a full port of the Phobos version? /// adapted from Phobos std.typecons.Tuple @@ -35,142 +35,32 @@ struct Tuple(Types...) if (is(Types)) { alias expand this; } -/// adapted from Phobos std.bitmanip.BitFlags -struct Flags(Enum) if (is(Enum == enum)) { -@safe @nogc pure nothrow: - public alias E = Enum; - -private: - template allAreBaseEnum(T...) - { - static foreach (Ti; T) - { - static if (!is(typeof(allAreBaseEnum) == bool) && // not yet defined - !is(Ti : E)) - { - enum allAreBaseEnum = false; - } - } - static if (!is(typeof(allAreBaseEnum) == bool)) // if not yet defined - { - enum allAreBaseEnum = true; - } - } - - static if (is(E U == enum)) { - alias Base = U; - } else static assert(0); - - Base mValue; - -public: - this(E flag) - { - this = flag; - } - - this(T...)(T flags) - if (allAreBaseEnum!(T)) - { - this = flags; - } +mixin template WitFlags(T) if (__traits(isUnsigned, T)) { + private alias F = typeof(this); - bool opCast(B: bool)() const - { - return mValue != 0; - } + T bits; - Base opCast(B)() const - if (is(Base : B)) - { - return mValue; - } + @safe nothrow @nogc pure: - auto opUnary(string op)() const - if (op == "~") - { - return WitFlags(cast(E) cast(Base) ~mValue); - } + static typeof(this) opIndex(size_t i) + in(i < T.sizeof*8) => F(cast(T)(1 << i)); - auto ref opAssign(T...)(T flags) - if (allAreBaseEnum!(T)) - { - mValue = 0; - foreach (E flag; flags) - { - mValue |= flag; - } - return this; - } + auto opUnary(string op : "~")() const => F(~bits); - auto ref opAssign(E flag) + auto ref opOpAssign(string op)(F rhs) + if (op == "|" || op == "&" || op == "^") { - mValue = flag; + mixin("bits "~op~"= rhs.bits;"); return this; } - auto ref opOpAssign(string op: "|")(WitFlags flags) + auto opBinary(string op)(F flags) const + if (op == "|" || op == "&" || op == "^") { - mValue |= flags.mValue; - return this; - } - - auto ref opOpAssign(string op: "&")(WitFlags flags) - { - mValue &= flags.mValue; - return this; - } - - auto ref opOpAssign(string op: "|")(E flag) - { - mValue |= flag; - return this; - } - - auto ref opOpAssign(string op: "&")(E flag) - { - mValue &= flag; - return this; - } - - auto opBinary(string op)(WitFlags flags) const - if (op == "|" || op == "&") - { - WitFlags result = this; + F result = this; result.opOpAssign!op(flags); return result; } - - auto opBinary(string op)(E flag) const - if (op == "|" || op == "&") - { - WitFlags result = this; - result.opOpAssign!op(flag); - return result; - } - - auto opBinaryRight(string op)(E flag) const - if (op == "|" || op == "&") - { - return opBinary!op(flag); - } - - bool opDispatch(string name)() const - if (__traits(hasMember, E, name)) - { - enum e = __traits(getMember, E, name); - return (mValue & e) == e; - } - - void opDispatch(string name)(bool set) - if (__traits(hasMember, E, name)) - { - enum e = __traits(getMember, E, name); - if (set) - mValue |= e; - else - mValue &= ~e; - } } /// Based on Rust's Option From 615677f46c578a8d05342460d6b8c48c5808544b Mon Sep 17 00:00:00 2001 From: Demetrius Kanios Date: Sun, 15 Mar 2026 15:39:51 -0700 Subject: [PATCH 5/6] Resource import handles, WitVariant, and raw function imports. --- crates/d/src/lib.rs | 301 ++++++++++++++++++++++++++++---------- crates/d/src/wit_common.d | 124 ++++++++++++---- 2 files changed, 321 insertions(+), 104 deletions(-) diff --git a/crates/d/src/lib.rs b/crates/d/src/lib.rs index e39a1efaa..bb719a054 100644 --- a/crates/d/src/lib.rs +++ b/crates/d/src/lib.rs @@ -4,8 +4,10 @@ use std::borrow::Cow; use std::collections::{BTreeSet, HashMap, HashSet}; use std::mem::take; use std::path::PathBuf; -use wit_bindgen_core::{Direction, Types}; -use wit_bindgen_core::{Files, InterfaceGenerator, Source, WorldGenerator, wit_parser::*}; +use wit_bindgen_core::{ + Direction, Files, InterfaceGenerator, Source, Types, WorldGenerator, abi::WasmType, + wit_parser::*, +}; #[derive(Default)] struct D { @@ -181,12 +183,26 @@ fn escape_d_identifier(name: &str) -> &str { "WitFlags" => "WitFlags_", "Option" => "Option_", "Result" => "Result_", - "bits" => "bits_", // part of WitFlags + "bits" => "bits_", // part of WitFlags + "borrow" => "borrow_", // part of the expansion of `resource` + "drop" => "drop_", // part of the expansion of `resource` s => s, } } +pub fn wasm_type(ty: WasmType) -> &'static str { + match ty { + WasmType::I32 => "uint", + WasmType::I64 => "ulong", + WasmType::F32 => "float", + WasmType::F64 => "double", + WasmType::Pointer => "void*", + WasmType::PointerOrI64 => "ulong", + WasmType::Length => "size_t", + } +} + fn get_package_fqn(id: PackageId, resolve: &Resolve) -> String { let pkg = &resolve.packages[id]; let pkg_has_multiple_versions = resolve.packages.iter().any(|(_, p)| { @@ -407,6 +423,7 @@ impl WorldGenerator for D { r#gen.interface = Some(id); r#gen.prologue(); + r#gen.src.push_str("// Types"); if let WorldKey::Name(_) = name { // We have an inline interface imported in a world. // Emit the "common" types as well @@ -418,6 +435,16 @@ impl WorldGenerator for D { r#gen.types(id); + r#gen.src.push_str("\n// Functions\n"); + for (_name, func) in &resolve.interfaces[id].functions { + match func.kind { + FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => { + r#gen.import_func(func); + } + _ => {} + } + } + let mut interface_filepath = PathBuf::from_iter(fqn.split(".")); interface_filepath.add_extension("d"); @@ -663,11 +690,11 @@ impl<'a> DInterfaceGenerator<'a> { TypeDefKind::Resource => { Cow::Owned(self.scoped_type_name(*id, from_module_fqn)) } - TypeDefKind::Handle(Handle::Own(_id)) => { - Cow::Borrowed("/* todo - type_name of `own` */") + TypeDefKind::Handle(Handle::Own(id)) => { + Cow::Owned(self.scoped_type_name(*id, from_module_fqn)) } TypeDefKind::Handle(Handle::Borrow(_id)) => { - Cow::Borrowed("/* todo - type_name of `borrow` */") + Cow::Owned(self.scoped_type_name(*id, from_module_fqn) + ".Borrow") } TypeDefKind::Tuple(t) => Cow::Owned(format!( "Tuple!({})", @@ -798,6 +825,76 @@ impl<'a> DInterfaceGenerator<'a> { type_info.has_resource } + + fn import_func(&mut self, func: &Function) { + match &func.kind { + FunctionKind::Freestanding => {} + FunctionKind::Method(_) => {} + kind => { + self.src + .push_str(&format!("// TODO: Import {kind:?} - {}\n", func.name)); + return; + } + } + + let sig = self + .resolve + .wasm_signature(abi::AbiVariant::GuestImport, func); + + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + func.docs.contents.as_deref().unwrap_or_default() + )); + self.src.push_str(&format!( + "@wasmImport!(\"{}\", \"{}\")\n", + self.wasm_import_module.unwrap(), + func.name + )); + // The mangle is not important, as long as it won't conflict with other symbols + // WebAssembly symbol identifiers are much more permissive than C (can be any UTF-8). + // Yet, LDC before 1.42 don't allow full use of this fact. We make some substitutions. + self.src.push_str(&format!( + "pragma(mangle, \"__wit_import_{}__{}\")\n", + self.wasm_import_module + .unwrap() + .replace("/", "__") + .replace("-", "_"), + func.name + .replace("-", "_") + .replace("[", ":") + .replace("]", ":") + )); + + let split_name = match &func.kind { + FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => &func.name, + FunctionKind::Method(_) + | FunctionKind::Static(_) + | FunctionKind::Constructor(_) + | FunctionKind::AsyncMethod(_) + | FunctionKind::AsyncStatic(_) => { + self.src.push_str("static "); + + func.name.split(".").skip(1).next().unwrap() + } + }; + + let lower_name = split_name.to_lower_camel_case(); + let escaped_name = escape_d_identifier(&lower_name); + + self.src.push_str(&format!( + "/*private*/ extern(C) {} __import_{escaped_name}({});\n", + match sig.results.len() { + 0 => "void", + 1 => wasm_type(sig.results[0]), + _ => unimplemented!("multi-value return not supported"), + }, + sig.params + .iter() + .map(|param| wasm_type(*param)) + .collect::>() + .join(", ") + )); + } } impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { @@ -852,9 +949,99 @@ impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { self.src.push_str("}\n"); } - fn type_resource(&mut self, _id: TypeId, name: &str, _docs: &Docs) { - self.src - .push_str(&format!("// TODO: def of `resource` - {name}\n")) + fn type_resource(&mut self, id: TypeId, name: &str, docs: &Docs) { + let upper_name = name.to_upper_camel_case(); + let escaped_name = escape_d_identifier(&upper_name); + + let ty = &self.resolve.types[id]; + + match self.direction { + None => panic!("Resources can only be generated for imports, or exports. Not common."), + Some(Direction::Import) => match ty.owner { + TypeOwner::Interface(owner_id) => { + if let Some(cur_interface) = self.interface + && cur_interface == owner_id + { + } else { + panic!("Emitting resource from `interface` outside that interface?"); + } + + self.src.push_str(&format!( + "\n/++\n{}\n+/\n", + docs.contents.as_deref().unwrap_or_default() + )); + + self.src.push_str(&format!( + "struct {escaped_name} {{ + package(wit) int __handle = 0; + + package(wit) this(int handle) {{ + __handle = handle; + }} + + @disable this(); + + // TODO: make RAII? disable copy for the own + + + auto borrow() => Borrow(__handle); + alias borrow this; + + " + )); + + for (_, func) in &self.resolve.interfaces[owner_id].functions { + if match &func.kind { + FunctionKind::Freestanding => false, + FunctionKind::Method(_) => false, + FunctionKind::Static(mid) => *mid == id, + FunctionKind::Constructor(mid) => *mid == id, + FunctionKind::AsyncFreestanding => false, + FunctionKind::AsyncMethod(_) => false, + FunctionKind::AsyncStatic(_) => todo!(), + } { + self.import_func(func); + } + } + + self.src.push_str(&format!( + "struct Borrow {{ + package(wit) int __handle = 0; + + package(wit) this(int handle) {{ + __handle = handle; + }} + + @disable this(); + + " + )); + + for (_, func) in &self.resolve.interfaces[owner_id].functions { + if match &func.kind { + FunctionKind::Freestanding => false, + FunctionKind::Method(mid) => *mid == id, + FunctionKind::Static(_) => false, + FunctionKind::Constructor(_) => false, + FunctionKind::AsyncFreestanding => false, + FunctionKind::AsyncMethod(_) => todo!(), + FunctionKind::AsyncStatic(_) => false, + } { + self.import_func(func); + } + } + + self.src.push_str("}\n"); + + self.src.push_str("}\n"); + } + TypeOwner::World(_) => todo!("resources in worlds"), + TypeOwner::None => { + panic!("Resource definition without owner?"); + } + }, + Some(Direction::Export) => todo!("export of resource"), + } //todo!("def of `resource`") } @@ -937,8 +1124,30 @@ impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { docs.contents.as_deref().unwrap_or_default() )); self.src.push_str(&format!("struct {escaped_name} {{\n")); + + self.src.push_str("mixin WitVariant!(\n"); + self.src.indent(1); + + for case in &variant.cases { + self.src.push_str(&format!( + "{}, // {}\n", + match &case.ty { + None => Cow::Borrowed("void"), + Some(ty) => self.type_name(ty, &owner_fqn), + }, + escape_d_identifier(&case.name.to_lower_camel_case()) + )); + } + self.src.deindent(1); - self.src.push_str("@safe @nogc nothrow:\n"); + self.src.push_str(");\n"); + + self.src.deindent(1); + //self.src.push_str("@safe @nogc nothrow:\n"); + self.src.indent(1); + + self.src.deindent(1); + self.src.push_str("\npublic:\n"); self.src.indent(1); self.src @@ -963,42 +1172,6 @@ impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { self.src.push_str("}\n"); - self.src.deindent(1); - self.src.push_str("\nprivate:\n"); - self.src.indent(1); - - if variant.cases.iter().any(|case| case.ty.is_some()) { - self.src.push_str("union Storage {\n"); - self.src.push_str("ubyte __zeroinit = 0;\n"); - for case in &variant.cases { - if let Some(ty) = &case.ty { - self.src.push_str(&format!( - "{} {};\n", - self.type_name(ty, &owner_fqn), - escape_d_identifier(&case.name.to_lower_camel_case()) - )); - } - } - - self.src.push_str("}\n\n"); - - self.src.push_str("Tag _tag;\n"); - self.src.push_str("Storage _storage;\n\n"); - } else { - self.src.push_str("Tag _tag;\n\n"); - } - - self.src.push_str("@disable this();\n"); - self.src - .push_str("this(Tag tag, Storage storage = Storage.init) {\n"); - self.src.push_str("_tag = tag;\n"); - self.src.push_str("_storage = storage;\n"); - self.src.push_str("}\n"); - - self.src.deindent(1); - self.src.push_str("\npublic:\n"); - self.src.indent(1); - self.src.push_str("Tag tag() => _tag;\n"); for case in &variant.cases { @@ -1012,39 +1185,19 @@ impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { let lower_case_name = case.name.to_lower_camel_case(); let escaped_lower_case_name = escape_d_identifier(&lower_case_name); - if let Some(ty) = &case.ty { - self.src.push_str(&format!( - "static {escaped_name} {escaped_lower_case_name}({} val) {{\n", - self.type_name(ty, &owner_fqn) - )); - self.src.push_str("Storage storage;\n"); - self.src - .push_str(&format!("storage.{escaped_lower_case_name} = val;\n")); - self.src.push_str(&format!( - "return {escaped_name}(Tag.{escaped_lower_case_name}, storage);\n" - )); - self.src.push_str("}\n"); - - self.src.push_str(&format!( - "/// ditto\n ref inout({}) get{escaped_upper_case_name}() inout return\n", - self.type_name(ty, &owner_fqn) - )); + self.src.push_str(&format!( + "alias {escaped_lower_case_name} = _create!(Tag.{escaped_lower_case_name});\n", + )); + self.src.push_str(&format!( + "/// ditto\nbool is{escaped_upper_case_name}() const => _tag == Tag.{escaped_lower_case_name};\n", + )); - self.src - .push_str(&format!("in (is{escaped_upper_case_name}) ")); - self.src.push_str(&format!( - "do {{ return _storage.{escaped_lower_case_name}; }}\n" - )); - } else { + if let Some(ty) = &case.ty { self.src.push_str(&format!( - "static {escaped_name} {escaped_lower_case_name}() => {escaped_name}(Tag.{escaped_lower_case_name});\n", + "///ditto\nalias get{escaped_upper_case_name} = _get!(Tag.{escaped_lower_case_name});\n", )); } - self.src.push_str(&format!( - "/// ditto\nbool is{escaped_upper_case_name}() const => _tag == Tag.{escaped_lower_case_name};\n", - )); } - self.src.push_str("}\n"); } diff --git a/crates/d/src/wit_common.d b/crates/d/src/wit_common.d index 755caeca3..e254125ed 100644 --- a/crates/d/src/wit_common.d +++ b/crates/d/src/wit_common.d @@ -1,5 +1,16 @@ module wit.common; +import core.attribute : mustuse; +import ldc.attributes : llvmAttr; + +// from std.meta +package(wit) alias AliasSeq(T...) = T; + +alias wasmImport(string mod, string name) = AliasSeq!( + llvmAttr("wasm-import-module", mod), + llvmAttr("wasm-import-name", name) +); + /// Thin CABI compliant wrapper over `T[]` struct WitList(T) { @safe @nogc pure nothrow: @@ -20,12 +31,14 @@ struct WitList(T) { return (ptr && length) ? ptr[0..length] : null; } } +auto witList(T : U[], U)(T slice) => WitList!U(slice); + // WIT ABI for string matches List, // except list in WIT is actually List!(dchar) // // We assume UTF-8 data (as D native strings are UTF-8) -alias WitString = List!(char); +alias WitString = WitList!(char); // TODO: split this file up and give Tuple a full port of the Phobos version? /// adapted from Phobos std.typecons.Tuple @@ -63,16 +76,67 @@ mixin template WitFlags(T) if (__traits(isUnsigned, T)) { } } + +mixin template WitVariant(Types...) { +private: + static assert(is(typeof(this).Tag)); + static assert(is(Tag U == enum) && __traits(isIntegral, U)); + + static assert(__traits(allMembers, Tag).length == Types.length); + static foreach (i, M; __traits(allMembers, Tag)) { + static assert(i == __traits(getMember, Tag, M)); + } + + union Storage { + template ReplacedTypes() { + alias ReplacedTypes = AliasSeq!(); + + static foreach (T; Types) { + static if (is(T == void)) + ReplacedTypes = AliasSeq!(ReplacedTypes, void[0]); + else + ReplacedTypes = AliasSeq!(ReplacedTypes, T); + } + } + + ubyte __zeroinit = 0; + ReplacedTypes!() members; + } + + Tag _tag; + Storage _storage; + + + @disable this(); + + this(Tag tag, Storage storage = Storage.init) { + _tag = tag; + _storage = storage; + } + + + static auto _create(Tag tag)() if (is(Types[tag] == void)) { + return typeof(this)(tag); + } + static auto _create(Tag tag)(Types[tag] val) if (!is(Types[tag] == void)) { + Storage storage = Storage.init; + storage.tupleof[tag+1] = val; + return typeof(this)(tag, storage); + } + + ref auto _get(Tag tag)() inout return if (!is(Types[tag] == void)) + in (_tag == tag) do { return cast(inout)_storage.tupleof[tag+1]; } +} + /// Based on Rust's Option struct Option(T) { private: - bool present = false; - T value; + bool _present = false; + T _value; - @disable this(); this(bool present, T value = T.init) @safe @nogc nothrow { - this.present = present; - this.value = value; + _present = present; + _value = value; } public: static Option some(T value) @safe @nogc nothrow { @@ -83,25 +147,26 @@ public: return Option(false); } - bool isSome() const @safe @nogc nothrow => present; + bool isSome() const @safe @nogc nothrow => _present; alias isSome this; // implicit conversion to bool - bool isNone() const @safe @nogc nothrow => !present; + bool isNone() const @safe @nogc nothrow => !_present; ref inout(T) unwrap() inout @safe @nogc nothrow return - in (present) do { return value; } + in (_present) do { return _value; } - T unwrapOr(T fallback) @safe @nogc nothrow => present ? value : fallback; + T unwrapOr(T fallback) @safe @nogc nothrow => _present ? _value : fallback; T unwrapOrElse(D)(scope D fallback) if (is(D R == return) && is(R : T) && is(D == __parameters)) - { return present ? value : fallback(); } + { return _present ? _value : fallback(); } } /// Based on Rust's Result +@mustuse struct Result(T, E) { private: - bool hasError; + bool _hasError; union Storage { ubyte __zeroinit = 0; static if (!is(T == void)) { @@ -111,20 +176,19 @@ private: E error; } } - Storage storage; + Storage _storage; - @disable this(); this(bool hasError, Storage storage) @safe @nogc nothrow { - this.hasError = hasError; - this.storage = storage; + _hasError = hasError; + _storage = storage; } public: static if (is(T == void)) { - static Result ok() @safe @nogc nothrow => Result(false, Storage()); + static Result ok() @safe @nogc nothrow => Result(false, Storage.init); } else { - static Result ok(T value) @safe @nogc nothrow { - Storage newStorage; + static Result ok(T value) @trusted @nogc nothrow { + Storage newStorage = Storage.init; newStorage.value = value; return Result(false, newStorage); @@ -132,34 +196,34 @@ public: } static if (is(E == void)) { - static Result err() @safe @nogc nothrow => Result(true, Storage()); + static Result err() @safe @nogc nothrow => Result(true, Storage.init); } else { - static Result err(E error) @safe @nogc nothrow { - Storage newStorage; + static Result err(E error) @trusted @nogc nothrow { + Storage newStorage = Storage.init; newStorage.error = error; return Result(true, newStorage); } } - bool isOk() const @safe @nogc nothrow => !hasError; + bool isOk() const @safe @nogc nothrow => !_hasError; - bool isErr() const @safe @nogc nothrow => hasError; + bool isErr() const @safe @nogc nothrow => _hasError; alias isErr this; // implicit conversion to bool static if (!is(T == void)) { - ref inout(T) unwrap() inout @safe @nogc nothrow return - in (isOk) do { return storage.value; } + ref inout(T) unwrap() inout @trusted @nogc nothrow return + in (isOk) do { return _storage.value; } - T unwrapOr(T fallback) @safe @nogc nothrow => isOk ? storage.value : fallback; + T unwrapOr(T fallback) @safe @nogc nothrow => isOk ? _storage.value : fallback; T unwrapOrElse(D)(scope D fallback) if (is(D R == return) && is(R : T) && is(D == __parameters)) - { return isOk ? storage.value : fallback(); } + { return isOk ? _storage.value : fallback(); } } static if (!is(E == void)) { - ref inout(E) unwrapErr() inout @safe @nogc nothrow return - in (isErr) do { return storage.error; } + ref inout(E) unwrapErr() inout @trusted @nogc nothrow return + in (isErr) do { return _storage.error; } } } From 22ba37d4f9cfaa013374892a416da9512d61d23f Mon Sep 17 00:00:00 2001 From: Demetrius Kanios Date: Wed, 18 Mar 2026 00:54:30 -0700 Subject: [PATCH 6/6] Initial pass implementing of `FunctionBindgen` for imports. [skip-ci] --- crates/d/src/lib.rs | 949 ++++++++++++++++++++++++++++++++++++-- crates/d/src/wit_common.d | 18 +- 2 files changed, 914 insertions(+), 53 deletions(-) diff --git a/crates/d/src/lib.rs b/crates/d/src/lib.rs index bb719a054..b0de7ddc8 100644 --- a/crates/d/src/lib.rs +++ b/crates/d/src/lib.rs @@ -2,13 +2,27 @@ use anyhow::Result; use heck::*; use std::borrow::Cow; use std::collections::{BTreeSet, HashMap, HashSet}; -use std::mem::take; +use std::mem::{replace, take}; use std::path::PathBuf; use wit_bindgen_core::{ - Direction, Files, InterfaceGenerator, Source, Types, WorldGenerator, abi::WasmType, + Direction, Files, InterfaceGenerator, Source, Types, WorldGenerator, + abi::{self, Bindgen, WasmType}, wit_parser::*, }; +type DType = String; +#[derive(Default, Debug)] +struct DSig { + const_member: bool, + static_member: bool, + result: DType, + arguments: Vec<(String, DType)>, + name: String, + //namespace: Vec, + implicit_self: bool, + post_return: bool, +} + #[derive(Default)] struct D { used_interfaces: HashSet<(WorldKey, InterfaceId)>, @@ -693,7 +707,7 @@ impl<'a> DInterfaceGenerator<'a> { TypeDefKind::Handle(Handle::Own(id)) => { Cow::Owned(self.scoped_type_name(*id, from_module_fqn)) } - TypeDefKind::Handle(Handle::Borrow(_id)) => { + TypeDefKind::Handle(Handle::Borrow(id)) => { Cow::Owned(self.scoped_type_name(*id, from_module_fqn) + ".Borrow") } TypeDefKind::Tuple(t) => Cow::Owned(format!( @@ -709,14 +723,8 @@ impl<'a> DInterfaceGenerator<'a> { } TypeDefKind::Result(r) => Cow::Owned(format!( "Result!({}, {})", - match r.ok { - Some(ok_type) => self.type_name(&ok_type, from_module_fqn), - None => Cow::Borrowed("void"), - }, - match r.err { - Some(err_type) => self.type_name(&err_type, from_module_fqn), - None => Cow::Borrowed("void"), - } + self.optional_type_name(r.ok.as_ref(), from_module_fqn), + self.optional_type_name(r.err.as_ref(), from_module_fqn), )), TypeDefKind::List(ty) => Cow::Owned(format!( "WitList!({})", @@ -746,6 +754,13 @@ impl<'a> DInterfaceGenerator<'a> { } } + fn optional_type_name(&self, ty: Option<&Type>, from_module_fqn: &str) -> Cow<'static, str> { + match ty { + Some(ty) => self.type_name(ty, from_module_fqn), + None => Cow::Borrowed("void"), + } + } + fn type_owner_fqn(&self, owner: &TypeOwner) -> Option<&str> { match &owner { TypeOwner::None => None, @@ -826,6 +841,67 @@ impl<'a> DInterfaceGenerator<'a> { type_info.has_resource } + fn get_d_signature(&mut self, func: &Function) -> DSig { + match &func.kind { + FunctionKind::Freestanding | FunctionKind::Method(_) => {} + + FunctionKind::AsyncFreestanding + | FunctionKind::Static(_) + | FunctionKind::Constructor(_) + | FunctionKind::AsyncMethod(_) + | FunctionKind::AsyncStatic(_) => { + todo!() + } + } + + let mut res = DSig::default(); + + let split_name = match &func.kind { + FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => &func.name, + FunctionKind::Method(_) + | FunctionKind::Static(_) + | FunctionKind::Constructor(_) + | FunctionKind::AsyncMethod(_) + | FunctionKind::AsyncStatic(_) => func.name.split(".").skip(1).next().unwrap(), + }; + + let lower_name = split_name.to_lower_camel_case(); + let escaped_name = escape_d_identifier(&lower_name); + + res.name = escaped_name.into(); + + res.result + .push_str(&(self.optional_type_name(func.result.as_ref(), self.fqn))); + + for ( + i, + Param { + name, ty: param, .. + }, + ) in func.params.iter().enumerate() + { + if i == 0 && name == "self" { + match &func.kind { + FunctionKind::Method(_) => { + res.implicit_self = true; + continue; + } + _ => {} + } + } + + let lower_param_name = name.to_lower_camel_case(); + let escaped_param_name = escape_d_identifier(&lower_param_name); + + res.arguments.push(( + escaped_param_name.into(), + self.type_name(¶m, self.fqn).into(), + )); + } + + res + } + fn import_func(&mut self, func: &Function) { match &func.kind { FunctionKind::Freestanding => {} @@ -837,22 +913,68 @@ impl<'a> DInterfaceGenerator<'a> { } } - let sig = self + let wasm_sig = self .resolve .wasm_signature(abi::AbiVariant::GuestImport, func); + let d_sig = self.get_d_signature(func); + self.src.push_str(&format!( "\n/++\n{}\n+/\n", func.docs.contents.as_deref().unwrap_or_default() )); + + if d_sig.static_member { + self.src.push_str("static "); + } + self.src.push_str(&format!( + "{} {}({}) {{\n", + d_sig.result, + d_sig.name, + d_sig + .arguments + .iter() + .map(|(name, ty)| "in ".to_owned() + ty + " " + name) + .collect::>() + .join(", ") + )); + + let mut params = Vec::new(); + + if d_sig.implicit_self { + params.push("this"); + } + for (arg, ty) in &d_sig.arguments { + params.push(arg); + } + + let mut f = FunctionBindgen::new(self, ¶ms); + abi::call( + f.r#gen.resolve, + abi::AbiVariant::GuestImport, + abi::LiftLower::LowerArgsLiftResults, + func, + &mut f, + false, + ); + let ret_area_decl = f.emit_ret_area_if_needed(); + + let FunctionBindgen { src, .. } = f; + self.src.push_str(&ret_area_decl); + self.src.push_str(&src.to_string()); + + self.src.push_str("}\n"); + + self.src.push_str("/// ditto\n"); self.src.push_str(&format!( "@wasmImport!(\"{}\", \"{}\")\n", self.wasm_import_module.unwrap(), func.name )); + // The mangle is not important, as long as it won't conflict with other symbols // WebAssembly symbol identifiers are much more permissive than C (can be any UTF-8). - // Yet, LDC before 1.42 don't allow full use of this fact. We make some substitutions. + // Yet, LDC before 1.42 doesn't allow full use of this fact. We make some substitutions. self.src.push_str(&format!( "pragma(mangle, \"__wit_import_{}__{}\")\n", self.wasm_import_module @@ -865,30 +987,16 @@ impl<'a> DInterfaceGenerator<'a> { .replace("]", ":") )); - let split_name = match &func.kind { - FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => &func.name, - FunctionKind::Method(_) - | FunctionKind::Static(_) - | FunctionKind::Constructor(_) - | FunctionKind::AsyncMethod(_) - | FunctionKind::AsyncStatic(_) => { - self.src.push_str("static "); - - func.name.split(".").skip(1).next().unwrap() - } - }; - - let lower_name = split_name.to_lower_camel_case(); - let escaped_name = escape_d_identifier(&lower_name); - self.src.push_str(&format!( - "/*private*/ extern(C) {} __import_{escaped_name}({});\n", - match sig.results.len() { + "static private extern(C) {} __import_{}({});\n", + match wasm_sig.results.len() { 0 => "void", - 1 => wasm_type(sig.results[0]), + 1 => wasm_type(wasm_sig.results[0]), _ => unimplemented!("multi-value return not supported"), }, - sig.params + d_sig.name, + wasm_sig + .params .iter() .map(|param| wasm_type(*param)) .collect::>() @@ -973,9 +1081,9 @@ impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { self.src.push_str(&format!( "struct {escaped_name} {{ - package(wit) int __handle = 0; + package(wit) uint __handle = 0; - package(wit) this(int handle) {{ + package(wit) this(uint handle) {{ __handle = handle; }} @@ -1004,11 +1112,34 @@ impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { } } + self.src + .push_str("void drop() {\n__import__drop(__handle);\n}\n"); + + self.src.push_str(&format!( + "@wasmImport!(\"{}\", \"[resource-drop]{}\")\n", + self.wasm_import_module.unwrap(), + name + )); + + // The mangle is not important, as long as it won't conflict with other symbols + // WebAssembly symbol identifiers are much more permissive than C (can be any UTF-8). + // Yet, LDC before 1.42 doesn't allow full use of this fact. We make some substitutions. + self.src.push_str(&format!( + "pragma(mangle, \"__wit_import_{}__:resource_drop:{}\")\n", + self.wasm_import_module + .unwrap() + .replace("/", "__") + .replace("-", "_"), + name.replace("-", "_") + )); + self.src + .push_str("static private extern(C) void __import__drop(uint);\n\n"); + self.src.push_str(&format!( "struct Borrow {{ - package(wit) int __handle = 0; + package(wit) uint __handle = 0; - package(wit) this(int handle) {{ + package(wit) this(uint handle) {{ __handle = handle; }} @@ -1131,10 +1262,7 @@ impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { for case in &variant.cases { self.src.push_str(&format!( "{}, // {}\n", - match &case.ty { - None => Cow::Borrowed("void"), - Some(ty) => self.type_name(ty, &owner_fqn), - }, + self.optional_type_name(case.ty.as_ref(), &owner_fqn), escape_d_identifier(&case.name.to_lower_camel_case()) )); } @@ -1172,7 +1300,7 @@ impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { self.src.push_str("}\n"); - self.src.push_str("Tag tag() => _tag;\n"); + self.src.push_str("Tag tag() const => _tag;\n"); for case in &variant.cases { self.src.push_str(&format!( @@ -1229,14 +1357,8 @@ impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { let owner_fqn = self.type_owner_fqn(&self.resolve.types[id].owner).unwrap(); self.src.push_str(&format!( "alias {escaped_name} = Result!({}, {});", - match result.ok { - Some(ok_type) => self.type_name(&ok_type, owner_fqn), - None => Cow::Borrowed("void"), - }, - match result.err { - Some(err_type) => self.type_name(&err_type, owner_fqn), - None => Cow::Borrowed("void"), - } + self.optional_type_name(result.ok.as_ref(), owner_fqn), + self.optional_type_name(result.err.as_ref(), owner_fqn), )); } @@ -1347,3 +1469,730 @@ impl<'a> InterfaceGenerator<'a> for DInterfaceGenerator<'a> { todo!("def of `builtin` - {name}"); } } + +struct FunctionBindgen<'a, 'b> { + r#gen: &'b mut DInterfaceGenerator<'a>, + params: &'b [&'b str], + tmp: usize, + src: Source, + block_storage: Vec, + /// intermediate calculations for contained objects + blocks: Vec<(String, Vec)>, + payloads: Vec, + return_pointer_area_size: ArchitectureSize, + return_pointer_area_align: Alignment, +} + +fn tempname(base: &str, idx: usize) -> String { + format!("{base}{idx}") +} + +impl<'a, 'b> FunctionBindgen<'a, 'b> { + fn new(r#gen: &'b mut DInterfaceGenerator<'a>, params: &'b [&'b str]) -> Self { + Self { + r#gen, + params, + tmp: 0, + src: Default::default(), + block_storage: Default::default(), + blocks: Default::default(), + payloads: Default::default(), + return_pointer_area_size: Default::default(), + return_pointer_area_align: Default::default(), + } + } + + fn tmp(&mut self) -> usize { + let ret = self.tmp; + self.tmp += 1; + ret + } + + fn push_str(&mut self, s: &str) { + self.src.push_str(s); + } + + fn load( + &mut self, + ty: &str, + offset: ArchitectureSize, + operands: &[String], + results: &mut Vec, + ) { + results.push(format!( + "*(cast({}*)({} + {}))", + ty, + operands[0], + offset.format("size_t.sizeof") + )); + } + + fn load_ext( + &mut self, + ty: &str, + offset: ArchitectureSize, + operands: &[String], + results: &mut Vec, + ) { + self.load(ty, offset, operands, results); + let result = results.pop().unwrap(); + results.push(format!("cast(uint)({result})")); + } + + fn store(&mut self, ty: &str, offset: ArchitectureSize, operands: &[String]) { + self.push_str(&format!( + "*(cast({ty}*)({} + {})) = cast({ty})({});\n", + operands[1], + offset.format("size_t.sizeof"), + operands[0] + )); + } + + /// Emits a shared return area declaration if needed by this function. + /// + /// During code generation, `return_pointer()` may be called multiple times for: + /// - Indirect parameter storage (when too many/large params) + /// - Return value storage (when return type is too large) + /// + /// **Safety:** This is safe because return pointers are used sequentially: + /// 1. Parameter marshaling (before call) + /// 2. Function execution + /// 3. Return value unmarshaling (after call) + /// + /// The scratch space is reused but never accessed simultaneously. + fn emit_ret_area_if_needed(&self) -> String { + if !self.return_pointer_area_size.is_empty() { + format!( + "align({}) void[{}] _retArea = void;\n", + self.return_pointer_area_align.format("size_t.sizeof"), + self.return_pointer_area_size.format("size_t.sizeof") + ) + } else { + String::new() + } + } +} + +impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { + type Operand = String; + + fn emit( + &mut self, + _resolve: &Resolve, + inst: &wit_bindgen_core::abi::Instruction<'_>, + operands: &mut Vec, + results: &mut Vec, + ) { + let mut top_as = |cvt: &str| { + results.push(format!("(cast({cvt})({}))", operands.pop().unwrap())); + }; + + match inst { + abi::Instruction::GetArg { nth } => { + if *nth == 0 && &self.params[0] == &"self" { + results.push("this".into()); + } else { + results.push(self.params[*nth].into()); + } + } + abi::Instruction::I32Const { val } => results.push(val.to_string()), + abi::Instruction::ConstZero { tys } => { + for _ in tys.iter() { + results.push("0".to_string()); + } + } + abi::Instruction::ListCanonLower { .. } | abi::Instruction::StringLower { .. } => { + results.push(format!("cast(void*)({}.ptr)", operands[0])); + results.push(format!("{}.length", operands[0])); + } + abi::Instruction::ListCanonLift { element, ty, .. } => { + let list_name = self.r#gen.type_name(&Type::Id(*ty), self.r#gen.fqn); + let elem_name = self.r#gen.type_name(element, self.r#gen.fqn); + let tmp = self.tmp(); + + let ptr = tempname("_ptr", tmp); + let len = tempname("_len", tmp); + + self.push_str(&format!( + "auto {ptr} = cast({elem_name}*)({}); + auto {len} = {}; + ", + operands[0], operands[1] + )); + + results.push(format!("{}({ptr}[0..{len}])", list_name)); + } + + abi::Instruction::IterElem { .. } => results.push("_elem".into()), + abi::Instruction::IterBasePointer => results.push("_base".into()), + abi::Instruction::ListLower { element, .. } => { + let body = self.blocks.pop().unwrap(); + let tmp = self.tmp(); + + let size = self.r#gen.sizes.size(element); + let size_str = size.format("size_t.sizeof"); + + let list = tempname("_list", tmp); + let list_src = tempname("_listSrc", tmp); + + self.push_str(&format!( + "auto {list_src} = {}; + auto {list} = wit.common.malloc({list_src}.length * ({size_str})); + scope(exit) {{ wit.common.free({list}); }}\n", + operands[0] + )); + + self.push_str(&format!("foreach (i, const ref _elem; {list_src}) {{\n")); + self.push_str(&format!("auto _base = {list} + i * {size_str};\n")); + self.push_str(&body.0); + //self.push_str(&format!("_targetElem = {};", body.1[0])); + self.push_str("\n}\n"); + + results.push(format!("{list}")); + results.push(format!("{}.length", operands[0])); + } + abi::Instruction::ListLift { ty, element, .. } => { + let body = self.blocks.pop().unwrap(); + let tmp = self.tmp(); + let size = self.r#gen.sizes.size(element); + let size_str = size.format("size_t.sizeof"); + let elem_type_name = self.r#gen.type_name(element, self.r#gen.fqn); + + let list = tempname("_list", tmp); + let list_len = tempname("_listLen", tmp); + let list_src = tempname("_listSrcPtr", tmp); + self.push_str(&format!("auto {list_src} = {};\n", operands[0])); + self.push_str(&format!("auto {list_len} = {};\n", operands[1])); + self.push_str(&format!( + "auto {list} = wit.common.mallocSlice!({elem_type_name})({list_len});\n", + )); + + self.push_str(&format!("foreach (i, ref _elem; {list}) {{\n",)); + self.push_str(&format!( + "const auto _base = {list_src} + i * {size_str};\n" + )); + self.push_str(&body.0); + self.push_str(&format!("_elem = {};", body.1[0])); + self.push_str("\n}\n"); + + let list_name = self.r#gen.type_name(&Type::Id(*ty), self.r#gen.fqn); + results.push(format!("{list_name}({list})")); + } + abi::Instruction::RecordLower { record, .. } => { + for field in record.fields.iter() { + let lower_name = field.name.to_lower_camel_case(); + let escaped_name = escape_d_identifier(&lower_name); + + results.push(format!("{}.{escaped_name}", operands[0])); + } + } + abi::Instruction::RecordLift { ty, record, .. } => { + let name = self.r#gen.type_name(&Type::Id(*ty), self.r#gen.fqn); + + let mut tmpvar = tempname("_record", self.tmp()); + + self.push_str(&format!("{name} {tmpvar} = {{\n")); + for (field, op) in record.fields.iter().zip(operands.iter()) { + let lower_name = field.name.to_lower_camel_case(); + let escaped_name = escape_d_identifier(&lower_name); + + self.push_str(&format!("{escaped_name}: {op},\n")); + } + self.push_str("};\n"); + + results.push(tmpvar); + } + + abi::Instruction::TupleLower { tuple, .. } => { + for i in 0..tuple.types.len() { + results.push(format!("{}[{i}]", &operands[0])); + } + } + + abi::Instruction::TupleLift { tuple, ty, .. } => { + let name = tempname("_tuple", self.tmp()); + self.push_str(&format!( + "auto {name} = {}(\n", + self.r#gen.type_name(&Type::Id(*ty), self.r#gen.fqn), + )); + self.src.indent(1); + for op in operands.iter() { + self.push_str(op); + self.push_str(",\n"); + } + self.src.deindent(1); + self.push_str(");\n"); + results.push(name); + } + + abi::Instruction::StringLift { .. } => { + let tmp = self.tmp(); + + let ptr = tempname("_ptr", tmp); + let len = tempname("_len", tmp); + + self.push_str(&format!( + "auto {ptr} = cast(char*)({}); + auto {len} = {}; + ", + operands[0], operands[1] + )); + + results.push(format!("WitString({ptr}[0..{len}])")); + } + + abi::Instruction::VariantPayloadName => { + let name = tempname("_payload", self.tmp()); + results.push(name.clone()); + self.payloads.push(name); + } + abi::Instruction::VariantLower { + ty, + variant, + results: result_types, + .. + } => { + let blocks = self + .blocks + .drain(self.blocks.len() - variant.cases.len()..) + .collect::>(); + let payloads = self + .payloads + .drain(self.payloads.len() - variant.cases.len()..) + .collect::>(); + + let mut variant_results = Vec::with_capacity(result_types.len()); + for res_ty in result_types.iter() { + let name = tempname("_variantPart", self.tmp()); + results.push(name.clone()); + self.src + .push_str(&format!("{} {name} = void;\n", wasm_type(*res_ty))); + variant_results.push(name); + } + + let ty_name = self.r#gen.type_name(&Type::Id(*ty), self.r#gen.fqn); + + self.push_str(&format!( + "final switch ({}.tag) with ({ty_name}.Tag) {{\n", + operands[0] + )); + + for (i, ((case, (block, block_results)), payload)) in + variant.cases.iter().zip(blocks).zip(payloads).enumerate() + { + let lower_name = case.name.to_lower_camel_case(); + let lower_escaped_name = escape_d_identifier(&lower_name); + + let uppper_name = case.name.to_upper_camel_case(); + let upper_escaped_name = escape_d_identifier(&uppper_name); + + self.push_str(&format!("case {lower_escaped_name}: {{\n")); + if let Some(ty) = case.ty.as_ref() { + let ty_name = self.r#gen.type_name(ty, self.r#gen.fqn); + self.push_str(&format!( + "const ref {ty_name} {payload} = {}.get{upper_escaped_name}();\n", + operands[0], + )); + } + self.src.push_str(&block); + + for (name, result) in variant_results.iter().zip(&block_results) { + self.push_str(&format!("{name} = {result};\n")); + } + self.src.push_str("break;\n}\n"); + } + + self.src.push_str("}\n"); + } + abi::Instruction::VariantLift { variant, ty, .. } => { + let blocks = self + .blocks + .drain(self.blocks.len() - variant.cases.len()..) + .collect::>(); + + let ty = self.r#gen.type_name(&Type::Id(*ty), self.r#gen.fqn); + + let tmp = self.tmp(); + let result = tempname("_variant", tmp); + let tag = tempname("_tag", tmp); + + self.push_str(&format!("{ty} {result} = void;\n")); + self.push_str(&format!("auto {tag} = {};\n", operands[0])); + self.push_str(&format!( + "final switch (cast({ty}.Tag){tag}) with ({ty}.Tag) {{\n" + )); + for (i, (case, (block, block_results))) in + variant.cases.iter().zip(blocks).enumerate() + { + let lower_name = case.name.to_lower_camel_case(); + let escaped_name = escape_d_identifier(&lower_name); + + let payload = tempname("_payload", self.tmp()); + + self.push_str(&format!("case {escaped_name}: {{\n")); + self.src.push_str(&block); + assert!(block_results.len() == (case.ty.is_some() as usize)); + + let val = if let Some(_) = case.ty.as_ref() { + self.push_str(&format!("auto {payload} = {};\n", block_results[0])); + &payload + } else { + "" + }; + self.push_str(&format!("{result} = {ty}.{escaped_name}({val});\n")); + self.src.push_str("break;\n}\n"); + } + self.src.push_str("}\n"); + results.push(result); + } + abi::Instruction::OptionLower { + results: result_types, + .. + } => { + let (mut some, some_results) = self.blocks.pop().unwrap(); + let (mut none, none_results) = self.blocks.pop().unwrap(); + let some_payload = self.payloads.pop().unwrap(); + let _none_payload = self.payloads.pop().unwrap(); + + for (i, ty) in result_types.iter().enumerate() { + let name = tempname("_option", self.tmp()); + results.push(name.clone()); + self.push_str(&format!("{} {name} = void;\n", wasm_type(*ty))); + let some_result = &some_results[i]; + some.push_str(&format!("{name} = {some_result};\n")); + let none_result = &none_results[i]; + none.push_str(&format!("{name} = {none_result};\n")); + } + + let bind_some = format!("ref {some_payload} = {}.unwrap();", operands[0]); + + self.push_str(&format!( + "\ + if ({}.isSome) {{ + {bind_some} + {some}}} else {{ + {none}}} + ", + operands[0] + )); + } + abi::Instruction::OptionLift { ty, .. } => { + let (some, some_results) = self.blocks.pop().unwrap(); + let (_none, none_results) = self.blocks.pop().unwrap(); + assert!(none_results.is_empty()); + assert!(some_results.len() == 1); + + let type_name = self.r#gen.type_name(&Type::Id(*ty), self.r#gen.fqn); + let op0 = &operands[0]; + + let tmp = self.tmp(); + let resultname = tempname("_option", tmp); + let is_some = tempname("_isSome", tmp); + let some_value = &some_results[0]; + self.push_str(&format!( + "{type_name} {resultname} = void; + bool {is_some} = ({op0}) != 0; + if ({is_some}) {{ + {some} + {resultname} = {type_name}.some({some_value}); + }} else {{ + {resultname} = {type_name}.none; + }} + " + )); + results.push(format!("{resultname}")); + } + + abi::Instruction::ResultLower { + results: result_types, + result, + .. + } => { + let (mut err, err_results) = self.blocks.pop().unwrap(); + let (mut ok, ok_results) = self.blocks.pop().unwrap(); + let err_payload = self.payloads.pop().unwrap(); + let ok_payload = self.payloads.pop().unwrap(); + + for (i, ty) in result_types.iter().enumerate() { + let tmp = self.tmp(); + let name = tempname("_resultPart", tmp); + results.push(name.clone()); + self.src.push_str(wasm_type(*ty)); + self.src.push_str(" "); + self.src.push_str(&name); + self.src.push_str(";\n"); + let ok_result = &ok_results[i]; + ok.push_str(&format!("{name} = {ok_result};\n")); + let err_result = &err_results[i]; + err.push_str(&format!("{name} = {err_result};\n")); + } + + let op0 = &operands[0]; + let bind_ok = if let Some(_ok) = result.ok.as_ref() { + format!("ref {ok_payload} = {op0}.unwrap();") + } else { + String::new() + }; + let bind_err = if let Some(_err) = result.err.as_ref() { + format!("ref {err_payload} = {op0}.unwrapErr();") + } else { + String::new() + }; + + self.push_str(&format!( + "\ + if ({op0}.isErr) {{ + {bind_err} + {err}}} else {{ + {bind_ok} + {ok}}} + " + )); + } + + abi::Instruction::ResultLift { result, ty, .. } => { + let (err, err_results) = self.blocks.pop().unwrap(); + assert!(err_results.len() == (result.err.is_some() as usize)); + let (ok, ok_results) = self.blocks.pop().unwrap(); + assert!(ok_results.len() == (result.ok.is_some() as usize)); + + let full_type = self.r#gen.type_name(&Type::Id(*ty), self.r#gen.fqn); + let op0 = &operands[0]; + + let tmp = self.tmp(); + let resultname = tempname("_result", tmp); + let is_err = tempname("_isErr", tmp); + + let ok_value = if result.ok.is_some() { + &ok_results[0] + } else { + "" + }; + + let err_value = if result.err.is_some() { + &err_results[0] + } else { + "" + }; + + self.push_str(&format!( + "{full_type} {resultname} = void; + bool {is_err} = ({op0}) != 0; + if ({is_err}) {{ + {err} + {resultname} = {full_type}.err({err_value}); + }} else {{ + {ok} + {resultname} = {full_type}.ok({ok_value}); + }}\n" + )); + results.push(resultname); + } + + abi::Instruction::EnumLower { .. } => { + results.push(format!("cast(uint)({})", operands[0])) + } + abi::Instruction::EnumLift { ty, .. } => { + let type_name = self.r#gen.type_name(&Type::Id(*ty), self.r#gen.fqn); + results.push(format!("cast({})({})", type_name, operands.pop().unwrap())) + } + + abi::Instruction::FlagsLower { flags, .. } => match flags.repr() { + FlagsRepr::U8 | FlagsRepr::U16 | FlagsRepr::U32(1) => { + results.push(format!("cast(uint)({}.bits)", operands.pop().unwrap())); + } + FlagsRepr::U32(2) => { + let tempname = tempname("_flags", self.tmp()); + + self.push_str(&format!("auto {tempname} = {};", operands[0])); + results.push(format!("cast(uint)({tempname}.bits & 0xffffffff)")); + results.push(format!("cast(uint)(({tempname}.bits >> 32) & 0xffffffff)")); + } + _ => todo!(), + }, + abi::Instruction::FlagsLift { flags, ty, .. } => { + let type_name = self.r#gen.type_name(&Type::Id(*ty), self.r#gen.fqn); + + match flags.repr() { + FlagsRepr::U8 | FlagsRepr::U16 | FlagsRepr::U32(1) => { + results.push(format!("{type_name}({})", operands.pop().unwrap())); + } + FlagsRepr::U32(2) => { + results.push(format!( + "({type_name}({}) | {type_name}({} << 32))", + operands[0], operands[1] + )); + } + _ => todo!(), + } + } + + abi::Instruction::HandleLower { .. } => { + let op = &operands[0]; + results.push(format!("{op}.__handle")) + } + + abi::Instruction::HandleLift { ty, .. } => { + let name = self.r#gen.type_name(&Type::Id(*ty), self.r#gen.fqn); + results.push(format!("{name}({})", operands[0])); + } + + abi::Instruction::CallWasm { name, sig } => { + let split_name = if name.contains('.') { + name.split(".").skip(1).next().unwrap() + } else { + name + }; + + let lower_name = split_name.to_lower_camel_case(); + let escaped_name = escape_d_identifier(&lower_name); + + if !sig.results.is_empty() { + self.src.push_str("auto _ret = "); + results.push("_ret".to_string()); + } + self.push_str(&format!( + "__import_{escaped_name}({});\n", + operands + .iter() + .map(|op| { op.clone() }) + .collect::>() + .join(", ") + )); + } + abi::Instruction::Return { amt, func } => match amt { + 0 => {} + _ => { + assert!(*amt == operands.len()); + + if *amt == 1 { + self.push_str("return "); + self.src.push_str(&operands[0]); + self.push_str(";\n"); + } else { + todo!(); + } + } + }, + abi::Instruction::I32Load { offset } => self.load("uint", *offset, operands, results), + abi::Instruction::I64Load { offset } => self.load("ulong", *offset, operands, results), + abi::Instruction::F32Load { offset } => self.load("float", *offset, operands, results), + abi::Instruction::F64Load { offset } => self.load("double", *offset, operands, results), + abi::Instruction::PointerLoad { offset } => { + self.load("void*", *offset, operands, results) + } + abi::Instruction::LengthLoad { offset } => { + self.load("size_t", *offset, operands, results) + } + abi::Instruction::I32Store { offset } => self.store("uint", *offset, operands), + abi::Instruction::I64Store { offset } => self.store("ulong", *offset, operands), + abi::Instruction::F32Store { offset } => self.store("float", *offset, operands), + abi::Instruction::F64Store { offset } => self.store("double", *offset, operands), + abi::Instruction::I32Store8 { offset } => self.store("ubyte", *offset, operands), + abi::Instruction::I32Store16 { offset } => self.store("ushort", *offset, operands), + abi::Instruction::PointerStore { offset } => self.store("void*", *offset, operands), + abi::Instruction::LengthStore { offset } => self.store("size_t", *offset, operands), + abi::Instruction::I32Load8U { offset } => { + self.load_ext("ubyte", *offset, operands, results) + } + abi::Instruction::I32Load8S { offset } => { + self.load_ext("byte", *offset, operands, results) + } + abi::Instruction::I32Load16U { offset } => { + self.load_ext("ushort", *offset, operands, results) + } + abi::Instruction::I32Load16S { offset } => { + self.load_ext("short", *offset, operands, results) + } + abi::Instruction::I32FromChar + | abi::Instruction::I32FromBool + | abi::Instruction::I32FromU8 + | abi::Instruction::I32FromS8 + | abi::Instruction::I32FromU16 + | abi::Instruction::I32FromS16 + | abi::Instruction::I32FromU32 + | abi::Instruction::I32FromS32 => top_as("uint"), + abi::Instruction::I64FromU64 | abi::Instruction::I64FromS64 => top_as("ulong"), + abi::Instruction::F32FromCoreF32 => top_as("float"), + abi::Instruction::F64FromCoreF64 => top_as("double"), + abi::Instruction::S8FromI32 => top_as("byte"), + abi::Instruction::U8FromI32 => top_as("ubyte"), + abi::Instruction::S16FromI32 => top_as("short"), + abi::Instruction::U16FromI32 => top_as("ushort"), + abi::Instruction::S32FromI32 => top_as("int"), + abi::Instruction::U32FromI32 => top_as("uint"), + abi::Instruction::S64FromI64 => top_as("long"), + abi::Instruction::U64FromI64 => top_as("ulong"), + abi::Instruction::CharFromI32 => top_as("dchar"), + abi::Instruction::CoreF32FromF32 => top_as("float"), + abi::Instruction::CoreF64FromF64 => top_as("double"), + abi::Instruction::BoolFromI32 => results.push(format!("({} != 0)", operands[0])), + + abi::Instruction::Flush { amt } => { + for op in operands.iter().take(*amt) { + let result = tempname("_flush", self.tmp()); + self.push_str(&format!("auto {result} = {};\n", op)); + results.push(result); + } + } + unk => todo!("emit instruction: {unk:?}"), + } + } + + fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> Self::Operand { + // Track maximum return area requirements + self.return_pointer_area_size = self.return_pointer_area_size.max(size); + self.return_pointer_area_align = self.return_pointer_area_align.max(align); + + "_retArea.ptr".into() + } + + fn push_block(&mut self) { + let prev = take(&mut self.src); + self.block_storage.push(prev); + } + + fn finish_block(&mut self, operands: &mut Vec) { + let to_restore = self.block_storage.pop().unwrap(); + let src = replace(&mut self.src, to_restore); + self.blocks.push((src.into(), take(operands))); + } + + fn sizes(&self) -> &SizeAlign { + &self.r#gen.sizes + } + + fn is_list_canonical(&self, _resolve: &Resolve, ty: &Type) -> bool { + self.r#gen.resolve.all_bits_valid(ty) + } +} + +/// This describes the common ABI function referenced or implemented, the C++ side might correspond to a different type +enum SpecialMethod { + None, + ResourceDrop, // ([export]) [resource-drop] + ResourceNew, // [export][resource-new] + ResourceRep, // [export][resource-rep] + Dtor, // [dtor] (guest export only) + Allocate, // internal: allocate new object (called from generated code) +} + +fn is_special_method(func: &Function) -> SpecialMethod { + if matches!(func.kind, FunctionKind::Static(_)) { + if func.name.starts_with("[resource-drop]") { + SpecialMethod::ResourceDrop + } else if func.name.starts_with("[resource-new]") { + SpecialMethod::ResourceNew + } else if func.name.starts_with("[resource-rep]") { + SpecialMethod::ResourceRep + } else if func.name.starts_with("[dtor]") { + SpecialMethod::Dtor + } else if func.name == "$alloc" { + SpecialMethod::Allocate + } else { + SpecialMethod::None + } + } else { + SpecialMethod::None + } +} diff --git a/crates/d/src/wit_common.d b/crates/d/src/wit_common.d index e254125ed..cc8ea75d8 100644 --- a/crates/d/src/wit_common.d +++ b/crates/d/src/wit_common.d @@ -3,6 +3,18 @@ module wit.common; import core.attribute : mustuse; import ldc.attributes : llvmAttr; +package(wit) extern(C) { +void* malloc(size_t size); +void free(void* ptr); +} + +package(wit) auto mallocSlice(T)(size_t count) { + auto ptr = malloc(count*T.sizeof); + if (ptr is null) return null; + + return (cast(T*)ptr)[0..count]; +} + // from std.meta package(wit) alias AliasSeq(T...) = T; @@ -152,10 +164,10 @@ public: bool isNone() const @safe @nogc nothrow => !_present; - ref inout(T) unwrap() inout @safe @nogc nothrow return + ref inout(T) unwrap() inout @trusted @nogc nothrow return in (_present) do { return _value; } - T unwrapOr(T fallback) @safe @nogc nothrow => _present ? _value : fallback; + T unwrapOr(T fallback) @trusted @nogc nothrow => _present ? _value : fallback; T unwrapOrElse(D)(scope D fallback) if (is(D R == return) && is(R : T) && is(D == __parameters)) @@ -215,7 +227,7 @@ public: ref inout(T) unwrap() inout @trusted @nogc nothrow return in (isOk) do { return _storage.value; } - T unwrapOr(T fallback) @safe @nogc nothrow => isOk ? _storage.value : fallback; + T unwrapOr(T fallback) @trusted @nogc nothrow => isOk ? _storage.value : fallback; T unwrapOrElse(D)(scope D fallback) if (is(D R == return) && is(R : T) && is(D == __parameters))