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:
parent
99d2835099
commit
fcd5db3bfa
|
@ -515,7 +515,10 @@ static llvm::cl::list<std::string>
|
||||||
TargetFeatures("mattr", llvm::cl::CommaSeparated,
|
TargetFeatures("mattr", llvm::cl::CommaSeparated,
|
||||||
llvm::cl::desc("Target specific attributes (-mattr=help for details)"));
|
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:
|
// FIXME: add:
|
||||||
// -fdollars-in-identifiers
|
// -fdollars-in-identifiers
|
||||||
|
@ -642,6 +645,8 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK,
|
||||||
|
|
||||||
Options.MathErrno = MathErrno;
|
Options.MathErrno = MathErrno;
|
||||||
|
|
||||||
|
Options.InstantiationDepth = TemplateDepth;
|
||||||
|
|
||||||
// Override the default runtime if the user requested it.
|
// Override the default runtime if the user requested it.
|
||||||
if (NeXTRuntime)
|
if (NeXTRuntime)
|
||||||
Options.NeXTRuntime = 1;
|
Options.NeXTRuntime = 1;
|
||||||
|
|
|
@ -640,6 +640,11 @@ DIAG(err_template_spec_redecl_global_scope, ERROR,
|
||||||
"class template specialization of %0 must occur in at global scope")
|
"class template specialization of %0 must occur in at global scope")
|
||||||
|
|
||||||
// C++ Template Instantiation
|
// 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,
|
DIAG(err_template_implicit_instantiate_undefined, ERROR,
|
||||||
"implicit instantiation of undefined template %0")
|
"implicit instantiation of undefined template %0")
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ private:
|
||||||
// this enum as unsigned because MSVC insists on making enums
|
// this enum as unsigned because MSVC insists on making enums
|
||||||
// signed. Set/Query this value using accessors.
|
// signed. Set/Query this value using accessors.
|
||||||
public:
|
public:
|
||||||
|
unsigned InstantiationDepth; // Maximum template instantiation depth.
|
||||||
|
|
||||||
enum GCMode { NonGC, GCOnly, HybridGC };
|
enum GCMode { NonGC, GCOnly, HybridGC };
|
||||||
|
|
||||||
|
@ -80,6 +81,8 @@ public:
|
||||||
Blocks = 0;
|
Blocks = 0;
|
||||||
EmitAllDecls = 0;
|
EmitAllDecls = 0;
|
||||||
MathErrno = 1;
|
MathErrno = 1;
|
||||||
|
|
||||||
|
InstantiationDepth = 99;
|
||||||
}
|
}
|
||||||
|
|
||||||
GCMode getGCMode() const { return (GCMode) GC; }
|
GCMode getGCMode() const { return (GCMode) GC; }
|
||||||
|
|
|
@ -1664,6 +1664,61 @@ public:
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
// C++ Template Instantiation
|
// 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,
|
QualType InstantiateType(QualType T, const TemplateArgument *TemplateArgs,
|
||||||
unsigned NumTemplateArgs,
|
unsigned NumTemplateArgs,
|
||||||
SourceLocation Loc, DeclarationName Entity);
|
SourceLocation Loc, DeclarationName Entity);
|
||||||
|
|
|
@ -21,6 +21,35 @@
|
||||||
|
|
||||||
using namespace clang;
|
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
|
// Template Instantiation for Types
|
||||||
//===----------------------------------------------------------------------===/
|
//===----------------------------------------------------------------------===/
|
||||||
|
@ -526,6 +555,11 @@ Sema::InstantiateClassTemplateSpecialization(
|
||||||
|
|
||||||
bool Invalid = false;
|
bool Invalid = false;
|
||||||
|
|
||||||
|
InstantiatingTemplate Inst(*this, ClassTemplateSpec->getLocation(),
|
||||||
|
ClassTemplateSpec);
|
||||||
|
if (Inst)
|
||||||
|
return true;
|
||||||
|
|
||||||
// Enter the scope of this instantiation. We don't use
|
// Enter the scope of this instantiation. We don't use
|
||||||
// PushDeclContext because we don't have a scope.
|
// PushDeclContext because we don't have a scope.
|
||||||
DeclContext *PreviousContext = CurContext;
|
DeclContext *PreviousContext = CurContext;
|
||||||
|
|
|
@ -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>);
|
||||||
|
}
|
Loading…
Reference in New Issue