From 4066cb5f773ae2921583bee6a4c787b5f4756e81 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 23 Dec 2016 01:30:39 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@290399 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/TemplateBase.h | 4 ++ include/clang/Basic/DiagnosticSemaKinds.td | 6 +- lib/AST/TemplateBase.cpp | 25 +++++++ lib/Sema/SemaOverload.cpp | 20 +++++- lib/Sema/SemaTemplateDeduction.cpp | 69 +++++++++++-------- .../CXX/temp/temp.fct.spec/temp.deduct/p9.cpp | 5 +- test/SemaTemplate/deduction.cpp | 65 ++++++++++++++++- 7 files changed, 161 insertions(+), 33 deletions(-) diff --git a/include/clang/AST/TemplateBase.h b/include/clang/AST/TemplateBase.h index bccb578792..4f268a646c 100644 --- a/include/clang/AST/TemplateBase.h +++ b/include/clang/AST/TemplateBase.h @@ -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"); diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 7a6598baab..a957924587 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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< diff --git a/lib/AST/TemplateBase.cpp b/lib/AST/TemplateBase.cpp index b1c88f93b6..099f939c7a 100644 --- a/lib/AST/TemplateBase.cpp +++ b/lib/AST/TemplateBase.cpp @@ -243,6 +243,31 @@ Optional 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()); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 5252e6a26c..626a6ca981 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -9593,9 +9593,25 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated, int which = 0; if (isa(ParamD)) which = 0; - else if (isa(ParamD)) + else if (isa(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; } diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 9ec5bb9939..9d970cfe59 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -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(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 diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp index 9fd3df59d1..f7103a33cc 100644 --- a/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp @@ -32,8 +32,11 @@ namespace PR6707 { static const unsigned char ten = 10; template void f2(X, X); + // 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(), X()); + f2(X(), X()); // expected-error {{no matching}} + f2(X(), X()); // expected-error {{no matching}} } } diff --git a/test/SemaTemplate/deduction.cpp b/test/SemaTemplate/deduction.cpp index 7c7b0ab40d..2713ac1d43 100644 --- a/test/SemaTemplate/deduction.cpp +++ b/test/SemaTemplate/deduction.cpp @@ -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 class A> @@ -266,10 +267,72 @@ int main() { } // end ns2 } +namespace multiple_deduction_different_type { + template struct X {}; + template class X, typename T, typename U, int N> + void f(X, X) {} // expected-note 2{{values of conflicting types}} + template class X, typename T, typename U, const int *N> + void g(X, X) {} // expected-note 0-2{{values of conflicting types}} + int n; + void h() { + f(X(), X()); // expected-error {{no matching function}} + f(X(), X()); // expected-error {{no matching function}} +#if __cplusplus > 201402L + g(X(), X()); // expected-error {{no matching function}} + g(X(), X()); // expected-error {{no matching function}} +#endif + } + + template class X, typename T, typename U, T N> + void x(X, int(*)[N], X) {} // expected-note 1+{{candidate}} + template class X, typename T, typename U, T N> + void x(int(*)[N], X, X) {} // expected-note 1+{{candidate}} + int arr[3]; + void y() { + x(X(), &arr, X()); + x(&arr, X(), X()); + + x(X(), &arr, X()); // expected-error {{no matching function}} + x(&arr, X(), X()); // expected-error {{no matching function}} + + x(X(), &arr, X()); + x(&arr, X(), X()); + } +} + namespace nullptr_deduction { + using nullptr_t = decltype(nullptr); + template struct X {}; template void f(X) { static_assert(!v, ""); } - void g() { f(X()); } + void g() { + f(X()); + f(X()); + } + + template class X, typename T, typename U, int *P> + void f1(X, X) {} // expected-note 2{{values of conflicting types}} + void h() { + f1(X(), X()); // expected-error {{no matching function}} + f1(X(), X()); // expected-error {{no matching function}} + } + + template class X, typename T, typename U, nullptr_t P> + void f2(X, X) {} // expected-note 2{{values of conflicting types}} + void i() { + f2(X(), X()); // expected-error {{no matching function}} + f2(X(), X()); // expected-error {{no matching function}} + } +} + +namespace member_pointer { + struct A { void f(int); }; + template struct B; + template struct C; + template struct C> { + C() { A a; T t; (a.*F)(t); } + }; + C> c; } -- 2.40.0