[cfe-commits] r81777 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/Sema.h lib/Sema/SemaOverload.cpp lib/Sema/SemaTemplate.h lib/Sema/SemaTemplateDeduction.cpp test/CXX/temp/temp.decls/temp.fct/temp.func.order/p4.cpp test/CXX/temp/temp.decls/temp.fct/temp.func.order/p5.cpp test/SemaTemplate/temp_func_order.cpp

Douglas Gregor dgregor at apple.com
Mon Sep 14 11:39:43 PDT 2009


Author: dgregor
Date: Mon Sep 14 13:39:43 2009
New Revision: 81777

URL: http://llvm.org/viewvc/llvm-project?rev=81777&view=rev
Log:
Implement partial ordering of function template specializations 
(C++ [temp.func.order]). 


Added:
    cfe/trunk/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p4.cpp
    cfe/trunk/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p5.cpp
    cfe/trunk/test/SemaTemplate/temp_func_order.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaTemplate.h
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp

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

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Sep 14 13:39:43 2009
@@ -959,10 +959,6 @@
   "partial ordering of class template partial specializations is not yet "
   "supported">;
 
-def unsup_function_template_partial_ordering : Warning<
-  "partial ordering of function templates is unsupported; overload resolution "
-  "may result in an ambiguity that would not occur with a conforming compiler">;
-  
 // C++ Template Instantiation
 def err_template_recursion_depth_exceeded : Error<
   "recursive template instantiation exceeded maximum depth of %0">,

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

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Mon Sep 14 13:39:43 2009
@@ -2730,7 +2730,7 @@
 
   FunctionTemplateDecl *getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
                                                    FunctionTemplateDecl *FT2,
-                                                   bool isCallContext);
+                                           TemplatePartialOrderingContext TPOC);
 
   void MarkDeducedTemplateParameters(const TemplateArgumentList &TemplateArgs,
                                      llvm::SmallVectorImpl<bool> &Deduced);

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Mon Sep 14 13:39:43 2009
@@ -3708,7 +3708,7 @@
     if (FunctionTemplateDecl *BetterTemplate
           = getMoreSpecializedTemplate(Cand1.Function->getPrimaryTemplate(),
                                        Cand2.Function->getPrimaryTemplate(),
-                                       true))
+                                       TPOC_Call))
       return BetterTemplate == Cand1.Function->getPrimaryTemplate();
 
   //   -- the context is an initialization by user-defined conversion
@@ -4029,7 +4029,7 @@
     for (++M; M != MEnd; ++M)
       if (getMoreSpecializedTemplate((*M)->getPrimaryTemplate(),
                                      (*Best)->getPrimaryTemplate(),
-                                     false)
+                                     TPOC_Other)
             == (*M)->getPrimaryTemplate())
         Best = M;
 
@@ -4040,7 +4040,7 @@
       if (M != Best &&
           getMoreSpecializedTemplate((*M)->getPrimaryTemplate(),
                                      (*Best)->getPrimaryTemplate(),
-                                     false)
+                                     TPOC_Other)
            != (*Best)->getPrimaryTemplate()) {
         Ambiguous = true;
         break;

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.h (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.h Mon Sep 14 13:39:43 2009
@@ -86,6 +86,19 @@
       return *TemplateArgumentLists.front();
     }
   };
+  
+  /// \brief The context in which partial ordering of function templates occurs.
+  enum TemplatePartialOrderingContext {
+    /// \brief Partial ordering of function templates for a function call.
+    TPOC_Call,
+    /// \brief Partial ordering of function templates for a call to a 
+    /// conversion function.
+    TPOC_Conversion,
+    /// \brief Partial ordering of function templates in other contexts, e.g.,
+    /// taking the address of a function template or matching a function 
+    /// template specialization to a function template.
+    TPOC_Other
+  };
 }
 
 #endif // LLVM_CLANG_SEMA_TEMPLATE_H

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Mon Sep 14 13:39:43 2009
@@ -18,6 +18,7 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/Parse/DeclSpec.h"
 #include "llvm/Support/Compiler.h"
