forked from OSchip/llvm-project
Basic parsing and semantic analysis for out-of-line definitions of the
member functions of class templates, e.g., template<typename T> struct X { void f(T); }; template<typename T> X<T>::f(T) { /* ... */ } llvm-svn: 76692
This commit is contained in:
parent
93a7ed1fc7
commit
d8d297c0ac
|
@ -796,8 +796,12 @@ def err_template_arg_extra_parens : Error<
|
||||||
// C++ class template specialization
|
// C++ class template specialization
|
||||||
def err_template_spec_needs_header : Error<
|
def err_template_spec_needs_header : Error<
|
||||||
"template specialization requires 'template<>'">;
|
"template specialization requires 'template<>'">;
|
||||||
|
def err_template_spec_needs_template_parameters : Error<
|
||||||
|
"template specialization or definition requires a template parameter list"
|
||||||
|
"corresponding to the nested type %0">;
|
||||||
def err_template_spec_extra_headers : Error<
|
def err_template_spec_extra_headers : Error<
|
||||||
"template specialization must have a single 'template<>' header">;
|
"extraneous template parameter list in template specialization or "
|
||||||
|
"out-of-line template definition">;
|
||||||
def err_template_spec_decl_out_of_scope_global : Error<
|
def err_template_spec_decl_out_of_scope_global : Error<
|
||||||
"class template %select{|partial }0specialization of %1 must occur in the "
|
"class template %select{|partial }0specialization of %1 must occur in the "
|
||||||
"global scope">;
|
"global scope">;
|
||||||
|
|
|
@ -1775,7 +1775,8 @@ public:
|
||||||
|
|
||||||
bool RequireCompleteDeclContext(const CXXScopeSpec &SS);
|
bool RequireCompleteDeclContext(const CXXScopeSpec &SS);
|
||||||
|
|
||||||
DeclContext *computeDeclContext(const CXXScopeSpec &SS);
|
DeclContext *computeDeclContext(const CXXScopeSpec &SS,
|
||||||
|
bool EnteringContext = false);
|
||||||
bool isDependentScopeSpecifier(const CXXScopeSpec &SS);
|
bool isDependentScopeSpecifier(const CXXScopeSpec &SS);
|
||||||
CXXRecordDecl *getCurrentInstantiationOf(NestedNameSpecifier *NNS);
|
CXXRecordDecl *getCurrentInstantiationOf(NestedNameSpecifier *NNS);
|
||||||
bool isUnknownSpecialization(const CXXScopeSpec &SS);
|
bool isUnknownSpecialization(const CXXScopeSpec &SS);
|
||||||
|
@ -2064,7 +2065,12 @@ public:
|
||||||
SourceLocation RAngleLoc);
|
SourceLocation RAngleLoc);
|
||||||
bool CheckTemplateParameterList(TemplateParameterList *NewParams,
|
bool CheckTemplateParameterList(TemplateParameterList *NewParams,
|
||||||
TemplateParameterList *OldParams);
|
TemplateParameterList *OldParams);
|
||||||
|
TemplateParameterList *
|
||||||
|
MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc,
|
||||||
|
const CXXScopeSpec &SS,
|
||||||
|
TemplateParameterList **ParamLists,
|
||||||
|
unsigned NumParamLists);
|
||||||
|
|
||||||
virtual DeclResult
|
virtual DeclResult
|
||||||
ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK,
|
ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK,
|
||||||
SourceLocation KWLoc, const CXXScopeSpec &SS,
|
SourceLocation KWLoc, const CXXScopeSpec &SS,
|
||||||
|
|
|
@ -21,7 +21,19 @@ using namespace clang;
|
||||||
|
|
||||||
/// \brief Compute the DeclContext that is associated with the given
|
/// \brief Compute the DeclContext that is associated with the given
|
||||||
/// scope specifier.
|
/// scope specifier.
|
||||||
DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS) {
|
///
|
||||||
|
/// \param SS the C++ scope specifier as it appears in the source
|
||||||
|
///
|
||||||
|
/// \param EnteringContext when true, we will be entering the context of
|
||||||
|
/// this scope specifier, so we can retrieve the declaration context of a
|
||||||
|
/// class template or class template partial specialization even if it is
|
||||||
|
/// not the current instantiation.
|
||||||
|
///
|
||||||
|
/// \returns the declaration context represented by the scope specifier @p SS,
|
||||||
|
/// or NULL if the declaration context cannot be computed (e.g., because it is
|
||||||
|
/// dependent and not the current instantiation).
|
||||||
|
DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
|
||||||
|
bool EnteringContext) {
|
||||||
if (!SS.isSet() || SS.isInvalid())
|
if (!SS.isSet() || SS.isInvalid())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -32,8 +44,29 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS) {
|
||||||
// instantiation, return its DeclContext.
|
// instantiation, return its DeclContext.
|
||||||
if (CXXRecordDecl *Record = getCurrentInstantiationOf(NNS))
|
if (CXXRecordDecl *Record = getCurrentInstantiationOf(NNS))
|
||||||
return Record;
|
return Record;
|
||||||
else
|
|
||||||
return 0;
|
if (EnteringContext) {
|
||||||
|
// We are entering the context of the nested name specifier, so try to
|
||||||
|
// match the nested name specifier to either a primary class template
|
||||||
|
// or a class template partial specialization
|
||||||
|
if (const TemplateSpecializationType *SpecType
|
||||||
|
= dyn_cast_or_null<TemplateSpecializationType>(NNS->getAsType())) {
|
||||||
|
if (ClassTemplateDecl *ClassTemplate
|
||||||
|
= dyn_cast_or_null<ClassTemplateDecl>(
|
||||||
|
SpecType->getTemplateName().getAsTemplateDecl())) {
|
||||||
|
// If the type of the nested name specifier is the same as the
|
||||||
|
// injected class name of the named class template, we're entering
|
||||||
|
// into that class template definition.
|
||||||
|
QualType Injected = ClassTemplate->getInjectedClassNameType(Context);
|
||||||
|
if (Context.hasSameType(Injected, QualType(SpecType, 0)))
|
||||||
|
return ClassTemplate->getTemplatedDecl();
|
||||||
|
|
||||||
|
// FIXME: Class template partial specializations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (NNS->getKind()) {
|
switch (NNS->getKind()) {
|
||||||
|
@ -89,7 +122,10 @@ CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier *NNS) {
|
||||||
assert(getLangOptions().CPlusPlus && "Only callable in C++");
|
assert(getLangOptions().CPlusPlus && "Only callable in C++");
|
||||||
assert(NNS->isDependent() && "Only dependent nested-name-specifier allowed");
|
assert(NNS->isDependent() && "Only dependent nested-name-specifier allowed");
|
||||||
|
|
||||||
QualType T = QualType(NNS->getAsType(), 0);
|
if (!NNS->getAsType())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
QualType T = Context.getCanonicalType(QualType(NNS->getAsType(), 0));
|
||||||
// If the nested name specifier does not refer to a type, then it
|
// If the nested name specifier does not refer to a type, then it
|
||||||
// does not refer to the current instantiation.
|
// does not refer to the current instantiation.
|
||||||
if (T.isNull())
|
if (T.isNull())
|
||||||
|
@ -138,7 +174,7 @@ CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier *NNS) {
|
||||||
// nested-name-specifier type that is equivalent to the
|
// nested-name-specifier type that is equivalent to the
|
||||||
// injected-class-name of one of the types that is currently in
|
// injected-class-name of one of the types that is currently in
|
||||||
// our context.
|
// our context.
|
||||||
if (Context.getTypeDeclType(Record) == T)
|
if (Context.getCanonicalType(Context.getTypeDeclType(Record)) == T)
|
||||||
return Record;
|
return Record;
|
||||||
|
|
||||||
if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) {
|
if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) {
|
||||||
|
@ -286,7 +322,7 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
|
||||||
/// The 'SS' should be a non-empty valid CXXScopeSpec.
|
/// The 'SS' should be a non-empty valid CXXScopeSpec.
|
||||||
void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
|
void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
|
||||||
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
|
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
|
||||||
if (DeclContext *DC = computeDeclContext(SS))
|
if (DeclContext *DC = computeDeclContext(SS, true))
|
||||||
EnterDeclaratorContext(S, DC);
|
EnterDeclaratorContext(S, DC);
|
||||||
else
|
else
|
||||||
const_cast<CXXScopeSpec&>(SS).setScopeRep(0);
|
const_cast<CXXScopeSpec&>(SS).setScopeRep(0);
|
||||||
|
@ -299,7 +335,7 @@ void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
|
||||||
/// defining scope.
|
/// defining scope.
|
||||||
void Sema::ActOnCXXExitDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
|
void Sema::ActOnCXXExitDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
|
||||||
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
|
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
|
||||||
assert((SS.isInvalid() || S->getEntity() == computeDeclContext(SS)) &&
|
assert((SS.isInvalid() || S->getEntity() == computeDeclContext(SS, true)) &&
|
||||||
"Context imbalance!");
|
"Context imbalance!");
|
||||||
if (!SS.isInvalid())
|
if (!SS.isInvalid())
|
||||||
ExitDeclaratorContext(S);
|
ExitDeclaratorContext(S);
|
||||||
|
|
|
@ -707,8 +707,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) {
|
||||||
const CXXMethodDecl* OldMethod = dyn_cast<CXXMethodDecl>(Old);
|
const CXXMethodDecl* OldMethod = dyn_cast<CXXMethodDecl>(Old);
|
||||||
const CXXMethodDecl* NewMethod = dyn_cast<CXXMethodDecl>(New);
|
const CXXMethodDecl* NewMethod = dyn_cast<CXXMethodDecl>(New);
|
||||||
if (OldMethod && NewMethod &&
|
if (OldMethod && NewMethod &&
|
||||||
OldMethod->getLexicalDeclContext() ==
|
NewMethod->getLexicalDeclContext()->isRecord()) {
|
||||||
NewMethod->getLexicalDeclContext()) {
|
|
||||||
// -- Member function declarations with the same name and the
|
// -- Member function declarations with the same name and the
|
||||||
// same parameter types cannot be overloaded if any of them
|
// same parameter types cannot be overloaded if any of them
|
||||||
// is a static member function declaration.
|
// is a static member function declaration.
|
||||||
|
@ -1445,7 +1444,7 @@ Sema::HandleDeclarator(Scope *S, Declarator &D,
|
||||||
NameKind == LookupRedeclarationWithLinkage,
|
NameKind == LookupRedeclarationWithLinkage,
|
||||||
D.getIdentifierLoc());
|
D.getIdentifierLoc());
|
||||||
} else { // Something like "int foo::x;"
|
} else { // Something like "int foo::x;"
|
||||||
DC = computeDeclContext(D.getCXXScopeSpec());
|
DC = computeDeclContext(D.getCXXScopeSpec(), true);
|
||||||
// FIXME: RequireCompleteDeclContext(D.getCXXScopeSpec()); ?
|
// FIXME: RequireCompleteDeclContext(D.getCXXScopeSpec()); ?
|
||||||
PrevDecl = LookupQualifiedName(DC, Name, LookupOrdinaryName, true);
|
PrevDecl = LookupQualifiedName(DC, Name, LookupOrdinaryName, true);
|
||||||
|
|
||||||
|
@ -2191,14 +2190,15 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
|
||||||
// from the semantic context.
|
// from the semantic context.
|
||||||
NewFD->setLexicalDeclContext(CurContext);
|
NewFD->setLexicalDeclContext(CurContext);
|
||||||
|
|
||||||
// If there is a template parameter list, then we are dealing with a
|
// Match up the template parameter lists with the scope specifier, then
|
||||||
// template declaration or specialization.
|
// determine whether we have a template or a template specialization.
|
||||||
FunctionTemplateDecl *FunctionTemplate = 0;
|
FunctionTemplateDecl *FunctionTemplate = 0;
|
||||||
if (TemplateParamLists.size()) {
|
if (TemplateParameterList *TemplateParams
|
||||||
// FIXME: member templates!
|
= MatchTemplateParametersToScopeSpecifier(
|
||||||
TemplateParameterList *TemplateParams
|
D.getDeclSpec().getSourceRange().getBegin(),
|
||||||
= static_cast<TemplateParameterList *>(*TemplateParamLists.release());
|
D.getCXXScopeSpec(),
|
||||||
|
(TemplateParameterList**)TemplateParamLists.release(),
|
||||||
|
TemplateParamLists.size())) {
|
||||||
if (TemplateParams->size() > 0) {
|
if (TemplateParams->size() > 0) {
|
||||||
// This is a function template
|
// This is a function template
|
||||||
FunctionTemplate = FunctionTemplateDecl::Create(Context, CurContext,
|
FunctionTemplate = FunctionTemplateDecl::Create(Context, CurContext,
|
||||||
|
@ -2210,6 +2210,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
|
||||||
// FIXME: Handle function template specializations
|
// FIXME: Handle function template specializations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// C++ [dcl.fct.spec]p5:
|
// C++ [dcl.fct.spec]p5:
|
||||||
// The virtual specifier shall only be used in declarations of
|
// The virtual specifier shall only be used in declarations of
|
||||||
|
@ -3517,7 +3518,7 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK,
|
||||||
if (RequireCompleteDeclContext(SS))
|
if (RequireCompleteDeclContext(SS))
|
||||||
return DeclPtrTy::make((Decl *)0);
|
return DeclPtrTy::make((Decl *)0);
|
||||||
|
|
||||||
DC = computeDeclContext(SS);
|
DC = computeDeclContext(SS, true);
|
||||||
SearchDC = DC;
|
SearchDC = DC;
|
||||||
// Look-up name inside 'foo::'.
|
// Look-up name inside 'foo::'.
|
||||||
PrevDecl
|
PrevDecl
|
||||||
|
|
|
@ -739,6 +739,123 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
|
||||||
return Invalid;
|
return Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Match the given template parameter lists to the given scope
|
||||||
|
/// specifier, returning the template parameter list that applies to the
|
||||||
|
/// name.
|
||||||
|
///
|
||||||
|
/// \param DeclStartLoc the start of the declaration that has a scope
|
||||||
|
/// specifier or a template parameter list.
|
||||||
|
///
|
||||||
|
/// \param SS the scope specifier that will be matched to the given template
|
||||||
|
/// parameter lists. This scope specifier precedes a qualified name that is
|
||||||
|
/// being declared.
|
||||||
|
///
|
||||||
|
/// \param ParamLists the template parameter lists, from the outermost to the
|
||||||
|
/// innermost template parameter lists.
|
||||||
|
///
|
||||||
|
/// \param NumParamLists the number of template parameter lists in ParamLists.
|
||||||
|
///
|
||||||
|
/// \returns the template parameter list, if any, that corresponds to the
|
||||||
|
/// name that is preceded by the scope specifier @p SS. This template
|
||||||
|
/// parameter list may be have template parameters (if we're declaring a
|
||||||
|
/// template) or may have no template parameters (if we're declaring a
|
||||||
|
/// template specialization), or may be NULL (if we were's declaring isn't
|
||||||
|
/// itself a template).
|
||||||
|
TemplateParameterList *
|
||||||
|
Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc,
|
||||||
|
const CXXScopeSpec &SS,
|
||||||
|
TemplateParameterList **ParamLists,
|
||||||
|
unsigned NumParamLists) {
|
||||||
|
// FIXME: This routine will need a lot more testing once we have support for
|
||||||
|
// member templates.
|
||||||
|
|
||||||
|
// Find the template-ids that occur within the nested-name-specifier. These
|
||||||
|
// template-ids will match up with the template parameter lists.
|
||||||
|
llvm::SmallVector<const TemplateSpecializationType *, 4>
|
||||||
|
TemplateIdsInSpecifier;
|
||||||
|
for (NestedNameSpecifier *NNS = (NestedNameSpecifier *)SS.getScopeRep();
|
||||||
|
NNS; NNS = NNS->getPrefix()) {
|
||||||
|
if (const TemplateSpecializationType *SpecType
|
||||||
|
= dyn_cast_or_null<TemplateSpecializationType>(NNS->getAsType())) {
|
||||||
|
TemplateDecl *Template = SpecType->getTemplateName().getAsTemplateDecl();
|
||||||
|
if (!Template)
|
||||||
|
continue; // FIXME: should this be an error? probably...
|
||||||
|
|
||||||
|
if (const RecordType *Record = SpecType->getAsRecordType()) {
|
||||||
|
ClassTemplateSpecializationDecl *SpecDecl
|
||||||
|
= cast<ClassTemplateSpecializationDecl>(Record->getDecl());
|
||||||
|
// If the nested name specifier refers to an explicit specialization,
|
||||||
|
// we don't need a template<> header.
|
||||||
|
if (SpecDecl->getSpecializationKind() == TSK_ExplicitSpecialization)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateIdsInSpecifier.push_back(SpecType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse the list of template-ids in the scope specifier, so that we can
|
||||||
|
// more easily match up the template-ids and the template parameter lists.
|
||||||
|
std::reverse(TemplateIdsInSpecifier.begin(), TemplateIdsInSpecifier.end());
|
||||||
|
|
||||||
|
SourceLocation FirstTemplateLoc = DeclStartLoc;
|
||||||
|
if (NumParamLists)
|
||||||
|
FirstTemplateLoc = ParamLists[0]->getTemplateLoc();
|
||||||
|
|
||||||
|
// Match the template-ids found in the specifier to the template parameter
|
||||||
|
// lists.
|
||||||
|
unsigned Idx = 0;
|
||||||
|
for (unsigned NumTemplateIds = TemplateIdsInSpecifier.size();
|
||||||
|
Idx != NumTemplateIds; ++Idx) {
|
||||||
|
bool DependentTemplateId = TemplateIdsInSpecifier[Idx]->isDependentType();
|
||||||
|
if (Idx >= NumParamLists) {
|
||||||
|
// We have a template-id without a corresponding template parameter
|
||||||
|
// list.
|
||||||
|
if (DependentTemplateId) {
|
||||||
|
// FIXME: the location information here isn't great.
|
||||||
|
Diag(SS.getRange().getBegin(),
|
||||||
|
diag::err_template_spec_needs_template_parameters)
|
||||||
|
<< QualType(TemplateIdsInSpecifier[Idx], 0)
|
||||||
|
<< SS.getRange();
|
||||||
|
} else {
|
||||||
|
Diag(SS.getRange().getBegin(), diag::err_template_spec_needs_header)
|
||||||
|
<< SS.getRange()
|
||||||
|
<< CodeModificationHint::CreateInsertion(FirstTemplateLoc,
|
||||||
|
"template<> ");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the template parameter list against its corresponding template-id.
|
||||||
|
TemplateDecl *Template
|
||||||
|
= TemplateIdsInSpecifier[Idx]->getTemplateName().getAsTemplateDecl();
|
||||||
|
TemplateParameterListsAreEqual(ParamLists[Idx],
|
||||||
|
Template->getTemplateParameters(),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there were at least as many template-ids as there were template
|
||||||
|
// parameter lists, then there are no template parameter lists remaining for
|
||||||
|
// the declaration itself.
|
||||||
|
if (Idx >= NumParamLists)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// If there were too many template parameter lists, complain about that now.
|
||||||
|
if (Idx != NumParamLists - 1) {
|
||||||
|
while (Idx < NumParamLists - 1) {
|
||||||
|
Diag(ParamLists[Idx]->getTemplateLoc(),
|
||||||
|
diag::err_template_spec_extra_headers)
|
||||||
|
<< SourceRange(ParamLists[Idx]->getTemplateLoc(),
|
||||||
|
ParamLists[Idx]->getRAngleLoc());
|
||||||
|
++Idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the last template parameter list, which corresponds to the
|
||||||
|
// entity being declared.
|
||||||
|
return ParamLists[NumParamLists - 1];
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Translates template arguments as provided by the parser
|
/// \brief Translates template arguments as provided by the parser
|
||||||
/// into template arguments used by semantic analysis.
|
/// into template arguments used by semantic analysis.
|
||||||
static void
|
static void
|
||||||
|
@ -2572,12 +2689,12 @@ Sema::ActOnStartOfFunctionTemplateDef(Scope *FnBodyScope,
|
||||||
DeclPtrTy DP = HandleDeclarator(ParentScope, D,
|
DeclPtrTy DP = HandleDeclarator(ParentScope, D,
|
||||||
move(TemplateParameterLists),
|
move(TemplateParameterLists),
|
||||||
/*IsFunctionDefinition=*/true);
|
/*IsFunctionDefinition=*/true);
|
||||||
FunctionTemplateDecl *FunctionTemplate
|
if (FunctionTemplateDecl *FunctionTemplate
|
||||||
= cast_or_null<FunctionTemplateDecl>(DP.getAs<Decl>());
|
= dyn_cast_or_null<FunctionTemplateDecl>(DP.getAs<Decl>()))
|
||||||
if (FunctionTemplate)
|
|
||||||
return ActOnStartOfFunctionDef(FnBodyScope,
|
return ActOnStartOfFunctionDef(FnBodyScope,
|
||||||
DeclPtrTy::make(FunctionTemplate->getTemplatedDecl()));
|
DeclPtrTy::make(FunctionTemplate->getTemplatedDecl()));
|
||||||
|
if (FunctionDecl *Function = dyn_cast_or_null<FunctionDecl>(DP.getAs<Decl>()))
|
||||||
|
return ActOnStartOfFunctionDef(FnBodyScope, DeclPtrTy::make(Function));
|
||||||
return DeclPtrTy();
|
return DeclPtrTy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1151,7 +1151,8 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip,
|
||||||
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef &&
|
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef &&
|
||||||
((D.getContext() != Declarator::MemberContext &&
|
((D.getContext() != Declarator::MemberContext &&
|
||||||
(!D.getCXXScopeSpec().isSet() ||
|
(!D.getCXXScopeSpec().isSet() ||
|
||||||
!computeDeclContext(D.getCXXScopeSpec())->isRecord())) ||
|
!computeDeclContext(D.getCXXScopeSpec(), /*FIXME:*/true)
|
||||||
|
->isRecord())) ||
|
||||||
D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)) {
|
D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)) {
|
||||||
if (D.isFunctionDeclarator())
|
if (D.isFunctionDeclarator())
|
||||||
Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type);
|
Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type);
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// RUN: clang-cc -fsyntax-only -verify %s
|
||||||
|
// XFAIL
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct X0 {
|
||||||
|
typedef int size_type;
|
||||||
|
|
||||||
|
size_type f0() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename X0<T>::size_type X0<T>::f0() const { }
|
|
@ -0,0 +1,45 @@
|
||||||
|
// RUN: clang-cc -fsyntax-only -verify %s
|
||||||
|
|
||||||
|
template<typename T, typename U> // expected-note{{previous template}}
|
||||||
|
class X0 {
|
||||||
|
public:
|
||||||
|
typedef int size_type;
|
||||||
|
|
||||||
|
void f0(const T&, const U&);
|
||||||
|
|
||||||
|
T& operator[](int i) const;
|
||||||
|
|
||||||
|
void f1(size_type) const;
|
||||||
|
void f2(size_type) const;
|
||||||
|
void f3(size_type) const;
|
||||||
|
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
void X0<T, U>::f0(const T&, const U&) { // expected-note{{previous definition}}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class X, class Y>
|
||||||
|
X& X0<X, Y>::operator[](int i) const {
|
||||||
|
(void)i;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class X, class Y>
|
||||||
|
void X0<X, Y>::f1(int) const { }
|
||||||
|
|
||||||
|
template<class X, class Y>
|
||||||
|
void X0<X, Y>::f2(size_type) const { }
|
||||||
|
|
||||||
|
template<class X, class Y, class Z> // expected-error{{too many template parameters}}
|
||||||
|
void X0<X, Y>::f3(size_type) const {
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: error message should probably say, "redefinition of 'X0<T, U>::f0'"
|
||||||
|
// rather than just "redefinition of 'f0'"
|
||||||
|
template<typename T, typename U>
|
||||||
|
void X0<T, U>::f0(const T&, const U&) { // expected-error{{redefinition}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: test out-of-line constructors, destructors
|
Loading…
Reference in New Issue