def err_template_arg_extra_parens : Error<
"non-type template argument cannot be surrounded by parentheses">;
+// C++ template specialization
+def err_template_spec_unknown_kind : Error<
+ "can only provide an explicit %select{<error>|<error>|specialization|"
+ "instantiation|instantiation}0 for a class template, function template, or "
+ "a member function, static data member, or member class of a class template">;
+def note_specialized_entity : Note<
+ "explicit %select{<error>|<error>|specialized|instantiated|instantiated}0 is "
+ "here">;
+def err_template_spec_decl_function_scope : Error<
+ "explicit %select{<error>|<error>|specialization|instantiation|"
+ "instantiation}0 of %1 in function scope">;
+def err_template_spec_decl_out_of_scope_global : Error<
+ "%select{class template|class template partial|function template|member "
+ "function|static data member|member class}0 specialization of %1 must "
+ "originally be declared in the global scope">;
+def err_template_spec_decl_out_of_scope : Error<
+ "%select{class template|class template partial|function template|member "
+ "function|static data member|member class}0 specialization of %1 must "
+ "originally be declared in namespace %2">;
+def err_template_spec_redecl_out_of_scope : Error<
+ "%select{class template|class template partial|function template|member "
+ "function|static data member|member class}0 specialization of %1 not in a "
+ "namespace enclosing %2">;
+def err_template_spec_redecl_global_scope : Error<
+ "%select{class template|class template partial|function template|member "
+ "function|static data member|member class}0 specialization of %1 must occur "
+ "at global scope">;
+
// C++ class template specializations and out-of-line definitions
def err_template_spec_needs_header : Error<
"template specialization requires 'template<>'">;
def err_template_qualified_declarator_no_match : Error<
"nested name specifier %0 for declaration does not refer into a class, "
"class template or class template partial specialization">;
-def err_template_spec_decl_out_of_scope_global : Error<
- "class template %select{|partial }0specialization of %1 must occur in the "
- "global scope">;
-def err_template_spec_decl_out_of_scope : Error<
- "class template %select{|partial }0specialization of %1 not in namespace %2">;
-def err_template_spec_decl_function_scope : Error<
- "%select{class template specialization|class template partial specialization|"
- "explicit instantiation}0 of %1 in function scope">;
-def err_template_spec_redecl_out_of_scope : Error<
- "%select{class template specialization|class template partial specialization|"
- "explicit instantiation}0 of %1 not in a namespace enclosing %2">;
-def err_template_spec_redecl_global_scope : Error<
- "%select{class template specialization|class template partial specialization|"
- "explicit instantiation}0 of %1 must occur at global scope">;
// C++ Class Template Partial Specialization
def err_default_arg_in_partial_spec : Error<
"arguments to identify a particular function template">;
def note_function_template_spec_matched : Note<
"function template matches specialization %0">;
-
+
// C++ Template Instantiation
def err_template_recursion_depth_exceeded : Error<
"recursive template instantiation exceeded maximum depth of %0">,
<< TemplateParams->getSourceRange();
}
-/// \brief Check whether a class template specialization or explicit
-/// instantiation in the current context is well-formed.
+/// \brief Determine what kind of template specialization the given declaration
+/// is.
+static TemplateSpecializationKind getTemplateSpecializationKind(NamedDecl *D) {
+ if (!D)
+ return TSK_Undeclared;
+
+ if (ClassTemplateSpecializationDecl *CTS
+ = dyn_cast<ClassTemplateSpecializationDecl>(D))
+ return CTS->getSpecializationKind();
+ if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D))
+ return Function->getTemplateSpecializationKind();
+
+ // FIXME: static data members!
+ // FIXME: member classes of class templates!
+ return TSK_Undeclared;
+}
+
+/// \brief Check whether a specialization or explicit instantiation is
+/// well-formed in the current context.
///
-/// This routine determines whether a class template specialization or
+/// This routine determines whether a template specialization or
/// explicit instantiation can be declared in the current context
-/// (C++ [temp.expl.spec]p2, C++0x [temp.explicit]p2) and emits
-/// appropriate diagnostics if there was an error. It returns true if
-// there was an error that we cannot recover from, and false otherwise.
-bool
-Sema::CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
- ClassTemplateSpecializationDecl *PrevDecl,
- SourceLocation TemplateNameLoc,
- SourceRange ScopeSpecifierRange,
- bool PartialSpecialization,
- bool ExplicitInstantiation) {
+/// (C++ [temp.expl.spec]p2, C++0x [temp.explicit]p2).
+///
+/// \param S the semantic analysis object for which this check is being
+/// performed.
+///
+/// \param Specialized the entity being specialized or instantiated, which
+/// may be a kind of template (class template, function template, etc.) or
+/// a member of a class template (member function, static data member,
+/// member class).
+///
+/// \param PrevDecl the previous declaration of this entity, if any.
+///
+/// \param Loc the location of the explicit specialization or instantiation of
+/// this entity.
+///
+/// \param IsPartialSpecialization whether this is a partial specialization of
+/// a class template.
+///
+/// \param TSK the kind of specialization or implicit instantiation being
+/// performed.
+///
+/// \returns true if there was an error that we cannot recover from, false
+/// otherwise.
+static bool CheckTemplateSpecializationScope(Sema &S,
+ NamedDecl *Specialized,
+ NamedDecl *PrevDecl,
+ SourceLocation Loc,
+ bool IsPartialSpecialization,
+ TemplateSpecializationKind TSK) {
+ // Keep these "kind" numbers in sync with the %select statements in the
+ // various diagnostics emitted by this routine.
+ int EntityKind = 0;
+ if (isa<ClassTemplateDecl>(Specialized))
+ EntityKind = IsPartialSpecialization? 1 : 0;
+ else if (isa<FunctionTemplateDecl>(Specialized))
+ EntityKind = 2;
+ else if (isa<CXXMethodDecl>(Specialized))
+ EntityKind = 3;
+ else if (isa<VarDecl>(Specialized))
+ EntityKind = 4;
+ else if (isa<RecordDecl>(Specialized))
+ EntityKind = 5;
+ else {
+ S.Diag(Loc, diag::err_template_spec_unknown_kind) << TSK;
+ S.Diag(Specialized->getLocation(), diag::note_specialized_entity) << TSK;
+ return true;
+ }
+
// C++ [temp.expl.spec]p2:
// An explicit specialization shall be declared in the namespace
// of which the template is a member, or, for member templates, in
// the explicit specialization was declared, or in a namespace
// that encloses the one in which the explicit specialization was
// declared.
- if (CurContext->getLookupContext()->isFunctionOrMethod()) {
- int Kind = ExplicitInstantiation? 2 : PartialSpecialization? 1 : 0;
- Diag(TemplateNameLoc, diag::err_template_spec_decl_function_scope)
- << Kind << ClassTemplate;
+ if (S.CurContext->getLookupContext()->isFunctionOrMethod()) {
+ S.Diag(Loc, diag::err_template_spec_decl_function_scope)
+ << TSK << Specialized;
return true;
}
-
- DeclContext *DC = CurContext->getEnclosingNamespaceContext();
- DeclContext *TemplateContext
- = ClassTemplate->getDeclContext()->getEnclosingNamespaceContext();
- if ((!PrevDecl || PrevDecl->getSpecializationKind() == TSK_Undeclared) &&
- !ExplicitInstantiation) {
- // There is no prior declaration of this entity, so this
- // specialization must be in the same context as the template
- // itself.
- if (DC != TemplateContext) {
- if (isa<TranslationUnitDecl>(TemplateContext))
- Diag(TemplateNameLoc, diag::err_template_spec_decl_out_of_scope_global)
- << PartialSpecialization
- << ClassTemplate << ScopeSpecifierRange;
- else if (isa<NamespaceDecl>(TemplateContext))
- Diag(TemplateNameLoc, diag::err_template_spec_decl_out_of_scope)
- << PartialSpecialization << ClassTemplate
- << cast<NamedDecl>(TemplateContext) << ScopeSpecifierRange;
-
- Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
+
+ bool ComplainedAboutScope = false;
+ DeclContext *SpecializedContext
+ = Specialized->getDeclContext()->getEnclosingNamespaceContext();
+ if (TSK == TSK_ExplicitSpecialization) {
+ DeclContext *DC = S.CurContext->getEnclosingNamespaceContext();
+ if ((!PrevDecl ||
+ getTemplateSpecializationKind(PrevDecl) == TSK_Undeclared ||
+ getTemplateSpecializationKind(PrevDecl) == TSK_ImplicitInstantiation)){
+ // There is no prior declaration of this entity, so this
+ // specialization must be in the same context as the template
+ // itself.
+ if (!DC->Equals(SpecializedContext)) {
+ if (isa<TranslationUnitDecl>(SpecializedContext))
+ S.Diag(Loc, diag::err_template_spec_decl_out_of_scope_global)
+ << EntityKind << Specialized;
+ else if (isa<NamespaceDecl>(SpecializedContext))
+ S.Diag(Loc, diag::err_template_spec_decl_out_of_scope)
+ << EntityKind << Specialized
+ << cast<NamedDecl>(SpecializedContext);
+
+ S.Diag(Specialized->getLocation(), diag::note_template_decl_here);
+ ComplainedAboutScope = true;
+ }
}
-
- return false;
}
-
- // We have a previous declaration of this entity. Make sure that
- // this redeclaration (or definition) occurs in an enclosing namespace.
- if (!CurContext->Encloses(TemplateContext)) {
- // FIXME: In C++98, we would like to turn these errors into warnings,
- // dependent on a -Wc++0x flag.
- bool SuppressedDiag = false;
- int Kind = ExplicitInstantiation? 2 : PartialSpecialization? 1 : 0;
- if (isa<TranslationUnitDecl>(TemplateContext)) {
- if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x)
- Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope)
- << Kind << ClassTemplate << ScopeSpecifierRange;
- else
- SuppressedDiag = true;
- } else if (isa<NamespaceDecl>(TemplateContext)) {
- if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x)
- Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope)
- << Kind << ClassTemplate
- << cast<NamedDecl>(TemplateContext) << ScopeSpecifierRange;
- else
- SuppressedDiag = true;
- }
-
- if (!SuppressedDiag)
- Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
+
+ // Make sure that this redeclaration (or definition) occurs in an enclosing
+ // namespace. We perform this check for explicit specializations and, in
+ // C++0x, for explicit instantiations as well (per DR275).
+ // FIXME: -Wc++0x should make these warnings.
+ // Note that HandleDeclarator() performs this check for explicit
+ // specializations of function templates, static data members, and member
+ // functions, so we skip the check here for those kinds of entities.
+ // FIXME: HandleDeclarator's diagnostics aren't quite as good, though.
+ // Should we refactor the check, so that it occurs later?
+ if (!ComplainedAboutScope && !S.CurContext->Encloses(SpecializedContext) &&
+ ((TSK == TSK_ExplicitSpecialization &&
+ !(isa<FunctionTemplateDecl>(Specialized) || isa<VarDecl>(Specialized) ||
+ isa<FunctionDecl>(Specialized))) ||
+ S.getLangOptions().CPlusPlus0x)) {
+ if (isa<TranslationUnitDecl>(SpecializedContext))
+ S.Diag(Loc, diag::err_template_spec_redecl_global_scope)
+ << EntityKind << Specialized;
+ else if (isa<NamespaceDecl>(SpecializedContext))
+ S.Diag(Loc, diag::err_template_spec_redecl_out_of_scope)
+ << EntityKind << Specialized
+ << cast<NamedDecl>(SpecializedContext);
+
+ S.Diag(Specialized->getLocation(), diag::note_template_decl_here);
}
-
+
+ // FIXME: check for specialization-after-instantiation errors and such.
+
return false;
}
-
+
/// \brief Check the non-type template arguments of a class template
/// partial specialization according to C++ [temp.class.spec]p9.
///
// Check whether we can declare a class template specialization in
// the current scope.
if (TUK != TUK_Friend &&
- CheckClassTemplateSpecializationScope(ClassTemplate, PrevDecl,
- TemplateNameLoc,
- SS.getRange(),
- isPartialSpecialization,
- /*ExplicitInstantiation=*/false))
+ CheckTemplateSpecializationScope(*this, ClassTemplate, PrevDecl,
+ TemplateNameLoc, isPartialSpecialization,
+ TSK_ExplicitSpecialization))
return true;
// The canonical type
// FIXME: Check if the prior specialization has a point of instantiation.
// If so, we have run afoul of C++ [temp.expl.spec]p6.
+ // Check the scope of this explicit specialization.
+ if (CheckTemplateSpecializationScope(*this,
+ Specialization->getPrimaryTemplate(),
+ Specialization, FD->getLocation(),
+ false, TSK_ExplicitSpecialization))
+ return true;
+
// Mark the prior declaration as an explicit specialization, so that later
// clients know that this is an explicit specialization.
// FIXME: Check for prior explicit instantiations?
Kind = ClassTemplate->getTemplatedDecl()->getTagKind();
}
- // C++0x [temp.explicit]p2:
- // [...] An explicit instantiation shall appear in an enclosing
- // namespace of its template. [...]
- //
- // This is C++ DR 275.
- if (CheckClassTemplateSpecializationScope(ClassTemplate, 0,
- TemplateNameLoc,
- SS.getRange(),
- /*PartialSpecialization=*/false,
- /*ExplicitInstantiation=*/true))
- return true;
-
+ TemplateSpecializationKind TSK
+ = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
+ : TSK_ExplicitInstantiationDeclaration;
+
// Translate the parser's template argument list in our AST format.
llvm::SmallVector<TemplateArgument, 16> TemplateArgs;
translateTemplateArguments(TemplateArgsIn, TemplateArgLocs, TemplateArgs);
ClassTemplateSpecializationDecl *PrevDecl
= ClassTemplate->getSpecializations().FindNodeOrInsertPos(ID, InsertPos);
+ // C++0x [temp.explicit]p2:
+ // [...] An explicit instantiation shall appear in an enclosing
+ // namespace of its template. [...]
+ //
+ // This is C++ DR 275.
+ if (CheckTemplateSpecializationScope(*this, ClassTemplate, PrevDecl,
+ TemplateNameLoc, false,
+ TSK))
+ return true;
+
ClassTemplateSpecializationDecl *Specialization = 0;
bool SpecializationRequiresInstantiation = true;
//
// This check comes when we actually try to perform the
// instantiation.
- TemplateSpecializationKind TSK
- = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
- : TSK_ExplicitInstantiationDeclaration;
if (SpecializationRequiresInstantiation)
InstantiateClassTemplateSpecialization(Specialization, TSK);
else // Instantiate the members of this class template specialization.
--- /dev/null
+// RUN: clang-cc -fsyntax-only -verify %s
+
+// This test creates cases where implicit instantiations of various entities
+// would cause a diagnostic, but provides expliict specializations for those
+// entities that avoid the diagnostic. The specializations are alternately
+// declarations and definitions, and the intent of this test is to verify
+// that we allow specializations only in the appropriate namespaces (and
+// nowhere else).
+struct NonDefaultConstructible {
+ NonDefaultConstructible(int);
+};
+
+
+// C++ [temp.expl.spec]p1:
+// An explicit specialization of any of the following:
+
+// -- function template
+namespace N0 {
+ template<typename T> void f0(T) { // expected-note{{here}}
+ T t;
+ }
+
+ template<> void f0(NonDefaultConstructible) { }
+
+ void test_f0(NonDefaultConstructible NDC) {
+ f0(NDC);
+ }
+
+ template<> void f0(int);
+ template<> void f0(long);
+}
+
+template<> void N0::f0(int) { } // okay
+
+namespace N1 {
+ template<> void N0::f0(long) { } // expected-error{{not in a namespace enclosing}}
+}
+
+template<> void N0::f0(double) { } // expected-error{{originally be declared}}
+
+// FIXME: update the remainder of this test to check for scopes properly.
+// -- class template
+template<typename T>
+struct X0 {
+ static T member;
+
+ void f1(T t) {
+ t = 17;
+ }
+
+ struct Inner : public T { };
+
+ template<typename U>
+ struct InnerTemplate : public T { };
+
+ template<typename U>
+ void ft1(T t, U u);
+};
+
+template<typename T>
+template<typename U>
+void X0<T>::ft1(T t, U u) {
+ t = u;
+}
+
+template<typename T> T X0<T>::member;
+
+template<> struct X0<void> { };
+X0<void> test_X0;
+
+
+// -- member function of a class template
+template<> void X0<void*>::f1(void *) { }
+
+void test_spec(X0<void*> xvp, void *vp) {
+ xvp.f1(vp);
+}
+
+// -- static data member of a class template
+template<>
+NonDefaultConstructible X0<NonDefaultConstructible>::member = 17;
+
+NonDefaultConstructible &get_static_member() {
+ return X0<NonDefaultConstructible>::member;
+}
+
+// -- member class of a class template
+template<>
+struct X0<void*>::Inner { };
+
+X0<void*>::Inner inner0;
+
+// -- member class template of a class template
+template<>
+template<>
+struct X0<void*>::InnerTemplate<int> { };
+
+X0<void*>::InnerTemplate<int> inner_template0;
+
+// -- member function template of a class template
+template<>
+template<>
+void X0<void*>::ft1(void*, const void*) { }
+
+void test_func_template(X0<void *> xvp, void *vp, const void *cvp) {
+ xvp.ft1(vp, cvp);
+}
+
+// example from the standard:
+template<class T> class stream;
+template<> class stream<char> { /* ... */ };
+template<class T> class Array { /* ... */ };
+template<class T> void sort(Array<T>& v) { /* ... */ }
+template<> void sort<char*>(Array<char*>&) ;