+#include <algorithm>
 
 namespace clang {
   /// \brief Various flags that control template argument deduction.
@@ -1581,6 +1582,209 @@
   return Result;
 }
 
+/// \brief Stores the result of comparing the qualifiers of two types.
+enum DeductionQualifierComparison { 
+  NeitherMoreQualified = 0, 
+  ParamMoreQualified, 
+  ArgMoreQualified 
+};
+
+/// \brief Deduce the template arguments during partial ordering by comparing 
+/// the parameter type and the argument type (C++0x [temp.deduct.partial]).
+///
+/// \param Context the AST context in which this deduction occurs.
+///
+/// \param TemplateParams the template parameters that we are deducing
+///
+/// \param ParamIn the parameter type
+///
+/// \param ArgIn the argument type
+///
+/// \param Info information about the template argument deduction itself
+///
+/// \param Deduced the deduced template arguments
+///
+/// \returns the result of template argument deduction so far. Note that a
+/// "success" result means that template argument deduction has not yet failed,
+/// but it may still fail, later, for other reasons.
+static Sema::TemplateDeductionResult
+DeduceTemplateArgumentsDuringPartialOrdering(ASTContext &Context,
+                                          TemplateParameterList *TemplateParams,
+                                             QualType ParamIn, QualType ArgIn,
+                                             Sema::TemplateDeductionInfo &Info,
+                             llvm::SmallVectorImpl<TemplateArgument> &Deduced,
+    llvm::SmallVectorImpl<DeductionQualifierComparison> *QualifierComparisons) {
+  CanQualType Param = Context.getCanonicalType(ParamIn);
+  CanQualType Arg = Context.getCanonicalType(ArgIn);
+
+  // C++0x [temp.deduct.partial]p5:
+  //   Before the partial ordering is done, certain transformations are 
+  //   performed on the types used for partial ordering: 
+  //     - If P is a reference type, P is replaced by the type referred to. 
+  CanQual<ReferenceType> ParamRef = Param->getAs<ReferenceType>();
+  if (ParamRef)
+    Param = ParamRef->getPointeeType();
+  
+  //     - If A is a reference type, A is replaced by the type referred to.
+  CanQual<ReferenceType> ArgRef = Arg->getAs<ReferenceType>();
+  if (ArgRef)
+    Arg = ArgRef->getPointeeType();
+  
+  if (QualifierComparisons && ParamRef && ArgRef) {
+    // C++0x [temp.deduct.partial]p6:
+    //   If both P and A were reference types (before being replaced with the 
+    //   type referred to above), determine which of the two types (if any) is 
+    //   more cv-qualified than the other; otherwise the types are considered to 
+    //   be equally cv-qualified for partial ordering purposes. The result of this
+    //   determination will be used below.
+    //
+    // We save this information for later, using it only when deduction 
+    // succeeds in both directions.
+    DeductionQualifierComparison QualifierResult = NeitherMoreQualified;
+    if (Param.isMoreQualifiedThan(Arg))
+      QualifierResult = ParamMoreQualified;
+    else if (Arg.isMoreQualifiedThan(Param))
+      QualifierResult = ArgMoreQualified;
+    QualifierComparisons->push_back(QualifierResult);
+  }
+  
+  // C++0x [temp.deduct.partial]p7:
+  //   Remove any top-level cv-qualifiers:
+  //     - If P is a cv-qualified type, P is replaced by the cv-unqualified 
+  //       version of P.
+  Param = Param.getUnqualifiedType();
+  //     - If A is a cv-qualified type, A is replaced by the cv-unqualified 
+  //       version of A.
+  Arg = Arg.getUnqualifiedType();
+  
+  // C++0x [temp.deduct.partial]p8:
+  //   Using the resulting types P and A the deduction is then done as 
+  //   described in 14.9.2.5. If deduction succeeds for a given type, the type
+  //   from the argument template is considered to be at least as specialized
+  //   as the type from the parameter template.
+  return DeduceTemplateArguments(Context, TemplateParams, Param, Arg, Info,
+                                 Deduced, TDF_None);
+}
+
+static void
+MarkDeducedTemplateParameters(Sema &SemaRef, QualType T,
+                              llvm::SmallVectorImpl<bool> &Deduced);
+  
+/// \brief Determine whether the function template \p FT1 is at least as
+/// specialized as \p FT2.
+static bool isAtLeastAsSpecializedAs(Sema &S,
+                                     FunctionTemplateDecl *FT1,
+                                     FunctionTemplateDecl *FT2,
+                                     TemplatePartialOrderingContext TPOC,
+    llvm::SmallVectorImpl<DeductionQualifierComparison> *QualifierComparisons) {
+  FunctionDecl *FD1 = FT1->getTemplatedDecl();
+  FunctionDecl *FD2 = FT2->getTemplatedDecl();  
+  const FunctionProtoType *Proto1 = FD1->getType()->getAs<FunctionProtoType>();
+  const FunctionProtoType *Proto2 = FD2->getType()->getAs<FunctionProtoType>();
+  
+  assert(Proto1 && Proto2 && "Function templates must have prototypes");
+  TemplateParameterList *TemplateParams = FT2->getTemplateParameters();
+  llvm::SmallVector<TemplateArgument, 4> Deduced;
+  Deduced.resize(TemplateParams->size());
+
+  // C++0x [temp.deduct.partial]p3:
+  //   The types used to determine the ordering depend on the context in which
+  //   the partial ordering is done:
+  Sema::TemplateDeductionInfo Info(S.Context);
+  switch (TPOC) {
+  case TPOC_Call: {
+    //   - In the context of a function call, the function parameter types are
+    //     used.
+    unsigned NumParams = std::min(Proto1->getNumArgs(), Proto2->getNumArgs());
+    for (unsigned I = 0; I != NumParams; ++I)
+      if (DeduceTemplateArgumentsDuringPartialOrdering(S.Context,
+                                                       TemplateParams,
+                                                       Proto2->getArgType(I),
+                                                       Proto1->getArgType(I),
+                                                       Info,
+                                                       Deduced,
+                                                       QualifierComparisons))
+        return false;
+    
+    break;
+  }
+    
+  case TPOC_Conversion:
+    //   - In the context of a call to a conversion operator, the return types
+    //     of the conversion function templates are used.
+    if (DeduceTemplateArgumentsDuringPartialOrdering(S.Context,
+                                                     TemplateParams,
+                                                     Proto2->getResultType(),
+                                                     Proto1->getResultType(),
+                                                     Info,
+                                                     Deduced,
+                                                     QualifierComparisons))
+      return false;
+    break;
+    
+  case TPOC_Other:
+    //   - In other contexts (14.6.6.2) the function template’s function type 
+    //     is used.
+    if (DeduceTemplateArgumentsDuringPartialOrdering(S.Context,
+                                                     TemplateParams,
+                                                     FD2->getType(),
+                                                     FD1->getType(),
+                                                     Info,
+                                                     Deduced,
+                                                     QualifierComparisons))
+      return false;
+    break;
+  }
+  
+  // C++0x [temp.deduct.partial]p11:
+  //   In most cases, all template parameters must have values in order for 
+  //   deduction to succeed, but for partial ordering purposes a template 
+  //   parameter may remain without a value provided it is not used in the 
+  //   types being used for partial ordering. [ Note: a template parameter used
+  //   in a non-deduced context is considered used. -end note]
+  unsigned ArgIdx = 0, NumArgs = Deduced.size();
+  for (; ArgIdx != NumArgs; ++ArgIdx)
+    if (Deduced[ArgIdx].isNull())
+      break;
+
+  if (ArgIdx == NumArgs) {
+    // All template arguments were deduced. FT1 is at least as specialized 
+    // as FT2.
+    return true;
+  }
+
+  // FIXME: MarkDeducedTemplateParameters needs to become 
+  // MarkUsedTemplateParameters with a flag that tells us whether to mark
+  // template parameters that are used in non-deduced contexts.
+  llvm::SmallVector<bool, 4> UsedParameters;
+  UsedParameters.resize(TemplateParams->size());
+  switch (TPOC) {
+  case TPOC_Call: {
+    unsigned NumParams = std::min(Proto1->getNumArgs(), Proto2->getNumArgs());
+    for (unsigned I = 0; I != NumParams; ++I)
+      ::MarkDeducedTemplateParameters(S, Proto2->getArgType(I), UsedParameters);
+    break;
+  }
+    
+  case TPOC_Conversion:
+    ::MarkDeducedTemplateParameters(S, Proto2->getResultType(), UsedParameters);
+    break;
+    
+  case TPOC_Other:
+    ::MarkDeducedTemplateParameters(S, FD2->getType(), UsedParameters);
+    break;
+  }
+  
+  for (; ArgIdx != NumArgs; ++ArgIdx)
+    // If this argument had no value deduced but was used in one of the types
+    // used for partial ordering, then deduction fails.
+    if (Deduced[ArgIdx].isNull() && UsedParameters[ArgIdx])
+      return false;
+  
+  return true;
+}
+                                    
+                                     
 /// \brief Returns the more specialization function template according
 /// to the rules of function template partial ordering (C++ [temp.func.order]).
 ///
