diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 6 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 10 | ||||
-rw-r--r-- | lib/Sema/SemaCXXScopeSpec.cpp | 50 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 23 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 125 | ||||
-rw-r--r-- | lib/Sema/SemaType.cpp | 3 | ||||
-rw-r--r-- | test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1-retmem.cpp | 12 | ||||
-rw-r--r-- | test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp | 45 |
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 |