diff options
Diffstat (limited to 'clang')
-rw-r--r-- | clang/include/clang/AST/TemplateBase.h | 4 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 6 | ||||
-rw-r--r-- | clang/lib/AST/TemplateBase.cpp | 25 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 20 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateDeduction.cpp | 69 | ||||
-rw-r--r-- | clang/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp | 5 | ||||
-rw-r--r-- | clang/test/SemaTemplate/deduction.cpp | 65 |
7 files changed, 161 insertions, 33 deletions
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h index bccb5787925..4f268a646c2 100644 --- a/clang/include/clang/AST/TemplateBase.h +++ b/clang/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/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7a6598baab1..a9579245879 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/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/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp index b1c88f93b6a..099f939c7a7 100644 --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -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()); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 5252e6a26c9..626a6ca9815 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -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; } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 9ec5bb9939f..9d970cfe59a 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/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<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 diff --git a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp index 9fd3df59d10..f7103a33cc7 100644 --- a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp +++ b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/p9.cpp @@ -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}} } } diff --git a/clang/test/SemaTemplate/deduction.cpp b/clang/test/SemaTemplate/deduction.cpp index 7c7b0ab40da..2713ac1d43e 100644 --- a/clang/test/SemaTemplate/deduction.cpp +++ b/clang/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<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; } |