@@ -1588,29 +1792,70 @@
 ///
 /// \param FT2 the second function template
 ///
-/// \param isCallContext whether partial ordering is being performed
-/// for a function call (which ignores the return types of the
-/// functions).
+/// \param TPOC the context in which we are performing partial ordering of
+/// function templates.
 ///
 /// \returns the more specialization function template. If neither
 /// template is more specialized, returns NULL.
 FunctionTemplateDecl *
 Sema::getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
                                  FunctionTemplateDecl *FT2,
-                                 bool isCallContext) {
-#if 0
+                                 TemplatePartialOrderingContext TPOC) {
   // FIXME: Implement this
-  bool Better1 = isAtLeastAsSpecializedAs(*this, FT1, FT2, isCallContext);
-  bool Better2 = isAtLeastAsSpecializedAs(*this, FT2, FT1, isCallContext);
-  if (Better1 == Better2)
+  llvm::SmallVector<DeductionQualifierComparison, 4> QualifierComparisons;
+  bool Better1 = isAtLeastAsSpecializedAs(*this, FT1, FT2, TPOC, 0);
+  bool Better2 = isAtLeastAsSpecializedAs(*this, FT2, FT1, TPOC, 
+                                          &QualifierComparisons);
+  
+  if (Better1 != Better2) // We have a clear winner
+    return Better1? FT1 : FT2;
+  
+  if (!Better1 && !Better2) // Neither is better than the other
     return 0;
+
+
+  // C++0x [temp.deduct.partial]p10:
+  //   If for each type being considered a given template is at least as 
+  //   specialized for all types and more specialized for some set of types and
+  //   the other template is not more specialized for any types or is not at 
+  //   least as specialized for any types, then the given template is more
+  //   specialized than the other template. Otherwise, neither template is more
+  //   specialized than the other.
+  Better1 = false;
+  Better2 = false;
+  for (unsigned I = 0, N = QualifierComparisons.size(); I != N; ++I) {
+    // C++0x [temp.deduct.partial]p9:
+    //   If, for a given type, deduction succeeds in both directions (i.e., the
+    //   types are identical after the transformations above) and if the type
+    //   from the argument template is more cv-qualified than the type from the
+    //   parameter template (as described above) that type is considered to be
+    //   more specialized than the other. If neither type is more cv-qualified 
+    //   than the other then neither type is more specialized than the other.
+    switch (QualifierComparisons[I]) {
+      case NeitherMoreQualified:
+        break;
+        
+      case ParamMoreQualified:
+        Better1 = true;
+        if (Better2)
+          return 0;
+        break;
+        
+      case ArgMoreQualified:
+        Better2 = true;
+        if (Better1)
+          return 0;
+        break;
+    }
+  }
+   
+  assert(!(Better1 && Better2) && "Should have broken out in the loop above");
   if (Better1)
     return FT1;
-  return FT2;
-#else
-  Diag(SourceLocation(), diag::unsup_function_template_partial_ordering);
-  return 0;
-#endif
+  else if (Better2)
+    return FT2;
+  else
+    return 0;
 }
 
 static void

