[cfe-commits] r83974 - in /cfe/trunk: include/clang/AST/DeclTemplate.h lib/AST/Decl.cpp lib/Sema/Sema.h lib/Sema/SemaDecl.cpp lib/Sema/SemaTemplate.cpp lib/Sema/SemaTemplateInstantiate.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp test/CXX/temp/temp.spec/temp.expl.spec/p18.cpp test/CXX/temp/temp.spec/temp.expl.spec/p19.cpp

Douglas Gregor dgregor at apple.com
Tue Oct 13 09:30:37 PDT 2009


Author: dgregor
Date: Tue Oct 13 11:30:37 2009
New Revision: 83974

URL: http://llvm.org/viewvc/llvm-project?rev=83974&view=rev
Log:
When explicitly specializing a member that is a template, mark the
template as a specialization. For example, this occurs with:

  template<typename T>
  struct X {
    template<typename U> struct Inner { /* ... */ };
  };

  template<> template<typename T>
  struct X<int>::Inner {
    T member;
  };

We need to treat templates that are member specializations as special
in two contexts:

  - When looking for a definition of a member template, we look
    through the instantiation chain until we hit the primary template
    *or a member specialization*. This allows us to distinguish
    between the primary "Inner" definition and the X<int>::Inner
    definition, above.
  - When computing all of the levels of template arguments needed to
    instantiate a member template, don't add template arguments
    from contexts outside of the instantiation of a member
    specialization, since the user has already manually substituted
    those arguments.

Fix up the existing test for p18, which was actually wrong (but we
didn't diagnose it because of our poor handling of member
specializations of templates), and add a new test for member
specializations of templates.


Added:
    cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p19.cpp   (with props)
Modified:
    cfe/trunk/include/clang/AST/DeclTemplate.h
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
    cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p18.cpp

Modified: cfe/trunk/include/clang/AST/DeclTemplate.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclTemplate.h?rev=83974&r1=83973&r2=83974&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/DeclTemplate.h (original)
+++ cfe/trunk/include/clang/AST/DeclTemplate.h Tue Oct 13 11:30:37 2009
@@ -621,7 +621,7 @@
   /// \brief Data that is common to all of the declarations of a given
   /// function template.
   struct Common {
-    Common() : InstantiatedFromMember(0) { }
+    Common() : InstantiatedFromMember(0, false) { }
 
     /// \brief The function template specializations for this function
     /// template, including explicit specializations and instantiations.
@@ -629,7 +629,10 @@
 
     /// \brief The member function template from which this was most
     /// directly instantiated (or null).
-    FunctionTemplateDecl *InstantiatedFromMember;
+    ///
+    /// The boolean value indicates whether this member function template
+    /// was explicitly specialized.
+    llvm::PointerIntPair<FunctionTemplateDecl*, 1, bool> InstantiatedFromMember;
   };
 
   /// \brief A pointer to the previous declaration (if this is a redeclaration)
@@ -704,14 +707,40 @@
   /// \returns NULL if this is not an instantiation of a member function
   /// template.
   FunctionTemplateDecl *getInstantiatedFromMemberTemplate() {
-    return getCommonPtr()->InstantiatedFromMember;
+    return getCommonPtr()->InstantiatedFromMember.getPointer();
   }
 
   void setInstantiatedFromMemberTemplate(FunctionTemplateDecl *FTD) {
-    assert(!getCommonPtr()->InstantiatedFromMember);
-    getCommonPtr()->InstantiatedFromMember = FTD;
+    assert(!getCommonPtr()->InstantiatedFromMember.getPointer());
+    getCommonPtr()->InstantiatedFromMember.setPointer(FTD);
   }
 
