interop: keep interop traits next to the templates they specialize - #3921
Open
aleksisch wants to merge 1 commit into
Open
interop: keep interop traits next to the templates they specialize#3921aleksisch wants to merge 1 commit into
aleksisch wants to merge 1 commit into
Conversation
aleksisch
force-pushed
the
fix/cast-arg-string-instantiation
branch
from
September 1, 2026 20:10
e2dcbd5 to
39ebd8e
Compare
aleksisch
force-pushed
the
fix/cast-arg-string-instantiation
branch
from
September 1, 2026 20:22
39ebd8e to
95956eb
Compare
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
force-pushed
the
fix/cast-arg-string-instantiation
branch
from
September 1, 2026 20:29
95956eb to
c93f036
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.
The null-string wrap (
cast_arg<char *>/cast_arg<const char *>, null ->"") lived inast/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, andImGui::PushIDgot anullptrand crashed inImHashStr. C++ AOT never had the hole:das_string_castwraps unconditionally.Moves each trait next to the template it specializes: the
cast_argones intosimulate/interop.h,typeFactory<ResT (*)(Args...)>intoast/ast_typedecl.h. That emptiesast_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_stringbinds aconst char *extern the way an embedder does:-1(nullptr seen) before,0after.