Added: cfe/trunk/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p4.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p4.cpp?rev=81777&view=auto

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p4.cpp (added)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p4.cpp Mon Sep 14 13:39:43 2009
@@ -0,0 +1,23 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template<class T> struct A { A(); };
+template<class T> int &f(T); 
+template<class T> float &f(T*); 
+template<class T> double &f(const T*);
+
+template<class T> void g(T); // expected-note{{candidate}}
+template<class T> void g(T&); // expected-note{{candidate}}
+
+template<class T> int &h(const T&); 
+template<class T> float &h(A<T>&);
+
+void m() { 
+  const int *p; 
+  double &dr1 = f(p); 
+  float x; 
+  g(x); // expected-error{{ambiguous}}
+  A<int> z; 
+  float &fr1 = h(z);
+  const A<int> z2; 
+  int &ir1 = h(z2);
+}

Added: cfe/trunk/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p5.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p5.cpp?rev=81777&view=auto

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p5.cpp (added)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p5.cpp Mon Sep 14 13:39:43 2009
@@ -0,0 +1,12 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+template<class T> int &f(T); 
+template<class T> float &f(T*, int=1); 
+
+template<class T> int &g(T); 
+template<class T> float &g(T*, ...);
+
+int main() { 
+  int* ip; 
+  float &fr1 = f(ip); 
+  float &fr2 = g(ip);
+}

