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.

llvm-svn: 66513
This commit is contained in:
Douglas Gregor 2009-03-10 00:06:19 +00:00
parent 99d2835099
commit fcd5db3bfa
7 changed files with 112 additions and 2 deletions

View 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;

View 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")

View 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; }

View 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);

View File

@ -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 @@ 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;

View File

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