[Clang][ASTMergeAction] -ast-merge crashes instead of diagnosing C++ AST merged into non-C++ cc1 invocation
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
This issue originates from [a comment](https://github.com/llvm/llvm-project/issues/74765#issuecomment-1849154537) on #74765. The underlying problem is different, so I broke it out into its own issue.
[Compiler Explorer of Example](https://godbolt.org/z/sYTEoe15q) (You have to scroll all the way down in the output window)
When running the following on trunk (6a898832ff382b1a288f9eb3bc5cd1f37d0fc29f)
test.sh
```sh
./bin/clang++ -cc1 -std=c++17 -emit-pch -o qminimalintegration.cpp.oclango qminimalintegration.cpp
./bin/clang++ -cc1 -ast-merge qminimalintegration.cpp.oclango /dev/null -ast-dump-all=json
```
qminimalintegration.cpp
```cpp
struct QPropertyBindingSourceLocation {};
void makePropertyBinding(
QPropertyBindingSourceLocation = QPropertyBindingSourceLocation());
template class QBindableInterfaceForProperty {
int iface{[] { makePropertyBinding; }};
};
```
The second command crashes with this backtrace
```console
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and dumped files.
Stack dump:
0. Program arguments: ./bin/clang++ -cc1 -ast-merge qminimalintegration.cpp.oclango /dev/null -ast-dump-all=json
1. parser at end of file
#0 0x00005cea9d2df22b llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/widberg/llvm-project-widberg-extensions/llvm/lib/Support/Unix/Signals.inc:928:13
#1 0x00005cea9d2dcb13 llvm::sys::RunSignalHandlers() /home/widberg/llvm-project-widberg-extensions/llvm/lib/Support/Signals.cpp:109:18
#2 0x00005cea9d2dffbb SignalHandler(int, siginfo_t*, void*) /home/widberg/llvm-project-widberg-extensions/llvm/lib/Support/Unix/Signals.inc:474:38
#3 0x00005cea9d2dfe28 SignalHandlerTerminate(int, siginfo_t*, void*) /home/widberg/llvm-project-widberg-extensions/llvm/lib/Support/Unix/Signals.inc:0:3
#4 0x00007fe99bf7a330 (/lib/x86_64-linux-gnu/libc.so.6+0x45330)
#5 0x00005cea9fe6a64b clang::FunctionDecl::getConstexprKind() const /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/Decl.h:2513:60
#6 0x00005cea9fe6a64b clang::FunctionDecl::isConsteval() const /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/Decl.h:2519:12
#7 0x00005cea9fe6a64b clang::FunctionDecl::isImmediateFunction() const /home/widberg/llvm-project-widberg-extensions/clang/lib/AST/Decl.cpp:3324:7
#8 0x00005ceaa030915e clang::JSONNodeDumper::attributeOnlyIfTrue(llvm::StringRef, bool) /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/JSONNodeDumper.h:146:9
#9 0x00005ceaa030915e clang::JSONNodeDumper::VisitFunctionDecl(clang::FunctionDecl const*) /home/widberg/llvm-project-widberg-extensions/clang/lib/AST/JSONNodeDumper.cpp:991:3
#10 0x00005ceaa02fdfa5 clang::JSONNodeDumper::Visit(clang::Decl const*) /home/widberg/llvm-project-widberg-extensions/clang/lib/AST/JSONNodeDumper.cpp:146:1
#11 0x00005cea9fca88e4 clang::ASTNodeTraverser::Visit(clang::Decl const*, bool)::'lambda'()::operator()() const /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/ASTNodeTraverser.h:107:12
#12 0x00005cea9fcb89c0 llvm::SmallVectorBase::size() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/SmallVector.h:83:32
#13 0x00005cea9fcb89c0 void clang::NodeStreamer::AddChild::Visit(clang::Decl const*, bool)::'lambda'()>(llvm::StringRef, clang::ASTNodeTraverser::Visit(clang::Decl const*, bool)::'lambda'())::'lambda'(bool)::operator()(bool) const /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/JSONNodeDumper.h:87:30
#14 0x00005cea9fcb89f9 llvm::SmallVectorBase::size() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/SmallVector.h:83:32
#15 0x00005cea9fcb89f9 llvm::SmallVectorTemplateBase, false>::pop_back() /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/SmallVector.h:436:26
#16 0x00005cea9fcb89f9 void clang::NodeStreamer::AddChild::Visit(clang::Decl const*, bool)::'lambda'()>(llvm::StringRef, clang::ASTNodeTraverser::Visit(clang::Decl const*, bool)::'lambda'())::'lambda'(bool)::operator()(bool) const /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/JSONNodeDumper.h:89:23
#17 0x00005cea9fca8681 llvm::SmallVectorBase::empty() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/SmallVector.h:86:46
#18 0x00005cea9fca8681 llvm::SmallVectorTemplateCommon, void>::back() /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/SmallVector.h:318:13
#19 0x00005cea9fca8681 void clang::NodeStreamer::AddChild::Visit(clang::Decl const*, bool)::'lambda'()>(llvm::StringRef, clang::ASTNodeTraverser::Visit(clang::Decl const*, bool)::'lambda'()) /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/JSONNodeDumper.h:104:15
#20 0x00005cea9fca8c8f llvm::detail::PunnedPointer::asInt() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/PointerIntPair.h:41:5
#21 0x00005cea9fca8c8f llvm::detail::PunnedPointer::operator long() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/PointerIntPair.h:45:48
#22 0x00005cea9fca8c8f llvm::PointerIntPair, llvm::PointerIntPairInfo>>::getPointer() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/PointerIntPair.h:94:58
#23 0x00005cea9fca8c8f clang::Decl::getNextDeclInContext() /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/DeclBase.h:453:62
#24 0x00005cea9fca8c8f clang::DeclContext::decl_iterator::operator++() /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/DeclBase.h:2380:26
#25 0x00005cea9fca8c8f clang::ASTNodeTraverser::dumpDeclContext(clang::DeclContext const*) /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/ASTNodeTraverser.h:341:24
#26 0x00005cea9fcb89c0 llvm::SmallVectorBase::size() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/SmallVector.h:83:32
#27 0x00005cea9fcb89c0 void clang::NodeStreamer::AddChild::Visit(clang::Decl const*, bool)::'lambda'()>(llvm::StringRef, clang::ASTNodeTraverser::Visit(clang::Decl const*, bool)::'lambda'())::'lambda'(bool)::operator()(bool) const /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/JSONNodeDumper.h:87:30
#28 0x00005cea9fcb89f9 llvm::SmallVectorBase::size() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/SmallVector.h:83:32
#29 0x00005cea9fcb89f9 llvm::SmallVectorTemplateBase, false>::pop_back() /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/SmallVector.h:436:26
#30 0x00005cea9fcb89f9 void clang::NodeStreamer::AddChild::Visit(clang::Decl const*, bool)::'lambda'()>(llvm::StringRef, clang::ASTNodeTraverser::Visit(clang::Decl const*, bool)::'lambda'())::'lambda'(bool)::operator()(bool) const /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/JSONNodeDumper.h:89:23
#31 0x00005cea9fca8681 llvm::SmallVectorBase::empty() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/SmallVector.h:86:46
#32 0x00005cea9fca8681 llvm::SmallVectorTemplateCommon, void>::back() /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/SmallVector.h:318:13
#33 0x00005cea9fca8681 void clang::NodeStreamer::AddChild::Visit(clang::Decl const*, bool)::'lambda'()>(llvm::StringRef, clang::ASTNodeTraverser::Visit(clang::Decl const*, bool)::'lambda'()) /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/JSONNodeDumper.h:104:15
#34 0x00005cea9fca8c8f llvm::detail::PunnedPointer::asInt() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/PointerIntPair.h:41:5
#35 0x00005cea9fca8c8f llvm::detail::PunnedPointer::operator long() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/PointerIntPair.h:45:48
#36 0x00005cea9fca8c8f llvm::PointerIntPair, llvm::PointerIntPairInfo>>::getPointer() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/PointerIntPair.h:94:58
#37 0x00005cea9fca8c8f clang::Decl::getNextDeclInContext() /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/DeclBase.h:453:62
#38 0x00005cea9fca8c8f clang::DeclContext::decl_iterator::operator++() /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/DeclBase.h:2380:26
#39 0x00005cea9fca8c8f clang::ASTNodeTraverser::dumpDeclContext(clang::DeclContext const*) /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/ASTNodeTraverser.h:341:24
#40 0x00005cea9fca8451 llvm::SmallVectorBase::empty() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/SmallVector.h:86:46
#41 0x00005cea9fca8451 void clang::NodeStreamer::AddChild::Visit(clang::Decl const*, bool)::'lambda'()>(llvm::StringRef, clang::ASTNodeTraverser::Visit(clang::Decl const*, bool)::'lambda'()) /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/JSONNodeDumper.h:59:23
#42 0x00005cea9fc9aa9c clang::JSONNodeDumper::~JSONNodeDumper() /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/JSONNodeDumper.h:117:7
#43 0x00005cea9fc9aa9c clang::JSONDumper::~JSONDumper() /home/widberg/llvm-project-widberg-extensions/clang/include/clang/AST/JSONNodeDumper.h:384:7
#44 0x00005cea9fc9aa9c clang::Decl::dump(llvm::raw_ostream&, bool, clang::ASTDumpOutputFormat) const /home/widberg/llvm-project-widberg-extensions/clang/lib/AST/ASTDumper.cpp:238:3
#45 0x00005cea9e0fae97 (anonymous namespace)::ASTPrinter::print(clang::Decl*) /home/widberg/llvm-project-widberg-extensions/clang/lib/Frontend/ASTConsumers.cpp:108:11
#46 0x00005cea9efeee19 clang::ParseAST(clang::Sema&, bool, bool) /home/widberg/llvm-project-widberg-extensions/clang/lib/Parse/ParseAST.cpp:0:13
#47 0x00005cea9e11d716 llvm::IntrusiveRefCntPtr::operator bool() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/ADT/IntrusiveRefCntPtr.h:207:43
#48 0x00005cea9e11d716 clang::CompilerInstance::getDiagnostics() const /home/widberg/llvm-project-widberg-extensions/clang/include/clang/Frontend/CompilerInstance.h:369:12
#49 0x00005cea9e11d716 clang::ASTMergeAction::ExecuteAction() /home/widberg/llvm-project-widberg-extensions/clang/lib/Frontend/ASTMerge.cpp:80:6
#50 0x00005cea9e1c4a86 clang::FrontendAction::Execute() /home/widberg/llvm-project-widberg-extensions/clang/lib/Frontend/FrontendAction.cpp:1350:10
#51 0x00005cea9e142b59 llvm::Error::getPtr() const /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/Support/Error.h:278:42
#52 0x00005cea9e142b59 llvm::Error::operator bool() /home/widberg/llvm-project-widberg-extensions/llvm/include/llvm/Support/Error.h:241:16
#53 0x00005cea9e142b59 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) /home/widberg/llvm-project-widberg-extensions/clang/lib/Frontend/CompilerInstance.cpp:1035:23
#54 0x00005cea9e258e34 std::__shared_ptr::get() const /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/bits/shared_ptr_base.h:1673:16
#55 0x00005cea9e258e34 std::__shared_ptr_access::_M_get() const /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/bits/shared_ptr_base.h:1370:66
#56 0x00005cea9e258e34 std::__shared_ptr_access::operator->() const /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/bits/shared_ptr_base.h:1364:9
#57 0x00005cea9e258e34 clang::CompilerInstance::getFrontendOpts() /home/widberg/llvm-project-widberg-extensions/clang/include/clang/Frontend/CompilerInstance.h:314:12
#58 0x00005cea9e258e34 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) /home/widberg/llvm-project-widberg-extensions/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp:332:14
#59 0x00005cea9b970641 cc1_main(llvm::ArrayRef, char const*, void*) /home/widberg/llvm-project-widberg-extensions/clang/tools/driver/cc1_main.cpp:296:13
#60 0x00005cea9b96c495 ExecuteCC1Tool(llvm::SmallVectorImpl&, llvm::ToolContext const&, llvm::IntrusiveRefCntPtr) /home/widberg/llvm-project-widberg-extensions/clang/tools/driver/driver.cpp:229:12
#61 0x00005cea9b96b618 clang_main(int, char**, llvm::ToolContext const&) /home/widberg/llvm-project-widberg-extensions/clang/tools/driver/driver.cpp:277:12
#62 0x00005cea9b97be8d main /home/widberg/llvm-project-widberg-extensions/build/tools/clang/tools/driver/clang-driver.cpp:17:10
#63 0x00007fe99bf5f1ca (/lib/x86_64-linux-gnu/libc.so.6+0x2a1ca)
#64 0x00007fe99bf5f28b __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28b)
#65 0x00005cea9b96a025 _start (./bin/clang+++0x2b3d025)
./test.sh: line 2: 202968 Segmentation fault ./bin/clang++ -cc1 -ast-merge qminimalintegration.cpp.oclango /dev/null -ast-dump-all=json
```
Seems to be a null dereference on the following line due to merging a C++ AST into a non-C++ cc1 invocation
https://github.com/llvm/llvm-project/blob/60f965b1f62c0c77bcdb2997ea9bb6603aa0d002/clang/lib/AST/Decl.cpp#L3343
As discussed in the other thread, adding `-x c++` after `-cc1` fixes this. But it would be nice if there was an error message instead of a crash. Even in the cases that don't crash, it looks like merging C++ into non-C++ drops information from the AST.
I prototyped what that could look like on [my err_ast_merge_cxx_lang_mismatch branch](https://github.com/llvm/llvm-project/compare/main...widberg:llvm-project-widberg-extensions:err_ast_merge_cxx_lang_mismatch). I won't submit a PR because I don't have the energy to do so, and I'm not sure that is the best solution. There should probably be stronger checks around the actual dereference, for example. Or maybe instead of an error message, it could discern the language from the input files. I'm not sure.
Whatever ends up happening, I think only merging C++ into non-C++ should be disallowed, and not the other way around. I know of several shops that depend on merging a C AST into a C++ AST, and it would inconvenience them if that broke. My only guidance is to keep that working unless there's a major correctness issue with allowing it, such as dropping information.
Contributor guide
Research direction
Reproduce the crash with test.sh and the two clang++ -cc1 commands shown in the issue. Start with clang/lib/Frontend/ASTMerge.cpp and the JSON dumping path in clang/lib/AST/JSONNodeDumper.cpp, using the stack trace to follow the merged AST into JSON dumping. Done means the non-C++ AST merge invocation diagnoses the invalid situation instead of crashing.
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
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100