Codegen supports recursive by-value container trees.
For example, gd::map<int, gd::vector<int>> becomes { [number]: { number } }.
Descendant composite pointers are unsupported except in seven audited fields.
Composite := gd::vector<Value>
| gd::map<Key, Value> | gd::unordered_map<Key, Value>
| gd::set<Value> | gd::unordered_set<Value>
| std::array<Value, N>
| std::pair<Value, Value>
| std::tuple<Value...>
Value := supported scalar, enum, value struct, bound object pointer, or opaque handle
| Composite
Key := supported scalar
| std::pair<non-container pair-key leaf, non-container pair-key leaf>
The non-container pair-key leaf set is scalar, enum, supported value struct, or bound object pointer.
There is no explicit nesting limit.
Supported std::array sizes are listed in Limits and errors.
Only the listed gd containers and std::array, std::pair, and std::tuple take part.
std::vector, std::map, and std::set remain unsupported.
Direct gd::vector<Object*> and gd::vector<Opaque*> values use read-only sequence views.
Other recursive values use table snapshots. Plain Lua table rules apply to snapshots.
A nil indexed leaf creates a hole. A nil value in a dictionary-shaped map removes that entry.
gd::map<int, gd::vector<int>>becomes{ [number]: { number } }.gd::vector<gd::unordered_map<int, bool>>becomes{ { [number]: boolean } }.std::array<gd::set<gd::string>, 4>becomes{ { string } }.std::pair<A, B>stays{ first: A, second: B }.- A map with a pair key becomes
{ { first: K1, second: K2, value: V } }. std::tuple<>becomes{}.- A homogeneous tuple becomes
{ T }. - A heterogeneous tuple becomes
{ (T1 | T2 | ...) }.
Luau array types cannot describe each tuple index separately.
The runtime still requires exact tuple arity and checks every position.
It rejects holes and extra numeric positions.
std::array tables also require exactly the declared size with no holes or extra numeric positions.
Array output order from unordered sets and unordered pair-key maps is unspecified.
By-value trees bind on class fields, method arguments, method returns, and free functions. Existing root pointer and out-parameter behavior stays unchanged. Hooks remain container-free.
Recursive values reject:
- any descendant
Composite* - composite map keys other than the restricted pair form
Container**- nested
ccCArray - callback, delegate, task, result, and unsupported leaves
char*, string-view, andZStringViewleaves
Seven audited GJBaseGameLayer pointer-grid fields use one field-only adapter.
The read-only fields are m_sectionSizes, m_nonEffectObjectsSizes, and m_collisionBlockSectionSizes.
The writable fields are m_nonEffectObjectsFlags, m_collisionBlockSections, m_sections, and m_nonEffectObjects.
Null children through this adapter still appear as empty tables. Lua cannot distinguish those null children from empty children.
Writing {} creates or keeps a non-null empty row. Codegen emits the adapter only for these seven fields.
TypeInfo stores the full tree through element_type, key_type, value_type, and tuple_types.
One tree walk drives dependency, inaccessible-class, hook, and value-struct checks.
Generated recursive values use:
checkContainerValue<T>for table checkspushContainerValue<T>for table pushesassignContainerValuefor recursive field updates
assignContainerValue avoids whole-container assignment for Geode container ABI support.
It clears and reinserts vectors, maps, and sets.
Arrays, pairs, and tuples update recursively in place.
The seven pointer-grid fields classify as one audited_pointer_grid kind.
It emits pushAuditedPointerGrid, checkAuditedPointerGrid<Actual>, and assignAuditedPointerGrid.
These helpers preserve the existing read, write, and null-child behavior without broadening recursive container support.
tools/luau_codegen/convert/type_classification.pytools/luau_codegen/convert/type_primitives.pytools/luau_codegen/convert/marshalling.pytools/luau_codegen/model/nested_containers.pytools/luau_codegen/emit/bindings/class_file.pysrc/framework/stack/ContainerTables.hpp