Skip to content

tsbindgen: a template's specializations are one generic tuple - #565

Merged
ASDAlexander77 merged 2 commits into
mainfrom
tsbindgen-generic-templates
Oct 13, 2026
Merged

ASDAlexander77 merged 2 commits into
mainfrom
tsbindgen-generic-templates

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

A field holding a class template's specialization used to declare that specialization on its own, named by its arguments: static_vector_IBindingSet_8, RefCountPtr_IShader, and a vector_X per element type with MSVC's helper structs. It is now an instance of one generic tuple per template.

What changes

  • Generic instances. nvrhi::static_vector<IBindingSet *, 8> is static_vector8<IBindingSet | null>, with type static_vector8<T> = [_Elems: [_0: T, ...], current_size: index]. RefCountPtr<IShader> is RefCountPtr<IShader>, with type RefCountPtr<T> = [ptr_: T | null].

  • MSVC's std::vector<T> collapses: its pointers live two specializations down (_Compressed_pair, then _Vector_val<_Simple_types<T>>), and those helpers are inlined. The result is type vector<T> = [_Mypair: [_Myval2: [_Myfirst: Reference<T> | null, ...]]], and the helpers are no longer printed. libc++'s is [__begin_: Reference<T> | null, ...].

  • How (TypeMapper::genericInstance): the specialization is laid out exactly as before, then rewritten in terms of its type arguments.

    • A part of the layout equal to an argument's mapped type becomes its parameter. For a @cpp class argument this covers its pointers too, as T | null.
    • Integer arguments go into the name (static_vector8). Defaulted ones, such as an allocator, are left out.
  • Fallback to the concrete name: a specialization keeps its own declaration, named by its arguments, in two cases:

    • its layout doesn't show a type argument: std::string's characters are raw words, so it stays basic_string_char;
    • it is laid out unlike its template's other specializations (an explicit specialization).

    This check is what keeps every instance's layout identical to clang's.

  • Selection: a specialization's declaration is no longer a seed; it is printed only for what refers to it.

  • Naming: Ref joins tslang's type names, so a template named Ref is written Ref_ instead of shadowing tslang's Ref<T>.

  • TsType: named types carry type arguments, and the printer writes a generic's parameters.

Also adds issue 13 to TSLANG_ISSUES.md: tslang doesn't infer T from an argument of a generic tuple alias (count(v) with v: Vec<Item>); passing T explicitly works.

Tests

  • LayoutTest:
    • SpecializationHeldByValue updated (static_vector2<Item | null>, Ref_<Item>);
    • new MsvcShapedVectorIsOneGeneric: two element types give one vector<T>, and no helper struct is printed;
    • new SpecializationThatIsNoInstance: an explicit specialization, and a template whose layout doesn't show its argument;
    • the MSVC-shaped std::string stays basic_string_char.
  • NamespacedStructTest updated (Vec<s32>). 78/78 pass.
  • ctest -R bindgen: 4/4. In test-bindgen-cpp, the fixture's Batch now holds a SmallVec3<Shape | null>, passed to compiled C++.
  • NVRHI under MSVC and Android (x86_64 and aarch64, which agree apart from plain char's sign): the generics match what native_ts_graphics' gen_nvrhi_classes.py wrote, and its regenerated files are byte for byte the committed ones (NVRHI's template specializations are generic tuples native_ts_graphics#59, #60).

🤖 Generated with Claude Code

ASDAlexander77 and others added 2 commits October 13, 2026 13:51
A field holding a class template's specialization was declared once per specialization, named by
its arguments (static_vector_IBindingSet_8, RefCountPtr_IShader, and std::vector's per element
type with MSVC's helper structs). It is now an instance of one generic tuple per template:
static_vector8<IBindingSet | null> with `type static_vector8<T> = [_Elems: [_0: T, ...], current_size: index]`.

TypeMapper::genericInstance lays the specialization out as before, then writes it in terms of its
type arguments: a part of the layout that is an argument's mapped type is its parameter (a @cpp
class argument's pointers too, `T | null`), and a nested specialization holding one is inlined
(MSVC's std::vector through _Compressed_pair and _Vector_val). Integer arguments go into the name;
defaulted ones (an allocator) are left out. A specialization stays declared on its own, named by
its arguments, when its layout doesn't show a type argument (std::string's characters are raw
words: basic_string_char) or differs from its template's other specializations' (an explicit
specialization). TsType's named types carry type arguments; the printer writes a generic's
parameters. A specialization's declaration is no longer a selection seed: it is printed only for
what refers to it, so the inlined helpers are not. `Ref` is one of tslang's type names, so a
template named Ref is written Ref_.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 20c20ea into main Oct 13, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the tsbindgen-generic-templates branch October 13, 2026 13:24
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.

1 participant