llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
27.97k stars 11.54k forks source link

Clang thread-safety-analysis crash #97171

Open eaeltsin opened 2 months ago

eaeltsin commented 2 months ago

https://godbolt.org/z/Yn3M74cKa

Flags

-Wthread-safety-analysis -std=gnu++20

Code

template <int __v> struct integral_constant {
  static const int value = __v;
  typedef integral_constant type;
};
template <class _Tp>
struct is_default_constructible : integral_constant<__is_constructible(_Tp)> {};
template <class _Tp, class = is_default_constructible<_Tp>::type>
struct __is_implicitly_default_constructible : integral_constant<false> {};
template <class _T2> struct pair {
  struct _CheckArgs {
    static constexpr bool __enable_implicit_default() {
      return __is_implicitly_default_constructible<_T2>::value;
    }
  };
  explicit(_CheckArgs::__enable_implicit_default()) pair();
};
template <class P> struct common_policy_traits {
  typedef decltype(P::element(0)) value_type;
};
template <class P> struct raw_hash_set {
  using value_type = common_policy_traits<P>::value_type;
};
template <class V> struct FP {
  static pair<V> element(int);
};
template <class V> struct flat_hash_map : raw_hash_set<FP<V>> {};
template <typename Type> struct M {
  alignas(Type) char space_;
};
template <typename N> struct small_map {
  M<typename N::value_type> array_;
};
class G operator==(G, G);
struct {
  void foo() {
    bar().type.m != 0;
  }
  template <typename> struct RV {
    long m = 0;
  };
  struct R {
    RV<int> type;
  };
  R bar();
  small_map<flat_hash_map<R>> registers_;
};

Output:

PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.  Program arguments: /opt/compiler-explorer/clang-trunk/bin/clang++ -gdwarf-4 -g -o /app/output.s -mllvm --x86-asm-syntax=intel -S --gcc-toolchain=/opt/compiler-explorer/gcc-snapshot -fcolor-diagnostics -fno-crash-diagnostics -Wthread-safety-analysis -std=gnu++20 <source> -isystem/opt/compiler-explorer/libs/abseil -isystem/opt/compiler-explorer/libs/fmt/7.0.0/include
1.  <eof> parser at end of file
2.  <source>:34:1: parsing struct/union/class body '(unnamed struct at <source>:34:1)'
3.  <source>:35:14: parsing function body '(anonymous struct)::foo'
 #0 0x00000000037b2bd8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x37b2bd8)
 #1 0x00000000037b0d24 llvm::sys::CleanupOnSignal(unsigned long) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x37b0d24)
 #2 0x00000000036f9628 CrashRecoverySignalHandler(int) CrashRecoveryContext.cpp:0:0
 #3 0x0000750127442520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #4 0x0000000000b499d0 (anonymous namespace)::BuildLockset::VisitCallExpr(clang::CallExpr const*) (.cold) ThreadSafety.cpp:0:0
 #5 0x0000000006ea25a5 (anonymous namespace)::ThreadSafetyAnalyzer::runAnalysis(clang::AnalysisDeclContext&) ThreadSafety.cpp:0:0
 #6 0x0000000006ea41f7 clang::threadSafety::runThreadSafetyAnalysis(clang::AnalysisDeclContext&, clang::threadSafety::ThreadSafetyHandler&, clang::threadSafety::BeforeSet**) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x6ea41f7)
 #7 0x0000000006d1437e clang::sema::AnalysisBasedWarnings::IssueWarnings(clang::sema::AnalysisBasedWarnings::Policy, clang::sema::FunctionScopeInfo*, clang::Decl const*, clang::QualType) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x6d1437e)
 #8 0x00000000061020b3 clang::Sema::PopFunctionScopeInfo(clang::sema::AnalysisBasedWarnings::Policy const*, clang::Decl const*, clang::QualType) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x61020b3)
 #9 0x000000000631bd3a clang::Sema::ActOnFinishFunctionBody(clang::Decl*, clang::Stmt*, bool) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x631bd3a)
