This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git


The following commit(s) were added to refs/heads/main by this push:
     new 458aaf73 [FEAT][RUST]Add tvm_ffi::optional in Rust (#630)
458aaf73 is described below

commit 458aaf73b62285e5cd4481b9d197ac13b7c8cdb2
Author: Linzhang Li <[email protected]>
AuthorDate: Sat Jul 4 11:55:45 2026 -0400

    [FEAT][RUST]Add tvm_ffi::optional in Rust (#630)
    
    This PR supports `ffi::optionalPod`, `ffi::optionalStr` in `Rust`. It
    keeps the same memory layout as C++'s ffi::optional.
    
    ---------
    
    Signed-off-by: yuchuan <[email protected]>
---
 rust/tvm-ffi/src/lib.rs             |   1 +
 rust/tvm-ffi/src/option.rs          | 350 ++++++++++++++++++++++++++++++++++++
 rust/tvm-ffi/src/string.rs          |  17 ++
 rust/tvm-ffi/tests/test_optional.rs | 220 +++++++++++++++++++++++
 tests/cpp/test_optional.cc          |  46 +++++
 5 files changed, 634 insertions(+)

diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index 891f734e..a05e3c06 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -27,6 +27,7 @@ pub mod function;
 pub mod function_internal;
 pub mod macros;
 pub mod object;
+pub mod option;
 pub mod string;
 pub mod type_traits;
 pub use tvm_ffi_sys;
diff --git a/rust/tvm-ffi/src/option.rs b/rust/tvm-ffi/src/option.rs
new file mode 100644
index 00000000..d40e3ce9
--- /dev/null
+++ b/rust/tvm-ffi/src/option.rs
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+//! In-place mirrors of C++ `ffi::Optional<T>` (`include/tvm/ffi/optional.h`).
+//!
+//! `ffi::Optional<T>` stores its value inline, in one of three ABI layouts
+//! depending on `T`. The types here decode such a field's bytes directly — no
+//! FFI call, no allocation, no reflection getter/setter. Pick the counterpart 
for
+//! the field's `T`:
+//!
+//! - POD scalar (`i32`, `f64`, `bool`, …) → [`OptionPod<T>`](OptionPod)
+//! - `String` → [`OptionStr`]
+//! - `ObjectRef` subtype → [`OptionObjRef<T>`](OptionObjRef), an alias of 
plain
+//!   `Option<T>` (a single nullable pointer, `nullptr` == `None`)
+//!
+//! # `OptionPod<T>` — POD scalars
+//! Mirrors the `std::optional<T>` fallback as `#[repr(C)] { value: T, engaged:
+//! bool }` (payload at offset 0, flag at `size_of::<T>()`), byte-verified 
against
+//! libstdc++/libc++. `T` must implement [`OptionalCompatiblePod`] — the 
marker trait
+//! carried by the fixed set of fixed-width scalars. Read with 
[`get`](OptionPod::get),
+//! write with [`set`](OptionPod::set).
+//!
+//! # `OptionStr` — `String`
+//! The C++ `String` specialization keeps the 16-byte string cell inline and 
marks
+//! `nullopt` with the `type_index == kTVMFFINone` sentinel; [`OptionStr`] 
wraps
+//! [`String`] the same way and reuses its refcounting `Clone`/`Drop`. Borrow 
with
+//! [`as_str`](OptionStr::as_str), write with [`set`](OptionStr::set).
+//! (`ffi::Optional<Bytes>` would follow the same pattern.)
+
+use crate::String;
+use std::fmt::{self, Debug};
+use std::mem::MaybeUninit;
+
+//-----------------------------------------------------
+// OptionPod<T> — POD scalars
+//-----------------------------------------------------
+
+/// Marker for a POD scalar `T` that can back an [`OptionPod<T>`]; see the
+/// [module docs](self).
+///
+/// Unsafe: an implementor guarantees `T` is trivially copyable and its Rust
+/// representation is byte-identical to the C++ field type (`i32` ↔ `int32_t`,
+/// `f64` ↔ `double`, …), so the mirror can overlay the C++ `std::optional<T>`.
+///
+/// Non-scalar payloads are rejected at compile time with a pointer to the
+/// right counterpart:
+///
+/// ```compile_fail,E0277
+/// // `Array` is an `ObjectRef` subtype → use `Option<Array<i64>>` 
(`OptionObjRef`).
+/// let _ = tvm_ffi::option::OptionPod::<tvm_ffi::Array<i64>>::none();
+/// ```
+#[diagnostic::on_unimplemented(
+    message = "`OptionPod<{Self}>` only mirrors `ffi::Optional` of fixed-width 
POD scalars",
+    label = "`{Self}` is not a fixed-width POD scalar",
+    note = "for an `ObjectRef` subtype use plain `Option<{Self}>` (alias 
`tvm_ffi::option::OptionObjRef`): the C++ side is a single nullable pointer",
+    note = "for `String` use `tvm_ffi::option::OptionStr`"
+)]
+pub unsafe trait OptionalCompatiblePod: Copy {}
+
+/// In-place mirror of C++ `ffi::Optional<T>` for POD `T`, laid out as
+/// `std::optional<T>`: `{ T value @0; bool engaged @sizeof(T) }`.
+///
+/// Layout-compatible with the C++ type; see the [module docs](self).
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct OptionPod<T: OptionalCompatiblePod> {
+    value: MaybeUninit<T>,
+    engaged: bool,
+}
+
+impl<T: OptionalCompatiblePod> OptionPod<T> {
+    /// Builds an engaged optional holding `value`.
+    #[inline]
+    pub fn some(value: T) -> Self {
+        // Only payload+flag are written; padding isn't part of the ABI.
+        Self {
+            value: MaybeUninit::new(value),
+            engaged: true,
+        }
+    }
+
+    /// Builds a disengaged optional (`nullopt`).
+    #[inline]
+    pub fn none() -> Self {
+        // Zeroed (not `uninit`) payload keeps the byte-image tests reading 
init bytes.
+        Self {
+            value: MaybeUninit::zeroed(),
+            engaged: false,
+        }
+    }
+
+    /// Decodes the value in place. No FFI call, no allocation.
+    #[inline]
+    pub fn get(&self) -> Option<T> {
+        if self.engaged {
+            // The payload is written whenever `engaged` is set.
+            Some(unsafe { self.value.assume_init() })
+        } else {
+            None
+        }
+    }
+
+    /// Returns whether a value is present.
+    #[inline]
+    pub fn has_value(&self) -> bool {
+        self.engaged
+    }
+
+    /// Returns whether the optional is `nullopt`.
+    #[inline]
+    pub fn is_none(&self) -> bool {
+        !self.has_value()
+    }
+
+    /// Overwrites the value in place.
+    ///
+    /// Mirrors C++ assignment: `Some(v)` engages and stores `v`; `None`
+    /// disengages without touching the payload bytes, as 
`std::optional::reset`
+    /// does for trivial `T`.
+    #[inline]
+    pub fn set(&mut self, value: Option<T>) {
+        match value {
+            Some(v) => {
+                self.value = MaybeUninit::new(v);
+                self.engaged = true;
+            }
+            None => self.engaged = false,
+        }
+    }
+}
+
+impl<T: OptionalCompatiblePod> Default for OptionPod<T> {
+    /// `nullopt`, matching the C++ default constructor.
+    #[inline]
+    fn default() -> Self {
+        Self::none()
+    }
+}
+
+impl<T: OptionalCompatiblePod + PartialEq> PartialEq for OptionPod<T> {
+    #[inline]
+    fn eq(&self, other: &Self) -> bool {
+        self.get() == other.get()
+    }
+}
+
+impl<T: OptionalCompatiblePod + Eq> Eq for OptionPod<T> {}
+
+impl<T: OptionalCompatiblePod> From<Option<T>> for OptionPod<T> {
+    #[inline]
+    fn from(value: Option<T>) -> Self {
+        match value {
+            Some(v) => Self::some(v),
+            None => Self::none(),
+        }
+    }
+}
+
+impl<T: OptionalCompatiblePod> From<OptionPod<T>> for Option<T> {
+    #[inline]
+    fn from(value: OptionPod<T>) -> Self {
+        value.get()
+    }
+}
+
+impl<T: OptionalCompatiblePod + Debug> Debug for OptionPod<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self.get() {
+            Some(v) => write!(f, "OptionPod::Some({v:?})"),
+            None => f.write_str("OptionPod::None"),
+        }
+    }
+}
+
+// Registers each supported scalar from one list: the `OptionalCompatiblePod` 
impl and a
+// compile-time guard that `OptionPod<T>` matches the `std::optional<T>` 
footprint
+// (`size == round_up(size_of::<T>()+1, align)`). One list keeps the impl and 
its
+// layout check from drifting.
+macro_rules! impl_optional_compatible_pod {
+    ($($t:ty),* $(,)?) => { $(
+        // Fixed-width scalar; repr matches the C++ field's `std::optional`
+        // fallback (layout proven by the `const` block below).
+        unsafe impl OptionalCompatiblePod for $t {}
+        const _: () = {
+            let tsz = core::mem::size_of::<$t>();
+            let tal = core::mem::align_of::<$t>();
+            let expect = (tsz + 1).div_ceil(tal) * tal;
+            assert!(core::mem::align_of::<OptionPod<$t>>() == tal);
+            assert!(core::mem::size_of::<OptionPod<$t>>() == expect);
+        };
+    )* };
+}
+// Keep in sync with the static_assert guard at the end of 
include/tvm/ffi/optional.h.
+impl_optional_compatible_pod!(bool, i8, i16, i32, i64, u8, u16, u32, u64, f32, 
f64);
+
+//-----------------------------------------------------
+// OptionStr — String
+//-----------------------------------------------------
+
+/// In-place mirror of C++ `ffi::Optional<String>`: the 16-byte string cell
+/// itself, with `type_index == kTVMFFINone` meaning `nullopt` (the C++
+/// String/Bytes spec stores the sentinel in-cell, not as a separate flag).
+/// Reuses [`String`]'s `Clone`/`Drop`, whose refcounting is a no-op on the
+/// `nullopt` cell (`type_index` below `kTVMFFIStaticObjectBegin`).
+#[repr(transparent)]
+#[derive(Clone)]
+pub struct OptionStr {
+    // Never handed out or accessed while disengaged (a `nullopt` cell is not a
+    // valid string).
+    inner: String,
+}
+
+// Must stay 16 bytes to overlay C++ `ffi::Optional<String>` (parity with the 
POD
+// guard in `impl_optional_compatible_pod!`).
+const _: () = assert!(
+    std::mem::size_of::<OptionStr>() == 16
+        && std::mem::align_of::<OptionStr>() == 
std::mem::align_of::<crate::TVMFFIAny>()
+);
+
+impl OptionStr {
+    /// An engaged optional holding `value`.
+    #[inline]
+    pub fn some(value: String) -> Self {
+        Self { inner: value }
+    }
+
+    /// A disengaged optional (`nullopt`).
+    #[inline]
+    pub fn none() -> Self {
+        Self {
+            inner: String::none_cell(),
+        }
+    }
+
+    /// Whether a value is present.
+    #[inline]
+    pub fn has_value(&self) -> bool {
+        !self.inner.is_none_cell()
+    }
+
+    /// Whether the optional is `nullopt`.
+    #[inline]
+    pub fn is_none(&self) -> bool {
+        self.inner.is_none_cell()
+    }
+
+    /// Borrows the engaged string as `&str`, or `None` when `nullopt`.
+    #[inline]
+    pub fn as_str(&self) -> Option<&str> {
+        if self.has_value() {
+            Some(self.inner.as_str())
+        } else {
+            None
+        }
+    }
+
+    /// Takes the value out, consuming self.
+    #[inline]
+    pub fn get(self) -> Option<String> {
+        let OptionStr { inner } = self; // no `Drop` impl, so the move is 
allowed
+        if inner.is_none_cell() {
+            None
+        } else {
+            Some(inner)
+        }
+    }
+
+    /// Overwrites the value in place, dropping the previous one first 
(dec-ref'd
+    /// if it was a heap string).
+    #[inline]
+    pub fn set(&mut self, value: Option<String>) {
+        // Assignment drops the old `String` (dec_ref if heap) before moving 
in the new.
+        self.inner = match value {
+            Some(s) => s,
+            None => String::none_cell(),
+        };
+    }
+}
+
+impl Default for OptionStr {
+    /// `nullopt`, matching the C++ default constructor.
+    #[inline]
+    fn default() -> Self {
+        Self::none()
+    }
+}
+
+impl From<Option<String>> for OptionStr {
+    #[inline]
+    fn from(value: Option<String>) -> Self {
+        match value {
+            Some(s) => Self::some(s),
+            None => Self::none(),
+        }
+    }
+}
+
+impl From<OptionStr> for Option<String> {
+    #[inline]
+    fn from(value: OptionStr) -> Self {
+        value.get()
+    }
+}
+
+impl PartialEq for OptionStr {
+    // NOT derivable: derived eq would run `String::eq` on a `nullopt` cell and
+    // dereference its null object pointer; `as_str` checks the sentinel first.
+    #[inline]
+    fn eq(&self, other: &Self) -> bool {
+        self.as_str() == other.as_str()
+    }
+}
+
+impl Eq for OptionStr {}
+
+impl Debug for OptionStr {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self.as_str() {
+            Some(s) => write!(f, "OptionStr::Some({s:?})"),
+            None => f.write_str("OptionStr::None"),
+        }
+    }
+}
+
+//-----------------------------------------------------
+// OptionObjRef — ObjectRef subtypes
+//-----------------------------------------------------
+
+/// Alias of [`Option`] for `ObjectRef`-subtype fields.
+///
+/// C++ `ffi::Optional<SomeRef>` is a single nullable pointer, so 
`Option<SomeRef>`
+/// (niche-optimized over the ref's non-null [`ObjectArc`](crate::ObjectArc)
+/// pointer) already mirrors it in place: `None` == `nullptr`. The alias only
+/// names that contract for consistency.
+pub type OptionObjRef<T> = Option<T>;
diff --git a/rust/tvm-ffi/src/string.rs b/rust/tvm-ffi/src/string.rs
index 94739a4c..70768d6f 100644
--- a/rust/tvm-ffi/src/string.rs
+++ b/rust/tvm-ffi/src/string.rs
@@ -284,6 +284,23 @@ impl String {
     pub fn as_str(&self) -> &str {
         unsafe { std::str::from_utf8_unchecked(self.as_bytes()) }
     }
+
+    /// Whether the cell is the `nullopt` sentinel (`type_index == 
kTVMFFINone`).
+    /// Such a cell is NOT a valid string; only 
[`OptionStr`](crate::option::OptionStr)
+    /// holds one, and never calls string accessors on it.
+    #[inline]
+    pub(crate) fn is_none_cell(&self) -> bool {
+        self.data.type_index == TypeIndex::kTVMFFINone as i32
+    }
+
+    /// A `nullopt` sentinel cell for [`OptionStr`](crate::option::OptionStr).
+    /// `TVMFFIAny::new()` is exactly the all-zero `kTVMFFINone` cell.
+    #[inline]
+    pub(crate) fn none_cell() -> Self {
+        Self {
+            data: TVMFFIAny::new(),
+        }
+    }
 }
 
 impl<T> From<T> for String
diff --git a/rust/tvm-ffi/tests/test_optional.rs 
b/rust/tvm-ffi/tests/test_optional.rs
new file mode 100644
index 00000000..18eadec0
--- /dev/null
+++ b/rust/tvm-ffi/tests/test_optional.rs
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+use tvm_ffi::option::{OptionObjRef, OptionPod, OptionStr, 
OptionalCompatiblePod};
+use tvm_ffi::*;
+
+/// Payload+flag bytes `[0, size_of::<T>()]`; padding is excluded (not ABI, not
+/// guaranteed initialized).
+fn image<T: OptionalCompatiblePod>(opt: &OptionPod<T>) -> Vec<u8> {
+    let p = opt as *const OptionPod<T> as *const u8;
+    let n = std::mem::size_of::<T>() + 1; // payload + flag, no padding
+    // Payload and flag are always initialized; padding is not read.
+    unsafe { std::slice::from_raw_parts(p, n).to_vec() }
+}
+
+/// The scalar's own bytes, to assert the optional's payload matches it 
verbatim.
+fn raw_bytes<T: OptionalCompatiblePod>(v: &T) -> Vec<u8> {
+    let p = v as *const T as *const u8;
+    // size_of::<T>() bytes of an initialized `Copy` scalar.
+    unsafe { std::slice::from_raw_parts(p, std::mem::size_of::<T>()).to_vec() }
+}
+
+#[test]
+#[cfg(target_endian = "little")]
+fn byte_image_some_i32_matches_cpp() {
+    // C++ probe (both STLs): some(0x12345678) => 78 56 34 12 | 01 ..
+    let o = OptionPod::<i32>::some(0x1234_5678);
+    let b = image(&o);
+    assert_eq!(&b[0..4], &0x1234_5678_i32.to_le_bytes());
+    assert_eq!(b[4], 1, "engaged flag must sit at offset size_of::<i32>()");
+}
+
+#[test]
+#[cfg(target_endian = "little")]
+fn byte_image_some_i64_matches_cpp() {
+    let o = OptionPod::<i64>::some(0x1122_3344_5566_7788);
+    let b = image(&o);
+    assert_eq!(&b[0..8], &0x1122_3344_5566_7788_i64.to_le_bytes());
+    assert_eq!(b[8], 1, "engaged flag must sit at offset size_of::<i64>()");
+}
+
+/// Payload@0 and flag@`size_of::<T>()` for every supported type, not just 
i32/i64.
+#[test]
+fn flag_offset_all_supported_types() {
+    fn check<T: OptionalCompatiblePod + PartialEq + std::fmt::Debug>(val: T) {
+        let ty = std::any::type_name::<T>();
+        let sz = std::mem::size_of::<T>();
+        let some = OptionPod::<T>::some(val);
+        let b = image(&some);
+        assert_eq!(&b[0..sz], &raw_bytes(&val)[..], "payload@0 for {ty}");
+        assert_eq!(b[sz], 1, "engaged flag must sit at offset size_of for 
{ty}");
+        assert_eq!(some.get(), Some(val), "engaged roundtrip for {ty}");
+        let none = OptionPod::<T>::none();
+        assert_eq!(image(&none)[sz], 0, "flag must be clear for none for 
{ty}");
+        assert!(none.get().is_none(), "disengaged roundtrip for {ty}");
+    }
+    check::<bool>(true);
+    check::<i8>(0x12);
+    check::<i16>(0x1234);
+    check::<i32>(0x1234_5678);
+    check::<i64>(0x1122_3344_5566_7788);
+    check::<u8>(0xAB);
+    check::<u16>(0xABCD);
+    check::<u32>(0xABCD_EF01);
+    check::<u64>(0xABCD_EF01_2345_6789);
+    check::<f32>(1.5);
+    check::<f64>(2.5);
+}
+
+#[test]
+fn roundtrip_get_set() {
+    let mut o = OptionPod::<i32>::some(42);
+    assert_eq!(o.get(), Some(42));
+    assert!(o.has_value());
+
+    o.set(None);
+    assert_eq!(o.get(), None);
+    assert!(o.is_none());
+
+    o.set(Some(-7));
+    assert_eq!(o.get(), Some(-7));
+}
+
+#[test]
+fn conversions_and_default() {
+    assert_eq!(OptionPod::<f64>::from(Some(2.5)).get(), Some(2.5));
+    assert_eq!(OptionPod::<f64>::from(None).get(), None);
+    let back: Option<i16> = OptionPod::<i16>::some(9).into();
+    assert_eq!(back, Some(9));
+    assert_eq!(OptionPod::<u8>::default().get(), None);
+    assert_eq!(OptionPod::<bool>::some(true).get(), Some(true));
+}
+
+#[test]
+#[allow(clippy::clone_on_copy)] // explicitly exercising the Clone impl
+fn clone_preserves_state() {
+    let some = OptionPod::<i64>::some(123);
+    assert_eq!(some.clone().get(), Some(123));
+    let none = OptionPod::<i64>::none();
+    assert_eq!(none.clone().get(), None);
+}
+
+#[test]
+fn equality() {
+    assert_eq!(OptionPod::<i32>::some(1), OptionPod::<i32>::some(1));
+    assert_ne!(OptionPod::<i32>::some(1), OptionPod::<i32>::some(2));
+    assert_ne!(OptionPod::<i32>::some(1), OptionPod::<i32>::none());
+    assert_eq!(OptionPod::<i32>::none(), OptionPod::<i32>::none());
+    // `set(None)` leaves stale payload bytes; equality must still see plain 
None.
+    let mut stale = OptionPod::<i32>::some(7);
+    stale.set(None);
+    assert_eq!(stale, OptionPod::<i32>::none());
+
+    let a = || OptionStr::some(String::from("a"));
+    assert_eq!(a(), a());
+    assert_ne!(a(), OptionStr::some(String::from("b")));
+    assert_ne!(a(), OptionStr::none());
+    assert_eq!(OptionStr::none(), OptionStr::none());
+}
+
+// 16-byte cell (type_index@0, small_str_len@4, union@8); no padding.
+fn str_image(o: &OptionStr) -> [u8; 16] {
+    let p = o as *const OptionStr as *const u8;
+    let mut b = [0u8; 16];
+    // OptionStr is a fully-initialized 16-byte TVMFFIAny cell.
+    unsafe { std::ptr::copy_nonoverlapping(p, b.as_mut_ptr(), 16) };
+    b
+}
+
+#[test]
+#[cfg(target_endian = "little")]
+fn optional_str_byte_image_matches_cpp() {
+    // C++ probe: ffi::Optional<String> none => 16 zero bytes;
+    //            some("hi")           => 0b 00 00 00 | 02 00 00 00 | 68 69 00 
...
+    assert_eq!(str_image(&OptionStr::none()), [0u8; 16]);
+    let some = OptionStr::some(String::from("hi"));
+    let b = str_image(&some);
+    assert_eq!(&b[0..4], &[0x0b, 0, 0, 0], "type_index = kTVMFFISmallStr");
+    assert_eq!(&b[4..8], &[0x02, 0, 0, 0], "small_str_len = 2");
+    assert_eq!(&b[8..10], b"hi", "inline payload");
+}
+
+#[test]
+fn optional_str_roundtrip_and_conversions() {
+    // small (inline) string
+    let s = OptionStr::some(String::from("hi"));
+    assert!(s.has_value());
+    assert_eq!(s.as_str(), Some("hi"));
+    assert_eq!(s.get().as_deref(), Some("hi"));
+
+    // nullopt
+    let n = OptionStr::none();
+    assert!(n.is_none());
+    assert_eq!(n.as_str(), None);
+    assert_eq!(n.get(), None);
+
+    // conversions + default
+    let from_some: OptionStr = Some(String::from("x")).into();
+    assert_eq!(from_some.as_str(), Some("x"));
+    let back: Option<String> = OptionStr::none().into();
+    assert!(back.is_none());
+    assert!(OptionStr::default().is_none());
+}
+
+#[test]
+fn optional_str_heap_clone_no_double_free() {
+    // long (heap) string exercises refcounted Clone/Drop through the wrapper.
+    let long = String::from("a-very-long-heap-allocated-string-value");
+    let a = OptionStr::some(long);
+    let b = a.clone();
+    assert_eq!(a.as_str(), Some("a-very-long-heap-allocated-string-value"));
+    assert_eq!(b.as_str(), Some("a-very-long-heap-allocated-string-value"));
+    // both drop here: two dec_refs balancing the clone's inc_ref, no leak/UAF.
+}
+
+#[test]
+fn optional_str_set_in_place() {
+    // Start engaged with a heap string, then replace it: `set` must drop
+    // (dec_ref) the old heap string before moving the new one in.
+    let mut o = 
OptionStr::some(String::from("first-long-heap-allocated-value"));
+    assert_eq!(o.as_str(), Some("first-long-heap-allocated-value"));
+
+    o.set(Some(String::from("second-long-heap-allocated-value")));
+    assert_eq!(o.as_str(), Some("second-long-heap-allocated-value"));
+
+    // disengage (drops the heap string), then re-engage
+    o.set(None);
+    assert!(o.is_none());
+    assert_eq!(o.as_str(), None);
+
+    o.set(Some(String::from("x")));
+    assert_eq!(o.as_str(), Some("x"));
+}
+
+#[test]
+fn option_obj_ref_is_nullable_pointer() {
+    // `OptionObjRef<SomeRef>` == `Option<SomeRef>`: the niche over the ref's
+    // non-null object pointer makes it a single nullable pointer, `None` == 
`nullptr`.
+    assert_eq!(
+        std::mem::size_of::<OptionObjRef<Array<i64>>>(),
+        std::mem::size_of::<*mut ()>()
+    );
+    let none: OptionObjRef<Array<i64>> = None;
+    assert!(none.is_none());
+}
diff --git a/tests/cpp/test_optional.cc b/tests/cpp/test_optional.cc
index a9a66629..f9dedd1d 100644
--- a/tests/cpp/test_optional.cc
+++ b/tests/cpp/test_optional.cc
@@ -22,6 +22,9 @@
 #include <tvm/ffi/memory.h>
 #include <tvm/ffi/optional.h>
 
+#include <cstdint>
+#include <cstring>
+
 #include "./testing_object.h"
 
 namespace {
@@ -202,4 +205,47 @@ TEST(Optional, Bytes) {
   EXPECT_TRUE(opt_bytes != std::nullopt);
   static_assert(sizeof(Optional<Bytes>) == sizeof(Bytes));
 }
+
+// The Rust binding (rust/tvm-ffi/src/option.rs) mirrors Optional<T> in place 
as
+// `{ T value; bool engaged; }`; fail early if a toolchain's std::optional 
deviates from that.
+template <typename... T>
+constexpr bool all_optional_layouts_match_rust_mirror_v =
+    ((sizeof(Optional<T>) == sizeof(T) + alignof(T) && alignof(Optional<T>) == 
alignof(T)) && ...);
+static_assert(
+    all_optional_layouts_match_rust_mirror_v<bool, int8_t, int16_t, int32_t, 
int64_t, uint8_t,
+                                             uint16_t, uint32_t, uint64_t, 
float, double>);
+// Same contract for the String/Bytes specialization (Rust OptionStr overlays 
the cell).
+static_assert(sizeof(Optional<String>) == sizeof(TVMFFIAny) &&
+              sizeof(Optional<Bytes>) == sizeof(TVMFFIAny));
+
+// The overlay also assumes the field order `{ T value @0; bool engaged 
@sizeof(T) }`;
+// pin that here (the static_asserts above pin only size/align).
+template <typename T>
+void CheckRustMirrorFieldOrder(T v) {
+  Optional<T> opt(v);
+  const char* base = reinterpret_cast<const char*>(&opt);
+  T payload;
+  std::memcpy(&payload, base, sizeof(T));
+  EXPECT_EQ(payload, v) << "payload must sit at offset 0";
+  bool engaged = false;
+  std::memcpy(&engaged, base + sizeof(T), sizeof(bool));
+  EXPECT_TRUE(engaged) << "engaged flag must sit at offset sizeof(T)";
+  opt = std::nullopt;
+  std::memcpy(&engaged, base + sizeof(T), sizeof(bool));
+  EXPECT_FALSE(engaged) << "disengaging must clear the flag at offset 
sizeof(T)";
+}
+
+TEST(Optional, RustMirrorFieldOrder) {
+  CheckRustMirrorFieldOrder<bool>(true);
+  CheckRustMirrorFieldOrder<int8_t>(0x12);
+  CheckRustMirrorFieldOrder<int16_t>(0x1234);
+  CheckRustMirrorFieldOrder<int32_t>(0x12345678);
+  CheckRustMirrorFieldOrder<int64_t>(0x1122334455667788LL);
+  CheckRustMirrorFieldOrder<uint8_t>(0xAB);
+  CheckRustMirrorFieldOrder<uint16_t>(0xABCD);
+  CheckRustMirrorFieldOrder<uint32_t>(0xABCDEF01u);
+  CheckRustMirrorFieldOrder<uint64_t>(0xABCDEF0123456789ULL);
+  CheckRustMirrorFieldOrder<float>(1.5f);
+  CheckRustMirrorFieldOrder<double>(2.5);
+}
 }  // namespace

Reply via email to