[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-10 Thread Younan Zhang via cfe-commits

https://github.com/zyn0217 closed 
https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-10 Thread LLVM Continuous Integration via cfe-commits

llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `clang-aarch64-global-isel` 
running on `linaro-clang-aarch64-global-isel` while building `clang` at step 14 
"test-suite".

Full details are available at: 
https://lab.llvm.org/buildbot/#/builders/125/builds/14554


Here is the relevant piece of the build log for the reference

```
Step 14 (test-suite) failure: test (failure)
 TEST 'test-suite :: tools/test/alloc_mem.test' FAILED 


/home/tcwg-buildbot/worker/clang-aarch64-global-isel/test/sandbox/build/tools/timeit-target
 --timeout 7200 --limit-core 0 --limit-cpu 7200 --limit-file-size 209715200 
--limit-rss-size 838860800 --redirect-input /dev/null --summary 
/home/tcwg-buildbot/worker/clang-aarch64-global-isel/test/sandbox/build/tools/test/Output/alloc_mem.test.time
 /usr/bin/python3.10 
/home/tcwg-buildbot/worker/clang-aarch64-global-isel/test/sandbox/build/tools/test/test_maxrss.py
 
/home/tcwg-buildbot/worker/clang-aarch64-global-isel/test/sandbox/build/tools/timeit-target
 
/home/tcwg-buildbot/worker/clang-aarch64-global-isel/test/sandbox/build/tools/test/alloc_mem
maxrss too low: 132120576 < 134217728

+ 
/home/tcwg-buildbot/worker/clang-aarch64-global-isel/test/sandbox/build/tools/timeit-target
 --timeout 7200 --limit-core 0 --limit-cpu 7200 --limit-file-size 209715200 
--limit-rss-size 838860800 --redirect-input /dev/null --summary 
/home/tcwg-buildbot/worker/clang-aarch64-global-isel/test/sandbox/build/tools/test/Output/alloc_mem.test.time
 /usr/bin/python3.10 
/home/tcwg-buildbot/worker/clang-aarch64-global-isel/test/sandbox/build/tools/test/test_maxrss.py
 
/home/tcwg-buildbot/worker/clang-aarch64-global-isel/test/sandbox/build/tools/timeit-target
 
/home/tcwg-buildbot/worker/clang-aarch64-global-isel/test/sandbox/build/tools/test/alloc_mem




```



https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-09 Thread Younan Zhang via cfe-commits

https://github.com/zyn0217 edited 
https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-09 Thread Matheus Izvekov via cfe-commits


@@ -1876,11 +1901,25 @@ namespace {
   TemplateParameterList *OrigTPL)  {
   if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
 
+  std::optional 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() &&
+  getDepthAndIndex(OrigTPL->getParam(0)).first >=
+  TemplateArgs.getNumSubstitutedLevels())
+OldMLTAL = ForgetSubstitution();

mizvekov wrote:

Okay, I think I understand now what you are trying to do. I initially 
misunderstood the problem as you trying to avoid getting the lambda's depth to 
go below zero.

I am not sure I understand why changing the depth is a problem for deduction, 
because deduction accepts the template depth as a parameter and this should be 
easy to figure out for the lambda since it has template parameters.

Anyway, you can use the `getDepth` method on the template parameter list 
directly:
```suggestion
  std::optional 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();
```


https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-09 Thread via cfe-commits

github-actions[bot] wrote:




:warning: C/C++ code formatter, clang-format found issues in your code. 
:warning:



You can test this locally with the following command:


``bash
git-clang-format --diff origin/main HEAD --extensions h,cpp -- 
clang/lib/Parse/ParseTemplate.cpp clang/lib/Sema/SemaConcept.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 --diff_from_common_commit
``

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:





View the diff from clang-format here.


``diff
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 97b4f709d..444795c3b 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16069,8 +16069,8 @@ TreeTransform::TransformLambdaExpr(LambdaExpr 
*E) {
 
   AssociatedConstraint TRC = E->getCallOperator()->getTrailingRequiresClause();
   if (TRC) {
-ExprResult E =
-getDerived().TransformConstraint(const_cast(TRC.ConstraintExpr));
+ExprResult E = getDerived().TransformConstraint(
+const_cast(TRC.ConstraintExpr));
 if (E.isInvalid())
   return E;
 TRC.ConstraintExpr = E.get();

``




https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-09 Thread Matheus Izvekov via cfe-commits

https://github.com/mizvekov approved this pull request.

Thanks, this looks better!

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-09 Thread Younan Zhang via cfe-commits

https://github.com/zyn0217 updated 
https://github.com/llvm/llvm-project/pull/195995

>From 6271200c059ff757333247e286b85538735ffe74 Mon Sep 17 00:00:00 2001
From: Younan Zhang 
Date: Wed, 6 May 2026 12:04:13 +0800
Subject: [PATCH 1/3] [Clang] Transform lambda's constraints when instantiating
 parameter mapping

This way we can remove a few workarounds of lambda expressions where
outer template arguments of concepts have to be preserved through
ImplicitConceptSpecializationDecls.
---
 clang/lib/Parse/ParseTemplate.cpp |  6 ---
 clang/lib/Sema/SemaConcept.cpp| 30 
 clang/lib/Sema/SemaTemplate.cpp   |  2 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp  | 14 --
 clang/lib/Sema/SemaTemplateInstantiate.cpp| 47 +--
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  7 +++
 clang/lib/Sema/TreeTransform.h| 12 ++---
 clang/test/SemaTemplate/concepts-lambda.cpp   | 34 +-
 8 files changed, 97 insertions(+), 55 deletions(-)

diff --git a/clang/lib/Parse/ParseTemplate.cpp 
b/clang/lib/Parse/ParseTemplate.cpp
index 330a9c6aea0c5..dbc7cbc6cdc0c 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 3f04922a5647e..8bd3a8f6e1a45 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -482,11 +482,6 @@ class ConstraintSatisfactionChecker {
   ConstraintSatisfaction &Satisfaction;
   bool BuildExpression;
 
-  // 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;
-
   // This is for TemplateInstantiator to not instantiate the same template
   // parameter mapping many times, in order to improve substitution 
performance.
   llvm::DenseMap
@@ -730,20 +725,6 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
 return ExprEmpty();
   }
 
-  // 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) {
-// 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(
   Constraint.getConstraintExpr(), *SubstitutedArgs);
@@ -1052,9 +1033,6 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
   if (InstTemplate.isInvalid())
 return ExprError();
 
-  llvm::SaveAndRestore PushConceptDecl(
-  ParentConcept, cast(ConceptId->getNamedConcept()));
-
   unsigned Size = Satisfaction.Details.size();
 
   ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL);
@@ -2291,6 +2269,14 @@ bool 
SubstituteParameterMappings::substitute(NormalizedConstraint &N) {
 }
 assert(!ArgsAsWritten);
 const ConceptSpecializationExpr *CSE = CC.getConceptSpecializationExpr();
+// This is to make sure that lambdas within template arguments live in a
+// dependent context such that they are assured to be transformed in
+// evaluation.
+EnterExpressionEvaluationContext EECtx(
+SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated,
+/*LambdaContextDecl=*/
+const_cast(
+CSE->getSpecializationDecl()));
 SmallVector InnerArgs(CSE->getTemplateArguments());
 ConceptDecl *Concept = CSE->getNamedConcept();
 if (RemovePacksForFoldExpr) {
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c436b7018a2bd..6b7dadcf123a7 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4912,7 +4912,7 @@ ExprResult Sema::CheckConceptTemplateId(
 LocalInstantiationScope Scope(*this);
 
 EnterExpression

[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-09 Thread Younan Zhang via cfe-commits


@@ -1876,11 +1901,25 @@ namespace {
   TemplateParameterList *OrigTPL)  {
   if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
 
+  std::optional 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() &&
+  getDepthAndIndex(OrigTPL->getParam(0)).first >=
+  TemplateArgs.getNumSubstitutedLevels())
+OldMLTAL = ForgetSubstitution();

zyn0217 wrote:

> I am not sure I understand why changing the depth is a problem for deduction, 
> because deduction accepts the template depth as a parameter and this should 
> be easy to figure out for the lambda since it has template parameters.

It's all about the collection of explicit template arguments: 
`PackDeductionScope::finishConstruction` requires a depth-level-match of 
template parameters.

And I don't think we can add template depth to deduction to the code path that 
performs a deduction on a function call? we seem to always assume their depths 
are 0.

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-09 Thread Younan Zhang via cfe-commits

https://github.com/zyn0217 updated 
https://github.com/llvm/llvm-project/pull/195995

>From 6271200c059ff757333247e286b85538735ffe74 Mon Sep 17 00:00:00 2001
From: Younan Zhang 
Date: Wed, 6 May 2026 12:04:13 +0800
Subject: [PATCH 1/3] [Clang] Transform lambda's constraints when instantiating
 parameter mapping

This way we can remove a few workarounds of lambda expressions where
outer template arguments of concepts have to be preserved through
ImplicitConceptSpecializationDecls.
---
 clang/lib/Parse/ParseTemplate.cpp |  6 ---
 clang/lib/Sema/SemaConcept.cpp| 30 
 clang/lib/Sema/SemaTemplate.cpp   |  2 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp  | 14 --
 clang/lib/Sema/SemaTemplateInstantiate.cpp| 47 +--
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  7 +++
 clang/lib/Sema/TreeTransform.h| 12 ++---
 clang/test/SemaTemplate/concepts-lambda.cpp   | 34 +-
 8 files changed, 97 insertions(+), 55 deletions(-)

diff --git a/clang/lib/Parse/ParseTemplate.cpp 
b/clang/lib/Parse/ParseTemplate.cpp
index 330a9c6aea0c5..dbc7cbc6cdc0c 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 3f04922a5647e..8bd3a8f6e1a45 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -482,11 +482,6 @@ class ConstraintSatisfactionChecker {
   ConstraintSatisfaction &Satisfaction;
   bool BuildExpression;
 
-  // 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;
-
   // This is for TemplateInstantiator to not instantiate the same template
   // parameter mapping many times, in order to improve substitution 
performance.
   llvm::DenseMap
@@ -730,20 +725,6 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
 return ExprEmpty();
   }
 
-  // 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) {
-// 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(
   Constraint.getConstraintExpr(), *SubstitutedArgs);
@@ -1052,9 +1033,6 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
   if (InstTemplate.isInvalid())
 return ExprError();
 
-  llvm::SaveAndRestore PushConceptDecl(
-  ParentConcept, cast(ConceptId->getNamedConcept()));
-
   unsigned Size = Satisfaction.Details.size();
 
   ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL);
@@ -2291,6 +2269,14 @@ bool 
SubstituteParameterMappings::substitute(NormalizedConstraint &N) {
 }
 assert(!ArgsAsWritten);
 const ConceptSpecializationExpr *CSE = CC.getConceptSpecializationExpr();
+// This is to make sure that lambdas within template arguments live in a
+// dependent context such that they are assured to be transformed in
+// evaluation.
+EnterExpressionEvaluationContext EECtx(
+SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated,
+/*LambdaContextDecl=*/
+const_cast(
+CSE->getSpecializationDecl()));
 SmallVector InnerArgs(CSE->getTemplateArguments());
 ConceptDecl *Concept = CSE->getNamedConcept();
 if (RemovePacksForFoldExpr) {
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c436b7018a2bd..6b7dadcf123a7 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4912,7 +4912,7 @@ ExprResult Sema::CheckConceptTemplateId(
 LocalInstantiationScope Scope(*this);
 
 EnterExpression

[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-09 Thread Younan Zhang via cfe-commits

https://github.com/zyn0217 updated 
https://github.com/llvm/llvm-project/pull/195995

>From 6271200c059ff757333247e286b85538735ffe74 Mon Sep 17 00:00:00 2001
From: Younan Zhang 
Date: Wed, 6 May 2026 12:04:13 +0800
Subject: [PATCH 1/3] [Clang] Transform lambda's constraints when instantiating
 parameter mapping

This way we can remove a few workarounds of lambda expressions where
outer template arguments of concepts have to be preserved through
ImplicitConceptSpecializationDecls.
---
 clang/lib/Parse/ParseTemplate.cpp |  6 ---
 clang/lib/Sema/SemaConcept.cpp| 30 
 clang/lib/Sema/SemaTemplate.cpp   |  2 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp  | 14 --
 clang/lib/Sema/SemaTemplateInstantiate.cpp| 47 +--
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  7 +++
 clang/lib/Sema/TreeTransform.h| 12 ++---
 clang/test/SemaTemplate/concepts-lambda.cpp   | 34 +-
 8 files changed, 97 insertions(+), 55 deletions(-)

diff --git a/clang/lib/Parse/ParseTemplate.cpp 
b/clang/lib/Parse/ParseTemplate.cpp
index 330a9c6aea0c5..dbc7cbc6cdc0c 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 3f04922a5647e..8bd3a8f6e1a45 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -482,11 +482,6 @@ class ConstraintSatisfactionChecker {
   ConstraintSatisfaction &Satisfaction;
   bool BuildExpression;
 
-  // 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;
-
   // This is for TemplateInstantiator to not instantiate the same template
   // parameter mapping many times, in order to improve substitution 
performance.
   llvm::DenseMap
@@ -730,20 +725,6 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
 return ExprEmpty();
   }
 
-  // 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) {
-// 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(
   Constraint.getConstraintExpr(), *SubstitutedArgs);
@@ -1052,9 +1033,6 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
   if (InstTemplate.isInvalid())
 return ExprError();
 
-  llvm::SaveAndRestore PushConceptDecl(
-  ParentConcept, cast(ConceptId->getNamedConcept()));
-
   unsigned Size = Satisfaction.Details.size();
 
   ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL);
@@ -2291,6 +2269,14 @@ bool 
SubstituteParameterMappings::substitute(NormalizedConstraint &N) {
 }
 assert(!ArgsAsWritten);
 const ConceptSpecializationExpr *CSE = CC.getConceptSpecializationExpr();
+// This is to make sure that lambdas within template arguments live in a
+// dependent context such that they are assured to be transformed in
+// evaluation.
+EnterExpressionEvaluationContext EECtx(
+SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated,
+/*LambdaContextDecl=*/
+const_cast(
+CSE->getSpecializationDecl()));
 SmallVector InnerArgs(CSE->getTemplateArguments());
 ConceptDecl *Concept = CSE->getNamedConcept();
 if (RemovePacksForFoldExpr) {
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c436b7018a2bd..6b7dadcf123a7 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4912,7 +4912,7 @@ ExprResult Sema::CheckConceptTemplateId(
 LocalInstantiationScope Scope(*this);
 
 EnterExpression

[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-08 Thread Younan Zhang via cfe-commits

https://github.com/zyn0217 edited 
https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-08 Thread Younan Zhang via cfe-commits

https://github.com/zyn0217 edited 
https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-08 Thread Younan Zhang via cfe-commits


@@ -1876,11 +1901,25 @@ namespace {
   TemplateParameterList *OrigTPL)  {
   if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
 
+  std::optional 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() &&
+  getDepthAndIndex(OrigTPL->getParam(0)).first >=
+  TemplateArgs.getNumSubstitutedLevels())
+OldMLTAL = ForgetSubstitution();

zyn0217 wrote:

Hmm, isn't that what I am doing now? 
`getDepthAndIndex(OrigTPL->getParam(0)).first >= 
TemplateArgs.getNumSubstitutedLevels()` so the outer levels == the whole thing?

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-08 Thread Younan Zhang via cfe-commits


@@ -1876,11 +1901,25 @@ namespace {
   TemplateParameterList *OrigTPL)  {
   if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
 
+  std::optional 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() &&
+  getDepthAndIndex(OrigTPL->getParam(0)).first >=
+  TemplateArgs.getNumSubstitutedLevels())
+OldMLTAL = ForgetSubstitution();

zyn0217 wrote:

the decl instantiator reduces the depth by getNumSubstitutedLevels(), I don't 
know how we can clamp MLTAL without forgetting the outer template arguments?

Are you concerning about associated type constraints getting untransformed, I 
guess?

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-08 Thread Younan Zhang via cfe-commits

https://github.com/zyn0217 edited 
https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-08 Thread Younan Zhang via cfe-commits


@@ -1876,11 +1901,25 @@ namespace {
   TemplateParameterList *OrigTPL)  {
   if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
 
+  std::optional 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() &&
+  getDepthAndIndex(OrigTPL->getParam(0)).first >=
+  TemplateArgs.getNumSubstitutedLevels())
+OldMLTAL = ForgetSubstitution();

zyn0217 wrote:

> Also, I don't see how the level check is necessary. 

@mizvekov

The fact is that we will transform the lambda expression in both building and 
substitution of parameter mapping.

Say we have

```cpp
template
concept PredicateFor_bad = pass_a_concept_inside_a_lambda<[]
  
requires(__is_same(decltype(Pred.template operator()()), bool) and ...)
  {},
  Ts...>;
```

When building, we only have outer template arguments `>`, and 
since the transform of parameter list will reduce the depth of  anyway 
if only partial template arguments are provided, depth of  will be 
converted to 0.

And in the second transform where we started to substitute real template 
arguments into parameter mapping (i.e. when we have `>`), the depth of `Xs` is reduced before deduction happens. This 
results in a -1 depth during argument deduction and thus we won't have a 
correct template argument list when checking the inner constraint.

Note that we didn't transform the lambda expression in the second transfrom, 
because the lambda was seen as non-dependent. I do want the lambda to have a 
second transform, because otherwise we don't have a way to get substituted 
`Pred` in constraint evaluation. (`Pred` is normalized and we have to evaluate 
the lambda's inner constraint during parameter mapping substitution, when we 
dont have any proper template arguments at that moment, which is an 
egg-and-chicken problem though)

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-08 Thread Younan Zhang via cfe-commits


@@ -1763,9 +1763,34 @@ namespace {
   if (TA.isDependent())
 return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
   }
+  if (auto *CD = dyn_cast_if_present(
+  LSI->Lambda->getLambdaContextDecl())) {
+if (llvm::any_of(CD->getTemplateArguments(),
+ [](const auto &TA) { return TA.isDependent(); }))
+  return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
+  }
   return inherited::ComputeLambdaDependency(LSI);
 }
 
+AssociatedConstraint TransformConstraint(AssociatedConstraint AC) {
+  // If the concept refers to any outer parameter packs, we track the
+  // SubstIndex for evaluation.
+  if (AC && AC.ConstraintExpr->containsUnexpandedParameterPack() &&
+  !AC.ArgPackSubstIndex)
+AC.ArgPackSubstIndex = SemaRef.ArgPackSubstIndex;

zyn0217 wrote:

I made the default behavior no-op in TreeTransform. We want the transfrom of 
constraints in template instantiator.


https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-08 Thread Younan Zhang via cfe-commits


@@ -1876,11 +1901,25 @@ namespace {
   TemplateParameterList *OrigTPL)  {
   if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
 
+  std::optional 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() &&
+  getDepthAndIndex(OrigTPL->getParam(0)).first >=
+  TemplateArgs.getNumSubstitutedLevels())
+OldMLTAL = ForgetSubstitution();

zyn0217 wrote:

> Would look cleaner if this used ForgetSubstitutionRAII.

Oh thanks, I wasn't aware of that

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-08 Thread Matheus Izvekov via cfe-commits


@@ -1876,11 +1901,25 @@ namespace {
   TemplateParameterList *OrigTPL)  {
   if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
 
+  std::optional 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() &&
+  getDepthAndIndex(OrigTPL->getParam(0)).first >=
+  TemplateArgs.getNumSubstitutedLevels())
+OldMLTAL = ForgetSubstitution();

mizvekov wrote:

I am talking about forgetting just the outer levels, instead of the whole 
thing, yes.

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-08 Thread Younan Zhang via cfe-commits

https://github.com/zyn0217 edited 
https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-08 Thread Younan Zhang via cfe-commits

https://github.com/zyn0217 edited 
https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-07 Thread Matheus Izvekov via cfe-commits


@@ -1876,11 +1901,25 @@ namespace {
   TemplateParameterList *OrigTPL)  {
   if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
 
+  std::optional 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() &&
+  getDepthAndIndex(OrigTPL->getParam(0)).first >=
+  TemplateArgs.getNumSubstitutedLevels())
+OldMLTAL = ForgetSubstitution();

mizvekov wrote:

Would look cleaner if this used `ForgetSubstitutionRAII`.

Also, I don't see how the level check is necessary. The comment doesn't explain 
it either.

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-07 Thread Matheus Izvekov via cfe-commits


@@ -1763,9 +1763,34 @@ namespace {
   if (TA.isDependent())
 return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
   }
+  if (auto *CD = dyn_cast_if_present(
+  LSI->Lambda->getLambdaContextDecl())) {
+if (llvm::any_of(CD->getTemplateArguments(),
+ [](const auto &TA) { return TA.isDependent(); }))
+  return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
+  }
   return inherited::ComputeLambdaDependency(LSI);
 }
 
+AssociatedConstraint TransformConstraint(AssociatedConstraint AC) {
+  // If the concept refers to any outer parameter packs, we track the
+  // SubstIndex for evaluation.
+  if (AC && AC.ConstraintExpr->containsUnexpandedParameterPack() &&
+  !AC.ArgPackSubstIndex)
+AC.ArgPackSubstIndex = SemaRef.ArgPackSubstIndex;

mizvekov wrote:

As far as I remember when I implemented this, this `ArgPackSubstIndex` should 
track what index was used to expand this constraint, so I think it only makes 
sense to change this when the constraint is actually transformed.

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-07 Thread Matheus Izvekov via cfe-commits


@@ -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;

mizvekov wrote:

Yeah, this certainly looked wrong, this is not a place in the grammar you would 
see an increase in template depth, so props for getting rid of this.

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-07 Thread Younan Zhang via cfe-commits

https://github.com/zyn0217 updated 
https://github.com/llvm/llvm-project/pull/195995

>From 6271200c059ff757333247e286b85538735ffe74 Mon Sep 17 00:00:00 2001
From: Younan Zhang 
Date: Wed, 6 May 2026 12:04:13 +0800
Subject: [PATCH 1/2] [Clang] Transform lambda's constraints when instantiating
 parameter mapping

This way we can remove a few workarounds of lambda expressions where
outer template arguments of concepts have to be preserved through
ImplicitConceptSpecializationDecls.
---
 clang/lib/Parse/ParseTemplate.cpp |  6 ---
 clang/lib/Sema/SemaConcept.cpp| 30 
 clang/lib/Sema/SemaTemplate.cpp   |  2 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp  | 14 --
 clang/lib/Sema/SemaTemplateInstantiate.cpp| 47 +--
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  7 +++
 clang/lib/Sema/TreeTransform.h| 12 ++---
 clang/test/SemaTemplate/concepts-lambda.cpp   | 34 +-
 8 files changed, 97 insertions(+), 55 deletions(-)

diff --git a/clang/lib/Parse/ParseTemplate.cpp 
b/clang/lib/Parse/ParseTemplate.cpp
index 330a9c6aea0c5..dbc7cbc6cdc0c 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 3f04922a5647e..8bd3a8f6e1a45 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -482,11 +482,6 @@ class ConstraintSatisfactionChecker {
   ConstraintSatisfaction &Satisfaction;
   bool BuildExpression;
 
-  // 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;
-
   // This is for TemplateInstantiator to not instantiate the same template
   // parameter mapping many times, in order to improve substitution 
performance.
   llvm::DenseMap
@@ -730,20 +725,6 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
 return ExprEmpty();
   }
 
-  // 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) {
-// 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(
   Constraint.getConstraintExpr(), *SubstitutedArgs);
@@ -1052,9 +1033,6 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
   if (InstTemplate.isInvalid())
 return ExprError();
 
-  llvm::SaveAndRestore PushConceptDecl(
-  ParentConcept, cast(ConceptId->getNamedConcept()));
-
   unsigned Size = Satisfaction.Details.size();
 
   ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL);
@@ -2291,6 +2269,14 @@ bool 
SubstituteParameterMappings::substitute(NormalizedConstraint &N) {
 }
 assert(!ArgsAsWritten);
 const ConceptSpecializationExpr *CSE = CC.getConceptSpecializationExpr();
+// This is to make sure that lambdas within template arguments live in a
+// dependent context such that they are assured to be transformed in
+// evaluation.
+EnterExpressionEvaluationContext EECtx(
+SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated,
+/*LambdaContextDecl=*/
+const_cast(
+CSE->getSpecializationDecl()));
 SmallVector InnerArgs(CSE->getTemplateArguments());
 ConceptDecl *Concept = CSE->getNamedConcept();
 if (RemovePacksForFoldExpr) {
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c436b7018a2bd..6b7dadcf123a7 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4912,7 +4912,7 @@ ExprResult Sema::CheckConceptTemplateId(
 LocalInstantiationScope Scope(*this);
 
 EnterExpression

[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-06 Thread Erich Keane via cfe-commits

https://github.com/erichkeane approved this pull request.

Not sure I am completely understanding the consequences of this, but I think 
this makes sense?  I'd love to see @mizvekov take a look though if possible 
before merging.

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-06 Thread Corentin Jabot via cfe-commits


@@ -2312,6 +2269,14 @@ bool 
SubstituteParameterMappings::substitute(NormalizedConstraint &N) {
 }
 assert(!ArgsAsWritten);
 const ConceptSpecializationExpr *CSE = CC.getConceptSpecializationExpr();
+// This is to make sure that lambdas within template arguments live in a
+// dependent context such that they are assured to be transformed in
+// evaluation.

cor3ntin wrote:

```suggestion
// Make sure that lambdas within template arguments live in a
// dependent context such that they are assured to be transformed during 
constraint
// evaluation.
```

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-06 Thread Corentin Jabot via cfe-commits

https://github.com/cor3ntin edited 
https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] Transform lambda's constraints when instantiating parameter mapping (PR #195995)

2026-05-06 Thread Corentin Jabot via cfe-commits

https://github.com/cor3ntin commented:

Thanks.
I think i need more time to think about that but it looks cleaner than the 
previous approach

https://github.com/llvm/llvm-project/pull/195995
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits