[clang] Revert "[Clang] Transform lambda's constraints when instantiating parameter mapping (#195995)" (PR #199228)
via cfe-commits
cfe-commits at lists.llvm.org
Fri May 22 09:19:26 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Alexander Kornienko (alexfh)
<details>
<summary>Changes</summary>
This reverts commit 7e2821e025f8ee4add31693ddf462947d7618016.
---
Full diff: https://github.com/llvm/llvm-project/pull/199228.diff
8 Files Affected:
- (modified) clang/lib/Parse/ParseTemplate.cpp (+6)
- (modified) clang/lib/Sema/SemaConcept.cpp (+13-9)
- (modified) clang/lib/Sema/SemaTemplate.cpp (+1-1)
- (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+14)
- (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+4-32)
- (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (-7)
- (modified) clang/lib/Sema/TreeTransform.h (+2-12)
- (modified) clang/test/SemaTemplate/concepts-lambda.cpp (+3-33)
``````````diff
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index dbc7cbc6cdc0c..330a9c6aea0c5 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 ac1c716b5c385..2b4d2b4d9f8db 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -732,8 +732,17 @@ 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(
@@ -1043,6 +1052,9 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
if (InstTemplate.isInvalid())
return ExprError();
+ llvm::SaveAndRestore PushConceptDecl(
+ ParentConcept, cast<ConceptDecl>(ConceptId->getNamedConcept()));
+
unsigned Size = Satisfaction.Details.size();
llvm::SaveAndRestore PushConceptDecl(
@@ -2296,14 +2308,6 @@ 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 e2aecd21bed70..5667cf53fee0b 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4932,7 +4932,7 @@ ExprResult Sema::CheckConceptTemplateId(
LocalInstantiationScope Scope(*this);
EnterExpressionEvaluationContext EECtx{
- *this, ExpressionEvaluationContext::Unevaluated};
+ *this, ExpressionEvaluationContext::Unevaluated, CSD};
Error = CheckConstraintSatisfaction(
NamedConcept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL,
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c04fff6cbd964..1544fd56b82ae 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5161,6 +5161,20 @@ 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 3c9ffdb42eeec..14af282131a5c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1753,24 +1753,9 @@ 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.
@@ -1881,24 +1866,11 @@ 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(), 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;
+ TemplateDeclInstantiator DeclInstantiator(getSema(),
+ /* DeclContext *Owner */ Owner, TemplateArgs);
+ DeclInstantiator.setEvaluateConstraints(EvaluateConstraints);
+ return DeclInstantiator.SubstTemplateParams(OrigTPL);
}
concepts::TypeRequirement *
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index ce0e390e371e1..c9bc613a7c4ea 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4862,13 +4862,6 @@ 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 9c0479fe8aa4f..c3bf71dbf10df 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -836,8 +836,6 @@ class TreeTransform {
LSI->Lambda->getLambdaDependencyKind());
}
- ExprResult TransformConstraint(Expr *AC) { return AC; }
-
QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL);
StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr);
@@ -16069,16 +16067,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
assert(FPTL && "Not a FunctionProtoType?");
AssociatedConstraint TRC = E->getCallOperator()->getTrailingRequiresClause();
- 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 the concept refers to any outer parameter packs, we track the SubstIndex
+ // for evaluation.
if (TRC && TRC.ConstraintExpr->containsUnexpandedParameterPack() &&
!TRC.ArgPackSubstIndex)
TRC.ArgPackSubstIndex = SemaRef.ArgPackSubstIndex;
diff --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp
index a08cd52843c73..5a19105a5bf51 100644
--- a/clang/test/SemaTemplate/concepts-lambda.cpp
+++ b/clang/test/SemaTemplate/concepts-lambda.cpp
@@ -56,7 +56,9 @@ namespace GH57971 {
function_ptr ptr = f<void>;
}
-namespace GH58368 {
+// GH58368: A lambda defined in a concept requires we store
+// the concept as a part of the lambda context.
+namespace LambdaInConcept {
using size_t = unsigned long;
template<size_t...Ts>
@@ -368,35 +370,3 @@ 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 at -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}}
-
-}
``````````
</details>
https://github.com/llvm/llvm-project/pull/199228
More information about the cfe-commits
mailing list