llvm / llvm/llvm-project

[Clang][TableGen] CLANG_ABI on Attr classes breaks inline methods with dllimport on Windows

Open
#163,349 15 comments 0 reactions 0 assignees View on GitHub
ABI clang platform:windows
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

**Summary**

Commit d7979c111ef8 added `CLANG_ABI` to all TableGen-generated Attr classes to support plugin exports. However, on Windows this expands to `__declspec(dllimport)` for consumers, which causes linker errors for inline methods like `classof()` and `getAnnotation()` that are defined in the generated header files.

**Affected Versions**

- LLVM 20.x (release/20.x branch)
- LLVM 21.x (release/21.x branch)
- LLVM main branch

**Problem Description**

When building external Clang plugins on Windows that use generated Attr classes, the linker fails with unresolved external symbol errors for inline methods:

```
error LNK2001: unresolved external symbol "__declspec(dllimport) public: static bool __cdecl clang::AnnotateAttr::classof(class clang::Attr const *)"
error LNK2001: unresolved external symbol "__declspec(dllimport) public: class llvm::StringRef __cdecl clang::AnnotateAttr::getAnnotation(void)const"
```

The issue is that when `CLANG_ABI` expands to `__declspec(dllimport)`, it marks the entire class for import, including inline methods defined in the header. These inline methods have no corresponding export in clang.dll because they're defined inline and never emitted as exports.

**Root Cause**

In clang/utils/TableGen/ClangAttrEmitter.cpp (from commit d7979c111ef8):

```
if (Header)
OS << "class CLANG_ABI " << R.getName() << "Attr : public " << SuperName
<< " {\n";
```

On Windows when `CLANG_EXPORTS` is not defined (i.e., in plugin code consuming clang.dll), `CLANG_ABI` expands to `__declspec(dllimport)` per `clang/include/clang/Support/Compiler.h`:

```
#elif defined(_WIN32) && !defined(__MINGW32__)
#if defined(CLANG_EXPORTS)
#define CLANG_ABI __declspec(dllexport)
#else
#define CLANG_ABI __declspec(dllimport)
#endif
```

The `__declspec(dllimport)` applies to the entire class, causing the linker to expect all methods (including inline ones) to be imported from the DLL.

**Reproduction**

1. Build LLVM/Clang 20+ on Windows with shared library support (LLVM_BUILD_LLVM_DYLIB=ON, CLANG_LINK_CLANG_DYLIB=ON)
2. Create an external plugin that includes generated Attr headers (e.g., clang/AST/Attrs.inc)
3. Use any inline methods from Attr classes (e.g., AnnotateAttr::classof())
4. Link the plugin as a DLL against clang.dll
5. Observe linker errors for unresolved dllimport symbols

**Proposed Solution**

The `CLANG_ABI` macro should not be applied to classes that contain inline methods defined in headers. Possible approaches:

1. Remove CLANG_ABI from Attr classes entirely (revert to pre-d7979c111ef8 behavior)
- Con: Breaks the AnnotateFunctions and Attribute example plugins on some configurations
2. Mark specific methods for export instead of the entire class
- Use CLANG_ABI on individual virtual methods that need exports
- Leave inline methods unmarked
3. Use CLANG_ABI_NOT_EXPORTED for inline methods
- Apply CLANG_ABI to the class
- Override with CLANG_ABI_NOT_EXPORTED for inline methods
- Requires TableGen changes to detect inline vs. non-inline methods

**Workaround**

External plugin developers can work around this by removing CLANG_ABI from the generated Attr class declarations with a patch to ClangAttrEmitter.cpp:

```
- OS << "class CLANG_ABI " << R.getName() << "Attr : public " << SuperName
+ OS << "class " << R.getName() << "Attr : public " << SuperName
```

**Related Issues**

- Original PR: #109362 (commit d7979c111ef8)
- Dependency PR: #108276 (added CLANG_ABI macros)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.