From 1530138fd04d7fa9ceb0ba84ff2df131f8f7fef0 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 30 Jul 2009 17:40:51 +0000 Subject: [PATCH] Support out-of-line definitions of the members of class template partial specializations. llvm-svn: 77606 --- clang/include/clang/AST/DeclTemplate.h | 10 ++++ clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 ++ clang/lib/AST/ASTContext.cpp | 15 +++--- clang/lib/AST/DeclTemplate.cpp | 15 ++++++ clang/lib/Sema/SemaCXXScopeSpec.cpp | 14 +++++- clang/lib/Sema/SemaTemplate.cpp | 53 ++++++++++++++++++---- .../temp.class.spec.mfunc/p1-neg.cpp | 25 ++++++++++ .../temp.class.spec/temp.class.spec.mfunc/p1.cpp | 15 ++++++ 8 files changed, 132 insertions(+), 18 deletions(-) create mode 100644 clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1-neg.cpp create mode 100644 clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1.cpp diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 7cf8bfd..501f82d 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1057,6 +1057,16 @@ public: return CommonPtr->PartialSpecializations; } + /// \brief Find a class template partial specialization with the given + /// type T. + /// + /// \brief A dependent type that names a specialization of this class + /// template. + /// + /// \returns the class template partial specialization that exactly matches + /// the type \p T, or NULL if no such partial specialization exists. + ClassTemplatePartialSpecializationDecl *findPartialSpecialization(QualType T); + /// \brief Retrieve the type of the injected-class-name for this /// class template. /// diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ab454d0..25e3317 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -858,6 +858,9 @@ def err_template_spec_needs_header : Error< 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_param_list_matches_nontemplate : Error< + "template parameter list matching the non-templated nested type %0 should " + "be empty ('template<>')">; def err_template_spec_extra_headers : Error< "extraneous template parameter list in template specialization or " "out-of-line template definition">; diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 66aa363..e9bcc04 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1669,9 +1669,10 @@ ASTContext::getTemplateSpecializationType(TemplateName Template, const TemplateArgument *Args, unsigned NumArgs, QualType Canon) { - if (Canon.isNull()) { - // Build the canonical template specialization type, since no type - // was provided. + if (!Canon.isNull()) + Canon = getCanonicalType(Canon); + else { + // Build the canonical template specialization type. TemplateName CanonTemplate = getCanonicalTemplateName(Template); llvm::SmallVector CanonArgs; CanonArgs.reserve(NumArgs); @@ -1695,16 +1696,16 @@ ASTContext::getTemplateSpecializationType(TemplateName Template, 8); Spec = new (Mem) TemplateSpecializationType(*this, CanonTemplate, CanonArgs.data(), NumArgs, - QualType()); + Canon); Types.push_back(Spec); TemplateSpecializationTypes.InsertNode(Spec, InsertPos); } - Canon = QualType(Spec, 0); + if (Canon.isNull()) + Canon = QualType(Spec, 0); assert(Canon->isDependentType() && "Non-dependent template-id type must have a canonical type"); - } else - Canon = getCanonicalType(Canon); + } // Allocate the (non-canonical) template specialization type, but don't // try to unique it: these types typically have location information that diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 4677d17..6b3ea43 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -161,6 +161,21 @@ void ClassTemplateDecl::Destroy(ASTContext& C) { C.Deallocate((void*)this); } +ClassTemplatePartialSpecializationDecl * +ClassTemplateDecl::findPartialSpecialization(QualType T) { + ASTContext &Context = getASTContext(); + typedef llvm::FoldingSet::iterator + partial_spec_iterator; + for (partial_spec_iterator P = getPartialSpecializations().begin(), + PEnd = getPartialSpecializations().end(); + P != PEnd; ++P) { + if (Context.hasSameType(Context.getTypeDeclType(&*P), T)) + return &*P; + } + + return 0; +} + QualType ClassTemplateDecl::getInjectedClassNameType(ASTContext &Context) { if (!CommonPtr->InjectedClassNameType.isNull()) return CommonPtr->InjectedClassNameType; diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index a5a2030..1580e72 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -55,14 +55,23 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS, if (ClassTemplateDecl *ClassTemplate = dyn_cast_or_null( SpecType->getTemplateName().getAsTemplateDecl())) { + QualType ContextType + = Context.getCanonicalType(QualType(SpecType, 0)); + // 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))) + if (Context.hasSameType(Injected, ContextType)) return ClassTemplate->getTemplatedDecl(); - // FIXME: Class template partial specializations + // If the type of the nested name specifier is the same as the + // type of one of the class template's class template partial + // specializations, we're entering into the definition of that + // class template partial specialization. + if (ClassTemplatePartialSpecializationDecl *PartialSpec + = ClassTemplate->findPartialSpecialization(ContextType)) + return PartialSpec; } } @@ -195,6 +204,7 @@ CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier *NNS) { if (T == Context.getCanonicalType(InjectedClassName)) return Template->getTemplatedDecl(); } + // FIXME: check for class template partial specializations } return 0; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 425b502..4264f32 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -815,6 +815,8 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, = cast(Record->getDecl()); // If the nested name specifier refers to an explicit specialization, // we don't need a template<> header. + // FIXME: revisit this approach once we cope with specialization + // properly. if (SpecDecl->getSpecializationKind() == TSK_ExplicitSpecialization) continue; } @@ -836,7 +838,8 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, unsigned Idx = 0; for (unsigned NumTemplateIds = TemplateIdsInSpecifier.size(); Idx != NumTemplateIds; ++Idx) { - bool DependentTemplateId = TemplateIdsInSpecifier[Idx]->isDependentType(); + QualType TemplateId = QualType(TemplateIdsInSpecifier[Idx], 0); + bool DependentTemplateId = TemplateId->isDependentType(); if (Idx >= NumParamLists) { // We have a template-id without a corresponding template parameter // list. @@ -844,7 +847,7 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, // FIXME: the location information here isn't great. Diag(SS.getRange().getBegin(), diag::err_template_spec_needs_template_parameters) - << QualType(TemplateIdsInSpecifier[Idx], 0) + << TemplateId << SS.getRange(); } else { Diag(SS.getRange().getBegin(), diag::err_template_spec_needs_header) @@ -856,11 +859,32 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, } // Check the template parameter list against its corresponding template-id. - TemplateDecl *Template - = TemplateIdsInSpecifier[Idx]->getTemplateName().getAsTemplateDecl(); - TemplateParameterListsAreEqual(ParamLists[Idx], - Template->getTemplateParameters(), - true); + if (DependentTemplateId) { + TemplateDecl *Template + = TemplateIdsInSpecifier[Idx]->getTemplateName().getAsTemplateDecl(); + + if (ClassTemplateDecl *ClassTemplate + = dyn_cast(Template)) { + TemplateParameterList *ExpectedTemplateParams = 0; + // Is this template-id naming the primary template? + if (Context.hasSameType(TemplateId, + ClassTemplate->getInjectedClassNameType(Context))) + ExpectedTemplateParams = ClassTemplate->getTemplateParameters(); + // ... or a partial specialization? + else if (ClassTemplatePartialSpecializationDecl *PartialSpec + = ClassTemplate->findPartialSpecialization(TemplateId)) + ExpectedTemplateParams = PartialSpec->getTemplateParameters(); + + if (ExpectedTemplateParams) + TemplateParameterListsAreEqual(ParamLists[Idx], + ExpectedTemplateParams, + true); + } + } else if (ParamLists[Idx]->size() > 0) + Diag(ParamLists[Idx]->getTemplateLoc(), + diag::err_template_param_list_matches_nontemplate) + << TemplateId + << ParamLists[Idx]->getSourceRange(); } // If there were at least as many template-ids as there were template @@ -2493,6 +2517,8 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, /*ExplicitInstantiation=*/false)) return true; + // The canonical type + QualType CanonType; if (PrevDecl && PrevDecl->getSpecializationKind() == TSK_Undeclared) { // Since the only prior class template specialization with these // arguments was referenced but not declared, reuse that @@ -2501,7 +2527,15 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, Specialization = PrevDecl; Specialization->setLocation(TemplateNameLoc); PrevDecl = 0; + CanonType = Context.getTypeDeclType(Specialization); } else if (isPartialSpecialization) { + // Build the canonical type that describes the converted template + // arguments of the class template partial specialization. + CanonType = Context.getTemplateSpecializationType( + TemplateName(ClassTemplate), + Converted.getFlatArguments(), + Converted.flatSize()); + // Create a new class template partial specialization declaration node. TemplateParameterList *TemplateParams = static_cast(*TemplateParameterLists.get()); @@ -2554,7 +2588,6 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, } } } - } else { // Create a new class template specialization declaration node for // this explicit specialization. @@ -2573,6 +2606,8 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, ClassTemplate->getSpecializations().InsertNode(Specialization, InsertPos); } + + CanonType = Context.getTypeDeclType(Specialization); } // Note that this is an explicit specialization. @@ -2603,7 +2638,7 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, = Context.getTemplateSpecializationType(Name, TemplateArgs.data(), TemplateArgs.size(), - Context.getTypeDeclType(Specialization)); + CanonType); Specialization->setTypeAsWritten(WrittenTy); TemplateArgsIn.release(); diff --git a/clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1-neg.cpp b/clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1-neg.cpp new file mode 100644 index 0000000..47cf837 --- /dev/null +++ b/clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1-neg.cpp @@ -0,0 +1,25 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +template +struct A; + +template // expected-note{{previous template declaration}} +struct A { + void f0(); + void f1(); + void f2(); +}; + +template<> +struct A { + void g0(); +}; + +// FIXME: We should probably give more precise diagnostics here, but the +// diagnostics we give aren't terrible. +// FIXME: why not point to the first parameter that's "too many"? +template // expected-error{{too many template parameters}} +void A::f0() { } + +template +void A::f1() { } // expected-error{{out-of-line definition}} diff --git a/clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1.cpp new file mode 100644 index 0000000..0321a7c --- /dev/null +++ b/clang/test/CXX/temp/temp.decls/temp.class.spec/temp.class.spec.mfunc/p1.cpp @@ -0,0 +1,15 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +template +struct A; + +template +struct A { + void f(T*); + + static T value; +}; + +template void A::f(X*) { } + +template X A::value; -- 2.7.4