From: Douglas Gregor Date: Thu, 8 Oct 2009 07:24:58 +0000 (+0000) Subject: For instantiations of static data members of class templates, keep X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=251b4ff2578e26959a4c036140ccd61c5e9292f2;p=clang For instantiations of static data members of class templates, keep track of the kind of specialization or instantiation. Also, check the scope of the specialization and ensure that a specialization declaration without an initializer is not a definition. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@83533 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index bb9e3861be..63f909146e 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -154,7 +154,8 @@ class ASTContext { /// /// This data structure stores the mapping from instantiations of static /// data members to the static data member representations within the - /// class template from which they were instantiated. + /// class template from which they were instantiated along with the kind + /// of instantiation or specialization (a TemplateSpecializationKind - 1). /// /// Given the following example: /// @@ -172,8 +173,9 @@ class ASTContext { /// /// This mapping will contain an entry that maps from the VarDecl for /// X::value to the corresponding VarDecl for X::value (within the - /// class template X). - llvm::DenseMap InstantiatedFromStaticDataMember; + /// class template X) and will be marked TSK_ImplicitInstantiation. + llvm::DenseMap + InstantiatedFromStaticDataMember; /// \brief Keeps track of the UnresolvedUsingDecls from which UsingDecls /// where created during instantiation. @@ -265,11 +267,12 @@ public: /// \brief If this variable is an instantiated static data member of a /// class template specialization, returns the templated static data member /// from which it was instantiated. - VarDecl *getInstantiatedFromStaticDataMember(VarDecl *Var); + MemberSpecializationInfo *getInstantiatedFromStaticDataMember(VarDecl *Var); /// \brief Note that the static data member \p Inst is an instantiation of /// the static data member template \p Tmpl of a class template. - void setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl); + void setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl, + TemplateSpecializationKind TSK); /// \brief If this using decl is instantiated from an unresolved using decl, /// return it. diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index f2ffccd7e0..0e91511dbd 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -300,6 +300,29 @@ struct EvaluatedStmt { APValue Evaluated; }; +// \brief Describes the kind of template specialization that a +// particular template specialization declaration represents. +enum TemplateSpecializationKind { + /// This template specialization was formed from a template-id but + /// has not yet been declared, defined, or instantiated. + TSK_Undeclared = 0, + /// This template specialization was implicitly instantiated from a + /// template. (C++ [temp.inst]). + TSK_ImplicitInstantiation, + /// This template specialization was declared or defined by an + /// explicit specialization (C++ [temp.expl.spec]) or partial + /// specialization (C++ [temp.class.spec]). + TSK_ExplicitSpecialization, + /// This template specialization was instantiated from a template + /// due to an explicit instantiation declaration request + /// (C++0x [temp.explicit]). + TSK_ExplicitInstantiationDeclaration, + /// This template specialization was instantiated from a template + /// due to an explicit instantiation definition request + /// (C++ [temp.explicit]). + TSK_ExplicitInstantiationDefinition +}; + /// VarDecl - An instance of this class is created to represent a variable /// declaration or definition. class VarDecl : public DeclaratorDecl, public Redeclarable { @@ -566,6 +589,14 @@ public: /// from which it was instantiated. VarDecl *getInstantiatedFromStaticDataMember(); + /// \brief If this variable is a static data member, determine what kind of + /// template specialization or instantiation this is. + TemplateSpecializationKind getTemplateSpecializationKind(); + + /// \brief For a static data member that was instantiated from a static + /// data member of a class template, set the template specialiation kind. + void setTemplateSpecializationKind(TemplateSpecializationKind TSK); + /// isFileVarDecl - Returns true for file scoped variable declaration. bool isFileVarDecl() const { if (getKind() != Decl::Var) @@ -754,29 +785,6 @@ public: static bool classof(const OriginalParmVarDecl *D) { return true; } }; -// \brief Describes the kind of template specialization that a -// particular template specialization declaration represents. -enum TemplateSpecializationKind { - /// This template specialization was formed from a template-id but - /// has not yet been declared, defined, or instantiated. - TSK_Undeclared = 0, - /// This template specialization was implicitly instantiated from a - /// template. (C++ [temp.inst]). - TSK_ImplicitInstantiation, - /// This template specialization was declared or defined by an - /// explicit specialization (C++ [temp.expl.spec]) or partial - /// specialization (C++ [temp.class.spec]). - TSK_ExplicitSpecialization, - /// This template specialization was instantiated from a template - /// due to an explicit instantiation declaration request - /// (C++0x [temp.explicit]). - TSK_ExplicitInstantiationDeclaration, - /// This template specialization was instantiated from a template - /// due to an explicit instantiation definition request - /// (C++ [temp.explicit]). - TSK_ExplicitInstantiationDefinition -}; - /// FunctionDecl - An instance of this class is created to represent a /// function declaration or definition. /// diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 3273f2b783..09f39513b4 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -948,9 +948,8 @@ def err_template_spec_redecl_global_scope : Error< "%select{class template|class template partial|function template|member " "function|static data member|member class}0 specialization of %1 must occur " "at global scope">; -def err_function_spec_not_instantiated : Error< - "specialization of member function %q0 does not specialize an instantiated " - "member function">; +def err_spec_member_not_instantiated : Error< + "specialization of member %q0 does not specialize an instantiated member">; def note_specialized_decl : Note<"attempt to specialize declaration here">; // C++ class template specializations and out-of-line definitions diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 1fa492c874..85b4fd6d6c 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -232,9 +232,10 @@ void ASTContext::InitBuiltinTypes() { InitBuiltinType(NullPtrTy, BuiltinType::NullPtr); } -VarDecl *ASTContext::getInstantiatedFromStaticDataMember(VarDecl *Var) { +MemberSpecializationInfo * +ASTContext::getInstantiatedFromStaticDataMember(VarDecl *Var) { assert(Var->isStaticDataMember() && "Not a static data member"); - llvm::DenseMap::iterator Pos + llvm::DenseMap::iterator Pos = InstantiatedFromStaticDataMember.find(Var); if (Pos == InstantiatedFromStaticDataMember.end()) return 0; @@ -243,12 +244,14 @@ VarDecl *ASTContext::getInstantiatedFromStaticDataMember(VarDecl *Var) { } void -ASTContext::setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl) { +ASTContext::setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl, + TemplateSpecializationKind TSK) { assert(Inst->isStaticDataMember() && "Not a static data member"); assert(Tmpl->isStaticDataMember() && "Not a static data member"); assert(!InstantiatedFromStaticDataMember[Inst] && "Already noted what static data member was instantiated from"); - InstantiatedFromStaticDataMember[Inst] = Tmpl; + InstantiatedFromStaticDataMember[Inst] + = new (*this) MemberSpecializationInfo(Tmpl, TSK); } UnresolvedUsingDecl * diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a5c9fa4bac..5584e4c3da 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -371,7 +371,26 @@ SourceRange VarDecl::getSourceRange() const { } VarDecl *VarDecl::getInstantiatedFromStaticDataMember() { - return getASTContext().getInstantiatedFromStaticDataMember(this); + if (MemberSpecializationInfo *MSI + = getASTContext().getInstantiatedFromStaticDataMember(this)) + return cast(MSI->getInstantiatedFrom()); + + return 0; +} + +TemplateSpecializationKind VarDecl::getTemplateSpecializationKind() { + if (MemberSpecializationInfo *MSI + = getASTContext().getInstantiatedFromStaticDataMember(this)) + return MSI->getTemplateSpecializationKind(); + + return TSK_Undeclared; +} + +void VarDecl::setTemplateSpecializationKind(TemplateSpecializationKind TSK) { + MemberSpecializationInfo *MSI + = getASTContext().getInstantiatedFromStaticDataMember(this); + assert(MSI && "Not an instantiated static data member?"); + MSI->setTemplateSpecializationKind(TSK); } bool VarDecl::isTentativeDefinition(ASTContext &Context) const { diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 6142eacd57..08cb06db56 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2519,8 +2519,7 @@ public: unsigned NumExplicitTemplateArgs, SourceLocation RAngleLoc, NamedDecl *&PrevDecl); - bool CheckMemberFunctionSpecialization(CXXMethodDecl *FD, - NamedDecl *&PrevDecl); + bool CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl); virtual DeclResult ActOnExplicitInstantiation(Scope *S, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index d612bb8351..2070335a61 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2193,7 +2193,6 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, // Match up the template parameter lists with the scope specifier, then // determine whether we have a template or a template specialization. - // FIXME: Actually record when this is an explicit specialization! bool isExplicitSpecialization = false; if (TemplateParameterList *TemplateParams = MatchTemplateParametersToScopeSpecifier( @@ -2259,7 +2258,7 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, !(NewVD->hasLinkage() && isOutOfScopePreviousDeclaration(PrevDecl, DC, Context))) PrevDecl = 0; - + // Merge the decl with the existing one if appropriate. if (PrevDecl) { if (isa(PrevDecl) && D.getCXXScopeSpec().isSet()) { @@ -2281,6 +2280,11 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, CheckVariableDeclaration(NewVD, PrevDecl, Redeclaration); + // This is an explicit specialization of a static data member. Check it. + if (isExplicitSpecialization && !NewVD->isInvalidDecl() && + CheckMemberSpecialization(NewVD, PrevDecl)) + NewVD->setInvalidDecl(); + // attributes declared post-definition are currently ignored if (PrevDecl) { const VarDecl *Def = 0, *PrevVD = dyn_cast(PrevDecl); @@ -2837,8 +2841,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, PrevDecl)) NewFD->setInvalidDecl(); } else if (isExplicitSpecialization && isa(NewFD) && - CheckMemberFunctionSpecialization(cast(NewFD), - PrevDecl)) + CheckMemberSpecialization(NewFD, PrevDecl)) NewFD->setInvalidDecl(); // Perform semantic checking on the function declaration. @@ -3400,6 +3403,15 @@ void Sema::ActOnUninitializedDecl(DeclPtrTy dcl, return; } + // C++ [temp.expl.spec]p15: + // An explicit specialization of a static data member of a template is a + // definition if the declaration includes an initializer; otherwise, it + // is a declaration. + if (Var->isStaticDataMember() && + Var->getInstantiatedFromStaticDataMember() && + Var->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return; + // C++ [dcl.init]p9: // If no initializer is specified for an object, and the object // is of (possibly cv-qualified) non-POD class type (or array diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 3bd72d07d2..c9525f39e5 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -6209,11 +6209,9 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) { if (VarDecl *Var = dyn_cast(D)) { // Implicit instantiation of static data members of class templates. - // FIXME: distinguish between implicit instantiations (which we need to - // actually instantiate) and explicit specializations. - // FIXME: extern templates if (Var->isStaticDataMember() && - Var->getInstantiatedFromStaticDataMember()) + Var->getInstantiatedFromStaticDataMember() && + Var->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) PendingImplicitInstantiations.push_back(std::make_pair(Var, Loc)); // FIXME: keep track of references to static data? diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 9d3efa60b0..5c6ec0004a 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2365,8 +2365,9 @@ static TemplateSpecializationKind getTemplateSpecializationKind(NamedDecl *D) { return CTS->getSpecializationKind(); if (FunctionDecl *Function = dyn_cast(D)) return Function->getTemplateSpecializationKind(); - - // FIXME: static data members! + if (VarDecl *Var = dyn_cast(D)) + return Var->getTemplateSpecializationKind(); + // FIXME: member classes of class templates! return TSK_Undeclared; } @@ -3118,7 +3119,7 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD, return false; } -/// \brief Perform semantic analysis for the given member function +/// \brief Perform semantic analysis for the given non-template member /// specialization. /// /// This routine performs all of the semantic analysis required for an @@ -3126,27 +3127,45 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD, /// the function declaration \p FD will become a member function /// specialization. /// -/// \param FD the function declaration, which will be updated to become a -/// function template specialization. +/// \param Member the member declaration, which will be updated to become a +/// specialization. /// /// \param PrevDecl the set of declarations, one of which may be specialized /// by this function specialization. bool -Sema::CheckMemberFunctionSpecialization(CXXMethodDecl *FD, - NamedDecl *&PrevDecl) { - // Try to find the member function we are instantiating. - CXXMethodDecl *Instantiation = 0; - for (OverloadIterator Ovl(PrevDecl), OvlEnd; Ovl != OvlEnd; ++Ovl) { - if (CXXMethodDecl *Method = dyn_cast(*Ovl)) { - if (Context.hasSameType(FD->getType(), Method->getType())) { - Instantiation = Method; - break; +Sema::CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl) { + assert(!isa(Member) && "Only for non-template members"); + + // Try to find the member we are instantiating. + NamedDecl *Instantiation = 0; + NamedDecl *InstantiatedFrom = 0; + if (!PrevDecl) { + // Nowhere to look anyway. + } else if (FunctionDecl *Function = dyn_cast(Member)) { + for (OverloadIterator Ovl(PrevDecl), OvlEnd; Ovl != OvlEnd; ++Ovl) { + if (CXXMethodDecl *Method = dyn_cast(*Ovl)) { + if (Context.hasSameType(Function->getType(), Method->getType())) { + Instantiation = Method; + InstantiatedFrom = Method->getInstantiatedFromMemberFunction(); + break; + } + } + } + } else if (isa(Member)) { + if (VarDecl *PrevVar = dyn_cast(PrevDecl)) + if (PrevVar->isStaticDataMember()) { + Instantiation = PrevDecl; + InstantiatedFrom = PrevVar->getInstantiatedFromStaticDataMember(); } + } else if (isa(Member)) { + if (CXXRecordDecl *PrevRecord = dyn_cast(PrevDecl)) { + Instantiation = PrevDecl; + InstantiatedFrom = PrevRecord->getInstantiatedFromMemberClass(); } } if (!Instantiation) { - // There is no previous declaration that matches. Since member function + // There is no previous declaration that matches. Since member // specializations are always out-of-line, the caller will complain about // this mismatch later. return false; @@ -3155,30 +3174,43 @@ Sema::CheckMemberFunctionSpecialization(CXXMethodDecl *FD, // FIXME: Check if the prior declaration has a point of instantiation. // If so, we have run afoul of C++ [temp.expl.spec]p6. - // Make sure that this is a specialization of a member function. - FunctionDecl *FunctionInTemplate - = Instantiation->getInstantiatedFromMemberFunction(); - if (!FunctionInTemplate) { - Diag(FD->getLocation(), diag::err_function_spec_not_instantiated) - << FD; + // Make sure that this is a specialization of a member. + if (!InstantiatedFrom) { + Diag(Member->getLocation(), diag::err_spec_member_not_instantiated) + << Member; Diag(Instantiation->getLocation(), diag::note_specialized_decl); return true; } // Check the scope of this explicit specialization. if (CheckTemplateSpecializationScope(*this, - FunctionInTemplate, - Instantiation, FD->getLocation(), + InstantiatedFrom, + Instantiation, Member->getLocation(), false, TSK_ExplicitSpecialization)) return true; // FIXME: Check for specialization-after-instantiation errors and such. - // Note that this function is an explicit instantiation of a member function. - Instantiation->setTemplateSpecializationKind(TSK_ExplicitSpecialization); - FD->setInstantiationOfMemberFunction(FunctionInTemplate, - TSK_ExplicitSpecialization); - + // Note that this is an explicit instantiation of a member. + if (isa(Member)) { + // FIXME: We're also setting the original instantiation we found to be + // an explicit specialization, although I'd rather not have to do this. + cast(Instantiation)->setTemplateSpecializationKind( + TSK_ExplicitSpecialization); + cast(Member)->setInstantiationOfMemberFunction( + cast(InstantiatedFrom), + TSK_ExplicitSpecialization); + } else if (isa(Member)) { + Context.setInstantiatedFromStaticDataMember(cast(Member), + cast(InstantiatedFrom), + TSK_ExplicitSpecialization); + } else { + assert(isa(Member) && "Only member classes remain"); + // FIXME: Record TSK_ExplicitSpecialization. + cast(Member)->setInstantiationOfMemberClass( + cast(InstantiatedFrom)); + } + // Save the caller the trouble of having to figure out which declaration // this specialization matches. PrevDecl = Instantiation; @@ -3547,7 +3579,8 @@ Sema::DeclResult Sema::ActOnExplicitInstantiation(Scope *S, } // Instantiate static data member. - // FIXME: Note that this is an explicit instantiation. + // FIXME: Check for prior specializations and such. + Prev->setTemplateSpecializationKind(TSK); if (TSK == TSK_ExplicitInstantiationDefinition) InstantiateStaticDataMemberDefinition(D.getIdentifierLoc(), Prev, false); diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index ee6600adc6..2f7af60e05 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -982,9 +982,12 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation, if (!Function->getBody() && TSK != TSK_ExplicitInstantiationDeclaration) InstantiateFunctionDefinition(PointOfInstantiation, Function); } else if (VarDecl *Var = dyn_cast(*D)) { - if (Var->isStaticDataMember() && - TSK != TSK_ExplicitInstantiationDeclaration) - InstantiateStaticDataMemberDefinition(PointOfInstantiation, Var); + if (Var->isStaticDataMember()) { + Var->setTemplateSpecializationKind(TSK); + + if (TSK != TSK_ExplicitInstantiationDeclaration) + InstantiateStaticDataMemberDefinition(PointOfInstantiation, Var); + } } else if (CXXRecordDecl *Record = dyn_cast(*D)) { if (Record->isInjectedClassName()) continue; diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index fcacb4a3f0..bafcea054e 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -155,6 +155,12 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { Owner->addDecl(Var); } + // Link instantiations of static data members back to the template from + // which they were instantiated. + if (Var->isStaticDataMember()) + SemaRef.Context.setInstantiatedFromStaticDataMember(Var, D, + TSK_ImplicitInstantiation); + if (D->getInit()) { OwningExprResult Init = SemaRef.SubstExpr(D->getInit(), TemplateArgs); @@ -191,11 +197,6 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { } else if (!Var->isStaticDataMember() || Var->isOutOfLine()) SemaRef.ActOnUninitializedDecl(Sema::DeclPtrTy::make(Var), false); - // Link instantiations of static data members back to the template from - // which they were instantiated. - if (Var->isStaticDataMember()) - SemaRef.Context.setInstantiatedFromStaticDataMember(Var, D); - return Var; } @@ -977,6 +978,10 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, assert(!Function->getBody() && "Already instantiated!"); + // Never instantiate an explicit specialization. + if (Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return; + // Find the function body that we'll be substituting. const FunctionDecl *PatternDecl = 0; if (FunctionTemplateDecl *Primary = Function->getPrimaryTemplate()) { @@ -1084,7 +1089,6 @@ void Sema::InstantiateStaticDataMemberDefinition( return; // Find the out-of-line definition of this static data member. - // FIXME: Do we have to look for specializations separately? VarDecl *Def = Var->getInstantiatedFromStaticDataMember(); bool FoundOutOfLineDef = false; assert(Def && "This data member was not instantiated from a template?"); @@ -1106,7 +1110,17 @@ void Sema::InstantiateStaticDataMemberDefinition( return; } - // FIXME: extern templates + // Never instantiate an explicit specialization. + if (Def->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return; + + // C++0x [temp.explicit]p9: + // Except for inline functions, other explicit instantiation declarations + // have the effect of suppressing the implicit instantiation of the entity + // to which they refer. + if (Def->getTemplateSpecializationKind() + == TSK_ExplicitInstantiationDeclaration) + return; InstantiatingTemplate Inst(*this, PointOfInstantiation, Var); if (Inst) diff --git a/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp index 413d0b949d..438c41159a 100644 --- a/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp +++ b/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp @@ -49,7 +49,7 @@ namespace N0 { template struct X0 { // expected-note 2{{here}} - static T member; + static T member; // expected-note{{here}} void f1(T t) { // expected-note{{explicitly specialized declaration is here}} t = 17; @@ -106,16 +106,27 @@ void test_x0_cvvoid(N0::X0 x0, const volatile void *cvp) { x0.f1(cvp); // okay: we've explicitly specialized } -#if 0 -// FIXME: update the remainder of this test to check for scopes properly. // -- static data member of a class template -template<> -NonDefaultConstructible X0::member = 17; +namespace N0 { + // This actually tests p15; the following is a declaration, not a definition. + template<> + NonDefaultConstructible X0::member; + + template<> long X0::member = 17; + + template<> float X0::member; +} NonDefaultConstructible &get_static_member() { - return X0::member; + return N0::X0::member; } +template<> int N0::X0::member; // expected-error{{originally}} + +template<> float N0::X0::member = 3.14f; + +#if 0 +// FIXME: update the remainder of this test to check for scopes properly. // -- member class of a class template template<> struct X0::Inner { };