diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 5f7dcd5d46a9..9648e62b11f6 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1218,7 +1218,7 @@ CXXRecordDecl::setInstantiationOfMemberClass(CXXRecordDecl *RD, TemplateSpecializationKind TSK) { assert(TemplateOrInstantiation.isNull() && "Previous template or instantiation?"); - assert(!isa(this)); + assert(!isa(this)); TemplateOrInstantiation = new (getASTContext()) MemberSpecializationInfo(RD, TSK); } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index b10cb397ffe2..fc9b363780b6 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2321,8 +2321,144 @@ Decl *TemplateDeclInstantiator::VisitRecordDecl(RecordDecl *D) { Decl * TemplateDeclInstantiator::VisitClassTemplateSpecializationDecl( ClassTemplateSpecializationDecl *D) { - llvm_unreachable("Only ClassTemplatePartialSpecializationDecls occur" - "inside templates"); + // As a MS extension, we permit class-scope explicit specialization + // of member class templates. + ClassTemplateDecl *ClassTemplate = D->getSpecializedTemplate(); + assert(ClassTemplate->getDeclContext()->isRecord() && + D->getTemplateSpecializationKind() == TSK_ExplicitSpecialization && + "can only instantiate an explicit specialization " + "for a member class template"); + + // Lookup the already-instantiated declaration in the instantiation + // of the class template. FIXME: Diagnose or assert if this fails? + DeclContext::lookup_result Found + = Owner->lookup(ClassTemplate->getDeclName()); + if (Found.empty()) + return 0; + ClassTemplateDecl *InstClassTemplate + = dyn_cast(Found.front()); + if (!InstClassTemplate) + return 0; + + // Substitute into the template arguments of the class template explicit + // specialization. + TemplateSpecializationTypeLoc Loc = D->getTypeAsWritten()->getTypeLoc(). + castAs(); + TemplateArgumentListInfo InstTemplateArgs(Loc.getLAngleLoc(), + Loc.getRAngleLoc()); + SmallVector ArgLocs; + for (unsigned I = 0; I != Loc.getNumArgs(); ++I) + ArgLocs.push_back(Loc.getArgLoc(I)); + if (SemaRef.Subst(ArgLocs.data(), ArgLocs.size(), + InstTemplateArgs, TemplateArgs)) + return 0; + + // Check that the template argument list is well-formed for this + // class template. + SmallVector Converted; + if (SemaRef.CheckTemplateArgumentList(InstClassTemplate, + D->getLocation(), + InstTemplateArgs, + false, + Converted)) + return 0; + + // Figure out where to insert this class template explicit specialization + // in the member template's set of class template explicit specializations. + void *InsertPos = 0; + ClassTemplateSpecializationDecl *PrevDecl = + InstClassTemplate->findSpecialization(Converted.data(), Converted.size(), + InsertPos); + + // Check whether we've already seen a conflicting instantiation of this + // declaration (for instance, if there was a prior implicit instantiation). + bool Ignored; + if (PrevDecl && + SemaRef.CheckSpecializationInstantiationRedecl(D->getLocation(), + D->getSpecializationKind(), + PrevDecl, + PrevDecl->getSpecializationKind(), + PrevDecl->getPointOfInstantiation(), + Ignored)) + return 0; + + // If PrevDecl was a definition and D is also a definition, diagnose. + // This happens in cases like: + // + // template + // struct Outer { + // template struct Inner; + // template<> struct Inner {}; + // template<> struct Inner {}; + // }; + // + // Outer outer; // error: the explicit specializations of Inner + // // have the same signature. + if (PrevDecl && PrevDecl->getDefinition() && + D->isThisDeclarationADefinition()) { + SemaRef.Diag(D->getLocation(), diag::err_redefinition) << PrevDecl; + SemaRef.Diag(PrevDecl->getDefinition()->getLocation(), + diag::note_previous_definition); + return 0; + } + + // Create the class template partial specialization declaration. + ClassTemplateSpecializationDecl *InstD + = ClassTemplateSpecializationDecl::Create(SemaRef.Context, + D->getTagKind(), + Owner, + D->getLocStart(), + D->getLocation(), + InstClassTemplate, + Converted.data(), + Converted.size(), + PrevDecl); + + // Add this partial specialization to the set of class template partial + // specializations. + if (!PrevDecl) + InstClassTemplate->AddSpecialization(InstD, InsertPos); + + // Substitute the nested name specifier, if any. + if (SubstQualifier(D, InstD)) + return 0; + + // Build the canonical type that describes the converted template + // arguments of the class template explicit specialization. + QualType CanonType = SemaRef.Context.getTemplateSpecializationType( + TemplateName(InstClassTemplate), Converted.data(), Converted.size(), + SemaRef.Context.getRecordType(InstD)); + + // Build the fully-sugared type for this class template + // specialization as the user wrote in the specialization + // itself. This means that we'll pretty-print the type retrieved + // from the specialization's declaration the way that the user + // actually wrote the specialization, rather than formatting the + // name based on the "canonical" representation used to store the + // template arguments in the specialization. + TypeSourceInfo *WrittenTy = SemaRef.Context.getTemplateSpecializationTypeInfo( + TemplateName(InstClassTemplate), D->getLocation(), InstTemplateArgs, + CanonType); + + InstD->setAccess(D->getAccess()); + InstD->setInstantiationOfMemberClass(D, TSK_ImplicitInstantiation); + InstD->setSpecializationKind(D->getSpecializationKind()); + InstD->setTypeAsWritten(WrittenTy); + InstD->setExternLoc(D->getExternLoc()); + InstD->setTemplateKeywordLoc(D->getTemplateKeywordLoc()); + + Owner->addDecl(InstD); + + // Instantiate the members of the class-scope explicit specialization eagerly. + // We don't have support for lazy instantiation of an explicit specialization + // yet, and MSVC eagerly instantiates in this case. + if (D->isThisDeclarationADefinition() && + SemaRef.InstantiateClass(D->getLocation(), InstD, D, TemplateArgs, + TSK_ImplicitInstantiation, + /*Complain=*/true)) + return 0; + + return InstD; } Decl *TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl( diff --git a/clang/test/SemaTemplate/ms-class-specialization-class-scope.cpp b/clang/test/SemaTemplate/ms-class-specialization-class-scope.cpp new file mode 100644 index 000000000000..3ebb1c9c555e --- /dev/null +++ b/clang/test/SemaTemplate/ms-class-specialization-class-scope.cpp @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify %s -Wno-microsoft +// RUN: %clang_cc1 -fms-extensions -fdelayed-template-parsing -fsyntax-only -verify %s -Wno-microsoft + +class A { +public: + template struct X { typedef int x; }; + + X::x a; // expected-note {{implicit instantiation first required here}} + + template<> struct X; // expected-error {{explicit specialization of 'A::X' after instantiation}} + template<> struct X; // expected-note {{forward declaration}} + + X::x b; // expected-error {{incomplete type 'A::X' named in nested name specifier}} + + template<> struct X { + typedef int y; + }; + + X::y c; + + template<> struct X {}; // expected-note {{previous definition is here}} + template<> struct X {}; // expected-error {{redefinition of 'A::X'}} +}; + +A::X::x axv; +A::X::x axf; // expected-error {{no type named 'x'}} + +template class B { +public: + template struct X { typedef int x; }; + + typename X::x a; // expected-note {{implicit instantiation first required here}} + + template<> struct X; // expected-error {{explicit specialization of 'X' after instantiation}} + template<> struct X; // expected-note {{forward declaration}} + + typename X::x b; // expected-error {{incomplete type 'B::X' named in nested name specifier}} + + template<> struct X { + typedef int y; + }; + + typename X::y c; + + template<> struct X {}; // expected-note {{previous definition is here}} + template<> struct X {}; // expected-error {{redefinition of 'X'}} +}; + +B b; // expected-note {{in instantiation of}}