Skip to content

[FEAT][RUST] Support runtime-checked try_cast between ObjectRef types#684

Open
Seven-Streams wants to merge 6 commits into
apache:mainfrom
Seven-Streams:feat/2026-07-23/ancestor
Open

[FEAT][RUST] Support runtime-checked try_cast between ObjectRef types#684
Seven-Streams wants to merge 6 commits into
apache:mainfrom
Seven-Streams:feat/2026-07-23/ancestor

Conversation

@Seven-Streams

@Seven-Streams Seven-Streams commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

This PR adds direct, runtime-checked casting between Rust object handles, mirroring C++ ObjectRef::as<T> / Downcast<T>.

ObjectRefCast is blanket-implemented for types implementing both ObjectRefCore and AnyCompatible. try_cast::<B>(self) consumes the source and, on success, rewraps the same object as B without cloning or copying it. A failed cast returns a TypeError describing the source and target types.

Target compatibility is delegated to B::check_any_strict. ObjectRef hierarchies accept B and its runtime subtypes, while parameterized containers use their complete type semantics.

let base: TestBase = derived.try_cast()?; // upcast
let obj: ObjectRef = base.try_cast()?;    // upcast to root
let d: TestDerived = obj.try_cast()?;     // checked downcast

The direct A -> B path reuses the existing AnyCompatible conversion traits through a raw TVMFFIAny, so callers do not need to construct an Any or AnyView. It first creates a non-owning view for the target check and transfers ownership only after that check succeeds, keeping failure and panic paths leak-free.

Derived ObjectRef conversions preserve the runtime type index stored in the object header. A derived object upcast to a base handle can therefore still round-trip through Any or AnyView and downcast correctly.

A hidden is_instance_of helper provides hierarchy checks for derive-generated object containers. The cast path uses assert_unchecked after establishing the object type-index invariant; release codegen confirms that the intermediate TVMFFIAny storage and redundant object-range check are eliminated.

The PR also includes the derive fixes required by downstream crates and focused tests covering hierarchy casts, failure ownership, dynamic-type preservation through Any/AnyView, direct try_cast_from_any_view, and parameterized-container mismatch.

Signed-off-by: yuchuan <yuchuan.7streams@gmail.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@Seven-Streams
Seven-Streams marked this pull request as ready for review July 23, 2026 18:39
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@Seven-Streams
Seven-Streams marked this pull request as draft July 23, 2026 18:39
@tlopex tlopex changed the title [FEAT][RUST] support try_cast between any object-based structs. [FEAT][RUST] Support runtime-checked try_cast between ObjectRef types Jul 24, 2026
data.data_union.v_obj =
data_ptr as *mut ContainerType as *mut #tvm_ffi_crate::tvm_ffi_sys::TVMFFIObject;
let object_ptr =
data_ptr as *mut ContainerType as *mut #tvm_ffi_crate::tvm_ffi_sys::TVMFFIObject;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the order is changed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need the object pointer first because the type index is now read from the runtime object header instead of ContainerType::type_index(), which preserves the dynamic subtype after an upcast.

Comment thread rust/tvm-ffi/src/object.rs Outdated
Comment on lines +142 to +143
// Every registered object is an instance of the root Object type.
if target_type_index == object_begin {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if it's necessary

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is. this is the root object fast path. Every object-range type must cast to objectref, and the C++ uses the same range check while avoiding type-table lookups.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given this is runtime check, likely not having the specialization is ok, depending on how freq conversion to ObjectRef is.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed

Comment thread rust/tvm-ffi/src/object.rs Outdated
Comment on lines +142 to +143
// Every registered object is an instance of the root Object type.
if target_type_index == object_begin {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given this is runtime check, likely not having the specialization is ok, depending on how freq conversion to ObjectRef is.

Comment thread rust/tvm-ffi/src/object.rs Outdated
if object_info.is_null() || target_info.is_null() {
return false;
}
let target_depth = (*target_info).type_depth;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might worthcheck other template trick for target_type_index. Note in the case of C++ TargetType can be a type T, where things like type_depth can be directly queried as a compile time constant rather than runtime

@tlopex
tlopex marked this pull request as ready for review July 24, 2026 05:05
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@tlopex
tlopex requested a review from tqchen July 24, 2026 06:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants