]> granicus.if.org Git - clang/commitdiff
Limit the template instantiation depth to some user-configurable value
authorDouglas Gregor <dgregor@apple.com>
Tue, 10 Mar 2009 00:06:19 +0000 (00:06 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 10 Mar 2009 00:06:19 +0000 (00:06 +0000)
(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

Driver/clang.cpp
include/clang/Basic/DiagnosticSemaKinds.def
include/clang/Basic/LangOptions.h
lib/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaTemplateInstantiate.cpp
test/SemaTemplate/instantiation-depth.cpp [new file with mode: 0644]

index 59c3799023f73c4baad858d3c3782d02feea6600..72d2b9c1db75d3814b8e35189328558ae514bdd1 100644 (file)
@@ -515,7 +515,10 @@ static llvm::cl::list<std::string>
 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 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK,
 
   Options.MathErrno = MathErrno;
 
+  Options.InstantiationDepth = TemplateDepth;
+
   // Override the default runtime if the user requested it.
   if (NeXTRuntime)
     Options.NeXTRuntime = 1;
index 17eb0270f166da08c22ae9cb33188f1070279ab4..543391f292f8a14c43c174a907626f8752bf1e12 100644 (file)
@@ -640,6 +640,11 @@ DIAG(err_template_spec_redecl_global_scope, ERROR,
      "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")
 
index 0124b04aa920d6bf4f8e9298f547b57c554ce538..fcfe0ca1e93eaf0687fc4ef3e544ee6c7370cf47 100644 (file)
@@ -62,6 +62,7 @@ private:
                    // 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 @@ public:
     Blocks = 0;
     EmitAllDecls = 0;
     MathErrno = 1;
+
+    InstantiationDepth = 99;
   }
   
   GCMode getGCMode() const { return (GCMode) GC; }
index 83be895909077c47ec25d3b073d808d2fae86bb2..1acd6b2a85cae20e8304ad1a9932b024606a859e 100644 (file)
@@ -1664,6 +1664,61 @@ public:
   //===--------------------------------------------------------------------===//
   // 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);
index e9e2e3c2acec2a2f4834449ad41f23e393673f93..b708598c620ffb6c1cccce4494cac8f96802e724 100644 (file)
@@ -351,7 +351,7 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class,
   //   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.
index 71bba49af9d5d8272fa9229af57036679fd0d3f0..0990057df4f16e375f3182292222627493e2d307 100644 (file)
 
 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 @@ Sema::InstantiateClassTemplateSpecialization(
 
   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 (file)
index 0000000..3b8acf2
--- /dev/null
@@ -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>);
+}