This is an archive of the discontinued LLVM Phabricator instance.

Constexpr not supported with __declspec(dllimport).
AbandonedPublic

Authored by zahiraam on Jan 18 2022, 8:52 AM.

Diff Detail

Event Timeline

zahiraam requested review of this revision.Jan 18 2022, 8:52 AM
zahiraam created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptJan 18 2022, 8:52 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
zahiraam updated this revision to Diff 401415.Jan 19 2022, 2:59 PM
Jeroen removed a reviewer: JVApen.Jan 19 2022, 10:01 PM
majnemer requested changes to this revision.Jan 19 2022, 10:18 PM
majnemer added a subscriber: majnemer.

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.

This revision now requires changes to proceed.Jan 19 2022, 10:18 PM
majnemer added inline comments.Jan 19 2022, 10:23 PM
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.

majnemer added inline comments.Jan 19 2022, 10:31 PM
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.

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.

@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?

zahiraam updated this revision to Diff 401644.Jan 20 2022, 8:00 AM

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.

@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?

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.

@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.

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?

Your example is different from mine as it nests the constexpr variable inside the function rather than having it at translation-unit scope.

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)
$

@majnemer

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?

@majnemer

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?

zahiraam updated this revision to Diff 403354.Jan 26 2022, 11:38 AM
zahiraam updated this revision to Diff 403370.Jan 26 2022, 12:18 PM
zahiraam marked an inline comment as done.
zahiraam updated this revision to Diff 404587.Jan 31 2022, 10:02 AM

@majnemer Any feedback please? thanks.

zahiraam updated this revision to Diff 405775.Feb 3 2022, 1:47 PM
zahiraam updated this revision to Diff 406443.Feb 7 2022, 6:50 AM
zahiraam abandoned this revision.Oct 26 2022, 7:38 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 26 2022, 7:38 AM