From 86a5850a1e6f0a1fbbddc3f3f0ddff86dbfb8542 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 30 May 2026 12:01:58 +0200 Subject: [PATCH 1/4] don't lower the vector contents in place (different layout breaks this) --- crates/cpp/src/lib.rs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index e672feb10..adffa9ce8 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2619,30 +2619,41 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let body = self.blocks.pop().unwrap(); let val = format!("_vec{tmp}"); - let ptr = format!("_ptr{tmp}"); + let vres = format!("_res{tmp}"); + let vptr = format!("_vptr{tmp}"); let len = format!("_len{tmp}"); let size = self.r#gen.sizes.size(element); - self.push_str(&format!("auto&& {} = {};\n", val, operands[0])); + self.push_str(&format!("auto&& {val} = {};\n", operands[0])); + self.push_str(&format!("auto {len} = (size_t)({val}.size());\n")); + let tpe = self + .r#gen + .type_name(element, &self.namespace, Flavor::InStruct); self.push_str(&format!( - "auto {} = ({})({}.data());\n", - ptr, - self.r#gen.r#gen.opts.ptr_type(), - val + "auto {vres} = wit::vector<{tpe}>::allocate({len});\n" + )); + self.push_str(&format!( + "auto {vptr} = ({})({vres}.data());\n", + self.r#gen.r#gen.opts.ptr_type() )); - self.push_str(&format!("auto {len} = (size_t)({val}.size());\n")); self.push_str(&format!("for (size_t i = 0; i < {len}; ++i) {{\n")); self.push_str(&format!( - "auto _base = {ptr} + i * {size};\n", + "auto _base = {vptr} + i * {size};\n", size = size.format(POINTER_SIZE_EXPRESSION) )); self.push_str(&format!("auto&& _iter_elem = {val}[i];\n")); self.push_str(&format!("{}\n", body.0)); self.push_str("}\n"); if realloc.is_none() { - results.push(ptr); + results.push(vptr); } else { - uwriteln!(self.src, "{}.leak();\n", operands[0]); - results.push(ptr); + // free the vector storage, but don't destroy elements + uwriteln!( + self.src, + "wit::vector<{tpe}>::drop_raw({}.leak());\n", + operands[0] + ); + uwriteln!(self.src, "{vres}.leak();\n"); + results.push(vptr); } results.push(len); } From 5aa17a9d03300456c7bd68ce844f3c00d1cc4611 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 30 May 2026 13:56:31 +0200 Subject: [PATCH 2/4] introduce types represented as canonical ABI representation, with borrow and owned part of the type this needs more work to get it done everywhere --- crates/cpp/src/lib.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index adffa9ce8..3b83eb58b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -34,12 +34,19 @@ pub const POINTER_SIZE_EXPRESSION: &str = "sizeof(void*)"; type CppType = String; +#[derive(Clone, Copy, Debug)] +enum SubFlavor { + Owned, + Borrowed, +} + #[derive(Clone, Copy, Debug)] enum Flavor { Argument(AbiVariant), Result(AbiVariant), InStruct, BorrowedArgument, + CanonicalAbi(SubFlavor), } #[derive(Default)] @@ -2625,9 +2632,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let size = self.r#gen.sizes.size(element); self.push_str(&format!("auto&& {val} = {};\n", operands[0])); self.push_str(&format!("auto {len} = (size_t)({val}.size());\n")); - let tpe = self - .r#gen - .type_name(element, &self.namespace, Flavor::InStruct); + let subvar = match self.variant { + AbiVariant::GuestImport => SubFlavor::Borrowed, + AbiVariant::GuestExport => todo!(), + AbiVariant::GuestImportAsync => todo!(), + AbiVariant::GuestExportAsync => todo!(), + AbiVariant::GuestExportAsyncStackful => todo!(), + }; + let tpe = + self.r#gen + .type_name(element, &self.namespace, Flavor::CanonicalAbi(subvar)); self.push_str(&format!( "auto {vres} = wit::vector<{tpe}>::allocate({len});\n" )); From e5ed538023da487d1a2ffa74ea6849218a233958 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 2 Jul 2026 00:46:40 +0200 Subject: [PATCH 3/4] differentiation --- crates/cpp/helper-types/wit.h | 115 ++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 48 deletions(-) diff --git a/crates/cpp/helper-types/wit.h b/crates/cpp/helper-types/wit.h index e643a5262..16f70ce1e 100644 --- a/crates/cpp/helper-types/wit.h +++ b/crates/cpp/helper-types/wit.h @@ -46,11 +46,8 @@ template class ResourceTable { /// @brief Replaces void in the error position of a result struct Void {}; -/// A string in linear memory, freed unconditionally using free -/// -/// A normal C++ string makes no guarantees about where the characters -/// are stored and how this is freed. -class string { +class borrowed_string { +protected: uint8_t const *data_; size_t length; // C++ is horrible! @@ -58,44 +55,18 @@ class string { static uint8_t const* empty_ptr() { return (uint8_t const *)1; } public: - // 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; } - string &operator=(string const &) = delete; - string &operator=(string &&b) { - if (data_ && data_!=empty_ptr()) { - free(const_cast(data_)); - } - data_ = b.data_; - length = b.length; - b.data_ = nullptr; - return *this; - } - string(char const *d, size_t l) : data_((uint8_t const *)d), length(l) {} + borrowed_string(borrowed_string &&b) : data_(b.data_), length(b.length) {} + borrowed_string &operator=(borrowed_string const &) = delete; + borrowed_string(char const *d, size_t l) : data_((uint8_t const *)d), length(l) {} char const *data() const { return (char const *)data_; } size_t size() const { return length; } bool empty() const { return !length; } - ~string() { - if (data_ && data_!=empty_ptr()) { - free(const_cast(data_)); - } - } - // leak the memory - void leak() { data_ = nullptr; } - // typically called by post - static void drop_raw(void *ptr) { free(ptr); } std::string_view get_view() const { return std::string_view((const char *)data_, length); } std::string to_string() const { return std::string((const char *)data_, length); } - static string from_view(std::string_view v) { - if (!v.size()) return string((char const*)empty_ptr(), 0); - char* addr = (char*)malloc(v.size()); - memcpy(addr, v.data(), v.size()); - return string(addr, v.size()); - } char* begin() { return (char*)data_; } @@ -110,27 +81,55 @@ class string { } }; -/// A vector in linear memory, freed unconditionally using free +/// A string in linear memory, freed unconditionally using free /// -/// You can't detach the data memory from a vector, nor create one -/// in a portable way from a buffer and lenght without copying. -template class vector { +/// A normal C++ string makes no guarantees about where the characters +/// are stored and how this is freed. +class string : public borrowed_string { + // this constructor is helpful for creating vector + string(string const &b) : string(string::from_view(b.get_view())) {} + string(string &&b) : borrowed_string(std::move(b)) { b.data_ = nullptr; } + string &operator=(string const &) = delete; + string(char const *d, size_t l) : borrowed_string(d, l) {} + string &operator=(string &&b) { + if (this->data_ && this->data_!=this->empty_ptr()) { + free(const_cast(this->data_)); + } + this->data_ = b.data_; + this->length = b.length; + b.data_ = nullptr; + return *this; + } + // leak the memory + void leak() { data_ = nullptr; } + // typically called by post + static void drop_raw(void *ptr) { free(ptr); } + ~string() { + if (this->data_ && this->data_!=this->empty_ptr()) { + free(const_cast(this->data_)); + } + } + static string from_view(std::string_view v) { + if (!v.size()) return string((char const*)empty_ptr(), 0); + char* addr = (char*)malloc(v.size()); + memcpy(addr, v.data(), v.size()); + return string(addr, v.size()); + } +}; + +template class borrowed_vector { T *data_; size_t length; static T* empty_ptr() { return (T*)alignof(T); } public: - vector(vector const &) = delete; - vector(vector &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; } - vector &operator=(vector const &) = delete; - vector &operator=(vector &&b) { - if (data_ && length>0) { - free(data_); - } + borrowed_vector(borrowed_vector const &) = delete; + borrowed_vector(borrowed_vector &&b) : data_(b.data_), length(b.length) {} + borrowed_vector &operator=(borrowed_vector const &) = delete; + borrowed_vector &operator=(borrowed_vector &&b) { data_ = b.data_; length = b.length; - b.data_ = nullptr; return *this; } vector(T *d, size_t l) : data_(d), length(l) {} @@ -142,6 +141,28 @@ template class vector { T const &operator[](size_t n) const { return data_[n]; } size_t size() const { return length; } bool empty() const { return !length; } + std::span get_view() const { return std::span(data_, length); } + std::span get_const_view() const { return std::span(data_, length); } +}; + +/// A vector in linear memory, freed unconditionally using free +/// +/// You can't detach the data memory from a vector, nor create one +/// in a portable way from a buffer and lenght without copying. +template class vector : public borrowed_vector { +public: + vector(vector const &) = delete; + vector(vector &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; } + vector &operator=(vector const &) = delete; + vector &operator=(vector &&b) { + if (data_ && length>0) { + free(data_); + } + data_ = b.data_; + length = b.length; + b.data_ = nullptr; + return *this; + } ~vector() { if (data_ && length>0) { for (unsigned i=0;i class vector { T* leak() { T*result = data_; data_ = nullptr; return result; } // typically called by post static void drop_raw(void *ptr) { if (ptr!=empty_ptr()) free(ptr); } - std::span get_view() const { return std::span(data_, length); } - std::span get_const_view() const { return std::span(data_, length); } template static vector from_view(std::span const& a) { auto result = vector::allocate(a.size()); for (uint32_t i=0;i Date: Fri, 3 Jul 2026 00:37:47 +0200 Subject: [PATCH 4/4] removing unused type and fixing the C++ implementation --- crates/cpp/helper-types/wit.h | 60 +++++++++++------------------------ 1 file changed, 18 insertions(+), 42 deletions(-) diff --git a/crates/cpp/helper-types/wit.h b/crates/cpp/helper-types/wit.h index 16f70ce1e..acad20c37 100644 --- a/crates/cpp/helper-types/wit.h +++ b/crates/cpp/helper-types/wit.h @@ -16,33 +16,6 @@ #include // pair namespace wit { -/// @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 {}; @@ -86,6 +59,7 @@ class borrowed_string { /// A normal C++ string makes no guarantees about where the characters /// are stored and how this is freed. class string : public borrowed_string { +public: // this constructor is helpful for creating vector string(string const &b) : string(string::from_view(b.get_view())) {} string(string &&b) : borrowed_string(std::move(b)) { b.data_ = nullptr; } @@ -118,6 +92,7 @@ class string : public borrowed_string { }; template class borrowed_vector { +protected: T *data_; size_t length; @@ -132,9 +107,9 @@ template class borrowed_vector { length = b.length; return *this; } - vector(T *d, size_t l) : data_(d), length(l) {} + borrowed_vector(T *d, size_t l) : data_(d), length(l) {} // Rust needs a nonzero pointer here (alignment is typical) - vector() : data_(empty_ptr()), length() {} + borrowed_vector() : data_(empty_ptr()), length() {} T const *data() const { return data_; } T *data() { return data_; } T &operator[](size_t n) { return data_[n]; } @@ -149,38 +124,39 @@ template class borrowed_vector { /// /// You can't detach the data memory from a vector, nor create one /// in a portable way from a buffer and lenght without copying. -template class vector : public borrowed_vector { +template class vector : public borrowed_vector { public: vector(vector const &) = delete; - vector(vector &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; } + vector(vector &&b) : borrowed_vector(b.data_, b.length) { b.data_ = nullptr; } + vector(T *d, size_t l) : borrowed_vector(d, l) {} vector &operator=(vector const &) = delete; vector &operator=(vector &&b) { - if (data_ && length>0) { - free(data_); + if (this->data_ && this->length>0) { + free(this->data_); } - data_ = b.data_; - length = b.length; + this->data_ = b.data_; + this->length = b.length; b.data_ = nullptr; return *this; } ~vector() { - if (data_ && length>0) { - for (unsigned i=0;idata_ && this->length>0) { + for (unsigned i=0;ilength;++i) { this->data_[i].~T(); } + free((void*)this->data_); } } // WARNING: vector contains uninitialized elements static vector allocate(size_t len) { - if (!len) return vector(empty_ptr(), 0); + if (!len) return vector(borrowed_vector::empty_ptr(), 0); return vector((T*)malloc(sizeof(T)*len), len); } void initialize(size_t n, T&& elem) { - new ((void*)(data_+n)) T(std::move(elem)); + new ((void*)(this->data_+n)) T(std::move(elem)); } // leak the memory - T* leak() { T*result = data_; data_ = nullptr; return result; } + T* leak() { T*result = this->data_; this->data_ = nullptr; return result; } // typically called by post - static void drop_raw(void *ptr) { if (ptr!=empty_ptr()) free(ptr); } + static void drop_raw(void *ptr) { if (ptr!=borrowed_vector::empty_ptr()) free(ptr); } template static vector from_view(std::span const& a) { auto result = vector::allocate(a.size()); for (uint32_t i=0;i