Fix bugzilla issue #22688 - disambiguate module and struct mangling#22747
Fix bugzilla issue #22688 - disambiguate module and struct mangling#22747oCHIKIo wants to merge 1 commit intodlang:masterfrom
Conversation
|
This is not as simple as changing the compiler.
Without context I'm not convinced that using
This comment almost confirms my concerns that Given that modules are encoded everywhere into symbols. The object size increase ought to be justified as well. |
ec7bc3b to
a7782fc
Compare
|
Thanks for your pull request and interest in making D better, @oCHIKIo! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.
|
a7782fc to
d839f08
Compare
Thanks for the feedback, I reworked this patch to address the concerns raised in review. The previous version only changed compiler mangling and used M, which can overlap with existing member-function mangling context. In this update I switched to a dedicated module marker P so module path components are explicitly disambiguated from symbols like structs with the same name. I also extended core.demangle in druntime to understand the new encoding, updated the ABI spec grammar to document it, and added regression coverage for the exact module/struct same-name collision case from #22688, including checks for the resulting mangled symbols and behavior. This version is intended to be a full fix path rather than a compiler-only change, and to avoid the ambiguity risk from the earlier M approach. |
d839f08 to
5f2cbad
Compare
|
This builds and passes the targeted regression on my Windows machine, but CI still fails broadly. Could you point me to the first failing log/job I should focus on so I can fix the actual breakage quickly? |
There's many tests in the compiler/test folder that need to be updated. However, before you do that, first let's agree on a good mangling scheme. The PR as it stands now is a bit hacky. This forward scan looks ugly: And the |
|
From my testing, it seems that existing demanglers tolerate leading zeros in the length prefix. So a forward compatible thing we can do is still mangle packages or modules as |
The mangler uses a single '0' to encode an (anonymous?) symbol without an identifier, see https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/package.d#L575. Not sure it can be produced in any way, though.
'P' can start a type if it is followed by a 'Q', as types also use back references. The character pointed to by the back reference can be used to disambiguate with an LName, though. Here is a bison grammar of the mangling that can be used to verify whether an LALR(1) parser with some extra tokens for specific look-ahead has any ambiguities: https://gist.githubusercontent.com/rainers/6cdf73b48837defb9f88/raw/3db6a3c8e72222ccd6b22e8ae01c601bd585e9d8/backref6.bison BTW: it might be shorter to mark the module only, anything before that must be a package name. |
|
I have revised the implementation based on the feedback to drop the P marker approach and instead implement @dkorpel suggestion. The new approach resolves the collision by prepending a leading 0 to the length encoding of any non-module/non-package aggregate (like a struct or class) when it appears as a parent component in a mangled name. For example, a struct bar is now mangled as 03bar, while a module bar remains 3bar. This is elegantly backward-compatible: the existing demangler's decodeNumber naturally parses 03 as 3, requiring absolutely zero logic changes in core.demangle. I have updated the ABI documentation, added demangler validation tests, and updated the hardcoded strings in |
5f2cbad to
dbb9561
Compare
Tah. I haven't updated libiberty in the gnu toolchain for a long while, so I think it's still correct. Might be good to check-in that bison grammar to the |
|
@dkorpel @ibuclaw I implemented the suggestion to prefix the length of aggregate types with 0 (struct bar becomes 03bar while module bar remains 3bar). This perfectly resolves the collision and decodeNumber natively handles it without any demangler regressions. However, the CI pipelines (specifically bootstrap and linker stages for tests like foreach.d) are violently failing. After investigating the logs, applying the zero-padding uniformly causes core language types like object.Object to change their mangling to 06Object. When the CI tries to bootstrap and runs tests, it triggers massive undefined reference linker errors because the new compiler expects _D6object06Object... but the dynamically linked libdruntime/libphobos standard libraries (or cached bootstrap binaries) export the legacy _D6object6Object... without the zero. Because this padding suggestion inherently breaks the ABI of Object and prevents the CI from successfully bootstrapping the compiler, how would you like me to proceed? Should this be gated behind an ABI -preview= transition switch, or is there another disambiguation strategy that avoids altering the core Object library symbols? |
|
The zero-padding approach correctly disambiguates the collision at the compilable level, but it fundamentally changes the mangling of all aggregate types that appear as parents, including core types like object.Object. During CI bootstrap, the new compiler tries to link against the pre-built druntime, which still exports symbols under the old mangling, causing linker failures everywhere. Should this be gated behind a -preview= flag (similar to -preview=dip1000) to allow an opt-in transition period before it becomes the default? |
|
The preview doesn't solve a real-world problem (like the issue mentioned, this is mostly a theoretical issue because conventions avoid triggering it) so people are not going to use it, and so it will just sit there doing nothing.
@ibuclaw is that intentional? It seems wrong that new dmd links with old libphobos, but I have encountered weird linker errors before (for example missing |
dbb9561 to
da9d004
Compare
|
The failures were in druntime/test/profile specifically bothnew.def.exp which contains hardcoded mangled symbols. The struct Num's constructor symbol changed from _D4both3Num6__ctorMFNckZSQxQu to _D4both03Num6__ctorMFNckZSQyQv due to the zero-padding. I've updated the expected output file and force-pushed. The compiler test suite passes locally. |
67c8150 to
08b37dd
Compare
Zero-pad struct and class lengths in mangle paths to prevent collisions with module names.
08b37dd to
28df9ec
Compare
|
I tracked down why the CI linker was failing earlier. the zero-padding fix successfully resolves the mangling block collision, but since it also applies to standard classes it forces |
Fixes #22688
I changed how modules and packages are mangled to stop them from colliding with struct names. Previously, if you had a module and a struct with the same name, they would produce the exact same mangled string because both were treated as basic identifiers.
I added a small 'M' character after each module or package component in the mangled name. This happens in two places: in the mangleParent function where the parent packages are processed, and in the visit function for the symbols themselves.
Now, module names are clearly marked, while struct names stay the same. This ensures the final mangled names are different even if the identifiers are identical. I checked this with the test case and confirmed that the linker now sees them as distinct symbols. Existing demangling tools still work fine because they are already designed to handle this marker.