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.

llvm-svn: 290399
This commit is contained in:
Richard Smith 2016-12-23 01:30:39 +00:00
parent fcd33149b4
commit 593d6a168f
7 changed files with 160 additions and 32 deletions

View File

@ -301,6 +301,10 @@ public:
Integer.Type = T.getAsOpaquePtr(); 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. /// \brief Retrieve the template argument as an expression.
Expr *getAsExpr() const { Expr *getAsExpr() const {
assert(getKind() == Expression && "Unexpected kind"); assert(getKind() == Expression && "Unexpected kind");

View File

@ -3343,6 +3343,10 @@ def note_ovl_candidate_incomplete_deduction : Note<"candidate template ignored:
def note_ovl_candidate_inconsistent_deduction : Note< def note_ovl_candidate_inconsistent_deduction : Note<
"candidate template ignored: deduced conflicting %select{types|values|" "candidate template ignored: deduced conflicting %select{types|values|"
"templates}0 for parameter %1%diff{ ($ vs. $)|}2,3">; "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< def note_ovl_candidate_explicit_arg_mismatch_named : Note<
"candidate template ignored: invalid explicitly-specified argument " "candidate template ignored: invalid explicitly-specified argument "
"for template parameter %0">; "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">; "non-type template parameter %0 with type %1 has incompatible initializer of type %2">;
def err_deduced_non_type_template_arg_type_mismatch : Error< def err_deduced_non_type_template_arg_type_mismatch : Error<
"deduced non-type template argument does not have the same type as the " "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< def err_non_type_template_arg_subobject : Error<
"non-type template argument refers to subobject '%0'">; "non-type template argument refers to subobject '%0'">;
def err_non_type_template_arg_addr_label_diff : Error< def err_non_type_template_arg_addr_label_diff : Error<

View File

@ -243,6 +243,31 @@ Optional<unsigned> TemplateArgument::getNumTemplateExpansions() const {
return None; 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, void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID,
const ASTContext &Context) const { const ASTContext &Context) const {
ID.AddInteger(getKind()); ID.AddInteger(getKind());

View File

@ -9593,9 +9593,25 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
int which = 0; int which = 0;
if (isa<TemplateTypeParmDecl>(ParamD)) if (isa<TemplateTypeParmDecl>(ParamD))
which = 0; 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; which = 1;
else { } else {
which = 2; which = 2;
} }

View File

@ -158,6 +158,20 @@ checkDeducedTemplateArguments(ASTContext &Context,
if (Y.isNull()) if (Y.isNull())
return X; 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()) { switch (X.getKind()) {
case TemplateArgument::Null: case TemplateArgument::Null:
llvm_unreachable("Non-deduced template arguments handled above"); llvm_unreachable("Non-deduced template arguments handled above");
@ -184,9 +198,7 @@ checkDeducedTemplateArguments(ASTContext &Context,
Y.getKind() == TemplateArgument::Declaration || Y.getKind() == TemplateArgument::Declaration ||
(Y.getKind() == TemplateArgument::Integral && (Y.getKind() == TemplateArgument::Integral &&
hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral()))) hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral())))
return DeducedTemplateArgument(X, return X.wasDeducedFromArrayBound() ? Y : X;
X.wasDeducedFromArrayBound() &&
Y.wasDeducedFromArrayBound());
// All other combinations are incompatible. // All other combinations are incompatible.
return DeducedTemplateArgument(); return DeducedTemplateArgument();
@ -208,37 +220,38 @@ checkDeducedTemplateArguments(ASTContext &Context,
// All other combinations are incompatible. // All other combinations are incompatible.
return DeducedTemplateArgument(); return DeducedTemplateArgument();
case TemplateArgument::Expression: case TemplateArgument::Expression: {
// If we deduced a dependent expression in one case and either an integral if (Y.getKind() != TemplateArgument::Expression)
// constant or a declaration in another case, keep the integral constant return checkDeducedTemplateArguments(Context, Y, X);
// 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
// Compare the expressions for equality llvm::FoldingSetNodeID ID1, ID2;
llvm::FoldingSetNodeID ID1, ID2; X.getAsExpr()->Profile(ID1, Context, true);
X.getAsExpr()->Profile(ID1, Context, true); Y.getAsExpr()->Profile(ID2, Context, true);
Y.getAsExpr()->Profile(ID2, Context, true); if (ID1 == ID2)
if (ID1 == ID2) return X.wasDeducedFromArrayBound() ? Y : X;
return X;
}
// All other combinations are incompatible. // Differing dependent expressions are incompatible.
return DeducedTemplateArgument(); return DeducedTemplateArgument();
}
case TemplateArgument::Declaration: case TemplateArgument::Declaration:
assert(!X.wasDeducedFromArrayBound());
// If we deduced a declaration and a dependent expression, keep the // If we deduced a declaration and a dependent expression, keep the
// declaration. // declaration.
if (Y.getKind() == TemplateArgument::Expression) if (Y.getKind() == TemplateArgument::Expression)
return X; return X;
// If we deduced a declaration and an integral constant, keep the // If we deduced a declaration and an integral constant, keep the
// integral constant. // integral constant and whichever type did not come from an array
if (Y.getKind() == TemplateArgument::Integral) // bound.
if (Y.getKind() == TemplateArgument::Integral) {
if (Y.wasDeducedFromArrayBound())
return TemplateArgument(Context, Y.getAsIntegral(),
X.getParamTypeForDecl());
return Y; return Y;
}
// If we deduced two declarations, make sure they they refer to the // If we deduced two declarations, make sure they they refer to the
// same declaration. // same declaration.
@ -260,9 +273,8 @@ checkDeducedTemplateArguments(ASTContext &Context,
if (Y.getKind() == TemplateArgument::Integral) if (Y.getKind() == TemplateArgument::Integral)
return Y; return Y;
// If we deduced two null pointers, make sure they have the same type. // If we deduced two null pointers, they are the same.
if (Y.getKind() == TemplateArgument::NullPtr && if (Y.getKind() == TemplateArgument::NullPtr)
Context.hasSameType(X.getNullPtrType(), Y.getNullPtrType()))
return X; return X;
// All other combinations are incompatible. // All other combinations are incompatible.
@ -405,7 +417,7 @@ DeduceNonTypeTemplateArgument(Sema &S,
"Cannot deduce non-type template argument with depth > 0"); "Cannot deduce non-type template argument with depth > 0");
D = D ? cast<ValueDecl>(D->getCanonicalDecl()) : nullptr; D = D ? cast<ValueDecl>(D->getCanonicalDecl()) : nullptr;
TemplateArgument New(D, NTTP->getType()); TemplateArgument New(D, T);
DeducedTemplateArgument NewDeduced(New); DeducedTemplateArgument NewDeduced(New);
DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context,
Deduced[NTTP->getIndex()], Deduced[NTTP->getIndex()],
@ -1685,7 +1697,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
llvm::APSInt ArgSize(S.Context.getTypeSize(S.Context.IntTy), false); llvm::APSInt ArgSize(S.Context.getTypeSize(S.Context.IntTy), false);
ArgSize = VectorArg->getNumElements(); ArgSize = VectorArg->getNumElements();
return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, ArgSize, return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, ArgSize,
S.Context.IntTy, false, Info, Deduced); S.Context.IntTy, false, Info,
Deduced);
} }
if (const DependentSizedExtVectorType *VectorArg if (const DependentSizedExtVectorType *VectorArg

View File

@ -32,8 +32,11 @@ namespace PR6707 {
static const unsigned char ten = 10; static const unsigned char ten = 10;
template<typename T, T Value, typename U> template<typename T, T Value, typename U>
void f2(X<T, Value>, X<U, Value>); 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() { 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}}
} }
} }

View File

@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 // 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 argument deduction with template template parameters.
template<typename T, template<T> class A> template<typename T, template<T> class A>
@ -266,10 +267,72 @@ int main() {
} // end ns2 } // 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 { namespace nullptr_deduction {
using nullptr_t = decltype(nullptr);
template<typename T, T v> struct X {}; template<typename T, T v> struct X {};
template<typename T, T v> void f(X<T, v>) { template<typename T, T v> void f(X<T, v>) {
static_assert(!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;
} }