+  /// \brief Determines whether this template was a specialization of a 
+  /// member template.
+  ///
+  /// In the following example, the function template \c X<int>::f is a 
+  /// member specialization.
+  ///
+  /// \code
+  /// template<typename T>
+  /// struct X {
+  ///   template<typename U> void f(T, U);
+  /// };
+  ///
+  /// template<> template<typename T>
+  /// void X<int>::f(int, T);
+  /// \endcode
+  bool isMemberSpecialization() {
+    return getCommonPtr()->InstantiatedFromMember.getInt();
+  }
+  
+  /// \brief Note that this member template is a specialization.
+  void setMemberSpecialization() {
+    assert(getCommonPtr()->InstantiatedFromMember.getPointer() &&
+           "Only member templates can be member template specializations");
+    getCommonPtr()->InstantiatedFromMember.setInt(true);
+  }
+  
   /// Create a template function node.
   static FunctionTemplateDecl *Create(ASTContext &C, DeclContext *DC,
                                       SourceLocation L,
@@ -1163,7 +1192,7 @@
   /// \brief Data that is common to all of the declarations of a given
   /// class template.
   struct Common {
-    Common() : InstantiatedFromMember(0) {}
+    Common() : InstantiatedFromMember(0, 0) {}
 
     /// \brief The class template specializations for this class
     /// template, including explicit specializations and instantiations.
@@ -1179,9 +1208,15 @@
 
     /// \brief The templated member class from which this was most
     /// directly instantiated (or null).
-    ClassTemplateDecl *InstantiatedFromMember;
+    ///
+    /// The boolean value indicates whether this member class template
+    /// was explicitly specialized.
+    llvm::PointerIntPair<ClassTemplateDecl *, 1, bool> InstantiatedFromMember;
   };
 
+  // FIXME: Combine PreviousDeclaration with CommonPtr, as in 
+  // FunctionTemplateDecl.
+  
   /// \brief Previous declaration of this class template.
   ClassTemplateDecl *PreviousDeclaration;
 
@@ -1280,14 +1315,40 @@
   ///
   /// \returns null if this is not an instantiation of a member class template.
   ClassTemplateDecl *getInstantiatedFromMemberTemplate() const {
-    return CommonPtr->InstantiatedFromMember;
+    return CommonPtr->InstantiatedFromMember.getPointer();
   }
 
   void setInstantiatedFromMemberTemplate(ClassTemplateDecl *CTD) {
-    assert(!CommonPtr->InstantiatedFromMember);
-    CommonPtr->InstantiatedFromMember = CTD;
+    assert(!CommonPtr->InstantiatedFromMember.getPointer());
+    CommonPtr->InstantiatedFromMember.setPointer(CTD);
   }
 
+  /// \brief Determines whether this template was a specialization of a 
+  /// member template.
+  ///
+  /// In the following example, the member template \c X<int>::Inner is a 
+  /// member specialization.
+  ///
+  /// \code
+  /// template<typename T>
+  /// struct X {
+  ///   template<typename U> struct Inner;
+  /// };
+  ///
+  /// template<> template<typename T>
+  /// struct X<int>::Inner { /* ... */ };
+  /// \endcode
+  bool isMemberSpecialization() {
+    return CommonPtr->InstantiatedFromMember.getInt();
+  }
+  
+  /// \brief Note that this member template is a specialization.
+  void setMemberSpecialization() {
+    assert(CommonPtr->InstantiatedFromMember.getPointer() &&
+           "Only member templates can be member template specializations");
+    CommonPtr->InstantiatedFromMember.setInt(true);
+  }
+  
   // Implement isa/cast/dyncast support
   static bool classof(const Decl *D)
   { return D->getKind() == ClassTemplate; }

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=83974&r1=83973&r2=83974&view=diff

==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Tue Oct 13 11:30:37 2009
@@ -737,8 +737,8 @@
 const TemplateArgumentList *
 FunctionDecl::getTemplateSpecializationArgs() const {
   if (FunctionTemplateSpecializationInfo *Info
-      = TemplateOrSpecialization
-      .dyn_cast<FunctionTemplateSpecializationInfo*>()) {
+        = TemplateOrSpecialization
+            .dyn_cast<FunctionTemplateSpecializationInfo*>()) {
     return Info->TemplateArguments;
   }
   return 0;

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

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue Oct 13 11:30:37 2009
@@ -551,6 +551,7 @@
                                      bool IsFunctionDefinition,
                                      bool &Redeclaration);
   void CheckFunctionDeclaration(FunctionDecl *NewFD, NamedDecl *&PrevDecl,
+                                bool IsExplicitSpecialization,
                                 bool &Redeclaration,
                                 bool &OverloadableAttrRequired);
   void CheckMain(FunctionDecl *FD);

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Tue Oct 13 11:30:37 2009
@@ -1735,6 +1735,10 @@
       return DeclPtrTy();
     }
 
+    if (!DC->isDependentContext() && 
+        RequireCompleteDeclContext(D.getCXXScopeSpec()))
+      return DeclPtrTy();
+    
     LookupResult Res;
     LookupQualifiedName(Res, DC, Name, LookupOrdinaryName, true);
     PrevDecl = Res.getAsSingleDecl(Context);
