https://github.com/HoBoIs updated https://github.com/llvm/llvm-project/pull/83279
From 68200ecf3267d1b3940fa73c25c50ee706932a98 Mon Sep 17 00:00:00 2001 From: Botond Istvan Horvath <horvath.botond.ist...@gmail.com> Date: Wed, 28 Feb 2024 13:09:15 +0100 Subject: [PATCH 1/8] Bugfix for choosing the more specialized overload There was a bug in clang where it couldn't choose which overload candidate is more specialized if it was comparing a member-function to a non-member function. Previously, this was detected as an ambigouity, now clang chooses correctly. This patch fixes the bug by fully implementing CWG2445 and moving the template transformation described in [temp.func.order] paragraph 3 from isAtLeastAsSpecializedAs to Sema::getMoreSpecializedTemplate so we have the transformed parameter list during the whole comperrassion. Also, to be able to add the correct type for the implicit object parameter Sema::getMoreSpecializedTemplate has new parameters for the object type. --- clang/include/clang/Sema/Sema.h | 4 +- clang/lib/Sema/SemaOverload.cpp | 12 +- clang/lib/Sema/SemaTemplateDeduction.cpp | 263 +++++++++++++++-------- 3 files changed, 186 insertions(+), 93 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ef4b93fac95ce5..1a2a3a6bebd95e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9463,7 +9463,9 @@ class Sema final { FunctionTemplateDecl *getMoreSpecializedTemplate( FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc, TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1, - unsigned NumCallArguments2, bool Reversed = false); + unsigned NumCallArguments2, QualType ObjType1 = {}, + QualType ObjType2 = {}, bool Reversed = false); + UnresolvedSetIterator getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd, TemplateSpecCandidateSet &FailedCandidates, diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 7d38043890ca20..60138236abf1d8 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -10526,14 +10526,24 @@ bool clang::isBetterOverloadCandidate( // according to the partial ordering rules described in 14.5.5.2, or, // if not that, if (Cand1IsSpecialization && Cand2IsSpecialization) { + const auto *ObjContext1 = + dyn_cast<CXXRecordDecl>(Cand1.FoundDecl->getDeclContext()); + const auto *ObjContext2 = + dyn_cast<CXXRecordDecl>(Cand2.FoundDecl->getDeclContext()); if (FunctionTemplateDecl *BetterTemplate = S.getMoreSpecializedTemplate( Cand1.Function->getPrimaryTemplate(), Cand2.Function->getPrimaryTemplate(), Loc, isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion : TPOC_Call, Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments, - Cand1.isReversed() ^ Cand2.isReversed())) + ObjContext1 ? QualType(ObjContext1->getTypeForDecl(), 0) + : QualType{}, + ObjContext2 ? QualType(ObjContext2->getTypeForDecl(), 0) + : QualType{}, + Cand1.isReversed() ^ Cand2.isReversed())) { return BetterTemplate == Cand1.Function->getPrimaryTemplate(); + } + } // -— F1 and F2 are non-template functions with the same diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 563491f76f5478..2af3c29ae1f1c4 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5333,11 +5333,31 @@ bool Sema::CheckIfFunctionSpecializationIsImmediate(FunctionDecl *FD, return false; } +static QualType GetImplicitObjectParameterTypeCXX20(ASTContext &Context, + const CXXMethodDecl *Method, + QualType rawType, + bool isOtherRvr) { + // C++20 [temp.func.order]p3.1, p3.2: + //- The type X(M ) is “rvalue reference to cv A” if the optional ref-qualifier + // of M is && or if M has no ref-qualifier and the positionally-corresponding + // parameter of the other transformed template has rvalue reference type; + // if this determination depends recursively upon whether X(M ) is an rvalue + // reference type, it is not considered to have rvalue reference type. + //- Otherwise, X(M ) is “lvalue reference to cv A”. + assert(Method && !Method->isExplicitObjectMemberFunction() && + "expected a member function with no explicit object parameter"); + + rawType = Context.getQualifiedType(rawType, Method->getMethodQualifiers()); + if (Method->getRefQualifier() == RQ_RValue || + (isOtherRvr && Method->getRefQualifier() == RQ_None)) + return Context.getRValueReferenceType(rawType); + return Context.getLValueReferenceType(rawType); +} + /// If this is a non-static member function, -static void -AddImplicitObjectParameterType(ASTContext &Context, - CXXMethodDecl *Method, - SmallVectorImpl<QualType> &ArgTypes) { +static QualType GetImplicitObjectParameterType(ASTContext &Context, + const CXXMethodDecl *Method, + QualType rawType) { // C++11 [temp.func.order]p3: // [...] The new parameter is of type "reference to cv A," where cv are // the cv-qualifiers of the function template (if any) and A is @@ -5347,24 +5367,21 @@ AddImplicitObjectParameterType(ASTContext &Context, // reference type based on [over.match.funcs]p4. assert(Method && Method->isImplicitObjectMemberFunction() && "expected an implicit objet function"); - QualType ArgTy = Context.getTypeDeclType(Method->getParent()); - ArgTy = Context.getQualifiedType(ArgTy, Method->getMethodQualifiers()); + rawType = Context.getQualifiedType(rawType, Method->getMethodQualifiers()); if (Method->getRefQualifier() == RQ_RValue) - ArgTy = Context.getRValueReferenceType(ArgTy); - else - ArgTy = Context.getLValueReferenceType(ArgTy); - ArgTypes.push_back(ArgTy); + return Context.getRValueReferenceType(rawType); + return Context.getLValueReferenceType(rawType); } /// Determine whether the function template \p FT1 is at least as /// specialized as \p FT2. -static bool isAtLeastAsSpecializedAs(Sema &S, - SourceLocation Loc, - FunctionTemplateDecl *FT1, - FunctionTemplateDecl *FT2, +static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc, + const FunctionTemplateDecl *FT1, + const FunctionTemplateDecl *FT2, TemplatePartialOrderingContext TPOC, - unsigned NumCallArguments1, - bool Reversed) { + bool Reversed, + const SmallVector<QualType, 4> &Args1, + const SmallVector<QualType, 4> &Args2) { assert(!Reversed || TPOC == TPOC_Call); FunctionDecl *FD1 = FT1->getTemplatedDecl(); @@ -5381,66 +5398,8 @@ static bool isAtLeastAsSpecializedAs(Sema &S, // The types used to determine the ordering depend on the context in which // the partial ordering is done: TemplateDeductionInfo Info(Loc); - SmallVector<QualType, 4> Args2; switch (TPOC) { - case TPOC_Call: { - // - In the context of a function call, the function parameter types are - // used. - CXXMethodDecl *Method1 = dyn_cast<CXXMethodDecl>(FD1); - CXXMethodDecl *Method2 = dyn_cast<CXXMethodDecl>(FD2); - - // C++11 [temp.func.order]p3: - // [...] If only one of the function templates is a non-static - // member, that function template is considered to have a new - // first parameter inserted in its function parameter list. The - // new parameter is of type "reference to cv A," where cv are - // the cv-qualifiers of the function template (if any) and A is - // the class of which the function template is a member. - // - // Note that we interpret this to mean "if one of the function - // templates is a non-static member and the other is a non-member"; - // otherwise, the ordering rules for static functions against non-static - // functions don't make any sense. - // - // C++98/03 doesn't have this provision but we've extended DR532 to cover - // it as wording was broken prior to it. - SmallVector<QualType, 4> Args1; - - unsigned NumComparedArguments = NumCallArguments1; - - if (!Method2 && Method1 && Method1->isImplicitObjectMemberFunction()) { - // Compare 'this' from Method1 against first parameter from Method2. - AddImplicitObjectParameterType(S.Context, Method1, Args1); - ++NumComparedArguments; - } else if (!Method1 && Method2 && - Method2->isImplicitObjectMemberFunction()) { - // Compare 'this' from Method2 against first parameter from Method1. - AddImplicitObjectParameterType(S.Context, Method2, Args2); - } else if (Method1 && Method2 && Reversed && - Method1->isImplicitObjectMemberFunction() && - Method2->isImplicitObjectMemberFunction()) { - // Compare 'this' from Method1 against second parameter from Method2 - // and 'this' from Method2 against second parameter from Method1. - AddImplicitObjectParameterType(S.Context, Method1, Args1); - AddImplicitObjectParameterType(S.Context, Method2, Args2); - ++NumComparedArguments; - } - - Args1.insert(Args1.end(), Proto1->param_type_begin(), - Proto1->param_type_end()); - Args2.insert(Args2.end(), Proto2->param_type_begin(), - Proto2->param_type_end()); - - // C++ [temp.func.order]p5: - // The presence of unused ellipsis and default arguments has no effect on - // the partial ordering of function templates. - if (Args1.size() > NumComparedArguments) - Args1.resize(NumComparedArguments); - if (Args2.size() > NumComparedArguments) - Args2.resize(NumComparedArguments); - if (Reversed) - std::reverse(Args2.begin(), Args2.end()); - + case TPOC_Call: if (DeduceTemplateArguments(S, TemplateParams, Args2.data(), Args2.size(), Args1.data(), Args1.size(), Info, Deduced, TDF_None, /*PartialOrdering=*/true) != @@ -5448,7 +5407,6 @@ static bool isAtLeastAsSpecializedAs(Sema &S, return false; break; - } case TPOC_Conversion: // - In the context of a call to a conversion operator, the return types @@ -5539,6 +5497,14 @@ static bool isAtLeastAsSpecializedAs(Sema &S, /// \param NumCallArguments2 The number of arguments in the call to FT2, used /// only when \c TPOC is \c TPOC_Call. /// +/// \param RawObjType1 The type of the object parameter of FT1 if a member +/// function only used if \c TPOC is \c TPOC_Call and FT1 is a Function +/// template from a member function +/// +/// \param RawObjType2 The type of the object parameter of FT2 if a member +/// function only used if \c TPOC is \c TPOC_Call and FT2 is a Function +/// template from a member function +/// /// \param Reversed If \c true, exactly one of FT1 and FT2 is an overload /// candidate with a reversed parameter order. In this case, the corresponding /// P/A pairs between FT1 and FT2 are reversed. @@ -5548,13 +5514,113 @@ static bool isAtLeastAsSpecializedAs(Sema &S, FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc, TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1, - unsigned NumCallArguments2, bool Reversed) { + unsigned NumCallArguments2, QualType RawObjType1, QualType RawObjType2, + bool Reversed) { + SmallVector<QualType, 4> Args1; + SmallVector<QualType, 4> Args2; + const FunctionDecl *FD1 = FT1->getTemplatedDecl(); + const FunctionDecl *FD2 = FT2->getTemplatedDecl(); + bool shouldConvert1 = false; + bool shouldConvert2 = false; + QualType ObjType1; + QualType ObjType2; + if (TPOC == TPOC_Call) { + const FunctionProtoType *Proto1 = + FD1->getType()->getAs<FunctionProtoType>(); + const FunctionProtoType *Proto2 = + FD2->getType()->getAs<FunctionProtoType>(); - bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC, - NumCallArguments1, Reversed); - bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC, - NumCallArguments2, Reversed); + // - In the context of a function call, the function parameter types are + // used. + const CXXMethodDecl *Method1 = dyn_cast<CXXMethodDecl>(FD1); + const CXXMethodDecl *Method2 = dyn_cast<CXXMethodDecl>(FD2); + + if (getLangOpts().CPlusPlus20) { + // C++20 [temp.func.order]p3 + // [...] Each function template M that is a member function is + // considered to have a new first parameter of type + // X(M), described below, inserted in its function parameter list. + // + // Note that we interpret "that is a member function" as + // "that is a member function with no expicit object argument". + // Otherwise the ordering rules for methods with expicit objet arguments + // against anything else make no sense. + shouldConvert1 = Method1 && !Method1->isExplicitObjectMemberFunction(); + shouldConvert2 = Method2 && !Method2->isExplicitObjectMemberFunction(); + if (shouldConvert1) { + bool isR2 = + Method2 && !Method2->isExplicitObjectMemberFunction() + ? Method2->getRefQualifier() == RQ_RValue + : Proto2->param_type_begin()[0]->isRValueReferenceType(); + // Compare 'this' from Method1 against first parameter from Method2. + ObjType1 = GetImplicitObjectParameterTypeCXX20(this->Context, Method1, + RawObjType1, isR2); + Args1.push_back(ObjType1); + } + if (shouldConvert2) { + bool isR1 = + Method1 && !Method1->isExplicitObjectMemberFunction() + ? Method1->getRefQualifier() == RQ_RValue + : Proto1->param_type_begin()[0]->isRValueReferenceType(); + // Compare 'this' from Method2 against first parameter from Method1. + ObjType2 = GetImplicitObjectParameterTypeCXX20(this->Context, Method2, + RawObjType2, isR1); + Args2.push_back(ObjType2); + } + } else { + // C++11 [temp.func.order]p3: + // [...] If only one of the function templates is a non-static + // member, that function template is considered to have a new + // first parameter inserted in its function parameter list. The + // new parameter is of type "reference to cv A," where cv are + // the cv-qualifiers of the function template (if any) and A is + // the class of which the function template is a member. + // + // Note that we interpret this to mean "if one of the function + // templates is a non-static member and the other is a non-member"; + // otherwise, the ordering rules for static functions against non-static + // functions don't make any sense. + // + // C++98/03 doesn't have this provision but we've extended DR532 to cover + // it as wording was broken prior to it. + shouldConvert1 = + !Method2 && Method1 && Method1->isImplicitObjectMemberFunction(); + shouldConvert2 = + !Method1 && Method2 && Method2->isImplicitObjectMemberFunction(); + if (shouldConvert1) { + // Compare 'this' from Method1 against first parameter from Method2. + ObjType1 = + GetImplicitObjectParameterType(this->Context, Method1, RawObjType1); + Args1.push_back(ObjType1); + } + if (shouldConvert2) { + // Compare 'this' from Method2 against first parameter from Method1. + ObjType2 = + GetImplicitObjectParameterType(this->Context, Method2, RawObjType2); + Args2.push_back(ObjType2); + } + } + unsigned NumComparedArguments = NumCallArguments1 + shouldConvert1; + + Args1.insert(Args1.end(), Proto1->param_type_begin(), + Proto1->param_type_end()); + Args2.insert(Args2.end(), Proto2->param_type_begin(), + Proto2->param_type_end()); + // C++ [temp.func.order]p5: + // The presence of unused ellipsis and default arguments has no effect on + // the partial ordering of function templates. + if (Args1.size() > NumComparedArguments) + Args1.resize(NumComparedArguments); + if (Args2.size() > NumComparedArguments) + Args2.resize(NumComparedArguments); + if (Reversed) + std::reverse(Args2.begin(), Args2.end()); + } + bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC, Reversed, + Args1, Args2); + bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC, Reversed, + Args2, Args1); // C++ [temp.deduct.partial]p10: // F is more specialized than G if F is at least as specialized as G and G // is not at least as specialized as F. @@ -5568,12 +5634,28 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( // ... and if G has a trailing function parameter pack for which F does not // have a corresponding parameter, and if F does not have a trailing // function parameter pack, then F is more specialized than G. - FunctionDecl *FD1 = FT1->getTemplatedDecl(); - FunctionDecl *FD2 = FT2->getTemplatedDecl(); - unsigned NumParams1 = FD1->getNumParams(); - unsigned NumParams2 = FD2->getNumParams(); - bool Variadic1 = NumParams1 && FD1->parameters().back()->isParameterPack(); - bool Variadic2 = NumParams2 && FD2->parameters().back()->isParameterPack(); + + SmallVector<QualType> param1; + param1.reserve(FD1->param_size() + shouldConvert1); + if (shouldConvert1) + param1.push_back(ObjType1); + for (const auto &x : FD1->parameters()) + param1.push_back(x->getType()); + + SmallVector<QualType> param2; + param2.reserve(FD2->param_size() + shouldConvert2); + if (shouldConvert2) + param2.push_back(ObjType2); + for (const auto &x : FD2->parameters()) + param2.push_back(x->getType()); + + unsigned NumParams1 = param1.size(); + unsigned NumParams2 = param2.size(); + + bool Variadic1 = + FD1->param_size() && FD1->parameters().back()->isParameterPack(); + bool Variadic2 = + FD2->param_size() && FD2->parameters().back()->isParameterPack(); if (Variadic1 != Variadic2) { if (Variadic1 && NumParams1 > NumParams2) return FT2; @@ -5584,8 +5666,8 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( // This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that // there is no wording or even resolution for this issue. for (int i = 0, e = std::min(NumParams1, NumParams2); i < e; ++i) { - QualType T1 = FD1->getParamDecl(i)->getType().getCanonicalType(); - QualType T2 = FD2->getParamDecl(i)->getType().getCanonicalType(); + QualType T1 = param1[i].getCanonicalType(); + QualType T2 = param2[i].getCanonicalType(); auto *TST1 = dyn_cast<TemplateSpecializationType>(T1); auto *TST2 = dyn_cast<TemplateSpecializationType>(T2); if (!TST1 || !TST2) @@ -5644,8 +5726,7 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( // Any top-level cv-qualifiers modifying a parameter type are deleted when // forming the function type. for (unsigned i = 0; i < NumParams1; ++i) - if (!Context.hasSameUnqualifiedType(FD1->getParamDecl(i)->getType(), - FD2->getParamDecl(i)->getType())) + if (!Context.hasSameUnqualifiedType(param1[i], param2[i])) return nullptr; // C++20 [temp.func.order]p6.3: From 3cb51452a25227afb0750ae65fcca9bf281433ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Botond=20Istv=C3=A1n=20Horv=C3=A1th?= <56926027+hob...@users.noreply.github.com> Date: Wed, 28 Feb 2024 16:50:30 +0100 Subject: [PATCH 2/8] Formatting SemaOverload.cpp --- clang/lib/Sema/SemaOverload.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 60138236abf1d8..74d714ad308af1 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -10543,7 +10543,6 @@ bool clang::isBetterOverloadCandidate( Cand1.isReversed() ^ Cand2.isReversed())) { return BetterTemplate == Cand1.Function->getPrimaryTemplate(); } - } // -— F1 and F2 are non-template functions with the same From 54e7fb1ae193196538423a0c1cbb62fc518f77ef Mon Sep 17 00:00:00 2001 From: Botond Istvan Horvath <horvath.botond.ist...@gmail.com> Date: Wed, 28 Feb 2024 18:46:07 +0100 Subject: [PATCH 3/8] Unified the branches for newer and older standards --- clang/lib/Sema/SemaTemplateDeduction.cpp | 97 +++++++++--------------- 1 file changed, 37 insertions(+), 60 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 2af3c29ae1f1c4..f7526933be12f4 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5333,10 +5333,10 @@ bool Sema::CheckIfFunctionSpecializationIsImmediate(FunctionDecl *FD, return false; } -static QualType GetImplicitObjectParameterTypeCXX20(ASTContext &Context, - const CXXMethodDecl *Method, - QualType rawType, - bool isOtherRvr) { +static QualType GetImplicitObjectParameterType(ASTContext &Context, + const CXXMethodDecl *Method, + QualType RawType, + bool IsOtherRvr) { // C++20 [temp.func.order]p3.1, p3.2: //- The type X(M ) is “rvalue reference to cv A” if the optional ref-qualifier // of M is && or if M has no ref-qualifier and the positionally-corresponding @@ -5344,20 +5344,7 @@ static QualType GetImplicitObjectParameterTypeCXX20(ASTContext &Context, // if this determination depends recursively upon whether X(M ) is an rvalue // reference type, it is not considered to have rvalue reference type. //- Otherwise, X(M ) is “lvalue reference to cv A”. - assert(Method && !Method->isExplicitObjectMemberFunction() && - "expected a member function with no explicit object parameter"); - - rawType = Context.getQualifiedType(rawType, Method->getMethodQualifiers()); - if (Method->getRefQualifier() == RQ_RValue || - (isOtherRvr && Method->getRefQualifier() == RQ_None)) - return Context.getRValueReferenceType(rawType); - return Context.getLValueReferenceType(rawType); -} - -/// If this is a non-static member function, -static QualType GetImplicitObjectParameterType(ASTContext &Context, - const CXXMethodDecl *Method, - QualType rawType) { + // // C++11 [temp.func.order]p3: // [...] The new parameter is of type "reference to cv A," where cv are // the cv-qualifiers of the function template (if any) and A is @@ -5365,12 +5352,15 @@ static QualType GetImplicitObjectParameterType(ASTContext &Context, // // The standard doesn't say explicitly, but we pick the appropriate kind of // reference type based on [over.match.funcs]p4. - assert(Method && Method->isImplicitObjectMemberFunction() && - "expected an implicit objet function"); - rawType = Context.getQualifiedType(rawType, Method->getMethodQualifiers()); - if (Method->getRefQualifier() == RQ_RValue) - return Context.getRValueReferenceType(rawType); - return Context.getLValueReferenceType(rawType); + + assert(Method && !Method->isExplicitObjectMemberFunction() && + "expected a member function with no explicit object parameter"); + + RawType = Context.getQualifiedType(RawType, Method->getMethodQualifiers()); + if (Method->getRefQualifier() == RQ_RValue || + (IsOtherRvr && Method->getRefQualifier() == RQ_None)) + return Context.getRValueReferenceType(RawType); + return Context.getLValueReferenceType(RawType); } /// Determine whether the function template \p FT1 is at least as @@ -5547,34 +5537,11 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( // against anything else make no sense. shouldConvert1 = Method1 && !Method1->isExplicitObjectMemberFunction(); shouldConvert2 = Method2 && !Method2->isExplicitObjectMemberFunction(); - if (shouldConvert1) { - bool isR2 = - Method2 && !Method2->isExplicitObjectMemberFunction() - ? Method2->getRefQualifier() == RQ_RValue - : Proto2->param_type_begin()[0]->isRValueReferenceType(); - // Compare 'this' from Method1 against first parameter from Method2. - ObjType1 = GetImplicitObjectParameterTypeCXX20(this->Context, Method1, - RawObjType1, isR2); - Args1.push_back(ObjType1); - } - if (shouldConvert2) { - bool isR1 = - Method1 && !Method1->isExplicitObjectMemberFunction() - ? Method1->getRefQualifier() == RQ_RValue - : Proto1->param_type_begin()[0]->isRValueReferenceType(); - // Compare 'this' from Method2 against first parameter from Method1. - ObjType2 = GetImplicitObjectParameterTypeCXX20(this->Context, Method2, - RawObjType2, isR1); - Args2.push_back(ObjType2); - } } else { // C++11 [temp.func.order]p3: // [...] If only one of the function templates is a non-static // member, that function template is considered to have a new - // first parameter inserted in its function parameter list. The - // new parameter is of type "reference to cv A," where cv are - // the cv-qualifiers of the function template (if any) and A is - // the class of which the function template is a member. + // first parameter inserted in its function parameter list. // // Note that we interpret this to mean "if one of the function // templates is a non-static member and the other is a non-member"; @@ -5587,18 +5554,28 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( !Method2 && Method1 && Method1->isImplicitObjectMemberFunction(); shouldConvert2 = !Method1 && Method2 && Method2->isImplicitObjectMemberFunction(); - if (shouldConvert1) { - // Compare 'this' from Method1 against first parameter from Method2. - ObjType1 = - GetImplicitObjectParameterType(this->Context, Method1, RawObjType1); - Args1.push_back(ObjType1); - } - if (shouldConvert2) { - // Compare 'this' from Method2 against first parameter from Method1. - ObjType2 = - GetImplicitObjectParameterType(this->Context, Method2, RawObjType2); - Args2.push_back(ObjType2); - } + } + if (shouldConvert1) { + bool isR2 = + getLangOpts().CPlusPlus20 && + (shouldConvert1 + ? Method2->getRefQualifier() == RQ_RValue + : Proto2->param_type_begin()[0]->isRValueReferenceType()); + // Compare 'this' from Method1 against first parameter from Method2. + ObjType1 = GetImplicitObjectParameterType(this->Context, Method1, + RawObjType1, isR2); + Args1.push_back(ObjType1); + } + if (shouldConvert2) { + bool isR1 = + getLangOpts().CPlusPlus20 && + (shouldConvert2 + ? Method1->getRefQualifier() == RQ_RValue + : Proto1->param_type_begin()[0]->isRValueReferenceType()); + // Compare 'this' from Method2 against first parameter from Method1. + ObjType2 = GetImplicitObjectParameterType(this->Context, Method2, + RawObjType2, isR1); + Args2.push_back(ObjType2); } unsigned NumComparedArguments = NumCallArguments1 + shouldConvert1; From e377e6e320d5c280e9364bbd14be456aab57ea46 Mon Sep 17 00:00:00 2001 From: Botond Istvan Horvath <horvath.botond.ist...@gmail.com> Date: Thu, 29 Feb 2024 15:35:12 +0100 Subject: [PATCH 4/8] minor fix + tests + release notes --- clang/docs/ReleaseNotes.rst | 6 ++++ clang/lib/Sema/SemaTemplateDeduction.cpp | 12 +++---- clang/test/SemaCXX/overload-template.cpp | 25 +++++++++++++++ clang/test/SemaCXX/overloaded-operator.cpp | 37 ++++++++++++++++++++++ 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 7e16b9f0c67dbd..6a1b93db9f6486 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -290,6 +290,12 @@ Bug Fixes to C++ Support lookup searches the bases of an incomplete class. - Fix a crash when an unresolved overload set is encountered on the RHS of a ``.*`` operator. (`#53815 <https://github.com/llvm/llvm-project/issues/53815>`_) +- Fix a bug where overload resolution falsly reported an ambiguity when it was compearing + a member-function against a non member function or a member-function with an + explicit object parameter against a member function with no explicit object parameter + when one of the function had more specialized templates. + Fixes (`#82509 <https://github.com/llvm/llvm-project/issues/82509>`_) + and (`#74494 <https://github.com/llvm/llvm-project/issues/74494>`_) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index f7526933be12f4..04e46ab69876f6 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5556,25 +5556,25 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( !Method1 && Method2 && Method2->isImplicitObjectMemberFunction(); } if (shouldConvert1) { - bool isR2 = + bool isRValRef2 = getLangOpts().CPlusPlus20 && - (shouldConvert1 + (shouldConvert2 ? Method2->getRefQualifier() == RQ_RValue : Proto2->param_type_begin()[0]->isRValueReferenceType()); // Compare 'this' from Method1 against first parameter from Method2. ObjType1 = GetImplicitObjectParameterType(this->Context, Method1, - RawObjType1, isR2); + RawObjType1, isRValRef2); Args1.push_back(ObjType1); } if (shouldConvert2) { - bool isR1 = + bool isRValRef1 = getLangOpts().CPlusPlus20 && - (shouldConvert2 + (shouldConvert1 ? Method1->getRefQualifier() == RQ_RValue : Proto1->param_type_begin()[0]->isRValueReferenceType()); // Compare 'this' from Method2 against first parameter from Method1. ObjType2 = GetImplicitObjectParameterType(this->Context, Method2, - RawObjType2, isR1); + RawObjType2, isRValRef1); Args2.push_back(ObjType2); } unsigned NumComparedArguments = NumCallArguments1 + shouldConvert1; diff --git a/clang/test/SemaCXX/overload-template.cpp b/clang/test/SemaCXX/overload-template.cpp index 0a23788ef3da6a..0fe13c479cce22 100644 --- a/clang/test/SemaCXX/overload-template.cpp +++ b/clang/test/SemaCXX/overload-template.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++23 -verify -fsyntax-only %s enum copy_traits { movable = 1 }; @@ -33,3 +34,27 @@ void ReproducesBugSimply() { InsertRow(3, B{}); // expected-error {{no matching function for call to 'InsertRow'}} } +#if __cplusplus >= 202302L +namespace overloadCheck{ + template<typename T> + concept AlwaysTrue = true; + + struct S { + int f(AlwaysTrue auto) { return 1; } + void f(this S&&, auto) {} + + void g(auto) {} + int g(this S&&,AlwaysTrue auto) {return 1;} + + int h(AlwaysTrue auto) { return 1; } //expected-note {{previous definition is here}} + int h(this S&&,AlwaysTrue auto) { // expected-error {{class member cannot be redeclared}} + return 1; + } + }; + + int main() { + int x = S{}.f(0); + int y = S{}.g(0); + } +} +#endif diff --git a/clang/test/SemaCXX/overloaded-operator.cpp b/clang/test/SemaCXX/overloaded-operator.cpp index 887848c29b83c5..49311625d7ab2d 100644 --- a/clang/test/SemaCXX/overloaded-operator.cpp +++ b/clang/test/SemaCXX/overloaded-operator.cpp @@ -645,3 +645,40 @@ class b { } + +#if __cplusplus >= 202002L +namespace nw{ + template<class T> + concept AlwaysTrue=true; + + struct S{ + template<class T> + void operator+(const T&)const{} + + template<AlwaysTrue T> + int operator-(const T&)const{return 0;} + + template<AlwaysTrue T> + int operator*(const T&)const{ // expected-note {{candidate function}} + return 0; + } + }; + + template<AlwaysTrue T> + int operator+(const S&, const T&){return 0;} + + template<class T> + void operator-(const S&, const T&){} + + template<AlwaysTrue T> + int operator*(const S&, const T&){ // expected-note {{candidate function}} + return 0; + } + + void foo(){ + int a = S{} + 1; + int b = S{} - 1; + int c = S{} * 1; // expected-error {{use of overloaded operator '*' is ambiguous (with operand types 'S' and 'int')}} + } +} +#endif From 74e69bf423645f98e342710f8e251365cdb7cd3b Mon Sep 17 00:00:00 2001 From: Botond Istvan Horvath <horvath.botond.ist...@gmail.com> Date: Fri, 1 Mar 2024 14:40:35 +0100 Subject: [PATCH 5/8] Set DR CWG2445 status --- clang/www/cxx_dr_status.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 8b638e06f4aab6..5973d825433463 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -14478,7 +14478,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/2445.html">2445</a></td> <td>C++20</td> <td>Partial ordering with rewritten candidates</td> - <td class="unknown" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 18</td> </tr> <tr id="2446"> <td><a href="https://cplusplus.github.io/CWG/issues/2446.html">2446</a></td> From d8c5aa26f7213e98f8030e1613efd5136b44b1a5 Mon Sep 17 00:00:00 2001 From: Botond Istvan Horvath <horvath.botond.ist...@gmail.com> Date: Fri, 1 Mar 2024 16:38:24 +0100 Subject: [PATCH 6/8] Added test to dr24xx.cpp, auto-generated cxx_dr_status.html --- clang/test/CXX/drs/dr24xx.cpp | 32 ++++++++++++++++++++++++++++++++ clang/www/cxx_dr_status.html | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/clang/test/CXX/drs/dr24xx.cpp b/clang/test/CXX/drs/dr24xx.cpp index ae8dda3351f48e..e756f43985517b 100644 --- a/clang/test/CXX/drs/dr24xx.cpp +++ b/clang/test/CXX/drs/dr24xx.cpp @@ -68,3 +68,35 @@ template<A> struct X {}; X<1> x; #endif } +namespace dr2445 { // dr2445: 19 +#if __cplusplus >= 202002L + template <typename> constexpr bool F = false; + template <typename T> struct A { }; + + template <typename T, typename U> + bool operator==(T, A<U *>); + + template <typename T, typename U> + bool operator!=(A<T>, U) { + static_assert(F<T>, "Isn't this less specialized?"); + return false; + } + + bool f(A<int> ax, A<int *> ay) { return ay != ax; } + + template<class T> concept AlwaysTrue=true; + template <class T> struct B { + template <AlwaysTrue U> + bool operator==(const B<U>&)const; + }; + + + template <typename U> + bool operator==(const B<int>&,const B<U>&) { + static_assert(F<int>, "Isn't this less specialized?"); + return false; + } + + bool g(B<int> bx, B<int *> by) { return bx == by; } +#endif +} diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 5973d825433463..7f288a084edf81 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -14478,7 +14478,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/2445.html">2445</a></td> <td>C++20</td> <td>Partial ordering with rewritten candidates</td> - <td class="unreleased" align="center">Clang 18</td> + <td class="unreleased" align="center">Clang 19</td> </tr> <tr id="2446"> <td><a href="https://cplusplus.github.io/CWG/issues/2446.html">2446</a></td> From d70ad7da5228383ff761759629b0430e2e4106eb Mon Sep 17 00:00:00 2001 From: Botond Istvan Horvath <horvath.botond.ist...@gmail.com> Date: Mon, 4 Mar 2024 14:11:14 +0100 Subject: [PATCH 7/8] added tests for rvalref and volatile --- clang/test/CXX/drs/dr24xx.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/clang/test/CXX/drs/dr24xx.cpp b/clang/test/CXX/drs/dr24xx.cpp index e756f43985517b..4534ed26e56d07 100644 --- a/clang/test/CXX/drs/dr24xx.cpp +++ b/clang/test/CXX/drs/dr24xx.cpp @@ -68,6 +68,7 @@ template<A> struct X {}; X<1> x; #endif } + namespace dr2445 { // dr2445: 19 #if __cplusplus >= 202002L template <typename> constexpr bool F = false; @@ -98,5 +99,33 @@ namespace dr2445 { // dr2445: 19 } bool g(B<int> bx, B<int *> by) { return bx == by; } + + struct C{ + template<AlwaysTrue T> + int operator+(T){return 0;} + template<class T> + void operator-(T){} + }; + template<class T> + void operator+(C&&,T){} + template<AlwaysTrue T> + int operator-(C&&,T){return 0;} + + void t(int* iptr){ + int x1 = C{} + iptr; + int x2 = C{} - iptr; + } + + struct D{ + template<AlwaysTrue T> + int operator+(T) volatile {return 1;} + }; + + template<class T> + void operator+(volatile D&,T) {} + + int foo(volatile D& d){ + return d + 1; + } #endif } From 9de8a5a861c16a0570b3f91967e906e916113e3e Mon Sep 17 00:00:00 2001 From: Botond Istvan Horvath <horvath.botond.ist...@gmail.com> Date: Tue, 5 Mar 2024 11:50:37 +0100 Subject: [PATCH 8/8] removed a now unsed parameter --- clang/include/clang/Sema/Sema.h | 3 +-- clang/lib/Sema/SemaOverload.cpp | 2 +- clang/lib/Sema/SemaTemplateDeduction.cpp | 12 ++++-------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1a2a3a6bebd95e..3e85f96f468e2b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9463,8 +9463,7 @@ class Sema final { FunctionTemplateDecl *getMoreSpecializedTemplate( FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc, TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1, - unsigned NumCallArguments2, QualType ObjType1 = {}, - QualType ObjType2 = {}, bool Reversed = false); + QualType ObjType1 = {}, QualType ObjType2 = {}, bool Reversed = false); UnresolvedSetIterator getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd, diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 74d714ad308af1..e9dace7ccbf10a 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -10535,7 +10535,7 @@ bool clang::isBetterOverloadCandidate( Cand2.Function->getPrimaryTemplate(), Loc, isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion : TPOC_Call, - Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments, + Cand1.ExplicitCallArguments, ObjContext1 ? QualType(ObjContext1->getTypeForDecl(), 0) : QualType{}, ObjContext2 ? QualType(ObjContext2->getTypeForDecl(), 0) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 04e46ab69876f6..9f8e7d9d17deff 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5484,9 +5484,6 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc, /// \param NumCallArguments1 The number of arguments in the call to FT1, used /// only when \c TPOC is \c TPOC_Call. /// -/// \param NumCallArguments2 The number of arguments in the call to FT2, used -/// only when \c TPOC is \c TPOC_Call. -/// /// \param RawObjType1 The type of the object parameter of FT1 if a member /// function only used if \c TPOC is \c TPOC_Call and FT1 is a Function /// template from a member function @@ -5504,8 +5501,7 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc, FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc, TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1, - unsigned NumCallArguments2, QualType RawObjType1, QualType RawObjType2, - bool Reversed) { + QualType RawObjType1, QualType RawObjType2, bool Reversed) { SmallVector<QualType, 4> Args1; SmallVector<QualType, 4> Args2; const FunctionDecl *FD1 = FT1->getTemplatedDecl(); @@ -5791,8 +5787,8 @@ UnresolvedSetIterator Sema::getMostSpecialized( FunctionTemplateDecl *Challenger = cast<FunctionDecl>(*I)->getPrimaryTemplate(); assert(Challenger && "Not a function template specialization?"); - if (isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger, - Loc, TPOC_Other, 0, 0), + if (isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger, Loc, + TPOC_Other, 0), Challenger)) { Best = I; BestTemplate = Challenger; @@ -5807,7 +5803,7 @@ UnresolvedSetIterator Sema::getMostSpecialized( = cast<FunctionDecl>(*I)->getPrimaryTemplate(); if (I != Best && !isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger, - Loc, TPOC_Other, 0, 0), + Loc, TPOC_Other, 0), BestTemplate)) { Ambiguous = true; break; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits