[cfe-commits] r71756 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td include/clang/Parse/Action.h include/clang/Parse/Parser.h lib/Parse/ParseDeclCXX.cpp lib/Parse/ParseExprCXX.cpp lib/Sema/Sema.h lib/Sema/SemaCXXScopeSpec.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaExprCXX.cpp lib/Sema/SemaTemplate.cpp lib/Sema/SemaTemplateInstantiate.cpp test/SemaCXX/member-pointer.cpp test/SemaTemplate/instantiate-complete.cpp test/SemaTemplate/instantiate-typedef.cpp test/SemaTemplate/temp_explicit.cpp

Douglas Gregor dgregor at apple.com
Wed May 13 17:28:30 PDT 2009


Author: dgregor
Date: Wed May 13 19:28:11 2009
New Revision: 71756

URL: http://llvm.org/viewvc/llvm-project?rev=71756&view=rev
Log:
Implement explicit instantiations of member classes of class templates, e.g.,

  template<typename T>
  struct X {
    struct Inner;
  };

  template struct X<int>::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<int>::Inner", we weren't instantiating X<int>. 

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.


Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Parse/Action.h
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Parse/ParseExprCXX.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
    cfe/trunk/test/SemaCXX/member-pointer.cpp
    cfe/trunk/test/SemaTemplate/instantiate-complete.cpp
    cfe/trunk/test/SemaTemplate/instantiate-typedef.cpp
    cfe/trunk/test/SemaTemplate/temp_explicit.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed May 13 19:28:11 2009
@@ -737,6 +737,14 @@
     "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_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">;

Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Wed May 13 19:28:11 2009
@@ -1373,7 +1373,56 @@
     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<int>::Inner:
+  ///
+  /// \code
+  /// template<typename T> class X { class Inner { /* ... */}; };
+  /// template class X<int>::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".

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Wed May 13 19:28:11 2009
@@ -560,7 +560,7 @@
         TemplateParams(TemplateParams) { }
 
     explicit ParsedTemplateInfo(SourceLocation TemplateLoc)
-      : Kind(ExplicitInstantiation), 
+      : Kind(ExplicitInstantiation), TemplateParams(0), 
         TemplateLoc(TemplateLoc) { }
 
     /// \brief The kind of template we are parsing.

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Wed May 13 19:28:11 2009
@@ -514,10 +514,10 @@
         //
         //   template class Foo<X>
         //
-        // 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 @@
                                  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 @@
                                                       &(*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<int>::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))

Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Wed May 13 19:28:11 2009
@@ -149,7 +149,7 @@
       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");

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Wed May 13 19:28:11 2009
@@ -1919,6 +1919,15 @@
                              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,

Modified: cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp Wed May 13 19:28:11 2009
@@ -273,8 +273,9 @@
                                                     SourceLocation CCLoc) {
   NestedNameSpecifier *Prefix 
     = static_cast<NestedNameSpecifier *>(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

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed May 13 19:28:11 2009
@@ -3254,12 +3254,15 @@
       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<TagDecl>(
-                 LookupQualifiedName(DC, Name, LookupTagName, true).getAsDecl());
+    PrevDecl 
+      = dyn_cast_or_null<TagDecl>(
+               LookupQualifiedName(DC, Name, LookupTagName, true).getAsDecl());
 
     // A tag 'foo::bar' must already exist.
     if (PrevDecl == 0) {

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Wed May 13 19:28:11 2009
@@ -994,10 +994,7 @@
     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);
 

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed May 13 19:28:11 2009
@@ -2155,6 +2155,7 @@
   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 @@
     }
 
     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 @@
   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<TagDecl>(TagD.getAs<Decl>());
+  if (Tag->isEnum()) {
+    Diag(TemplateLoc, diag::err_explicit_instantiation_enum)
+      << Context.getTypeDeclType(Tag);
+    return true;
+  }
+
+  CXXRecordDecl *Record = cast<CXXRecordDecl>(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<NamedDecl>(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<ClassTemplateSpecializationDecl>(EnclosingTemplateCtx))
+    EnclosingTemplateCtx = EnclosingTemplateCtx->getParent();
+  ClassTemplateSpecializationDecl *EnclosingTemplate 
+    = cast<ClassTemplateSpecializationDecl>(EnclosingTemplateCtx);
+  
+  if (!Record->getDefinition(Context)) {
+    // If the class has a definition, instantiate it (and all of its
+    // members, recursively).
+    Pattern = cast_or_null<CXXRecordDecl>(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) {

Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Wed May 13 19:28:11 2009
@@ -863,7 +863,7 @@
                                  ActOnCXXNestedNameSpecifier(0, SS,
                                                              Range.getEnd(),
                                                              Range.getEnd(),
-                                                   *NNS->getAsIdentifier()));
+                                                    *NNS->getAsIdentifier()));
     break;
   }
 