Added: cfe/trunk/test/SemaTemplate/temp_func_order.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_func_order.cpp?rev=81777&view=auto

==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_func_order.cpp (added)
+++ cfe/trunk/test/SemaTemplate/temp_func_order.cpp Mon Sep 14 13:39:43 2009
@@ -0,0 +1,86 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template<typename T>
+int &f0(T);
+
+template<typename T>
+float &f0(T*);
+
+void test_f0(int i, int *ip) {
+  int &ir = f0(i);
+  float &fr = f0(ip);
+}
+
+template<typename T, typename U>
+int &f1(T, U);
+
+template<typename T>
+float &f1(T, T);
+
+void test_f1(int i, float f) {
+  int &ir = f1(i, f);
+  float &fr1 = f1(i, i);
+  float &fr2 = f1(f, f);
+}
+
+template<typename T, typename U>
+struct A { };
+
+template<typename T>
+int &f2(T);
+
+template<typename T, typename U>
+float &f2(A<T, U>);
+
+template<typename T>
+double &f2(A<T, T>);
+
+void test_f2(int i, A<int, float> aif, A<int, int> aii) {
+  int &ir = f2(i);
+  float &fr = f2(aif);
+  double &dr = f2(aii);
+}
+
+template<typename T, typename U>
+int &f3(T*, U); // expected-note{{candidate}}
+
+template<typename T, typename U>
+float &f3(T, U*); // expected-note{{candidate}}
+
+void test_f3(int i, int *ip, float *fp) {
+  int &ir = f3(ip, i);
+  float &fr = f3(i, fp);
+  f3(ip, ip); // expected-error{{ambiguous}}
+}
+
+template<typename T>
+int &f4(T&);
+
+template<typename T>
+float &f4(const T&);
+
+void test_f4(int i, const int ic) {
+  int &ir1 = f4(i);
+  float &fr1 = f4(ic);
+}
+
+template<typename T, typename U>
+int &f5(T&, const U&); // expected-note{{candidate}}
+
+template<typename T, typename U>
+float &f5(const T&, U&); // expected-note{{candidate}}
+
+void test_f5(int i, const int ic) {
+  f5(i, i); // expected-error{{ambiguous}}
+}
+
+template<typename T, typename U>
+int &f6(T&, U&);
+
+template<typename T, typename U>
+float &f6(const T&, U&);
+
+void test_f6(int i, const int ic) {
+  int &ir = f6(i, i);
+  float &fr = f6(ic, ic);
+}





More information about the cfe-commits mailing list