diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -44,6 +44,7 @@ #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Stack.h" #include "clang/Basic/TokenKinds.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" @@ -388,6 +389,7 @@ std::unique_ptr CI, llvm::ArrayRef CompilerInvocationDiags, std::shared_ptr Preamble) { + clang::noteBottomOfStack(); trace::Span Tracer("BuildAST"); SPAN_ATTACH(Tracer, "File", Filename); diff --git a/clang-tools-extra/clangd/support/Threading.cpp b/clang-tools-extra/clangd/support/Threading.cpp --- a/clang-tools-extra/clangd/support/Threading.cpp +++ b/clang-tools-extra/clangd/support/Threading.cpp @@ -8,6 +8,7 @@ #include "support/Threading.h" #include "support/Trace.h" +#include "clang/Basic/Stack.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/Threading.h" #include "llvm/Support/thread.h" @@ -98,6 +99,9 @@ auto Task = [Name = Name.str(), Action = std::move(Action), Cleanup = std::move(CleanupTask)]() mutable { llvm::set_thread_name(Name); + // Mark the bottom of the stack for clang to be aware of the stack usage and + // prevent stack overflow. + clang::noteBottomOfStack(); Action(); // Make sure function stored by ThreadFunc is destroyed before Cleanup runs. Action = nullptr; diff --git a/clang-tools-extra/clangd/test/infinite-instantiation.test b/clang-tools-extra/clangd/test/infinite-instantiation.test new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/test/infinite-instantiation.test @@ -0,0 +1,13 @@ +// RUN: cp %s %t.cpp +// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s + +// CHECK: [template_recursion_depth_exceeded] + +template +constexpr int f(T... args) { + return f(0, args...); +} + +int main() { + auto i = f(); +} diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp --- a/clang-tools-extra/clangd/tool/Check.cpp +++ b/clang-tools-extra/clangd/tool/Check.cpp @@ -57,6 +57,7 @@ #include "clang/AST/ASTContext.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/Stack.h" #include "clang/Format/Format.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Tooling/CompilationDatabase.h" @@ -441,6 +442,7 @@ bool check(llvm::StringRef File, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts) { + clang::noteBottomOfStack(); std::optional LineRange; if (!CheckFileLines.empty()) { uint32_t Begin = 0, End = std::numeric_limits::max(); diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -15,6 +15,7 @@ #include "clang/Basic/FileEntry.h" #include "clang/Basic/LangStandard.h" #include "clang/Basic/Sarif.h" +#include "clang/Basic/Stack.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" @@ -1054,6 +1055,11 @@ } llvm::Error FrontendAction::Execute() { + // This is a fallback: If the client forgets to invoke this, we mark the + // current stack as the bottom. Though not optimal, this could help prevent + // stack overflow during deep recursion. + clang::noteBottomOfStack(); + CompilerInstance &CI = getCompilerInstance(); if (CI.hasFrontendTimer()) {