Limit the template instantiation depth to some user-configurable value
(default: 99). Beyond this limit, produce an error and consider the
current template instantiation a failure.

The stack we're building to track the instantiations will, eventually,
be used to produce instantiation backtraces from diagnostics within
template instantiation. However, we're not quite there yet.

This adds a new Clang driver option -ftemplate-depth=NNN, which should
eventually be generated from the GCC command-line operation
-ftemplate-depth-NNN (note the '-' rather than the '='!). I did not
make the driver changes to do this mapping.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66513 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/Driver/clang.cpp b/Driver/clang.cpp
index 59c3799..72d2b9c 100644
--- a/Driver/clang.cpp
+++ b/Driver/clang.cpp
@@ -515,7 +515,10 @@
 TargetFeatures("mattr", llvm::cl::CommaSeparated,
         llvm::cl::desc("Target specific attributes (-mattr=help for details)"));
 
-
+static llvm::cl::opt<unsigned>
+TemplateDepth("ftemplate-depth", llvm::cl::init(99),
+              llvm::cl::desc("Maximum depth of recursive template "
+                             "instantiation"));
 
 // FIXME: add:
 //   -fdollars-in-identifiers
@@ -642,6 +645,8 @@
 
   Options.MathErrno = MathErrno;
 
+  Options.InstantiationDepth = TemplateDepth;
+
   // Override the default runtime if the user requested it.
   if (NeXTRuntime)
     Options.NeXTRuntime = 1;
diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def
index 17eb027..543391f 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.def
+++ b/include/clang/Basic/DiagnosticSemaKinds.def
@@ -640,6 +640,11 @@
      "class template specialization of %0 must occur in at global scope")
 
 // C++ Template Instantiation
+DIAG(err_template_recursion_depth_exceeded, ERROR,
+     "recursive template instantiation exceeded maximum depth of %0")
+DIAG(note_template_recursion_depth, NOTE,
+     "use -ftemplate-depth=N to increase recursive template "
+     "instantiation depth")
 DIAG(err_template_implicit_instantiate_undefined, ERROR,
      "implicit instantiation of undefined template %0")
 
diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h
index 0124b04..fcfe0ca 100644
--- a/include/clang/Basic/LangOptions.h
+++ b/include/clang/Basic/LangOptions.h
@@ -62,6 +62,7 @@
                    // this enum as unsigned because MSVC insists on making enums
                    // signed.  Set/Query this value using accessors.  
 public:  
+  unsigned InstantiationDepth;    // Maximum template instantiation depth.
 
   enum GCMode { NonGC, GCOnly, HybridGC };
   
@@ -80,6 +81,8 @@
     Blocks = 0;
     EmitAllDecls = 0;
     MathErrno = 1;
+
+    InstantiationDepth = 99;
   }
   
   GCMode getGCMode() const { return (GCMode) GC; }
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 83be895..1acd6b2 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -1664,6 +1664,61 @@
   //===--------------------------------------------------------------------===//
   // C++ Template Instantiation
   //
