llvm / llvm/llvm-project

[clang][CodeGen] ICE: dllexporting a class that inherits a constructor with an incomplete-type parameter

Open
#219,455 7 comments 0 reactions 0 assignees View on GitHub
clang:codegen confirmed crash regression:23
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Summary

Clang crashes (access violation, `0xC0000005`) during LLVM IR generation when a `__declspec(dllexport)` class inherits a base-class constructor (`using Base::Base;`) whose parameter is an **incomplete** class type passed by value.

I have encountered this in clang-cl.exe, but it's not specific to `clang-cl` or the MS C++ ABI. it reproduces with the plain `clang` driver on any Windows target, both `x86_64-pc-windows-msvc` and `x86_64-pc-windows-gnu`.

The null dereference happens in `clang::ASTContext::getASTRecordLayout`

## Environment

- Compiler: [clang version 23.1.0](https://github.com/llvm/llvm-project/releases/tag/llvmorg-23.1.0)
- Reproduces with `clang`, `clang++`, and `clang-cl` drivers, and directly via `-cc1`.
- Language: default standard is enough (needs C++11 for inherited constructors)

## Minimal repro

Original repro minimized via `cvise` and a bit of hand-editing:

```cpp
class Incomplete;
struct Base { Base(Incomplete); };
struct __declspec(dllexport) Derived : Base {
using Base::Base;
};
```

Commands (any one of these):

```sh
# simplest: MSVC target and -fdeclspec implied
clang-cl -c crash-min.cpp

# plain clang driver, Itanium ABI (MinGW target)
clang --target=x86_64-pc-windows-gnu -fdeclspec -c crash-min.cpp -o crash-min.o

# frontend only, fastest (crash is in IRGen, before the backend)
clang -cc1 -triple x86_64-pc-windows-gnu -fdeclspec -emit-llvm-only crash-min.cpp
```

Any one of the following avoids the crash:

* making the ctor parameter a complete type
* dropping `__declspec(dllexport)` on `Derived`
* replacing `using Base::Base;` with a user-declared ctor
* non-Windows target

## Stack

```
Exception Code: 0xC0000005
#0 0x00007ff7d5665bed clang::Decl::getKind C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\AST\DeclBase.h:1937:0
#1 0x00007ff7d5665bed clang::CXXRecordDecl::classof C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\AST\DeclCXX.h:1935:0
#2 0x00007ff7d5665bed llvm::isa_impl::doit C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\Support\Casting.h:64:0
#3 0x00007ff7d5665bed llvm::isa_impl_cl::doit C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\Support\Casting.h:110:0
#4 0x00007ff7d5665bed llvm::isa_impl_wrap::doit C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\Support\Casting.h:137:0
#5 0x00007ff7d5665bed llvm::isa_impl_wrap::doit C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\Support\Casting.h:127:0
#6 0x00007ff7d5665bed llvm::CastIsPossible::isPossible C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\Support\Casting.h:255:0
#7 0x00007ff7d5665bed llvm::CastInfo::doCastIfPossible C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\Support\Casting.h:493:0
#8 0x00007ff7d5665bed llvm::dyn_cast C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\Support\Casting.h:657:0
#9 0x00007ff7d5665bed clang::ASTContext::getASTRecordLayout(class clang::RecordDecl const *) const C:\S\llvm-e9988a0e4bbdf\p\clang\lib\AST\RecordLayoutBuilder.cpp:0:0
#10 0x00007ff7d689a63d clang::ASTContext::getTypeInfoImpl C:\S\llvm-e9988a0e4bbdf\p\clang\lib\AST\ASTContext.cpp:2213:0
#11 0x00007ff7d5641391 llvm::DenseMap,llvm::detail::DenseMapPair >::getRep C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\ADT\DenseMap.h:905:0
#12 0x00007ff7d5641391 llvm::DenseMapBase,llvm::detail::DenseMapPair >,const clang::Type *,clang::TypeInfo,llvm::DenseMapInfo,llvm::detail::DenseMapPair >::getRep C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\ADT\DenseMap.h:672:0
#13 0x00007ff7d5641391 llvm::DenseMapBase,llvm::detail::DenseMapPair >,const clang::Type *,clang::TypeInfo,llvm::DenseMapInfo,llvm::detail::DenseMapPair >::LookupBucketFor C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\ADT\DenseMap.h:753:0
#14 0x00007ff7d5641391 llvm::DenseMapBase,llvm::detail::DenseMapPair >,const clang::Type *,clang::TypeInfo,llvm::DenseMapInfo,llvm::detail::DenseMapPair >::lookupOrInsertIntoBucket C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\ADT\DenseMap.h:633:0
#15 0x00007ff7d5641391 llvm::DenseMapBase,llvm::detail::DenseMapPair >,const clang::Type *,clang::TypeInfo,llvm::DenseMapInfo,llvm::detail::DenseMapPair >::operator[] C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\ADT\DenseMap.h:417:0
#16 0x00007ff7d5641391 clang::ASTContext::getTypeInfo(class clang::Type const *) const C:\S\llvm-e9988a0e4bbdf\p\clang\lib\AST\ASTContext.cpp:2099:0
#17 0x00007ff7d8388400 `anonymous namespace'::WinX86_64ABIInfo::classify C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\Targets\X86.cpp:3416:0
#18 0x00007ff7d720dd52 `anonymous namespace'::WinX86_64ABIInfo::computeInfo C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\Targets\X86.cpp:3602:0
#19 0x00007ff7d82f8c2f clang::CodeGen::ABIArgInfo::isDirect C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\CodeGen\CGFunctionInfo.h:319:0
#20 0x00007ff7d82f8c2f clang::CodeGen::ABIArgInfo::canHaveCoerceToType C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\CodeGen\CGFunctionInfo.h:330:0
#21 0x00007ff7d82f8c2f clang::CodeGen::CodeGenTypes::arrangeLLVMFunctionInfo(class clang::CanQual, enum clang::CodeGen::FnInfoOpts, class llvm::ArrayRef>, class clang::FunctionType::ExtInfo, class llvm::ArrayRef, class clang::CodeGen::RequiredArgs) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\CGCall.cpp:1074:0
#22 0x00007ff7d57782c0 clang::CodeGen::CodeGenTypes::arrangeCXXStructorDeclaration(class clang::GlobalDecl) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\CGCall.cpp:454:0
#23 0x00007ff7d5829756 clang::CodeGen::CodeGenModule::codegenCXXStructor(class clang::GlobalDecl) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\CGCXX.cpp:267:0
#24 0x00007ff7d5829489 `anonymous namespace'::MicrosoftCXXABI::emitCXXStructor C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\MicrosoftCXXABI.cpp:4092:0
#25 0x00007ff7d83adb62 clang::CodeGen::CodeGenModule::EmitGlobalDefinition C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\CodeGenModule.cpp:4993:0
#26 0x00007ff7d58f3a6c clang::CodeGen::CodeGenModule::addEmittedDeferredDecl C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\CodeGenModule.h:435:0
#27 0x00007ff7d58f3a6c clang::CodeGen::CodeGenModule::EmitGlobal(class clang::GlobalDecl) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\CodeGenModule.cpp:4770:0
#28 0x00007ff7d8477c9a clang::Decl::hasAttrs C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\AST\DeclBase.h:526:0
#29 0x00007ff7d8477c9a clang::Decl::hasAttr C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\AST\DeclBase.h:586:0
#30 0x00007ff7d8477c9a `anonymous namespace'::MicrosoftCXXABI::EmitCXXConstructors C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\MicrosoftCXXABI.cpp:1331:0
#31 0x00007ff7d832d5e8 `anonymous namespace'::CodeGeneratorImpl::HandleTagDeclDefinition C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\ModuleBuilder.cpp:280:0
#32 0x00007ff7d832d5e8 clang::BackendConsumer::HandleTagDeclDefinition C:\S\llvm-e9988a0e4bbdf\p\clang\lib\CodeGen\CodeGenAction.cpp:334:0
#33 0x00007ff7d56d25e1 clang::Sema::ActOnTagFinishDefinition(class clang::Scope *, class clang::Decl *, class clang::SourceRange) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Sema\SemaDecl.cpp:19219:0
#34 0x00007ff7d809452c clang::Parser::ParsingClassDefinition::Pop C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\Parse\Parser.h:2940:0
#35 0x00007ff7d809452c clang::Parser::ParseCXXMemberSpecification C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\ParseDeclCXX.cpp:3744:0
#36 0x00007ff7d8092829 clang::Parser::ParseClassSpecifier(enum clang::tok::TokenKind, class clang::SourceLocation, class clang::DeclSpec &, struct clang::Parser::ParsedTemplateInfo &, enum clang::AccessSpecifier, bool, enum clang::Parser::DeclSpecContext, class clang::ParsedAttributes &) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\ParseDeclCXX.cpp:2179:0
#37 0x00007ff7d57c2d75 llvm::SmallVectorBase::empty C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\ADT\SmallVector.h:86:0
#38 0x00007ff7d57c2d75 clang::ParsedAttributesView::empty C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\Sema\ParsedAttr.h:822:0
#39 0x00007ff7d57c2d75 clang::Parser::ParseDeclarationSpecifiers(class clang::DeclSpec &, struct clang::Parser::ParsedTemplateInfo &, enum clang::AccessSpecifier, enum clang::Parser::DeclSpecContext, class clang::LateParsedAttrList *, enum clang::ImplicitTypenameContext) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\ParseDecl.cpp:4517:0
#40 0x00007ff7d58525bd clang::Parser::ParseDeclOrFunctionDefInternal C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\Parser.cpp:1067:0
#41 0x00007ff7d842f668 llvm::SaveAndRestore::~SaveAndRestore C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\Support\SaveAndRestore.h:29:0
#42 0x00007ff7d842f668 clang::Parser::ObjCDeclContextSwitch::~ObjCDeclContextSwitch C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\Parse\Parser.h:5475:0
#43 0x00007ff7d842f668 clang::Parser::ParseDeclarationOrFunctionDefinition(class clang::ParsedAttributes &, class clang::ParsedAttributes &, class clang::ParsingDeclSpec *, enum clang::AccessSpecifier) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\Parser.cpp:1176:0
#44 0x00007ff7d599a892 clang::Parser::ParseExternalDeclaration(class clang::ParsedAttributes &, class clang::ParsedAttributes &, class clang::ParsingDeclSpec *) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\Parser.cpp:1003:0
#45 0x00007ff7d84a4524 clang::AttributePool::~AttributePool C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\Sema\ParsedAttr.h:709:0
#46 0x00007ff7d84a4524 clang::ParsedAttributes::~ParsedAttributes C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\Sema\ParsedAttr.h:937:0
#47 0x00007ff7d84a4524 clang::Parser::ParseInnerNamespace C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\ParseDeclCXX.cpp:237:0
#48 0x00007ff7d84a463d clang::Parser::ParseScope::Exit C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\Parse\Parser.h:518:0
#49 0x00007ff7d84a463d clang::Parser::ParseInnerNamespace C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\ParseDeclCXX.cpp:260:0
#50 0x00007ff7d59b364d clang::Parser::ParseScope::Exit C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\Parse\Parser.h:518:0
#51 0x00007ff7d59b364d clang::Parser::ParseNamespace(enum clang::DeclaratorContext, class clang::SourceLocation &, class clang::SourceLocation) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\ParseDeclCXX.cpp:217:0
#52 0x00007ff7d84a7840 clang::Parser::ParseDeclaration(enum clang::DeclaratorContext, class clang::SourceLocation &, class clang::ParsedAttributes &, class clang::ParsedAttributes &, class clang::SourceLocation *) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\ParseDecl.cpp:1935:0
#53 0x00007ff7d599a1f6 clang::SourceLocation::SourceLocation C:\S\llvm-e9988a0e4bbdf\p\clang\include\clang\Basic\SourceLocation.h:59:0
#54 0x00007ff7d599a1f6 clang::Parser::ParseExternalDeclaration(class clang::ParsedAttributes &, class clang::ParsedAttributes &, class clang::ParsingDeclSpec *) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\Parser.cpp:0:0
#55 0x00007ff7d5c889d1 clang::Parser::ParseTopLevelDecl(class clang::OpaquePtr &, enum clang::Sema::ModuleImportState &) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\Parser.cpp:711:0
#56 0x00007ff7d76f80a3 clang::ParseAST(class clang::Sema &, bool, bool) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Parse\ParseAST.cpp:169:0
#57 0x00007ff7d6a827f3 clang::FrontendAction::Execute(void) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Frontend\FrontendAction.cpp:1348:0
#58 0x00007ff7d5ccb0f7 llvm::Error::getPtr C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\Support\Error.h:281:0
#59 0x00007ff7d5ccb0f7 llvm::Error::operator bool C:\S\llvm-e9988a0e4bbdf\p\llvm\include\llvm\Support\Error.h:241:0
#60 0x00007ff7d5ccb0f7 clang::CompilerInstance::ExecuteAction(class clang::FrontendAction &) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\Frontend\CompilerInstance.cpp:1033:0
#61 0x00007ff7d5cca00d clang::ExecuteCompilerInvocation(class clang::CompilerInstance *) C:\S\llvm-e9988a0e4bbdf\p\clang\lib\FrontendTool\ExecuteCompilerInvocation.cpp:319:0
#62 0x00007ff7d5c1d32b cc1_main(class llvm::ArrayRef, char const *, void *) C:\S\llvm-e9988a0e4bbdf\p\clang\tools\driver\cc1_main.cpp:296:0
#63 0x00007ff7d5c1c6df ExecuteCC1Tool(class llvm::SmallVectorImpl &, struct llvm::ToolContext const &, class llvm::IntrusiveRefCntPtr) C:\S\llvm-e9988a0e4bbdf\p\clang\tools\driver\driver.cpp:229:0
#64 0x00007ff7d5c54bbf clang_main(int, char **, struct llvm::ToolContext const &) C:\S\llvm-e9988a0e4bbdf\p\clang\tools\driver\driver.cpp:277:0
#65 0x00007ff7d5c53828 main C:\S\b\clang85748b1fd1bb4\b\tools\clang\tools\driver\clang-driver.cpp:17:0
#66 0x00007ff7d85b8ae4 invoke_main D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78:0
#67 0x00007ff7d85b8ae4 __scrt_common_main_seh D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288:0
#68 0x00007ffd67bf7ac4 (C:\Windows\System32\KERNEL32.DLL+0x17ac4)
#69 0x00007ffd68b8a8c1 (C:\Windows\SYSTEM32\ntdll.dll+0x5a8c1)
```

## Original context

This is the real snippet that triggered the crash, a `NX_UTILS_API` (`__declspec(dllexport)`) class deriving from Qt's `QDataStream` and inheriting its constructors:

```cpp
class NX_UTILS_API DataStream: public QDataStream {
using base_type = QDataStream;
public:
using base_type::base_type; // inherits QDataStream(QIODevice*), QDataStream(QByteArray*, OpenMode), ...
DataStream(std::string_view buf);
};
```

Original crashing translation unit and the exact driver/`-cc1` command line are in the attached original repro dump (`data_stream-7a60a9.cpp` preprocessed source, `data_stream-7a60a9.sh` the clang-generated reproducer script).

Downstream workaround: move the export from the class to the single user-declared constructor, so the class is not force-exported and the inherited constructors are not emitted:

```cpp
class DataStream: public QDataStream {
using base_type = QDataStream;
public:
using base_type::base_type;
NX_UTILS_API DataStream(std::string_view buf); // export the member, not the class
};
```

[data_stream-7a60a9.sh](https://github.com/user-attachments/files/31554072/data_stream-7a60a9.sh)

[data_stream-7a60a9.cpp](https://github.com/user-attachments/files/31554076/data_stream-7a60a9.cpp)

Contributor guide

Open the contributing guide

Research direction

Reproduce the crash with the minimal C++ example using the listed clang or clang -cc1 command. Start at clang::ASTContext::getASTRecordLayout and follow the stack through WinX86_64ABIInfo::classify and CodeGenTypes::arrangeCXXStructorDeclaration. Done means the incomplete-type inherited-constructor case no longer crashes on the affected Windows targets and has regression coverage.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.