aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td6
-rw-r--r--lib/Sema/Sema.h10
-rw-r--r--lib/Sema/SemaCXXScopeSpec.cpp50
-rw-r--r--lib/Sema/SemaDecl.cpp23
-rw-r--r--lib/Sema/SemaTemplate.cpp125
-rw-r--r--lib/Sema/SemaType.cpp3
-rw-r--r--test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp12
-rw-r--r--test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp45
8 files changed, 248 insertions, 26 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 4de3a8489f..f225b7e808 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -796,8 +796,12 @@ def err_template_arg_extra_parens : Error<
// C++ class template specialization
def err_template_spec_needs_header : Error<
"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<
- "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<
"class template %select{|partial }0specialization of %1 must occur in the "
"global scope">;
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index f13ebd3eb3..22e4323ec1 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -1775,7 +1775,8 @@ public:
bool RequireCompleteDeclContext(const CXXScopeSpec &SS);
- DeclContext *computeDeclContext(const CXXScopeSpec &SS);
+ DeclContext *computeDeclContext(const CXXScopeSpec &SS,
+ bool EnteringContext = false);
bool isDependentScopeSpecifier(const CXXScopeSpec &SS);
CXXRecordDecl *getCurrentInstantiationOf(NestedNameSpecifier *NNS);
bool isUnknownSpecialization(const CXXScopeSpec &SS);
@@ -2064,7 +2065,12 @@ public:
SourceLocation RAngleLoc);
bool CheckTemplateParameterList(TemplateParameterList *NewParams,
TemplateParameterList *OldParams);
-
+ TemplateParameterList *
+ MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc,
+ const CXXScopeSpec &SS,
+ TemplateParameterList **ParamLists,
+ unsigned NumParamLists);
+
virtual DeclResult
ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK,
SourceLocation KWLoc, const CXXScopeSpec &SS,
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp
index aad6e6d68c..7db0da4553 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -21,7 +21,19 @@ using namespace clang;
/// \brief Compute the DeclContext that is associated with the given
/// 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())
return 0;
@@ -32,8 +44,29 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS) {
// instantiation, return its DeclContext.
if (CXXRecordDecl *Record = getCurrentInstantiationOf(NNS))
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()) {
@@ -89,7 +122,10 @@ CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier *NNS) {
assert(getLangOptions().CPlusPlus && "Only callable in C++");
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
// does not refer to the current instantiation.
if (T.isNull())
@@ -138,7 +174,7 @@ CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier *NNS) {
// nested-name-specifier type that is equivalent to the
// injected-class-name of one of the types that is currently in
// our context.
- if (Context.getTypeDeclType(Record) == T)
+ if (Context.getCanonicalType(Context.getTypeDeclType(Record)) == T)
return Record;
if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) {
@@ -286,7 +322,7 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
/// The 'SS' should be a non-empty valid CXXScopeSpec.
void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
- if (DeclContext *DC = computeDeclContext(SS))
+ if (DeclContext *DC = computeDeclContext(SS, true))
EnterDeclaratorContext(S, DC);
else
const_cast<CXXScopeSpec&>(SS).setScopeRep(0);
@@ -299,7 +335,7 @@ void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
/// defining scope.
void Sema::ActOnCXXExitDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
- assert((SS.isInvalid() || S->getEntity() == computeDeclContext(SS)) &&
+ assert((SS.isInvalid() || S->getEntity() == computeDeclContext(SS, true)) &&
"Context imbalance!");
if (!SS.isInvalid())
ExitDeclaratorContext(S);
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 4674327e6c..283033bd7c 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -707,8 +707,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) {
const CXXMethodDecl* OldMethod = dyn_cast<CXXMethodDecl>(Old);
const CXXMethodDecl* NewMethod = dyn_cast<CXXMethodDecl>(New);
if (OldMethod && NewMethod &&
- OldMethod->getLexicalDeclContext() ==
- NewMethod->getLexicalDeclContext()) {
+ NewMethod->getLexicalDeclContext()->isRecord()) {
// -- Member function declarations with the same name and the
// same parameter types cannot be overloaded if any of them
// is a static member function declaration.
@@ -1445,7 +1444,7 @@ Sema::HandleDeclarator(Scope *S, Declarator &D,
NameKind == LookupRedeclarationWithLinkage,
D.getIdentifierLoc());
} else { // Something like "int foo::x;"
- DC = computeDeclContext(D.getCXXScopeSpec());
+ DC = computeDeclContext(D.getCXXScopeSpec(), true);
// FIXME: RequireCompleteDeclContext(D.getCXXScopeSpec()); ?
PrevDecl = LookupQualifiedName(DC, Name, LookupOrdinaryName, true);
@@ -2191,14 +2190,15 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
// from the semantic context.
NewFD->setLexicalDeclContext(CurContext);
- // If there is a template parameter list, then we are dealing with a
- // template declaration or specialization.
+ // Match up the template parameter lists with the scope specifier, then
+ // determine whether we have a template or a template specialization.
FunctionTemplateDecl *FunctionTemplate = 0;
- if (TemplateParamLists.size()) {
- // FIXME: member templates!
- TemplateParameterList *TemplateParams
- = static_cast<TemplateParameterList *>(*TemplateParamLists.release());
-
+ if (TemplateParameterList *TemplateParams
+ = MatchTemplateParametersToScopeSpecifier(
+ D.getDeclSpec().getSourceRange().getBegin(),
+ D.getCXXScopeSpec(),
+ (TemplateParameterList**)TemplateParamLists.release(),
+ TemplateParamLists.size())) {
if (TemplateParams->size() > 0) {
// This is a function template
FunctionTemplate = FunctionTemplateDecl::Create(Context, CurContext,
@@ -2210,6 +2210,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
// FIXME: Handle function template specializations
}
}
+
// C++ [dcl.fct.spec]p5:
// 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))
return DeclPtrTy::make((Decl *)0);
- DC = computeDeclContext(SS);
+ DC = computeDeclContext(SS, true);
SearchDC = DC;
// Look-up name inside 'foo::'.
PrevDecl
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index c3acc24161..a55c956a2a 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -739,6 +739,123 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
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
/// into template arguments used by semantic analysis.
static void
@@ -2572,12 +2689,12 @@ Sema::ActOnStartOfFunctionTemplateDef(Scope *FnBodyScope,
DeclPtrTy DP = HandleDeclarator(ParentScope, D,
move(TemplateParameterLists),
/*IsFunctionDefinition=*/true);
- FunctionTemplateDecl *FunctionTemplate
- = cast_or_null<FunctionTemplateDecl>(DP.getAs<Decl>());
- if (FunctionTemplate)
+ if (FunctionTemplateDecl *FunctionTemplate
+ = dyn_cast_or_null<FunctionTemplateDecl>(DP.getAs<Decl>()))
return ActOnStartOfFunctionDef(FnBodyScope,
DeclPtrTy::make(FunctionTemplate->getTemplatedDecl()));
-
+ if (FunctionDecl *Function = dyn_cast_or_null<FunctionDecl>(DP.getAs<Decl>()))
+ return ActOnStartOfFunctionDef(FnBodyScope, DeclPtrTy::make(Function));
return DeclPtrTy();
}
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 5fbc8e813b..3964f767eb 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -1151,7 +1151,8 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip,
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef &&
((D.getContext() != Declarator::MemberContext &&
(!D.getCXXScopeSpec().isSet() ||
- !computeDeclContext(D.getCXXScopeSpec())->isRecord())) ||
+ !computeDeclContext(D.getCXXScopeSpec(), /*FIXME:*/true)
+ ->isRecord())) ||
D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)) {
if (D.isFunctionDeclarator())
Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type);
diff --git a/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp b/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp
new file mode 100644
index 0000000000..8b70ded8ca
--- /dev/null
+++ b/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp
@@ -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 { } \ No newline at end of file
diff --git a/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp b/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp
new file mode 100644
index 0000000000..6979b4135c
--- /dev/null
+++ b/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp
@@ -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