@@ -2807,11 +2811,11 @@
   } else if (isExplicitSpecialization && isa<CXXMethodDecl>(NewFD) &&
              CheckMemberSpecialization(NewFD, PrevDecl))
     NewFD->setInvalidDecl();
-           
+    
   // Perform semantic checking on the function declaration.
   bool OverloadableAttrRequired = false; // FIXME: HACK!
-  CheckFunctionDeclaration(NewFD, PrevDecl, Redeclaration,
-                           /*FIXME:*/OverloadableAttrRequired);
+  CheckFunctionDeclaration(NewFD, PrevDecl, isExplicitSpecialization,
+                           Redeclaration, /*FIXME:*/OverloadableAttrRequired);
 
   if (D.getCXXScopeSpec().isSet() && !NewFD->isInvalidDecl()) {
     // An out-of-line member function declaration must also be a
@@ -2914,8 +2918,12 @@
 /// that have been instantiated via C++ template instantiation (called
 /// via InstantiateDecl).
 ///
+/// \param IsExplicitSpecialiation whether this new function declaration is
+/// an explicit specialization of the previous declaration.
+///
 /// This sets NewFD->isInvalidDecl() to true if there was an error.
 void Sema::CheckFunctionDeclaration(FunctionDecl *NewFD, NamedDecl *&PrevDecl,
+                                    bool IsExplicitSpecialization,
                                     bool &Redeclaration,
                                     bool &OverloadableAttrRequired) {
   // If NewFD is already known erroneous, don't do any of this checking.
@@ -2990,7 +2998,7 @@
 
       if (FunctionTemplateDecl *OldTemplateDecl
                                     = dyn_cast<FunctionTemplateDecl>(OldDecl)) {
-        NewFD->setPreviousDeclaration(OldTemplateDecl->getTemplatedDecl());
+        NewFD->setPreviousDeclaration(OldTemplateDecl->getTemplatedDecl());        
         FunctionTemplateDecl *NewTemplateDecl
           = NewFD->getDescribedFunctionTemplate();
         assert(NewTemplateDecl && "Template/non-template mismatch");
@@ -2999,6 +3007,14 @@
           Method->setAccess(OldTemplateDecl->getAccess());
           NewTemplateDecl->setAccess(OldTemplateDecl->getAccess());
         }
+        
+        // If this is an explicit specialization of a member that is a function
+        // template, mark it as a member specialization.
+        if (IsExplicitSpecialization && 
+            NewTemplateDecl->getInstantiatedFromMemberTemplate()) {
+          NewTemplateDecl->setMemberSpecialization();
+          assert(OldTemplateDecl->isMemberSpecialization());
+        }
       } else {
         if (isa<CXXMethodDecl>(NewFD)) // Set access for out-of-line definitions
           NewFD->setAccess(OldDecl->getAccess());

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Tue Oct 13 11:30:37 2009
@@ -716,6 +716,12 @@
   assert(T->isDependentType() && "Class template type is not dependent?");
   (void)T;
 
+  // If we are providing an explicit specialization of a member that is a 
+  // class template, make a note of that.
+  if (PrevClassTemplate && 
+      PrevClassTemplate->getInstantiatedFromMemberTemplate())
+    PrevClassTemplate->setMemberSpecialization();
+  
   // Set the access specifier.
   if (!Invalid && TUK != TUK_Friend)
     SetMemberAccessSpecifier(NewTemplate, PrevClassTemplate, AS);

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Tue Oct 13 11:30:37 2009
@@ -46,15 +46,31 @@
         break;
 
       Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs());
+      
+      // If this class template specialization was instantiated from a 
+      // specialized member that is a class template, we're done.
+      assert(Spec->getSpecializedTemplate() && "No class template?");
+      if (Spec->getSpecializedTemplate()->isMemberSpecialization())
+        break;
     }
-
     // Add template arguments from a function template specialization.
     else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Ctx)) {
-      // FIXME: Check whether this is an explicit specialization.
+      if (Function->getTemplateSpecializationKind() 
+            == TSK_ExplicitSpecialization)
+        break;
+          
       if (const TemplateArgumentList *TemplateArgs
-            = Function->getTemplateSpecializationArgs())
+            = Function->getTemplateSpecializationArgs()) {
+        // Add the template arguments for this specialization.
         Result.addOuterTemplateArguments(TemplateArgs);
 
+        // If this function was instantiated from a specialized member that is
+        // a function template, we're done.
+        assert(Function->getPrimaryTemplate() && "No function template?");
+        if (Function->getPrimaryTemplate()->isMemberSpecialization())
+          break;
+      }
+      
       // If this is a friend declaration and it declares an entity at
       // namespace scope, take arguments from its lexical parent
       // instead of its semantic parent.
