Fix for issue https://github.com/llvm/llvm-project/issues/53182
Diff Detail
Event Timeline
I have a question regarding how this work with respect to the dllimport semantics known by the linker.
IIUC, we will now allow a program like:
extern int __declspec(dllimport) dll_import_int; constexpr int& dll_import_constexpr_ref = dll_import_int; int& get() { return dll_import_constexpr_ref; }
Here, get will load dll_import_constexpr_ref. However, what will dll_import_constexpr_ref hold? It ought to hold the contents of __imp_?dll_import_int@@3HA. However, we can't dereference __imp_?dll_import_int@@3HA to get to its contents.
clang/lib/AST/ExprConstant.cpp | ||
---|---|---|
2217 | The summary and the bug both mention dllimport but this is examining dllexport. Is this intended? If so, please update the comment above this if-statement. |
clang/test/CodeGenCXX/dllimport.cpp | ||
---|---|---|
2 | Why did this check-prefix change to MSC64? There is already a unique prefix for this RUN line: M64. |
@majnemer Thanks for the review.
This test case doesn't link with MSVC. It will generate this error:
Microsoft (R) Incremental Linker Version 14.29.30133.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:test1.exe
test1.obj
test1.obj : error LNK2001: unresolved external symbol "int dll_import_int" (?dll_import_int@@3HA)
test1.exe : fatal error LNK1120: 1 unresolved externals
The symbols generated with MSVC are:
07 00000000 UNDEF notype External | ?dll_import_int@@3HA (int dll_import_int)
015 00000000 SECT6 notype Static | ?dll_import_constexpr_ref@@3AEAHEA (int & dll_import_constexpr_ref)
Without this patch this test case errors. With this patch clang will generate these symbols:
010 00000000 UNDEF notype External | imp_?dll_import_int@@3HA (declspec(dllimport) int dll_import_int)
012 00000000 SECT5 notype External | ?dll_import_constexpr_ref@@3AEAHEA (int & dll_import_constexpr_ref)
013 00000000 UNDEF notype External | ?dll_import_int@@3HA (int dll_import_int)
and will error at link time with this error:
test1-f1f63b.o : error LNK2019: unresolved external symbol "declspec(dllimport) int dll_import_int" (imp_?dll_import_int@@3HA) referenced in function "int & __cdecl get(void)" (?get@@YAAEAHXZ)
test1-f1f63b.o : error LNK2001: unresolved external symbol "int dll_import_int" (?dll_import_int@@3HA)
a.exe : fatal error LNK1120: 2 unresolved externals
I think that's the behavior expected, right?
My interpretation is that MSVC has a bug: it is forming a reference to ?dll_import_int@@3HA and not __imp_?dll_import_int@@3HA. Could you run a more complete experiment where you have a dll which exports the symbol and you try to import it?
This is what I did (before the complete experiment).
For this test case:
int& get() {
extern int __declspec(dllimport) dll_import_int; constexpr int& dll_import_constexpr_ref = dll_import_int; return dll_import_constexpr_ref;
}
int main() {
get(); return 0;
}
This is generating this symbol (with imp):
ksh-3.2$ dumpbin /symbols test2.obj | grep dll
015 00000000 UNDEF notype External | imp_?dll_import_int@@3HA (declspec(dllimport) int dll_import_int)
ksh-3.2$
With clang (with this patch) it is generating the same symbol:
ksh-3.2$ dumpbin /symbols test2.o | grep dll
00E 00000000 UNDEF notype External | imp_?dll_import_int@@3HA (declspec(dllimport) int dll_import_int)
ksh-3.2$
For this test case:
extern int __declspec(dllexport) dll_export_int;
constexpr int& dll_export_constexpr_ref = dll_export_int;
int& get() {
extern int __declspec(dllimport) dll_import_int; constexpr int& dll_import_constexpr_ref = dll_import_int; return dll_export_constexpr_ref;
}
int main() {
get(); return 0;
}
MSVC is generating these symbols:
ksh-3.2$ dumpbin /symbols test3.obj | grep dll
007 00000000 UNDEF notype External | ?dll_export_int@@3HA (int dll_export_int)
018 00000000 SECT6 notype Static | ?dll_export_constexpr_ref@@3AEAHEA (int & dll_export_constexpr_ref)
019 00000000 UNDEF notype External | imp_?dll_import_int@@3HA (declspec(dllimport) int dll_import_int)
ksh-3.2$
clang (with this patch) is generating an error:
test3.cpp:2:16: error: constexpr variable 'dll_export_constexpr_ref' must be
initialized by a constant expression
constexpr int& dll_export_constexpr_ref = dll_export_int;
^ ~~~~~~~~~~~~~~
1 error generated.
Your thoughts?
Your example is different from mine as it nests the constexpr variable inside the function rather than having it at translation-unit scope.
And I suppose we are interested in the latter?
Aargh! Getting lots of link errors with various tests I tried.
Tried this:
dll.cpp:
__declspec(dllexport) int& get() {
extern int __declspec(dllimport) dll_import_int; constexpr int& dll_import_constexpr_ref = dll_import_int; return dll_import_constexpr_ref;
}
testapp.cpp:
extern int& __declspec(dllimport) get();
int main() {
get(); return 0;
}
$ cl dll.cpp /LD /EHsc
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30133 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
dll.cpp
Microsoft (R) Incremental Linker Version 14.29.30133.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:dll.dll
/dll
/implib:dll.lib
dll.obj
Creating library dll.lib and object dll.exp
dll.obj : error LNK2019: unresolved external symbol "declspec(dllimport) int dll_import_int" (imp_?dll_import_int@@3HA) referenced in function "int & __cdecl get(void)" (?get@@YAAEAHXZ)
dll.dll : fatal error LNK1120: 1 unresolved externals
$
Can't seem to find the right combination to find the potential bug!
Would that be enough to show that there is a MS bug?
extern int __declspec(dllimport) dll_import_int;
constexpr int& dll_import_constexpr_ref = dll_import_int;
int f() { return dll_import_constexpr_ref; }
int main() { return f(); }
Compiled with clang:
$ dumpbin /symbols a.o | grep dll
010 00000000 UNDEF notype External | imp_?dll_import_int@@3HA (declspec(dllimport) int dll_import_int)
012 00000000 SECT5 notype External | ?dll_import_constexpr_ref@@3AEAHEA (int & dll_import_constexpr_ref)
013 00000000 UNDEF notype External | ?dll_import_int@@3HA (int dll_import_int)
Compiled with MSVC:
$ dumpbin /symbols a.obj | grep dll
007 00000000 UNDEF notype External | ?dll_import_int@@3HA (int dll_import_int)
015 00000000 SECT6 notype Static | ?dll_import_constexpr_ref@@3AEAHEA (int & dll_import_constexpr_ref)
$
I did take an example completely different with no use of the constexpr in order to understand (at least for me) when the _imp prefix is generated with CL.
This is sample.cpp:
#include<iostream>
__declspec(dllexport) void PrintHello()
{
std::cout << "hello" << std::endl;
}
$ cl sample.cpp /LD /EHsc
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27045 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
sample.cpp
Microsoft (R) Incremental Linker Version 14.16.27045.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:sample.dll
/dll
/implib:sample.lib
sample.obj
Creating library sample.lib and object sample.exp
$
This is testapp.cpp:
extern void __declspec(dllimport)PrintHello();
void main()
{
PrintHello();
}
$ cl testapp.cpp /EHsc /link sample.lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27045 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
testapp.cpp
Microsoft (R) Incremental Linker Version 14.16.27045.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:testapp.exe
sample.lib
testapp.obj
$
$ dumpbin /symbols testapp.obj | grep imp
008 00000000 UNDEF notype External | imp_?PrintHello@@YAXXZ (declspec(dllimport) void __cdecl PrintHello(void))
$
My understanding that the _imp prefix is generated because of the dllimport in testapp.cpp.
So if we go back to the test case above:
extern int __declspec(dllimport) dll_import_int;
constexpr int& dll_import_constexpr_ref = dll_import_int;
int& get() {
return dll_import_constexpr_ref;
}
int main () {
get(); return 0;
}
CLANG (with this patch and ICL for that matter) generates the symbol:
011 00000000 UNDEF notype External | imp_?dll_import_int@@3HA (declspec(dllimport) int dll_import_int)
but not MSVC. This seems incorrect to me since the _imp prefix is always generated for dllimport symbols?
OTOH it does seem odd that clang is generating for the test case 2 UNDEF symbols. One with the _imp mangling and one without?
The summary and the bug both mention dllimport but this is examining dllexport. Is this intended? If so, please update the comment above this if-statement.