https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/207966
>From 10729a86948f4ad8e7d3f3d518cf909e9702c93a Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Tue, 7 Jul 2026 14:06:24 +0800 Subject: [PATCH 1/7] Reapply "[Clang] Transform lambda's constraints when instantiating parameter mapping" This reverts commit 4ad14a07f5704b950131f361edf05b96a871dfa2. --- clang/lib/Parse/ParseTemplate.cpp | 6 --- clang/lib/Sema/SemaConcept.cpp | 19 +++++----- clang/lib/Sema/SemaTemplate.cpp | 2 +- clang/lib/Sema/SemaTemplateDeduction.cpp | 14 ------- clang/lib/Sema/SemaTemplateInstantiate.cpp | 37 ++++++++++++++++--- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 7 ++++ clang/lib/Sema/TreeTransform.h | 14 ++++++- clang/test/SemaTemplate/concepts-lambda.cpp | 36 ++++++++++++++++-- clang/test/SemaTemplate/concepts.cpp | 3 ++ 9 files changed, 97 insertions(+), 41 deletions(-) diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 735a9bd1f9f1c..1529da187614f 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -533,12 +533,6 @@ bool Parser::isTypeConstraintAnnotation() { bool Parser::TryAnnotateTypeConstraint() { if (!getLangOpts().CPlusPlus20) return false; - // The type constraint may declare template parameters, notably - // if it contains a generic lambda, so we need to increment - // the template depth as these parameters would not be instantiated - // at the current depth. - TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); - ++CurTemplateDepthTracker; CXXScopeSpec SS; bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index f41f37b8a3d19..fc52d1c4b83bb 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -746,17 +746,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( @@ -2319,6 +2310,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) { 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 d93b528facbcc..078cd0717bb25 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5180,20 +5180,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 990e52eb27d39..210c970902f6a 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1753,9 +1753,24 @@ 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 TransformConstraint(Expr *AC) { + // We don't want the template argument substitution into parameter + // mappings to preserve the outer depths. + if (AC && SemaRef.inConstraintSubstitution()) + 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. @@ -1866,12 +1881,24 @@ namespace { TemplateParameterList *OrigTPL) { if (!OrigTPL || !OrigTPL->size()) return OrigTPL; + std::optional<MultiLevelTemplateArgumentList> OldMLTAL; + // We need to preserve the lambda depth in parameter mapping. + // Otherwise the template argument deduction would fail, if we reduced the + // depth too early. + if (SemaRef.inParameterMappingSubstitution() && + OrigTPL->getDepth() >= TemplateArgs.getNumSubstitutedLevels()) + OldMLTAL = ForgetSubstitution(); + DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext(); - TemplateDeclInstantiator DeclInstantiator(getSema(), - /* DeclContext *Owner */ Owner, - TemplateArgs); - DeclInstantiator.setEvaluateConstraints(EvaluateConstraints); - return DeclInstantiator.SubstTemplateParams(OrigTPL); + TemplateDeclInstantiator DeclInstantiator(getSema(), Owner, TemplateArgs); + // We don't want the template argument substitution into parameter + // mappings to preserve the outer depths. + DeclInstantiator.setEvaluateConstraints( + SemaRef.inConstraintSubstitution() || EvaluateConstraints); + auto *Transformed = DeclInstantiator.SubstTemplateParams(OrigTPL); + if (OldMLTAL) + RememberSubstitution(std::move(*OldMLTAL)); + return Transformed; } concepts::TypeRequirement * diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index a787d537f02f8..f0593e9bb1788 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4848,6 +4848,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 3c8fcbe582b43..477acaaea8576 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -837,6 +837,8 @@ class TreeTransform { LSI->Lambda->getLambdaDependencyKind()); } + ExprResult TransformConstraint(Expr *AC) { return AC; } + QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL); StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr); @@ -16095,8 +16097,16 @@ 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) { + ExprResult E = getDerived().TransformConstraint( + const_cast<Expr *>(TRC.ConstraintExpr)); + if (E.isInvalid()) + return E; + TRC.ConstraintExpr = E.get(); + } + // If the concept refers to any outer parameter packs, we track the + // SubstIndex for evaluation. + // FIXME: This seems unnecessary after transforming lambda constraints. if (TRC && TRC.ConstraintExpr->containsUnexpandedParameterPack() && !TRC.ArgPackSubstIndex) TRC.ArgPackSubstIndex = SemaRef.ArgPackSubstIndex; diff --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp index 2349953736c4f..840cd7cdb4d47 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> @@ -407,3 +405,35 @@ void test() { f<42>(); } } + +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}} + +} diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index f2478e1a22a6a..1ea97e5582374 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -1567,9 +1567,12 @@ template<generic_range_value<[]< >() {}> T> void x() {} +// FIXME: Crashes because it produces a template type parameter with invalid depth +#if 0 void foo() { x<vector<int>>(); } +#endif } namespace GH162770 { >From 03c1087c2168d193a1e2cd993b076920117f0067 Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Tue, 7 Jul 2026 18:36:34 +0800 Subject: [PATCH 2/7] Fix regressions --- clang/include/clang/Sema/Sema.h | 2 +- clang/lib/Parse/ParseTemplate.cpp | 6 ++++ clang/lib/Sema/SemaConcept.cpp | 40 ++++++++++++++------- clang/lib/Sema/SemaTemplateInstantiate.cpp | 37 ++++++++++++------- clang/lib/Sema/TreeTransform.h | 4 +-- clang/test/SemaTemplate/concepts-lambda.cpp | 37 +++++++++++++++++-- clang/test/SemaTemplate/concepts.cpp | 3 -- 7 files changed, 95 insertions(+), 34 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f7d0d493e7081..7c0b13335fc2b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13567,7 +13567,7 @@ class Sema final : public SemaBase { bool SubstTemplateArgumentsInParameterMapping( ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc, const MultiLevelTemplateArgumentList &TemplateArgs, - TemplateArgumentListInfo &Out); + bool TransformLambdaConstraint, TemplateArgumentListInfo &Out); /// Retrieve the template argument list(s) that should be used to /// instantiate the definition of the given declaration. diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 1529da187614f..735a9bd1f9f1c 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -533,6 +533,12 @@ bool Parser::isTypeConstraintAnnotation() { bool Parser::TryAnnotateTypeConstraint() { if (!getLangOpts().CPlusPlus20) return false; + // The type constraint may declare template parameters, notably + // if it contains a generic lambda, so we need to increment + // the template depth as these parameters would not be instantiated + // at the current depth. + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); + ++CurTemplateDepthTracker; CXXScopeSpec SS; bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index fc52d1c4b83bb..d683ac31850a8 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,9 +684,11 @@ 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)) { + /*TransformLambdaConstraint=*/true, SubstArgs)) { Satisfaction.IsSatisfied = false; return std::nullopt; } @@ -906,8 +911,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) { @@ -993,8 +999,13 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( const_cast<NamedDecl *>(Template), Constraint.getSourceRange()); TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc); - if (S.SubstTemplateArguments(Ori->arguments(), *SubstitutedArgs, OutArgs) || - Trap.hasErrorOccurred()) { + + if (ConceptId == TopLevelConceptId) { + 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(); @@ -1232,11 +1243,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; @@ -2192,7 +2204,8 @@ bool SubstituteParameterMappings::substitute( llvm::SaveAndRestore<decltype(SemaRef.CurrentCachedTemplateArgs)> DoNotCacheDependentArgs(SemaRef.CurrentCachedTemplateArgs, nullptr); if (SemaRef.SubstTemplateArgumentsInParameterMapping( - N.getParameterMapping(), N.getBeginLoc(), *MLTAL, SubstArgs)) + N.getParameterMapping(), N.getBeginLoc(), *MLTAL, + /*TransformLambdaConstraint=*/false, SubstArgs)) return true; Sema::CheckTemplateArgumentInfo CTAI; auto *TD = @@ -2264,7 +2277,8 @@ bool SubstituteParameterMappings::substitute(ConceptIdConstraint &CC) { const ASTTemplateArgumentListInfo *ArgsAsWritten = CSE->getTemplateArgsAsWritten(); if (SemaRef.SubstTemplateArgumentsInParameterMapping( - ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, Out)) + ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, + /*TransformLambdaConstraint=*/false, Out)) return true; Sema::CheckTemplateArgumentInfo CTAI; if (SemaRef.CheckTemplateArgumentList(CSE->getNamedConcept(), diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 210c970902f6a..208d0693b94e5 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1308,6 +1308,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; @@ -1346,10 +1347,14 @@ namespace { inline static struct ForParameterMappingSubstitution_t { } ForParameterMappingSubstitution; + inline static struct ForConstraintSubstitution_t { + } ForConstraintSubstitution; + TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef, - SourceLocation Loc, + SourceLocation Loc, bool TransformLambdaConstraint, const MultiLevelTemplateArgumentList &TemplateArgs) : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc), + EvaluateLambdaConstraint(TransformLambdaConstraint), BailOutOnIncomplete(false) { if (!SemaRef.CurrentCachedTemplateArgs) return; @@ -1359,6 +1364,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. /// @@ -1762,10 +1774,8 @@ namespace { return inherited::ComputeLambdaDependency(LSI); } - ExprResult TransformConstraint(Expr *AC) { - // We don't want the template argument substitution into parameter - // mappings to preserve the outer depths. - if (AC && SemaRef.inConstraintSubstitution()) + ExprResult TransformLambdaConstraint(Expr *AC) { + if (AC && EvaluateLambdaConstraint) return TransformExpr(const_cast<Expr *>(AC)); return AC; @@ -1885,8 +1895,7 @@ namespace { // We need to preserve the lambda depth in parameter mapping. // Otherwise the template argument deduction would fail, if we reduced the // depth too early. - if (SemaRef.inParameterMappingSubstitution() && - OrigTPL->getDepth() >= TemplateArgs.getNumSubstitutedLevels()) + if (SemaRef.inParameterMappingSubstitution()) OldMLTAL = ForgetSubstitution(); DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext(); @@ -4442,10 +4451,10 @@ bool Sema::SubstTemplateArguments( bool Sema::SubstTemplateArgumentsInParameterMapping( ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc, const MultiLevelTemplateArgumentList &TemplateArgs, - TemplateArgumentListInfo &Out) { + bool TransformLambdaConstraint, TemplateArgumentListInfo &Out) { TemplateInstantiator Instantiator( TemplateInstantiator::ForParameterMappingSubstitution, *this, BaseLoc, - TemplateArgs); + TransformLambdaConstraint, TemplateArgs); return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out); } @@ -4474,9 +4483,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 different? - 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/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 477acaaea8576..07f2dcf496f4b 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -837,7 +837,7 @@ class TreeTransform { LSI->Lambda->getLambdaDependencyKind()); } - ExprResult TransformConstraint(Expr *AC) { return AC; } + ExprResult TransformLambdaConstraint(Expr *AC) { return AC; } QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL); @@ -16098,7 +16098,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { AssociatedConstraint TRC = E->getCallOperator()->getTrailingRequiresClause(); if (TRC) { - ExprResult E = getDerived().TransformConstraint( + ExprResult E = getDerived().TransformLambdaConstraint( const_cast<Expr *>(TRC.ConstraintExpr)); if (E.isInvalid()) return E; diff --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp index 840cd7cdb4d47..19c0769833480 100644 --- a/clang/test/SemaTemplate/concepts-lambda.cpp +++ b/clang/test/SemaTemplate/concepts-lambda.cpp @@ -160,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}} @@ -406,6 +403,40 @@ void test() { } } +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> diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index 1ea97e5582374..f2478e1a22a6a 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -1567,12 +1567,9 @@ template<generic_range_value<[]< >() {}> T> void x() {} -// FIXME: Crashes because it produces a template type parameter with invalid depth -#if 0 void foo() { x<vector<int>>(); } -#endif } namespace GH162770 { >From 0ad7ab95cf210351384aa941f1731117921b747f Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Wed, 8 Jul 2026 19:50:03 +0800 Subject: [PATCH 3/7] Really fix the lambda depths issue --- clang/lib/Sema/SemaConcept.cpp | 12 +++++++++--- clang/lib/Sema/SemaLambda.cpp | 3 ++- clang/test/SemaTemplate/concepts.cpp | 11 ++++++++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index d683ac31850a8..8e94945c48e71 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -735,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 = diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 8572e3a742a6c..463163a31bca6 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -327,7 +327,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/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}} } } >From 680f9f82bb5f5d47f389a8d29a8b4d12e2bbb3a9 Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Fri, 10 Jul 2026 14:56:48 +0800 Subject: [PATCH 4/7] Fix another regression in 22 --- clang/include/clang/Sema/Sema.h | 17 +-------- clang/include/clang/Sema/Template.h | 6 +++ clang/lib/Sema/SemaConcept.cpp | 9 ++--- clang/lib/Sema/SemaTemplateInstantiate.cpp | 37 +++++++------------ .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 11 ++++-- clang/lib/Sema/TreeTransform.h | 6 --- clang/test/SemaTemplate/concepts-lambda.cpp | 25 +++++++++++++ 7 files changed, 57 insertions(+), 54 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7c0b13335fc2b..e80b4bed2c89d 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13567,7 +13567,7 @@ class Sema final : public SemaBase { bool SubstTemplateArgumentsInParameterMapping( ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc, const MultiLevelTemplateArgumentList &TemplateArgs, - bool TransformLambdaConstraint, TemplateArgumentListInfo &Out); + TemplateArgumentListInfo &Out); /// Retrieve the template argument list(s) that should be used to /// instantiate the definition of the given declaration. @@ -13872,21 +13872,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 8e94945c48e71..4d030d5115fc1 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -688,7 +688,7 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( // mappings to preserve the outer depths. if (S.SubstTemplateArgumentsInParameterMapping( Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL, - /*TransformLambdaConstraint=*/true, SubstArgs)) { + SubstArgs)) { Satisfaction.IsSatisfied = false; return std::nullopt; } @@ -2210,8 +2210,7 @@ bool SubstituteParameterMappings::substitute( llvm::SaveAndRestore<decltype(SemaRef.CurrentCachedTemplateArgs)> DoNotCacheDependentArgs(SemaRef.CurrentCachedTemplateArgs, nullptr); if (SemaRef.SubstTemplateArgumentsInParameterMapping( - N.getParameterMapping(), N.getBeginLoc(), *MLTAL, - /*TransformLambdaConstraint=*/false, SubstArgs)) + N.getParameterMapping(), N.getBeginLoc(), *MLTAL, SubstArgs)) return true; Sema::CheckTemplateArgumentInfo CTAI; auto *TD = @@ -2283,8 +2282,7 @@ bool SubstituteParameterMappings::substitute(ConceptIdConstraint &CC) { const ASTTemplateArgumentListInfo *ArgsAsWritten = CSE->getTemplateArgsAsWritten(); if (SemaRef.SubstTemplateArgumentsInParameterMapping( - ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, - /*TransformLambdaConstraint=*/false, Out)) + ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, Out)) return true; Sema::CheckTemplateArgumentInfo CTAI; if (SemaRef.CheckTemplateArgumentList(CSE->getNamedConcept(), @@ -2366,6 +2364,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/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 208d0693b94e5..15acc6bf9caee 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1351,11 +1351,10 @@ namespace { } ForConstraintSubstitution; TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef, - SourceLocation Loc, bool TransformLambdaConstraint, + SourceLocation Loc, const MultiLevelTemplateArgumentList &TemplateArgs) : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc), - EvaluateLambdaConstraint(TransformLambdaConstraint), - BailOutOnIncomplete(false) { + EvaluateLambdaConstraint(true), BailOutOnIncomplete(false) { if (!SemaRef.CurrentCachedTemplateArgs) return; auto &V = TemplateArgsHashValue.emplace(); @@ -1788,7 +1787,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); } @@ -1891,23 +1890,10 @@ namespace { TemplateParameterList *OrigTPL) { if (!OrigTPL || !OrigTPL->size()) return OrigTPL; - std::optional<MultiLevelTemplateArgumentList> OldMLTAL; - // We need to preserve the lambda depth in parameter mapping. - // Otherwise the template argument deduction would fail, if we reduced the - // depth too early. - if (SemaRef.inParameterMappingSubstitution()) - OldMLTAL = ForgetSubstitution(); - DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext(); TemplateDeclInstantiator DeclInstantiator(getSema(), Owner, TemplateArgs); - // We don't want the template argument substitution into parameter - // mappings to preserve the outer depths. - DeclInstantiator.setEvaluateConstraints( - SemaRef.inConstraintSubstitution() || EvaluateConstraints); - auto *Transformed = DeclInstantiator.SubstTemplateParams(OrigTPL); - if (OldMLTAL) - RememberSubstitution(std::move(*OldMLTAL)); - return Transformed; + DeclInstantiator.setEvaluateConstraints(EvaluateConstraints); + return DeclInstantiator.SubstTemplateParams(OrigTPL); } concepts::TypeRequirement * @@ -2202,6 +2188,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 @@ -2610,8 +2599,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; @@ -4451,10 +4442,10 @@ bool Sema::SubstTemplateArguments( bool Sema::SubstTemplateArgumentsInParameterMapping( ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc, const MultiLevelTemplateArgumentList &TemplateArgs, - bool TransformLambdaConstraint, TemplateArgumentListInfo &Out) { + TemplateArgumentListInfo &Out) { TemplateInstantiator Instantiator( TemplateInstantiator::ForParameterMappingSubstitution, *this, BaseLoc, - TransformLambdaConstraint, TemplateArgs); + TemplateArgs); return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out); } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index f0593e9bb1788..2461c5ae700da 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> @@ -2547,7 +2548,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); @@ -3577,9 +3578,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()); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 07f2dcf496f4b..0069a62ce184e 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -16104,12 +16104,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { return E; TRC.ConstraintExpr = E.get(); } - // If the concept refers to any outer parameter packs, we track the - // SubstIndex for evaluation. - // FIXME: This seems unnecessary after transforming lambda constraints. - if (TRC && TRC.ConstraintExpr->containsUnexpandedParameterPack() && - !TRC.ArgPackSubstIndex) - TRC.ArgPackSubstIndex = SemaRef.ArgPackSubstIndex; getSema().CompleteLambdaCallOperator( NewCallOperator, E->getCallOperator()->getLocation(), diff --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp index 19c0769833480..2010a028fce4a 100644 --- a/clang/test/SemaTemplate/concepts-lambda.cpp +++ b/clang/test/SemaTemplate/concepts-lambda.cpp @@ -467,4 +467,29 @@ static_assert(count_if_v_bad<L2, double> == 1); // 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); + } >From 7fd35dcdf0e6ef3d4fbae7d42184836da2bd154f Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Fri, 10 Jul 2026 15:39:27 +0800 Subject: [PATCH 5/7] revert format changes --- clang/lib/Sema/SemaTemplateInstantiate.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 15acc6bf9caee..3fede2b7c1d2f 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1891,7 +1891,9 @@ namespace { if (!OrigTPL || !OrigTPL->size()) return OrigTPL; DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext(); - TemplateDeclInstantiator DeclInstantiator(getSema(), Owner, TemplateArgs); + TemplateDeclInstantiator DeclInstantiator(getSema(), + /* DeclContext *Owner */ Owner, + TemplateArgs); DeclInstantiator.setEvaluateConstraints(EvaluateConstraints); return DeclInstantiator.SubstTemplateParams(OrigTPL); } >From c96ec40de0a8edecca8e72c2308c13ca039109c5 Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Fri, 10 Jul 2026 19:27:42 +0800 Subject: [PATCH 6/7] [Clang] Ensure correct template parameter depth for abbreviated templates --- clang/test/SemaTemplate/concepts-lambda.cpp | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp index 2010a028fce4a..f5eed4d993c27 100644 --- a/clang/test/SemaTemplate/concepts-lambda.cpp +++ b/clang/test/SemaTemplate/concepts-lambda.cpp @@ -403,6 +403,43 @@ void test() { } } +namespace GH200682 { + +template <typename... Args> struct A { + template <auto F> constexpr static bool B = F(42); +}; + +template <typename T> +concept c = true; + +template <typename T> +concept d = false; + +void f(auto &&...args) + requires( + A<decltype(args)...>::template B<[](auto x) { return c<decltype(x)>; }>); + +template <class> +void g(auto &&...args) + requires( + A<decltype(args)...>::template B<[](auto x) { return c<decltype(x)>; }>); + +void h(auto &&...args) // expected-note {{constraints not satisfied}} + requires( + A<decltype(args)...>::template B<[](auto x) { // expected-note {{evaluated to false}} + return d<decltype(x)>; + }>); + +void main() { + f(); + g<int>(); + + h(42); + // expected-error@-1 {{no matching}} +} + +} + namespace GH199209 { template <class T> auto declval() -> T &&; >From 1bb3570a49cabb51baeccc5afebb8628de3ab7f6 Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Fri, 10 Jul 2026 23:05:18 +0800 Subject: [PATCH 7/7] Address feedback --- clang/lib/Sema/SemaConcept.cpp | 5 ++- clang/test/SemaTemplate/concepts-lambda.cpp | 37 --------------------- 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 4d030d5115fc1..476910a9db528 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1006,7 +1006,10 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc); - if (ConceptId == TopLevelConceptId) { + // 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, diff --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp index f5eed4d993c27..2010a028fce4a 100644 --- a/clang/test/SemaTemplate/concepts-lambda.cpp +++ b/clang/test/SemaTemplate/concepts-lambda.cpp @@ -403,43 +403,6 @@ void test() { } } -namespace GH200682 { - -template <typename... Args> struct A { - template <auto F> constexpr static bool B = F(42); -}; - -template <typename T> -concept c = true; - -template <typename T> -concept d = false; - -void f(auto &&...args) - requires( - A<decltype(args)...>::template B<[](auto x) { return c<decltype(x)>; }>); - -template <class> -void g(auto &&...args) - requires( - A<decltype(args)...>::template B<[](auto x) { return c<decltype(x)>; }>); - -void h(auto &&...args) // expected-note {{constraints not satisfied}} - requires( - A<decltype(args)...>::template B<[](auto x) { // expected-note {{evaluated to false}} - return d<decltype(x)>; - }>); - -void main() { - f(); - g<int>(); - - h(42); - // expected-error@-1 {{no matching}} -} - -} - namespace GH199209 { template <class T> auto declval() -> T &&; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
