refactor(codegen): shared FFIType IR + single type parser for C/C++/Rust#104
Merged
Conversation
gmelodie
marked this pull request as ready for review
June 29, 2026 20:09
gmelodie
requested review from
Ivansete-status
and removed request for
Ivansete-status
July 1, 2026 19:02
gmelodie
marked this pull request as draft
July 1, 2026 19:03
gmelodie
force-pushed
the
chore/shared-codegen-utils
branch
7 times, most recently
from
July 3, 2026 19:11
47804a5 to
7f858c6
Compare
gmelodie
marked this pull request as ready for review
July 3, 2026 19:11
Ivansete-status
approved these changes
Jul 5, 2026
Ivansete-status
left a comment
Collaborator
There was a problem hiding this comment.
LGTM! Great simplification indeed! 🙌
gmelodie
force-pushed
the
chore/shared-codegen-utils
branch
from
July 6, 2026 13:58
7f858c6 to
59e9f1f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The three FFI codegen backends each carried their own ad-hoc parser for the Nim type strings in the metadata registry (
FFIFieldMeta.typeName, e.g."seq[Option[int]]"): C recursed viagenericInnerType+startsWith("seq["), C++ viagenericInnerType, and Rust via hand-rolledt[4 .. ^2]slicing. Three copies of the same leaf-scalar table, three places to fix any new shape — and they had already drifted:nimTypeToRustwas missing theint8/int16/uint8/uint16/uint32/byte/float32cases that C and C++ handle, so those types silently fell through tocapitalizeFirstLetter(t)and emitted invalid Rust.This introduces one structured type model and one parser that all three backends consume.
ffi/codegen/types_ir.nim: a recursiveFFITypedescriptor (ScalarKind+FFITypeKind), the singleparseFFITypeparser, and a genericrenderNativewalker driven by a small per-languageNativeTypeMap.genericInnerTypemoves here as a private parser helper (removed fromc_cpp_common.nim).cpp.nim/rust.nim:nimTypeToCpp/nimTypeToRustare nowrenderNative(map, parseFFIType(t)). Public signatures unchanged, so callers are untouched. This fixes the Rust scalar drift by construction (the scalar set now lives in exactly one place).c.nim:ensureCTypeswitches onFFIType.kindinstead of string prefixes; the leaf scalar table is expressed once asscalarCInfoTablekeyed byScalarKind, andleafSuffix/cTokenderive from it. Monomorphisation and codec-body emission (emitSeqType/emitOptType/emitStructType) are unchanged — only their input becomes the shared IR.Codec-body emission and the high-level ctor/method/event wrappers are deliberately left per-backend (they genuinely diverge:
Result<T>vsstd::futurevs flume,unique_ptrvs RAII vsDrop); a later phase could revisit them behind a backend vtable.ftHandleis parsed for completeness but no production caller passes ahandleNamesset (handles are pre-converted topointerupstream), so that arm is currently exercised only by the unit tests.Verification
tests/unit/test_rust_codegen.nimpins the drift fix directly:nimTypeToRustover the full scalar set (including the previously-brokenint8/int16/uint8/uint16/uint32/byte/float32),seq[byte]→Vec<u8>,ptr T/pointer, nested generics,Maybe==Option, and struct capitalisation.tests/unit/test_types_ir.nimcoversparseFFITypedirectly: the full scalar set,seq[byte]/seq[uint8]→ftBytes, nestedseq[Option[seq[int]]],Maybe[T]==Option[T], handle names →ftHandle,ptr T/pointer→ftPtr, unknown name →ftStruct, plusrenderNativeround-trips.examples/echoandexamples/timerare byte-identical to what is on disk, and the Rust output is identical (the examples don't use any of the previously-broken scalar types, so the drift fix changes no checked-in output).--mm:orcand--mm:refc(test_types_ir,test_rust_codegen,test_c_codegen,test_cddl_codegen,test_string_helpers,test_meta).