Additional semantic checking for explicit template instantiations,

focusing on the scope- and qualifier-related semantic requirements in
C++ [temp.explicit]p2.

llvm-svn: 84154
This commit is contained in:
Douglas Gregor 2009-10-14 23:41:34 +00:00
parent 2f61e0946a
commit e47f5a76cc
5 changed files with 192 additions and 24 deletions

View File

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

View File

@ -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<NamespaceDecl>(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<TemplateSpecializationType>(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<CXXRecordDecl>(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();
}

View File

@ -0,0 +1,43 @@
// RUN: clang-cc -fsyntax-only -verify %s
// Example from the standard
template<class T> class Array { void mf() { } };
template class Array<char>;
template void Array<int>::mf();
template<class T> void sort(Array<T>& v) { /* ... */ }
template void sort(Array<char>&);
namespace N {
template<class T> void f(T&) { }
}
template void N::f<int>(int&);
template<typename T>
struct X0 {
struct Inner {};
void f() { }
static T value;
};
template<typename T>
T X0<T>::value = 17;
typedef X0<int> 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<typename T>
struct X1 { // expected-note{{explicit instantiation refers here}}
};
template<typename T>
void f1(T); // expected-note{{explicit instantiation refers here}}
}
using namespace N;
template struct X1<int>; // expected-error{{must occur in}}
template void f1(int); // expected-error{{must occur in}}

View File

@ -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<typename U>
T f0(T, U);
T f0(T, U) { return T(); }
};
template<typename T>
T X0<T>::value; // expected-error{{no matching constructor}}
template int X0<int>::value;
struct NotDefaultConstructible {
NotDefaultConstructible(int);
};
template NotDefaultConstructible X0<NotDefaultConstructible>::value;
template NotDefaultConstructible X0<NotDefaultConstructible>::value; // expected-note{{instantiation}}
template int X0<int>::f0(int);
template int* X0<int>::f0(int*, int*);
@ -43,11 +46,11 @@ template MemPtr X0<MemPtr>::f0(MemPtr); // expected-note{{requested here}}
struct X2 {
int f0(int); // expected-note{{refers here}}
template<typename T> T f1(T);
template<typename T> T* f1(T*);
template<typename T> T f1(T) { return T(); }
template<typename T> T* f1(T*) { return 0; }
template<typename T, typename U> void f2(T, U*); // expected-note{{candidate}}
template<typename T, typename U> void f2(T*, U); // expected-note{{candidate}}
template<typename T, typename U> void f2(T, U*) { } // expected-note{{candidate}}
template<typename T, typename U> 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<typename T> void print_type();
template<typename T> void print_type() { }
template void print_type<int>();
template void print_type<float>();
template<typename T> void print_type(T*);
template<typename T> void print_type(T*) { }
template void print_type(int*);
template void print_type<int>(float*); // expected-error{{does not refer}}

View File

@ -15,7 +15,6 @@ template class N::X1<int>;
template class ::N::X1<int, float>;
using namespace N;
template class X1<float>;
// Check for some bogus syntax that probably means that the user
// wanted to write an explicit specialization, but forgot the '<>'