From: Douglas Gregor Date: Wed, 14 Oct 2009 23:41:34 +0000 (+0000) Subject: Additional semantic checking for explicit template instantiations, X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=558c03222c77873a934b002073667a3c971fe8a9;p=clang Additional semantic checking for explicit template instantiations, focusing on the scope- and qualifier-related semantic requirements in C++ [temp.explicit]p2. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@84154 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index d799bb52b9..4845314743 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1085,6 +1085,9 @@ def note_nontemplate_decl_here : Note< "non-templated declaration is here">; def err_explicit_instantiation_out_of_scope : Error< "explicit instantiation of %0 not in a namespace enclosing %1">; +def err_explicit_instantiation_must_be_global : Error< + "explicit instantiation of %0 must occur at global scope">; + def err_explicit_instantiation_requires_name : Error< "explicit instantiation declaration requires a name">; def err_explicit_instantiation_of_typedef : Error< @@ -1106,6 +1109,12 @@ def note_explicit_instantiation_candidate : Note< "explicit instantiation candidate function template here %0">; def err_explicit_instantiation_inline : Error< "explicit instantiation cannot be 'inline'">; +def err_explicit_instantiation_without_qualified_id : Error< + "qualifier in explicit instantiation of %q0 requires a template-id">; +def err_explicit_instantiation_without_qualified_id_quals : Error< + "qualifier in explicit instantiation of '%0%1' requires a template-id">; +def err_explicit_instantiation_unqualified_wrong_namespace : Error< + "explicit instantiation of %q0 must occur in %1">; // C++ typename-specifiers def err_typename_nested_not_found : Error<"no type named %0 in %1">; diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index ce76f4fecc..22c0e2975b 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2421,7 +2421,7 @@ static TemplateSpecializationKind getTemplateSpecializationKind(NamedDecl *D) { /// \param IsPartialSpecialization whether this is a partial specialization of /// a class template. /// -/// \param TSK the kind of specialization or implicit instantiation being +/// \param TSK the kind of specialization or explicit instantiation being /// performed. /// /// \returns true if there was an error that we cannot recover from, false @@ -3325,6 +3325,68 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl) { return false; } +/// \brief Check the scope of an explicit instantiation. +static void CheckExplicitInstantiationScope(Sema &S, NamedDecl *D, + SourceLocation InstLoc, + bool WasQualifiedName) { + DeclContext *ExpectedContext + = D->getDeclContext()->getEnclosingNamespaceContext()->getLookupContext(); + DeclContext *CurContext = S.CurContext->getLookupContext(); + + // C++0x [temp.explicit]p2: + // An explicit instantiation shall appear in an enclosing namespace of its + // template. + // + // This is DR275, which we do not retroactively apply to C++98/03. + if (S.getLangOptions().CPlusPlus0x && + !CurContext->Encloses(ExpectedContext)) { + if (NamespaceDecl *NS = dyn_cast(ExpectedContext)) + S.Diag(InstLoc, diag::err_explicit_instantiation_out_of_scope) + << D << NS; + else + S.Diag(InstLoc, diag::err_explicit_instantiation_must_be_global) + << D; + S.Diag(D->getLocation(), diag::note_explicit_instantiation_here); + return; + } + + // C++0x [temp.explicit]p2: + // If the name declared in the explicit instantiation is an unqualified + // name, the explicit instantiation shall appear in the namespace where + // its template is declared or, if that namespace is inline (7.3.1), any + // namespace from its enclosing namespace set. + if (WasQualifiedName) + return; + + if (CurContext->Equals(ExpectedContext)) + return; + + S.Diag(InstLoc, diag::err_explicit_instantiation_unqualified_wrong_namespace) + << D << ExpectedContext; + S.Diag(D->getLocation(), diag::note_explicit_instantiation_here); +} + +/// \brief Determine whether the given scope specifier has a template-id in it. +static bool ScopeSpecifierHasTemplateId(const CXXScopeSpec &SS) { + if (!SS.isSet()) + return false; + + // C++0x [temp.explicit]p2: + // If the explicit instantiation is for a member function, a member class + // or a static data member of a class template specialization, the name of + // the class template specialization in the qualified-id for the member + // name shall be a simple-template-id. + // + // C++98 has the same restriction, just worded differently. + for (NestedNameSpecifier *NNS = (NestedNameSpecifier *)SS.getScopeRep(); + NNS; NNS = NNS->getPrefix()) + if (Type *T = NNS->getAsType()) + if (isa(T)) + return true; + + return false; +} + // Explicit instantiation of a class template specialization // FIXME: Implement extern template semantics Sema::DeclResult @@ -3367,6 +3429,10 @@ Sema::ActOnExplicitInstantiation(Scope *S, Kind = ClassTemplate->getTemplatedDecl()->getTagKind(); } + // C++0x [temp.explicit]p2: + // There are two forms of explicit instantiation: an explicit instantiation + // definition and an explicit instantiation declaration. An explicit + // instantiation declaration begins with the extern keyword. [...] TemplateSpecializationKind TSK = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition : TSK_ExplicitInstantiationDeclaration; @@ -3404,10 +3470,8 @@ Sema::ActOnExplicitInstantiation(Scope *S, // namespace of its template. [...] // // This is C++ DR 275. - if (CheckTemplateSpecializationScope(*this, ClassTemplate, PrevDecl, - TemplateNameLoc, false, - TSK)) - return true; + CheckExplicitInstantiationScope(*this, ClassTemplate, TemplateNameLoc, + SS.isSet()); ClassTemplateSpecializationDecl *Specialization = 0; @@ -3563,7 +3627,7 @@ Sema::ActOnExplicitInstantiation(Scope *S, if (Tag->isInvalidDecl()) return true; - + CXXRecordDecl *Record = cast(Tag); CXXRecordDecl *Pattern = Record->getInstantiatedFromMemberClass(); if (!Pattern) { @@ -3573,7 +3637,20 @@ Sema::ActOnExplicitInstantiation(Scope *S, return true; } - // What kind of explicit instantiation? (for C++0x, GNU extern templates). + // C++0x [temp.explicit]p2: + // If the explicit instantiation is for a class or member class, the + // elaborated-type-specifier in the declaration shall include a + // simple-template-id. + // + // C++98 has the same restriction, just worded differently. + if (!ScopeSpecifierHasTemplateId(SS)) + Diag(TemplateLoc, diag::err_explicit_instantiation_without_qualified_id) + << Record << SS.getRange(); + + // C++0x [temp.explicit]p2: + // There are two forms of explicit instantiation: an explicit instantiation + // definition and an explicit instantiation declaration. An explicit + // instantiation declaration begins with the extern keyword. [...] TemplateSpecializationKind TSK = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition : TSK_ExplicitInstantiationDeclaration; @@ -3583,11 +3660,7 @@ Sema::ActOnExplicitInstantiation(Scope *S, // namespace of its template. [...] // // This is C++ DR 275. - if (CheckTemplateSpecializationScope(*this, Record, - Record->getPreviousDeclaration(), - NameLoc, false, - TSK)) - return true; + CheckExplicitInstantiationScope(*this, Record, NameLoc, true); if (!Record->getDefinition(Context)) { // If the class has a definition, instantiate it (and all of its @@ -3655,11 +3728,14 @@ Sema::DeclResult Sema::ActOnExplicitInstantiation(Scope *S, // FIXME: check for constexpr specifier. - // Determine what kind of explicit instantiation we have. + // C++0x [temp.explicit]p2: + // There are two forms of explicit instantiation: an explicit instantiation + // definition and an explicit instantiation declaration. An explicit + // instantiation declaration begins with the extern keyword. [...] TemplateSpecializationKind TSK = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition : TSK_ExplicitInstantiationDeclaration; - + LookupResult Previous; LookupParsedName(Previous, S, &D.getCXXScopeSpec(), Name, LookupOrdinaryName); @@ -3696,6 +3772,21 @@ Sema::DeclResult Sema::ActOnExplicitInstantiation(Scope *S, return true; } + // C++0x [temp.explicit]p2: + // If the explicit instantiation is for a member function, a member class + // or a static data member of a class template specialization, the name of + // the class template specialization in the qualified-id for the member + // name shall be a simple-template-id. + // + // C++98 has the same restriction, just worded differently. + if (!ScopeSpecifierHasTemplateId(D.getCXXScopeSpec())) + Diag(D.getIdentifierLoc(), + diag::err_explicit_instantiation_without_qualified_id) + << Prev << D.getCXXScopeSpec().getRange(); + + // Check the scope of this explicit instantiation. + CheckExplicitInstantiationScope(*this, Prev, D.getIdentifierLoc(), true); + // Instantiate static data member. // FIXME: Check for prior specializations and such. Prev->setTemplateSpecializationKind(TSK); @@ -3806,6 +3897,29 @@ Sema::DeclResult Sema::ActOnExplicitInstantiation(Scope *S, break; } + // Check the scope of this explicit instantiation. + FunctionTemplateDecl *FunTmpl = Specialization->getPrimaryTemplate(); + + // C++0x [temp.explicit]p2: + // If the explicit instantiation is for a member function, a member class + // or a static data member of a class template specialization, the name of + // the class template specialization in the qualified-id for the member + // name shall be a simple-template-id. + // + // C++98 has the same restriction, just worded differently. + if (D.getKind() != Declarator::DK_TemplateId && !FunTmpl && + D.getCXXScopeSpec().isSet() && + !ScopeSpecifierHasTemplateId(D.getCXXScopeSpec())) + Diag(D.getIdentifierLoc(), + diag::err_explicit_instantiation_without_qualified_id) + << Specialization << D.getCXXScopeSpec().getRange(); + + CheckExplicitInstantiationScope(*this, + FunTmpl? (NamedDecl *)FunTmpl + : Specialization->getInstantiatedFromMemberFunction(), + D.getIdentifierLoc(), + D.getCXXScopeSpec().isSet()); + // FIXME: Create some kind of ExplicitInstantiationDecl here. return DeclPtrTy(); } diff --git a/test/CXX/temp/temp.spec/temp.explicit/p2.cpp b/test/CXX/temp/temp.spec/temp.explicit/p2.cpp new file mode 100644 index 0000000000..aee0e5d19d --- /dev/null +++ b/test/CXX/temp/temp.spec/temp.explicit/p2.cpp @@ -0,0 +1,43 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +// Example from the standard +template class Array { void mf() { } }; + +template class Array; +template void Array::mf(); +template void sort(Array& v) { /* ... */ } +template void sort(Array&); +namespace N { + template void f(T&) { } +} +template void N::f(int&); + + +template +struct X0 { + struct Inner {}; + void f() { } + static T value; +}; + +template +T X0::value = 17; + +typedef X0 XInt; + +template struct XInt::Inner; // expected-error{{template-id}} +template void XInt::f(); // expected-error{{template-id}} +template int XInt::value; // expected-error{{template-id}} + +namespace N { + template + struct X1 { // expected-note{{explicit instantiation refers here}} + }; + + template + void f1(T); // expected-note{{explicit instantiation refers here}} +} +using namespace N; + +template struct X1; // expected-error{{must occur in}} +template void f1(int); // expected-error{{must occur in}} diff --git a/test/SemaTemplate/explicit-instantiation.cpp b/test/SemaTemplate/explicit-instantiation.cpp index b9a4ad282b..b04639f6e7 100644 --- a/test/SemaTemplate/explicit-instantiation.cpp +++ b/test/SemaTemplate/explicit-instantiation.cpp @@ -14,19 +14,22 @@ struct X0 { T f0(T x) { return x + 1; // expected-error{{invalid operands}} } - T* f0(T*, T*); + T* f0(T*, T*) { return T(); } template - T f0(T, U); + T f0(T, U) { return T(); } }; +template +T X0::value; // expected-error{{no matching constructor}} + template int X0::value; struct NotDefaultConstructible { NotDefaultConstructible(int); }; -template NotDefaultConstructible X0::value; +template NotDefaultConstructible X0::value; // expected-note{{instantiation}} template int X0::f0(int); template int* X0::f0(int*, int*); @@ -43,11 +46,11 @@ template MemPtr X0::f0(MemPtr); // expected-note{{requested here}} struct X2 { int f0(int); // expected-note{{refers here}} - template T f1(T); - template T* f1(T*); + template T f1(T) { return T(); } + template T* f1(T*) { return 0; } - template void f2(T, U*); // expected-note{{candidate}} - template void f2(T*, U); // expected-note{{candidate}} + template void f2(T, U*) { } // expected-note{{candidate}} + template void f2(T*, U) { } // expected-note{{candidate}} }; template int X2::f0(int); // expected-error{{not an instantiation}} @@ -57,12 +60,12 @@ template int *X2::f1(int *); // okay template void X2::f2(int *, int *); // expected-error{{ambiguous}} -template void print_type(); +template void print_type() { } template void print_type(); template void print_type(); -template void print_type(T*); +template void print_type(T*) { } template void print_type(int*); template void print_type(float*); // expected-error{{does not refer}} diff --git a/test/SemaTemplate/temp_explicit.cpp b/test/SemaTemplate/temp_explicit.cpp index 0292964a1a..9c824d6f41 100644 --- a/test/SemaTemplate/temp_explicit.cpp +++ b/test/SemaTemplate/temp_explicit.cpp @@ -15,7 +15,6 @@ template class N::X1; template class ::N::X1; using namespace N; -template class X1; // Check for some bogus syntax that probably means that the user // wanted to write an explicit specialization, but forgot the '<>'