Description
We got a report in BoringSSL of a conflict between some Windows headers and clang-cl:
https://bugs.chromium.org/p/boringssl/issues/detail?id=717
In short, if you build the following with MSVC's STL, clang-cl, and C++20...
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
extern "C++" { // Yes this is odd, see below for why
#include <memory>
}
int main() {}
The following error comes up:
c:\src\header-conflict>cmake -GNinja -B build -DCMAKE_C_COMPILER="c:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/Llvm/x64/bin/clang-cl.exe" -DCMAKE_CXX_COMPILER="c:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/Llvm/x64/bin/clang-cl.exe"
-- The C compiler identification is Clang 17.0.3 with MSVC-like command-line
-- The CXX compiler identification is Clang 17.0.3 with MSVC-like command-line
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: c:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/Llvm/x64/bin/clang-cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: c:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/Llvm/x64/bin/clang-cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (3.8s)
-- Generating done (0.0s)
-- Build files have been written to: C:/src/header-conflict/build
c:\src\header-conflict>ninja -C build
ninja: Entering directory `build'
[1/2] Building CXX object CMakeFiles\test.dir\test.cc.obj
FAILED: CMakeFiles/test.dir/test.cc.obj
c:\PROGRA~1\MICROS~3\2022\PROFES~1\VC\Tools\Llvm\x64\bin\clang-cl.exe /nologo -TP /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -std:c++20 -MDd /showIncludes /FoCMakeFiles\test.dir\test.cc.obj /FdCMakeFiles\test.dir\ -c -- C:\src\header-conflict\test.cc
In file included from C:\src\header-conflict\test.cc:5:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\memory:14:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\xmemory:12:
In file included from C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include\limits:15:
In file included from c:\PROGRA~1\MICROS~3\2022\PROFES~1\VC\Tools\Llvm\x64\lib\clang\17\include\intrin.h:20:
In file included from c:\PROGRA~1\MICROS~3\2022\PROFES~1\VC\Tools\Llvm\x64\lib\clang\17\include\x86intrin.h:19:
In file included from c:\PROGRA~1\MICROS~3\2022\PROFES~1\VC\Tools\Llvm\x64\lib\clang\17\include\mm3dnow.h:14:
c:\PROGRA~1\MICROS~3\2022\PROFES~1\VC\Tools\Llvm\x64\lib\clang\17\include\prfchwintrin.h(50,1): error: declaration of '_m_prefetchw' has a different language linkage
50 | _m_prefetchw(volatile const void *__P)
| ^
C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\um\winnt.h(3541,1): note: previous declaration is here
3541 | _m_prefetchw (
| ^
1 error generated.
ninja: build stopped: subcommand failed.
(This is MSVC's clang-cl, but the reporter also can repro on Clang 18.)
The significance of C++20 mode seems to be that, in older C++ versions, <windows.h>
does not pull in the Windows copy of the intrinsic. I have not determined why yet. The underlying conflict is language-independent, which is what Windows declares _m_prefetchw
as:
#ifdef __cplusplus
extern "C" {
#endif
...
VOID
_m_prefetchw (
_In_ volatile CONST VOID *Source
);
...
#pragma intrinsic(_m_prefetchw)
Whereas Clang declares it this way, with no extern
block:
static __inline__ void __attribute__((__always_inline__, __nodebug__))
_m_prefetchw(volatile const void *__P)
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcast-qual"
__builtin_prefetch ((const void*)__P, 1, 3 /* _MM_HINT_T0 */);
#pragma clang diagnostic pop
}
BoringSSL needs to wrap the C++ bits in its headers in extern "C++"
to undo other projects including our headers inside extern "C"
blocks, which was too common to clear out. (People don't expect <openssl/foo.h>
to include C++ code.) C++ headers (such as the MSVC STL!) do not react well to being included under extern "C"
. So we have to use extern "C++"
to restore the default language linkage. However, it seems that restoring the default language linkage explicitly is not quite the same as leaving it undecorated:
https://godbolt.org/z/8r8Mzhbvr
https://godbolt.org/z/aKoGzMof1
Bizarrely, even the static mismatch seems it would be fatal, yet it isn't here. Does clang-cl special-case this somehow?
https://godbolt.org/z/T84eq1j6q
I'm not sure what the right fix is, but given clang-cl is meant to coexist with Windows headers, I suspect the fix should be on the Clang side, one way or another. I'm not sure whether that means to give its intrinsics C linkage, add more quirks to this mismatch allowance, defer defining the intrinsics to Windows headers, or something else altogether.