Skip to content

interop: keep interop traits next to the templates they specialize - #3921

Open
aleksisch wants to merge 1 commit into
GaijinEntertainment:masterfrom
aleksisch:fix/cast-arg-string-instantiation
Open

interop: keep interop traits next to the templates they specialize#3921
aleksisch wants to merge 1 commit into
GaijinEntertainment:masterfrom
aleksisch:fix/cast-arg-string-instantiation

Conversation

@aleksisch

@aleksisch aleksisch commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

The null-string wrap (cast_arg<char *> / cast_arg<const char *>, null -> "") lived in ast/ast_typefactory_bind.h, a header only binding files include. That worked while the call node was per bound function. Now that it is per signature, one game link has 27 objects defining the same node symbol - 10 with the wrap, 17 without - so the surviving body depends on object order, and ImGui::PushID got a nullptr and crashed in ImHashStr. C++ AOT never had the hole: das_string_cast wraps unconditionally.

Moves each trait next to the template it specializes: the cast_arg ones into simulate/interop.h, typeFactory<ResT (*)(Args...)> into ast/ast_typedecl.h. That empties ast_typefactory_bind.h, so it is deleted, the include dropped from every binding TU, and the binder no longer emits it.

tests-cpp/small/test_cast_arg_empty_string binds a const char * extern the way an embedder does: -1 (nullptr seen) before, 0 after.

@aleksisch aleksisch closed this Sep 1, 2026
@aleksisch aleksisch reopened this Sep 1, 2026
@aleksisch
aleksisch force-pushed the fix/cast-arg-string-instantiation branch from e2dcbd5 to 39ebd8e Compare September 1, 2026 20:10
@aleksisch aleksisch changed the title interop: keep the null string wrap for every call node instantiation interop: keep interop traits next to the templates they specialize Sep 1, 2026
@aleksisch
aleksisch force-pushed the fix/cast-arg-string-instantiation branch from 39ebd8e to 95956eb Compare September 1, 2026 20:22
A null daslang string must reach a const char * binding as "", not as
nullptr. The wrap is a cast_arg<char *> / cast_arg<const char *>
specialization, and it lived in ast/ast_typefactory_bind.h - a header
only binding files include, 102 of them, mostly generated.

That was enough while SimNode_ExtFuncCall took the function as a non
type template parameter: the node was instantiated once per bound
function, in the translation unit that did the binding, so the symbol
had exactly one definition and every bind got the conversion its own
TU compiled.

Keyed on the signature alone, one node serves every bind of that
signature. A game link has 27 objects defining
SimNode_ExtFuncCallDirect<SimNode_ExtFuncCall<void (*)(char const*)>,
void>::eval - 10 generated dasImgui TUs with the wrap, 17 engine TUs
which never saw the header and instantiated the generic cast_arg
instead. Same weak symbol, different bodies, so the surviving one
depends on object order. ImGui::PushID then got a nullptr and crashed
in ImHashStr; ld.gold --detect-odr-violations and gcc -Wodr both stay
quiet about it.

C++ AOT never had the hole: das_string_cast wraps every string
argument of every extern, unconditionally. The interpreter is meant to
match, so the specialization belongs beside the generic where no TU can
miss it, not in a header that binding files opt into.

Move the string casts and cast_arg<das::string> next to the generic
cast_arg in simulate/interop.h, and typeFactory<ResT (*)(Args...)> next
to the primary typeFactory in ast/ast_typedecl.h. That empties
ast_typefactory_bind.h, so delete it, drop the include from every
binding TU, and stop the C++ binder from emitting it.

The remaining specializations in that family fail loudly rather than
silently when unseen: cast has a declaration-only primary, typeFactory
reaches ToBasicType's static_assert, and cast_arg<das::string> needs a
cast<das::string> which does not exist.

tests/handle_types/string_arg_never_null passes a null das string, and
an empty literal, to a const char * bind in the UnitTest module.
test_string_arg_length answers -1 for a nullptr, so both asserts fail
with the specializations removed and pass with them in place.
@aleksisch
aleksisch force-pushed the fix/cast-arg-string-instantiation branch from 95956eb to c93f036 Compare September 1, 2026 20:29
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