Fix drogon_ctl self-registration with clang-cl on Windows (COMDAT elimination of DrObject<T>::alloc_)#2477
Merged
an-tao merged 1 commit intodrogonframework:masterfrom Mar 31, 2026
Conversation
Contributor
Author
There was a problem hiding this comment.
Pull request overview
Fixes a Windows-only runtime failure of drogon_ctl when built with clang-cl + lld-link, where command handler self-registration is discarded and DrClassMap ends up empty.
Changes:
- Add explicit template instantiations of
drogon::DrObject<drogon_ctl::<command>>in each command implementation TU to force emission/retention of the registration static initializer on COFF. - Add a detailed rationale comment in
create.ccand reference it from the other command files.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| drogon_ctl/create.cc | Adds detailed rationale comment and explicit instantiation for create command registration. |
| drogon_ctl/create_controller.cc | Forces create_controller self-registration via explicit DrObject<> instantiation. |
| drogon_ctl/create_filter.cc | Forces create_filter self-registration via explicit DrObject<> instantiation. |
| drogon_ctl/create_model.cc | Forces create_model self-registration via explicit DrObject<> instantiation. |
| drogon_ctl/create_plugin.cc | Forces create_plugin self-registration via explicit DrObject<> instantiation. |
| drogon_ctl/create_project.cc | Forces create_project self-registration via explicit DrObject<> instantiation. |
| drogon_ctl/create_view.cc | Forces create_view self-registration via explicit DrObject<> instantiation. |
| drogon_ctl/help.cc | Forces help self-registration via explicit DrObject<> instantiation. |
| drogon_ctl/press.cc | Forces press self-registration via explicit DrObject<> instantiation. |
| drogon_ctl/version.cc | Forces version self-registration via explicit DrObject<> instantiation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
Problem
When building drogon with clang-cl (LLVM's MSVC-compatible compiler on Windows),
drogon_ctllinks successfully but fails at runtime: every command prints "command error! use help command to get usage!" becauseDrClassMapis empty - no command handler class is registered.This affects clang-cl 17 through 19 (at least) with lld-link.
MSVC's cl.exe and clang on Linux/macOS are not affected.
Root cause
Root cause analysis and fix were found with the help of GitHub Copilot (Claude Opus 4.6), through iterative investigation: reproducer construction, COMDAT section analysis, and testing of multiple candidate fixes ([[gnu::used]], /OPT:NOREF, constructor side-effects) before converging on explicit template instantiation.
DrObject<T>::alloc_is a static template member whose constructor registersTintoDrClassMapbefore main(). This self-registration relies on the CRT running the static initializer.On COFF targets (Windows), clang places the CRT initializer for a template static member in the same COMDAT section group as the variable itself.
When the optimizer determines that no code in the translation unit directly references alloc_ - which happens because clang aggressively inlines className() and eliminates the implicit this -> alloc_ reference - the entire COMDAT group becomes unreferenced.
The linker then discards it, so DrAllocator's constructor never runs.
On ELF targets (Linux, macOS), .init_array / .ctors entries are treated as GC roots and are never discarded, which is why this bug is invisible on those platforms.
MSVC's link.exe is also more conservative with CRT initializer sections and preserves them even when unreferenced.
Fix
This PR adds explicit template instantiation (
template class drogon::DrObject<T>) at the end of each drogon_ctl/*.cc file that defines a DrObject subclass.This forces the compiler to emit a strong (non-COMDAT) definition that the linker cannot discard.
This is standard C++ and has no effect on compilers/linkers that already preserve the initializer (MSVC, GCC, non-microsoft clang).
Scope and limitations
This PR only fixes
drogon_ctl.The same problem could potentially affect any application that statically links the drogon library with clang-cl on Windows and relies on DrObject self-registration without directly referencing the derived classes.
Users building their own apps with clang-cl would need to add similar explicit instantiations for their own DrObject subclasses.
There may be a better or more elegant compiler/linker-level solution, but we were unable to find one that works on COFF targets.
[[gnu::used]] / attribute((used)) do not prevent COMDAT elimination on COFF.
/OPT:NOREF does not help either — the issue is at the compiler level (clang-cl does not emit the initializer when it determines alloc_ is unused), not at the linker level.
Reproducer
A self-contained multi-file reproducer is attached to this PR.
Environment
Windows 10, Visual Studio 2022 Professional
clang-cl 19.1.5 (VS-bundled LLVM), lld-link
Also tested with MSVC 19.44 (works without the fix)