#10 0x00000000060898ef clang::Parser::ParseFunctionStatementBody(clang::Decl*, clang::Parser::ParseScope&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x60898ef)
#11 0x0000000005fa17ee clang::Parser::ParseLexedMethodDef(clang::Parser::LexedMethod&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x5fa17ee)
#12 0x0000000005fa149d clang::Parser::ParseLexedMethodDefs(clang::Parser::ParsingClass&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x5fa149d)
#13 0x0000000005ff588f clang::Parser::ParseCXXMemberSpecification(clang::SourceLocation, clang::SourceLocation, clang::ParsedAttributes&, unsigned int, clang::Decl*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x5ff588f)
#14 0x0000000005ff8838 clang::Parser::ParseClassSpecifier(clang::tok::TokenKind, clang::SourceLocation, clang::DeclSpec&, clang::Parser::ParsedTemplateInfo&, clang::AccessSpecifier, bool, clang::Parser::DeclSpecContext, clang::ParsedAttributes&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x5ff8838)
#15 0x0000000005fc3374 clang::Parser::ParseDeclarationSpecifiers(clang::DeclSpec&, clang::Parser::ParsedTemplateInfo&, clang::AccessSpecifier, clang::Parser::DeclSpecContext, clang::Parser::LateParsedAttrList*, clang::ImplicitTypenameContext) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x5fc3374)
#16 0x0000000005f833e6 clang::Parser::ParseDeclOrFunctionDefInternal(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec&, clang::AccessSpecifier) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x5f833e6)
#17 0x0000000005f84683 clang::Parser::ParseDeclarationOrFunctionDefinition(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*, clang::AccessSpecifier) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x5f84683)
#18 0x0000000005f8ca12 clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x5f8ca12)
#19 0x0000000005f8e7d7 clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x5f8e7d7)
#20 0x0000000005f7d96a clang::ParseAST(clang::Sema&, bool, bool) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x5f7d96a)
#21 0x000000000404d905 clang::CodeGenAction::ExecuteAction() (/opt/compiler-explorer/clang-trunk/bin/clang+++0x404d905)
#22 0x00000000042ec661 clang::FrontendAction::Execute() (/opt/compiler-explorer/clang-trunk/bin/clang+++0x42ec661)
#23 0x00000000042709db clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x42709db)
#24 0x00000000043d3693 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x43d3693)
#25 0x0000000000c7a7ec cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0xc7a7ec)
#26 0x0000000000c7469d ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) driver.cpp:0:0
#27 0x0000000004096399 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const::'lambda'()>(long) Job.cpp:0:0
#28 0x00000000036f9a53 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x36f9a53)
#29 0x00000000040965b9 clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const (.part.0) Job.cpp:0:0
#30 0x000000000405c817 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&, bool) const (/opt/compiler-explorer/clang-trunk/bin/clang+++0x405c817)
#31 0x000000000405d1cd clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&, bool) const (/opt/compiler-explorer/clang-trunk/bin/clang+++0x405d1cd)
#32 0x0000000004066d3c clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x4066d3c)
#33 0x0000000000c77a41 clang_main(int, char**, llvm::ToolContext const&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0xc77a41)
#34 0x0000000000b51fc4 main (/opt/compiler-explorer/clang-trunk/bin/clang+++0xb51fc4)
#35 0x0000750127429d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#36 0x0000750127429e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#37 0x0000000000c7413e _start (/opt/compiler-explorer/clang-trunk/bin/clang+++0xc7413e)
clang++: error: clang frontend command failed with exit code 139 (use -v to see invocation)
Compiler returned: 139
aaronpuchert commented 2 weeks ago

Apparently, this doesn't compile when we remove the warning flag:

crash97171.cpp:35:8: error: functions cannot be declared in an anonymous struct
   35 |   void foo() {
      |        ^
crash97171.cpp:38:30: error: anonymous struct can only contain non-static data members
   38 |   template <typename> struct RV {
      |                              ^
crash97171.cpp:41:10: error: types cannot be declared in an anonymous struct
   41 |   struct R {
      |          ^
crash97171.cpp:44:5: error: functions cannot be declared in an anonymous struct
   44 |   R bar();
      |     ^
crash97171.cpp:34:1: error: anonymous structs and classes must be class members
   34 | struct {
      | ^

I'm surprised that we run the Thread Safety Analysis at all in this context. My understanding was that we don't run it if there are any errors. In this case, it's actually the function that we crash on:

CXXMethodDecl 0x5574fcd86998 <line:35:3, line:37:3> line:35:8 foo 'void ()' implicit-inline
`-CompoundStmt 0x5574fcd90c60 <col:14, line:37:3>
  `-ExprWithCleanups 0x5574fcd90c48 <line:36:5, col:21> '<dependent type>' contains-errors
    `-CXXOperatorCallExpr 0x5574fcd90c10 <col:5, col:21> '<dependent type>' contains-errors '!='
      |-UnresolvedLookupExpr 0x5574fcd90bc8 <col:18> '<overloaded function type>' lvalue (ADL) = 'operator!=' 0x5574fcd866c8
      |-RecoveryExpr 0x5574fcd90b80 <col:5, col:16> '<dependent type>' contains-errors lvalue
      | `-MemberExpr 0x5574fcd90b50 <col:5, col:11> 'RV<int>' xvalue .type 0x5574fcd881c0
      |   `-MaterializeTemporaryExpr 0x5574fcd90b38 <col:5, col:9> 'R' xvalue
      |     `-CXXMemberCallExpr 0x5574fcd90890 <col:5, col:9> 'R'
      |       `-MemberExpr 0x5574fcd90830 <col:5> '<bound member function type>' ->bar 0x5574fcd882e8
      |         `-CXXThisExpr 0x5574fcd90820 <col:5> '(anonymous struct at crash97171.cpp:34:1) *' implicit this
      `-IntegerLiteral 0x5574fcd90ba8 <col:21> 'int' 0

We could quickly fix this particular bug by changing isa to isa_and_nonnull, but that doesn't sound right. If we only look at fully-instantiated functions without errors, a CXXOperatorCallExpr should always have a direct callee. (Unless I'm overlooking something.)

So we should find out why the analysis runs on a function with errors and how we can prevent that.

aaronpuchert commented 2 weeks ago

This is where we should exit:

void clang::sema::AnalysisBasedWarnings::IssueWarnings(
    sema::AnalysisBasedWarnings::Policy P, sema::FunctionScopeInfo *fscope,
    const Decl *D, QualType BlockType) {
  // ...
  if (S.hasUncompilableErrorOccurred()) {
    // Flush out any possibly unreachable diagnostics.
    flushDiagnostics(S, fscope);
    return;
  }

Which happens before we run the analysis. Why doesn't this return true?