[cfe-commits] r82728 - in /cfe/trunk: include/clang/AST/Decl.h include/clang/AST/DeclTemplate.h include/clang/Basic/DiagnosticSemaKinds.td lib/AST/Decl.cpp lib/AST/DeclTemplate.cpp lib/Sema/Sema.h lib/Sema/SemaDecl.cpp lib/Sema/SemaOverload.cpp lib/Sema/SemaTemplate.cpp lib/Sema/SemaTemplateDeduction.cpp test/SemaTemplate/function-template-specialization.cpp

Douglas Gregor dgregor at apple.com
Thu Sep 24 16:14:48 PDT 2009


Author: dgregor
Date: Thu Sep 24 18:14:47 2009
New Revision: 82728

URL: http://llvm.org/viewvc/llvm-project?rev=82728&view=rev
Log:
WIP implementation of explicit function template specialization. This
first implementation recognizes when a function declaration is an
explicit function template specialization (based on the presence of a
template<> header), performs template argument deduction + ambiguity
resolution to determine which template is being specialized, and hooks

There are many caveats here:
  - We completely and totally drop any explicitly-specified template
  arguments on the floor
  - We don't diagnose any of the extra semantic things that we should
  diagnose. 
  - I haven't looked to see that we're getting the right linkage for
  explicit specializations

On a happy note, this silences a bunch of errors that show up in
libstdc++'s <iostream>, although Clang still can't get through the
entire header.


Added:
    cfe/trunk/test/SemaTemplate/function-template-specialization.cpp
Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/AST/DeclTemplate.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/AST/DeclTemplate.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp

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

==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Thu Sep 24 18:14:47 2009
@@ -1081,6 +1081,12 @@
     TemplateOrSpecialization = Template;
   }
 
+  /// \brief Determine whether this function is a function template 
+  /// specialization.
+  bool isFunctionTemplateSpecialization() const {
+    return getPrimaryTemplate() != 0;
+  }
+                       
   /// \brief Retrieve the primary template that this function template
   /// specialization either specializes or was instantiated from.
   ///
@@ -1105,10 +1111,17 @@
   ///
   /// \param TemplateArgs the template arguments that produced this
   /// function template specialization from the template.
+  ///
+  /// \param InsertPos If non-NULL, the position in the function template
+  /// specialization set where the function template specialization data will
+  /// be inserted.
+  ///
+  /// \param TSK the kind of template specialization this is.
   void setFunctionTemplateSpecialization(ASTContext &Context,
                                          FunctionTemplateDecl *Template,
                                       const TemplateArgumentList *TemplateArgs,
-                                         void *InsertPos);
+                                         void *InsertPos,
+                    TemplateSpecializationKind TSK = TSK_ImplicitInstantiation);
 
   /// \brief Determine what kind of template instantiation this function
   /// represents.

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

==============================================================================
--- cfe/trunk/include/clang/AST/DeclTemplate.h (original)
+++ cfe/trunk/include/clang/AST/DeclTemplate.h Thu Sep 24 18:14:47 2009
@@ -419,6 +419,9 @@
                        TemplateArgumentListBuilder &Builder,
                        bool TakeArgs);
 
+  /// \brief Produces a shallow copy of the given template argument list
+  TemplateArgumentList(const TemplateArgumentList &Other);
+  
   ~TemplateArgumentList();
 
   /// \brief Retrieve the template argument at a given index.

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

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Sep 24 18:14:47 2009
@@ -967,6 +967,16 @@
     "ambiguous partial specializations of %0">;
 def note_partial_spec_match : Note<"partial specialization matches %0">;
 
+// C++ Function template specializations
+def err_function_template_spec_no_match : Error<
+    "no function template matches function template specialization %0">;
+def err_function_template_spec_ambiguous : Error<
+    "function template specialization %0 ambiguously refers to more than one "
+    "function template; explicitly specify%select{|additional }1 template "
+    "arguments to identify a particular function template">;
+def note_function_template_spec_matched : Note<
+    "function template matches specialization %0">;
+    
 // C++ Template Instantiation
 def err_template_recursion_depth_exceeded : Error<
   "recursive template instantiation exceeded maximum depth of %0">,

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

