Author: Younan Zhang Date: 2026-07-13T22:04:58+08:00 New Revision: 24df1d13d9090e075b162bf81d1a34a408e54924
URL: https://github.com/llvm/llvm-project/commit/24df1d13d9090e075b162bf81d1a34a408e54924 DIFF: https://github.com/llvm/llvm-project/commit/24df1d13d9090e075b162bf81d1a34a408e54924.diff LOG: Reapply "[Clang] Transform lambda's constraints when instantiating parameter mapping (#207966) This reapplies https://github.com/llvm/llvm-project/pull/195995 The last approach caused a regression because sometimes we transformed the constraints of lambda expression too aggressively when we should have preserved it. The basic idea of this patch is to transform constraints of lambda expressions when we instantiate the parameter mapping, thus the evaluation can be emancipated from preserving outer template arguments. Claude is used to help reduce the test case from #199209 Fixes https://github.com/llvm/llvm-project/issues/193944 Fixes https://github.com/llvm/llvm-project/issues/199209 Fixes https://github.com/llvm/llvm-project/issues/202957 Added: Modified: clang/include/clang/Sema/Sema.h clang/include/clang/Sema/Template.h clang/lib/Sema/SemaConcept.cpp clang/lib/Sema/SemaLambda.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/lib/Sema/SemaTemplateInstantiate.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/lib/Sema/TreeTransform.h clang/test/SemaTemplate/concepts-lambda.cpp clang/test/SemaTemplate/concepts.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6e6ca1c4aa61a..bb5697a16e090 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13924,21 +13924,6 @@ class Sema final : public SemaBase { ExprResult SubstCXXIdExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs); - // A RAII type used by the TemplateDeclInstantiator and TemplateInstantiator - // to disable constraint evaluation, then restore the state. - template <typename InstTy> struct ConstraintEvalRAII { - InstTy &TI; - bool OldValue; - - ConstraintEvalRAII(InstTy &TI) - : TI(TI), OldValue(TI.getEvaluateConstraints()) { - TI.setEvaluateConstraints(false); - } - ~ConstraintEvalRAII() { TI.setEvaluateConstraints(OldValue); } - ConstraintEvalRAII(const ConstraintEvalRAII &) = delete; - ConstraintEvalRAII &operator=(const ConstraintEvalRAII &) = delete; - }; - // Must be used instead of SubstExpr at 'constraint checking' time. ExprResult SubstConstraintExpr(Expr *E, diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index b0170c21feb1a..a3340d2f4a044 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -97,6 +97,8 @@ enum class TemplateSubstitutionKind : char { /// The kind of substitution described by this argument list. TemplateSubstitutionKind Kind = TemplateSubstitutionKind::Specialization; + bool RetainInnerDepths = false; + public: /// Construct an empty set of template argument lists. MultiLevelTemplateArgumentList() = default; @@ -108,6 +110,10 @@ enum class TemplateSubstitutionKind : char { void setKind(TemplateSubstitutionKind K) { Kind = K; } + void setRetainInnerDepths() { RetainInnerDepths = true; } + + bool retainInnerDepths() const { return RetainInnerDepths; } + /// Determine the kind of template substitution being performed. TemplateSubstitutionKind getKind() const { return Kind; } diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index f41f37b8a3d19..476910a9db528 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -491,6 +491,7 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> { class ConstraintSatisfactionChecker { Sema &S; const NamedDecl *Template; + const ConceptReference *TopLevelConceptId; SourceLocation TemplateNameLoc; UnsignedOrNone PackSubstitutionIndex; ConstraintSatisfaction &Satisfaction; @@ -549,11 +550,13 @@ class ConstraintSatisfactionChecker { public: ConstraintSatisfactionChecker(Sema &SemaRef, const NamedDecl *Template, + const ConceptReference *TopLevelConceptId, SourceLocation TemplateNameLoc, UnsignedOrNone PackSubstitutionIndex, ConstraintSatisfaction &Satisfaction, bool BuildExpression) - : S(SemaRef), Template(Template), TemplateNameLoc(TemplateNameLoc), + : S(SemaRef), Template(Template), TopLevelConceptId(TopLevelConceptId), + TemplateNameLoc(TemplateNameLoc), PackSubstitutionIndex(PackSubstitutionIndex), Satisfaction(Satisfaction), BuildExpression(BuildExpression) {} @@ -681,6 +684,8 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( llvm::SaveAndRestore PushTemplateArgsCache(S.CurrentCachedTemplateArgs, &CachedTemplateArgs); + // We don't want the template argument substitution into parameter + // mappings to preserve the outer depths. if (S.SubstTemplateArgumentsInParameterMapping( Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL, SubstArgs)) { @@ -730,9 +735,15 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( const AtomicConstraint &Constraint, const MultiLevelTemplateArgumentList &MLTAL) { std::optional<EnterExpressionEvaluationContext> EvaluationContext; - EvaluationContext.emplace( - S, Sema::ExpressionEvaluationContext::ConstantEvaluated, - Sema::ReuseLambdaContextDecl); + // The ConceptDecl as a ContextDecl ensures that, when evaluating constraints + // on transformed lambdas, we don't have extra outer template arguments. + if (ParentConcept) + EvaluationContext.emplace( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated, ParentConcept); + else + EvaluationContext.emplace( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated, + Sema::ReuseLambdaContextDecl); llvm::SmallVector<TemplateArgument> SubstitutedOutermost; std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs = @@ -746,17 +757,8 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( // i.e they should not have access to the current class object or its // non-public members. std::optional<Sema::ContextRAII> ConceptContext; - if (ParentConcept) { + if (ParentConcept) ConceptContext.emplace(S, ParentConcept->getDeclContext()); - // FIXME: the evaluation context should learn to track template arguments - // separately from a Decl. - EvaluationContext.emplace( - S, Sema::ExpressionEvaluationContext::ConstantEvaluated, - /*LambdaContextDecl=*/ - ImplicitConceptSpecializationDecl::Create( - S.Context, ParentConcept->getDeclContext(), - ParentConcept->getBeginLoc(), SubstitutedOutermost)); - } Sema::ArgPackSubstIndexRAII SubstIndex(S, PackSubstitutionIndex); ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint( @@ -915,8 +917,9 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( Satisfaction.IsSatisfied = false; Satisfaction.ContainsErrors = false; ExprResult Expr = - ConstraintSatisfactionChecker(S, Template, TemplateNameLoc, - UnsignedOrNone(I), Satisfaction, + ConstraintSatisfactionChecker(S, Template, TopLevelConceptId, + TemplateNameLoc, UnsignedOrNone(I), + Satisfaction, /*BuildExpression=*/false) .Evaluate(Constraint.getNormalizedPattern(), *SubstitutedArgs); if (BuildExpression) { @@ -1002,8 +1005,16 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( const_cast<NamedDecl *>(Template), Constraint.getSourceRange()); TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc); - if (S.SubstTemplateArguments(Ori->arguments(), *SubstitutedArgs, OutArgs) || - Trap.hasErrorOccurred()) { + + // There's a concern that even with the same concept, they may not have the + // same ConceptReference, if they come from modules. + if (TopLevelConceptId && + ConceptId->getNamedConcept() == TopLevelConceptId->getNamedConcept()) { + for (auto &A : Ori->arguments()) + OutArgs.addArgument(A); + } else if (S.SubstTemplateArguments(Ori->arguments(), *SubstitutedArgs, + OutArgs) || + Trap.hasErrorOccurred()) { Satisfaction.IsSatisfied = false; if (!Trap.hasErrorOccurred()) return ExprError(); @@ -1241,11 +1252,12 @@ static bool CheckConstraintSatisfaction( Template, /*CSE=*/nullptr, S.ArgPackSubstIndex); - ExprResult Res = ConstraintSatisfactionChecker( - S, Template, TemplateIDRange.getBegin(), - S.ArgPackSubstIndex, Satisfaction, - /*BuildExpression=*/ConvertedExpr != nullptr) - .Evaluate(*C, TemplateArgsLists); + ExprResult Res = + ConstraintSatisfactionChecker( + S, Template, TopLevelConceptId, TemplateIDRange.getBegin(), + S.ArgPackSubstIndex, Satisfaction, + /*BuildExpression=*/ConvertedExpr != nullptr) + .Evaluate(*C, TemplateArgsLists); if (Res.isInvalid()) return true; @@ -2319,6 +2331,14 @@ bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) { } assert(!ArgsAsWritten); const ConceptSpecializationExpr *CSE = CC.getConceptSpecializationExpr(); + // Make sure that lambdas within template arguments live in a + // dependent context such that they are assured to be transformed during + // constraint evaluation. + EnterExpressionEvaluationContext EECtx( + SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated, + /*LambdaContextDecl=*/ + const_cast<ImplicitConceptSpecializationDecl *>( + CSE->getSpecializationDecl())); SmallVector<TemplateArgument> InnerArgs(CSE->getTemplateArguments()); ConceptDecl *Concept = CSE->getNamedConcept(); if (RemovePacksForFoldExpr) { @@ -2347,6 +2367,7 @@ bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) { /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); + MLTAL.setRetainInnerDepths(); return SubstituteParameterMappings(SemaRef, &MLTAL, CSE->getTemplateArgsAsWritten(), diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index db3eae4be1c41..aa587e25bba27 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -324,7 +324,8 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) { } } else if (isa<FieldDecl>(ManglingContextDecl)) { return DataMember; - } else if (isa<ImplicitConceptSpecializationDecl>(ManglingContextDecl)) { + } else if (isa<ImplicitConceptSpecializationDecl, ConceptDecl>( + ManglingContextDecl)) { return Concept; } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index a7f252be1cb39..7286a0c2f6fbf 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4939,7 +4939,7 @@ ExprResult Sema::CheckConceptTemplateId( LocalInstantiationScope Scope(*this); EnterExpressionEvaluationContext EECtx{ - *this, ExpressionEvaluationContext::Unevaluated, CSD}; + *this, ExpressionEvaluationContext::Unevaluated}; Error = CheckConstraintSatisfaction( NamedConcept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL, diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index dc14a3b44ff13..3c45806c47a6e 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5220,20 +5220,6 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, return true; MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.SugaredConverted, /*Final=*/true); - // Build up an EvaluationContext with an ImplicitConceptSpecializationDecl so - // that the template arguments of the constraint can be preserved. For - // example: - // - // template <class T> - // concept C = []<D U = void>() { return true; }(); - // - // We need the argument for T while evaluating type constraint D in - // building the CallExpr to the lambda. - EnterExpressionEvaluationContext EECtx( - S, Sema::ExpressionEvaluationContext::Unevaluated, - ImplicitConceptSpecializationDecl::Create( - S.getASTContext(), Concept->getDeclContext(), Concept->getLocation(), - CTAI.SugaredConverted)); if (S.CheckConstraintSatisfaction( Concept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL, TypeLoc.getLocalSourceRange(), Satisfaction)) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index be5a28661058d..a7d9e67b01512 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1321,6 +1321,7 @@ namespace { DeclarationName Entity; // Whether to evaluate the C++20 constraints or simply substitute into them. bool EvaluateConstraints = true; + bool EvaluateLambdaConstraint = false; // Whether Substitution was Incomplete, that is, we tried to substitute in // any user provided template arguments which were null. bool IsIncomplete = false; @@ -1359,11 +1360,14 @@ namespace { inline static struct ForParameterMappingSubstitution_t { } ForParameterMappingSubstitution; + inline static struct ForConstraintSubstitution_t { + } ForConstraintSubstitution; + TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef, SourceLocation Loc, const MultiLevelTemplateArgumentList &TemplateArgs) : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc), - BailOutOnIncomplete(false) { + EvaluateLambdaConstraint(true), BailOutOnIncomplete(false) { if (!SemaRef.CurrentCachedTemplateArgs) return; auto &V = TemplateArgsHashValue.emplace(); @@ -1372,6 +1376,13 @@ namespace { Arg.Profile(V, SemaRef.Context); } + TemplateInstantiator(ForConstraintSubstitution_t, Sema &SemaRef, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceLocation Loc, DeclarationName Entity, + bool BailOutOnIncomplete = false) + : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc), + EvaluateLambdaConstraint(true), BailOutOnIncomplete(false) {} + /// Determine whether the given type \p T has already been /// transformed. /// @@ -1766,9 +1777,22 @@ namespace { if (TA.isDependent()) return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent; } + if (auto *CD = dyn_cast_if_present<ImplicitConceptSpecializationDecl>( + LSI->Lambda->getLambdaContextDecl())) { + if (llvm::any_of(CD->getTemplateArguments(), + [](const auto &TA) { return TA.isDependent(); })) + return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent; + } return inherited::ComputeLambdaDependency(LSI); } + ExprResult TransformLambdaConstraint(Expr *AC) { + if (AC && EvaluateLambdaConstraint) + return TransformExpr(const_cast<Expr *>(AC)); + + return AC; + } + ExprResult TransformLambdaExpr(LambdaExpr *E) { // Do not rebuild lambdas to avoid creating a new type. // Lambdas have already been processed inside their eval contexts. @@ -1776,7 +1800,7 @@ namespace { return E; LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true, /*InstantiatingLambdaOrBlock=*/true); - Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this); + llvm::SaveAndRestore RAII(EvaluateConstraints, EvaluateLambdaConstraint); return inherited::TransformLambdaExpr(E); } @@ -2185,6 +2209,9 @@ TemplateInstantiator::TransformPredefinedExpr(PredefinedExpr *E) { ExprResult TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, NonTypeTemplateParmDecl *NTTP) { + if (TemplateArgs.retainInnerDepths() && + NTTP->getDepth() >= TemplateArgs.getNumLevels()) + return E; // If the corresponding template argument is NULL or non-existent, it's // because we are performing instantiation from explicitly-specified // template arguments in a function template, but there were some @@ -2597,8 +2624,10 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB, NewTTPDecl = cast_or_null<TemplateTypeParmDecl>( TransformDecl(TL.getNameLoc(), OldTTPDecl)); QualType Result = getSema().Context.getTemplateTypeParmType( - T->getDepth() - TemplateArgs.getNumSubstitutedLevels(), T->getIndex(), - T->isParameterPack(), NewTTPDecl); + T->getDepth() - (TemplateArgs.retainInnerDepths() + ? 0 + : TemplateArgs.getNumSubstitutedLevels()), + T->getIndex(), T->isParameterPack(), NewTTPDecl); TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result); NewTL.setNameLoc(TL.getNameLoc()); return Result; @@ -4470,9 +4499,13 @@ Sema::SubstCXXIdExpr(Expr *E, ExprResult Sema::SubstConstraintExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) { - // FIXME: should call SubstExpr directly if this function is equivalent or - // should it be diff erent? - return SubstExpr(E, TemplateArgs); + if (!E) + return E; + + TemplateInstantiator Instantiator( + TemplateInstantiator::ForConstraintSubstitution, *this, TemplateArgs, + SourceLocation(), DeclarationName()); + return Instantiator.TransformExpr(E); } ExprResult Sema::SubstConstraintExprWithoutSatisfaction( diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 487d049166f13..c2e90624b8a6e 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -33,6 +33,7 @@ #include "clang/Sema/SemaSwift.h" #include "clang/Sema/Template.h" #include "clang/Sema/TemplateInstCallback.h" +#include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/TimeProfiler.h" #include <optional> @@ -2582,7 +2583,7 @@ TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { // merged with the local instantiation scope for the function template // itself. LocalInstantiationScope Scope(SemaRef); - Sema::ConstraintEvalRAII<TemplateDeclInstantiator> RAII(*this); + llvm::SaveAndRestore RAII(EvaluateConstraints, false); TemplateParameterList *TempParams = D->getTemplateParameters(); TemplateParameterList *InstParams = SubstTemplateParams(TempParams); @@ -3612,9 +3613,11 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl( TemplateTypeParmDecl *Inst = TemplateTypeParmDecl::Create( SemaRef.Context, Owner, D->getBeginLoc(), D->getLocation(), - D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), D->getIndex(), - D->getIdentifier(), D->wasDeclaredWithTypename(), D->isParameterPack(), - D->hasTypeConstraint(), NumExpanded); + D->getDepth() - (TemplateArgs.retainInnerDepths() + ? 0 + : TemplateArgs.getNumSubstitutedLevels()), + D->getIndex(), D->getIdentifier(), D->wasDeclaredWithTypename(), + D->isParameterPack(), D->hasTypeConstraint(), NumExpanded); Inst->setAccess(AS_public); Inst->setImplicit(D->isImplicit()); @@ -4883,6 +4886,13 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) { return nullptr; Expr *InstRequiresClause = L->getRequiresClause(); + if (InstRequiresClause && EvaluateConstraints) { + ExprResult E = + SemaRef.SubstConstraintExpr(InstRequiresClause, TemplateArgs); + if (E.isInvalid()) + return nullptr; + InstRequiresClause = E.get(); + } TemplateParameterList *InstL = TemplateParameterList::Create(SemaRef.Context, L->getTemplateLoc(), diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 1dd7be50c9ff2..cc126f9000717 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -837,6 +837,8 @@ class TreeTransform { LSI->Lambda->getLambdaDependencyKind()); } + ExprResult TransformLambdaConstraint(Expr *AC) { return AC; } + QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL); StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr); @@ -16295,11 +16297,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { assert(FPTL && "Not a FunctionProtoType?"); AssociatedConstraint TRC = E->getCallOperator()->getTrailingRequiresClause(); - // If the concept refers to any outer parameter packs, we track the SubstIndex - // for evaluation. - if (TRC && TRC.ConstraintExpr->containsUnexpandedParameterPack() && - !TRC.ArgPackSubstIndex) - TRC.ArgPackSubstIndex = SemaRef.ArgPackSubstIndex; + if (TRC) { + ExprResult E = getDerived().TransformLambdaConstraint( + const_cast<Expr *>(TRC.ConstraintExpr)); + if (E.isInvalid()) + return E; + TRC.ConstraintExpr = E.get(); + } getSema().CompleteLambdaCallOperator( NewCallOperator, E->getCallOperator()->getLocation(), diff --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp index 2349953736c4f..2010a028fce4a 100644 --- a/clang/test/SemaTemplate/concepts-lambda.cpp +++ b/clang/test/SemaTemplate/concepts-lambda.cpp @@ -56,9 +56,7 @@ namespace GH57971 { function_ptr ptr = f<void>; } -// GH58368: A lambda defined in a concept requires we store -// the concept as a part of the lambda context. -namespace LambdaInConcept { +namespace GH58368 { using size_t = unsigned long; template<size_t...Ts> @@ -162,13 +160,10 @@ static_assert(E<int>); // previously Asserted. namespace DIsFalse { template<auto Q> concept C = requires { Q.template operator()<float>(); }; // #GH60642-C template<class> concept D = false; -// FIXME: Crashes because it produces a template type parameter with invalid depth -#if 0 static_assert(C<[]<D>{}>); // expected-error@-1{{static assertion failed}} // expected-note@-2{{does not satisfy 'C'}} // expected-note@-5{{because 'Q.template operator()<float>()' would be invalid: no matching member function for call to 'operator()'}} -#endif template<class> concept E = C<[]<D>{}>; static_assert(E<int>); // expected-error@-1{{static assertion failed}} @@ -407,3 +402,94 @@ void test() { f<42>(); } } + +namespace GH199209 { + +template <class T> auto declval() -> T &&; +struct Id { + template <class T> using f = T; +}; +template <class... Ts> concept Ok = (__is_same(Ts, Ts) && ...); +template <bool> struct I; +template <class F, class... Ts> +using minvoke = I<Ok<Ts...>>::template f<F>::template f<Ts...>; +template <> struct I<true> { + template <template <class...> class F, class... Ts> + using g = F<Ts...>; + template <class F> using f = F; +}; +template <template <class...> class F> struct Q { + template <class... Ts> using f = I<Ok<Ts...>>::template g<F, Ts...>; +}; +template <class S> concept has_env = requires { declval<S>().get_env(); }; +struct Desc { + template <class F> using f = minvoke<F, int>; +}; +template <class... C> auto captures(C...) { + return []<class Cv>(Cv) -> int requires Ok<minvoke<Cv, C>...> {}; +} +template <class D> using captures_t = decltype(captures(declval<D>)); +template <class D> struct Sexpr { + minvoke<D, Q<captures_t>> impl; + auto get_env() -> decltype(impl(Id())); +}; +static_assert(has_env<Sexpr<Desc>>); + +} + +namespace GH193944 { + +template<auto L, typename... Ts> +concept pass_a_concept_inside_a_lambda = requires { L.template operator()<Ts...>(); }; // #requires_pass_a_concept_inside_a_lambda + +template<auto Pred, typename... Ts> +concept PredicateFor_bad = pass_a_concept_inside_a_lambda<[]<typename... Xs> // #pass_a_concept_inside_a_lambda + requires(__is_same(decltype(Pred.template operator()<Xs>()), bool) and ...) + {}, + Ts...>; + +template<auto Pred, typename... Ts> + requires PredicateFor_bad<Pred, Ts...> // #PredicateFor_bad +constexpr const unsigned count_if_v_bad = + [] { return (Pred.template operator()<Ts>() + ... + 0); }(); + +constexpr const auto L = []<typename T> +{ return __is_same(T, long); }; + +constexpr const auto L2 = []<typename T> +{ return 114514; }; + +static_assert(count_if_v_bad<L, double, int, long, void> == 1); + +static_assert(count_if_v_bad<L2, double> == 1); +// expected-error@-1 {{constraints not satisfied}} +// expected-note@#PredicateFor_bad {{evaluated to false}} +// expected-note@#pass_a_concept_inside_a_lambda {{evaluated to false}} +// expected-note@#requires_pass_a_concept_inside_a_lambda {{no matching member function}} + +template <typename ...Ts> +concept test = ((sizeof(Ts) < 2) && ...); + +template<auto L, typename... Ts> +concept pass_a_concept_inside_a_lambda_2 = requires { + L.template operator()<Ts...>(Ts()...); // #requires_pass_a_concept_inside_a_lambda_2 +}; + +template<auto Pred, typename... Ts> +concept PredicateFor_bad_2 = pass_a_concept_inside_a_lambda_2<[]<typename... Xs> // #pass_a_concept_inside_a_lambda_2 + requires(__is_same(decltype(Pred.template operator()<Xs>()), bool) and ...) + (test auto...){}, + Ts...>; + +template<auto Pred, typename... Ts> + requires PredicateFor_bad_2<Pred, Ts...> // #PredicateFor_bad_2 +constexpr const unsigned count_if_v_bad_2 = 111; + +static_assert(count_if_v_bad_2<L, double> == 111); +// expected-error@-1 {{constraints not satisfied}} +// expected-note@#PredicateFor_bad_2 {{false}} +// expected-note@#pass_a_concept_inside_a_lambda_2 {{false}} +// expected-note@#requires_pass_a_concept_inside_a_lambda_2 {{no matching member function}} +static_assert(count_if_v_bad_2<L, char> == 111); + +} diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index f2478e1a22a6a..b37e856fe84d0 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -1556,9 +1556,12 @@ struct vector; template <typename T, typename U> concept C = __is_same_as(T, U); +template <typename T, typename U> +concept D = false && __is_same_as(T, U); + template<class T, auto Cpt> concept generic_range_value = requires { - Cpt.template operator()<int>(); + Cpt.template operator()<int>(); // expected-note {{would be invalid}} }; @@ -1567,8 +1570,14 @@ template<generic_range_value<[]< >() {}> T> void x() {} +template<generic_range_value<[]< // expected-note {{evaluated to false}} + D<int> + >() {}> T> +void y() {} // expected-note {{ignored}} + void foo() { x<vector<int>>(); + y<vector<int>>(); // expected-error {{no matching function}} } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
