diff options
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticCommonKinds.td | 5 | ||||
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 1 | ||||
| -rw-r--r-- | clang/include/clang/Basic/Stack.h | 29 | ||||
| -rw-r--r-- | clang/include/clang/Sema/Sema.h | 12 | ||||
| -rw-r--r-- | clang/lib/Basic/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | clang/lib/Basic/Stack.cpp | 71 | ||||
| -rw-r--r-- | clang/lib/Frontend/CompilerInstance.cpp | 5 | ||||
| -rw-r--r-- | clang/lib/Sema/Sema.cpp | 14 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 194 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaInit.cpp | 7 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaLookup.cpp | 56 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 1 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplateDeduction.cpp | 14 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiate.cpp | 6 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 6 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaType.cpp | 22 | ||||
| -rw-r--r-- | clang/test/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | clang/test/SemaTemplate/stack-exhaustion.cpp | 18 | ||||
| -rw-r--r-- | clang/test/lit.cfg.py | 3 | ||||
| -rw-r--r-- | clang/test/lit.site.cfg.py.in | 1 | ||||
| -rw-r--r-- | clang/tools/driver/driver.cpp | 2 | 
21 files changed, 348 insertions, 123 deletions
diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index e5ad50fd0f1..f12ee1ba927 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -17,6 +17,11 @@ let Component = "Common" in {  def fatal_too_many_errors    : Error<"too many errors emitted, stopping now">, DefaultFatal; +def warn_stack_exhausted : Warning< +  "stack nearly exhausted; compilation time may suffer, and " +  "crashes due to stack overflow are likely">, +  InGroup<DiagGroup<"stack-exhausted">>, NoSFINAE; +  def note_declared_at : Note<"declared here">;  def note_previous_definition : Note<"previous definition is here">;  def note_previous_declaration : Note<"previous declaration is here">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 36a4d70c7c0..7d380338c2e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12,7 +12,6 @@  let Component = "Sema" in {  let CategoryName = "Semantic Issue" in { -  def note_previous_decl : Note<"%0 declared here">;  def note_entity_declared_at : Note<"%0 declared here">;  def note_callee_decl : Note<"%0 declared here">; diff --git a/clang/include/clang/Basic/Stack.h b/clang/include/clang/Basic/Stack.h index e0b04099de5..3418c3bad11 100644 --- a/clang/include/clang/Basic/Stack.h +++ b/clang/include/clang/Basic/Stack.h @@ -16,11 +16,40 @@  #include <cstddef> +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Compiler.h" +  namespace clang {    /// The amount of stack space that Clang would like to be provided with.    /// If less than this much is available, we may be unable to reach our    /// template instantiation depth limit and other similar limits.    constexpr size_t DesiredStackSize = 8 << 20; + +  /// Call this once on each thread, as soon after starting the thread as +  /// feasible, to note the approximate address of the bottom of the stack. +  void noteBottomOfStack(); + +  /// Determine whether the stack is nearly exhausted. +  bool isStackNearlyExhausted(); + +  void runWithSufficientStackSpaceSlow(llvm::function_ref<void()> Diag, +                                       llvm::function_ref<void()> Fn); + +  /// Run a given function on a stack with "sufficient" space. If stack space +  /// is insufficient, calls Diag to emit a diagnostic before calling Fn. +  inline void runWithSufficientStackSpace(llvm::function_ref<void()> Diag, +                                          llvm::function_ref<void()> Fn) { +#ifdef LLVM_ENABLE_THREADS +    if (LLVM_UNLIKELY(isStackNearlyExhausted())) +      runWithSufficientStackSpaceSlow(Diag, Fn); +    else +      Fn(); +#else +    if (LLVM_UNLIKELY(isStackNearlyExhausted())) +      Diag(); +    Fn(); +#endif +  }  } // end namespace clang  #endif // LLVM_CLANG_BASIC_STACK_H diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 255c453420a..19cf8041dd9 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1278,6 +1278,8 @@ public:    void addImplicitTypedef(StringRef Name, QualType T); +  bool WarnedStackExhausted = false; +  public:    Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,         TranslationUnitKind TUKind = TU_Complete, @@ -1309,6 +1311,16 @@ public:    void PrintStats() const; +  /// Warn that the stack is nearly exhausted. +  void warnStackExhausted(SourceLocation Loc); + +  /// Run some code with "sufficient" stack space. (Currently, at least 256K is +  /// guaranteed). Produces a warning if we're low on stack space and allocates +  /// more in that case. Use this in code that may recurse deeply (for example, +  /// in template instantiation) to avoid stack overflow. +  void runWithSufficientStackSpace(SourceLocation Loc, +                                   llvm::function_ref<void()> Fn); +    /// Helper class that creates diagnostics with optional    /// template instantiation stacks.    /// diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt index b4d6ced4d5f..be739c70468 100644 --- a/clang/lib/Basic/CMakeLists.txt +++ b/clang/lib/Basic/CMakeLists.txt @@ -60,6 +60,7 @@ add_clang_library(clangBasic    Sanitizers.cpp    SourceLocation.cpp    SourceManager.cpp +  Stack.cpp    TargetInfo.cpp    Targets.cpp    Targets/AArch64.cpp diff --git a/clang/lib/Basic/Stack.cpp b/clang/lib/Basic/Stack.cpp new file mode 100644 index 00000000000..667db5c51fe --- /dev/null +++ b/clang/lib/Basic/Stack.cpp @@ -0,0 +1,71 @@ +//===--- Stack.h - Utilities for dealing with stack space -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines utilities for dealing with stack allocation and stack space. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Stack.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/CrashRecoveryContext.h" + +static LLVM_THREAD_LOCAL void *BottomOfStack = nullptr; + +static void *getStackPointer() { +#if __GNUC__ || __has_builtin(__builtin_frame_address) +  return __builtin_frame_address(0); +#elif defined(_MSC_VER) +  return _AddressOfReturnAddress(); +#else +  char CharOnStack = 0; +  // The volatile store here is intended to escape the local variable, to +  // prevent the compiler from optimizing CharOnStack into anything other +  // than a char on the stack. +  // +  // Tested on: MSVC 2015 - 2019, GCC 4.9 - 9, Clang 3.2 - 9, ICC 13 - 19. +  char *volatile Ptr = &CharOnStack; +  return Ptr; +#endif +} + +void clang::noteBottomOfStack() { +  if (!BottomOfStack) +    BottomOfStack = getStackPointer(); +} + +bool clang::isStackNearlyExhausted() { +  // We consider 256 KiB to be sufficient for any code that runs between checks +  // for stack size. +  constexpr size_t SufficientStack = 256 << 10; + +  // If we don't know where the bottom of the stack is, hope for the best. +  if (!BottomOfStack) +    return false; + +  intptr_t StackDiff = (intptr_t)getStackPointer() - (intptr_t)BottomOfStack; +  size_t StackUsage = (size_t)std::abs(StackDiff); + +  // If the stack pointer has a surprising value, we do not understand this +  // stack usage scheme. (Perhaps the target allocates new stack regions on +  // demand for us.) Don't try to guess what's going on. +  if (StackUsage > DesiredStackSize) +    return false; + +  return StackUsage >= DesiredStackSize - SufficientStack; +} + +void clang::runWithSufficientStackSpaceSlow(llvm::function_ref<void()> Diag, +                                            llvm::function_ref<void()> Fn) { +  llvm::CrashRecoveryContext CRC; +  CRC.RunSafelyOnThread([&] { +    noteBottomOfStack(); +    Diag(); +    Fn(); +  }, DesiredStackSize); +} diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index cbf519758ec..f0227d0501c 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -890,6 +890,11 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) {    assert(!getFrontendOpts().ShowHelp && "Client must handle '-help'!");    assert(!getFrontendOpts().ShowVersion && "Client must handle '-version'!"); +  // Mark this point as the bottom of the stack if we don't have somewhere +  // better. We generally expect frontend actions to be invoked with (nearly) +  // DesiredStackSpace available. +  noteBottomOfStack(); +    // FIXME: Take this as an argument, once all the APIs we used have moved to    // taking it as an input instead of hard-coding llvm::errs.    raw_ostream &OS = llvm::errs(); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 17b414556ca..d4be3e92420 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -22,6 +22,7 @@  #include "clang/AST/StmtCXX.h"  #include "clang/Basic/DiagnosticOptions.h"  #include "clang/Basic/PartialDiagnostic.h" +#include "clang/Basic/Stack.h"  #include "clang/Basic/TargetInfo.h"  #include "clang/Lex/HeaderSearch.h"  #include "clang/Lex/Preprocessor.h" @@ -386,6 +387,19 @@ Sema::~Sema() {    assert(DelayedTypos.empty() && "Uncorrected typos!");  } +void Sema::warnStackExhausted(SourceLocation Loc) { +  // Only warn about this once. +  if (!WarnedStackExhausted) { +    Diag(Loc, diag::warn_stack_exhausted); +    WarnedStackExhausted = true; +  } +} + +void Sema::runWithSufficientStackSpace(SourceLocation Loc, +                                       llvm::function_ref<void()> Fn) { +  clang::runWithSufficientStackSpace([&] { warnStackExhausted(Loc); }, Fn); +} +  /// makeUnavailableInSystemHeader - There is an error in the current  /// context.  If we're still in a system header, and we can plausibly  /// make the relevant declaration unavailable instead of erroring, do diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index ea72bcb259f..43e61d3429f 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4832,8 +4832,10 @@ bool Sema::CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,        //   default argument expression appears.        ContextRAII SavedContext(*this, FD);        LocalInstantiationScope Local(*this); -      Result = SubstInitializer(UninstExpr, MutiLevelArgList, -                                /*DirectInit*/false); +      runWithSufficientStackSpace(CallLoc, [&] { +        Result = SubstInitializer(UninstExpr, MutiLevelArgList, +                                  /*DirectInit*/false); +      });      }      if (Result.isInvalid())        return true; @@ -15175,6 +15177,17 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,    if (IsRecursiveCall && OdrUse == OdrUseContext::Used)      OdrUse = OdrUseContext::FormallyOdrUsed; +  // Trivial default constructors and destructors are never actually used. +  // FIXME: What about other special members? +  if (Func->isTrivial() && !Func->hasAttr<DLLExportAttr>() && +      OdrUse == OdrUseContext::Used) { +    if (auto *Constructor = dyn_cast<CXXConstructorDecl>(Func)) +      if (Constructor->isDefaultConstructor()) +        OdrUse = OdrUseContext::FormallyOdrUsed; +    if (isa<CXXDestructorDecl>(Func)) +      OdrUse = OdrUseContext::FormallyOdrUsed; +  } +    // C++20 [expr.const]p12:    //   A function [...] is needed for constant evaluation if it is [...] a    //   constexpr function that is named by an expression that is potentially @@ -15235,98 +15248,101 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,    // If we need a definition, try to create one.    if (NeedDefinition && !Func->getBody()) { -    if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(Func)) { -      Constructor = cast<CXXConstructorDecl>(Constructor->getFirstDecl()); -      if (Constructor->isDefaulted() && !Constructor->isDeleted()) { -        if (Constructor->isDefaultConstructor()) { -          if (Constructor->isTrivial() && -              !Constructor->hasAttr<DLLExportAttr>()) +    runWithSufficientStackSpace(Loc, [&] { +      if (CXXConstructorDecl *Constructor = +              dyn_cast<CXXConstructorDecl>(Func)) { +        Constructor = cast<CXXConstructorDecl>(Constructor->getFirstDecl()); +        if (Constructor->isDefaulted() && !Constructor->isDeleted()) { +          if (Constructor->isDefaultConstructor()) { +            if (Constructor->isTrivial() && +                !Constructor->hasAttr<DLLExportAttr>()) +              return; +            DefineImplicitDefaultConstructor(Loc, Constructor); +          } else if (Constructor->isCopyConstructor()) { +            DefineImplicitCopyConstructor(Loc, Constructor); +          } else if (Constructor->isMoveConstructor()) { +            DefineImplicitMoveConstructor(Loc, Constructor); +          } +        } else if (Constructor->getInheritedConstructor()) { +          DefineInheritingConstructor(Loc, Constructor); +        } +      } else if (CXXDestructorDecl *Destructor = +                     dyn_cast<CXXDestructorDecl>(Func)) { +        Destructor = cast<CXXDestructorDecl>(Destructor->getFirstDecl()); +        if (Destructor->isDefaulted() && !Destructor->isDeleted()) { +          if (Destructor->isTrivial() && !Destructor->hasAttr<DLLExportAttr>())              return; -          DefineImplicitDefaultConstructor(Loc, Constructor); -        } else if (Constructor->isCopyConstructor()) { -          DefineImplicitCopyConstructor(Loc, Constructor); -        } else if (Constructor->isMoveConstructor()) { -          DefineImplicitMoveConstructor(Loc, Constructor); +          DefineImplicitDestructor(Loc, Destructor);          } -      } else if (Constructor->getInheritedConstructor()) { -        DefineInheritingConstructor(Loc, Constructor); -      } -    } else if (CXXDestructorDecl *Destructor = -                   dyn_cast<CXXDestructorDecl>(Func)) { -      Destructor = cast<CXXDestructorDecl>(Destructor->getFirstDecl()); -      if (Destructor->isDefaulted() && !Destructor->isDeleted()) { -        if (Destructor->isTrivial() && !Destructor->hasAttr<DLLExportAttr>()) -          return; -        DefineImplicitDestructor(Loc, Destructor); +        if (Destructor->isVirtual() && getLangOpts().AppleKext) +          MarkVTableUsed(Loc, Destructor->getParent()); +      } else if (CXXMethodDecl *MethodDecl = dyn_cast<CXXMethodDecl>(Func)) { +        if (MethodDecl->isOverloadedOperator() && +            MethodDecl->getOverloadedOperator() == OO_Equal) { +          MethodDecl = cast<CXXMethodDecl>(MethodDecl->getFirstDecl()); +          if (MethodDecl->isDefaulted() && !MethodDecl->isDeleted()) { +            if (MethodDecl->isCopyAssignmentOperator()) +              DefineImplicitCopyAssignment(Loc, MethodDecl); +            else if (MethodDecl->isMoveAssignmentOperator()) +              DefineImplicitMoveAssignment(Loc, MethodDecl); +          } +        } else if (isa<CXXConversionDecl>(MethodDecl) && +                   MethodDecl->getParent()->isLambda()) { +          CXXConversionDecl *Conversion = +              cast<CXXConversionDecl>(MethodDecl->getFirstDecl()); +          if (Conversion->isLambdaToBlockPointerConversion()) +            DefineImplicitLambdaToBlockPointerConversion(Loc, Conversion); +          else +            DefineImplicitLambdaToFunctionPointerConversion(Loc, Conversion); +        } else if (MethodDecl->isVirtual() && getLangOpts().AppleKext) +          MarkVTableUsed(Loc, MethodDecl->getParent());        } -      if (Destructor->isVirtual() && getLangOpts().AppleKext) -        MarkVTableUsed(Loc, Destructor->getParent()); -    } else if (CXXMethodDecl *MethodDecl = dyn_cast<CXXMethodDecl>(Func)) { -      if (MethodDecl->isOverloadedOperator() && -          MethodDecl->getOverloadedOperator() == OO_Equal) { -        MethodDecl = cast<CXXMethodDecl>(MethodDecl->getFirstDecl()); -        if (MethodDecl->isDefaulted() && !MethodDecl->isDeleted()) { -          if (MethodDecl->isCopyAssignmentOperator()) -            DefineImplicitCopyAssignment(Loc, MethodDecl); -          else if (MethodDecl->isMoveAssignmentOperator()) -            DefineImplicitMoveAssignment(Loc, MethodDecl); -        } -      } else if (isa<CXXConversionDecl>(MethodDecl) && -                 MethodDecl->getParent()->isLambda()) { -        CXXConversionDecl *Conversion = -            cast<CXXConversionDecl>(MethodDecl->getFirstDecl()); -        if (Conversion->isLambdaToBlockPointerConversion()) -          DefineImplicitLambdaToBlockPointerConversion(Loc, Conversion); -        else -          DefineImplicitLambdaToFunctionPointerConversion(Loc, Conversion); -      } else if (MethodDecl->isVirtual() && getLangOpts().AppleKext) -        MarkVTableUsed(Loc, MethodDecl->getParent()); -    } -    // Implicit instantiation of function templates and member functions of -    // class templates. -    if (Func->isImplicitlyInstantiable()) { -      TemplateSpecializationKind TSK = -          Func->getTemplateSpecializationKindForInstantiation(); -      SourceLocation PointOfInstantiation = Func->getPointOfInstantiation(); -      bool FirstInstantiation = PointOfInstantiation.isInvalid(); -      if (FirstInstantiation) { -        PointOfInstantiation = Loc; -        Func->setTemplateSpecializationKind(TSK, PointOfInstantiation); -      } else if (TSK != TSK_ImplicitInstantiation) { -        // Use the point of use as the point of instantiation, instead of the -        // point of explicit instantiation (which we track as the actual point -        // of instantiation). This gives better backtraces in diagnostics. -        PointOfInstantiation = Loc; -      } +      // Implicit instantiation of function templates and member functions of +      // class templates. +      if (Func->isImplicitlyInstantiable()) { +        TemplateSpecializationKind TSK = +            Func->getTemplateSpecializationKindForInstantiation(); +        SourceLocation PointOfInstantiation = Func->getPointOfInstantiation(); +        bool FirstInstantiation = PointOfInstantiation.isInvalid(); +        if (FirstInstantiation) { +          PointOfInstantiation = Loc; +          Func->setTemplateSpecializationKind(TSK, PointOfInstantiation); +        } else if (TSK != TSK_ImplicitInstantiation) { +          // Use the point of use as the point of instantiation, instead of the +          // point of explicit instantiation (which we track as the actual point +          // of instantiation). This gives better backtraces in diagnostics. +          PointOfInstantiation = Loc; +        } -      if (FirstInstantiation || TSK != TSK_ImplicitInstantiation || -          Func->isConstexpr()) { -        if (isa<CXXRecordDecl>(Func->getDeclContext()) && -            cast<CXXRecordDecl>(Func->getDeclContext())->isLocalClass() && -            CodeSynthesisContexts.size()) -          PendingLocalImplicitInstantiations.push_back( -              std::make_pair(Func, PointOfInstantiation)); -        else if (Func->isConstexpr()) -          // Do not defer instantiations of constexpr functions, to avoid the -          // expression evaluator needing to call back into Sema if it sees a -          // call to such a function. -          InstantiateFunctionDefinition(PointOfInstantiation, Func); -        else { -          Func->setInstantiationIsPending(true); -          PendingInstantiations.push_back( -              std::make_pair(Func, PointOfInstantiation)); -          // Notify the consumer that a function was implicitly instantiated. -          Consumer.HandleCXXImplicitFunctionInstantiation(Func); +        if (FirstInstantiation || TSK != TSK_ImplicitInstantiation || +            Func->isConstexpr()) { +          if (isa<CXXRecordDecl>(Func->getDeclContext()) && +              cast<CXXRecordDecl>(Func->getDeclContext())->isLocalClass() && +              CodeSynthesisContexts.size()) +            PendingLocalImplicitInstantiations.push_back( +                std::make_pair(Func, PointOfInstantiation)); +          else if (Func->isConstexpr()) +            // Do not defer instantiations of constexpr functions, to avoid the +            // expression evaluator needing to call back into Sema if it sees a +            // call to such a function. +            InstantiateFunctionDefinition(PointOfInstantiation, Func); +          else { +            Func->setInstantiationIsPending(true); +            PendingInstantiations.push_back( +                std::make_pair(Func, PointOfInstantiation)); +            // Notify the consumer that a function was implicitly instantiated. +            Consumer.HandleCXXImplicitFunctionInstantiation(Func); +          } +        } +      } else { +        // Walk redefinitions, as some of them may be instantiable. +        for (auto i : Func->redecls()) { +          if (!i->isUsed(false) && i->isImplicitlyInstantiable()) +            MarkFunctionReferenced(Loc, i, MightBeOdrUse);          }        } -    } else { -      // Walk redefinitions, as some of them may be instantiable. -      for (auto i : Func->redecls()) { -        if (!i->isUsed(false) && i->isImplicitlyInstantiable()) -          MarkFunctionReferenced(Loc, i, MightBeOdrUse); -      } -    } +    });    }    // If this is the first "real" use, act on that. @@ -16493,7 +16509,9 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,          if (UsableInConstantExpr) {            // Do not defer instantiations of variables that could be used in a            // constant expression. -          SemaRef.InstantiateVariableDefinition(PointOfInstantiation, Var); +          SemaRef.runWithSufficientStackSpace(PointOfInstantiation, [&] { +            SemaRef.InstantiateVariableDefinition(PointOfInstantiation, Var); +          });          } else if (FirstInstantiation ||                     isa<VarTemplateSpecializationDecl>(Var)) {            // FIXME: For a specialization of a variable template, we don't diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 3aa38b27c51..83c475a5f1c 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -6242,8 +6242,11 @@ PerformConstructorInitialization(Sema &S,      // the definition for completely trivial constructors.      assert(Constructor->getParent() && "No parent class for constructor.");      if (Constructor->isDefaulted() && Constructor->isDefaultConstructor() && -        Constructor->isTrivial() && !Constructor->isUsed(false)) -      S.DefineImplicitDefaultConstructor(Loc, Constructor); +        Constructor->isTrivial() && !Constructor->isUsed(false)) { +      S.runWithSufficientStackSpace(Loc, [&] { +        S.DefineImplicitDefaultConstructor(Loc, Constructor); +      }); +    }    }    ExprResult CurInit((Expr *)nullptr); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 8bae219f5ad..16fc822e5dd 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -3088,8 +3088,11 @@ Sema::SpecialMemberOverloadResult Sema::LookupSpecialMember(CXXRecordDecl *RD,    SpecialMemberCache.InsertNode(Result, InsertPoint);    if (SM == CXXDestructor) { -    if (RD->needsImplicitDestructor()) -      DeclareImplicitDestructor(RD); +    if (RD->needsImplicitDestructor()) { +      runWithSufficientStackSpace(RD->getLocation(), [&] { +        DeclareImplicitDestructor(RD); +      }); +    }      CXXDestructorDecl *DD = RD->getDestructor();      assert(DD && "record without a destructor");      Result->setMethod(DD); @@ -3112,21 +3115,36 @@ Sema::SpecialMemberOverloadResult Sema::LookupSpecialMember(CXXRecordDecl *RD,    if (SM == CXXDefaultConstructor) {      Name = Context.DeclarationNames.getCXXConstructorName(CanTy);      NumArgs = 0; -    if (RD->needsImplicitDefaultConstructor()) -      DeclareImplicitDefaultConstructor(RD); +    if (RD->needsImplicitDefaultConstructor()) { +      runWithSufficientStackSpace(RD->getLocation(), [&] { +        DeclareImplicitDefaultConstructor(RD); +      }); +    }    } else {      if (SM == CXXCopyConstructor || SM == CXXMoveConstructor) {        Name = Context.DeclarationNames.getCXXConstructorName(CanTy); -      if (RD->needsImplicitCopyConstructor()) -        DeclareImplicitCopyConstructor(RD); -      if (getLangOpts().CPlusPlus11 && RD->needsImplicitMoveConstructor()) -        DeclareImplicitMoveConstructor(RD); +      if (RD->needsImplicitCopyConstructor()) { +        runWithSufficientStackSpace(RD->getLocation(), [&] { +          DeclareImplicitCopyConstructor(RD); +        }); +      } +      if (getLangOpts().CPlusPlus11 && RD->needsImplicitMoveConstructor()) { +        runWithSufficientStackSpace(RD->getLocation(), [&] { +          DeclareImplicitMoveConstructor(RD); +        }); +      }      } else {        Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); -      if (RD->needsImplicitCopyAssignment()) -        DeclareImplicitCopyAssignment(RD); -      if (getLangOpts().CPlusPlus11 && RD->needsImplicitMoveAssignment()) -        DeclareImplicitMoveAssignment(RD); +      if (RD->needsImplicitCopyAssignment()) { +        runWithSufficientStackSpace(RD->getLocation(), [&] { +          DeclareImplicitCopyAssignment(RD); +        }); +      } +      if (getLangOpts().CPlusPlus11 && RD->needsImplicitMoveAssignment()) { +        runWithSufficientStackSpace(RD->getLocation(), [&] { +          DeclareImplicitMoveAssignment(RD); +        }); +      }      }      if (ConstArg) @@ -3283,12 +3301,14 @@ CXXConstructorDecl *Sema::LookupMovingConstructor(CXXRecordDecl *Class,  DeclContext::lookup_result Sema::LookupConstructors(CXXRecordDecl *Class) {    // If the implicit constructors have not yet been declared, do so now.    if (CanDeclareSpecialMemberFunction(Class)) { -    if (Class->needsImplicitDefaultConstructor()) -      DeclareImplicitDefaultConstructor(Class); -    if (Class->needsImplicitCopyConstructor()) -      DeclareImplicitCopyConstructor(Class); -    if (getLangOpts().CPlusPlus11 && Class->needsImplicitMoveConstructor()) -      DeclareImplicitMoveConstructor(Class); +    runWithSufficientStackSpace(Class->getLocation(), [&] { +      if (Class->needsImplicitDefaultConstructor()) +        DeclareImplicitDefaultConstructor(Class); +      if (Class->needsImplicitCopyConstructor()) +        DeclareImplicitCopyConstructor(Class); +      if (getLangOpts().CPlusPlus11 && Class->needsImplicitMoveConstructor()) +        DeclareImplicitMoveConstructor(Class); +    });    }    CanQualType T = Context.getCanonicalType(Context.getTypeDeclType(Class)); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index d230ec90d77..00771e22461 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -20,6 +20,7 @@  #include "clang/Basic/Builtins.h"  #include "clang/Basic/LangOptions.h"  #include "clang/Basic/PartialDiagnostic.h" +#include "clang/Basic/Stack.h"  #include "clang/Basic/TargetInfo.h"  #include "clang/Sema/DeclSpec.h"  #include "clang/Sema/Lookup.h" diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index b55a232d26c..a75af62bf35 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4632,8 +4632,11 @@ bool Sema::DeduceReturnType(FunctionDecl *FD, SourceLocation Loc,        // We might need to deduce the return type by instantiating the definition        // of the operator() function. -      if (CallOp->getReturnType()->isUndeducedType()) -        InstantiateFunctionDefinition(Loc, CallOp); +      if (CallOp->getReturnType()->isUndeducedType()) { +        runWithSufficientStackSpace(Loc, [&] { +          InstantiateFunctionDefinition(Loc, CallOp); +        }); +      }      }      if (CallOp->isInvalidDecl()) @@ -4654,8 +4657,11 @@ bool Sema::DeduceReturnType(FunctionDecl *FD, SourceLocation Loc,      return false;    } -  if (FD->getTemplateInstantiationPattern()) -    InstantiateFunctionDefinition(Loc, FD); +  if (FD->getTemplateInstantiationPattern()) { +    runWithSufficientStackSpace(Loc, [&] { +      InstantiateFunctionDefinition(Loc, FD); +    }); +  }    bool StillUndeduced = FD->getReturnType()->isUndeducedType();    if (StillUndeduced && Diagnose && !FD->isInvalidDecl()) { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 973f564d305..d1ecefcc0c0 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -19,6 +19,7 @@  #include "clang/AST/Expr.h"  #include "clang/AST/PrettyDeclStackTrace.h"  #include "clang/Basic/LangOptions.h" +#include "clang/Basic/Stack.h"  #include "clang/Sema/DeclSpec.h"  #include "clang/Sema/Initialization.h"  #include "clang/Sema/Lookup.h" @@ -365,6 +366,11 @@ void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) {    if (!Ctx.isInstantiationRecord())      ++NonInstantiationEntries; + +  // Check to see if we're low on stack space. We can't do anything about this +  // from here, but we can at least warn the user. +  if (isStackNearlyExhausted()) +    warnStackExhausted(Ctx.PointOfInstantiation);  }  void Sema::popCodeSynthesisContext() { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index f340b68df64..6e4f9b99478 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3413,7 +3413,11 @@ Decl *Sema::SubstDecl(Decl *D, DeclContext *Owner,    if (D->isInvalidDecl())      return nullptr; -  return Instantiator.Visit(D); +  Decl *SubstD; +  runWithSufficientStackSpace(D->getLocation(), [&] { +    SubstD = Instantiator.Visit(D); +  }); +  return SubstD;  }  /// Instantiates a nested template parameter list in the current diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 401874edf2f..4b16a09307c 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -7729,7 +7729,9 @@ void Sema::completeExprArrayBound(Expr *E) {          auto *Def = Var->getDefinition();          if (!Def) {            SourceLocation PointOfInstantiation = E->getExprLoc(); -          InstantiateVariableDefinition(PointOfInstantiation, Var); +          runWithSufficientStackSpace(PointOfInstantiation, [&] { +            InstantiateVariableDefinition(PointOfInstantiation, Var); +          });            Def = Var->getDefinition();            // If we don't already have a point of instantiation, and we managed @@ -8067,9 +8069,11 @@ bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T,      } else if (auto *ClassTemplateSpec =              dyn_cast<ClassTemplateSpecializationDecl>(RD)) {        if (ClassTemplateSpec->getSpecializationKind() == TSK_Undeclared) { -        Diagnosed = InstantiateClassTemplateSpecialization( -            Loc, ClassTemplateSpec, TSK_ImplicitInstantiation, -            /*Complain=*/Diagnoser); +        runWithSufficientStackSpace(Loc, [&] { +          Diagnosed = InstantiateClassTemplateSpecialization( +              Loc, ClassTemplateSpec, TSK_ImplicitInstantiation, +              /*Complain=*/Diagnoser); +        });          Instantiated = true;        }      } else { @@ -8080,10 +8084,12 @@ bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T,          // This record was instantiated from a class within a template.          if (MSI->getTemplateSpecializationKind() !=              TSK_ExplicitSpecialization) { -          Diagnosed = InstantiateClass(Loc, RD, Pattern, -                                       getTemplateInstantiationArgs(RD), -                                       TSK_ImplicitInstantiation, -                                       /*Complain=*/Diagnoser); +          runWithSufficientStackSpace(Loc, [&] { +            Diagnosed = InstantiateClass(Loc, RD, Pattern, +                                         getTemplateInstantiationArgs(RD), +                                         TSK_ImplicitInstantiation, +                                         /*Complain=*/Diagnoser); +          });            Instantiated = true;          }        } diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt index f1591602ced..69986498974 100644 --- a/clang/test/CMakeLists.txt +++ b/clang/test/CMakeLists.txt @@ -26,7 +26,8 @@ llvm_canonicalize_cmake_booleans(    ENABLE_EXPERIMENTAL_NEW_PASS_MANAGER    HAVE_LIBZ    LLVM_ENABLE_PER_TARGET_RUNTIME_DIR -  LLVM_ENABLE_PLUGINS) +  LLVM_ENABLE_PLUGINS +  LLVM_ENABLE_THREADS)  configure_lit_site_cfg(    ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in diff --git a/clang/test/SemaTemplate/stack-exhaustion.cpp b/clang/test/SemaTemplate/stack-exhaustion.cpp new file mode 100644 index 00000000000..2ee518d37fe --- /dev/null +++ b/clang/test/SemaTemplate/stack-exhaustion.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -verify %s +// REQUIRES: thread_support + +// expected-warning@* 0-1{{stack nearly exhausted}} +// expected-note@* 0+{{}} + +template<int N> struct X : X<N-1> {}; +template<> struct X<0> {}; +X<1000> x; + +template<typename ...T> struct tuple {}; +template<typename ...T> auto f(tuple<T...> t) -> decltype(f(tuple<T...>(t))) {} // expected-error {{exceeded maximum depth}} +void g() { f(tuple<int, int>()); } + +int f(X<0>); +template<int N> auto f(X<N>) -> f(X<N-1>()); + +int k = f(X<1000>()); diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py index ca8a05dbe82..9ffe30ec50d 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -175,6 +175,9 @@ if lit.util.which('xmllint'):  if config.enable_backtrace:      config.available_features.add('backtrace') +if config.enable_threads: +    config.available_features.add('thread_support') +  # Check if we should allow outputs to console.  run_console_tests = int(lit_config.params.get('enable_console', '0'))  if run_console_tests != 0: diff --git a/clang/test/lit.site.cfg.py.in b/clang/test/lit.site.cfg.py.in index 2be5a97f8fe..520afab6af8 100644 --- a/clang/test/lit.site.cfg.py.in +++ b/clang/test/lit.site.cfg.py.in @@ -25,6 +25,7 @@ config.clang_examples = @CLANG_BUILD_EXAMPLES@  config.enable_shared = @ENABLE_SHARED@  config.enable_backtrace = @ENABLE_BACKTRACES@  config.enable_experimental_new_pass_manager = @ENABLE_EXPERIMENTAL_NEW_PASS_MANAGER@ +config.enable_threads = @LLVM_ENABLE_THREADS@  config.host_arch = "@HOST_ARCH@"  config.python_executable = "@PYTHON_EXECUTABLE@"  config.use_z3_solver = lit_config.params.get('USE_Z3_SOLVER', "@USE_Z3_SOLVER@") diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp index dfc96d308ab..01338d01c30 100644 --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -13,6 +13,7 @@  #include "clang/Driver/Driver.h"  #include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/Stack.h"  #include "clang/Driver/Compilation.h"  #include "clang/Driver/DriverDiagnostic.h"  #include "clang/Driver/Options.h" @@ -319,6 +320,7 @@ static int ExecuteCC1Tool(ArrayRef<const char *> argv, StringRef Tool) {  }  int main(int argc_, const char **argv_) { +  noteBottomOfStack();    llvm::InitLLVM X(argc_, argv_);    SmallVector<const char *, 256> argv(argv_, argv_ + argc_);  | 

