[clang] [Clang] Track constraint's SubstIndex only if it contains outer parameter packs (PR #191484)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Sat Apr 11 09:25:08 PDT 2026
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/191484
>From c5f66b42960bfeaa8b7e12f1da783e3758266c35 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 10 Apr 2026 23:10:05 +0800
Subject: [PATCH 1/4] Revert "[Clang] Fix concept cache for normalized fold
expressions"
This reverts commit 257cc5ad89840cdfba4affcc8fe62cf9d02d9017.
---
clang/lib/Sema/SemaConcept.cpp | 29 +++++++++++++------------
clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 14 ------------
2 files changed, 15 insertions(+), 28 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 5cd2c3d326ff7..a58a3874149b7 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -493,12 +493,6 @@ class ConstraintSatisfactionChecker {
CachedTemplateArgs;
private:
- template <class Constraint>
- UnsignedOrNone getOuterPackIndex(const Constraint &C) const {
- return C.getPackSubstitutionIndex() ? C.getPackSubstitutionIndex()
- : PackSubstitutionIndex;
- }
-
ExprResult
EvaluateAtomicConstraint(const Expr *AtomicExpr,
const MultiLevelTemplateArgumentList &MLTAL);
@@ -812,11 +806,14 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
unsigned Size = Satisfaction.Details.size();
llvm::FoldingSetNodeID ID;
+ UnsignedOrNone OuterPackSubstIndex =
+ Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex;
+
ID.AddPointer(Constraint.getConstraintExpr());
- ID.AddInteger(
- Constraint.getPackSubstitutionIndex().toInternalRepresentation());
- ID.AddInteger(PackSubstitutionIndex.toInternalRepresentation());
- HashParameterMapping(S, MLTAL, ID, getOuterPackIndex(Constraint))
+ ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation());
+ HashParameterMapping(S, MLTAL, ID, OuterPackSubstIndex)
.VisitConstraint(Constraint);
if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID);
@@ -1045,6 +1042,12 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
const MultiLevelTemplateArgumentList &MLTAL) {
const ConceptReference *ConceptId = Constraint.getConceptId();
+
+ UnsignedOrNone OuterPackSubstIndex =
+ Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex;
+
Sema::InstantiatingTemplate InstTemplate(
S, ConceptId->getBeginLoc(),
Sema::InstantiatingTemplate::ConstraintsCheck{},
@@ -1080,10 +1083,8 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
llvm::FoldingSetNodeID ID;
ID.AddPointer(Constraint.getConceptId());
- ID.AddInteger(
- Constraint.getPackSubstitutionIndex().toInternalRepresentation());
- ID.AddInteger(PackSubstitutionIndex.toInternalRepresentation());
- HashParameterMapping(S, MLTAL, ID, getOuterPackIndex(Constraint))
+ ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation());
+ HashParameterMapping(S, MLTAL, ID, OuterPackSubstIndex)
.VisitConstraint(Constraint);
if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID);
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 5b993fead4877..89ddcbaf11583 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -508,17 +508,3 @@ static_assert(!__callable<__mdispatch<int>>);
}
}
-
-namespace GH190169 {
-
-template <typename Type, typename... Choices>
-concept OneOf = (... || __is_same(Type, Choices));
-
-template <typename F, typename... Ts>
-using check = decltype((F{}(Ts{}), ...));
-
-using X = check<decltype([](auto val) {
- [](OneOf<int, char> auto) {}(val);
-}), char>;
-
-}
>From e9e59a9d4f3926a5b36dca944bb10a9c0d68fd16 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 11 Apr 2026 02:30:32 +0800
Subject: [PATCH 2/4] [Clang] Track constraint's SubstIndex only if it contains
outer parameter packs
I believe that is the intent of SubstIndex in AssociatedConstraint.
So this applies the checking explicitly, in case nested SubstIndexes
confuses our poor constraint evaluator.
I reverted the previous fix 257cc5a because that was wrong.
No release note because this is a regression fix.
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 8 +++-
clang/lib/Sema/TreeTransform.h | 5 ++-
clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 48 ++++++++++++++++++++++
3 files changed, 59 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 5381a5a6f110d..80e3b1a5b11f7 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3085,7 +3085,13 @@ bool Sema::SubstTypeConstraint(
if (!EvaluateConstraints && !inParameterMappingSubstitution()) {
UnsignedOrNone Index = TC->getArgPackSubstIndex();
- if (!Index)
+ bool ContainsUnexpandedPack =
+ TemplArgInfo &&
+ llvm::any_of(
+ TemplArgInfo->arguments(), [](const TemplateArgumentLoc &TA) {
+ return TA.getArgument().containsUnexpandedParameterPack();
+ });
+ if (!Index && ContainsUnexpandedPack)
Index = SemaRef.ArgPackSubstIndex;
Inst->setTypeConstraint(TC->getConceptReference(),
TC->getImmediatelyDeclaredConstraint(), Index);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 8ae5df367e0dd..4c941e234d78d 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16004,7 +16004,10 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
assert(FPTL && "Not a FunctionProtoType?");
AssociatedConstraint TRC = E->getCallOperator()->getTrailingRequiresClause();
- if (!TRC.ArgPackSubstIndex)
+ // 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;
getSema().CompleteLambdaCallOperator(
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 89ddcbaf11583..f681fc6f594c2 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -508,3 +508,51 @@ static_assert(!__callable<__mdispatch<int>>);
}
}
+
+namespace GH190169 {
+
+namespace _1 {
+
+template <typename Type, typename... Choices>
+concept OneOf = (... || __is_same(Type, Choices));
+
+template <typename F, typename... Ts>
+using check = decltype((F{}(Ts{}), ...));
+
+using X = check<decltype([](auto val) {
+ [](OneOf<int, char> auto) {}(val);
+}), char>;
+
+}
+
+namespace _2 {
+
+template <typename T, typename U>
+concept Same = __is_same(T, U);
+
+template <typename Type, typename... Choices>
+concept OneOf = (... || Same<Type, Choices>);
+
+template <class... Ts>
+struct visitor : Ts... { using Ts::operator()...; };
+
+struct A {};
+struct B {};
+struct C {};
+
+template <typename F, typename T, typename = decltype(F{}(T{}))>
+struct check {};
+
+template <typename F, typename... Ts>
+struct check_all : check<F, Ts>... {};
+
+using F = decltype([](auto val) {
+ visitor{[](const A&) {},
+ [](const OneOf<B, C> auto&) {}}(val);
+});
+
+check_all<F, A, B, C> x;
+
+}
+
+}
>From 7b28c32b33dcf7951f8ff7ac38617aef871e970d Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 11 Apr 2026 23:37:46 +0800
Subject: [PATCH 3/4] Add back getOuterPackIndex
---
clang/lib/Sema/SemaConcept.cpp | 27 ++++++++++-----------------
1 file changed, 10 insertions(+), 17 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index a58a3874149b7..25b483b5e30b9 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -493,6 +493,12 @@ class ConstraintSatisfactionChecker {
CachedTemplateArgs;
private:
+ template <class Constraint>
+ UnsignedOrNone getOuterPackIndex(const Constraint &C) const {
+ return C.getPackSubstitutionIndex() ? C.getPackSubstitutionIndex()
+ : PackSubstitutionIndex;
+ }
+
ExprResult
EvaluateAtomicConstraint(const Expr *AtomicExpr,
const MultiLevelTemplateArgumentList &MLTAL);
@@ -658,10 +664,7 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
return std::nullopt;
TemplateArgumentListInfo SubstArgs;
- Sema::ArgPackSubstIndexRAII SubstIndex(
- S, Constraint.getPackSubstitutionIndex()
- ? Constraint.getPackSubstitutionIndex()
- : PackSubstitutionIndex);
+ Sema::ArgPackSubstIndexRAII SubstIndex(S, getOuterPackIndex(Constraint));
llvm::SaveAndRestore PushTemplateArgsCache(S.CurrentCachedTemplateArgs,
&CachedTemplateArgs);
@@ -806,10 +809,7 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
unsigned Size = Satisfaction.Details.size();
llvm::FoldingSetNodeID ID;
- UnsignedOrNone OuterPackSubstIndex =
- Constraint.getPackSubstitutionIndex()
- ? Constraint.getPackSubstitutionIndex()
- : PackSubstitutionIndex;
+ UnsignedOrNone OuterPackSubstIndex = getOuterPackIndex(Constraint);
ID.AddPointer(Constraint.getConstraintExpr());
ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation());
@@ -979,10 +979,7 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
return ExprError();
}
- Sema::ArgPackSubstIndexRAII SubstIndex(
- S, Constraint.getPackSubstitutionIndex()
- ? Constraint.getPackSubstitutionIndex()
- : PackSubstitutionIndex);
+ Sema::ArgPackSubstIndexRAII SubstIndex(S, getOuterPackIndex(Constraint));
const ASTTemplateArgumentListInfo *Ori =
ConceptId->getTemplateArgsAsWritten();
@@ -1043,11 +1040,6 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
const ConceptReference *ConceptId = Constraint.getConceptId();
- UnsignedOrNone OuterPackSubstIndex =
- Constraint.getPackSubstitutionIndex()
- ? Constraint.getPackSubstitutionIndex()
- : PackSubstitutionIndex;
-
Sema::InstantiatingTemplate InstTemplate(
S, ConceptId->getBeginLoc(),
Sema::InstantiatingTemplate::ConstraintsCheck{},
@@ -1081,6 +1073,7 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
if (Satisfaction.IsSatisfied)
return E;
+ UnsignedOrNone OuterPackSubstIndex = getOuterPackIndex(Constraint);
llvm::FoldingSetNodeID ID;
ID.AddPointer(Constraint.getConceptId());
ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation());
>From 618281e1175fe3ba53b1b4368a26c11b711a6694 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 12 Apr 2026 00:23:30 +0800
Subject: [PATCH 4/4] Add fix for GH188505
---
clang/lib/Sema/SemaConcept.cpp | 7 ++--
clang/lib/Sema/SemaTemplateInstantiate.cpp | 4 +--
clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 42 ++++++++++++++++++++++
3 files changed, 46 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 25b483b5e30b9..3f04922a5647e 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -905,8 +905,8 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
UnsignedOrNone(I), Satisfaction,
/*BuildExpression=*/false)
.Evaluate(Constraint.getNormalizedPattern(), *SubstitutedArgs);
- if (BuildExpression && Expr.isUsable()) {
- if (Out.isUnset())
+ if (BuildExpression) {
+ if (Out.isUnset() || !Expr.isUsable())
Out = Expr;
else
Out = BinaryOperator::Create(S.Context, Out.get(), Expr.get(),
@@ -915,8 +915,6 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
S.Context.BoolTy, VK_PRValue, OK_Ordinary,
Constraint.getBeginLoc(),
FPOptionsOverride{});
- } else {
- assert(!BuildExpression || !Satisfaction.IsSatisfied);
}
if (!Conjunction && Satisfaction.IsSatisfied) {
Satisfaction.Details.erase(Satisfaction.Details.begin() +
@@ -1039,7 +1037,6 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
const MultiLevelTemplateArgumentList &MLTAL) {
const ConceptReference *ConceptId = Constraint.getConceptId();
-
Sema::InstantiatingTemplate InstTemplate(
S, ConceptId->getBeginLoc(),
Sema::InstantiatingTemplate::ConstraintsCheck{},
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 80e3b1a5b11f7..8dfe33f8684bd 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2810,8 +2810,8 @@ TemplateInstantiator::TransformNestedRequirement(
return nullptr;
Success = !SemaRef.CheckConstraintSatisfaction(
- Req, AssociatedConstraint(Constraint, SemaRef.ArgPackSubstIndex),
- TemplateArgs, Constraint->getSourceRange(), Satisfaction,
+ Req, AssociatedConstraint(Constraint), TemplateArgs,
+ Constraint->getSourceRange(), Satisfaction,
/*TopLevelConceptId=*/nullptr, &NewConstraint);
}
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index f681fc6f594c2..0312022912dca 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -556,3 +556,45 @@ check_all<F, A, B, C> x;
}
}
+
+namespace GH188505 {
+
+template <class _Tp, class _Up>
+concept same_as = __is_same(_Tp, _Up) && __is_same(_Up, _Tp);
+
+template <typename T, typename... Ts>
+concept AnyOf = (same_as<T, Ts> || ...);
+
+struct Constant
+{
+ template <typename T>
+ using alias = const T;
+};
+
+struct Mutable
+{
+ template <typename T>
+ using alias = __remove_cv(T);
+};
+
+template <template <typename> typename M, typename T>
+concept MutabilityAliasFor = requires {
+ requires AnyOf<M<T>, const T, __remove_cv(T)>;
+ requires same_as<M<M<T>>, M<T>>;
+};
+
+template <template <typename> typename M, typename... Ts>
+concept MutabilityAliasForAllOf = (MutabilityAliasFor<M, Ts> && ...);
+
+template <template <typename T> typename M>
+concept MutabilityAlias = MutabilityAliasForAllOf<M, int, char, double>;
+
+static_assert(AnyOf<char, const char, char>);
+
+static_assert(MutabilityAliasFor<Constant::alias, int>);
+static_assert(MutabilityAliasForAllOf<Constant::alias, char, int>);
+
+static_assert(MutabilityAlias<Constant::alias>);
+static_assert(MutabilityAlias<Mutable::alias>);
+
+}
More information about the cfe-commits
mailing list