r290399 - When merging two deduced non-type template arguments for the same parameter,

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Dec 22 17:30:39 PST 2016


Author: rsmith
Date: Thu Dec 22 19:30:39 2016
New Revision: 290399

URL: http://llvm.org/viewvc/llvm-project?rev=290399&view=rev
Log:
When merging two deduced non-type template arguments for the same parameter,
fail the merge if the arguments have different types (except if one of them was
deduced from an array bound, in which case take the type from the other).

This is correct because (except in the array bound case) the type of the
template argument in each deduction must match the type of the parameter, so at
least one of the two deduced arguments must have a mismatched type.

This is necessary because we would otherwise lose the type information for the
discarded template argument in the merge, and fail to diagnose the mismatch.

In order to power this, we now properly retain the type of a deduced non-type
template argument deduced from a declaration, rather than giving it the type of
the template parameter; we'll convert it to the template parameter type when
checking the deduced arguments.

Modified:
    cfe/trunk/include/clang/AST/TemplateBase.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/AST/TemplateBase.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
    cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp
    cfe/trunk/test/SemaTemplate/deduction.cpp

Modified: cfe/trunk/include/clang/AST/TemplateBase.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/TemplateBase.h?rev=290399&r1=290398&r2=290399&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/TemplateBase.h (original)
+++ cfe/trunk/include/clang/AST/TemplateBase.h Thu Dec 22 19:30:39 2016
@@ -301,6 +301,10 @@ public:
     Integer.Type = T.getAsOpaquePtr();
   }
 
+  /// \brief If this is a non-type template argument, get its type. Otherwise,
+  /// returns a null QualType.
+  QualType getNonTypeTemplateArgumentType() const;
+
   /// \brief Retrieve the template argument as an expression.
   Expr *getAsExpr() const {
     assert(getKind() == Expression && "Unexpected kind");

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=290399&r1=290398&r2=290399&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Dec 22 19:30:39 2016
@@ -3343,6 +3343,10 @@ def note_ovl_candidate_incomplete_deduct
 def note_ovl_candidate_inconsistent_deduction : Note<
     "candidate template ignored: deduced conflicting %select{types|values|"
     "templates}0 for parameter %1%diff{ ($ vs. $)|}2,3">;
+def note_ovl_candidate_inconsistent_deduction_types : Note<
+    "candidate template ignored: deduced values %diff{"
+    "of conflicting types for parameter %0 (%1 of type $ vs. %3 of type $)|"
+    "%1 and %3 of conflicting types for parameter %0|}2,4">;
 def note_ovl_candidate_explicit_arg_mismatch_named : Note<
     "candidate template ignored: invalid explicitly-specified argument "
     "for template parameter %0">;
@@ -3840,7 +3844,7 @@ def err_non_type_template_parm_type_dedu
   "non-type template parameter %0 with type %1 has incompatible initializer of type %2">;
 def err_deduced_non_type_template_arg_type_mismatch : Error<
   "deduced non-type template argument does not have the same type as the "
-  "its corresponding template parameter%diff{ ($ vs $)|}0,1">;
+  "corresponding template parameter%diff{ ($ vs $)|}0,1">;
 def err_non_type_template_arg_subobject : Error<
   "non-type template argument refers to subobject '%0'">;
 def err_non_type_template_arg_addr_label_diff : Error<

Modified: cfe/trunk/lib/AST/TemplateBase.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/TemplateBase.cpp?rev=290399&r1=290398&r2=290399&view=diff
==============================================================================
--- cfe/trunk/lib/AST/TemplateBase.cpp (original)
+++ cfe/trunk/lib/AST/TemplateBase.cpp Thu Dec 22 19:30:39 2016
@@ -243,6 +243,31 @@ Optional<unsigned> TemplateArgument::get
   return None; 
 }
 
