https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/184319
>From 7afab57a768587f3d10f897eac88cb1c2df14080 Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Tue, 3 Mar 2026 18:48:12 +0800 Subject: [PATCH 1/2] [Clang] Fix the lambda context for constraint evaluation Constraint lambdas in the requires body need complete template arguments before they can be evaluated. That was connected by ImplicitConceptSpecializationDecl which is no longer created naturally after the normalization patch. This patch fixes the bug by creating a temporary decl for that purpose. Though the temporary object should go away once we have the evaluation context track template arguments. No release note for being a regression fix. --- clang/lib/Sema/SemaConcept.cpp | 22 ++++++++++++++++- clang/test/SemaTemplate/concepts.cpp | 37 ++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 38791940247cb..09f8d1b4adde9 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -477,6 +477,8 @@ class ConstraintSatisfactionChecker { ConstraintSatisfaction &Satisfaction; bool BuildExpression; + const ConceptReference *ParentConcept = nullptr; + private: ExprResult EvaluateAtomicConstraint(const Expr *AtomicExpr, @@ -693,7 +695,8 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( ExprResult ConstraintSatisfactionChecker::EvaluateSlow( const AtomicConstraint &Constraint, const MultiLevelTemplateArgumentList &MLTAL) { - EnterExpressionEvaluationContext ConstantEvaluated( + std::optional<EnterExpressionEvaluationContext> EvaluationContext; + EvaluationContext.emplace( S, Sema::ExpressionEvaluationContext::ConstantEvaluated, Sema::ReuseLambdaContextDecl); @@ -705,6 +708,21 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( return ExprEmpty(); } + // This is dumb: generic lambdas inside requires body require a lambda context + // decl from which to fetch correct template arguments. But we don't have any + // proper decls because the constraints are already normalized. + if (ParentConcept && SubstitutedArgs->getNumSubstitutedLevels()) { + auto *CD = ParentConcept->getNamedConcept(); + // 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, CD->getDeclContext(), CD->getBeginLoc(), + SubstitutedArgs->getOutermost())); + } + Sema::ArgPackSubstIndexRAII SubstIndex(S, PackSubstitutionIndex); ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint( Constraint.getConstraintExpr(), *SubstitutedArgs); @@ -1027,6 +1045,8 @@ ExprResult ConstraintSatisfactionChecker::Evaluate( if (InstTemplate.isInvalid()) return ExprError(); + llvm::SaveAndRestore PushConceptDecl(ParentConcept, ConceptId); + unsigned Size = Satisfaction.Details.size(); ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL); diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index 1d2ada3e0a398..321b69f79f2c8 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -1704,6 +1704,43 @@ struct ice_point : relative_point_origin<point<kelvin>> {}; } +namespace GH184047 { + +template <typename T, int N> +concept decomposable = requires { + []<template <typename...> class U, typename... Args> // #decomposable_lambda + requires(sizeof...(Args) >= N) + (U<Args...>*) {}(static_cast<T*>(nullptr)); +}; + +template<typename T> +struct foo {}; + +template<typename T> +concept decomposable_fails = decomposable<T, 2>; // #decomposable_fails + +template<typename T> +concept decomposable_works = requires { + requires decomposable<T, 1>; +}; + +static_assert(decomposable<foo<int>, 1>); + +static_assert(decomposable<foo<int>, 200>); +// expected-error@-1 {{static assertion failed}} +// expected-note@-2 {{evaluated to false}} +// expected-note@#decomposable_lambda {{invalid}} + +static_assert(decomposable_works<foo<int>>); + +static_assert(decomposable_fails<foo<int>>); +// expected-error@-1 {{static assertion failed}} +// expected-note@-2 {{'foo<int>' does not satisfy 'decomposable_fails'}} +// expected-note@#decomposable_fails {{evaluated to false}} +// expected-note@#decomposable_lambda {{invalid}} + +} + namespace GH182344 { template <typename T> >From 67b624845eaa90eec33f4775d8cef167724237ab Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Wed, 4 Mar 2026 12:14:39 +0800 Subject: [PATCH 2/2] Address feedback --- clang/lib/Sema/SemaConcept.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 09f8d1b4adde9..b4462c944a4ce 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -477,7 +477,10 @@ class ConstraintSatisfactionChecker { ConstraintSatisfaction &Satisfaction; bool BuildExpression; - const ConceptReference *ParentConcept = nullptr; + // The most closest concept declaration when evaluating atomic constriants. + // This is to make sure that lambdas in the atomic expression live in the + // right context. + ConceptDecl *ParentConcept = nullptr; private: ExprResult @@ -623,8 +626,11 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( const MultiLevelTemplateArgumentList &MLTAL, llvm::SmallVector<TemplateArgument> &SubstitutedOutermost) { - if (!Constraint.hasParameterMapping()) - return std::move(MLTAL); + if (!Constraint.hasParameterMapping()) { + if (MLTAL.getNumSubstitutedLevels()) + SubstitutedOutermost.assign(MLTAL.getOutermost()); + return MLTAL; + } // The mapping is empty, meaning no template arguments are needed for // evaluation. @@ -708,19 +714,18 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( return ExprEmpty(); } - // This is dumb: generic lambdas inside requires body require a lambda context + // Note that generic lambdas inside requires body require a lambda context // decl from which to fetch correct template arguments. But we don't have any // proper decls because the constraints are already normalized. - if (ParentConcept && SubstitutedArgs->getNumSubstitutedLevels()) { - auto *CD = ParentConcept->getNamedConcept(); + if (ParentConcept) { // 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, CD->getDeclContext(), CD->getBeginLoc(), - SubstitutedArgs->getOutermost())); + S.Context, ParentConcept->getDeclContext(), + ParentConcept->getBeginLoc(), SubstitutedOutermost)); } Sema::ArgPackSubstIndexRAII SubstIndex(S, PackSubstitutionIndex); @@ -1045,7 +1050,8 @@ ExprResult ConstraintSatisfactionChecker::Evaluate( if (InstTemplate.isInvalid()) return ExprError(); - llvm::SaveAndRestore PushConceptDecl(ParentConcept, ConceptId); + llvm::SaveAndRestore PushConceptDecl( + ParentConcept, cast<ConceptDecl>(ConceptId->getNamedConcept())); unsigned Size = Satisfaction.Details.size(); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
