2020Stub Generation
2121===============
2222
23- TVM-FFI provides ``tvm-ffi-stubgen ``, a tool that generates Python type stubs from C++
24- reflection metadata. It turns registered global functions and classes into proper Python
25- type hints, enabling IDE auto-completion and static type checking.
23+ TVM-FFI provides ``tvm-ffi-stubgen ``, a tool that generates typed language bindings from C++
24+ reflection metadata. It turns registered global functions and classes into native type hints,
25+ enabling IDE auto-completion and static type checking.
26+
27+ The sections below describe the default Python target. The Rust target is covered in
28+ :ref: `sec-stubgen-rust `.
2629
2730.. admonition :: Prerequisite
2831 :class: hint
@@ -46,6 +49,7 @@ This runs stub generation automatically after each build.
4649 [STUB_INIT ON|OFF]
4750 [STUB_PKG <pkg>]
4851 [STUB_PREFIX <prefix>]
52+ [STUB_TARGET python|rust]
4953 )
5054
5155 From the example's
@@ -239,6 +243,9 @@ All three are required together. When omitted, the tool operates in directive-on
239243
240244**Optional arguments: **
241245
246+ ``--target ``
247+ Code generator backend: ``python `` (default) or ``rust ``. See :ref: `sec-stubgen-rust `.
248+
242249``--verbose ``
243250 Print a unified diff of changes to each file.
244251
@@ -253,6 +260,120 @@ All three are required together. When omitted, the tool operates in directive-on
253260
254261For a complete list of options, run ``tvm-ffi-stubgen --help ``.
255262
263+ .. _sec-stubgen-rust :
264+
265+ Rust Code Stubgen (Experimental)
266+ --------------------------------
267+
268+ TVM-FFI provides an efficient, easy-to-use mechanism for exposing C++ classes to Rust.
269+ Objects share the same memory representation, so Rust code can directly access objects
270+ created in C++. However, users have to hand-write the Rust definition of each registered
271+ object to avoid memory layout and alignment mismatches between the two sides. To eliminate
272+ this manual work, ``tvm-ffi-stubgen `` supports generating Rust code directly.
273+
274+ To generate Rust stubs, pass ``STUB_TARGET rust `` to ``tvm_ffi_configure_target ``
275+ (see :ref: `sec-stubgen-cmake `) or ``--target rust `` on the command line
276+ (see :ref: `sec-stubgen-cli `).
277+
278+ Key Features
279+ ~~~~~~~~~~~~
280+
281+ - Generate Rust code for registered C++ classes automatically (via CLI or CMake).
282+ - Mirror the C++ memory layout exactly in Rust.
283+ - Provide Rust-native builder-style constructors for registered classes, with
284+ reflected default values prefilled and overridable through setters.
285+ - Expose methods of registered classes through cross-language calls.
286+
287+ Generation Output
288+ ~~~~~~~~~~~~~~~~~
289+
290+ For the type ``IntPair `` (fields ``a `` / ``b ``, plus ``scale `` registered with
291+ ``refl::default_value(1) ``), the generated Rust code looks like this:
292+
293+ .. code-block :: rust
294+
295+ #[repr(C)]
296+ #[derive(tvm_ffi::derive::Object)]
297+ #[type_key = "my_ffi_extension.IntPair"]
298+ pub struct IntPairObj {
299+ base: Object, // the parent type (or `tvm_ffi::Object` if no parent), embedded as the first field
300+ pub a: i64, // fields are public: read (and write, if mutable) via `Deref`/`DerefMut`
301+ pub b: i64,
302+ pub scale: i64,
303+ }
304+
305+ #[repr(C)]
306+ #[derive(tvm_ffi::derive::ObjectRef, Clone)]
307+ pub struct IntPair {
308+ data: ObjectArc<IntPairObj>,
309+ }
310+
311+ impl IntPair {
312+ /// Native (FFI-free) construction: opens the builder. `scale` starts
313+ /// prefilled with its reflected default (`1`); `a` / `b` start unset.
314+ pub fn ffi_new() -> IntPairBuilder { /* ... */ }
315+ }
316+
317+ pub struct IntPairBuilder { /* base + every own field */ }
318+
319+ impl IntPairBuilder {
320+ /// One consuming setter per field -- one uniform API.
321+ pub fn a(mut self, a: i64) -> Self { /* ... */ }
322+ pub fn b(mut self, b: i64) -> Self { /* ... */ }
323+ pub fn scale(mut self, scale: i64) -> Self { /* ... */ }
324+ /// Allocates the object (`ObjectArc::new`); errors if a field without
325+ /// a default is still unset.
326+ pub fn build(self) -> Result<IntPair> { /* ... */ }
327+ /// The bare struct value -- what a derived type's `base(...)` setter takes.
328+ pub fn build_obj(self) -> Result<IntPairObj> { /* ... */ }
329+ }
330+
331+ The object has the same memory layout as its C++ counterpart. Users can create new objects
332+ on the Rust side and call the methods defined in C++:
333+
334+ .. code-block :: rust
335+
336+ let pair = IntPair::ffi_new().a(1).b(2).build()?; // scale = 1 (default)
337+ let scaled = IntPair::ffi_new().a(1).b(2).scale(10).build()?; // override via setter
338+ let err = IntPair::ffi_new().a(1).build(); // Err: field `b` is not set
339+
340+ Construction is fully Rust-native and the API is uniform: ``ffi_new() `` is
341+ nullary for root and derived types alike, and every input -- own fields and a
342+ derived type's ``base `` -- is set through its like-named consuming setter.
343+ Fields with a ``refl::default_value `` are rendered as Rust literals at
344+ stub-generation time and prefilled in the builder; fields without a default
345+ start unset, and ``build() `` returns an error if any is still missing. The
346+ builder deliberately bypasses any C++ constructor logic; users who need the
347+ faithful C++ semantics can hand-write a ``new `` constructor (outside the
348+ generated markers) on top of the builder.
349+
350+ A derived type's builder feeds its embedded parent through ``base(...) ``,
351+ which takes the bare parent value produced by the parent builder's
352+ ``build_obj() `` (emitted on every builder):
353+
354+ .. code-block :: rust
355+
356+ let p3 = Point3D::ffi_new()
357+ .base(Point::ffi_new().x(1).y(2).build_obj()?)
358+ .z(3)
359+ .build()?;
360+
361+ When ``base `` is left unset, ``build() `` default-constructs the parent through
362+ its all-default builder -- silently when every parent field has a default, and
363+ with a build-time error naming ``base `` when one does not.
364+
365+ A type that mentions an origin the Rust crate cannot represent -- in any
366+ position: field, method argument, return type, or nested inside another
367+ container -- is explicitly unsupported: the whole binding is skipped with a
368+ warning. This covers ``Map `` / ``Dict `` / ``List `` / ``Union `` (no Rust
369+ counterpart) as well as ``Optional `` / ``tuple `` (std ``Option<T> `` and Rust
370+ tuples do not match the C++ ``ffi::Optional `` / ``ffi::Tuple `` memory layout).
371+ A constructor alone cannot be generated when a default comes from a
372+ ``refl::default_factory `` or when a default value has no Rust literal
373+ rendering: such types are emitted without ``ffi_new `` (a warning explains why),
374+ and construction stays on the C++ side -- e.g. through a ``def_static ``
375+ factory.
376+
256377.. _sec-stubgen-advanced :
257378
258379Advanced Topics
@@ -391,7 +512,8 @@ When you run the tool, it:
391512 # tvm-ffi-stubgen(import-object): ffi.Object;False;_ffi_Object
392513
393514 This imports ``ffi.Object `` as ``_ffi_Object `` for use in generated code. The second
394- field (``False ``) indicates the import is not TYPE_CHECKING-only.
515+ field (``False ``) indicates the import is not TYPE_CHECKING-only. The Rust target
516+ records the name as a plain ``use `` and ignores the other two fields.
395517
396518``skip-file `` - Skip File
397519 Prevents the tool from modifying the file. Place anywhere in the file.
0 commit comments