+
+  /// \brief A template instantiation that is currently in progress.
+  struct ActiveTemplateInstantiation {
+    /// \brief The point of instantiation within the source code.
+    SourceLocation PointOfInstantiation;
+
+    /// \brief The entity that is being instantiated.
+    ClassTemplateSpecializationDecl *Entity;
+
+    /// \brief The source range that covers the construct that cause
+    /// the instantiation, e.g., the template-id that causes a class
+    /// template instantiation.
+    SourceRange InstantiationRange;
+  };
+
+  /// \brief List of active template instantiations.
+  ///
+  /// This vector is treated as a stack. As one template instantiation
+  /// requires another template instantiation, additional
+  /// instantiations are pushed onto the stack up to a
+  /// user-configurable limit LangOptions::InstantiationDepth.
+  llvm::SmallVector<ActiveTemplateInstantiation, 16> 
+    ActiveTemplateInstantiations;
+
+  /// \brief A stack object to be created when performing template
+  /// instantiation.
+  ///
+  /// Construction of an object of type \c InstantiatingTemplate
+  /// pushes the current instantiation onto the stack of active
+  /// instantiations. If the size of this stack exceeds the maximum
+  /// number of recursive template instantiations, construction
+  /// produces an error and evaluates true.
+  ///
+  /// Destruction of this object will pop the named instantiation off
+  /// the stack.
+  struct InstantiatingTemplate {
+    InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+                          ClassTemplateSpecializationDecl *Entity,
+                          SourceRange InstantiationRange = SourceRange());
+    ~InstantiatingTemplate();
+
+    /// \brief Determines whether we have exceeded the maximum
+    /// recursive template instantiations.
+    operator bool() const { return Invalid; }
+
+  private:
+    Sema &SemaRef;
+    bool Invalid;
+
+    InstantiatingTemplate(const InstantiatingTemplate&); // not implemented
+
+    InstantiatingTemplate& 
+    operator=(const InstantiatingTemplate&); // not implemented
+  };
+
   QualType InstantiateType(QualType T, const TemplateArgument *TemplateArgs,
                            unsigned NumTemplateArgs,
                            SourceLocation Loc, DeclarationName Entity);
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index e9e2e3c..b708598 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -351,7 +351,7 @@
   //   The class-name in a base-specifier shall not be an incompletely
   //   defined class.
   if (RequireCompleteType(BaseLoc, BaseType, diag::err_incomplete_base_class,
-                             SpecifierRange))
+                          SpecifierRange))
     return 0;
 
   // If the base class is polymorphic, the new one is, too.
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 71bba49..0990057 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -21,6 +21,35 @@
 
 using namespace clang;
 
+Sema::InstantiatingTemplate::
+InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+                      ClassTemplateSpecializationDecl *Entity,
+                      SourceRange InstantiationRange)
+  :  SemaRef(SemaRef) {
+  if (SemaRef.ActiveTemplateInstantiations.size() 
+        > SemaRef.getLangOptions().InstantiationDepth) {
+    SemaRef.Diag(PointOfInstantiation, 
+                 diag::err_template_recursion_depth_exceeded)
+      << SemaRef.getLangOptions().InstantiationDepth
+      << InstantiationRange;
+    SemaRef.Diag(PointOfInstantiation, diag::note_template_recursion_depth)
+      << SemaRef.getLangOptions().InstantiationDepth;
+    Invalid = true;
+  } else {
+    ActiveTemplateInstantiation Inst;
+    Inst.PointOfInstantiation = PointOfInstantiation;
+    Inst.Entity = Entity;
+    Inst.InstantiationRange = InstantiationRange;
+    SemaRef.ActiveTemplateInstantiations.push_back(Inst);
+    Invalid = false;
+  }
+}
+
+Sema::InstantiatingTemplate::~InstantiatingTemplate() {
+  if (!Invalid)
+    SemaRef.ActiveTemplateInstantiations.pop_back();
+}
+
 //===----------------------------------------------------------------------===/
 // Template Instantiation for Types
 //===----------------------------------------------------------------------===/
@@ -526,6 +555,11 @@
 
   bool Invalid = false;
   
+  InstantiatingTemplate Inst(*this, ClassTemplateSpec->getLocation(),
+                             ClassTemplateSpec);
+  if (Inst)
+    return true;
+
   // Enter the scope of this instantiation. We don't use
   // PushDeclContext because we don't have a scope.
   DeclContext *PreviousContext = CurContext;
diff --git a/test/SemaTemplate/instantiation-depth.cpp b/test/SemaTemplate/instantiation-depth.cpp
new file mode 100644
index 0000000..3b8acf2
--- /dev/null
+++ b/test/SemaTemplate/instantiation-depth.cpp
@@ -0,0 +1,8 @@
+// RUN: clang -fsyntax-only -ftemplate-depth=5 -verify %s
+
+template<typename T> struct X : X<T*> { }; // expected-error{{recursive template instantiation exceeded maximum depth of 5}} \
+// expected-note{{use -ftemplate-depth=N to increase recursive template instantiation depth}}
+
+void test() { 
+  (void)sizeof(X<int>);
+}