[clang] 5dcd381 - [clang][ExprConstant] Fix error on static constexpr symbol in dllimport function (#171628)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Dec 20 06:20:58 PST 2025
Author: Alexandre Ganea
Date: 2025-12-20T09:20:54-05:00
New Revision: 5dcd3816ed700e792bf2fdaeae7b7001cec821cf
URL: https://github.com/llvm/llvm-project/commit/5dcd3816ed700e792bf2fdaeae7b7001cec821cf
DIFF: https://github.com/llvm/llvm-project/commit/5dcd3816ed700e792bf2fdaeae7b7001cec821cf.diff
LOG: [clang][ExprConstant] Fix error on static constexpr symbol in dllimport function (#171628)
Consider the following:
```
struct A {
__declspec(dllimport) __forceinline
static const int* foo() {
static constexpr int var = 42;
static constexpr const int* p = &var;
static_assert(*p == 42, "");
return p;
}
};
const int* (*pfoo)() = &A::foo;
int main() {
return pfoo() == A::foo();
}
```
With clang-cl, this generates an error:
```
> clang-cl /c C:\src\git\test\test.cpp
C:\src\git\test\test.cpp(5,37): error: constexpr variable 'p' must be initialized by a constant expression
5 | static constexpr const int* p = &var;
| ^ ~~~~
C:\src\git\test\test.cpp(6,23): error: static assertion expression is not an integral constant expression
6 | static_assert(*p == 42, "");
| ^~~~~~~~
C:\src\git\test\test.cpp(6,24): note: initializer of 'p' is not a constant expression
6 | static_assert(*p == 42, "");
| ^
C:\src\git\test\test.cpp(5,37): note: declared here
5 | static constexpr const int* p = &var;
| ^
2 errors generated.
```
MSVC cl.exe does not generate such error with the same snippet.
The problem here is that the static variable 'var' inherits the
dllimport attribute, and the const-init evaluation for 'p' is rejected
because of the dllimport attribute.
I think it's fine to accept the example above since the body of the
function will be discarded anyway; and the inlined version of the
function will contain a reference to the imported function-static
symbol, like MSVC does:
```
> cl.exe /c test.cpp /Ox
...
> dumpbin /disasm test.obj
Microsoft (R) COFF/PE Dumper Version 14.44.35222.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file test.obj
File Type: COFF OBJECT
main:
0000000000000000: 48 83 EC 28 sub rsp,28h
0000000000000004: FF 15 00 00 00 00 call qword ptr [?pfoo@@3P6APEBHXZEA]
000000000000000A: 48 8B 0D 00 00 00 mov rcx,qword ptr [__imp_?p@?1??foo at A@@SAPEBHXZ at 4QEBHEB]
00
0000000000000011: 33 D2 xor edx,edx
0000000000000013: 48 3B 01 cmp rax,qword ptr [rcx]
0000000000000016: 0F 94 C2 sete dl
0000000000000019: 8B C2 mov eax,edx
000000000000001B: 48 83 C4 28 add rsp,28h
000000000000001F: C3 ret
??__Epfoo@@YAXXZ (void __cdecl `dynamic initializer for 'pfoo''(void)):
0000000000000000: 48 8B 05 00 00 00 mov rax,qword ptr [__imp_?foo at A@@SAPEBHXZ]
00
0000000000000007: 48 89 05 00 00 00 mov qword ptr [?pfoo@@3P6APEBHXZEA],rax
00
000000000000000E: C3 ret
> clang-cl.exe /c test.cpp /Ox
> dumpbin /disasm test.obj
Microsoft (R) COFF/PE Dumper Version 14.44.35222.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file test.obj
File Type: COFF OBJECT
main:
0000000000000000: 48 83 EC 28 sub rsp,28h
0000000000000004: FF 15 00 00 00 00 call qword ptr [?pfoo@@3P6APEBHXZEA]
000000000000000A: 31 C9 xor ecx,ecx
000000000000000C: 48 3B 05 00 00 00 cmp rax,qword ptr [__imp_?var@?1??foo at A@@SAPEBHXZ at 4HB]
00
0000000000000013: 0F 94 C1 sete cl
0000000000000016: 89 C8 mov eax,ecx
0000000000000018: 48 83 C4 28 add rsp,28h
000000000000001C: C3 ret
000000000000001D: 0F 1F 00 nop dword ptr [rax]
_GLOBAL__sub_I_test.cpp:
0000000000000020: 48 8B 05 00 00 00 mov rax,qword ptr [__imp_?foo at A@@SAPEBHXZ]
00
0000000000000027: 48 89 05 00 00 00 mov qword ptr [?pfoo@@3P6APEBHXZEA],rax
00
000000000000002E: C3 ret
```
Thanks to @jfmarquis for crafting a reproducer.
Added:
Modified:
clang/lib/AST/ExprConstant.cpp
clang/test/SemaCXX/dllimport.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index eb07cfb938a21..6ccd57bdc4df8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2407,9 +2407,11 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
return false;
// A dllimport variable never acts like a constant, unless we're
- // evaluating a value for use only in name mangling.
- if (!isForManglingOnly(Kind) && Var->hasAttr<DLLImportAttr>())
- // FIXME: Diagnostic!
+ // evaluating a value for use only in name mangling, and unless it's a
+ // static local. For the latter case, we'd still need to evaluate the
+ // constant expression in case we're inside a (inlined) function.
+ if (!isForManglingOnly(Kind) && Var->hasAttr<DLLImportAttr>() &&
+ !Var->isStaticLocal())
return false;
// In CUDA/HIP device compilation, only device side variables have
diff --git a/clang/test/SemaCXX/dllimport.cpp b/clang/test/SemaCXX/dllimport.cpp
index b7a1a62b8725b..cecabe98cfb5c 100644
--- a/clang/test/SemaCXX/dllimport.cpp
+++ b/clang/test/SemaCXX/dllimport.cpp
@@ -1526,6 +1526,36 @@ template <typename T> struct __declspec(dllimport) PartiallySpecializedClassTemp
template <typename T> struct ExpliciallySpecializedClassTemplate {};
template <> struct __declspec(dllimport) ExpliciallySpecializedClassTemplate<int> { void f() {} };
+// Function-local static constexpr in dllimport function (or class).
+#if defined(GNU)
+// expected-warning at +2{{'dllimport' attribute ignored on inline function}}
+#endif
+__declspec(dllimport) inline const int *dLLImportFuncWithConstexprStatic() {
+ static constexpr int value = 42;
+ static constexpr const int *p = &value;
+ static_assert(*p == 42, "");
+ return p;
+}
+const int* (*pFunc)() = &dLLImportFuncWithConstexprStatic;
+bool UsedDLLImportFuncWithConstexprStatic() {
+ return pFunc() == dLLImportFuncWithConstexprStatic();
+}
+
+#if !defined(PS)
+#if defined(GNU)
+ // expected-warning at +2{{'dllimport' attribute ignored on inline function}}
+#endif
+__declspec(dllimport) __forceinline const int* dLLImportInlineFuncWithConstexprStatic() {
+ static constexpr int value = 42;
+ static constexpr const int* p = &value;
+ static_assert(*p == 42, "");
+ return p;
+}
+const int* (*pFuncForceInline)() = &dLLImportInlineFuncWithConstexprStatic;
+bool UsedDLLImportInlineFuncWithConstexprStatic() {
+ return pFuncForceInline() == dLLImportInlineFuncWithConstexprStatic();
+}
+#endif // !PS
//===----------------------------------------------------------------------===//
// Classes with template base classes
More information about the cfe-commits
mailing list