@@ -940,9 +956,15 @@
     //   -- If no matches are found, the instantiation is generated
     //      from the primary template.
     ClassTemplateDecl *OrigTemplate = Template;
-    while (OrigTemplate->getInstantiatedFromMemberTemplate())
+    while (OrigTemplate->getInstantiatedFromMemberTemplate()) {
+      // If we've found an explicit specialization of this class template,
+      // stop here and use that as the pattern.
+      if (OrigTemplate->isMemberSpecialization())
+        break;
+      
       OrigTemplate = OrigTemplate->getInstantiatedFromMemberTemplate();
-
+    }
+    
     Pattern = OrigTemplate->getTemplatedDecl();
   }
 

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Tue Oct 13 11:30:37 2009
@@ -581,7 +581,7 @@
       PrevDecl = 0;
   }
   
-  SemaRef.CheckFunctionDeclaration(Function, PrevDecl, Redeclaration,
+  SemaRef.CheckFunctionDeclaration(Function, PrevDecl, false, Redeclaration,
                                    /*FIXME:*/OverloadableAttrRequired);
 
   // If the original function was part of a friend declaration,
@@ -748,7 +748,7 @@
 
   bool Redeclaration = false;
   bool OverloadableAttrRequired = false;
-  SemaRef.CheckFunctionDeclaration(Method, PrevDecl, Redeclaration,
+  SemaRef.CheckFunctionDeclaration(Method, PrevDecl, false, Redeclaration,
                                    /*FIXME:*/OverloadableAttrRequired);
 
   if (!FunctionTemplate && (!Method->isInvalidDecl() || !PrevDecl) &&
@@ -1057,9 +1057,15 @@
   // Find the function body that we'll be substituting.
   const FunctionDecl *PatternDecl = 0;
   if (FunctionTemplateDecl *Primary = Function->getPrimaryTemplate()) {
-    while (Primary->getInstantiatedFromMemberTemplate())
+    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())
+        break;
+      
       Primary = Primary->getInstantiatedFromMemberTemplate();
-
+    }
+    
     PatternDecl = Primary->getTemplatedDecl();
   } else
     PatternDecl = Function->getInstantiatedFromMemberFunction();

Modified: cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p18.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p18.cpp?rev=83974&r1=83973&r2=83974&view=diff

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p18.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p18.cpp Tue Oct 13 11:30:37 2009
@@ -7,10 +7,14 @@
 }; 
 
 template<> template<class X>
-class A<int>::B { }; 
+class A<long>::B { }; 
 
+// FIXME: If we make the explicit specialization of A<long>::B, above, into
+// a specialization of A<int>::B, our diagnostic is correct but not very 
+// helpful.
 template<> template<> template<class T>
   void A<int>::B<double>::mf1(T t) { } 
 
+// FIXME: This diagnostic could probably be better.
 template<class Y> template<>
   void A<Y>::B<double>::mf2() { } // expected-error{{does not refer}}

Added: cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p19.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p19.cpp?rev=83974&view=auto

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p19.cpp (added)
+++ cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p19.cpp Tue Oct 13 11:30:37 2009
@@ -0,0 +1,30 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template<typename T>
+struct X {
+  template<typename U> struct Inner { };
+  
+  template<typename U> void f(T, U) { }
+};
+
+template<> template<typename U>
+struct X<int>::Inner {
+  U member;
+};
+
+template<> template<typename U>
+void X<int>::f(int x, U y) { 
+  x = y; // expected-error{{incompatible type}}
+}
+
+void test(X<int> xi, X<long> xl, float *fp) {
+  X<int>::Inner<float*> xii;
+  xii.member = fp;
+  xi.f(17, 25);
+  xi.f(17, 3.14159);
+  xi.f(17, fp); // expected-note{{instantiation}}
+  X<long>::Inner<float*> xli;
+  
+  xli.member = fp; // expected-error{{no member}}
+  xl.f(17, fp); // okay
+}

Propchange: cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p19.cpp

------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p19.cpp

------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/CXX/temp/temp.spec/temp.expl.spec/p19.cpp

------------------------------------------------------------------------------
    svn:mime-type = text/plain





More information about the cfe-commits mailing list