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@A@@SAPEBHXZ@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@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@A@@SAPEBHXZ@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@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@+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@+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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