==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Thu Sep 24 18:14:47 2009
@@ -689,7 +689,10 @@
 FunctionDecl::setFunctionTemplateSpecialization(ASTContext &Context,
                                                 FunctionTemplateDecl *Template,
                                      const TemplateArgumentList *TemplateArgs,
-                                                void *InsertPos) {
+                                                void *InsertPos,
+                                              TemplateSpecializationKind TSK) {
+  assert(TSK != TSK_Undeclared && 
+         "Must specify the type of function template specialization");
   FunctionTemplateSpecializationInfo *Info
     = TemplateOrSpecialization.dyn_cast<FunctionTemplateSpecializationInfo*>();
   if (!Info)
@@ -697,13 +700,24 @@
 
   Info->Function = this;
   Info->Template.setPointer(Template);
-  Info->Template.setInt(TSK_ImplicitInstantiation - 1);
+  Info->Template.setInt(TSK - 1);
   Info->TemplateArguments = TemplateArgs;
   TemplateOrSpecialization = Info;
 
   // Insert this function template specialization into the set of known
-  // function template specialiations.
-  Template->getSpecializations().InsertNode(Info, InsertPos);
+  // function template specializations.
+  if (InsertPos)
+    Template->getSpecializations().InsertNode(Info, InsertPos);
+  else {
+    // Try to insert the new node. If there is an existing node, remove it 
+    // first.
+    FunctionTemplateSpecializationInfo *Existing
+      = Template->getSpecializations().GetOrInsertNode(Info);
+    if (Existing) {
+      Template->getSpecializations().RemoveNode(Existing);
+      Template->getSpecializations().GetOrInsertNode(Info);
+    }
+  }
 }
 
 TemplateSpecializationKind FunctionDecl::getTemplateSpecializationKind() const {

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

==============================================================================
--- cfe/trunk/lib/AST/DeclTemplate.cpp (original)
+++ cfe/trunk/lib/AST/DeclTemplate.cpp Thu Sep 24 18:14:47 2009
@@ -373,6 +373,12 @@
   Builder.ReleaseArgs();
 }
 
+TemplateArgumentList::TemplateArgumentList(const TemplateArgumentList &Other)
+  : FlatArguments(Other.FlatArguments.getPointer(), 1),
+    NumFlatArguments(Other.flat_size()),
+    StructuredArguments(Other.StructuredArguments.getPointer(), 1),
+    NumStructuredArguments(Other.NumStructuredArguments) { }
+
 TemplateArgumentList::~TemplateArgumentList() {
   // FIXME: Deallocate template arguments
 }

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

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Thu Sep 24 18:14:47 2009
@@ -2515,6 +2515,14 @@
                                                     MultiTemplateParamsArg TemplateParameterLists,
                                                     Declarator &D);
 
+  bool CheckFunctionTemplateSpecialization(FunctionDecl *FD,
+                                           bool HasExplicitTemplateArgs,
+                                           SourceLocation LAngleLoc,
+                                  const TemplateArgument *ExplicitTemplateArgs,
+                                           unsigned NumExplicitTemplateArgs,
+                                           SourceLocation RAngleLoc,
+                                           NamedDecl *&PrevDecl);
+    
   virtual DeclResult
   ActOnExplicitInstantiation(Scope *S,
                              SourceLocation ExternLoc,

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Thu Sep 24 18:14:47 2009
@@ -1794,9 +1794,11 @@
   if (New == 0)
     return DeclPtrTy();
 
-  // If this has an identifier and is not an invalid redeclaration,
-  // add it to the scope stack.
-  if (Name && !(Redeclaration && New->isInvalidDecl()))
+  // If this has an identifier and is not an invalid redeclaration or 
+  // function template specialization, add it to the scope stack.
+  if (Name && !(Redeclaration && New->isInvalidDecl()) &&
+      !(isa<FunctionDecl>(New) && 
+        cast<FunctionDecl>(New)->isFunctionTemplateSpecialization()))
     PushOnScopeChains(New, S);
 
   return DeclPtrTy::make(New);
@@ -2516,6 +2518,7 @@
   // Match up the template parameter lists with the scope specifier, then
   // determine whether we have a template or a template specialization.
   FunctionTemplateDecl *FunctionTemplate = 0;
+  bool isFunctionTemplateSpecialization = false;
   if (TemplateParameterList *TemplateParams
         = MatchTemplateParametersToScopeSpecifier(
                                   D.getDeclSpec().getSourceRange().getBegin(),
@@ -2536,13 +2539,14 @@
       FunctionTemplate->setLexicalDeclContext(CurContext);
       NewFD->setDescribedFunctionTemplate(FunctionTemplate);
     } else {
-      // FIXME: Handle function template specializations
+      // This is a function template specialization.
+      isFunctionTemplateSpecialization = true;
     }
 
     // FIXME: Free this memory properly.
     TemplateParamLists.release();
   }
-
+  
   // C++ [dcl.fct.spec]p5:
   //   The virtual specifier shall only be used in declarations of
   //   nonstatic class member functions that appear within a
@@ -2678,6 +2682,18 @@
         isOutOfScopePreviousDeclaration(PrevDecl, DC, Context)))
     PrevDecl = 0;
 
+  // FIXME: If the declarator has a template argument list but 
+  // isFunctionTemplateSpecialization is false, this is a function template 
+  // specialization but the user forgot the "template<>" header. Complain about
+  // the missing template<> header and set isFunctionTemplateSpecialization.
+
+  if (isFunctionTemplateSpecialization &&
+      CheckFunctionTemplateSpecialization(NewFD, 
+                                          /*FIXME:*/false, SourceLocation(),
+                                          0, 0, SourceLocation(), 
+                                          PrevDecl))
+    NewFD->setInvalidDecl();
+  
   // Perform semantic checking on the function declaration.
   bool OverloadableAttrRequired = false; // FIXME: HACK!
   CheckFunctionDeclaration(NewFD, PrevDecl, Redeclaration,

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Thu Sep 24 18:14:47 2009
@@ -4055,6 +4055,7 @@
       //   resulting template argument list is used to generate a single
       //   function template specialization, which is added to the set of
       //   overloaded functions considered.
+      // FIXME: We don't really want to build the specialization here, do we?
       FunctionDecl *Specialization = 0;
       TemplateDeductionInfo Info(Context);
       if (TemplateDeductionResult Result
@@ -4064,6 +4065,8 @@
         // FIXME: make a note of the failed deduction for diagnostics.
         (void)Result;
       } else {
+        // FIXME: If the match isn't exact, shouldn't we just drop this as
+        // a candidate? Find a testcase before changing the code.
         assert(FunctionType
                  == Context.getCanonicalType(Specialization->getType()));
         Matches.insert(

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Thu Sep 24 18:14:47 2009
@@ -2877,6 +2877,166 @@
   return DeclPtrTy();
 }
 
+/// \brief Perform semantic analysis for the given function template 
+/// specialization.
+///
+/// This routine performs all of the semantic analysis required for an 
+/// explicit function template specialization. On successful completion,
+/// the function declaration \p FD will become a function template
+/// specialization.
+///
+/// \param FD the function declaration, which will be updated to become a
+/// function template specialization.
+///
+/// \param HasExplicitTemplateArgs whether any template arguments were
+/// explicitly provided.
+///
+/// \param LAngleLoc the location of the left angle bracket ('<'), if
+/// template arguments were explicitly provided.
+///
+/// \param ExplicitTemplateArgs the explicitly-provided template arguments, 
+/// if any.
+///
+/// \param NumExplicitTemplateArgs the number of explicitly-provided template
+/// arguments. This number may be zero even when HasExplicitTemplateArgs is
+/// true as in, e.g., \c void sort<>(char*, char*);
+///
+/// \param RAngleLoc the location of the right angle bracket ('>'), if
+/// template arguments were explicitly provided.
+/// 
+/// \param PrevDecl the set of declarations that 
+bool 
+Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD,
+                                          bool HasExplicitTemplateArgs,
+                                          SourceLocation LAngleLoc,
+                              const TemplateArgument *ExplicitTemplateArgs,
+                                          unsigned NumExplicitTemplateArgs,
+                                          SourceLocation RAngleLoc,
+                                          NamedDecl *&PrevDecl) {
+  // The set of function template specializations that could match this
+  // explicit function template specialization.
+  typedef llvm::SmallVector<FunctionDecl *, 8> CandidateSet;
+  CandidateSet Candidates;
+  
+  DeclContext *FDLookupContext = FD->getDeclContext()->getLookupContext();
+  for (OverloadIterator Ovl(PrevDecl), OvlEnd; Ovl != OvlEnd; ++Ovl) {
+    if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(*Ovl)) {
+      // Only consider templates found within the same semantic lookup scope as 
+      // FD.
+      if (!FDLookupContext->Equals(Ovl->getDeclContext()->getLookupContext()))
+        continue;
+      
+      // C++ [temp.expl.spec]p11:
+      //   A trailing template-argument can be left unspecified in the 
+      //   template-id naming an explicit function template specialization 
+      //   provided it can be deduced from the function argument type.
+      // Perform template argument deduction to determine whether we may be
+      // specializing this template.
+      // FIXME: It is somewhat wasteful to build
+      TemplateDeductionInfo Info(Context);
+      FunctionDecl *Specialization = 0;
+      if (TemplateDeductionResult TDK
+            = DeduceTemplateArguments(FunTmpl, HasExplicitTemplateArgs,
+                                      ExplicitTemplateArgs, 
+                                      NumExplicitTemplateArgs,
+                                      FD->getType(),
+                                      Specialization,
+                                      Info)) {
+        // FIXME: Template argument deduction failed; record why it failed, so
+        // that we can provide nifty diagnostics.
+        (void)TDK;
+        continue;
+      }
+      
+      // Record this candidate.
+      Candidates.push_back(Specialization);
+    }
+  }
+  
+  if (Candidates.empty()) {
+    Diag(FD->getLocation(), diag::err_function_template_spec_no_match)
+      << FD->getDeclName();
+    // FIXME: Print the almost-ran candidates.
+    return true;
+  }
+  
+  if (Candidates.size() > 1) {
+    // C++ [temp.func.order]p1:
+    //   Partial ordering of overloaded function template declarations is used
+    //   [...] when [...] an explicit specialization (14.7.3) refers to a 
+    //   function template specialization.
+    CandidateSet::iterator Best = Candidates.begin();
+    for (CandidateSet::iterator C = Best + 1, CEnd = Candidates.end();
+         C != CEnd; ++C) {
+      if (getMoreSpecializedTemplate((*Best)->getPrimaryTemplate(), 
+                                     (*C)->getPrimaryTemplate(),
+                                     TPOC_Other)
+            == (*C)->getPrimaryTemplate())
+        Best = C;
+    }
+    
+    bool Ambiguous = false;
+    for (CandidateSet::iterator C = Candidates.begin(), CEnd = Candidates.end();
+         C != CEnd; ++C) {
+      if (C != Best &&
+          getMoreSpecializedTemplate((*Best)->getPrimaryTemplate(), 
+                                     (*C)->getPrimaryTemplate(),
+                                     TPOC_Other)
+            != (*Best)->getPrimaryTemplate()) {
+        Ambiguous = true;
+        break;
+      }
+    }
+    
+    if (Ambiguous) {
+      // Partial ordering was ambiguous.
+      Diag(FD->getLocation(), diag::err_function_template_spec_ambiguous)
+        << FD->getDeclName()
+        << HasExplicitTemplateArgs;
+      
+      for (CandidateSet::iterator C = Candidates.begin(), 
+                               CEnd = Candidates.end();
+           C != CEnd; ++C)
+        Diag((*C)->getLocation(), diag::note_function_template_spec_matched)
+          << getTemplateArgumentBindingsText(
+                            (*C)->getPrimaryTemplate()->getTemplateParameters(),
+                                        *(*C)->getTemplateSpecializationArgs());
+      
+      return true;
+    }
+    
+    // Move the best candidate to the front of the candidates list.
+    std::swap(*Best, Candidates.front());
+  }
+  
+  // The first candidate is a prior declaration of the function template
+  // specialization we're declared here, which we may have created above.
+  FunctionDecl *Specialization = Candidates.front();
+  
+  // FIXME: Check if the prior specialization has a point of instantiation.
+  // If so, we have run afoul of C++ [temp.expl.spec]p6.
+  
+  // Mark the prior declaration as an explicit specialization, so that later
+  // clients know that this is an explicit specialization.
+  // FIXME: Check for prior explicit instantiations?
+  Specialization->setTemplateSpecializationKind(TSK_ExplicitSpecialization);
+  
+  // Turn the given function declaration into a function template
+  // specialization, with the template arguments from the previous
+  // specialization.
+  FD->setFunctionTemplateSpecialization(Context, 
+                                        Specialization->getPrimaryTemplate(),
+                         new (Context) TemplateArgumentList(
+                             *Specialization->getTemplateSpecializationArgs()), 
+                                        /*InsertPos=*/0, 
+                                        TSK_ExplicitSpecialization);
+  
+  // The "previous declaration" for this function template specialization is
+  // the prior function template specialization.
+  PrevDecl = Specialization;
+  return false;
+}
+
 // Explicit instantiation of a class template specialization
 // FIXME: Implement extern template semantics
 Sema::DeclResult

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Thu Sep 24 18:14:47 2009
@@ -1436,7 +1436,7 @@
 }
 
 /// \brief Deduce template arguments when taking the address of a function
