From a6b41d7c52ad4b8d2fd7f2f069645401b9bbc1e9 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 3 May 2019 23:51:38 +0000 Subject: [PATCH] CWG issue 727: Fix numerous bugs in support for class-scope explicit specializations for variable templates. llvm-svn: 359947 --- clang/include/clang/AST/Decl.h | 6 + clang/include/clang/Sema/Sema.h | 3 +- clang/include/clang/Sema/Template.h | 3 +- clang/lib/AST/Decl.cpp | 102 +++++++----- clang/lib/Sema/SemaExpr.cpp | 15 +- clang/lib/Sema/SemaTemplate.cpp | 9 +- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 181 +++++++++++---------- clang/test/CXX/drs/dr7xx.cpp | 99 ++++++++++- clang/test/PCH/cxx-templates.cpp | 34 ++-- clang/test/PCH/cxx-templates.h | 27 +++ .../explicit-specialization-member.cpp | 26 ++- 11 files changed, 342 insertions(+), 163 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 507ca73..da81289 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1434,6 +1434,12 @@ public: /// template specialization or instantiation this is. TemplateSpecializationKind getTemplateSpecializationKind() const; + /// Get the template specialization kind of this variable for the purposes of + /// template instantiation. This differs from getTemplateSpecializationKind() + /// for an instantiation of a class-scope explicit specialization. + TemplateSpecializationKind + getTemplateSpecializationKindForInstantiation() const; + /// If this variable is an instantiation of a variable template or a /// static data member of a class template, determine its point of /// instantiation. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 64b92d2..93932b4 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8043,7 +8043,8 @@ public: LateInstantiatedAttrVec *LateAttrs, DeclContext *Owner, LocalInstantiationScope *StartingScope, - bool InstantiatingVarTemplate = false); + bool InstantiatingVarTemplate = false, + VarTemplateSpecializationDecl *PrevVTSD = nullptr); void InstantiateVariableInitializer( VarDecl *Var, VarDecl *OldVar, const MultiLevelTemplateArgumentList &TemplateArgs); diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index 61de934..98f7dd5 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -545,7 +545,8 @@ class VarDecl; Decl *VisitVarTemplateSpecializationDecl( VarTemplateDecl *VarTemplate, VarDecl *FromVar, void *InsertPos, const TemplateArgumentListInfo &TemplateArgsInfo, - ArrayRef Converted); + ArrayRef Converted, + VarTemplateSpecializationDecl *PrevDecl = nullptr); Decl *InstantiateTypedefNameDecl(TypedefNameDecl *D, bool IsTypeAlias); ClassTemplatePartialSpecializationDecl * diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 2c1dbbd7..19fd388 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2414,48 +2414,61 @@ bool VarDecl::isNonEscapingByref() const { } VarDecl *VarDecl::getTemplateInstantiationPattern() const { - // If it's a variable template specialization, find the template or partial - // specialization from which it was instantiated. - if (auto *VDTemplSpec = dyn_cast(this)) { - auto From = VDTemplSpec->getInstantiatedFrom(); - if (auto *VTD = From.dyn_cast()) { - while (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate()) { - if (NewVTD->isMemberSpecialization()) - break; - VTD = NewVTD; - } - return getDefinitionOrSelf(VTD->getTemplatedDecl()); - } - if (auto *VTPSD = - From.dyn_cast()) { - while (auto *NewVTPSD = VTPSD->getInstantiatedFromMember()) { - if (NewVTPSD->isMemberSpecialization()) - break; - VTPSD = NewVTPSD; - } - return getDefinitionOrSelf(VTPSD); - } - } + const VarDecl *VD = this; - if (MemberSpecializationInfo *MSInfo = getMemberSpecializationInfo()) { + // If this is an instantiated member, walk back to the template from which + // it was instantiated. + if (MemberSpecializationInfo *MSInfo = VD->getMemberSpecializationInfo()) { if (isTemplateInstantiation(MSInfo->getTemplateSpecializationKind())) { - VarDecl *VD = getInstantiatedFromStaticDataMember(); + VD = VD->getInstantiatedFromStaticDataMember(); while (auto *NewVD = VD->getInstantiatedFromStaticDataMember()) VD = NewVD; - return getDefinitionOrSelf(VD); } } - if (VarTemplateDecl *VarTemplate = getDescribedVarTemplate()) { - while (VarTemplate->getInstantiatedFromMemberTemplate()) { - if (VarTemplate->isMemberSpecialization()) + // If it's an instantiated variable template specialization, find the + // template or partial specialization from which it was instantiated. + if (auto *VDTemplSpec = dyn_cast(VD)) { + if (isTemplateInstantiation(VDTemplSpec->getTemplateSpecializationKind())) { + auto From = VDTemplSpec->getInstantiatedFrom(); + if (auto *VTD = From.dyn_cast()) { + while (!VTD->isMemberSpecialization()) { + auto *NewVTD = VTD->getInstantiatedFromMemberTemplate(); + if (!NewVTD) + break; + VTD = NewVTD; + } + return getDefinitionOrSelf(VTD->getTemplatedDecl()); + } + if (auto *VTPSD = + From.dyn_cast()) { + while (!VTPSD->isMemberSpecialization()) { + auto *NewVTPSD = VTPSD->getInstantiatedFromMember(); + if (!NewVTPSD) + break; + VTPSD = NewVTPSD; + } + return getDefinitionOrSelf(VTPSD); + } + } + } + + // If this is the pattern of a variable template, find where it was + // instantiated from. FIXME: Is this necessary? + if (VarTemplateDecl *VarTemplate = VD->getDescribedVarTemplate()) { + while (!VarTemplate->isMemberSpecialization()) { + auto *NewVT = VarTemplate->getInstantiatedFromMemberTemplate(); + if (!NewVT) break; - VarTemplate = VarTemplate->getInstantiatedFromMemberTemplate(); + VarTemplate = NewVT; } return getDefinitionOrSelf(VarTemplate->getTemplatedDecl()); } - return nullptr; + + if (VD == this) + return nullptr; + return getDefinitionOrSelf(const_cast(VD)); } VarDecl *VarDecl::getInstantiatedFromStaticDataMember() const { @@ -2475,6 +2488,17 @@ TemplateSpecializationKind VarDecl::getTemplateSpecializationKind() const { return TSK_Undeclared; } +TemplateSpecializationKind +VarDecl::getTemplateSpecializationKindForInstantiation() const { + if (MemberSpecializationInfo *MSI = getMemberSpecializationInfo()) + return MSI->getTemplateSpecializationKind(); + + if (const auto *Spec = dyn_cast(this)) + return Spec->getSpecializationKind(); + + return TSK_Undeclared; +} + SourceLocation VarDecl::getPointOfInstantiation() const { if (const auto *Spec = dyn_cast(this)) return Spec->getPointOfInstantiation(); @@ -2535,15 +2559,14 @@ void VarDecl::setTemplateSpecializationKind(TemplateSpecializationKind TSK, if (VarTemplateSpecializationDecl *Spec = dyn_cast(this)) { Spec->setSpecializationKind(TSK); - if (TSK != TSK_ExplicitSpecialization && PointOfInstantiation.isValid() && + if (TSK != TSK_ExplicitSpecialization && + PointOfInstantiation.isValid() && Spec->getPointOfInstantiation().isInvalid()) { Spec->setPointOfInstantiation(PointOfInstantiation); if (ASTMutationListener *L = getASTContext().getASTMutationListener()) L->InstantiationRequested(this); } - } - - if (MemberSpecializationInfo *MSI = getMemberSpecializationInfo()) { + } else if (MemberSpecializationInfo *MSI = getMemberSpecializationInfo()) { MSI->setTemplateSpecializationKind(TSK); if (TSK != TSK_ExplicitSpecialization && PointOfInstantiation.isValid() && MSI->getPointOfInstantiation().isInvalid()) { @@ -3472,12 +3495,13 @@ FunctionDecl *FunctionDecl::getTemplateInstantiationPattern() const { return nullptr; if (FunctionTemplateDecl *Primary = getPrimaryTemplate()) { - while (Primary->getInstantiatedFromMemberTemplate()) { - // If we have hit a point where the user provided a specialization of - // this template, we're done looking. - if (Primary->isMemberSpecialization()) + // If we hit a point where the user provided a specialization of this + // template, we're done looking. + while (!Primary->isMemberSpecialization()) { + auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate(); + if (!NewPrimary) break; - Primary = Primary->getInstantiatedFromMemberTemplate(); + Primary = NewPrimary; } return getDefinitionOrSelf(Primary->getTemplatedDecl()); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 9564c8f..2d9dbdd 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -15721,7 +15721,12 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc, "Invalid Expr argument to DoMarkVarDeclReferenced"); Var->setReferenced(); - TemplateSpecializationKind TSK = Var->getTemplateSpecializationKind(); + if (Var->isInvalidDecl()) + return; + + auto *MSI = Var->getMemberSpecializationInfo(); + TemplateSpecializationKind TSK = MSI ? MSI->getTemplateSpecializationKind() + : Var->getTemplateSpecializationKind(); bool OdrUseContext = isOdrUseContext(SemaRef); bool UsableInConstantExpr = @@ -15754,11 +15759,15 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc, (TSK == TSK_ExplicitInstantiationDeclaration && UsableInConstantExpr); if (TryInstantiating) { - SourceLocation PointOfInstantiation = Var->getPointOfInstantiation(); + SourceLocation PointOfInstantiation = + MSI ? MSI->getPointOfInstantiation() : Var->getPointOfInstantiation(); bool FirstInstantiation = PointOfInstantiation.isInvalid(); if (FirstInstantiation) { PointOfInstantiation = Loc; - Var->setTemplateSpecializationKind(TSK, PointOfInstantiation); + if (MSI) + MSI->setPointOfInstantiation(PointOfInstantiation); + else + Var->setTemplateSpecializationKind(TSK, PointOfInstantiation); } bool InstantiationDependent = false; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 939941a..58ad439 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3927,13 +3927,6 @@ DeclResult Sema::ActOnVarTemplateSpecialization( Specialization->setAccess(VarTemplate->getAccess()); } - // Link instantiations of static data members back to the template from - // which they were instantiated. - if (Specialization->isStaticDataMember()) - Specialization->setInstantiationOfStaticDataMember( - VarTemplate->getTemplatedDecl(), - Specialization->getSpecializationKind()); - return Specialization; } @@ -9198,7 +9191,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, if (!PrevTemplate) { if (!Prev || !Prev->isStaticDataMember()) { - // We expect to see a data data member here. + // We expect to see a static data member here. Diag(D.getIdentifierLoc(), diag::err_explicit_instantiation_not_known) << Name; for (LookupResult::iterator P = Previous.begin(), PEnd = Previous.end(); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 90ae898..653eb69 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3132,13 +3132,10 @@ TemplateDeclInstantiator::VisitClassTemplateSpecializationDecl( "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 nullptr; - ClassTemplateDecl *InstClassTemplate - = dyn_cast(Found.front()); + // of the class template. + ClassTemplateDecl *InstClassTemplate = + cast_or_null(SemaRef.FindInstantiatedDecl( + D->getLocation(), ClassTemplate, TemplateArgs)); if (!InstClassTemplate) return nullptr; @@ -3247,6 +3244,7 @@ TemplateDeclInstantiator::VisitClassTemplateSpecializationDecl( // 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. + // FIXME: This is wrong in standard C++. if (D->isThisDeclarationADefinition() && SemaRef.InstantiateClass(D->getLocation(), InstD, D, TemplateArgs, TSK_ImplicitInstantiation, @@ -3264,6 +3262,12 @@ Decl *TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl( assert(VarTemplate && "A template specialization without specialized template?"); + VarTemplateDecl *InstVarTemplate = + cast_or_null(SemaRef.FindInstantiatedDecl( + D->getLocation(), VarTemplate, TemplateArgs)); + if (!InstVarTemplate) + return nullptr; + // Substitute the current template arguments. const TemplateArgumentListInfo &TemplateArgsInfo = D->getTemplateArgsInfo(); VarTemplateArgsInfo.setLAngleLoc(TemplateArgsInfo.getLAngleLoc()); @@ -3275,28 +3279,33 @@ Decl *TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl( // Check that the template argument list is well-formed for this template. SmallVector Converted; - if (SemaRef.CheckTemplateArgumentList( - VarTemplate, VarTemplate->getBeginLoc(), - const_cast(VarTemplateArgsInfo), false, - Converted)) + if (SemaRef.CheckTemplateArgumentList(InstVarTemplate, D->getLocation(), + VarTemplateArgsInfo, false, Converted)) return nullptr; - // Find the variable template specialization declaration that - // corresponds to these arguments. + // Check whether we've already seen a declaration of this specialization. void *InsertPos = nullptr; - if (VarTemplateSpecializationDecl *VarSpec = VarTemplate->findSpecialization( - Converted, InsertPos)) - // If we already have a variable template specialization, return it. - return VarSpec; + VarTemplateSpecializationDecl *PrevDecl = + InstVarTemplate->findSpecialization(Converted, 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 nullptr; - return VisitVarTemplateSpecializationDecl(VarTemplate, D, InsertPos, - VarTemplateArgsInfo, Converted); + return VisitVarTemplateSpecializationDecl( + InstVarTemplate, D, InsertPos, VarTemplateArgsInfo, Converted, PrevDecl); } Decl *TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl( VarTemplateDecl *VarTemplate, VarDecl *D, void *InsertPos, const TemplateArgumentListInfo &TemplateArgsInfo, - ArrayRef Converted) { + ArrayRef Converted, + VarTemplateSpecializationDecl *PrevDecl) { // Do substitution on the type of the declaration TypeSourceInfo *DI = @@ -3323,8 +3332,8 @@ Decl *TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl( if (SubstQualifier(D, Var)) return nullptr; - SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, - Owner, StartingScope); + SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, Owner, + StartingScope, false, PrevDecl); return Var; } @@ -4339,7 +4348,19 @@ void Sema::BuildVariableInstantiation( const MultiLevelTemplateArgumentList &TemplateArgs, LateInstantiatedAttrVec *LateAttrs, DeclContext *Owner, LocalInstantiationScope *StartingScope, - bool InstantiatingVarTemplate) { + bool InstantiatingVarTemplate, + VarTemplateSpecializationDecl *PrevDeclForVarTemplateSpecialization) { + // Instantiating a partial specialization to produce a partial + // specialization. + bool InstantiatingVarTemplatePartialSpec = + isa(OldVar) && + isa(NewVar); + // Instantiating from a variable template (or partial specialization) to + // produce a variable template specialization. + bool InstantiatingSpecFromTemplate = + isa(NewVar) && + (OldVar->getDescribedVarTemplate() || + isa(OldVar)); // If we are instantiating a local extern declaration, the // instantiation belongs lexically to the containing function. @@ -4385,8 +4406,11 @@ void Sema::BuildVariableInstantiation( NewVar->getLocation(), OldVar->getPreviousDecl(), TemplateArgs)) Previous.addDecl(NewPrev); } else if (!isa(NewVar) && - OldVar->hasLinkage()) + OldVar->hasLinkage()) { LookupQualifiedName(Previous, NewVar->getDeclContext(), false); + } else if (PrevDeclForVarTemplateSpecialization) { + Previous.addDecl(PrevDeclForVarTemplateSpecialization); + } CheckVariableDeclaration(NewVar, Previous); if (!InstantiatingVarTemplate) { @@ -4402,23 +4426,44 @@ void Sema::BuildVariableInstantiation( // Link instantiations of static data members back to the template from // which they were instantiated. - if (NewVar->isStaticDataMember() && !InstantiatingVarTemplate) + // + // Don't do this when instantiating a template (we link the template itself + // back in that case) nor when instantiating a static data member template + // (that's not a member specialization). + if (NewVar->isStaticDataMember() && !InstantiatingVarTemplate && + !InstantiatingSpecFromTemplate) NewVar->setInstantiationOfStaticDataMember(OldVar, TSK_ImplicitInstantiation); + // If the pattern is an (in-class) explicit specialization, then the result + // is also an explicit specialization. + if (VarTemplateSpecializationDecl *OldVTSD = + dyn_cast(OldVar)) { + if (OldVTSD->getSpecializationKind() == TSK_ExplicitSpecialization && + !isa(OldVTSD)) + cast(NewVar)->setSpecializationKind( + TSK_ExplicitSpecialization); + } + // Forward the mangling number from the template to the instantiated decl. Context.setManglingNumber(NewVar, Context.getManglingNumber(OldVar)); Context.setStaticLocalNumber(NewVar, Context.getStaticLocalNumber(OldVar)); - // Delay instantiation of the initializer for variable templates or inline - // static data members until a definition of the variable is needed. We need - // it right away if the type contains 'auto'. - if ((!isa(NewVar) && - !InstantiatingVarTemplate && - !(OldVar->isInline() && OldVar->isThisDeclarationADefinition() && - !NewVar->isThisDeclarationADefinition())) || - NewVar->getType()->isUndeducedType()) + // Figure out whether to eagerly instantiate the initializer. + if (InstantiatingVarTemplate || InstantiatingVarTemplatePartialSpec) { + // We're producing a template. Don't instantiate the initializer yet. + } else if (NewVar->getType()->isUndeducedType()) { + // We need the type to complete the declaration of the variable. InstantiateVariableInitializer(NewVar, OldVar, TemplateArgs); + } else if (InstantiatingSpecFromTemplate || + (OldVar->isInline() && OldVar->isThisDeclarationADefinition() && + !NewVar->isThisDeclarationADefinition())) { + // Delay instantiation of the initializer for variable template + // specializations or inline static data members until a definition of the + // variable is needed. + } else { + InstantiateVariableInitializer(NewVar, OldVar, TemplateArgs); + } // Diagnose unused local variables with dependent types, where the diagnostic // will have been deferred. @@ -4518,15 +4563,23 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, if (Var->isInvalidDecl()) return; - VarTemplateSpecializationDecl *VarSpec = - dyn_cast(Var); - VarDecl *PatternDecl = nullptr, *Def = nullptr; + // Never instantiate an explicitly-specialized entity. + TemplateSpecializationKind TSK = + Var->getTemplateSpecializationKindForInstantiation(); + if (TSK == TSK_ExplicitSpecialization) + return; + + // Find the pattern and the arguments to substitute into it. + VarDecl *PatternDecl = Var->getTemplateInstantiationPattern(); + assert(PatternDecl && "no pattern for templated variable"); MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(Var); + VarTemplateSpecializationDecl *VarSpec = + dyn_cast(Var); if (VarSpec) { // If this is a variable template specialization, make sure that it is - // non-dependent, then find its instantiation pattern. + // non-dependent. bool InstantiationDependent = false; assert(!TemplateSpecializationType::anyDependentTemplateArguments( VarSpec->getTemplateArgsInfo(), InstantiationDependent) && @@ -4534,37 +4587,6 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, "not type-dependent"); (void)InstantiationDependent; - // Find the variable initialization that we'll be substituting. If the - // pattern was instantiated from a member template, look back further to - // find the real pattern. - assert(VarSpec->getSpecializedTemplate() && - "Specialization without specialized template?"); - llvm::PointerUnion PatternPtr = - VarSpec->getSpecializedTemplateOrPartial(); - if (PatternPtr.is()) { - VarTemplatePartialSpecializationDecl *Tmpl = - PatternPtr.get(); - while (VarTemplatePartialSpecializationDecl *From = - Tmpl->getInstantiatedFromMember()) { - if (Tmpl->isMemberSpecialization()) - break; - - Tmpl = From; - } - PatternDecl = Tmpl; - } else { - VarTemplateDecl *Tmpl = PatternPtr.get(); - while (VarTemplateDecl *From = - Tmpl->getInstantiatedFromMemberTemplate()) { - if (Tmpl->isMemberSpecialization()) - break; - - Tmpl = From; - } - PatternDecl = Tmpl->getTemplatedDecl(); - } - // If this is a static data member template, there might be an // uninstantiated initializer on the declaration. If so, instantiate // it now. @@ -4608,20 +4630,12 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, Local.Exit(); GlobalInstantiations.perform(); } - - // Find actual definition - Def = PatternDecl->getDefinition(getASTContext()); } else { - // If this is a static data member, find its out-of-line definition. - assert(Var->isStaticDataMember() && "not a static data member?"); - PatternDecl = Var->getInstantiatedFromStaticDataMember(); - - assert(PatternDecl && "data member was not instantiated from a template?"); - assert(PatternDecl->isStaticDataMember() && "not a static data member?"); - Def = PatternDecl->getDefinition(); + assert(Var->isStaticDataMember() && PatternDecl->isStaticDataMember() && + "not a static data member?"); } - TemplateSpecializationKind TSK = Var->getTemplateSpecializationKind(); + VarDecl *Def = PatternDecl->getDefinition(getASTContext()); // If we don't have a definition of the variable template, we won't perform // any instantiation. Rather, we rely on the user to instantiate this @@ -4643,7 +4657,6 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, } return; } - } // FIXME: We need to track the instantiation stack in order to know which @@ -4655,11 +4668,6 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, /*Complain*/DefinitionRequired)) return; - - // Never instantiate an explicit specialization. - if (TSK == TSK_ExplicitSpecialization) - return; - // C++11 [temp.explicit]p10: // Except for inline functions, const variables of literal types, variables // of reference types, [...] explicit instantiation declarations @@ -5495,7 +5503,8 @@ void Sema::PerformPendingInstantiations(bool LocalOnly) { // Check if the most recent declaration has changed the specialization kind // and removed the need for implicit instantiation. - switch (Var->getMostRecentDecl()->getTemplateSpecializationKind()) { + switch (Var->getMostRecentDecl() + ->getTemplateSpecializationKindForInstantiation()) { case TSK_Undeclared: llvm_unreachable("Cannot instantitiate an undeclared specialization."); case TSK_ExplicitInstantiationDeclaration: diff --git a/clang/test/CXX/drs/dr7xx.cpp b/clang/test/CXX/drs/dr7xx.cpp index 2760bda..2d9d396 100644 --- a/clang/test/CXX/drs/dr7xx.cpp +++ b/clang/test/CXX/drs/dr7xx.cpp @@ -1,7 +1,8 @@ // RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++1z %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors namespace dr705 { // dr705: yes namespace N { @@ -66,7 +67,7 @@ namespace dr727 { // dr727: partial struct D { template struct C { typename T::error e; }; // expected-error {{no members}} template void f() { T::error; } // expected-error {{no members}} - template static const int N = T::error; // expected-error 2{{no members}} expected-error 0-1{{C++14}} + template static const int N = T::error; // expected-error {{no members}} expected-error 0-1{{C++14}} template<> struct C {}; template<> void f() {} @@ -79,7 +80,7 @@ namespace dr727 { // dr727: partial void d(D di) { D::C(); di.f(); - int a = D::N; // FIXME: expected-note {{instantiation of}} + int a = D::N; D::C(); int b = D::N; @@ -88,6 +89,98 @@ namespace dr727 { // dr727: partial di.f(); // expected-note {{instantiation of}} int c = D::N; // expected-note {{instantiation of}} } + + namespace mixed_inner_outer_specialization { +#if __cplusplus >= 201103L + template struct A { + template constexpr int f() const { return 1; } + template<> constexpr int f<0>() const { return 2; } + }; + template<> template constexpr int A<0>::f() const { return 3; } + template<> template<> constexpr int A<0>::f<0>() const { return 4; } + static_assert(A<1>().f<1>() == 1, ""); + static_assert(A<1>().f<0>() == 2, ""); + static_assert(A<0>().f<1>() == 3, ""); + static_assert(A<0>().f<0>() == 4, ""); +#endif + +#if __cplusplus >= 201402L + template struct B { + template static const int u = 1; + template<> static const int u<0> = 2; // expected-note {{here}} + + // Note that in C++17 onwards, these are implicitly inline, and so the + // initializer of v<0> is not instantiated with the declaration. In + // C++14, v<0> is a non-defining declaration and its initializer is + // instantiated with the class. + template static constexpr int v = 1; + template<> static constexpr int v<0> = 2; // #v0 + + template static const inline int w = 1; // expected-error 0-1{{C++17 extension}} + template<> static const inline int w<0> = 2; // expected-error 0-1{{C++17 extension}} + }; + + template<> template constexpr int B<0>::u = 3; + template<> template<> constexpr int B<0>::u<0> = 4; // expected-error {{already has an initializer}} + + template<> template constexpr int B<0>::v = 3; + template<> template<> constexpr int B<0>::v<0> = 4; +#if __cplusplus < 201702L + // expected-error@-2 {{already has an initializer}} + // expected-note@#v0 {{here}} +#endif + + template<> template constexpr int B<0>::w = 3; + template<> template<> constexpr int B<0>::w<0> = 4; + + static_assert(B<1>().u<1> == 1, ""); + static_assert(B<1>().u<0> == 2, ""); + static_assert(B<0>().u<1> == 3, ""); + + static_assert(B<1>().v<1> == 1, ""); + static_assert(B<1>().v<0> == 2, ""); + static_assert(B<0>().v<1> == 3, ""); + static_assert(B<0>().v<0> == 4, ""); +#if __cplusplus < 201702L + // expected-error@-2 {{failed}} +#endif + + static_assert(B<1>().w<1> == 1, ""); + static_assert(B<1>().w<0> == 2, ""); + static_assert(B<0>().w<1> == 3, ""); + static_assert(B<0>().w<0> == 4, ""); +#endif + } + + template struct Collision { + // FIXME: Missing diagnostic for duplicate function explicit specialization declaration. + template int f1(); + template<> int f1(); + template<> int f1(); + + // FIXME: Missing diagnostic for fucntion redefinition! + template int f2(); + template<> int f2() {} + template<> int f2() {} + + template static int v1; // expected-error 0-1{{C++14 extension}} + template<> static int v1; // expected-note {{previous}} + template<> static int v1; // expected-error {{duplicate member}} + + template static inline int v2; // expected-error 0-1{{C++17 extension}} expected-error 0-1{{C++14 extension}} + template<> static inline int v2; // expected-error 0-1{{C++17 extension}} expected-note {{previous}} + template<> static inline int v2; // expected-error 0-1{{C++17 extension}} expected-error {{duplicate member}} + + // FIXME: Missing diagnostic for duplicate class explicit specialization. + template struct S1; + template<> struct S1; + template<> struct S1; + + template struct S2; + template<> struct S2 {}; // expected-note {{previous}} + template<> struct S2 {}; // expected-error {{redefinition}} + }; + Collision c; // expected-note {{in instantiation of}} } namespace dr777 { // dr777: 3.7 diff --git a/clang/test/PCH/cxx-templates.cpp b/clang/test/PCH/cxx-templates.cpp index f41646d..78898f7 100644 --- a/clang/test/PCH/cxx-templates.cpp +++ b/clang/test/PCH/cxx-templates.cpp @@ -1,21 +1,21 @@ // Test this without pch. -// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -include %S/cxx-templates.h -verify %s -// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -include %S/cxx-templates.h %s -emit-llvm -o - -DNO_ERRORS | FileCheck %s +// RUN: %clang_cc1 -std=c++17 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -include %S/cxx-templates.h -verify %s +// RUN: %clang_cc1 -std=c++17 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -include %S/cxx-templates.h %s -emit-llvm -o - -DNO_ERRORS | FileCheck %s // Test with pch. -// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -x c++-header -emit-pch -o %t %S/cxx-templates.h -// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -include-pch %t -verify %s -// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -include-pch %t %s -emit-llvm -o - -error-on-deserialized-decl doNotDeserialize -DNO_ERRORS | FileCheck %s +// RUN: %clang_cc1 -std=c++17 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -x c++-header -emit-pch -o %t %S/cxx-templates.h +// RUN: %clang_cc1 -std=c++17 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -include-pch %t -verify %s +// RUN: %clang_cc1 -std=c++17 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -include-pch %t %s -emit-llvm -o - -error-on-deserialized-decl doNotDeserialize -DNO_ERRORS | FileCheck %s // Test with modules. -// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -fmodules -x c++-header -emit-pch -o %t %S/cxx-templates.h -// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -fmodules -include-pch %t -verify %s -// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -fmodules -include-pch %t %s -emit-llvm -o - -error-on-deserialized-decl doNotDeserialize -DNO_ERRORS -fmodules-ignore-macro=NO_ERRORS | FileCheck %s +// RUN: %clang_cc1 -std=c++17 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -fmodules -x c++-header -emit-pch -o %t %S/cxx-templates.h +// RUN: %clang_cc1 -std=c++17 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -fmodules -include-pch %t -verify %s +// RUN: %clang_cc1 -std=c++17 -triple %itanium_abi_triple -fcxx-exceptions -fexceptions -fmodules -include-pch %t %s -emit-llvm -o - -error-on-deserialized-decl doNotDeserialize -DNO_ERRORS -fmodules-ignore-macro=NO_ERRORS | FileCheck %s // Test with pch and delayed template parsing. -// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fcxx-exceptions -fdelayed-template-parsing -fexceptions -x c++-header -emit-pch -o %t %S/cxx-templates.h -// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fcxx-exceptions -fdelayed-template-parsing -fexceptions -include-pch %t -verify %s -// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fcxx-exceptions -fdelayed-template-parsing -fexceptions -include-pch %t %s -emit-llvm -o - -DNO_ERRORS | FileCheck %s +// RUN: %clang_cc1 -std=c++17 -triple %itanium_abi_triple -fcxx-exceptions -fdelayed-template-parsing -fexceptions -x c++-header -emit-pch -o %t %S/cxx-templates.h +// RUN: %clang_cc1 -std=c++17 -triple %itanium_abi_triple -fcxx-exceptions -fdelayed-template-parsing -fexceptions -include-pch %t -verify %s +// RUN: %clang_cc1 -std=c++17 -triple %itanium_abi_triple -fcxx-exceptions -fdelayed-template-parsing -fexceptions -include-pch %t %s -emit-llvm -o - -DNO_ERRORS | FileCheck %s // CHECK: define weak_odr {{.*}}void @_ZN2S4IiE1mEv // CHECK: define linkonce_odr {{.*}}void @_ZN2S3IiE1mEv @@ -143,14 +143,16 @@ namespace ClassScopeExplicitSpecializations { template int A<4>::f<1>() const; // expected-note@cxx-templates.h:403 2{{here}} - static_assert(A<0>().f<1>() == 3, ""); static_assert(A<0>().f<0>() == 4, ""); - static_assert(A<1>().f<1>() == 1, ""); + static_assert(A<0>().f<1>() == 5, ""); + static_assert(A<0>().f<2>() == 3, ""); static_assert(A<1>().f<0>() == 2, ""); - static_assert(A<2>().f<1>() == 1, ""); + static_assert(A<1>().f<1>() == 1, ""); + static_assert(A<1>().f<2>() == 1, ""); static_assert(A<2>().f<0>() == 2, ""); - static_assert(A<3>().f<1>() == 1, ""); + static_assert(A<2>().f<1>() == 1, ""); static_assert(A<3>().f<0>() == 2, ""); - static_assert(A<4>().f<1>() == 1, ""); + static_assert(A<3>().f<1>() == 1, ""); static_assert(A<4>().f<0>() == 2, ""); + static_assert(A<4>().f<1>() == 1, ""); } diff --git a/clang/test/PCH/cxx-templates.h b/clang/test/PCH/cxx-templates.h index f211142..bfdb3b8 100644 --- a/clang/test/PCH/cxx-templates.h +++ b/clang/test/PCH/cxx-templates.h @@ -402,8 +402,11 @@ namespace ClassScopeExplicitSpecializations { template constexpr int f() const { return 1; } template<> constexpr int f<0>() const { return 2; } }; + template<> template constexpr int A<0>::f() const { return 3; } template<> template<> constexpr int A<0>::f<0>() const { return 4; } + template<> template<> constexpr int A<0>::f<1>() const { return 5; } + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winstantiation-after-specialization" template int A<2>::f<0>() const; @@ -411,4 +414,28 @@ namespace ClassScopeExplicitSpecializations { template int A<2>::f<1>() const; extern template int A<3>::f<0>() const; extern template int A<3>::f<1>() const; + + template struct B { + template static const int v = 1; + template static const int v = 2; + template<> static const int v = 3; + + template static constexpr int w = 1; + template static constexpr int w = 2; + template<> static constexpr int w = 3; + }; + + template<> template constexpr int B<0>::v = 4; + template<> template constexpr int B<0>::v = 5; + template<> template constexpr int B<0>::v = 6; + // This is ill-formed: the initializer of v is instantiated with the + // class. + //template<> template<> constexpr int B<0>::v = 7; + template<> template<> constexpr int B<0>::v = 8; + + template<> template constexpr int B<0>::w = 4; + template<> template constexpr int B<0>::w = 5; + template<> template constexpr int B<0>::w = 6; + template<> template<> constexpr int B<0>::w = 7; + template<> template<> constexpr int B<0>::w = 8; } diff --git a/clang/test/SemaTemplate/explicit-specialization-member.cpp b/clang/test/SemaTemplate/explicit-specialization-member.cpp index e84364a..5dc8118 100644 --- a/clang/test/SemaTemplate/explicit-specialization-member.cpp +++ b/clang/test/SemaTemplate/explicit-specialization-member.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -fcxx-exceptions +// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s -fcxx-exceptions template struct X0 { typedef T* type; @@ -70,12 +70,26 @@ namespace PR41607 { static constexpr int f() { return N; } }; - template static int a; // expected-note 2{{}} - template<> static constexpr int a<> = 42; + template static int a; + template<> static constexpr int a<> = N; + + template static inline int b; + template<> static inline constexpr int b<> = N; + + template static constexpr int f(); + template<> static constexpr int f() { + return N; + } }; static_assert(Outer<123>::Inner<>::f() == 123, ""); static_assert(Outer<123>::Inner<>::f() != 125, ""); - // FIXME: The class-scope explicit specialization of the variable template doesn't work! - static_assert(Outer<123>::a<> == 42, ""); // expected-error {{}} expected-note {{}} - static_assert(Outer<123>::a<> != 43, ""); // expected-error {{}} expected-note {{}} + + static_assert(Outer<123>::a<> == 123, ""); + static_assert(Outer<123>::a<> != 125, ""); + + static_assert(Outer<123>::b<> == 123, ""); + static_assert(Outer<123>::b<> != 125, ""); + + static_assert(Outer<123>::f<>() == 123, ""); + static_assert(Outer<123>::f<>() != 125, ""); } -- 2.7.4