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>);
+}