-/// template (C++ [temp.deduct.funcaddr]).
+/// template (C++ [temp.deduct.funcaddr]) or matching a 
 ///
 /// \param FunctionTemplate the function template for which we are performing
 /// template argument deduction.

Added: cfe/trunk/test/SemaTemplate/function-template-specialization.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/function-template-specialization.cpp?rev=82728&view=auto

==============================================================================
--- cfe/trunk/test/SemaTemplate/function-template-specialization.cpp (added)
+++ cfe/trunk/test/SemaTemplate/function-template-specialization.cpp Thu Sep 24 18:14:47 2009
@@ -0,0 +1,25 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template<int N> void f0(int (&array)[N]);
+
+// Simple function template specialization (using overloading)
+template<> void f0(int (&array)[1]);
+
+void test_f0() {
+  int iarr1[1];
+  f0(iarr1);
+}
+
+// Function template specialization where there are no matches
+template<> void f0(char (&array)[1]); // expected-error{{no function template matches}}
+
+// Function template specialization that requires partial ordering
+template<typename T, int N> void f1(T (&array)[N]); // expected-note{{matches}}
+template<int N> void f1(int (&array)[N]); // expected-note{{matches}}
+
+template<> void f1(float (&array)[1]);
+template<> void f1(int (&array)[1]);
+
+// Function template specialization that results in an ambiguity
+template<typename T> void f1(T (&array)[17]); // expected-note{{matches}}
+template<> void f1(int (&array)[17]); // expected-error{{ambiguous}}





More information about the cfe-commits mailing list