Fix MSVC C2159: duplicate extern from GGML_API on Windows - #292
Conversation
GGML_API already expands to '__declspec(dllexport/dllimport) extern' on Windows shared builds, so the explicit extern on turbo3_cpu_wht_group_size produced 'extern extern', which MSVC rejects. GCC's visibility-attribute expansion of GGML_API doesn't include extern, which is why this built fine on Linux. Made the extern conditional so both platforms get the semantics the original comment intended. fix created using Claude.ai online
|
Thanks @sroller, and thanks for testing on real MSVC and saying which version. Your diagnosis is exactly right and it is my bug: I added that Before this goes in, I ran the patch through all four build configurations by expanding the real macro from MinGW loses the Static builds still produce Minor, and harmless here: the condition uses bitwise Suggested alternativeRather than adding a condition that has to be kept in sync with the macro, put the # else
-# define GGML_API __attribute__ ((visibility ("default")))
+# define GGML_API __attribute__ ((visibility ("default"))) extern
# endif-GGML_API extern int turbo3_cpu_wht_group_size;
+GGML_API int turbo3_cpu_wht_group_size;Same expansion test on that version: All four are a correct declaration, no duplication anywhere, and there is nothing to keep in sync later. One wart to be aware of if you take that route: the definition site in Heads up on a collisionThis exact change is already in flight. PR #289 and #291 carry the macro-side fix above, and correspondingly change Either way, thanks for finding this. It is a genuine portability bug I introduced, and the report was well diagnosed. |
|
Great! I'm going to make the suggested change and resubmit the PR later tonight. Do I understand correctly that you don't have a Windows platform to run the tests? |
The conditional worked for MSVC shared builds but disagreed with the macro
on two other configurations:
- MinGW defines _WIN32, so the condition took the no-explicit-extern arm
while GGML_API took the visibility arm, which has no extern. That makes
the line a second definition again, the exact bug the comment above it
warns about.
- Static builds have no GGML_SHARED, so GGML_API is plain 'extern' and the
explicit extern was still emitted, leaving 'extern extern' and C2159 on
MSVC static.
Adding extern to the visibility branch makes GGML_API carry it on every
path, so the use site needs no condition and there is nothing to keep in
sync later.
Expansion on all five configurations:
static extern int ...
shared ELF __attribute__((visibility("default"))) extern int ...
shared MSVC dllexport __declspec(dllexport) extern int ...
shared MSVC dllimport __declspec(dllimport) extern int ...
shared MinGW __attribute__((visibility("default"))) extern int ...
This also matches what PRs TheTom#289 and TheTom#291 already carry, so the two will no
longer conflict.
|
Thanks for the rebase @sroller. I hope you do not mind, I pushed The reason for changing approach rather than keeping the conditional is the two gaps I mentioned. Here is the expansion of your version against the real MinGW is the awkward one: it defines After the change, same five configurations: Correct declaration on every path, and nothing at the use site that has to be kept in sync with the macro later. Two practical notes:
I still cannot test MSVC, no Windows machine here, so please do re-run your original repro against this version. I verified the preprocessor expansion for all five configurations and that a full macOS build stays clean, but your actual MSVC 19.44 build is the thing that matters and I would rather you confirm it than take my word. Sorry again for the noise on your first contribution here. The diagnosis was correct and the bug was mine to begin with. |
a0d29dd to
faaf200
Compare
|
@sroller apologies, I rewrote this branch's commit metadata and force-pushed without asking first. That was my call to make only about my own commit and I overstepped by doing it on your branch. What changed: the author email on my commit only, which was wrongly attributed to a work address. Your commit If you have this branch locally, the SHAs moved: Nothing else about the PR changed. It still needs your MSVC 19.44 repro against the macro-side version, which is the part I cannot test from here. Sorry for the noise on what is your first contribution to this repo. |
|
Correct, sroller — no Windows machine here at all. Everything I claimed about the MSVC and MinGW behaviour came from expanding the real Your msys64/MinGW64 setup would be genuinely useful, and more than the MSVC side. MinGW is the exact configuration where the original conditional went wrong: it defines So a MinGW shared build ( Adding it to your routine would be valuable beyond this PR. The fork has repeatedly shipped code that nobody compiled on a given backend, which is how the HIP quality gate went its entire life without running, so a standing Windows datapoint is worth having. |
|
Correction to what I said above: we do have a Windows machine, an RTX 3090 box. It is powered down at the moment, so I could not use it for this PR, but it is not true that there is no Windows here and I should not have put it that way. It should be able to cover both configurations that matter for this change once it is up: MinGW64 shared, which is where the original conditional silently dropped the Your offer to add Windows to your routine still stands on its own merits, since one machine that is sometimes off is not coverage. But you should not feel obliged to carry this particular verification. |
|
The Windows box is up, so I ran what I said I would rather than leaving it with you. MSVC 19.28.29915, VS 2019 Community, x64. Building only Result on your branch with the macro-side change ( And the control, your original commit So the static gap I claimed is real and now measured rather than argued from preprocessor expansion. Your conditional fixes the shared build, which is what your C2159 report was about, and the static build still failed underneath it. The macro-side version passes both. One correction on my own method, because it nearly produced a false all-clear. My first control run came back exit 0 and I almost reported that your version was fine on static. It was not: I had built only the What I still have not covered: MinGW. That box has no msys64 and no gcc, only MSVC, so I cannot test it here. MinGW is the configuration where your original conditional silently dropped the Also worth saying plainly: my MSVC is 19.28 and yours is 19.44. C2159 is conformance behaviour so I would expect it to reproduce on both, but the versions differ and I would not want to claim your compiler is covered by mine. Thanks for the report and for chasing it into the right place. It was my bug, and the diagnosis in your first message was correct. |
|
MinGW is covered now. I installed MSYS2 on that box, so gcc 16.2.0, and ran the case neither of us had. This is the one where a passing build proves nothing, so the check is the symbol table rather than the exit code. Both configurations build and link with no error at all:
Full picture across every configuration now:
So your original conditional fixed the loud case and left both quiet ones. The macro-side version is correct on all three. Two things I want to be straight about. My MSVC is 19.28 and yours is 19.44, so your compiler still is not literally covered by mine, though C2159 is conformance behaviour and I would expect it to match. And this is gcc 16.2.0 from MSYS2, which is not the only MinGW distribution in the world. Given that, I am happy to merge this whenever you are ready, or leave it for you to run your own repro first if you would rather confirm on your setup. Your call, it is your PR. |
|
I pulled the pr 292 and was able to compile on my windows machine. I'm a bit confused about the process because I've never contributed on Github to a larger project. My major version management tools were RCS and VSS :-). BTW: I just found out that this fork is so amazingly fast on my limited hardware (RTX5080) because the MoE cache has been implemented, Chapeau! |
|
Re-reading this in the morning: Please go ahead and merge. |
Overview
GGML_API already expands to '__declspec(dllexport/dllimport) extern' on Windows shared builds, so the explicit extern on turbo3_cpu_wht_group_size produced 'extern extern', which MSVC rejects. GCC's visibility-attribute expansion of GGML_API doesn't include extern, which is why this built fine on Linux. Made the extern conditional so both platforms get the semantics the original comment intended.
Additional information
compiled and tested on Windows 11 using MSVC 19.44.35227 for x64
Requirements
I have read and agree with the contributing guidelines
AI usage disclosure: fix created with information from Claude.ai online