+QualType TemplateArgument::getNonTypeTemplateArgumentType() const {
+  switch (getKind()) {
+  case TemplateArgument::Null:
+  case TemplateArgument::Type:
+  case TemplateArgument::Template:
+  case TemplateArgument::TemplateExpansion:
+  case TemplateArgument::Pack:
+    return QualType();
+
+  case TemplateArgument::Integral:
+    return getIntegralType();
+
+  case TemplateArgument::Expression:
+    return getAsExpr()->getType();
+
+  case TemplateArgument::Declaration:
+    return getParamTypeForDecl();
+
+  case TemplateArgument::NullPtr:
+    return getNullPtrType();
+  }
+
+  llvm_unreachable("Invalid TemplateArgument Kind!");
+}
+
 void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID,
                                const ASTContext &Context) const {
   ID.AddInteger(getKind());

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=290399&r1=290398&r2=290399&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Thu Dec 22 19:30:39 2016
@@ -9593,9 +9593,25 @@ static void DiagnoseBadDeduction(Sema &S
     int which = 0;
     if (isa<TemplateTypeParmDecl>(ParamD))
       which = 0;
-    else if (isa<NonTypeTemplateParmDecl>(ParamD))
+    else if (isa<NonTypeTemplateParmDecl>(ParamD)) {
+      // Deduction might have failed because we deduced arguments of two
+      // different types for a non-type template parameter.
+      // FIXME: Use a different TDK value for this.
+      QualType T1 =
+          DeductionFailure.getFirstArg()->getNonTypeTemplateArgumentType();
+      QualType T2 =
+          DeductionFailure.getSecondArg()->getNonTypeTemplateArgumentType();
+      if (!S.Context.hasSameType(T1, T2)) {
+        S.Diag(Templated->getLocation(),
+               diag::note_ovl_candidate_inconsistent_deduction_types)
+          << ParamD->getDeclName() << *DeductionFailure.getFirstArg() << T1
+          << *DeductionFailure.getSecondArg() << T2;
+        MaybeEmitInheritedConstructorNote(S, Found);
+        return;
+      }
+
       which = 1;
-    else {
+    } else {
       which = 2;
     }
 

Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=290399&r1=290398&r2=290399&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Thu Dec 22 19:30:39 2016
@@ -158,6 +158,20 @@ checkDeducedTemplateArguments(ASTContext
   if (Y.isNull())
     return X;
 
+  // If we have two non-type template argument values deduced for the same
+  // parameter, they must both match the type of the parameter, and thus must
+  // match each other's type. As we're only keeping one of them, we must check
+  // for that now. The exception is that if either was deduced from an array
+  // bound, the type is permitted to differ.
+  if (!X.wasDeducedFromArrayBound() && !Y.wasDeducedFromArrayBound()) {
+    QualType XType = X.getNonTypeTemplateArgumentType();
+    if (!XType.isNull()) {
+      QualType YType = Y.getNonTypeTemplateArgumentType();
+      if (YType.isNull() || !Context.hasSameType(XType, YType))
+        return DeducedTemplateArgument();
+    }
+  }
+
   switch (X.getKind()) {
   case TemplateArgument::Null:
     llvm_unreachable("Non-deduced template arguments handled above");
@@ -184,9 +198,7 @@ checkDeducedTemplateArguments(ASTContext
         Y.getKind() == TemplateArgument::Declaration ||
         (Y.getKind() == TemplateArgument::Integral &&
          hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral())))
-      return DeducedTemplateArgument(X,
-                                     X.wasDeducedFromArrayBound() &&
-                                     Y.wasDeducedFromArrayBound());
+      return X.wasDeducedFromArrayBound() ? Y : X;
 
     // All other combinations are incompatible.
     return DeducedTemplateArgument();
@@ -208,37 +220,38 @@ checkDeducedTemplateArguments(ASTContext
     // All other combinations are incompatible.
     return DeducedTemplateArgument();
 
-  case TemplateArgument::Expression:
-    // If we deduced a dependent expression in one case and either an integral
-    // constant or a declaration in another case, keep the integral constant
-    // or declaration.
-    if (Y.getKind() == TemplateArgument::Integral ||
-        Y.getKind() == TemplateArgument::Declaration)
-      return DeducedTemplateArgument(Y, X.wasDeducedFromArrayBound() &&
-                                     Y.wasDeducedFromArrayBound());
-
-    if (Y.getKind() == TemplateArgument::Expression) {
-      // Compare the expressions for equality
-      llvm::FoldingSetNodeID ID1, ID2;
-      X.getAsExpr()->Profile(ID1, Context, true);
-      Y.getAsExpr()->Profile(ID2, Context, true);
-      if (ID1 == ID2)
-        return X;
-    }
+  case TemplateArgument::Expression: {
+    if (Y.getKind() != TemplateArgument::Expression)
+      return checkDeducedTemplateArguments(Context, Y, X);
+
+    // Compare the expressions for equality
+    llvm::FoldingSetNodeID ID1, ID2;
+    X.getAsExpr()->Profile(ID1, Context, true);
+    Y.getAsExpr()->Profile(ID2, Context, true);
+    if (ID1 == ID2)
+      return X.wasDeducedFromArrayBound() ? Y : X;
 
-    // All other combinations are incompatible.
+    // Differing dependent expressions are incompatible.
     return DeducedTemplateArgument();
+  }
 
   case TemplateArgument::Declaration:
+    assert(!X.wasDeducedFromArrayBound());
+
     // If we deduced a declaration and a dependent expression, keep the
     // declaration.
     if (Y.getKind() == TemplateArgument::Expression)
       return X;
 
     // If we deduced a declaration and an integral constant, keep the
-    // integral constant.
-    if (Y.getKind() == TemplateArgument::Integral)
+    // integral constant and whichever type did not come from an array
+    // bound.
+    if (Y.getKind() == TemplateArgument::Integral) {
+      if (Y.wasDeducedFromArrayBound())
+        return TemplateArgument(Context, Y.getAsIntegral(),
+                                X.getParamTypeForDecl());
       return Y;
+    }
 
     // If we deduced two declarations, make sure they they refer to the
     // same declaration.
@@ -260,9 +273,8 @@ checkDeducedTemplateArguments(ASTContext
     if (Y.getKind() == TemplateArgument::Integral)
       return Y;
 
-    // If we deduced two null pointers, make sure they have the same type.
-    if (Y.getKind() == TemplateArgument::NullPtr &&
-        Context.hasSameType(X.getNullPtrType(), Y.getNullPtrType()))
+    // If we deduced two null pointers, they are the same.
+    if (Y.getKind() == TemplateArgument::NullPtr)
       return X;
 
     // All other combinations are incompatible.
@@ -405,7 +417,7 @@ DeduceNonTypeTemplateArgument(Sema &S,
          "Cannot deduce non-type template argument with depth > 0");
 
   D = D ? cast<ValueDecl>(D->getCanonicalDecl()) : nullptr;
-  TemplateArgument New(D, NTTP->getType());
+  TemplateArgument New(D, T);
   DeducedTemplateArgument NewDeduced(New);
   DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context,
                                                      Deduced[NTTP->getIndex()],
@@ -1685,7 +1697,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema
         llvm::APSInt ArgSize(S.Context.getTypeSize(S.Context.IntTy), false);
         ArgSize = VectorArg->getNumElements();
         return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, ArgSize,
-                                             S.Context.IntTy, false, Info, Deduced);
+                                             S.Context.IntTy, false, Info,
+                                             Deduced);
       }
 
       if (const DependentSizedExtVectorType *VectorArg

Modified: cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp?rev=290399&r1=290398&r2=290399&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp Thu Dec 22 19:30:39 2016
@@ -32,8 +32,11 @@ namespace PR6707 {
   static const unsigned char ten = 10;
   template<typename T, T Value, typename U>
   void f2(X<T, Value>, X<U, Value>);
+  // expected-note at -1 {{candidate template ignored: deduced values of conflicting types for parameter 'Value' (10 of type 'int' vs. 10 of type 'char')}}
+  // expected-note at -2 {{candidate template ignored: deduced values of conflicting types for parameter 'Value' (10 of type 'char' vs. 10 of type 'int')}}
 
   void g2() {
-    f2(X<int, 10>(), X<char, ten>());
+    f2(X<int, 10>(), X<char, ten>()); // expected-error {{no matching}}
+    f2(X<char, 10>(), X<int, ten>()); // expected-error {{no matching}}
   }
 }

Modified: cfe/trunk/test/SemaTemplate/deduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/deduction.cpp?rev=290399&r1=290398&r2=290399&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/deduction.cpp (original)
+++ cfe/trunk/test/SemaTemplate/deduction.cpp Thu Dec 22 19:30:39 2016
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++1z
 
 // Template argument deduction with template template parameters.
 template<typename T, template<T> class A> 
@@ -266,10 +267,72 @@ int main() {
 } // end ns2 
 }
 
+namespace multiple_deduction_different_type {
+  template<typename T, T v> struct X {};
+  template<template<typename T, T> class X, typename T, typename U, int N>
+    void f(X<T, N>, X<U, N>) {} // expected-note 2{{values of conflicting types}}
+  template<template<typename T, T> class X, typename T, typename U, const int *N>
+    void g(X<T, N>, X<U, N>) {} // expected-note 0-2{{values of conflicting types}}
+  int n;
+  void h() {
+    f(X<int, 1+1>(), X<unsigned int, 3-1>()); // expected-error {{no matching function}}
+    f(X<unsigned int, 1+1>(), X<int, 3-1>()); // expected-error {{no matching function}}
+#if __cplusplus > 201402L
+    g(X<const int*, &n>(), X<int*, &n + 1 - 1>()); // expected-error {{no matching function}}
+    g(X<int*, &n>(), X<const int*, &n + 1 - 1>()); // expected-error {{no matching function}}
+#endif
+  }
+
+  template<template<typename T, T> class X, typename T, typename U, T N>
+    void x(X<T, N>, int(*)[N], X<U, N>) {} // expected-note 1+{{candidate}}
+  template<template<typename T, T> class X, typename T, typename U, T N>
+    void x(int(*)[N], X<T, N>, X<U, N>) {} // expected-note 1+{{candidate}}
+  int arr[3];
+  void y() {
+    x(X<int, 3>(), &arr, X<int, 3>());
+    x(&arr, X<int, 3>(), X<int, 3>());
+
+    x(X<int, 3>(), &arr, X<char, 3>()); // expected-error {{no matching function}}
+    x(&arr, X<int, 3>(), X<char, 3>()); // expected-error {{no matching function}}
+
+    x(X<char, 3>(), &arr, X<char, 3>());
+    x(&arr, X<char, 3>(), X<char, 3>());
+  }
+}
+
 namespace nullptr_deduction {
+  using nullptr_t = decltype(nullptr);
+
   template<typename T, T v> struct X {};
   template<typename T, T v> void f(X<T, v>) {
     static_assert(!v, "");
   }
-  void g() { f(X<int*, nullptr>()); }
+  void g() {
+    f(X<int*, nullptr>());
+    f(X<nullptr_t, nullptr>());
+  }
+
+  template<template<typename T, T> class X, typename T, typename U, int *P>
+    void f1(X<T, P>, X<U, P>) {} // expected-note 2{{values of conflicting types}}
+  void h() {
+    f1(X<int*, nullptr>(), X<nullptr_t, nullptr>()); // expected-error {{no matching function}}
+    f1(X<nullptr_t, nullptr>(), X<int*, nullptr>()); // expected-error {{no matching function}}
+  }
+
+  template<template<typename T, T> class X, typename T, typename U, nullptr_t P>
+    void f2(X<T, P>, X<U, P>) {} // expected-note 2{{values of conflicting types}}
+  void i() {
+    f2(X<int*, nullptr>(), X<nullptr_t, nullptr>()); // expected-error {{no matching function}}
+    f2(X<nullptr_t, nullptr>(), X<int*, nullptr>()); // expected-error {{no matching function}}
+  }
+}
+
+namespace member_pointer {
+  struct A { void f(int); };
+  template<typename T, void (A::*F)(T)> struct B;
+  template<typename T> struct C;
+  template<typename T, void (A::*F)(T)> struct C<B<T, F>> {
+    C() { A a; T t; (a.*F)(t); }
+  };
+  C<B<int, &A::f>> c;
 }




More information about the cfe-commits mailing list