From: Douglas Gregor Date: Thu, 14 May 2009 00:28:11 +0000 (+0000) Subject: Implement explicit instantiations of member classes of class templates, e.g., X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3f5b61c394f4f205bcb4d316eb2a7a0a68b8af86;p=clang Implement explicit instantiations of member classes of class templates, e.g., template struct X { struct Inner; }; template struct X::Inner; This change is larger than it looks because it also fixes some a problem with nested-name-specifiers and tags. We weren't requiring the DeclContext associated with the scope specifier of a tag to be complete. Therefore, when looking for something like "struct X::Inner", we weren't instantiating X. This, naturally, uncovered a problem with member pointers, where we were requiring the left-hand side of a member pointer access expression (e.g., x->*) to be a complete type. However, this is wrong: the semantics of this expression does not require a complete type (EDG agrees). Stuart vouched for me. Blame him. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@71756 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 79299a4700..4d018ae01b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -737,6 +737,14 @@ def ext_explicit_instantiation_after_specialization : Extension< "specialization will be ignored (C++0x extension)">; def note_previous_template_specialization : Note< "previous template specialization is here">; +def err_explicit_instantiation_enum : Error< + "explicit instantiation of enumeration type %0">; +def err_explicit_instantiation_nontemplate_type : Error< + "explicit instantiation of non-templated type %0">; +def note_nontemplate_decl_here : Note< + "non-templated declaration is here">; +def err_explicit_instantiation_out_of_scope : Error< + "explicit instantiation of %0 not in a namespace enclosing %1">; // C++ typename-specifiers def err_typename_nested_not_found : Error<"no type named %0 in %1">; @@ -1234,8 +1242,6 @@ def err_qualified_catch_declarator : Error< def err_early_catch_all : Error<"catch-all handler must come last">; def err_bad_memptr_rhs : Error< "right hand operand to %0 has non pointer-to-member type %1">; -def err_memptr_rhs_incomplete : Error< - "right hand operand is a pointer to member of incomplete type %0">; def err_bad_memptr_lhs : Error< "left hand operand to %0 must be a %select{|pointer to }1class " "compatible with the right hand operand, but is %2">; diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 10cdd73e1c..b65560bb24 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1373,7 +1373,56 @@ public: return DeclResult(); } - + /// \brief Process the explicit instantiation of a member class of a + /// class template specialization. + /// + /// This routine is invoked when an explicit instantiation of a + /// member class of a class template specialization is + /// encountered. In the following example, + /// ActOnExplicitInstantiation will be invoked to force the + /// instantiation of X::Inner: + /// + /// \code + /// template class X { class Inner { /* ... */}; }; + /// template class X::Inner; // explicit instantiation + /// \endcode + /// + /// \param S the current scope + /// + /// \param TemplateLoc the location of the 'template' keyword that + /// specifies that this is an explicit instantiation. + /// + /// \param TagSpec whether this declares a class, struct, or union + /// (template). + /// + /// \param KWLoc the location of the 'class', 'struct', or 'union' + /// keyword. + /// + /// \param SS the scope specifier preceding the template-id. + /// + /// \param Template the declaration of the class template that we + /// are instantiation. + /// + /// \param LAngleLoc the location of the '<' token in the template-id. + /// + /// \param TemplateArgs the template arguments used to form the + /// template-id. + /// + /// \param TemplateArgLocs the locations of the template arguments. + /// + /// \param RAngleLoc the location of the '>' token in the template-id. + /// + /// \param Attr attributes that apply to this instantiation. + virtual DeclResult + ActOnExplicitInstantiation(Scope *S, SourceLocation TemplateLoc, + unsigned TagSpec, + SourceLocation KWLoc, + const CXXScopeSpec &SS, + IdentifierInfo *Name, + SourceLocation NameLoc, + AttributeList *Attr) { + return DeclResult(); + } /// \brief Called when the parser has parsed a C++ typename /// specifier that ends in an identifier, e.g., "typename T::type". diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index ed20befc34..b70f2bba78 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -560,7 +560,7 @@ private: TemplateParams(TemplateParams) { } explicit ParsedTemplateInfo(SourceLocation TemplateLoc) - : Kind(ExplicitInstantiation), + : Kind(ExplicitInstantiation), TemplateParams(0), TemplateLoc(TemplateLoc) { } /// \brief The kind of template we are parsing. diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 56503961fa..963c5adf00 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -514,10 +514,10 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // // template class Foo // - // but it is actually a declaration. Most likely, this was + // but it actually has a definition. Most likely, this was // meant to be an explicit specialization, but the user forgot // the '<>' after 'template'. - assert(TK == Action::TK_Definition && "Can only get a definition here"); + assert(TK == Action::TK_Definition && "Expected a definition here"); SourceLocation LAngleLoc = PP.getLocForEndOfToken(TemplateInfo.TemplateLoc); @@ -554,7 +554,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, TemplateParams? TemplateParams->size() : 0)); } TemplateId->Destroy(); - } else if (TemplateParams && TK != Action::TK_Reference) + } else if (TemplateParams && TK != Action::TK_Reference) { + // Class template declaration or definition. TagOrTempResult = Actions.ActOnClassTemplate(CurScope, TagType, TK, StartLoc, SS, Name, NameLoc, Attr, @@ -562,9 +563,28 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, &(*TemplateParams)[0], TemplateParams->size()), AS); - else - TagOrTempResult = Actions.ActOnTag(CurScope, TagType, TK, StartLoc, SS, Name, - NameLoc, Attr, AS); + } else if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation && + TK == Action::TK_Declaration) { + // Explicit instantiation of a member of a class template + // specialization, e.g., + // + // template struct Outer::Inner; + // + TagOrTempResult + = Actions.ActOnExplicitInstantiation(CurScope, + TemplateInfo.TemplateLoc, + TagType, StartLoc, SS, Name, + NameLoc, Attr); + } else { + if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation && + TK == Action::TK_Definition) { + // FIXME: Diagnose this particular error. + } + + // Declaration or definition of a class type + TagOrTempResult = Actions.ActOnTag(CurScope, TagType, TK, StartLoc, SS, + Name, NameLoc, Attr, AS); + } // Parse the optional base clause (C++ only). if (getLang().CPlusPlus && Tok.is(tok::colon)) diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 2f1b9c84d2..4becfc83c9 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -149,7 +149,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS) { if (TemplateId->Kind == TNK_Type_template || TemplateId->Kind == TNK_Dependent_template_name) { AnnotateTemplateIdTokenAsType(&SS); - SS.clear(); + SS.setScopeRep(0); assert(Tok.is(tok::annot_typename) && "AnnotateTemplateIdTokenAsType isn't working"); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 08f9b02d77..bd01ec9e51 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1919,6 +1919,15 @@ public: SourceLocation RAngleLoc, AttributeList *Attr); + virtual DeclResult + ActOnExplicitInstantiation(Scope *S, SourceLocation TemplateLoc, + unsigned TagSpec, + SourceLocation KWLoc, + const CXXScopeSpec &SS, + IdentifierInfo *Name, + SourceLocation NameLoc, + AttributeList *Attr); + bool CheckTemplateArgumentList(TemplateDecl *Template, SourceLocation TemplateLoc, SourceLocation LAngleLoc, diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index bbc1bc52cb..11ac0bd300 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -273,8 +273,9 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, SourceLocation CCLoc) { NestedNameSpecifier *Prefix = static_cast(SS.getScopeRep()); + QualType T = QualType::getFromOpaquePtr(Ty); return NestedNameSpecifier::Create(Context, Prefix, /*FIXME:*/false, - QualType::getFromOpaquePtr(Ty).getTypePtr()); + T.getTypePtr()); } /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 5914292dab..e27517e689 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3254,12 +3254,15 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, goto CreateNewDecl; } - // FIXME: RequireCompleteDeclContext(SS)? + if (RequireCompleteDeclContext(SS)) + return DeclPtrTy::make((Decl *)0); + DC = computeDeclContext(SS); SearchDC = DC; // Look-up name inside 'foo::'. - PrevDecl = dyn_cast_or_null( - LookupQualifiedName(DC, Name, LookupTagName, true).getAsDecl()); + PrevDecl + = dyn_cast_or_null( + LookupQualifiedName(DC, Name, LookupTagName, true).getAsDecl()); // A tag 'foo::bar' must already exist. if (PrevDecl == 0) { diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 5f2b705498..57aae29631 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -994,10 +994,7 @@ QualType Sema::CheckPointerToMemberOperands( Diag(Loc, diag::err_bad_memptr_rhs) << OpSpelling << RType << rex->getSourceRange(); return QualType(); - } else if (RequireCompleteType(Loc, QualType(MemPtr->getClass(), 0), - diag::err_memptr_rhs_incomplete, - rex->getSourceRange())) - return QualType(); + } QualType Class(MemPtr->getClass(), 0); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 22f2bef0e9..7190df107e 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2155,6 +2155,7 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, return DeclPtrTy::make(Specialization); } +// Explicit instantiation of a class template specialization Sema::DeclResult Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation TemplateLoc, unsigned TagSpec, @@ -2244,7 +2245,7 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation TemplateLoc, } if (PrevDecl->getSpecializationKind() == TSK_ExplicitSpecialization) { - // C++0x [temp.explicit]p4: + // C++ DR 259, C++0x [temp.explicit]p4: // For a given set of template parameters, if an explicit // instantiation of a template appears after a declaration of // an explicit specialization for that template, the explicit @@ -2342,6 +2343,84 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation TemplateLoc, return DeclPtrTy::make(Specialization); } +// Explicit instantiation of a member class of a class template. +Sema::DeclResult +Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation TemplateLoc, + unsigned TagSpec, + SourceLocation KWLoc, + const CXXScopeSpec &SS, + IdentifierInfo *Name, + SourceLocation NameLoc, + AttributeList *Attr) { + + DeclPtrTy TagD = ActOnTag(S, TagSpec, Action::TK_Reference, + KWLoc, SS, Name, NameLoc, Attr, AS_none); + if (!TagD) + return true; + + TagDecl *Tag = cast(TagD.getAs()); + if (Tag->isEnum()) { + Diag(TemplateLoc, diag::err_explicit_instantiation_enum) + << Context.getTypeDeclType(Tag); + return true; + } + + CXXRecordDecl *Record = cast(Tag); + CXXRecordDecl *Pattern = Record->getInstantiatedFromMemberClass(); + if (!Pattern) { + Diag(TemplateLoc, diag::err_explicit_instantiation_nontemplate_type) + << Context.getTypeDeclType(Record); + Diag(Record->getLocation(), diag::note_nontemplate_decl_here); + return true; + } + + // C++0x [temp.explicit]p2: + // [...] An explicit instantiation shall appear in an enclosing + // namespace of its template. [...] + // + // This is C++ DR 275. + if (getLangOptions().CPlusPlus0x) { + // FIXME: In C++98, we would like to turn these errors into + // warnings, dependent on a -Wc++0x flag. + DeclContext *PatternContext + = Pattern->getDeclContext()->getEnclosingNamespaceContext(); + if (!CurContext->Encloses(PatternContext)) { + Diag(TemplateLoc, diag::err_explicit_instantiation_out_of_scope) + << Record << cast(PatternContext) << SS.getRange(); + Diag(Pattern->getLocation(), diag::note_previous_declaration); + } + } + + // Find the enclosing template, because we need its template + // arguments to instantiate this class. + DeclContext *EnclosingTemplateCtx = Record->getDeclContext(); + while (!isa(EnclosingTemplateCtx)) + EnclosingTemplateCtx = EnclosingTemplateCtx->getParent(); + ClassTemplateSpecializationDecl *EnclosingTemplate + = cast(EnclosingTemplateCtx); + + if (!Record->getDefinition(Context)) { + // If the class has a definition, instantiate it (and all of its + // members, recursively). + Pattern = cast_or_null(Pattern->getDefinition(Context)); + if (Pattern && InstantiateClass(TemplateLoc, Record, Pattern, + EnclosingTemplate->getTemplateArgs(), + /*ExplicitInstantiation=*/true)) + return true; + } else { + // Instantiate all of the members of class. + InstantiatingTemplate Inst(*this, TemplateLoc, Record); + InstantiateClassMembers(TemplateLoc, Record, + EnclosingTemplate->getTemplateArgs()); + } + + // FIXME: We don't have any representation for explicit + // instantiations of member classes. Such a representation is not + // needed for compilation, but it should be available for clients + // that want to see all of the declarations in the source code. + return TagD; +} + Sema::TypeResult Sema::ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, const IdentifierInfo &II, SourceLocation IdLoc) { diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 9c2c4230f9..5c6ed758d9 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -863,7 +863,7 @@ Sema::InstantiateNestedNameSpecifier(NestedNameSpecifier *NNS, ActOnCXXNestedNameSpecifier(0, SS, Range.getEnd(), Range.getEnd(), - *NNS->getAsIdentifier())); + *NNS->getAsIdentifier())); break; } diff --git a/test/SemaCXX/member-pointer.cpp b/test/SemaCXX/member-pointer.cpp index 1a663f6e1c..cfe4f75dd1 100644 --- a/test/SemaCXX/member-pointer.cpp +++ b/test/SemaCXX/member-pointer.cpp @@ -80,7 +80,7 @@ void g() { void (HasMembers::*pmd)() = &HasMembers::d; } -struct Incomplete; // expected-note{{forward declaration}} +struct Incomplete; void h() { HasMembers hm, *phm = &hm; @@ -115,7 +115,7 @@ void h() { Incomplete *inc; int Incomplete::*pii = 0; - (void)inc->*pii; // expected-error {{right hand operand is a pointer to member of incomplete type 'struct Incomplete'}} + (void)(inc->*pii); // okay } struct OverloadsPtrMem diff --git a/test/SemaTemplate/instantiate-complete.cpp b/test/SemaTemplate/instantiate-complete.cpp index 7b4373538d..babc55217a 100644 --- a/test/SemaTemplate/instantiate-complete.cpp +++ b/test/SemaTemplate/instantiate-complete.cpp @@ -11,8 +11,7 @@ struct X { // expected-error{{data member instantiated with function type 'int (int)'}} \ // expected-error{{data member instantiated with function type 'char (char)'}} \ // expected-error{{data member instantiated with function type 'short (short)'}} \ - // expected-error{{data member instantiated with function type 'float (float)'}} \ - // expected-error{{data member instantiated with function type 'long (long)'}} + // expected-error{{data member instantiated with function type 'float (float)'}} }; X f() { return 0; } @@ -41,7 +40,8 @@ void test_new() { } void test_memptr(X *p1, long X::*pm1, - X *p2, long (X::*pm2)(long)) { + X *p2, + long (X::*pm2)(long)) { (void)(p1->*pm1); - (void)(p2->*pm2); // expected-note{{in instantiation of template class 'struct X' requested here}} + (void)((p2->*pm2)(0)); } diff --git a/test/SemaTemplate/instantiate-typedef.cpp b/test/SemaTemplate/instantiate-typedef.cpp index c1355fca8d..d30309cc86 100644 --- a/test/SemaTemplate/instantiate-typedef.cpp +++ b/test/SemaTemplate/instantiate-typedef.cpp @@ -11,5 +11,6 @@ add_pointer::type test2(int * ptr) { return ptr; // expected-error{{incompatible type returning 'int *', expected 'add_pointer::type' (aka 'float *')}} } -add_pointer::type // expected-note{{in instantiation of template class 'struct add_pointer' requested here}} expected-error {{unknown type name 'type'}} +add_pointer::type // expected-note{{in instantiation of template class 'struct add_pointer' requested here}} \ +// expected-error {{unknown type name 'type'}} test3(); diff --git a/test/SemaTemplate/temp_explicit.cpp b/test/SemaTemplate/temp_explicit.cpp index 0b96c73c1f..6394f1daf4 100644 --- a/test/SemaTemplate/temp_explicit.cpp +++ b/test/SemaTemplate/temp_explicit.cpp @@ -71,3 +71,41 @@ void f3(X4::Inner); // okay, Inner::VeryInner, not instantiated template struct X4; // expected-note{{instantiation}} template struct X4; // expected-note{{instantiation}} + +// Check explicit instantiation of member classes +namespace N2 { + +template +struct X5 { + struct Inner1 { + void f(T&); + }; + + struct Inner2 { + struct VeryInner { // expected-note 2{{instantiation}} + void g(T*); // expected-error 2{{pointer to a reference}} + }; + }; +}; + +} + +template struct N2::X5::Inner2; + +using namespace N2; +template struct X5::Inner2; // expected-note{{instantiation}} + +void f4(X5::Inner2); +template struct X5::Inner2; // expected-note{{instantiation}} + +namespace N3 { + template struct N2::X5::Inner2; +} + +struct X6 { + struct Inner { // expected-note{{here}} + void f(); + }; +}; + +template struct X6::Inner; // expected-error{{non-templated}}