@@ -66,22 +66,57 @@ def _rust_string_literal(s: str) -> str:
6666 return "" .join (out )
6767
6868
69- def _default_expr ( field : NamedTypeSchema ) -> str | None :
70- """Render ``field``'s registered default as a Rust expression (``None``: can't).
69+ def _scalar_literal ( value : object ) -> str | None :
70+ """Render a ``bool``/``int``/finite ``float`` value as a Rust literal (``None``: can't).
7171
72- Only values whose Rust spelling is self-evident are supported: ``bool`` /
73- ``int`` / finite ``float`` literals (which coerce to the field's possibly
74- narrowed scalar type in the struct-literal position) and ``str`` (which
75- becomes a ``tvm_ffi::String``). Anything else -- objects, containers,
76- non-finite floats, factories -- has no native materialization.
72+ The literal coerces to the field's possibly narrowed scalar type in the
73+ struct-literal position.
7774 """
78- value = field .default
7975 if isinstance (value , bool ):
8076 return "true" if value else "false"
8177 if isinstance (value , int ):
8278 return repr (value )
8379 if isinstance (value , float ):
8480 return repr (value ) if math .isfinite (value ) else None
81+ return None
82+
83+
84+ def _optional_default_expr (field : NamedTypeSchema ) -> str | None :
85+ """Render an ``Optional`` field's ``nullopt`` default as the mirror's disengaged state.
86+
87+ Only the ``None`` default is supported: an engaged default's type-erased
88+ value can disagree with the payload's kind or width, so it is treated as
89+ unrenderable (``ffi_new`` is then skipped loudly).
90+ """
91+ if field .default is not None :
92+ return None
93+ (payload ,) = field .args or (None ,) # Optional always has exactly one argument
94+ assert payload is not None
95+ origin = payload .origin
96+ if origin in C_RUST .RUST_OPTION_POD_ORIGINS :
97+ return f"{ C_RUST .RUST_OPTION_POD_PATH } ::none()"
98+ if origin in C_RUST .RUST_OPTION_STR_ORIGINS :
99+ return f"{ C_RUST .RUST_OPTION_STR_PATH } ::none()"
100+ is_ptr_based = (
101+ "." in origin or origin in C_RUST .RUST_OPTION_PTR_ORIGINS
102+ ) and origin not in C_RUST .RUST_OPTION_BYTES_ORIGINS
103+ # Ptr-based fields are plain `Option<T>`, so disengaged is the literal `None`.
104+ return "None" if is_ptr_based else None
105+
106+
107+ def _default_expr (field : NamedTypeSchema ) -> str | None :
108+ """Render ``field``'s registered default as a Rust expression (``None``: can't).
109+
110+ Supported: ``bool``/``int``/finite ``float`` literals, ``str`` (as
111+ ``tvm_ffi::String``), and the ``nullopt`` default of ``Optional`` fields.
112+ Anything else has no native materialization.
113+ """
114+ if field .origin == "Optional" :
115+ return _optional_default_expr (field )
116+ value = field .default
117+ literal = _scalar_literal (value )
118+ if literal is not None :
119+ return literal
85120 if isinstance (value , str ):
86121 return f"tvm_ffi::String::from({ _rust_string_literal (value )} )"
87122 return None
@@ -209,11 +244,83 @@ def render_struct_field(self, schema: NamedTypeSchema) -> str:
209244
210245 An ``int32_t`` field must render as ``i32``, not the schema-erased
211246 default ``i64``; the width comes from reflection's per-field ``size``.
212- Non-scalar origins (or schemas without a size) render plainly.
247+ ``Optional`` fields are layout-sensitive and route to their in-place
248+ mirror. Non-scalar origins (or schemas without a size) render plainly.
213249 """
250+ if schema .origin == "Optional" :
251+ return self ._render_optional_field (schema )
214252 narrowed = C_RUST .RUST_SCALAR_BY_SIZE .get ((schema .origin , schema .size ))
215253 return narrowed if narrowed is not None else render_rust_type (schema , self ._ty_render )
216254
255+ def _render_optional_field (self , schema : NamedTypeSchema ) -> str :
256+ """Route an ``Optional<T>`` FIELD to the mirror of its C++ in-place layout.
257+
258+ POD scalar -> ``OptionPod<T>``, ``String`` -> ``OptionStr``,
259+ ``ObjectRef`` subtype -> ``Option<T>`` (see ``RUST_OPTION_*_ORIGINS``);
260+ anything else raises and skips the object.
261+ """
262+ (payload ,) = schema .args or (None ,) # Optional always has exactly one argument
263+ assert payload is not None
264+ origin = payload .origin
265+ if origin in C_RUST .RUST_OPTION_POD_ORIGINS :
266+ payload_ty = None
267+ if origin == "bool" :
268+ payload_ty = "bool" # always one byte; no width recovery needed
269+ elif schema .size is None :
270+ # No layout metadata (synthetic schemas): schema-erased default width.
271+ payload_ty = render_rust_type (payload , self ._ty_render )
272+ else :
273+ # sizeof(ffi::Optional<T>) == 2 * sizeof(T) for every supported scalar.
274+ half , rem = divmod (schema .size , 2 )
275+ if rem == 0 :
276+ payload_ty = C_RUST .RUST_SCALAR_BY_SIZE .get ((origin , half ))
277+ if payload_ty is None :
278+ raise UnsupportedTypeError (
279+ "Optional" ,
280+ f"cannot recover the `Optional<{ origin } >` payload width from "
281+ f"field size { schema .size } " ,
282+ )
283+ pod = self .imports .record (C_RUST .RUST_OPTION_POD_PATH )
284+ return f"{ pod } <{ payload_ty } >"
285+ if origin in C_RUST .RUST_OPTION_STR_ORIGINS :
286+ if schema .size not in (None , 16 ):
287+ # Origin folding also maps `std::string` to "str"; only the
288+ # 16-byte in-cell `ffi::Optional<String>` has the OptionStr mirror.
289+ raise UnsupportedTypeError (
290+ "Optional" ,
291+ f"`Optional<{ origin } >` field has size { schema .size } , not the "
292+ "16-byte in-cell `ffi::Optional<String>` layout" ,
293+ )
294+ return self .imports .record (C_RUST .RUST_OPTION_STR_PATH )
295+ if origin in C_RUST .RUST_OPTION_BYTES_ORIGINS :
296+ raise UnsupportedTypeError (
297+ "Optional" ,
298+ "`Optional<bytes>` fields have no crate mirror yet (OptionalBytes "
299+ "is not implemented)" ,
300+ )
301+ if origin in C_RUST .RUST_NON_ELEMENT_ORIGINS :
302+ # Before the dotted-key rule: `Object`/`ffi.Object` render as the
303+ # header struct, which is no valid mirror (see RUST_NON_ELEMENT_ORIGINS).
304+ raise UnsupportedTypeError (
305+ "Optional" , f"`Optional<{ origin } >` fields have no in-place Rust mirror"
306+ )
307+ if "." in origin or origin in C_RUST .RUST_OPTION_PTR_ORIGINS :
308+ if schema .size not in (None , 8 ):
309+ # Folded aliases (`DLTensor*` -> "Tensor", `void*` ->
310+ # "ctypes.c_void_p") are std::optional-backed, not a nullable
311+ # pointer; only the 8-byte layout may take this branch.
312+ raise UnsupportedTypeError (
313+ "Optional" ,
314+ f"`Optional<{ origin } >` field has size { schema .size } , not the "
315+ "8-byte nullable pointer of a ptr-based `ffi::Optional`" ,
316+ )
317+ # `Option<T>` niche-optimizes over the ref's NonNull data, matching
318+ # C++'s single nullable pointer (`None` == nullptr).
319+ return f"Option<{ render_rust_type (payload , self ._ty_render )} >"
320+ raise UnsupportedTypeError (
321+ "Optional" , f"`Optional<{ origin } >` fields have no in-place Rust mirror"
322+ )
323+
217324 def render_param (self , schema : TypeSchema ) -> str :
218325 """Render an argument type (a top-level ``Any`` is the non-owning ``AnyView``)."""
219326 if schema .origin == "Any" :
0 commit comments