[llvm-branch-commits] [clang] release/22.x: [Clang] Track constraint's SubstIndex only if it contains outer parameter packs (PR #191646)
Younan Zhang via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sat Apr 11 10:11:54 PDT 2026
https://github.com/zyn0217 created https://github.com/llvm/llvm-project/pull/191646
I believe that is the intent of SubstIndex in AssociatedConstraint. So this enforces the checking explicitly, in case nested SubstIndexes confuses our poor constraint evaluator.
As a drive-by fix, this also removes an strange assertion and an unnecessary
SubstIndex setup in nested requirement transform.
No release note because this is a regression fix.
This backports #191484
>From 3c877c628ad406c6520b454ec95e44cc67886d85 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 5 Apr 2026 10:36:50 +0800
Subject: [PATCH] release/22.x: [Clang] Track constraint's SubstIndex only if
it contains outer parameter packs
I believe that is the intent of SubstIndex in AssociatedConstraint.
So this enforces the checking explicitly, in case nested SubstIndexes
confuses our poor constraint evaluator.
As a drive-by fix, this also removes an strange assertion and an
unnecessary
SubstIndex setup in nested requirement transform.
No release note because this is a regression fix.
This backports #191484
---
clang/lib/Sema/SemaConcept.cpp | 34 +++-----
clang/lib/Sema/SemaTemplateInstantiate.cpp | 12 ++-
clang/lib/Sema/TreeTransform.h | 5 +-
clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 90 ++++++++++++++++++++++
4 files changed, 115 insertions(+), 26 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index f55f3a9a61ab8..f6b3243047707 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -478,6 +478,12 @@ class ConstraintSatisfactionChecker {
bool BuildExpression;
private:
+ template <class Constraint>
+ UnsignedOrNone getOuterPackIndex(const Constraint &C) const {
+ return C.getPackSubstitutionIndex() ? C.getPackSubstitutionIndex()
+ : PackSubstitutionIndex;
+ }
+
ExprResult
EvaluateAtomicConstraint(const Expr *AtomicExpr,
const MultiLevelTemplateArgumentList &MLTAL);
@@ -640,10 +646,7 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
return std::nullopt;
TemplateArgumentListInfo SubstArgs;
- Sema::ArgPackSubstIndexRAII SubstIndex(
- S, Constraint.getPackSubstitutionIndex()
- ? Constraint.getPackSubstitutionIndex()
- : PackSubstitutionIndex);
+ Sema::ArgPackSubstIndexRAII SubstIndex(S, getOuterPackIndex(Constraint));
if (S.SubstTemplateArgumentsInParameterMapping(
Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL,
@@ -770,10 +773,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());
@@ -869,8 +869,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(),
@@ -879,8 +879,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() +
@@ -943,10 +941,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();
@@ -1006,12 +1001,6 @@ 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{},
@@ -1042,6 +1031,7 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
if (Satisfaction.IsSatisfied)
return E;
+ UnsignedOrNone OuterPackSubstIndex = getOuterPackIndex(Constraint);
llvm::FoldingSetNodeID ID;
ID.AddPointer(Constraint.getConceptId());
ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation());
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 50def4e181ab8..7fc1ade737363 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2718,8 +2718,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);
}
@@ -2993,7 +2993,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 5a1e5fedba9da..00be4e8c1be95 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -15890,7 +15890,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..0312022912dca 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -508,3 +508,93 @@ 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;
+
+}
+
+}
+
+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 llvm-branch-commits
mailing list