[clang] [clang] function template non-call partial ordering fixes (PR #106829)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 30 22:56:55 PDT 2024
https://github.com/mizvekov created https://github.com/llvm/llvm-project/pull/106829
This applies to function template non-call partial ordering the same provisional wording change applied in the call context: Don't perform the consistency check on return type and parameters which didn't have any template parameters deduced from.
Fixes regression introduced in #100692, which was reported on the PR.
>From 290661a3c6dc1c309876f5bfeaa4caf9b3da2e0c Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Fri, 30 Aug 2024 17:37:55 -0300
Subject: [PATCH] [clang] function template non-call partial ordering fixes
This applies to function template non-call partial ordering
the same provisional wording change applied in the call context:
Don't perform the consistency check on return type and parameters
which didn't have any template parameters deduced from.
Fixes regression introduced in #100692, which was reported on
the PR.
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 218 ++++++++++-------------
clang/test/SemaTemplate/GH18291.cpp | 101 ++++++++---
2 files changed, 170 insertions(+), 149 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 78c22a7dae7725..98875ea092709b 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -1187,9 +1187,8 @@ class PackDeductionScope {
template <class T>
static TemplateDeductionResult DeduceForEachType(
- Sema &S, TemplateParameterList *TemplateParams, const QualType *Params,
- unsigned NumParams, const QualType *Args, unsigned NumArgs,
- TemplateDeductionInfo &Info,
+ Sema &S, TemplateParameterList *TemplateParams, ArrayRef<QualType> Params,
+ ArrayRef<QualType> Args, TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced, bool PartialOrdering,
bool FinishingDeduction, T &&DeductFunc) {
// C++0x [temp.deduct.type]p10:
@@ -1198,7 +1197,7 @@ static TemplateDeductionResult DeduceForEachType(
// corresponding parameter type Ai of the corresponding parameter-type-list
// of A. [...]
unsigned ArgIdx = 0, ParamIdx = 0;
- for (; ParamIdx != NumParams; ++ParamIdx) {
+ for (; ParamIdx != Params.size(); ++ParamIdx) {
// Check argument types.
const PackExpansionType *Expansion
= dyn_cast<PackExpansionType>(Params[ParamIdx]);
@@ -1206,7 +1205,7 @@ static TemplateDeductionResult DeduceForEachType(
// Simple case: compare the parameter and argument types at this point.
// Make sure we have an argument.
- if (ArgIdx >= NumArgs)
+ if (ArgIdx >= Args.size())
return TemplateDeductionResult::MiscellaneousDeductionFailure;
if (isa<PackExpansionType>(Args[ArgIdx])) {
@@ -1243,8 +1242,8 @@ static TemplateDeductionResult DeduceForEachType(
// A pack scope with fixed arity is not really a pack any more, so is not
// a non-deduced context.
- if (ParamIdx + 1 == NumParams || PackScope.hasFixedArity()) {
- for (; ArgIdx < NumArgs && PackScope.hasNextElement(); ++ArgIdx) {
+ if (ParamIdx + 1 == Params.size() || PackScope.hasFixedArity()) {
+ for (; ArgIdx < Args.size() && PackScope.hasNextElement(); ++ArgIdx) {
// Deduce template arguments from the pattern.
if (TemplateDeductionResult Result = DeductFunc(
S, TemplateParams, ParamIdx, ArgIdx,
@@ -1274,7 +1273,7 @@ static TemplateDeductionResult DeduceForEachType(
// by the expansion.
std::optional<unsigned> NumExpansions = Expansion->getNumExpansions();
if (NumExpansions && !PackScope.isPartiallyExpanded()) {
- for (unsigned I = 0; I != *NumExpansions && ArgIdx < NumArgs;
+ for (unsigned I = 0; I != *NumExpansions && ArgIdx < Args.size();
++I, ++ArgIdx)
PackScope.nextPackElement();
}
@@ -1293,12 +1292,12 @@ static TemplateDeductionResult DeduceForEachType(
// During partial ordering, if Ai was originally a function parameter pack:
// - if P does not contain a function parameter type corresponding to Ai then
// Ai is ignored;
- if (PartialOrdering && ArgIdx + 1 == NumArgs &&
+ if (PartialOrdering && ArgIdx + 1 == Args.size() &&
isa<PackExpansionType>(Args[ArgIdx]))
return TemplateDeductionResult::Success;
// Make sure we don't have any extra arguments.
- if (ArgIdx < NumArgs)
+ if (ArgIdx < Args.size())
return TemplateDeductionResult::MiscellaneousDeductionFailure;
return TemplateDeductionResult::Success;
@@ -1314,12 +1313,8 @@ static TemplateDeductionResult DeduceForEachType(
///
/// \param Params The list of parameter types
///
-/// \param NumParams The number of types in \c Params
-///
/// \param Args The list of argument types
///
-/// \param NumArgs The number of types in \c Args
-///
/// \param Info information about the template argument deduction itself
///
/// \param Deduced the deduced template arguments
@@ -1341,15 +1336,14 @@ static TemplateDeductionResult DeduceForEachType(
/// "success" result means that template argument deduction has not yet failed,
/// but it may still fail, later, for other reasons.
static TemplateDeductionResult DeduceTemplateArguments(
- Sema &S, TemplateParameterList *TemplateParams, const QualType *Params,
- unsigned NumParams, const QualType *Args, unsigned NumArgs,
- TemplateDeductionInfo &Info,
+ Sema &S, TemplateParameterList *TemplateParams, ArrayRef<QualType> Params,
+ ArrayRef<QualType> Args, TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced, unsigned TDF,
bool PartialOrdering, bool *HasDeducedAnyParam,
llvm::SmallBitVector *HasDeducedParam) {
return ::DeduceForEachType(
- S, TemplateParams, Params, NumParams, Args, NumArgs, Info, Deduced,
- PartialOrdering, /*FinishingDeduction=*/false,
+ S, TemplateParams, Params, Args, Info, Deduced, PartialOrdering,
+ /*FinishingDeduction=*/false,
[&](Sema &S, TemplateParameterList *TemplateParams, int ParamIdx,
int ArgIdx, QualType P, QualType A, TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
@@ -2028,9 +2022,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
// Check parameter types.
if (auto Result = DeduceTemplateArguments(
- S, TemplateParams, FPP->param_type_begin(), FPP->getNumParams(),
- FPA->param_type_begin(), FPA->getNumParams(), Info, Deduced,
- TDF & TDF_TopLevelParameterTypeList, PartialOrdering,
+ S, TemplateParams, FPP->param_types(), FPA->param_types(), Info,
+ Deduced, TDF & TDF_TopLevelParameterTypeList, PartialOrdering,
HasDeducedAnyParam, /*HasDeducedParam=*/nullptr);
Result != TemplateDeductionResult::Success)
return Result;
@@ -5623,9 +5616,8 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
FunctionTemplateDecl *FT1,
FunctionTemplateDecl *FT2,
TemplatePartialOrderingContext TPOC,
- bool Reversed,
- const SmallVector<QualType> &Args1,
- const SmallVector<QualType> &Args2) {
+ bool Reversed, ArrayRef<QualType> Args1,
+ ArrayRef<QualType> Args2) {
assert(!Reversed || TPOC == TPOC_Call);
FunctionDecl *FD1 = FT1->getTemplatedDecl();
@@ -5635,123 +5627,95 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
assert(Proto1 && Proto2 && "Function templates must have prototypes");
TemplateParameterList *TemplateParams = FT2->getTemplateParameters();
- SmallVector<DeducedTemplateArgument, 4> Deduced;
- Deduced.resize(TemplateParams->size());
+ SmallVector<DeducedTemplateArgument, 4> Deduced(TemplateParams->size());
- // C++0x [temp.deduct.partial]p3:
- // The types used to determine the ordering depend on the context in which
- // the partial ordering is done:
TemplateDeductionInfo Info(Loc);
- switch (TPOC) {
- case TPOC_Call: {
- llvm::SmallBitVector HasDeducedParam(Args2.size());
- if (DeduceTemplateArguments(
- S, TemplateParams, Args2.data(), Args2.size(), Args1.data(),
- Args1.size(), Info, Deduced, TDF_None, /*PartialOrdering=*/true,
- /*HasDeducedAnyParam=*/nullptr,
- &HasDeducedParam) != TemplateDeductionResult::Success)
- return false;
+ if (TPOC == TPOC_Other) {
+ // We wouldn't be partial ordering these candidates if these didn't match.
+ assert(Proto2->getMethodQuals() == Proto1->getMethodQuals());
+ assert(Proto2->getRefQualifier() == Proto1->getRefQualifier());
+ assert(Proto2->isVariadic() == Proto1->isVariadic());
- SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(),
- Deduced.end());
- Sema::InstantiatingTemplate Inst(
- S, Info.getLocation(), FT2, DeducedArgs,
- Sema::CodeSynthesisContext::DeducedTemplateArgumentSubstitution, Info);
- if (Inst.isInvalid())
- return false;
-
- bool AtLeastAsSpecialized = true;
- S.runWithSufficientStackSpace(Info.getLocation(), [&] {
- AtLeastAsSpecialized =
- ::FinishTemplateArgumentDeduction(
- S, FT2, Deduced, Info,
- [&](Sema &S, FunctionTemplateDecl *FTD,
- ArrayRef<TemplateArgument> DeducedArgs) {
- return ::DeduceForEachType(
- S, TemplateParams, Args2.data(), Args2.size(), Args1.data(),
- Args1.size(), Info, Deduced,
- /*PartialOrdering=*/true, /*FinishingDeduction=*/true,
- [&](Sema &S, TemplateParameterList *, int ParamIdx,
- int ArgIdx, QualType P, QualType A,
- TemplateDeductionInfo &Info,
- SmallVectorImpl<DeducedTemplateArgument> &Deduced,
- bool) {
- // As a provisional fix for a core issue that does not
- // exist yet, only check the consistency of parameters
- // which participated in deduction. We still try to
- // substitute them though.
- return ::CheckDeductionConsistency(
- S, FTD, ArgIdx, P, A, DeducedArgs,
- HasDeducedParam[ParamIdx]);
- });
- }) == TemplateDeductionResult::Success;
- });
- if (!AtLeastAsSpecialized)
- return false;
- } break;
- case TPOC_Conversion:
- case TPOC_Other: {
- // - In the context of a call to a conversion operator, the return types
- // of the conversion function templates are used.
- // - In other contexts (14.6.6.2) the function template's function type
- // is used.
- auto [A, P, TDF] = TPOC == TPOC_Other
- ? std::make_tuple(FD1->getType(), FD2->getType(),
- TDF_AllowCompatibleFunctionType)
- : std::make_tuple(Proto1->getReturnType(),
- Proto2->getReturnType(), TDF_None);
+ assert(Args1.empty() && Args2.empty());
+ Args1 = Proto1->getParamTypes();
+ Args2 = Proto2->getParamTypes();
+ }
+ // C++26 [temp.deduct.partial]p3:
+ // The types used to determine the ordering depend on the context in which
+ // the partial ordering is done:
+ // - In the context of a function call, the types used are those function
+ // parameter types for which the function call has arguments.
+ // - In the context of a call to a conversion operator, the return types
+ // of the conversion function templates are used.
+ // - In other contexts (14.6.6.2) the function template's function type
+ // is used.
+ bool HasDeducedAnyParamFromReturnType = false;
+ if (TPOC != TPOC_Call) {
if (DeduceTemplateArgumentsByTypeMatch(
- S, TemplateParams, P, A, Info, Deduced, TDF,
+ S, TemplateParams, Proto2->getReturnType(), Proto1->getReturnType(),
+ Info, Deduced, TDF_None,
/*PartialOrdering=*/true, /*DeducedFromArrayBound=*/false,
- /*HasDeducedAnyParam=*/nullptr) != TemplateDeductionResult::Success)
+ &HasDeducedAnyParamFromReturnType) !=
+ TemplateDeductionResult::Success)
return false;
+ }
- SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(),
- Deduced.end());
- Sema::InstantiatingTemplate Inst(
- S, Info.getLocation(), FT2, DeducedArgs,
- Sema::CodeSynthesisContext::DeducedTemplateArgumentSubstitution, Info);
- if (Inst.isInvalid())
+ llvm::SmallBitVector HasDeducedParam;
+ if (TPOC != TPOC_Conversion) {
+ HasDeducedParam.resize(Args2.size());
+ if (DeduceTemplateArguments(S, TemplateParams, Args2, Args1, Info, Deduced,
+ TDF_None, /*PartialOrdering=*/true,
+ /*HasDeducedAnyParam=*/nullptr,
+ &HasDeducedParam) !=
+ TemplateDeductionResult::Success)
return false;
+ }
- bool AtLeastAsSpecialized;
- S.runWithSufficientStackSpace(Info.getLocation(), [&] {
- AtLeastAsSpecialized =
- ::FinishTemplateArgumentDeduction(
- S, FT2, Deduced, Info,
- [&](Sema &S, FunctionTemplateDecl *FTD,
- ArrayRef<TemplateArgument> DeducedArgs) {
+ SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
+ Sema::InstantiatingTemplate Inst(
+ S, Info.getLocation(), FT2, DeducedArgs,
+ Sema::CodeSynthesisContext::DeducedTemplateArgumentSubstitution, Info);
+ if (Inst.isInvalid())
+ return false;
+
+ bool AtLeastAsSpecialized;
+ S.runWithSufficientStackSpace(Info.getLocation(), [&] {
+ AtLeastAsSpecialized =
+ ::FinishTemplateArgumentDeduction(
+ S, FT2, Deduced, Info,
+ [&](Sema &S, FunctionTemplateDecl *FTD,
+ ArrayRef<TemplateArgument> DeducedArgs) {
+ // As a provisional fix for a core issue that does not
+ // exist yet, , which may be related to CWG2160, only check the
+ // consistency of parameters and return types which participated
+ // in deduction. We will still try to substitute them though.
+ if (TPOC != TPOC_Call) {
if (auto TDR = ::CheckDeductionConsistency(
S, FTD, /*ArgIdx=*/-1, Proto2->getReturnType(),
Proto1->getReturnType(), DeducedArgs,
- /*CheckConsistency=*/true);
+ /*CheckConsistency=*/HasDeducedAnyParamFromReturnType);
TDR != TemplateDeductionResult::Success)
return TDR;
+ }
- if (TPOC != TPOC_Conversion)
- return TemplateDeductionResult::Success;
-
- return ::DeduceForEachType(
- S, TemplateParams, Proto2->getParamTypes().data(),
- Proto2->getParamTypes().size(),
- Proto1->getParamTypes().data(),
- Proto1->getParamTypes().size(), Info, Deduced,
- /*PartialOrdering=*/true, /*FinishingDeduction=*/true,
- [&](Sema &S, TemplateParameterList *, int ParamIdx,
- int ArgIdx, QualType P, QualType A,
- TemplateDeductionInfo &Info,
- SmallVectorImpl<DeducedTemplateArgument> &Deduced,
- bool) {
- return ::CheckDeductionConsistency(
- S, FTD, ArgIdx, P, A, DeducedArgs,
- /*CheckConsistency=*/true);
- });
- }) == TemplateDeductionResult::Success;
- });
- if (!AtLeastAsSpecialized)
- return false;
- } break;
- }
+ if (TPOC == TPOC_Conversion)
+ return TemplateDeductionResult::Success;
+
+ return ::DeduceForEachType(
+ S, TemplateParams, Args2, Args1, Info, Deduced,
+ /*PartialOrdering=*/true, /*FinishingDeduction=*/true,
+ [&](Sema &S, TemplateParameterList *, int ParamIdx,
+ int ArgIdx, QualType P, QualType A,
+ TemplateDeductionInfo &Info,
+ SmallVectorImpl<DeducedTemplateArgument> &Deduced, bool) {
+ return ::CheckDeductionConsistency(
+ S, FTD, ArgIdx, P, A, DeducedArgs,
+ /*CheckConsistency=*/HasDeducedParam[ParamIdx]);
+ });
+ }) == TemplateDeductionResult::Success;
+ });
+ if (!AtLeastAsSpecialized)
+ return false;
// C++0x [temp.deduct.partial]p11:
// In most cases, all template parameters must have values in order for
diff --git a/clang/test/SemaTemplate/GH18291.cpp b/clang/test/SemaTemplate/GH18291.cpp
index e77a29c8e2c244..ca1e69e4ca3f53 100644
--- a/clang/test/SemaTemplate/GH18291.cpp
+++ b/clang/test/SemaTemplate/GH18291.cpp
@@ -1,32 +1,89 @@
// RUN: %clang_cc1 -std=c++23 -verify %s
namespace t1 {
-template<bool> struct enable_if { typedef void type; };
-template <class T> class Foo {};
-template <class X> constexpr bool check() { return true; }
-template <class X, class Enable = void> struct Bar {};
+ template<bool> struct enable_if { typedef void type; };
+ template <class T> class Foo {};
+ template <class X> constexpr bool check() { return true; }
+ template <class X, class Enable = void> struct Bar {};
-template<class X> void func(Bar<X, typename enable_if<check<X>()>::type>) {}
-// expected-note at -1 {{candidate function}}
+ namespace param {
+ template<class X> void func(Bar<X, typename enable_if<check<X>()>::type>) {}
+ // expected-note at -1 2{{candidate function}}
+ template<class T> void func(Bar<Foo<T>>) {}
+ // expected-note at -1 2{{candidate function}}
-template<class T> void func(Bar<Foo<T>>) {}
-// expected-note at -1 {{candidate function}}
+ void g() {
+ func(Bar<Foo<int>>()); // expected-error {{call to 'func' is ambiguous}}
+ void (*ptr)(Bar<Foo<int>>){func};
+ // expected-error at -1 {{address of overloaded function 'func' is ambiguous}}
+ }
+ } // namespace param
+ namespace ret {
+ template<class X> Bar<X, typename enable_if<check<X>()>::type> func();
+ // expected-note at -1 {{candidate function}}
+ template<class T> Bar<Foo<T>> func();
+ // expected-note at -1 {{candidate function}}
-void g() {
- func(Bar<Foo<int>>()); // expected-error {{call to 'func' is ambiguous}}
-}
+ void g() {
+ Bar<Foo<int>> (*ptr)(){func};
+ // expected-error at -1 {{address of overloaded function 'func' is ambiguous}}
+ }
+ } // namespace ret
+ namespace conv {
+ struct A {
+ template<class X> operator Bar<X, typename enable_if<check<X>()>::type>();
+ // expected-note at -1 {{candidate function}}
+ template<class T> operator Bar<Foo<T>>();
+ // expected-note at -1 {{candidate function}}
+ };
+ void g() {
+ Bar<Foo<int>> x = A();
+ // expected-error at -1 {{conversion from 'A' to 'Bar<Foo<int>>' is ambiguous}}
+ }
+ } // namespace conv
} // namespace t1
namespace t2 {
-template <bool> struct enable_if;
-template <> struct enable_if<true> {
- typedef int type;
-};
-struct pair {
- template <int = 0> pair(int);
- template <class _U2, enable_if<__is_constructible(int &, _U2)>::type = 0>
- pair(_U2 &&);
-};
-int test_test_i;
-void test() { pair{test_test_i}; }
+ template <bool> struct enable_if;
+ template <> struct enable_if<true> {
+ typedef int type;
+ };
+ struct pair {
+ template <int = 0> pair(int);
+ template <class _U2, enable_if<__is_constructible(int &, _U2)>::type = 0>
+ pair(_U2 &&);
+ };
+ int test_test_i;
+ void test() { pair{test_test_i}; }
} // namespace t2
+
+namespace t3 {
+ template <class _Tp> void to_address(_Tp);
+ template <class _Pointer> auto to_address(_Pointer __p) -> decltype(__p);
+
+ template <class _CharT> struct basic_string_view {
+ basic_string_view(_CharT);
+
+ template <class _It> requires requires(_It __i) { to_address(__i); }
+ basic_string_view(_It);
+ };
+ void operatorsv() { basic_string_view(0); }
+} // namespace t3
+
+namespace func_pointer {
+ template <class> struct __promote {
+ using type = float;
+ };
+ template <class> class complex {};
+
+ namespace ret {
+ template <class _Tp> complex<_Tp> pow(const complex<_Tp> &) {};
+ template <class _Tp> complex<typename __promote<_Tp>::type> pow(_Tp) = delete;
+ complex<float> (*ptr)(const complex<float> &){pow};
+ } // namespace ret
+ namespace param {
+ template <class _Tp> void pow(const complex<_Tp> &, complex<_Tp>) {};
+ template <class _Tp> void pow(_Tp, complex<typename __promote<_Tp>::type>) = delete;
+ void (*ptr)(const complex<float> &, complex<float>){pow};
+ } // namespace param
+} // namespace t3
More information about the cfe-commits
mailing list