diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -630,6 +630,8 @@ - Allow abstract parameter and return types in functions that are either deleted or not defined. (`#63012 `_) +- Fix constraint checking of non-generic lambdas. + (`#63181 `_) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4714,6 +4714,9 @@ def note_ovl_candidate_constraints_not_satisfied : Note< "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: constraints " "not satisfied">; +def note_ovl_surrogate_constraints_not_satisfied : Note< + "candidate surrogate function %0 not viable: constraints " + "not satisfied">; def note_implicit_member_target_infer_collision : Note< "implicit %sub{select_special_member_kind}0 inferred target collision: call to both " "%select{__device__|__global__|__host__|__host__ __device__}1 and " diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -1633,6 +1633,14 @@ Conversion->setAccess(AS_public); Conversion->setImplicit(true); + // A non-generic lambda may still be a templated entity + // We need to preserve constraints when + // taking the address of the call operator as well as performing + // surrogate calls. + // See GH63181. + if (Expr *Requires = CallOperator->getTrailingRequiresClause()) + Conversion->setTrailingRequiresClause(Requires); + if (Class->isGenericLambda()) { // Create a template version of the conversion operator, using the template // parameter list of the function call operator. diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" +#include "clang/AST/ASTLambda.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -7578,8 +7579,15 @@ } if (Conversion->getTrailingRequiresClause()) { + CXXMethodDecl *M = Conversion; + // A lambda conversion operator has the same constraints + // as the call operator, and constraints checking + // relies on whether we are in a lambda call operator + // so use that instead. + if (isLambdaConversionOperator(Conversion)) + M = Conversion->getParent()->getLambdaCallOperator(); ConstraintSatisfaction Satisfaction; - if (CheckFunctionConstraints(Conversion, Satisfaction) || + if (CheckFunctionConstraints(M, Satisfaction) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -7851,6 +7859,25 @@ } } + if (Conversion->getTrailingRequiresClause()) { + CXXMethodDecl *M = Conversion; + // A lambda conversion operator has the same constraints + // as the call operator, and constraints checking + // relies on whether we are in a lambda call operator + // so use that instead. + if (isLambdaConversionOperator(Conversion)) + M = Conversion->getParent()->getLambdaCallOperator(); + + ConstraintSatisfaction Satisfaction; + if (CheckFunctionConstraints(M, Satisfaction, /*Loc*/ {}, + /*ForOverloadResolution*/ true) || + !Satisfaction.IsSatisfied) { + Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_constraints_not_satisfied; + return; + } + } + if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, CandidateSet.getLocation(), std::nullopt)) { Candidate.Viable = false; @@ -11620,8 +11647,17 @@ if (isRValueReference) FnType = S.Context.getRValueReferenceType(FnType); if (isLValueReference) FnType = S.Context.getLValueReferenceType(FnType); - S.Diag(Cand->Surrogate->getLocation(), diag::note_ovl_surrogate_cand) - << FnType; + if (Cand->FailureKind == ovl_fail_constraints_not_satisfied) { + S.Diag(Cand->Surrogate->getLocation(), + diag::note_ovl_surrogate_constraints_not_satisfied) + << Cand->Surrogate; + ConstraintSatisfaction Satisfaction; + if (S.CheckFunctionConstraints(Cand->Surrogate, Satisfaction)) + S.DiagnoseUnsatisfiedConstraint(Satisfaction); + } else { + S.Diag(Cand->Surrogate->getLocation(), diag::note_ovl_surrogate_cand) + << FnType; + } } static void NoteBuiltinOperatorCandidate(Sema &S, StringRef Opc, @@ -14944,6 +14980,22 @@ /*SuppressUserConversion=*/false); } + // When calling a lambda, both the call operator, and + // the conversion operator to function pointer + // are considered. But when constraint checking + // on the call operator fails, it will also fail on the + // conversion operator as the constraints are always the same. + // As the user probably do not intend to perform a surrogate call, + // we filter them out to produce better error diagnostics, ie to avoid + // showing 2 failed overloads instead of one. + bool IgnoreSurrogateFunctions = false; + if (CandidateSet.size() == 1 && Record->getAsCXXRecordDecl()->isLambda()) { + const OverloadCandidate &Candidate = *CandidateSet.begin(); + if (!Candidate.Viable && + Candidate.FailureKind == ovl_fail_constraints_not_satisfied) + IgnoreSurrogateFunctions = true; + } + // C++ [over.call.object]p2: // In addition, for each (non-explicit in C++0x) conversion function // declared in T of the form @@ -14963,7 +15015,8 @@ // within T by another intervening declaration. const auto &Conversions = cast(Record->getDecl())->getVisibleConversionFunctions(); - for (auto I = Conversions.begin(), E = Conversions.end(); I != E; ++I) { + for (auto I = Conversions.begin(), E = Conversions.end(); + !IgnoreSurrogateFunctions && I != E; ++I) { NamedDecl *D = *I; CXXRecordDecl *ActingContext = cast(D->getDeclContext()); if (isa(D)) diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -410,8 +410,8 @@ template void SingleDepthReferencesTopLambda(U &&u) { - []() - requires IsInt + []() // #SDRTL_OP + requires IsInt // #SDRTL_REQ {}(); } @@ -434,8 +434,8 @@ template void DoubleDepthReferencesTopLambda(U &&u) { - []() { []() - requires IsInt + []() { []() // #DDRTL_OP + requires IsInt // #DDRTL_REQ {}(); }(); } @@ -459,10 +459,11 @@ template void DoubleDepthReferencesAllLambda(U &&u) { - [](U &&u2) { - [](U && u3) - requires IsInt && - IsInt && IsInt + [](U &&u2) { // #DDRAL_OP1 + [](U && u3) // #DDRAL_OP2 + requires IsInt // #DDRAL_REQ + && IsInt + && IsInt {}(u2); }(u); } @@ -484,8 +485,8 @@ template void ChecksLocalVar(T x) { T Local; - []() - requires(IsInt) + []() // #CLV_OP + requires(IsInt) // #CLV_REQ {}(); } @@ -529,6 +530,11 @@ SingleDepthReferencesTopLambda(v); // FIXME: This should error on constraint failure! (Lambda!) SingleDepthReferencesTopLambda(will_fail); + // expected-note@-1{{in instantiation of function template specialization}} + // expected-error@#SDRTL_OP{{no matching function for call to object of type}} + // expected-note@#SDRTL_OP{{candidate function not viable: constraints not satisfied}} + // expected-note@#SDRTL_REQ{{because 'IsInt' evaluated to false}} + DoubleDepthReferencesTop(v); DoubleDepthReferencesTop(will_fail); // expected-error@#DDRT_CALL{{no matching function for call to object of type 'lc2'}} @@ -538,8 +544,12 @@ // expected-note@#DDRT_REQ{{'IsInt' evaluated to false}} DoubleDepthReferencesTopLambda(v); - // FIXME: This should error on constraint failure! (Lambda!) DoubleDepthReferencesTopLambda(will_fail); + // expected-note@-1{{in instantiation of function template specialization}} + // expected-error@#DDRTL_OP{{no matching function for call to object of type}} + // expected-note@#DDRTL_OP{{candidate function not viable: constraints not satisfied}} + // expected-note@#DDRTL_OP{{while substituting into a lambda expression here}} + // expected-note@#DDRTL_REQ{{because 'IsInt' evaluated to false}} DoubleDepthReferencesAll(v); DoubleDepthReferencesAll(will_fail); // expected-error@#DDRA_CALL{{no matching function for call to object of type 'lc2'}} @@ -549,8 +559,12 @@ // expected-note@#DDRA_REQ{{'IsInt' evaluated to false}} DoubleDepthReferencesAllLambda(v); - // FIXME: This should error on constraint failure! (Lambda!) DoubleDepthReferencesAllLambda(will_fail); + // expected-note@-1{{in instantiation of function template specialization}} + // expected-note@#DDRAL_OP1{{while substituting into a lambda expression here}} + // expected-error@#DDRAL_OP2{{no matching function for call to object of type}} + // expected-note@#DDRAL_OP2{{candidate function not viable: constraints not satisfied}} + // expected-note@#DDRAL_REQ{{because 'IsInt' evaluated to false}} CausesFriendConstraint CFC; FriendFunc(CFC, 1); @@ -565,6 +579,12 @@ ChecksLocalVar(v); // FIXME: This should error on constraint failure! (Lambda!) ChecksLocalVar(will_fail); + // expected-note@-1{{in instantiation of function template specialization}} + // expected-error@#CLV_OP{{no matching function for call to object of type}} + // expected-note@#CLV_OP{{candidate function not viable: constraints not satisfied}} + // expected-note@#CLV_REQ{{because 'IsInt' evaluated to false}} + + LocalStructMemberVar(v); LocalStructMemberVar(will_fail); @@ -701,6 +721,18 @@ } // namespace SelfFriend +namespace Surrogates { +int f1(int); +template +struct A { + using F = int(int); + operator F*() requires N { return f1; } // expected-note{{candidate surrogate function 'operator int (*)(int)' not viable: constraints not satisfied}} +}; +int i = A{}(0); +int j = A{}(0); // expected-error{{no matching function for call to object of type 'A'}} +} + + namespace ConstrainedMemberVarTemplate { template struct Container { static constexpr long arity = Size; @@ -914,3 +946,34 @@ static_assert(W0<0>::W1<1>::F::value == 1); } // TemplateInsideTemplateInsideTemplate + + +namespace GH63181 { + +template void f() { +auto l = []() requires N { }; // expected-note 2{{candidate function not viable: constraints not satisfied}} \ + // expected-note 2{{because 'false' evaluated to false}} + +l(); +// expected-error@-1 {{no matching function for call to object of type}} +void(*ptr)() = l; +// expected-error-re@-1 {{no viable conversion from '(lambda {{.*}})' to 'void (*)()'}} +} + +template void f(); // expected-note {{in instantiation of function template specialization 'GH63181::f' requested here}} +template void f(); + +template concept C = __is_same(T, int); // expected-note{{because '__is_same(char, int)' evaluated to false}} + +template void f() { + ([]() requires C { return Ts(); }(), ...); + // expected-error@-1 {{no matching function for call to object of type}} \ + // expected-note@-1 {{candidate function not viable: constraints not satisfied}} \ + // expected-note@-1 {{because 'char' does not satisfy 'C'}} +} + +template void f(); +template void f(); +//expected-note@-1{{in instantiation of function template specialization 'GH63181::f' requested here}} + +}