From 833c84fc5aa71fdbef417dbfe0bd0628b737a94f Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Sun, 22 Jun 2025 14:06:42 +0100 Subject: [PATCH 01/12] Use value types for asymmetric API --- crates/cpp/src/lib.rs | 48 +++++++++++++++++++ ...rship-both_list_and_resource-TheResource.h | 45 +++++++++++++++++ tests/runtime/lists/test.cpp | 3 ++ tests/runtime/options/test.cpp | 9 ++++ tests/runtime/strings/runner.cpp | 5 ++ tests/runtime/strings/test.cpp | 4 ++ 6 files changed, 114 insertions(+) create mode 100644 tests/runtime/cpp/param-ownership/cpp/exports-test-ownership-both_list_and_resource-TheResource.h diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 027064f6d..4942a64df 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -216,7 +216,11 @@ pub struct Opts { #[cfg_attr( feature = "clap", arg( +<<<<<<< HEAD long, +======= + long, +>>>>>>> 2661d5e6 (Use value types for asymmetric API) default_value_t = APIStyle::default(), value_name = "STYLE", ), @@ -254,10 +258,14 @@ impl FromStr for APIStyle { match s { "asymmetric" => Ok(APIStyle::Asymmetric), "symmetric" => Ok(APIStyle::Symmetric), +<<<<<<< HEAD _ => bail!( "unrecognized API style: `{}`; expected `asymmetric` or `symmetric`", s ), +======= + _ => bail!("unrecognized API style: `{}`; expected `asymmetric` or `symmetric`", s), +>>>>>>> 2661d5e6 (Use value types for asymmetric API) } } } @@ -720,10 +728,13 @@ impl WorldGenerator for Cpp { .as_slice(), ); +<<<<<<< HEAD if self.dependencies.needs_wit { files.push(&format!("wit.h"), include_bytes!("../helper-types/wit.h")); } +======= +>>>>>>> 2661d5e6 (Use value types for asymmetric API) Ok(()) } } @@ -1223,6 +1234,7 @@ impl CppInterfaceGenerator<'_> { let special = is_special_method(func); if !matches!(special, SpecialMethod::Allocate) { self.gen.c_src.src.push_str("{\n"); +<<<<<<< HEAD let needs_dealloc = if self.gen.opts.api_style == APIStyle::Symmetric && matches!(variant, AbiVariant::GuestExport) { @@ -1235,6 +1247,19 @@ impl CppInterfaceGenerator<'_> { } else { false }; +======= + let needs_dealloc = + if self.gen.opts.api_style == APIStyle::Symmetric && matches!(variant, AbiVariant::GuestExport) { + self.gen + .c_src + .src + .push_str("std::vector _deallocate;\n"); + self.gen.dependencies.needs_vector = true; + true + } else { + false + }; +>>>>>>> 2661d5e6 (Use value types for asymmetric API) let lift_lower = if export { LiftLower::LiftArgsLowerResults } else { @@ -1443,8 +1468,12 @@ impl CppInterfaceGenerator<'_> { "std::string_view".into() } Flavor::Argument(var) +<<<<<<< HEAD if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => +======= + if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => +>>>>>>> 2661d5e6 (Use value types for asymmetric API) { self.gen.dependencies.needs_string_view = true; "std::string_view".into() @@ -1533,8 +1562,12 @@ impl CppInterfaceGenerator<'_> { format!("std::span<{inner} const>") } Flavor::Argument(var) +<<<<<<< HEAD if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => +======= + if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => +>>>>>>> 2661d5e6 (Use value types for asymmetric API) { self.gen.dependencies.needs_span = true; format!("std::span<{inner} const>") @@ -2382,9 +2415,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { r#"auto {result} = wit::vector<{vtype}>::allocate({len}); "#, )); +<<<<<<< HEAD if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) { +======= + if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) { +>>>>>>> 2661d5e6 (Use value types for asymmetric API) assert!(self.needs_dealloc); self.push_str(&format!("if ({len}>0) _deallocate.push_back({base});\n")); } @@ -2404,12 +2441,18 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // inplace construct uwriteln!(self.src, "{result}.initialize(i, std::move(e{tmp}));"); uwriteln!(self.src, "}}"); +<<<<<<< HEAD if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) { results.push(format!("{result}.get_const_view()")); if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) +======= + if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) { + results.push(format!("{result}.get_const_view()")); + if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) +>>>>>>> 2661d5e6 (Use value types for asymmetric API) { self.leak_on_insertion.replace(format!( "if ({len}>0) _deallocate.push_back((void*){result}.leak());\n" @@ -2720,7 +2763,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } let op0 = &operands[0]; +<<<<<<< HEAD let flavor = if matches!(self.variant, AbiVariant::GuestImport) { +======= + let flavor = if matches!(self.variant, AbiVariant::GuestImport) + { +>>>>>>> 2661d5e6 (Use value types for asymmetric API) Flavor::BorrowedArgument } else { Flavor::InStruct diff --git a/tests/runtime/cpp/param-ownership/cpp/exports-test-ownership-both_list_and_resource-TheResource.h b/tests/runtime/cpp/param-ownership/cpp/exports-test-ownership-both_list_and_resource-TheResource.h new file mode 100644 index 000000000..04cb1aff9 --- /dev/null +++ b/tests/runtime/cpp/param-ownership/cpp/exports-test-ownership-both_list_and_resource-TheResource.h @@ -0,0 +1,45 @@ +#pragma once +#include "wit.h" +#include +#include +#include +/* User class definition file, autogenerated once, then user modified + * Updated versions of this file are generated into TheResource.template. + */ +namespace exports { +namespace test { +namespace ownership { +namespace both_list_and_resource { +class TheResource : public wit::ResourceExportBase { + +public: + static void Dtor(both_list_and_resource::TheResource *self) { delete self; } + TheResource(wit::vector the_list) { + the_list_ = std::move(the_list); + } + static Owned New(wit::vector the_list) { + return Owned(new TheResource(std::move(the_list))); + } + wit::vector ToUpper() { + wit::vector result; + result.reserve(the_list_.size()); + for (size_t i = 0; i < the_list_.size(); ++i) { + auto str = the_list_[i]; + for (char &c : str) { + c = std::toupper(c); + } + result.emplace_back(std::move(str)); + } + return result; + } + static int32_t ResourceNew(both_list_and_resource::TheResource *self); + static TheResource *ResourceRep(int32_t id); + static void ResourceDrop(int32_t id); + +private: + wit::vector the_list_; +}; +} // namespace both_list_and_resource +} // namespace ownership +} // namespace test +} // namespace exports diff --git a/tests/runtime/lists/test.cpp b/tests/runtime/lists/test.cpp index 0324b652a..8d1841277 100644 --- a/tests/runtime/lists/test.cpp +++ b/tests/runtime/lists/test.cpp @@ -82,7 +82,10 @@ void exports::test::lists::to_test::ListParam4(wit::vector>>>>>> 2661d5e6 (Use value types for asymmetric API) void exports::test::lists::to_test::ListParam5(wit::vector> a) { } diff --git a/tests/runtime/options/test.cpp b/tests/runtime/options/test.cpp index 1f295f469..05601898a 100644 --- a/tests/runtime/options/test.cpp +++ b/tests/runtime/options/test.cpp @@ -4,7 +4,11 @@ #include static bool equal(std::optional const& a, std::optional const& b) { +<<<<<<< HEAD return a.has_value() == b.has_value() && a->get_view()==b.value(); +======= + return a->get_view()==b.value(); +>>>>>>> 2661d5e6 (Use value types for asymmetric API) } void exports::test::options::to_test::OptionNoneParam(std::optional a) @@ -25,7 +29,12 @@ std::optional exports::test::options::to_test::OptionSomeResult() { } std::optional exports::test::options::to_test::OptionRoundtrip(std::optional a) { +<<<<<<< HEAD return a; +======= + if (!a.has_value()) return std::optional(); + return std::optional(a); +>>>>>>> 2661d5e6 (Use value types for asymmetric API) } std::optional> exports::test::options::to_test::DoubleOptionRoundtrip(std::optional> a) { diff --git a/tests/runtime/strings/runner.cpp b/tests/runtime/strings/runner.cpp index d4fd0e719..ffde352fd 100644 --- a/tests/runtime/strings/runner.cpp +++ b/tests/runtime/strings/runner.cpp @@ -1,3 +1,8 @@ +<<<<<<< HEAD +======= +//@ args = '--api-style symmetric' + +>>>>>>> 2661d5e6 (Use value types for asymmetric API) #include #include #include diff --git a/tests/runtime/strings/test.cpp b/tests/runtime/strings/test.cpp index 19957b292..6e82a3cc8 100644 --- a/tests/runtime/strings/test.cpp +++ b/tests/runtime/strings/test.cpp @@ -25,5 +25,9 @@ wit::string exports::test::strings::to_test::ReturnEmpty() { } wit::string exports::test::strings::to_test::Roundtrip(wit::string str) { +<<<<<<< HEAD +======= + assert(str.size() > 0); +>>>>>>> 2661d5e6 (Use value types for asymmetric API) return str; } From 9bfd0eefe58b740fc4b2c33f17028eaeae6e8886 Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Sun, 22 Jun 2025 14:10:11 +0100 Subject: [PATCH 02/12] Correct test arguments --- tests/runtime/strings/runner.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/runtime/strings/runner.cpp b/tests/runtime/strings/runner.cpp index ffde352fd..6d7f9b814 100644 --- a/tests/runtime/strings/runner.cpp +++ b/tests/runtime/strings/runner.cpp @@ -1,8 +1,11 @@ <<<<<<< HEAD +<<<<<<< HEAD ======= //@ args = '--api-style symmetric' >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= +>>>>>>> a6728b69 (Correct test arguments) #include #include #include From 4d82cafd33209a8dc2b6af24206aeb27b3c218ee Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Sun, 22 Jun 2025 14:13:07 +0100 Subject: [PATCH 03/12] Update docs --- crates/cpp/DESIGN.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index c50dd5ede..2da6ca212 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -19,7 +19,11 @@ | API | | | ABI | | | --- | --- | --- | --- | --- | | 🌓 | asymmetric | | 📘 | canonical | +<<<<<<< HEAD | ⚖️ | symmetric | | 🪞 | symmetric | +======= +| 🪞 | symmetric | | 🪞 | symmetric | +>>>>>>> 95814ef8 (Update docs) | Code | mode | WIT Type | Rust type | C++ Type | Lower | Reason | | --- | --- | --- | --- | --- | --- | --- | @@ -36,19 +40,32 @@ | | | list | Vec | wit::vector | &(a,l) | | | | result | Result | std::expected | &(d,a,l) | | GEA | t | string | String | 🌓 wit::string | addr, len | +<<<<<<< HEAD | | | | | ⚖️ string_view | | | | | result | Result | 🌓 std::expected | d,a,l | | | | | | ⚖️ std::expected | | | GER | p | string | String | wit::string (or std?) | 📘 -> &(a,l) cabi_post_N:P/I#F [^6] | +======= +| | | | | 🪞 string_view | | +| | | result | Result | 🌓 std::expected | d,a,l | +| | | | | 🪞 std::expected | | +| GER | p | string | String | wit::string (or std?) | 📘 -> &(a,l) cabi_post_N:P/I#F [^7] | +>>>>>>> 95814ef8 (Update docs) | | | | | | 🪞 &(a,l) | | | | result | Result | std::expected | 📘 -> &(d,a,l) cabi_post | | --S | ? | string | String | wit::string | addr, len | | HIA | v | string | | string_view | a,l | | HIR | t | string | | wit::string[^3] | &(a,l) | | HEA | t | string | | 🌓 wit::string[^4] | a,l | +<<<<<<< HEAD | | | | | ⚖️ string_view [^5] | | | HER | p | string | | 🌓 wit::guest_owned | 📘 -> &(a,l) | | | | | | ⚖️ wit::string [^5] | 🪞 &(a,l) | +======= +| | | | | 🪞 string_view [^6] | | +| HER | p | string | | 🌓 wit::guest_owned | 📘 -> &(a,l) | +| | | | | 🪞 wit::string [^6] | 🪞 &(a,l) | +>>>>>>> 95814ef8 (Update docs) [^1]: The host never frees memory (is never passed ownership)! From 89878de1841061f4faa0dac7cbc7de5938049579 Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Sun, 22 Jun 2025 14:27:44 +0100 Subject: [PATCH 04/12] Format --- crates/cpp/src/lib.rs | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 4942a64df..4a80343b3 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -216,11 +216,15 @@ pub struct Opts { #[cfg_attr( feature = "clap", arg( +<<<<<<< HEAD <<<<<<< HEAD long, ======= long, >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= + long, +>>>>>>> 78169a8a (Format) default_value_t = APIStyle::default(), value_name = "STYLE", ), @@ -259,13 +263,19 @@ impl FromStr for APIStyle { "asymmetric" => Ok(APIStyle::Asymmetric), "symmetric" => Ok(APIStyle::Symmetric), <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> 78169a8a (Format) _ => bail!( "unrecognized API style: `{}`; expected `asymmetric` or `symmetric`", s ), +<<<<<<< HEAD ======= _ => bail!("unrecognized API style: `{}`; expected `asymmetric` or `symmetric`", s), >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= +>>>>>>> 78169a8a (Format) } } } @@ -1235,6 +1245,9 @@ impl CppInterfaceGenerator<'_> { if !matches!(special, SpecialMethod::Allocate) { self.gen.c_src.src.push_str("{\n"); <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> 78169a8a (Format) let needs_dealloc = if self.gen.opts.api_style == APIStyle::Symmetric && matches!(variant, AbiVariant::GuestExport) { @@ -1247,6 +1260,7 @@ impl CppInterfaceGenerator<'_> { } else { false }; +<<<<<<< HEAD ======= let needs_dealloc = if self.gen.opts.api_style == APIStyle::Symmetric && matches!(variant, AbiVariant::GuestExport) { @@ -1260,6 +1274,8 @@ impl CppInterfaceGenerator<'_> { false }; >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= +>>>>>>> 78169a8a (Format) let lift_lower = if export { LiftLower::LiftArgsLowerResults } else { @@ -1468,12 +1484,17 @@ impl CppInterfaceGenerator<'_> { "std::string_view".into() } Flavor::Argument(var) +<<<<<<< HEAD <<<<<<< HEAD if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => ======= if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= + if matches!(var, AbiVariant::GuestImport) + || self.gen.opts.api_style == APIStyle::Symmetric => +>>>>>>> 78169a8a (Format) { self.gen.dependencies.needs_string_view = true; "std::string_view".into() @@ -1562,12 +1583,17 @@ impl CppInterfaceGenerator<'_> { format!("std::span<{inner} const>") } Flavor::Argument(var) +<<<<<<< HEAD <<<<<<< HEAD if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => ======= if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= + if matches!(var, AbiVariant::GuestImport) + || self.gen.opts.api_style == APIStyle::Symmetric => +>>>>>>> 78169a8a (Format) { self.gen.dependencies.needs_span = true; format!("std::span<{inner} const>") @@ -2415,6 +2441,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { r#"auto {result} = wit::vector<{vtype}>::allocate({len}); "#, )); +<<<<<<< HEAD <<<<<<< HEAD if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) @@ -2422,6 +2449,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ======= if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) { >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= + if self.gen.gen.opts.api_style == APIStyle::Symmetric + && matches!(self.variant, AbiVariant::GuestExport) + { +>>>>>>> 78169a8a (Format) assert!(self.needs_dealloc); self.push_str(&format!("if ({len}>0) _deallocate.push_back({base});\n")); } @@ -2441,6 +2473,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // inplace construct uwriteln!(self.src, "{result}.initialize(i, std::move(e{tmp}));"); uwriteln!(self.src, "}}"); +<<<<<<< HEAD <<<<<<< HEAD if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) @@ -2453,6 +2486,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(format!("{result}.get_const_view()")); if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= + if self.gen.gen.opts.api_style == APIStyle::Symmetric + && matches!(self.variant, AbiVariant::GuestExport) + { + results.push(format!("{result}.get_const_view()")); + if self.gen.gen.opts.api_style == APIStyle::Symmetric + && matches!(self.variant, AbiVariant::GuestExport) +>>>>>>> 78169a8a (Format) { self.leak_on_insertion.replace(format!( "if ({len}>0) _deallocate.push_back((void*){result}.leak());\n" @@ -2763,12 +2804,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } let op0 = &operands[0]; +<<<<<<< HEAD <<<<<<< HEAD let flavor = if matches!(self.variant, AbiVariant::GuestImport) { ======= let flavor = if matches!(self.variant, AbiVariant::GuestImport) { >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= + let flavor = if matches!(self.variant, AbiVariant::GuestImport) { +>>>>>>> 78169a8a (Format) Flavor::BorrowedArgument } else { Flavor::InStruct From b43212778ed9639d40a8dabc176f9097145a043c Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Sun, 22 Jun 2025 16:16:23 +0100 Subject: [PATCH 05/12] Review feedback --- crates/cpp/DESIGN.md | 10 +++++++++- tests/runtime/options/test.cpp | 8 ++++++++ tests/runtime/strings/test.cpp | 3 +++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index 2da6ca212..6c5922f89 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -20,10 +20,14 @@ | --- | --- | --- | --- | --- | | 🌓 | asymmetric | | 📘 | canonical | <<<<<<< HEAD +<<<<<<< HEAD | ⚖️ | symmetric | | 🪞 | symmetric | ======= | 🪞 | symmetric | | 🪞 | symmetric | >>>>>>> 95814ef8 (Update docs) +======= +| ⚖️ | symmetric | | 🪞 | symmetric | +>>>>>>> efa3a695 (Review feedback) | Code | mode | WIT Type | Rust type | C++ Type | Lower | Reason | | --- | --- | --- | --- | --- | --- | --- | @@ -41,14 +45,18 @@ | | | result | Result | std::expected | &(d,a,l) | | GEA | t | string | String | 🌓 wit::string | addr, len | <<<<<<< HEAD +<<<<<<< HEAD | | | | | ⚖️ string_view | | | | | result | Result | 🌓 std::expected | d,a,l | | | | | | ⚖️ std::expected | | | GER | p | string | String | wit::string (or std?) | 📘 -> &(a,l) cabi_post_N:P/I#F [^6] | ======= | | | | | 🪞 string_view | | +======= +| | | | | ⚖️ string_view | | +>>>>>>> efa3a695 (Review feedback) | | | result | Result | 🌓 std::expected | d,a,l | -| | | | | 🪞 std::expected | | +| | | | | ⚖️ std::expected | | | GER | p | string | String | wit::string (or std?) | 📘 -> &(a,l) cabi_post_N:P/I#F [^7] | >>>>>>> 95814ef8 (Update docs) | | | | | | 🪞 &(a,l) | diff --git a/tests/runtime/options/test.cpp b/tests/runtime/options/test.cpp index 05601898a..440e1b47a 100644 --- a/tests/runtime/options/test.cpp +++ b/tests/runtime/options/test.cpp @@ -4,11 +4,15 @@ #include static bool equal(std::optional const& a, std::optional const& b) { +<<<<<<< HEAD <<<<<<< HEAD return a.has_value() == b.has_value() && a->get_view()==b.value(); ======= return a->get_view()==b.value(); >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= + return a.has_value() == b.has_value() && a->get_view()==b.value(); +>>>>>>> efa3a695 (Review feedback) } void exports::test::options::to_test::OptionNoneParam(std::optional a) @@ -29,12 +33,16 @@ std::optional exports::test::options::to_test::OptionSomeResult() { } std::optional exports::test::options::to_test::OptionRoundtrip(std::optional a) { +<<<<<<< HEAD <<<<<<< HEAD return a; ======= if (!a.has_value()) return std::optional(); return std::optional(a); >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= + return a; +>>>>>>> efa3a695 (Review feedback) } std::optional> exports::test::options::to_test::DoubleOptionRoundtrip(std::optional> a) { diff --git a/tests/runtime/strings/test.cpp b/tests/runtime/strings/test.cpp index 6e82a3cc8..9eeb2352a 100644 --- a/tests/runtime/strings/test.cpp +++ b/tests/runtime/strings/test.cpp @@ -26,8 +26,11 @@ wit::string exports::test::strings::to_test::ReturnEmpty() { wit::string exports::test::strings::to_test::Roundtrip(wit::string str) { <<<<<<< HEAD +<<<<<<< HEAD ======= assert(str.size() > 0); >>>>>>> 2661d5e6 (Use value types for asymmetric API) +======= +>>>>>>> efa3a695 (Review feedback) return str; } From f809b1b0a9cd408f8c3656ccb4250755148a0dde Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Mon, 23 Jun 2025 12:37:20 +0100 Subject: [PATCH 06/12] Implement different ownership rules --- crates/cpp/helper-types/wit-common.h | 76 ++++ crates/cpp/helper-types/wit.h | 15 +- crates/cpp/src/lib.rs | 391 +++++++++++------- ...rship-both_list_and_resource-TheResource.h | 45 ++ .../runner-coarse-borrowing.cpp | 15 + .../param-ownership/runner-fine-borrowing.cpp | 15 + .../cpp/param-ownership/runner-owning.cpp | 11 + tests/runtime/cpp/param-ownership/test.cpp | 47 +++ tests/runtime/cpp/param-ownership/test.wit | 66 +++ tests/runtime/records/test.cpp | 18 +- tests/runtime/results/intermediate.cpp | 78 ++-- tests/runtime/results/leaf.cpp | 26 +- tests/runtime/variants/test.cpp | 20 +- 13 files changed, 602 insertions(+), 221 deletions(-) create mode 100644 crates/cpp/helper-types/wit-common.h create mode 100644 tests/runtime/cpp/param-ownership/cpp17/exports-test-ownership-both_list_and_resource-TheResource.h create mode 100644 tests/runtime/cpp/param-ownership/test.cpp create mode 100644 tests/runtime/cpp/param-ownership/test.wit diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h new file mode 100644 index 000000000..6d9d585f3 --- /dev/null +++ b/crates/cpp/helper-types/wit-common.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include +#include +#include // size_t +#include +#if __cplusplus > 202001L +#include +#else +#include +#endif + +namespace wit { +#if __cplusplus > 202001L +using std::span; +#else +/// Minimal span (vector view) implementation for older C++ environments +template class span { + T const *address; + size_t length; + +public: + T const *data() const { return address; } + size_t size() const { return length; } + + typedef T const *const_iterator; + + const_iterator begin() const { return address; } + const_iterator end() const { return address + length; } + bool empty() const { return !length; } + T const &operator[](size_t index) const { return address[index]; } + span(T *a, size_t l) : address(a), length(l) {} + span() : address((T*)alignof(T)), length(0) {} + // create from any compatible vector (borrows data!) + template + span(std::vector const &vec) : address(vec.data()), length(vec.size()) {} +}; +#endif + +/// @brief Helper class to map between IDs and resources +/// @tparam R Type of the Resource +template class ResourceTable { + static std::map resources; + +public: + static R *lookup_resource(int32_t id) { + auto result = resources.find(id); + return result == resources.end() ? nullptr : &result->second; + } + static int32_t store_resource(R &&value) { + auto last = resources.rbegin(); + int32_t id = last == resources.rend() ? 0 : last->first + 1; + resources.insert(std::pair(id, std::move(value))); + return id; + } + static std::optional remove_resource(int32_t id) { + auto iter = resources.find(id); + std::optional result; + if (iter != resources.end()) { + result = std::move(iter->second); + resources.erase(iter); + } + return std::move(result); + } +}; + +/// @brief Replaces void in the error position of a result +struct Void {}; + +template +constexpr To bit_cast(const From& from) noexcept { + union Bits { From from; To to; }; + Bits b; b.from = from; return b.to; +} +} // namespace wit diff --git a/crates/cpp/helper-types/wit.h b/crates/cpp/helper-types/wit.h index c84f181c9..80f2e124c 100644 --- a/crates/cpp/helper-types/wit.h +++ b/crates/cpp/helper-types/wit.h @@ -57,6 +57,7 @@ class string { static uint8_t const* empty_ptr() { return (uint8_t const *)1; } public: + string() : data_(empty_ptr()), length(0) {} // this constructor is helpful for creating vector string(string const &b) : string(string::from_view(b.get_view())) {} string(string &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; } @@ -95,6 +96,18 @@ class string { memcpy(addr, v.data(), v.size()); return string(addr, v.size()); } + char* begin() { + return (char*)data_; + } + char* end() { + return (char*)data_ + length; + } + char const* begin() const { + return (char const*)data_; + } + char const* end() const { + return (char const*)data_ + length; + } }; /// A vector in linear memory, freed unconditionally using free @@ -113,7 +126,7 @@ template class vector { vector &operator=(vector const &) = delete; vector &operator=(vector &&b) { if (data_ && length>0) { - free(const_cast(data_)); + free(data_); } data_ = b.data_; length = b.length; diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 4a80343b3..d431f0f8d 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1,8 +1,9 @@ use anyhow::bail; use heck::{ToPascalCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; use std::{ + any::Any, collections::{HashMap, HashSet}, - fmt::{Display, Write as FmtWrite}, + fmt::{self, Display, Write as FmtWrite}, io::{Read, Write}, path::PathBuf, process::{Command, Stdio}, @@ -15,7 +16,8 @@ use wit_bindgen_core::{ uwrite, uwriteln, wit_parser::{ Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, - Resolve, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, + Resolve, SizeAlign, Stability, Type, TypeDef, TypeDefKind, TypeId, TypeOwner, WorldId, + WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, }; @@ -119,59 +121,6 @@ struct Cpp { import_prefix: Option, } -#[derive(Default, Debug, Clone, Copy)] -pub enum Ownership { - /// Generated types will be composed entirely of owning fields, regardless - /// of whether they are used as parameters to imports or not. - #[default] - Owning, - - /// Generated types used as parameters to imports will be "deeply - /// borrowing", i.e. contain references rather than owned values when - /// applicable. - Borrowing { - /// Whether or not to generate "duplicate" type definitions for a single - /// WIT type if necessary, for example if it's used as both an import - /// and an export, or if it's used both as a parameter to an import and - /// a return value from an import. - duplicate_if_necessary: bool, - }, -} - -impl FromStr for Ownership { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "owning" => Ok(Self::Owning), - "borrowing" => Ok(Self::Borrowing { - duplicate_if_necessary: false, - }), - "borrowing-duplicate-if-necessary" => Ok(Self::Borrowing { - duplicate_if_necessary: true, - }), - _ => Err(format!( - "unrecognized ownership: `{s}`; \ - expected `owning`, `borrowing`, or `borrowing-duplicate-if-necessary`" - )), - } - } -} - -impl core::fmt::Display for Ownership { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.write_str(match self { - Ownership::Owning => "owning", - Ownership::Borrowing { - duplicate_if_necessary: false, - } => "borrowing", - Ownership::Borrowing { - duplicate_if_necessary: true, - } => "borrowing-duplicate-if-necessary", - }) - } -} - #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Args))] pub struct Opts { @@ -196,22 +145,6 @@ pub struct Opts { #[cfg_attr(feature = "clap", arg(long))] pub internal_prefix: Option, - /// Whether to generate owning or borrowing type definitions. - /// - /// Valid values include: - /// - /// - `owning`: Generated types will be composed entirely of owning fields, - /// regardless of whether they are used as parameters to imports or not. - /// - /// - `borrowing`: Generated types used as parameters to imports will be - /// "deeply borrowing", i.e. contain references rather than owned values - /// when applicable. - /// - /// - `borrowing-duplicate-if-necessary`: As above, but generating distinct - /// types for borrowing and owning, if necessary. - #[cfg_attr(feature = "clap", arg(long, default_value_t = Ownership::Owning))] - pub ownership: Ownership, - /// Set API style to symmetric or asymmetric #[cfg_attr( feature = "clap", @@ -231,6 +164,24 @@ pub struct Opts { )] pub api_style: APIStyle, + /// Whether to generate owning or borrowing type definitions for `record` arguments to imported functions. + /// + /// Valid values include: + /// + /// - `owning`: Generated types will be composed entirely of owning fields, + /// regardless of whether they are used as parameters to imports or not. + /// + /// - `coarse-borrowing`: Generated types used as parameters to imports will be + /// "deeply borrowing", i.e. contain references rather than owned values, + /// so long as they don't contain resources, in which case they will be + /// owning. + /// + /// - `fine-borrowing": Generated types used as parameters to imports will be + /// "deeply borrowing", i.e. contain references rather than owned values + /// for all fields that are not resources, which will be owning. + #[cfg_attr(feature = "clap", arg(long, default_value_t = Ownership::Owning))] + pub ownership: Ownership, + /// Where to place output files #[cfg_attr(feature = "clap", arg(skip))] out_dir: Option, @@ -280,6 +231,50 @@ impl FromStr for APIStyle { } } +#[derive(Default, Debug, Clone, Copy)] +pub enum Ownership { + /// Generated types will be composed entirely of owning fields, regardless + /// of whether they are used as parameters to imports or not. + #[default] + Owning, + + /// Generated types used as parameters to imports will be "deeply + /// borrowing", i.e. contain references rather than owned values when + /// applicable. + CoarseBorrowing, + + /// Generated types used as parameters to imports will be "deeply + /// borrowing", i.e. contain references rather than owned values + /// for all fields that are not resources, which will be owning. + FineBorrowing, +} + +impl FromStr for Ownership { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "owning" => Ok(Self::Owning), + "coarse-borrowing" => Ok(Self::CoarseBorrowing), + "fine-borrowing" => Ok(Self::FineBorrowing), + _ => Err(format!( + "unrecognized ownership: `{s}`; \ + expected `owning`, `coarse-borrowing`, or `fine-borrowing`" + )), + } + } +} + +impl fmt::Display for Ownership { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + Ownership::Owning => "owning", + Ownership::CoarseBorrowing => "coarse-borrowing", + Ownership::FineBorrowing => "fine-borrowing", + }) + } +} + impl Opts { pub fn build(mut self, out_dir: Option<&PathBuf>) -> Box { let mut r = Cpp::new(); @@ -786,15 +781,11 @@ impl SourceWithState { } } for _i in same..self.namespace.len() { - uwrite!(self.src, "}}"); - } - if same != self.namespace.len() { - // finish closing brackets by a newline - uwriteln!(self.src, ""); + uwrite!(self.src, "}}\n"); } self.namespace.truncate(same); for i in target.iter().skip(same) { - uwrite!(self.src, "namespace {} {{", i); + uwrite!(self.src, "namespace {} {{\n", i); self.namespace.push(i.clone()); } } @@ -1150,7 +1141,7 @@ impl CppInterfaceGenerator<'_> { cpp_sig .arguments .iter() - .map(|(arg, _)| arg.clone()) + .map(|(arg, _)| format!("std::move({})", arg)) .collect::>() .join(", ") ); @@ -1446,6 +1437,44 @@ impl CppInterfaceGenerator<'_> { } } + fn scoped_record_name( + &self, + id: TypeId, + from_namespace: &[String], + guest_export: bool, + flavor: Flavor, + ) -> String { + let name = self.scoped_type_name(id, from_namespace, guest_export); + + if let Flavor::Argument(AbiVariant::GuestImport) = flavor { + match self.gen.opts.ownership { + Ownership::Owning => { + if self.has_resources(&id) { + format!("{}&&", name) + } else { + format!("{} const&", name) + } + } + Ownership::CoarseBorrowing => { + if self.has_resources(&id) { + format!("{}&&", name) + } else { + format!("{}Param const&", name) + } + } + Ownership::FineBorrowing => { + if self.has_resources(&id) { + format!("{}Param&&", name) + } else { + format!("{}Param const&", name) + } + } + } + } else { + name + } + } + fn scoped_type_name( &self, id: TypeId, @@ -1509,11 +1538,13 @@ impl CppInterfaceGenerator<'_> { } }, Type::Id(id) => match &self.resolve.types[*id].kind { - TypeDefKind::Record(_r) => { - self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + TypeDefKind::Record(_) => { + let guest_export = self.is_exported_type(&self.resolve.types[*id]); + self.scoped_record_name(*id, from_namespace, guest_export, flavor) } TypeDefKind::Resource => { - self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + let guest_export = self.is_exported_type(&self.resolve.types[*id]); + self.scoped_type_name(*id, from_namespace, guest_export) } TypeDefKind::Handle(Handle::Own(id)) => { let mut typename = self.type_name(&Type::Id(*id), from_namespace, flavor); @@ -1534,6 +1565,10 @@ impl CppInterfaceGenerator<'_> { (false, Flavor::BorrowedArgument) => (), (_, _) => todo!(), } + let ty = &self.resolve.types[*id]; + if matches!(flavor, Flavor::InStruct) && self.is_exported_type(ty) { + typename.push_str(&format!("::{OWNED_CLASS_NAME}")) + } typename } TypeDefKind::Handle(Handle::Borrow(id)) => { @@ -1542,7 +1577,9 @@ impl CppInterfaceGenerator<'_> { + ">" } TypeDefKind::Flags(_f) => { - self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + let ty = &self.resolve.types[*id]; + let guest_export = self.is_exported_type(ty); + self.scoped_type_name(*id, from_namespace, guest_export) } TypeDefKind::Tuple(t) => { let types = t.types.iter().fold(String::new(), |mut a, b| { @@ -1555,10 +1592,14 @@ impl CppInterfaceGenerator<'_> { String::from("std::tuple<") + &types + ">" } TypeDefKind::Variant(_v) => { - self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + let ty = &self.resolve.types[*id]; + let guest_export = self.is_exported_type(ty); + self.scoped_type_name(*id, from_namespace, guest_export) } TypeDefKind::Enum(_e) => { - self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) + let ty = &self.resolve.types[*id]; + let guest_export = self.is_exported_type(ty); + self.scoped_type_name(*id, from_namespace, guest_export) } TypeDefKind::Option(o) => { self.gen.dependencies.needs_optional = true; @@ -1666,6 +1707,104 @@ impl CppInterfaceGenerator<'_> { } } } + + fn variant_for_type(&self, ty: &TypeDef) -> AbiVariant { + if self.is_exported_type(ty) { + AbiVariant::GuestExport + } else { + AbiVariant::GuestImport + } + } + + fn has_resources2(&self, ty: &Type) -> bool { + match ty { + Type::Bool + | Type::U8 + | Type::U16 + | Type::U32 + | Type::U64 + | Type::S8 + | Type::S16 + | Type::S32 + | Type::S64 + | Type::F32 + | Type::F64 + | Type::Char => false, + Type::String => false, + Type::Id(id) => self.has_resources(id), + Type::ErrorContext => todo!(), + } + } + fn has_resources(&self, id: &TypeId) -> bool { + match &self.resolve.types[*id].kind { + TypeDefKind::Record(r) => r.fields.iter().any(|f| self.has_resources2(&f.ty)), + TypeDefKind::Resource => true, + TypeDefKind::Handle(_) => true, + TypeDefKind::Flags(_) => false, + TypeDefKind::Tuple(t) => t.types.iter().any(|ty| self.has_resources2(ty)), + TypeDefKind::Variant(v) => v + .cases + .iter() + .any(|c| c.ty.map_or(false, |ty| self.has_resources2(&ty))), + TypeDefKind::Enum(_) => false, + TypeDefKind::Option(ty) => self.has_resources2(ty), + TypeDefKind::Result(r) => { + r.ok.as_ref().map_or(false, |ok| self.has_resources2(ok)) + || r.err.as_ref().map_or(false, |err| self.has_resources2(err)) + } + TypeDefKind::List(ty) => self.has_resources2(ty), + TypeDefKind::Future(_) => todo!(), + TypeDefKind::Stream(_) => todo!(), + TypeDefKind::Type(ty) => match ty { + Type::Id(id) => self.has_resources(id), + _ => false, + }, + TypeDefKind::FixedSizeList(_, _) => todo!(), + TypeDefKind::Unknown => todo!(), + } + } + + fn type_record_param( + &mut self, + id: TypeId, + name: &str, + record: &wit_bindgen_core::wit_parser::Record, + namespc: &[String], + ) { + let (flavor, needs_param_type) = { + match self.gen.opts.ownership { + Ownership::Owning => (Flavor::InStruct, false), + Ownership::CoarseBorrowing => { + if self.has_resources(&id) { + (Flavor::InStruct, false) + } else { + (Flavor::BorrowedArgument, true) + } + } + Ownership::FineBorrowing => (Flavor::BorrowedArgument, true), + } + }; + + if needs_param_type { + let pascal = format!("{name}-param").to_pascal_case(); + + uwriteln!(self.gen.h_src.src, "struct {pascal} {{"); + for field in record.fields.iter() { + let typename = self.type_name(&field.ty, namespc, flavor); + let fname = field.name.to_snake_case(); + uwriteln!(self.gen.h_src.src, "{typename} {fname};"); + } + uwriteln!(self.gen.h_src.src, "}};"); + } + } + + fn is_exported_type(&self, ty: &TypeDef) -> bool { + if let TypeOwner::Interface(intf) = ty.owner { + !self.gen.imported_interfaces.contains(&intf) + } else { + true + } + } } impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> { @@ -1681,16 +1820,14 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace( - self.resolve, - &ty.owner, - NOT_IN_EXPORTED_NAMESPACE, - &self.gen.opts, - ); - if self.gen.is_first_definition(&namespc, name) { + let guest_export = self.is_exported_type(ty); + let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts); + + if self.gen.is_first_definition(&namespc, &name) { self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); let pascal = name.to_pascal_case(); + uwriteln!(self.gen.h_src.src, "struct {pascal} {{"); for field in record.fields.iter() { Self::docs(&mut self.gen.h_src.src, &field.docs); @@ -1699,6 +1836,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> uwriteln!(self.gen.h_src.src, "{typename} {fname};"); } uwriteln!(self.gen.h_src.src, "}};"); + self.type_record_param(id, name, record, namespc.as_slice()); } } @@ -1864,10 +2002,11 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; + let guest_export = self.is_exported_type(ty); let namespc = namespace( self.resolve, &ty.owner, - NOT_IN_EXPORTED_NAMESPACE, + guest_export, &self.gen.opts, ); if self.gen.is_first_definition(&namespc, name) { @@ -1909,12 +2048,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace( - self.resolve, - &ty.owner, - NOT_IN_EXPORTED_NAMESPACE, - &self.gen.opts, - ); + let guest_export = self.is_exported_type(ty); + let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts); if self.gen.is_first_definition(&namespc, name) { self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); @@ -1971,12 +2106,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace( - self.resolve, - &ty.owner, - NOT_IN_EXPORTED_NAMESPACE, - &self.gen.opts, - ); + let guest_export = self.is_exported_type(ty); + let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts); if self.gen.is_first_definition(&namespc, name) { self.gen.h_src.change_namespace(&namespc); let pascal = name.to_pascal_case(); @@ -2003,12 +2134,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace( - self.resolve, - &ty.owner, - NOT_IN_EXPORTED_NAMESPACE, - &self.gen.opts, - ); + let guest_export = self.is_exported_type(ty); + let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts); self.gen.h_src.change_namespace(&namespc); let pascal = name.to_pascal_case(); Self::docs(&mut self.gen.h_src.src, docs); @@ -2154,48 +2281,6 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { operands[0] ); } - - fn has_resources2(&self, ty: &Type) -> bool { - match ty { - Type::Bool - | Type::U8 - | Type::U16 - | Type::U32 - | Type::U64 - | Type::S8 - | Type::S16 - | Type::S32 - | Type::S64 - | Type::F32 - | Type::F64 - | Type::Char => false, - Type::String => false, - Type::Id(id) => self.has_resources(id), - Type::ErrorContext => todo!(), - } - } - fn has_resources(&self, id: &TypeId) -> bool { - match &self.gen.resolve.types[*id].kind { - TypeDefKind::Record(_) => todo!(), - TypeDefKind::Resource => true, - TypeDefKind::Handle(_) => true, - TypeDefKind::Flags(_) => false, - TypeDefKind::Tuple(t) => t.types.iter().any(|ty| self.has_resources2(ty)), - TypeDefKind::Variant(_) => todo!(), - TypeDefKind::Enum(_) => false, - TypeDefKind::Option(_) => todo!(), - TypeDefKind::Result(_) => todo!(), - TypeDefKind::List(_) => todo!(), - TypeDefKind::Future(_) => todo!(), - TypeDefKind::Stream(_) => todo!(), - TypeDefKind::Type(ty) => match ty { - Type::Id(id) => self.has_resources(id), - _ => false, - }, - TypeDefKind::FixedSizeList(_, _) => todo!(), - TypeDefKind::Unknown => todo!(), - } - } } fn move_if_necessary(arg: &str) -> String { @@ -3220,7 +3305,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { return false; } match ty { - Type::Id(id) => !self.has_resources(id), + Type::Id(id) => !self.gen.has_resources(id), _ => true, } } diff --git a/tests/runtime/cpp/param-ownership/cpp17/exports-test-ownership-both_list_and_resource-TheResource.h b/tests/runtime/cpp/param-ownership/cpp17/exports-test-ownership-both_list_and_resource-TheResource.h new file mode 100644 index 000000000..86e921e9c --- /dev/null +++ b/tests/runtime/cpp/param-ownership/cpp17/exports-test-ownership-both_list_and_resource-TheResource.h @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include +#include +/* User class definition file, autogenerated once, then user modified + * Updated versions of this file are generated into TheResource.template. + */ +namespace exports { +namespace test { +namespace ownership { +namespace both_list_and_resource { +class TheResource : public wit::ResourceExportBase { + +public: + static void Dtor(both_list_and_resource::TheResource *self) { delete self; } + TheResource(wit::vector the_list) { + the_list_ = std::move(the_list); + } + static Owned New(wit::vector the_list) { + return Owned(new TheResource(std::move(the_list))); + } + wit::vector ToUpper() { + wit::vector result = + wit::vector::allocate(the_list_.size()); + for (size_t i = 0; i < the_list_.size(); ++i) { + auto str = the_list_[i]; + for (char &c : str) { + c = std::toupper(c); + } + result.initialize(i, std::move(str)); + } + return result; + } + static int32_t ResourceNew(both_list_and_resource::TheResource *self); + static TheResource *ResourceRep(int32_t id); + static void ResourceDrop(int32_t id); + +private: + wit::vector the_list_; +}; +} // namespace both_list_and_resource +} // namespace ownership +} // namespace test +} // namespace exports diff --git a/tests/runtime/cpp/param-ownership/runner-coarse-borrowing.cpp b/tests/runtime/cpp/param-ownership/runner-coarse-borrowing.cpp index d18c37263..e403ef98c 100644 --- a/tests/runtime/cpp/param-ownership/runner-coarse-borrowing.cpp +++ b/tests/runtime/cpp/param-ownership/runner-coarse-borrowing.cpp @@ -5,10 +5,17 @@ int main() { std::array a1 = {"value1", "value2"}; std::array a2 = {"value3", "value4"}; +<<<<<<< HEAD std::array, 2> as = { std::span(a1.data(), a1.size()), std::span(a2.data(), a2.size())}; std::span const> input(as.data(), +======= + std::array, 2> as = { + wit::span(a1.data(), a1.size()), + wit::span(a2.data(), a2.size())}; + wit::span const> input(as.data(), +>>>>>>> 25d17a0c (Implement different ownership rules) as.size()); auto res = test::ownership::Foo(input); assert(res.size() == 2); @@ -22,7 +29,11 @@ int main() { test::ownership::ThingParam thing; thing.name = std::string_view("thing"); std::array values = {"value1", "value2"}; +<<<<<<< HEAD thing.value = std::span(values.data(), values.size()); +======= + thing.value = wit::span(values.data(), values.size()); +>>>>>>> 25d17a0c (Implement different ownership rules) test::ownership::Bar(thing); auto result = test::ownership::Baz(thing); assert(result.name.get_view() == "THING"); @@ -37,7 +48,11 @@ int main() { test::ownership::both_list_and_resource::Thing resource_thing{ std::move(v1), test::ownership::both_list_and_resource::TheResource( +<<<<<<< HEAD std::span(v2.data(), v2.size()))}; +======= + wit::span(v2.data(), v2.size()))}; +>>>>>>> 25d17a0c (Implement different ownership rules) test::ownership::both_list_and_resource::ListAndResource( std::move(resource_thing)); } \ No newline at end of file diff --git a/tests/runtime/cpp/param-ownership/runner-fine-borrowing.cpp b/tests/runtime/cpp/param-ownership/runner-fine-borrowing.cpp index 6266eca8e..598a76a70 100644 --- a/tests/runtime/cpp/param-ownership/runner-fine-borrowing.cpp +++ b/tests/runtime/cpp/param-ownership/runner-fine-borrowing.cpp @@ -5,10 +5,17 @@ int main() { std::array a1 = {"value1", "value2"}; std::array a2 = {"value3", "value4"}; +<<<<<<< HEAD std::array, 2> as = { std::span(a1.data(), a1.size()), std::span(a2.data(), a2.size())}; std::span const> input(as.data(), +======= + std::array, 2> as = { + wit::span(a1.data(), a1.size()), + wit::span(a2.data(), a2.size())}; + wit::span const> input(as.data(), +>>>>>>> 25d17a0c (Implement different ownership rules) as.size()); auto res = test::ownership::Foo(input); assert(res.size() == 2); @@ -22,7 +29,11 @@ int main() { test::ownership::ThingParam thing; thing.name = std::string_view("thing"); std::array values = {"value1", "value2"}; +<<<<<<< HEAD thing.value = std::span(values.data(), values.size()); +======= + thing.value = wit::span(values.data(), values.size()); +>>>>>>> 25d17a0c (Implement different ownership rules) test::ownership::Bar(thing); auto result = test::ownership::Baz(thing); assert(result.name.get_view() == "THING"); @@ -31,7 +42,11 @@ int main() { assert(result.value[1].get_view() == "VALUE2"); std::array v1 = {"value1", "value2"}; +<<<<<<< HEAD std::span v2(v1.data(), v1.size()); +======= + wit::span v2(v1.data(), v1.size()); +>>>>>>> 25d17a0c (Implement different ownership rules) test::ownership::both_list_and_resource::ThingParam resource_thing{ v2, test::ownership::both_list_and_resource::TheResource(v2)}; diff --git a/tests/runtime/cpp/param-ownership/runner-owning.cpp b/tests/runtime/cpp/param-ownership/runner-owning.cpp index 82444bf1b..fd3e20098 100644 --- a/tests/runtime/cpp/param-ownership/runner-owning.cpp +++ b/tests/runtime/cpp/param-ownership/runner-owning.cpp @@ -5,10 +5,17 @@ int main() { std::array a1 = {"value1", "value2"}; std::array a2 = {"value3", "value4"}; +<<<<<<< HEAD std::array, 2> as = { std::span(a1.data(), a1.size()), std::span(a2.data(), a2.size())}; std::span const> input(as.data(), +======= + std::array, 2> as = { + wit::span(a1.data(), a1.size()), + wit::span(a2.data(), a2.size())}; + wit::span const> input(as.data(), +>>>>>>> 25d17a0c (Implement different ownership rules) as.size()); auto res = test::ownership::Foo(input); assert(res.size() == 2); @@ -37,7 +44,11 @@ int main() { std::array v2 = {"value1", "value2"}; test::ownership::both_list_and_resource::Thing resource_thing { std::move(v1), +<<<<<<< HEAD test::ownership::both_list_and_resource::TheResource(std::span(v2.data(), v2.size())) +======= + test::ownership::both_list_and_resource::TheResource(wit::span(v2.data(), v2.size())) +>>>>>>> 25d17a0c (Implement different ownership rules) }; test::ownership::both_list_and_resource::ListAndResource( std::move(resource_thing)); diff --git a/tests/runtime/cpp/param-ownership/test.cpp b/tests/runtime/cpp/param-ownership/test.cpp new file mode 100644 index 000000000..250be578c --- /dev/null +++ b/tests/runtime/cpp/param-ownership/test.cpp @@ -0,0 +1,47 @@ +#include "test_cpp.h" +#include +#include +#include +namespace exports::test::ownership { +wit::vector> +Foo(wit::vector> a) { + for (size_t i = 0; i < a.size(); ++i) { + for (size_t j = 0; j < a[i].size(); ++j) { + for (char &c : a[i][j]) { + c = std::toupper(c); + } + } + } + return a; +} + +void Bar(Thing a) { + assert(a.name.get_view() == "thing"); + assert(a.value.size() == 2); + assert(a.value[0].get_view() == "value1"); + assert(a.value[1].get_view() == "value2"); +} +Thing Baz(Thing a) { + for (char &c : a.name) { + c = std::toupper(c); + } + for (size_t i = 0; i < a.value.size(); ++i) { + for (char &c : a.value[i]) { + c = std::toupper(c); + } + } + return a; +} +namespace both_list_and_resource { +void ListAndResource(Thing a) { + auto upper = a.b->ToUpper(); + assert(upper.size() == a.a.size()); + for (size_t i = 0; i < a.a.size(); ++i) { + auto v1 = a.a[i].get_view(); + auto v2 = upper[i].get_view(); + assert(std::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), + [](char c1, char c2) { return std::toupper(c1) == c2; })); + } +} +} // namespace both_list_and_resource +} // namespace exports::test::ownership \ No newline at end of file diff --git a/tests/runtime/cpp/param-ownership/test.wit b/tests/runtime/cpp/param-ownership/test.wit new file mode 100644 index 000000000..0626c768b --- /dev/null +++ b/tests/runtime/cpp/param-ownership/test.wit @@ -0,0 +1,66 @@ +package test:ownership; + +interface both-list-and-resource { + resource the-resource { + constructor(the-list: list); + to-upper: func() -> list; + } + record thing { + a: list, + b: the-resource, + } + + list-and-resource: func(a: thing); +} + +world runner { + import lists: interface { + foo: func(a: list>) -> list>; + } + + import thing-in: interface { + record thing { + name: string, + value: list + } + + bar: func(a: thing); + } + + import thing-in-and-out: interface { + record thing { + name: string, + value: list + } + + baz: func(a: thing) -> thing; + } + + import both-list-and-resource; +} + +world test { + export lists: interface { + foo: func(a: list>) -> list>; + } + + export thing-in: interface { + record thing { + name: string, + value: list + } + + bar: func(a: thing); + } + + export thing-in-and-out: interface { + record thing { + name: string, + value: list + } + + baz: func(a: thing) -> thing; + } + + export both-list-and-resource; +} diff --git a/tests/runtime/records/test.cpp b/tests/runtime/records/test.cpp index 6d69cc193..1f134ee2d 100644 --- a/tests/runtime/records/test.cpp +++ b/tests/runtime/records/test.cpp @@ -1,30 +1,32 @@ #include #include -std::tuple exports::test::records::to_test::MultipleResults() { +namespace test_exports = ::exports::test::records::to_test; + +std::tuple test_exports::MultipleResults() { return std::tuple(4, 5); } -std::tuple exports::test::records::to_test::SwapTuple(std::tuple a) { +std::tuple test_exports::SwapTuple(std::tuple a) { return std::tuple(std::get<1>(a), std::get<0>(a)); } -test::records::to_test::F1 exports::test::records::to_test::RoundtripFlags1(::test::records::to_test::F1 a) { +test_exports::F1 test_exports::RoundtripFlags1(test_exports::F1 a) { return a; } -test::records::to_test::F2 exports::test::records::to_test::RoundtripFlags2(::test::records::to_test::F2 a) { +test_exports::F2 test_exports::RoundtripFlags2(test_exports::F2 a) { return a; } -std::tuple exports::test::records::to_test::RoundtripFlags3(::test::records::to_test::Flag8 a, ::test::records::to_test::Flag16 b, ::test::records::to_test::Flag32 c) { - return std::tuple<::test::records::to_test::Flag8, ::test::records::to_test::Flag16, ::test::records::to_test::Flag32>(a, b, c); +std::tuple test_exports::RoundtripFlags3(test_exports::Flag8 a, test_exports::Flag16 b, test_exports::Flag32 c) { + return std::tuple(a, b, c); } -test::records::to_test::R1 exports::test::records::to_test::RoundtripRecord1(::test::records::to_test::R1 a) { +test_exports::R1 test_exports::RoundtripRecord1(R1 a) { return a; } -std::tuple exports::test::records::to_test::Tuple1(std::tuple a) { +std::tuple test_exports::Tuple1(std::tuple a) { return std::tuple(std::get<0>(a)); } diff --git a/tests/runtime/results/intermediate.cpp b/tests/runtime/results/intermediate.cpp index 5aca8eaf6..89b46fc36 100644 --- a/tests/runtime/results/intermediate.cpp +++ b/tests/runtime/results/intermediate.cpp @@ -6,51 +6,53 @@ bool equal(T const&a, T const&b) { return a==b; } -std::expected exports::test::results::test::StringError(float a) { - return ::test::results::test::StringError(a); +namespace test_imports = ::test::results::test; +namespace test_exports = ::exports::test::results::test; + +std::expected test_exports::StringError(float a) { + return test_imports::StringError(a); +} + +test_exports::E to_exports_e(test_imports::E e) { + switch (e) { + case test_imports::E::kA: return test_exports::E::kA; + case test_imports::E::kB: return test_exports::E::kB; + case test_imports::E::kC: return test_exports::E::kC; + } } -std::expected exports::test::results::test::EnumError(float a) { - auto result = ::test::results::test::EnumError(a); +std::expected test_exports::EnumError(float a) { + auto result = test_imports::EnumError(a); if (result.has_value()) { return result.value(); } - return std::unexpected(result.error()); - // if (result.error()==::test::results::test::E::kA) { return std::unexpected(::test::results::test::E::kA); } - // if (result.error()==::test::results::test::E::kB) { return std::unexpected(::test::results::test::E::kB); } - // if (result.error()==::test::results::test::E::kC) { return std::unexpected(::test::results::test::E::kC); } + return std::unexpected(to_exports_e(result.error())); } -std::expected exports::test::results::test::RecordError(float a) { - auto result = ::test::results::test::RecordError(a); +std::expected test_exports::RecordError(float a) { + auto result = test_imports::RecordError(a); if (result.has_value()) { return result.value(); } - return std::unexpected(::test::results::test::E2{ result.error().line, result.error().column }); + return std::unexpected(test_exports::E2{ result.error().line, result.error().column }); } -std::expected exports::test::results::test::VariantError(float a) { - auto result = ::test::results::test::VariantError(a); +template +struct overloaded : Fs... { + using Fs::operator()...; +}; +template +overloaded(Fs...) -> overloaded; + +std::expected test_exports::VariantError(float a) { + auto result = test_imports::VariantError(a); if (result.has_value()) { return result.value(); } - return std::unexpected(result.error()); - - // match test_imports::variant_error(a) { - // Ok(b) => Ok(b), - // Err(test_imports::E3::E1(test_imports::E::A)) => { - // Err(test_exports::E3::E1(test_exports::E::A)) - // } - // Err(test_imports::E3::E1(test_imports::E::B)) => { - // Err(test_exports::E3::E1(test_exports::E::B)) - // } - // Err(test_imports::E3::E1(test_imports::E::C)) => { - // Err(test_exports::E3::E1(test_exports::E::C)) - // } - // Err(test_imports::E3::E2(test_imports::E2 { line, column })) => { - // Err(test_exports::E3::E2(test_exports::E2 { line, column })) - // } - // } -} - -std::expected exports::test::results::test::EmptyError(uint32_t a) { - return ::test::results::test::EmptyError(a); -} - -std::expected, wit::string> exports::test::results::test::DoubleError(uint32_t a) { - return ::test::results::test::DoubleError(a); + return std::visit(overloaded{ + [](test_imports::E3::E1 const& e1) { return std::unexpected(test_exports::E3{test_exports::E3::E1{to_exports_e(e1.value)}}); }, + [](test_imports::E3::E2 const& e2) { return std::unexpected(test_exports::E3{test_exports::E3::E2{e2.value.line, e2.value.column}}); } + }, result.error().variants); +} + +std::expected test_exports::EmptyError(uint32_t a) { + return test_imports::EmptyError(a); +} + +std::expected, wit::string> test_exports::DoubleError(uint32_t a) { + return test_imports::DoubleError(a); } diff --git a/tests/runtime/results/leaf.cpp b/tests/runtime/results/leaf.cpp index edc91da94..d37b8fe70 100644 --- a/tests/runtime/results/leaf.cpp +++ b/tests/runtime/results/leaf.cpp @@ -6,7 +6,9 @@ bool equal(T const&a, T const&b) { return a==b; } -std::expected exports::test::results::test::StringError(float a) { +namespace test_exports = ::exports::test::results::test; + +std::expected test_exports::StringError(float a) { if (a==0.0) { return std::unexpected(wit::string::from_view("zero")); } @@ -15,43 +17,43 @@ std::expected exports::test::results::test::StringError(floa } } -std::expected exports::test::results::test::EnumError(float a) { +std::expected test_exports::EnumError(float a) { if (a==0.0) { - return std::unexpected(::test::results::test::E::kA); + return std::unexpected(test_exports::E::kA); } else { return a; } } -std::expected exports::test::results::test::RecordError(float a) { +std::expected test_exports::RecordError(float a) { if (a==0.0) { - return std::unexpected(::test::results::test::E2{420, 0}); + return std::unexpected(test_exports::E2{420, 0}); } else if (a==1.0) { - return std::unexpected(::test::results::test::E2{77, 2}); + return std::unexpected(test_exports::E2{77, 2}); } else { return a; } } -std::expected exports::test::results::test::VariantError(float a) { +std::expected test_exports::VariantError(float a) { if (a==0.0) { - return std::unexpected(::test::results::test::E3{::test::results::test::E3::E2{::test::results::test::E2{420, 0}}}); + return std::unexpected(test_exports::E3{test_exports::E3::E2{test_exports::E2{420, 0}}}); } else if (a==1.0) { - return std::unexpected(::test::results::test::E3{::test::results::test::E3::E1{::test::results::test::E::kB}}); + return std::unexpected(test_exports::E3{test_exports::E3::E1{test_exports::E::kB}}); } else if (a==2.0) { - return std::unexpected(::test::results::test::E3{::test::results::test::E3::E1{::test::results::test::E::kC}}); + return std::unexpected(test_exports::E3{test_exports::E3::E1{test_exports::E::kC}}); } else { return a; } } -std::expected exports::test::results::test::EmptyError(uint32_t a) { +std::expected test_exports::EmptyError(uint32_t a) { if (a==0) { return std::unexpected(wit::Void{}); } @@ -63,7 +65,7 @@ std::expected exports::test::results::test::EmptyError(uint } } -std::expected, wit::string> exports::test::results::test::DoubleError(uint32_t a) { +std::expected, wit::string> test_exports::DoubleError(uint32_t a) { if (a==0) { return std::expected(); } diff --git a/tests/runtime/variants/test.cpp b/tests/runtime/variants/test.cpp index c1eda6b9c..f0d6270a8 100644 --- a/tests/runtime/variants/test.cpp +++ b/tests/runtime/variants/test.cpp @@ -2,7 +2,9 @@ #include #include -std::optional exports::test::variants::to_test::RoundtripOption(std::optional a) { +namespace test_exports = ::exports::test::variants::to_test; + +std::optional test_exports::RoundtripOption(std::optional a) { if (a.has_value()) { return std::optional(a); } else { @@ -10,7 +12,7 @@ std::optional exports::test::variants::to_test::RoundtripOption(std::op } } -std::expected exports::test::variants::to_test::RoundtripResult(std::expected a) { +std::expected test_exports::RoundtripResult(std::expected a) { if (a.has_value()) { return std::expected(double(a.value())); } else { @@ -18,26 +20,26 @@ std::expected exports::test::variants::to_test::RoundtripResult } } -::test::variants::to_test::E1 exports::test::variants::to_test::RoundtripEnum(::test::variants::to_test::E1 a) { +test_exports::E1 test_exports::RoundtripEnum(test_exports::E1 a) { return a; } -bool exports::test::variants::to_test::InvertBool(bool a) { +bool test_exports::InvertBool(bool a) { return !a; } -std::tuple<::test::variants::to_test::C1, ::test::variants::to_test::C2, ::test::variants::to_test::C3, ::test::variants::to_test::C4, ::test::variants::to_test::C5, ::test::variants::to_test::C6> exports::test::variants::to_test::VariantCasts(std::tuple<::test::variants::to_test::C1, ::test::variants::to_test::C2, ::test::variants::to_test::C3, ::test::variants::to_test::C4, ::test::variants::to_test::C5, ::test::variants::to_test::C6> a) { +std::tuple test_exports::VariantCasts(std::tuple a) { return a; } -std::tuple<::test::variants::to_test::Z1, ::test::variants::to_test::Z2, ::test::variants::to_test::Z3, ::test::variants::to_test::Z4> exports::test::variants::to_test::VariantZeros(std::tuple<::test::variants::to_test::Z1, ::test::variants::to_test::Z2, ::test::variants::to_test::Z3, ::test::variants::to_test::Z4> a) { +std::tuple test_exports::VariantZeros(std::tuple a) { return a; } -void exports::test::variants::to_test::VariantTypedefs(std::optional a, bool b, std::expected c) { +void test_exports::VariantTypedefs(std::optional a, bool b, std::expected c) { } -std::tuple, ::test::variants::to_test::MyErrno> exports::test::variants::to_test::VariantEnums(bool a, std::expected b, ::test::variants::to_test::MyErrno c) { - return std::tuple, ::test::variants::to_test::MyErrno>(a, b, c); +std::tuple, test_exports::MyErrno> test_exports::VariantEnums(bool a, std::expected b, test_exports::MyErrno c) { + return std::tuple, test_exports::MyErrno>(a, b, c); } From 103a13291cde29ef9689057ca13b20419279a8db Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Thu, 26 Jun 2025 09:05:56 +0100 Subject: [PATCH 07/12] Fix rebase errors --- crates/cpp/DESIGN.md | 25 ---- crates/cpp/helper-types/wit-common.h | 76 ----------- crates/cpp/src/lib.rs | 122 +----------------- ...rship-both_list_and_resource-TheResource.h | 6 +- .../runner-coarse-borrowing.cpp | 15 --- .../param-ownership/runner-fine-borrowing.cpp | 15 --- .../cpp/param-ownership/runner-owning.cpp | 11 -- tests/runtime/lists/test.cpp | 6 +- tests/runtime/options/test.cpp | 17 --- tests/runtime/strings/runner.cpp | 8 -- tests/runtime/strings/test.cpp | 7 - 11 files changed, 7 insertions(+), 301 deletions(-) delete mode 100644 crates/cpp/helper-types/wit-common.h diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index 6c5922f89..c50dd5ede 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -19,15 +19,7 @@ | API | | | ABI | | | --- | --- | --- | --- | --- | | 🌓 | asymmetric | | 📘 | canonical | -<<<<<<< HEAD -<<<<<<< HEAD | ⚖️ | symmetric | | 🪞 | symmetric | -======= -| 🪞 | symmetric | | 🪞 | symmetric | ->>>>>>> 95814ef8 (Update docs) -======= -| ⚖️ | symmetric | | 🪞 | symmetric | ->>>>>>> efa3a695 (Review feedback) | Code | mode | WIT Type | Rust type | C++ Type | Lower | Reason | | --- | --- | --- | --- | --- | --- | --- | @@ -44,36 +36,19 @@ | | | list | Vec | wit::vector | &(a,l) | | | | result | Result | std::expected | &(d,a,l) | | GEA | t | string | String | 🌓 wit::string | addr, len | -<<<<<<< HEAD -<<<<<<< HEAD | | | | | ⚖️ string_view | | | | | result | Result | 🌓 std::expected | d,a,l | | | | | | ⚖️ std::expected | | | GER | p | string | String | wit::string (or std?) | 📘 -> &(a,l) cabi_post_N:P/I#F [^6] | -======= -| | | | | 🪞 string_view | | -======= -| | | | | ⚖️ string_view | | ->>>>>>> efa3a695 (Review feedback) -| | | result | Result | 🌓 std::expected | d,a,l | -| | | | | ⚖️ std::expected | | -| GER | p | string | String | wit::string (or std?) | 📘 -> &(a,l) cabi_post_N:P/I#F [^7] | ->>>>>>> 95814ef8 (Update docs) | | | | | | 🪞 &(a,l) | | | | result | Result | std::expected | 📘 -> &(d,a,l) cabi_post | | --S | ? | string | String | wit::string | addr, len | | HIA | v | string | | string_view | a,l | | HIR | t | string | | wit::string[^3] | &(a,l) | | HEA | t | string | | 🌓 wit::string[^4] | a,l | -<<<<<<< HEAD | | | | | ⚖️ string_view [^5] | | | HER | p | string | | 🌓 wit::guest_owned | 📘 -> &(a,l) | | | | | | ⚖️ wit::string [^5] | 🪞 &(a,l) | -======= -| | | | | 🪞 string_view [^6] | | -| HER | p | string | | 🌓 wit::guest_owned | 📘 -> &(a,l) | -| | | | | 🪞 wit::string [^6] | 🪞 &(a,l) | ->>>>>>> 95814ef8 (Update docs) [^1]: The host never frees memory (is never passed ownership)! diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h deleted file mode 100644 index 6d9d585f3..000000000 --- a/crates/cpp/helper-types/wit-common.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include -#include -#include -#include // size_t -#include -#if __cplusplus > 202001L -#include -#else -#include -#endif - -namespace wit { -#if __cplusplus > 202001L -using std::span; -#else -/// Minimal span (vector view) implementation for older C++ environments -template class span { - T const *address; - size_t length; - -public: - T const *data() const { return address; } - size_t size() const { return length; } - - typedef T const *const_iterator; - - const_iterator begin() const { return address; } - const_iterator end() const { return address + length; } - bool empty() const { return !length; } - T const &operator[](size_t index) const { return address[index]; } - span(T *a, size_t l) : address(a), length(l) {} - span() : address((T*)alignof(T)), length(0) {} - // create from any compatible vector (borrows data!) - template - span(std::vector const &vec) : address(vec.data()), length(vec.size()) {} -}; -#endif - -/// @brief Helper class to map between IDs and resources -/// @tparam R Type of the Resource -template class ResourceTable { - static std::map resources; - -public: - static R *lookup_resource(int32_t id) { - auto result = resources.find(id); - return result == resources.end() ? nullptr : &result->second; - } - static int32_t store_resource(R &&value) { - auto last = resources.rbegin(); - int32_t id = last == resources.rend() ? 0 : last->first + 1; - resources.insert(std::pair(id, std::move(value))); - return id; - } - static std::optional remove_resource(int32_t id) { - auto iter = resources.find(id); - std::optional result; - if (iter != resources.end()) { - result = std::move(iter->second); - resources.erase(iter); - } - return std::move(result); - } -}; - -/// @brief Replaces void in the error position of a result -struct Void {}; - -template -constexpr To bit_cast(const From& from) noexcept { - union Bits { From from; To to; }; - Bits b; b.from = from; return b.to; -} -} // namespace wit diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d431f0f8d..012c824fd 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1,7 +1,6 @@ use anyhow::bail; use heck::{ToPascalCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; use std::{ - any::Any, collections::{HashMap, HashSet}, fmt::{self, Display, Write as FmtWrite}, io::{Read, Write}, @@ -30,8 +29,6 @@ pub const RESOURCE_EXPORT_BASE_CLASS_NAME: &str = "ResourceExportBase"; pub const RESOURCE_TABLE_NAME: &str = "ResourceTable"; pub const OWNED_CLASS_NAME: &str = "Owned"; pub const POINTER_SIZE_EXPRESSION: &str = "sizeof(void*)"; -// these types are always defined in the non-exports namespace -const NOT_IN_EXPORTED_NAMESPACE: bool = false; type CppType = String; @@ -43,16 +40,6 @@ enum Flavor { BorrowedArgument, } -impl Flavor { - fn is_guest_export(&self) -> bool { - match self { - Flavor::Argument(var) => matches!(var, AbiVariant::GuestExport), - Flavor::Result(var) => matches!(var, AbiVariant::GuestExport), - Flavor::InStruct | Flavor::BorrowedArgument => false, - } - } -} - #[derive(Default)] struct HighlevelSignature { /// this is a constructor or destructor without a written type @@ -149,15 +136,7 @@ pub struct Opts { #[cfg_attr( feature = "clap", arg( -<<<<<<< HEAD -<<<<<<< HEAD - long, -======= - long, ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= long, ->>>>>>> 78169a8a (Format) default_value_t = APIStyle::default(), value_name = "STYLE", ), @@ -213,20 +192,7 @@ impl FromStr for APIStyle { match s { "asymmetric" => Ok(APIStyle::Asymmetric), "symmetric" => Ok(APIStyle::Symmetric), -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> 78169a8a (Format) - _ => bail!( - "unrecognized API style: `{}`; expected `asymmetric` or `symmetric`", - s - ), -<<<<<<< HEAD -======= _ => bail!("unrecognized API style: `{}`; expected `asymmetric` or `symmetric`", s), ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= ->>>>>>> 78169a8a (Format) } } } @@ -733,13 +699,9 @@ impl WorldGenerator for Cpp { .as_slice(), ); -<<<<<<< HEAD if self.dependencies.needs_wit { files.push(&format!("wit.h"), include_bytes!("../helper-types/wit.h")); } - -======= ->>>>>>> 2661d5e6 (Use value types for asymmetric API) Ok(()) } } @@ -1235,10 +1197,6 @@ impl CppInterfaceGenerator<'_> { let special = is_special_method(func); if !matches!(special, SpecialMethod::Allocate) { self.gen.c_src.src.push_str("{\n"); -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> 78169a8a (Format) let needs_dealloc = if self.gen.opts.api_style == APIStyle::Symmetric && matches!(variant, AbiVariant::GuestExport) { @@ -1251,22 +1209,6 @@ impl CppInterfaceGenerator<'_> { } else { false }; -<<<<<<< HEAD -======= - let needs_dealloc = - if self.gen.opts.api_style == APIStyle::Symmetric && matches!(variant, AbiVariant::GuestExport) { - self.gen - .c_src - .src - .push_str("std::vector _deallocate;\n"); - self.gen.dependencies.needs_vector = true; - true - } else { - false - }; ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= ->>>>>>> 78169a8a (Format) let lift_lower = if export { LiftLower::LiftArgsLowerResults } else { @@ -1513,17 +1455,7 @@ impl CppInterfaceGenerator<'_> { "std::string_view".into() } Flavor::Argument(var) -<<<<<<< HEAD -<<<<<<< HEAD - if matches!(var, AbiVariant::GuestImport) - || self.gen.opts.api_style == APIStyle::Symmetric => -======= if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= - if matches!(var, AbiVariant::GuestImport) - || self.gen.opts.api_style == APIStyle::Symmetric => ->>>>>>> 78169a8a (Format) { self.gen.dependencies.needs_string_view = true; "std::string_view".into() @@ -1624,17 +1556,9 @@ impl CppInterfaceGenerator<'_> { format!("std::span<{inner} const>") } Flavor::Argument(var) -<<<<<<< HEAD -<<<<<<< HEAD - if matches!(var, AbiVariant::GuestImport) - || self.gen.opts.api_style == APIStyle::Symmetric => -======= - if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => ->>>>>>> 78169a8a (Format) + { self.gen.dependencies.needs_span = true; format!("std::span<{inner} const>") @@ -1708,14 +1632,6 @@ impl CppInterfaceGenerator<'_> { } } - fn variant_for_type(&self, ty: &TypeDef) -> AbiVariant { - if self.is_exported_type(ty) { - AbiVariant::GuestExport - } else { - AbiVariant::GuestImport - } - } - fn has_resources2(&self, ty: &Type) -> bool { match ty { Type::Bool @@ -2526,19 +2442,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { r#"auto {result} = wit::vector<{vtype}>::allocate({len}); "#, )); -<<<<<<< HEAD -<<<<<<< HEAD - if self.gen.gen.opts.api_style == APIStyle::Symmetric - && matches!(self.variant, AbiVariant::GuestExport) - { -======= - if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) { ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= + if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) { ->>>>>>> 78169a8a (Format) assert!(self.needs_dealloc); self.push_str(&format!("if ({len}>0) _deallocate.push_back({base});\n")); } @@ -2558,27 +2465,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // inplace construct uwriteln!(self.src, "{result}.initialize(i, std::move(e{tmp}));"); uwriteln!(self.src, "}}"); -<<<<<<< HEAD -<<<<<<< HEAD - if self.gen.gen.opts.api_style == APIStyle::Symmetric - && matches!(self.variant, AbiVariant::GuestExport) - { - results.push(format!("{result}.get_const_view()")); - if self.gen.gen.opts.api_style == APIStyle::Symmetric - && matches!(self.variant, AbiVariant::GuestExport) -======= - if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) { - results.push(format!("{result}.get_const_view()")); - if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= + if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) { results.push(format!("{result}.get_const_view()")); if self.gen.gen.opts.api_style == APIStyle::Symmetric && matches!(self.variant, AbiVariant::GuestExport) ->>>>>>> 78169a8a (Format) { self.leak_on_insertion.replace(format!( "if ({len}>0) _deallocate.push_back((void*){result}.leak());\n" @@ -2889,16 +2782,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } let op0 = &operands[0]; -<<<<<<< HEAD -<<<<<<< HEAD - let flavor = if matches!(self.variant, AbiVariant::GuestImport) { -======= - let flavor = if matches!(self.variant, AbiVariant::GuestImport) - { ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= let flavor = if matches!(self.variant, AbiVariant::GuestImport) { ->>>>>>> 78169a8a (Format) Flavor::BorrowedArgument } else { Flavor::InStruct diff --git a/tests/runtime/cpp/param-ownership/cpp/exports-test-ownership-both_list_and_resource-TheResource.h b/tests/runtime/cpp/param-ownership/cpp/exports-test-ownership-both_list_and_resource-TheResource.h index 04cb1aff9..d6d9073ee 100644 --- a/tests/runtime/cpp/param-ownership/cpp/exports-test-ownership-both_list_and_resource-TheResource.h +++ b/tests/runtime/cpp/param-ownership/cpp/exports-test-ownership-both_list_and_resource-TheResource.h @@ -21,14 +21,14 @@ class TheResource : public wit::ResourceExportBase { return Owned(new TheResource(std::move(the_list))); } wit::vector ToUpper() { - wit::vector result; - result.reserve(the_list_.size()); + wit::vector result = + wit::vector::allocate(the_list_.size()); for (size_t i = 0; i < the_list_.size(); ++i) { auto str = the_list_[i]; for (char &c : str) { c = std::toupper(c); } - result.emplace_back(std::move(str)); + result.initialize(i, std::move(str)); } return result; } diff --git a/tests/runtime/cpp/param-ownership/runner-coarse-borrowing.cpp b/tests/runtime/cpp/param-ownership/runner-coarse-borrowing.cpp index e403ef98c..d18c37263 100644 --- a/tests/runtime/cpp/param-ownership/runner-coarse-borrowing.cpp +++ b/tests/runtime/cpp/param-ownership/runner-coarse-borrowing.cpp @@ -5,17 +5,10 @@ int main() { std::array a1 = {"value1", "value2"}; std::array a2 = {"value3", "value4"}; -<<<<<<< HEAD std::array, 2> as = { std::span(a1.data(), a1.size()), std::span(a2.data(), a2.size())}; std::span const> input(as.data(), -======= - std::array, 2> as = { - wit::span(a1.data(), a1.size()), - wit::span(a2.data(), a2.size())}; - wit::span const> input(as.data(), ->>>>>>> 25d17a0c (Implement different ownership rules) as.size()); auto res = test::ownership::Foo(input); assert(res.size() == 2); @@ -29,11 +22,7 @@ int main() { test::ownership::ThingParam thing; thing.name = std::string_view("thing"); std::array values = {"value1", "value2"}; -<<<<<<< HEAD thing.value = std::span(values.data(), values.size()); -======= - thing.value = wit::span(values.data(), values.size()); ->>>>>>> 25d17a0c (Implement different ownership rules) test::ownership::Bar(thing); auto result = test::ownership::Baz(thing); assert(result.name.get_view() == "THING"); @@ -48,11 +37,7 @@ int main() { test::ownership::both_list_and_resource::Thing resource_thing{ std::move(v1), test::ownership::both_list_and_resource::TheResource( -<<<<<<< HEAD std::span(v2.data(), v2.size()))}; -======= - wit::span(v2.data(), v2.size()))}; ->>>>>>> 25d17a0c (Implement different ownership rules) test::ownership::both_list_and_resource::ListAndResource( std::move(resource_thing)); } \ No newline at end of file diff --git a/tests/runtime/cpp/param-ownership/runner-fine-borrowing.cpp b/tests/runtime/cpp/param-ownership/runner-fine-borrowing.cpp index 598a76a70..6266eca8e 100644 --- a/tests/runtime/cpp/param-ownership/runner-fine-borrowing.cpp +++ b/tests/runtime/cpp/param-ownership/runner-fine-borrowing.cpp @@ -5,17 +5,10 @@ int main() { std::array a1 = {"value1", "value2"}; std::array a2 = {"value3", "value4"}; -<<<<<<< HEAD std::array, 2> as = { std::span(a1.data(), a1.size()), std::span(a2.data(), a2.size())}; std::span const> input(as.data(), -======= - std::array, 2> as = { - wit::span(a1.data(), a1.size()), - wit::span(a2.data(), a2.size())}; - wit::span const> input(as.data(), ->>>>>>> 25d17a0c (Implement different ownership rules) as.size()); auto res = test::ownership::Foo(input); assert(res.size() == 2); @@ -29,11 +22,7 @@ int main() { test::ownership::ThingParam thing; thing.name = std::string_view("thing"); std::array values = {"value1", "value2"}; -<<<<<<< HEAD thing.value = std::span(values.data(), values.size()); -======= - thing.value = wit::span(values.data(), values.size()); ->>>>>>> 25d17a0c (Implement different ownership rules) test::ownership::Bar(thing); auto result = test::ownership::Baz(thing); assert(result.name.get_view() == "THING"); @@ -42,11 +31,7 @@ int main() { assert(result.value[1].get_view() == "VALUE2"); std::array v1 = {"value1", "value2"}; -<<<<<<< HEAD std::span v2(v1.data(), v1.size()); -======= - wit::span v2(v1.data(), v1.size()); ->>>>>>> 25d17a0c (Implement different ownership rules) test::ownership::both_list_and_resource::ThingParam resource_thing{ v2, test::ownership::both_list_and_resource::TheResource(v2)}; diff --git a/tests/runtime/cpp/param-ownership/runner-owning.cpp b/tests/runtime/cpp/param-ownership/runner-owning.cpp index fd3e20098..82444bf1b 100644 --- a/tests/runtime/cpp/param-ownership/runner-owning.cpp +++ b/tests/runtime/cpp/param-ownership/runner-owning.cpp @@ -5,17 +5,10 @@ int main() { std::array a1 = {"value1", "value2"}; std::array a2 = {"value3", "value4"}; -<<<<<<< HEAD std::array, 2> as = { std::span(a1.data(), a1.size()), std::span(a2.data(), a2.size())}; std::span const> input(as.data(), -======= - std::array, 2> as = { - wit::span(a1.data(), a1.size()), - wit::span(a2.data(), a2.size())}; - wit::span const> input(as.data(), ->>>>>>> 25d17a0c (Implement different ownership rules) as.size()); auto res = test::ownership::Foo(input); assert(res.size() == 2); @@ -44,11 +37,7 @@ int main() { std::array v2 = {"value1", "value2"}; test::ownership::both_list_and_resource::Thing resource_thing { std::move(v1), -<<<<<<< HEAD test::ownership::both_list_and_resource::TheResource(std::span(v2.data(), v2.size())) -======= - test::ownership::both_list_and_resource::TheResource(wit::span(v2.data(), v2.size())) ->>>>>>> 25d17a0c (Implement different ownership rules) }; test::ownership::both_list_and_resource::ListAndResource( std::move(resource_thing)); diff --git a/tests/runtime/lists/test.cpp b/tests/runtime/lists/test.cpp index 8d1841277..c4073bdc9 100644 --- a/tests/runtime/lists/test.cpp +++ b/tests/runtime/lists/test.cpp @@ -33,7 +33,7 @@ static bool equal(std::span const&a, std::vector const& b) { } template static bool equal(wit::vector const&a, std::vector const& b) { - return equal(a.get_view(), std::span(b)); + return equal(a.get_view(), std::span(b)); } template static bool equal(std::tuple const&a, std::tuple const& b) { @@ -82,10 +82,6 @@ void exports::test::lists::to_test::ListParam4(wit::vector>>>>>> 2661d5e6 (Use value types for asymmetric API) void exports::test::lists::to_test::ListParam5(wit::vector> a) { } diff --git a/tests/runtime/options/test.cpp b/tests/runtime/options/test.cpp index 440e1b47a..1f295f469 100644 --- a/tests/runtime/options/test.cpp +++ b/tests/runtime/options/test.cpp @@ -4,15 +4,7 @@ #include static bool equal(std::optional const& a, std::optional const& b) { -<<<<<<< HEAD -<<<<<<< HEAD return a.has_value() == b.has_value() && a->get_view()==b.value(); -======= - return a->get_view()==b.value(); ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= - return a.has_value() == b.has_value() && a->get_view()==b.value(); ->>>>>>> efa3a695 (Review feedback) } void exports::test::options::to_test::OptionNoneParam(std::optional a) @@ -33,16 +25,7 @@ std::optional exports::test::options::to_test::OptionSomeResult() { } std::optional exports::test::options::to_test::OptionRoundtrip(std::optional a) { -<<<<<<< HEAD -<<<<<<< HEAD - return a; -======= - if (!a.has_value()) return std::optional(); - return std::optional(a); ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= return a; ->>>>>>> efa3a695 (Review feedback) } std::optional> exports::test::options::to_test::DoubleOptionRoundtrip(std::optional> a) { diff --git a/tests/runtime/strings/runner.cpp b/tests/runtime/strings/runner.cpp index 6d7f9b814..d4fd0e719 100644 --- a/tests/runtime/strings/runner.cpp +++ b/tests/runtime/strings/runner.cpp @@ -1,11 +1,3 @@ -<<<<<<< HEAD -<<<<<<< HEAD -======= -//@ args = '--api-style symmetric' - ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= ->>>>>>> a6728b69 (Correct test arguments) #include #include #include diff --git a/tests/runtime/strings/test.cpp b/tests/runtime/strings/test.cpp index 9eeb2352a..19957b292 100644 --- a/tests/runtime/strings/test.cpp +++ b/tests/runtime/strings/test.cpp @@ -25,12 +25,5 @@ wit::string exports::test::strings::to_test::ReturnEmpty() { } wit::string exports::test::strings::to_test::Roundtrip(wit::string str) { -<<<<<<< HEAD -<<<<<<< HEAD -======= - assert(str.size() > 0); ->>>>>>> 2661d5e6 (Use value types for asymmetric API) -======= ->>>>>>> efa3a695 (Review feedback) return str; } From 8077194fce3a553ffdcf7b1c7c12780b76721d14 Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Thu, 26 Jun 2025 09:07:37 +0100 Subject: [PATCH 08/12] Remove old file --- ...rship-both_list_and_resource-TheResource.h | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 tests/runtime/cpp/param-ownership/cpp17/exports-test-ownership-both_list_and_resource-TheResource.h diff --git a/tests/runtime/cpp/param-ownership/cpp17/exports-test-ownership-both_list_and_resource-TheResource.h b/tests/runtime/cpp/param-ownership/cpp17/exports-test-ownership-both_list_and_resource-TheResource.h deleted file mode 100644 index 86e921e9c..000000000 --- a/tests/runtime/cpp/param-ownership/cpp17/exports-test-ownership-both_list_and_resource-TheResource.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include -#include -#include -#include -/* User class definition file, autogenerated once, then user modified - * Updated versions of this file are generated into TheResource.template. - */ -namespace exports { -namespace test { -namespace ownership { -namespace both_list_and_resource { -class TheResource : public wit::ResourceExportBase { - -public: - static void Dtor(both_list_and_resource::TheResource *self) { delete self; } - TheResource(wit::vector the_list) { - the_list_ = std::move(the_list); - } - static Owned New(wit::vector the_list) { - return Owned(new TheResource(std::move(the_list))); - } - wit::vector ToUpper() { - wit::vector result = - wit::vector::allocate(the_list_.size()); - for (size_t i = 0; i < the_list_.size(); ++i) { - auto str = the_list_[i]; - for (char &c : str) { - c = std::toupper(c); - } - result.initialize(i, std::move(str)); - } - return result; - } - static int32_t ResourceNew(both_list_and_resource::TheResource *self); - static TheResource *ResourceRep(int32_t id); - static void ResourceDrop(int32_t id); - -private: - wit::vector the_list_; -}; -} // namespace both_list_and_resource -} // namespace ownership -} // namespace test -} // namespace exports From e9e8f01ed10e6130158f0f7d053ae7018a0c41ad Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Thu, 26 Jun 2025 09:18:32 +0100 Subject: [PATCH 09/12] Revert wit::string changes --- crates/cpp/helper-types/wit.h | 1 - tests/runtime/cpp/param-ownership/runner-owning.cpp | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cpp/helper-types/wit.h b/crates/cpp/helper-types/wit.h index 80f2e124c..8a79db32c 100644 --- a/crates/cpp/helper-types/wit.h +++ b/crates/cpp/helper-types/wit.h @@ -57,7 +57,6 @@ class string { static uint8_t const* empty_ptr() { return (uint8_t const *)1; } public: - string() : data_(empty_ptr()), length(0) {} // this constructor is helpful for creating vector string(string const &b) : string(string::from_view(b.get_view())) {} string(string &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; } diff --git a/tests/runtime/cpp/param-ownership/runner-owning.cpp b/tests/runtime/cpp/param-ownership/runner-owning.cpp index 82444bf1b..edb31c4d9 100644 --- a/tests/runtime/cpp/param-ownership/runner-owning.cpp +++ b/tests/runtime/cpp/param-ownership/runner-owning.cpp @@ -19,9 +19,10 @@ int main() { assert(res[1][0].get_view() == "VALUE3"); assert(res[1][1].get_view() == "VALUE4"); - test::ownership::Thing thing; - thing.name = wit::string::from_view("thing"); - thing.value = wit::vector::allocate(2); + test::ownership::Thing thing { + wit::string::from_view("thing"), + wit::vector::allocate(2) + }; thing.value.initialize(0, wit::string::from_view("value1")); thing.value.initialize(1, wit::string::from_view("value2")); test::ownership::Bar(thing); From dd3969386db92434f479db6137ad30bfe3051d40 Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Thu, 26 Jun 2025 09:47:18 +0100 Subject: [PATCH 10/12] format --- crates/cpp/src/lib.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 012c824fd..ed7e89d1e 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -192,7 +192,10 @@ impl FromStr for APIStyle { match s { "asymmetric" => Ok(APIStyle::Asymmetric), "symmetric" => Ok(APIStyle::Symmetric), - _ => bail!("unrecognized API style: `{}`; expected `asymmetric` or `symmetric`", s), + _ => bail!( + "unrecognized API style: `{}`; expected `asymmetric` or `symmetric`", + s + ), } } } @@ -1455,7 +1458,8 @@ impl CppInterfaceGenerator<'_> { "std::string_view".into() } Flavor::Argument(var) - if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => + if matches!(var, AbiVariant::GuestImport) + || self.gen.opts.api_style == APIStyle::Symmetric => { self.gen.dependencies.needs_string_view = true; "std::string_view".into() @@ -1558,7 +1562,6 @@ impl CppInterfaceGenerator<'_> { Flavor::Argument(var) if matches!(var, AbiVariant::GuestImport) || self.gen.opts.api_style == APIStyle::Symmetric => - { self.gen.dependencies.needs_span = true; format!("std::span<{inner} const>") @@ -1919,12 +1922,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> ) { let ty = &self.resolve.types[id]; let guest_export = self.is_exported_type(ty); - let namespc = namespace( - self.resolve, - &ty.owner, - guest_export, - &self.gen.opts, - ); + let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts); if self.gen.is_first_definition(&namespc, name) { self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); From 8120c93d996bb300ac9c45cfdf341144208ce80d Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Thu, 26 Jun 2025 13:25:40 +0100 Subject: [PATCH 11/12] Fix borrowed handles in lists --- crates/cpp/src/lib.rs | 100 ++++++------------ .../cpp/param-ownership/runner-owning.cpp | 17 ++- ...-resource_borrow_in_record-to_test-Thing.h | 32 ++++++ .../resource_borrow_in_record/runner.cpp | 12 +++ .../resource_borrow_in_record/test.cpp | 12 +++ 5 files changed, 99 insertions(+), 74 deletions(-) create mode 100644 tests/runtime/resource_borrow_in_record/cpp/exports-test-resource_borrow_in_record-to_test-Thing.h create mode 100644 tests/runtime/resource_borrow_in_record/runner.cpp create mode 100644 tests/runtime/resource_borrow_in_record/test.cpp diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index ed7e89d1e..27aa5daf3 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -18,7 +18,7 @@ use wit_bindgen_core::{ Resolve, SizeAlign, Stability, Type, TypeDef, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, }, - Files, InterfaceGenerator, Source, WorldGenerator, + Files, InterfaceGenerator, Source, Types, WorldGenerator, }; // mod wamr; @@ -102,6 +102,7 @@ struct Cpp { imported_interfaces: HashSet, user_class_files: HashMap, defined_types: HashSet<(Vec, String)>, + types: Types, // needed for symmetric disambiguation interface_prefixes: HashMap<(Direction, WorldKey), String>, @@ -451,6 +452,7 @@ impl WorldGenerator for Cpp { fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { let name = &resolve.worlds[world].name; self.world = name.to_string(); + self.types.analyze(resolve); self.world_id = Some(world); uwriteln!( self.c_src_head, @@ -1394,25 +1396,17 @@ impl CppInterfaceGenerator<'_> { if let Flavor::Argument(AbiVariant::GuestImport) = flavor { match self.gen.opts.ownership { Ownership::Owning => { - if self.has_resources(&id) { - format!("{}&&", name) - } else { - format!("{} const&", name) - } + format!("{}", name) } Ownership::CoarseBorrowing => { - if self.has_resources(&id) { - format!("{}&&", name) + if self.gen.types.get(id).has_own_handle { + format!("{}", name) } else { - format!("{}Param const&", name) + format!("{}Param", name) } } Ownership::FineBorrowing => { - if self.has_resources(&id) { - format!("{}Param&&", name) - } else { - format!("{}Param const&", name) - } + format!("{}Param", name) } } } else { @@ -1564,7 +1558,13 @@ impl CppInterfaceGenerator<'_> { || self.gen.opts.api_style == APIStyle::Symmetric => { self.gen.dependencies.needs_span = true; - format!("std::span<{inner} const>") + // If the list has an owning handle, it must support moving, so can't be const + let constness = if self.gen.types.get(*id).has_own_handle { + "" + } else { + " const" + }; + format!("std::span<{inner}{constness}>") } Flavor::Argument(AbiVariant::GuestExport) => { self.gen.dependencies.needs_wit = true; @@ -1635,54 +1635,6 @@ impl CppInterfaceGenerator<'_> { } } - fn has_resources2(&self, ty: &Type) -> bool { - match ty { - Type::Bool - | Type::U8 - | Type::U16 - | Type::U32 - | Type::U64 - | Type::S8 - | Type::S16 - | Type::S32 - | Type::S64 - | Type::F32 - | Type::F64 - | Type::Char => false, - Type::String => false, - Type::Id(id) => self.has_resources(id), - Type::ErrorContext => todo!(), - } - } - fn has_resources(&self, id: &TypeId) -> bool { - match &self.resolve.types[*id].kind { - TypeDefKind::Record(r) => r.fields.iter().any(|f| self.has_resources2(&f.ty)), - TypeDefKind::Resource => true, - TypeDefKind::Handle(_) => true, - TypeDefKind::Flags(_) => false, - TypeDefKind::Tuple(t) => t.types.iter().any(|ty| self.has_resources2(ty)), - TypeDefKind::Variant(v) => v - .cases - .iter() - .any(|c| c.ty.map_or(false, |ty| self.has_resources2(&ty))), - TypeDefKind::Enum(_) => false, - TypeDefKind::Option(ty) => self.has_resources2(ty), - TypeDefKind::Result(r) => { - r.ok.as_ref().map_or(false, |ok| self.has_resources2(ok)) - || r.err.as_ref().map_or(false, |err| self.has_resources2(err)) - } - TypeDefKind::List(ty) => self.has_resources2(ty), - TypeDefKind::Future(_) => todo!(), - TypeDefKind::Stream(_) => todo!(), - TypeDefKind::Type(ty) => match ty { - Type::Id(id) => self.has_resources(id), - _ => false, - }, - TypeDefKind::FixedSizeList(_, _) => todo!(), - TypeDefKind::Unknown => todo!(), - } - } - fn type_record_param( &mut self, id: TypeId, @@ -1694,7 +1646,7 @@ impl CppInterfaceGenerator<'_> { match self.gen.opts.ownership { Ownership::Owning => (Flavor::InStruct, false), Ownership::CoarseBorrowing => { - if self.has_resources(&id) { + if self.gen.types.get(id).has_own_handle { (Flavor::InStruct, false) } else { (Flavor::BorrowedArgument, true) @@ -2313,7 +2265,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let val = format!("vec{}", tmp); let ptr = format!("ptr{}", tmp); let len = format!("len{}", tmp); - self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); + self.push_str(&format!("auto&& {} = {};\n", val, operands[0])); self.push_str(&format!( "auto {} = ({})({}.data());\n", ptr, @@ -2334,7 +2286,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let val = format!("vec{}", tmp); let ptr = format!("ptr{}", tmp); let len = format!("len{}", tmp); - self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); + self.push_str(&format!("auto&& {} = {};\n", val, operands[0])); self.push_str(&format!( "auto {} = ({})({}.data());\n", ptr, @@ -2351,14 +2303,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(len); } abi::Instruction::ListLower { - element: _, + element, realloc, } => { let tmp = self.tmp(); + let body = self.blocks.pop().unwrap(); let val = format!("vec{}", tmp); let ptr = format!("ptr{}", tmp); let len = format!("len{}", tmp); - self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); + let size = self.gen.sizes.size(element); + self.push_str(&format!("auto&& {} = {};\n", val, operands[0])); self.push_str(&format!( "auto {} = ({})({}.data());\n", ptr, @@ -2366,6 +2320,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { val )); self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); + self.push_str(&format!("for (size_t i = 0; i < {len}; ++i) {{\n")); + self.push_str(&format!( + "auto base = {ptr} + i * {size};\n", + size = size.format(POINTER_SIZE_EXPRESSION) + )); + self.push_str(&format!("auto&& IterElem = {val}[i];\n")); + self.push_str(&format!("{}\n", body.0)); + self.push_str("}\n"); if realloc.is_none() { results.push(ptr); } else { @@ -3187,7 +3149,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { return false; } match ty { - Type::Id(id) => !self.gen.has_resources(id), + Type::Id(id) => !self.gen.gen.types.get(*id).has_resource, _ => true, } } diff --git a/tests/runtime/cpp/param-ownership/runner-owning.cpp b/tests/runtime/cpp/param-ownership/runner-owning.cpp index edb31c4d9..fe6e4a66d 100644 --- a/tests/runtime/cpp/param-ownership/runner-owning.cpp +++ b/tests/runtime/cpp/param-ownership/runner-owning.cpp @@ -19,14 +19,21 @@ int main() { assert(res[1][0].get_view() == "VALUE3"); assert(res[1][1].get_view() == "VALUE4"); - test::ownership::Thing thing { + test::ownership::Thing thing1 { wit::string::from_view("thing"), wit::vector::allocate(2) }; - thing.value.initialize(0, wit::string::from_view("value1")); - thing.value.initialize(1, wit::string::from_view("value2")); - test::ownership::Bar(thing); - auto result = test::ownership::Baz(thing); + thing1.value.initialize(0, wit::string::from_view("value1")); + thing1.value.initialize(1, wit::string::from_view("value2")); + test::ownership::Bar(std::move(thing1)); + + test::ownership::Thing thing2 { + wit::string::from_view("thing"), + wit::vector::allocate(2) + }; + thing2.value.initialize(0, wit::string::from_view("value1")); + thing2.value.initialize(1, wit::string::from_view("value2")); + auto result = test::ownership::Baz(std::move(thing2)); assert(result.name.get_view() == "THING"); assert(result.value.size() == 2); assert(result.value[0].get_view() == "VALUE1"); diff --git a/tests/runtime/resource_borrow_in_record/cpp/exports-test-resource_borrow_in_record-to_test-Thing.h b/tests/runtime/resource_borrow_in_record/cpp/exports-test-resource_borrow_in_record-to_test-Thing.h new file mode 100644 index 000000000..fbd735158 --- /dev/null +++ b/tests/runtime/resource_borrow_in_record/cpp/exports-test-resource_borrow_in_record-to_test-Thing.h @@ -0,0 +1,32 @@ +#pragma once +#include "wit.h" +#include +#include +#include +#include +/* User class definition file, autogenerated once, then user modified + * Updated versions of this file are generated into Thing.template. + */ +namespace exports { +namespace test { +namespace resource_borrow_in_record { +namespace to_test { +class Thing : public wit::ResourceExportBase { + +public: + static void Dtor(to_test::Thing *self) { delete self; } + Thing(wit::string s) : contents(s.to_string()) {} + static Owned New(wit::string s) { return Owned(new Thing(std::move(s))); } + wit::string Get() const { return wit::string::from_view(std::string_view(contents)); } + static int32_t ResourceNew(to_test::Thing *self); + static Thing *ResourceRep(int32_t id); + static void ResourceDrop(int32_t id); + +private: + std::string contents; +}; + +} // namespace to_test +} // namespace resource_borrow_in_record +} // namespace test +} // namespace exports diff --git a/tests/runtime/resource_borrow_in_record/runner.cpp b/tests/runtime/resource_borrow_in_record/runner.cpp new file mode 100644 index 000000000..074dc49d2 --- /dev/null +++ b/tests/runtime/resource_borrow_in_record/runner.cpp @@ -0,0 +1,12 @@ +#include + +namespace test_imports = ::test::resource_borrow_in_record::to_test; +#include +int main() { + auto thing1 = test_imports::Thing("Bonjour"); + auto thing2 = test_imports::Thing("mon cher"); + std::cout << thing1.Get().to_string() << ' ' << thing1.get_handle() << std::endl; + std::cout << thing2.Get().to_string() << ' ' << thing2.get_handle() << std::endl; + std::array things {test_imports::Foo{thing1}, test_imports::Foo{thing2}}; + auto result = test_imports::Test(things); +} diff --git a/tests/runtime/resource_borrow_in_record/test.cpp b/tests/runtime/resource_borrow_in_record/test.cpp new file mode 100644 index 000000000..572766076 --- /dev/null +++ b/tests/runtime/resource_borrow_in_record/test.cpp @@ -0,0 +1,12 @@ +#include + +namespace test_exports = ::exports::test::resource_borrow_in_record::to_test; + +wit::vector test_exports::Test(wit::vector list) { + auto result = wit::vector::allocate(list.size()); + for (size_t i = 0; i < list.size(); ++i) { + auto str = wit::string::from_view(std::string_view(list[i].thing.get().Get().to_string() + " test")); + result.initialize(i, test_exports::Thing::New(std::move(str))); + } + return result; +} \ No newline at end of file From e31121e041d9684d5e9f41a3fb886b910bae92e7 Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Thu, 26 Jun 2025 13:32:40 +0100 Subject: [PATCH 12/12] Formatting --- crates/cpp/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 27aa5daf3..9ba821db6 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2302,10 +2302,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } results.push(len); } - abi::Instruction::ListLower { - element, - realloc, - } => { + abi::Instruction::ListLower { element, realloc } => { let tmp = self.tmp(); let body = self.blocks.pop().unwrap(); let val = format!("vec{}", tmp);