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

Reply via email to