]> granicus.if.org Git - clang/commitdiff
When merging two deduced non-type template arguments for the same parameter,
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 23 Dec 2016 01:30:39 +0000 (01:30 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 23 Dec 2016 01:30:39 +0000 (01:30 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@290399 91177308-0d34-0410-b5e6-96231b3b80d8

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

index bccb5787925941ad6dd0489d4215479d4e753abb..4f268a646c2184ba49cec259709946aef16b08b3 100644 (file)
@@ -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");
index 7a6598baab1f6454ab3a2b1a1c6dee1f28d00557..a95792458799a27d5e866308c093fbd0b552b4ec 100644 (file)
@@ -3343,6 +3343,10 @@ def note_ovl_candidate_incomplete_deduction : Note<"candidate template ignored:
 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_deduction_failure : Error<
   "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<
index b1c88f93b6a75d67adf2fba4401feb48193fa2ef..099f939c7a7591a372c3f59b039d4b3c8dce8194 100644 (file)
@@ -243,6 +243,31 @@ Optional<unsigned> TemplateArgument::getNumTemplateExpansions() const {
   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());
index 5252e6a26c9a07e12ef985906e9bec0c8d254018..626a6ca98155d6f1b3e77c29be1963429f5d55a9 100644 (file)
@@ -9593,9 +9593,25 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
     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;
     }
 
index 9ec5bb9939f76bb2d2c53cfe948037fda2a79654..9d970cfe59a20d06fa79c329e2ce627cdad83fc1 100644 (file)
@@ -158,6 +158,20 @@ checkDeducedTemplateArguments(ASTContext &Context,
   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 &Context,
         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 &Context,
     // 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 &Context,
     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 &S,
         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
index 9fd3df59d103e658804d9499466b94a3436cdf2f..f7103a33cc72d586758101b3d9b524e21195ee44 100644 (file)
@@ -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@-1 {{candidate template ignored: deduced values of conflicting types for parameter 'Value' (10 of type 'int' vs. 10 of type 'char')}}
+  // expected-note@-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}}
   }
 }
index 7c7b0ab40dadfb8983b09fe6680818b0fa175f88..2713ac1d43ef233df96d6f73c3b460ecd7d9dfdf 100644 (file)
@@ -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;
 }