diff options
-rw-r--r-- | Driver/clang.cpp | 7 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.def | 5 | ||||
-rw-r--r-- | include/clang/Basic/LangOptions.h | 3 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 55 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateInstantiate.cpp | 34 | ||||
-rw-r--r-- | test/SemaTemplate/instantiation-depth.cpp | 8 |
7 files changed, 112 insertions, 2 deletions
diff --git a/Driver/clang.cpp b/Driver/clang.cpp index 59c3799023..72d2b9c1db 100644 --- a/Driver/clang.cpp +++ b/Driver/clang.cpp @@ -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; diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 17eb0270f1..543391f292 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -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") diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index 0124b04aa9..fcfe0ca1e9 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -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; } diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 83be895909..1acd6b2a85 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index e9e2e3c2ac..b708598c62 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -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. diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 71bba49af9..0990057df4 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 @@ 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 index 0000000000..3b8acf20dd --- /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>); +} |