Modified: cfe/trunk/test/SemaCXX/member-pointer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/member-pointer.cpp?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/test/SemaCXX/member-pointer.cpp (original)
+++ cfe/trunk/test/SemaCXX/member-pointer.cpp Wed May 13 19:28:11 2009
@@ -80,7 +80,7 @@
   void (HasMembers::*pmd)() = &HasMembers::d;
 }
 
-struct Incomplete; // expected-note{{forward declaration}}
+struct Incomplete;
 
 void h() {
   HasMembers hm, *phm = &hm;
@@ -115,7 +115,7 @@
 
   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

Modified: cfe/trunk/test/SemaTemplate/instantiate-complete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/instantiate-complete.cpp?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/instantiate-complete.cpp (original)
+++ cfe/trunk/test/SemaTemplate/instantiate-complete.cpp Wed May 13 19:28:11 2009
@@ -11,8 +11,7 @@
        // 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<int> f() { return 0; }
@@ -41,7 +40,8 @@
 }
 
 void test_memptr(X<long> *p1, long X<long>::*pm1,
-                 X<long(long)> *p2, long (X<long(long)>::*pm2)(long)) {
+                 X<long(long)> *p2, 
+                 long (X<long(long)>::*pm2)(long)) {
   (void)(p1->*pm1);
-  (void)(p2->*pm2); // expected-note{{in instantiation of template class 'struct X<long (long)>' requested here}}
+  (void)((p2->*pm2)(0));
 }

Modified: cfe/trunk/test/SemaTemplate/instantiate-typedef.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/instantiate-typedef.cpp?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/instantiate-typedef.cpp (original)
+++ cfe/trunk/test/SemaTemplate/instantiate-typedef.cpp Wed May 13 19:28:11 2009
@@ -11,5 +11,6 @@
   return ptr; // expected-error{{incompatible type returning 'int *', expected 'add_pointer<float>::type' (aka 'float *')}}
 }
 
-add_pointer<int&>::type // expected-note{{in instantiation of template class 'struct add_pointer<int &>' requested here}} expected-error {{unknown type name 'type'}}
+add_pointer<int&>::type // expected-note{{in instantiation of template class 'struct add_pointer<int &>' requested here}} \
+// expected-error {{unknown type name 'type'}}
 test3(); 

Modified: cfe/trunk/test/SemaTemplate/temp_explicit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_explicit.cpp?rev=71756&r1=71755&r2=71756&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_explicit.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_explicit.cpp Wed May 13 19:28:11 2009
@@ -71,3 +71,41 @@
 
 template struct X4<int&>; // expected-note{{instantiation}}
 template struct X4<float&>; // expected-note{{instantiation}}
+
+// Check explicit instantiation of member classes
+namespace N2 {
+
+template<typename T>
+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<void>::Inner2;
+
+using namespace N2;
+template struct X5<int&>::Inner2; // expected-note{{instantiation}}
+
+void f4(X5<float&>::Inner2);
+template struct X5<float&>::Inner2; // expected-note{{instantiation}}
+
+namespace N3 {
+  template struct N2::X5<int>::Inner2;
+}
+
+struct X6 {
+  struct Inner { // expected-note{{here}}
+    void f();
+  };
+};
+
+template struct X6::Inner; // expected-error{{non-templated}}





More information about the cfe-commits mailing list