llvm / llvm/llvm-project

[Clang][ASTMergeAction] -ast-merge crashes instead of diagnosing C++ AST merged into non-C++ cc1 invocation

Open
#213,441 1 comment 0 reactions 0 assignees View on GitHub
clang:frontend confirmed crash
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.