[llvm-branch-commits] [clang] release/22.x: [Clang] Fix the normalization of fold constraints (#177531) (PR #179153)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Feb 1 18:06:10 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: None (llvmbot)
<details>
<summary>Changes</summary>
Backport adc64c6e1745a14896efa48d23fc541e4efe5a53
Requested by: @<!-- -->zyn0217
---
Full diff: https://github.com/llvm/llvm-project/pull/179153.diff
5 Files Affected:
- (modified) clang/include/clang/Sema/Sema.h (+1-11)
- (modified) clang/lib/Sema/SemaConcept.cpp (+89-53)
- (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+12-30)
- (modified) clang/lib/Sema/TreeTransform.h (+1-1)
- (modified) clang/test/SemaCXX/cxx2c-fold-exprs.cpp (+16)
``````````diff
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 654bebbaf8ddd..d68ed7c75b86f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13472,7 +13472,7 @@ class Sema final : public SemaBase {
bool SubstTemplateArgumentsInParameterMapping(
ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc,
const MultiLevelTemplateArgumentList &TemplateArgs,
- TemplateArgumentListInfo &Out, bool BuildPackExpansionTypes);
+ TemplateArgumentListInfo &Out);
/// Retrieve the template argument list(s) that should be used to
/// instantiate the definition of the given declaration.
@@ -14888,16 +14888,6 @@ class Sema final : public SemaBase {
const ConceptReference *TopLevelConceptId = nullptr,
Expr **ConvertedExpr = nullptr);
- /// \brief Check whether the given non-dependent constraint expression is
- /// satisfied. Returns false and updates Satisfaction with the satisfaction
- /// verdict if successful, emits a diagnostic and returns true if an error
- /// occurred and satisfaction could not be determined.
- ///
- /// \returns true if an error occurred, false otherwise.
- bool
- CheckConstraintSatisfaction(const ConceptSpecializationExpr *ConstraintExpr,
- ConstraintSatisfaction &Satisfaction);
-
/// Check whether the given function decl's trailing requires clause is
/// satisfied, if any. Returns false and updates Satisfaction with the
/// satisfaction verdict if successful, emits a diagnostic and returns true if
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 2674f0a7b8749..f55f3a9a61ab8 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -213,13 +213,41 @@ CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
}
namespace {
-class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
+class AdjustConstraints : public TreeTransform<AdjustConstraints> {
unsigned TemplateDepth = 0;
+ bool RemoveNonPackExpansionPacks = false;
+
public:
- using inherited = TreeTransform<AdjustConstraintDepth>;
- AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth)
- : inherited(SemaRef), TemplateDepth(TemplateDepth) {}
+ using inherited = TreeTransform<AdjustConstraints>;
+ AdjustConstraints(Sema &SemaRef, unsigned TemplateDepth,
+ bool RemoveNonPackExpansionPacks = false)
+ : inherited(SemaRef), TemplateDepth(TemplateDepth),
+ RemoveNonPackExpansionPacks(RemoveNonPackExpansionPacks) {}
+
+ ExprResult RebuildPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc,
+ UnsignedOrNone NumExpansions) {
+ return inherited::RebuildPackExpansion(Pattern, EllipsisLoc, NumExpansions);
+ }
+
+ TemplateArgumentLoc RebuildPackExpansion(TemplateArgumentLoc Pattern,
+ SourceLocation EllipsisLoc,
+ UnsignedOrNone NumExpansions) {
+ if (!RemoveNonPackExpansionPacks)
+ return inherited::RebuildPackExpansion(Pattern, EllipsisLoc,
+ NumExpansions);
+ return Pattern;
+ }
+
+ bool PreparePackForExpansion(TemplateArgumentLoc In, bool Uneval,
+ TemplateArgumentLoc &Out, UnexpandedInfo &Info) {
+ if (!RemoveNonPackExpansionPacks)
+ return inherited::PreparePackForExpansion(In, Uneval, Out, Info);
+ assert(In.getArgument().isPackExpansion());
+ Out = In;
+ Info.Expand = false;
+ return false;
+ }
using inherited::TransformTemplateTypeParmType;
QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
@@ -232,8 +260,8 @@ class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
TransformDecl(TL.getNameLoc(), OldTTPDecl));
QualType Result = getSema().Context.getTemplateTypeParmType(
- T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(),
- NewTTPDecl);
+ T->getDepth() + TemplateDepth, T->getIndex(),
+ RemoveNonPackExpansionPacks ? false : T->isParameterPack(), NewTTPDecl);
TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result);
NewTL.setNameLoc(TL.getNameLoc());
return Result;
@@ -288,7 +316,13 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
TemplateArgument Arg = TemplateArgs(T->getDepth(), T->getIndex());
- if (T->isParameterPack() && SemaRef.ArgPackSubstIndex) {
+ // In concept parameter mapping for fold expressions, packs that aren't
+ // expanded in place are treated as having non-pack dependency, so that
+ // a PackExpansionType won't prevent expanding the packs outside the
+ // TreeTransform. However we still need to check the pack at this point.
+ if ((T->isParameterPack() ||
+ (T->getDecl() && T->getDecl()->isTemplateParameterPack())) &&
+ SemaRef.ArgPackSubstIndex) {
assert(Arg.getKind() == TemplateArgument::Pack &&
"Missing argument pack");
@@ -506,10 +540,6 @@ StringRef allocateStringFromConceptDiagnostic(const Sema &S,
ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint(
const Expr *AtomicExpr, const MultiLevelTemplateArgumentList &MLTAL) {
- EnterExpressionEvaluationContext ConstantEvaluated(
- S, Sema::ExpressionEvaluationContext::ConstantEvaluated,
- Sema::ReuseLambdaContextDecl);
-
llvm::FoldingSetNodeID ID;
if (Template &&
DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, &MLTAL)) {
@@ -617,7 +647,7 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
if (S.SubstTemplateArgumentsInParameterMapping(
Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL,
- SubstArgs, /*BuildPackExpansionTypes=*/true)) {
+ SubstArgs)) {
Satisfaction.IsSatisfied = false;
return std::nullopt;
}
@@ -663,6 +693,9 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
const AtomicConstraint &Constraint,
const MultiLevelTemplateArgumentList &MLTAL) {
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ S, Sema::ExpressionEvaluationContext::ConstantEvaluated,
+ Sema::ReuseLambdaContextDecl);
llvm::SmallVector<TemplateArgument> SubstitutedOutermost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
@@ -706,8 +739,6 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
return SubstitutedAtomicExpr;
}
- EnterExpressionEvaluationContext ConstantEvaluated(
- S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
Expr::EvalResult EvalResult;
EvalResult.Diag = &EvaluationDiags;
@@ -1290,28 +1321,6 @@ SubstituteConceptsInConstraintExpression(Sema &S, const NamedDecl *D,
MLTAL);
}
-bool Sema::CheckConstraintSatisfaction(
- const ConceptSpecializationExpr *ConstraintExpr,
- ConstraintSatisfaction &Satisfaction) {
-
- ExprResult Res = SubstituteConceptsInConstraintExpression(
- *this, nullptr, ConstraintExpr, ArgPackSubstIndex);
- if (!Res.isUsable())
- return true;
-
- llvm::SmallVector<AssociatedConstraint, 1> Constraints;
- Constraints.emplace_back(Res.get());
-
- MultiLevelTemplateArgumentList MLTAL(ConstraintExpr->getNamedConcept(),
- ConstraintExpr->getTemplateArguments(),
- true);
-
- return CheckConstraintSatisfaction(
- ConstraintExpr->getNamedConcept(), Constraints, MLTAL,
- ConstraintExpr->getSourceRange(), Satisfaction,
- ConstraintExpr->getConceptReference());
-}
-
bool Sema::SetupConstraintScope(
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
const MultiLevelTemplateArgumentList &MLTAL,
@@ -2000,14 +2009,20 @@ class SubstituteParameterMappings {
const MultiLevelTemplateArgumentList *MLTAL;
const ASTTemplateArgumentListInfo *ArgsAsWritten;
- bool InFoldExpr;
+ // When normalizing a fold constraint, e.g.
+ // C<Pack1, Pack2...> && ...
+ // we want the TreeTransform to expand only Pack2 but not Pack1,
+ // since Pack1 will be expanded during the evaluation of the fold expression.
+ // This flag helps rewrite any non-PackExpansion packs into "expanded"
+ // parameters.
+ bool RemovePacksForFoldExpr;
SubstituteParameterMappings(Sema &SemaRef,
const MultiLevelTemplateArgumentList *MLTAL,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
- bool InFoldExpr)
+ bool RemovePacksForFoldExpr)
: SemaRef(SemaRef), MLTAL(MLTAL), ArgsAsWritten(ArgsAsWritten),
- InFoldExpr(InFoldExpr) {}
+ RemovePacksForFoldExpr(RemovePacksForFoldExpr) {}
void buildParameterMapping(NormalizedConstraintWithParamMapping &N);
@@ -2016,9 +2031,10 @@ class SubstituteParameterMappings {
bool substitute(ConceptIdConstraint &CC);
public:
- SubstituteParameterMappings(Sema &SemaRef, bool InFoldExpr = false)
+ SubstituteParameterMappings(Sema &SemaRef,
+ bool RemovePacksForFoldExpr = false)
: SemaRef(SemaRef), MLTAL(nullptr), ArgsAsWritten(nullptr),
- InFoldExpr(InFoldExpr) {}
+ RemovePacksForFoldExpr(RemovePacksForFoldExpr) {}
bool substitute(NormalizedConstraint &N);
};
@@ -2121,8 +2137,7 @@ bool SubstituteParameterMappings::substitute(
// which is wrong.
TemplateArgumentListInfo SubstArgs;
if (SemaRef.SubstTemplateArgumentsInParameterMapping(
- N.getParameterMapping(), N.getBeginLoc(), *MLTAL, SubstArgs,
- /*BuildPackExpansionTypes=*/!InFoldExpr))
+ N.getParameterMapping(), N.getBeginLoc(), *MLTAL, SubstArgs))
return true;
Sema::CheckTemplateArgumentInfo CTAI;
auto *TD =
@@ -2192,8 +2207,7 @@ bool SubstituteParameterMappings::substitute(ConceptIdConstraint &CC) {
const ASTTemplateArgumentListInfo *ArgsAsWritten =
CSE->getTemplateArgsAsWritten();
if (SemaRef.SubstTemplateArgumentsInParameterMapping(
- ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, Out,
- /*BuildPackExpansionTypes=*/!InFoldExpr))
+ ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, Out))
return true;
Sema::CheckTemplateArgumentInfo CTAI;
if (SemaRef.CheckTemplateArgumentList(CSE->getNamedConcept(),
@@ -2206,7 +2220,7 @@ bool SubstituteParameterMappings::substitute(ConceptIdConstraint &CC) {
TemplateArgs.replaceOutermostTemplateArguments(CSE->getNamedConcept(),
CTAI.SugaredConverted);
return SubstituteParameterMappings(SemaRef, &TemplateArgs, ArgsAsWritten,
- InFoldExpr)
+ RemovePacksForFoldExpr)
.substitute(CC.getNormalizedConstraint());
}
@@ -2222,13 +2236,13 @@ bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) {
case NormalizedConstraint::ConstraintKind::FoldExpanded: {
auto &FE = static_cast<FoldExpandedConstraint &>(N);
if (!MLTAL) {
- llvm::SaveAndRestore _1(InFoldExpr, true);
+ llvm::SaveAndRestore _1(RemovePacksForFoldExpr, true);
assert(!ArgsAsWritten);
return substitute(FE.getNormalizedPattern());
}
Sema::ArgPackSubstIndexRAII _(SemaRef, std::nullopt);
substitute(static_cast<NormalizedConstraintWithParamMapping &>(FE));
- return SubstituteParameterMappings(SemaRef, /*InFoldExpr=*/true)
+ return SubstituteParameterMappings(SemaRef, /*RemovePacksForFoldExpr=*/true)
.substitute(FE.getNormalizedPattern());
}
case NormalizedConstraint::ConstraintKind::ConceptId: {
@@ -2239,16 +2253,38 @@ bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) {
}
assert(!ArgsAsWritten);
const ConceptSpecializationExpr *CSE = CC.getConceptSpecializationExpr();
+ SmallVector<TemplateArgument> InnerArgs(CSE->getTemplateArguments());
ConceptDecl *Concept = CSE->getNamedConcept();
+ if (RemovePacksForFoldExpr) {
+ TemplateArgumentListInfo OutArgs;
+ ArrayRef<TemplateArgumentLoc> InputArgLoc =
+ CSE->getConceptReference()->getTemplateArgsAsWritten()->arguments();
+ if (AdjustConstraints(SemaRef, /*TemplateDepth=*/0,
+ /*RemoveNonPackExpansionPacks=*/true)
+ .TransformTemplateArguments(InputArgLoc.begin(),
+ InputArgLoc.end(), OutArgs))
+ return true;
+ Sema::CheckTemplateArgumentInfo CTAI;
+ // Repack the packs.
+ if (SemaRef.CheckTemplateArgumentList(
+ Concept, Concept->getTemplateParameters(), Concept->getBeginLoc(),
+ OutArgs,
+ /*DefaultArguments=*/{},
+ /*PartialTemplateArgs=*/false, CTAI))
+ return true;
+ InnerArgs = std::move(CTAI.SugaredConverted);
+ }
+
MultiLevelTemplateArgumentList MLTAL = SemaRef.getTemplateInstantiationArgs(
Concept, Concept->getLexicalDeclContext(),
- /*Final=*/true, CSE->getTemplateArguments(),
+ /*Final=*/true, InnerArgs,
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);
- return SubstituteParameterMappings(
- SemaRef, &MLTAL, CSE->getTemplateArgsAsWritten(), InFoldExpr)
+ return SubstituteParameterMappings(SemaRef, &MLTAL,
+ CSE->getTemplateArgsAsWritten(),
+ RemovePacksForFoldExpr)
.substitute(CC.getNormalizedConstraint());
}
case NormalizedConstraint::ConstraintKind::Compound: {
@@ -2496,12 +2532,12 @@ bool Sema::IsAtLeastAsConstrained(const NamedDecl *D1,
for (size_t I = 0; I != AC1.size() && I != AC2.size(); ++I) {
if (Depth2 > Depth1) {
AC1[I].ConstraintExpr =
- AdjustConstraintDepth(*this, Depth2 - Depth1)
+ AdjustConstraints(*this, Depth2 - Depth1)
.TransformExpr(const_cast<Expr *>(AC1[I].ConstraintExpr))
.get();
} else if (Depth1 > Depth2) {
AC2[I].ConstraintExpr =
- AdjustConstraintDepth(*this, Depth1 - Depth2)
+ AdjustConstraints(*this, Depth1 - Depth2)
.TransformExpr(const_cast<Expr *>(AC2[I].ConstraintExpr))
.get();
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 35205f40cbcef..50def4e181ab8 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1281,11 +1281,6 @@ namespace {
// Whether an incomplete substituion should be treated as an error.
bool BailOutOnIncomplete;
- // Whether to rebuild pack expansion types; We don't do that when
- // rebuilding the parameter mapping of a fold expression appearing
- // in a constraint expression.
- bool BuildPackExpansionTypes = true;
-
// CWG2770: Function parameters should be instantiated when they are
// needed by a satisfaction check of an atomic constraint or
// (recursively) by another function parameter.
@@ -1313,11 +1308,9 @@ namespace {
TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef,
SourceLocation Loc,
- const MultiLevelTemplateArgumentList &TemplateArgs,
- bool BuildPackExpansionTypes)
+ const MultiLevelTemplateArgumentList &TemplateArgs)
: inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
- BailOutOnIncomplete(false),
- BuildPackExpansionTypes(BuildPackExpansionTypes) {}
+ BailOutOnIncomplete(false) {}
/// Determine whether the given type \p T has already been
/// transformed.
@@ -1601,24 +1594,6 @@ namespace {
return inherited::TransformTemplateArgument(Input, Output, Uneval);
}
- // This has to be here to allow its overload.
- ExprResult RebuildPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc,
- UnsignedOrNone NumExpansions) {
- return inherited::RebuildPackExpansion(Pattern, EllipsisLoc,
- NumExpansions);
- }
-
- TemplateArgumentLoc RebuildPackExpansion(TemplateArgumentLoc Pattern,
- SourceLocation EllipsisLoc,
- UnsignedOrNone NumExpansions) {
- // We don't rewrite a PackExpansion type when we want to normalize a
- // CXXFoldExpr constraint. We'll expand it when evaluating the constraint.
- if (BuildPackExpansionTypes)
- return inherited::RebuildPackExpansion(Pattern, EllipsisLoc,
- NumExpansions);
- return Pattern;
- }
-
using TreeTransform::TransformTemplateSpecializationType;
QualType
TransformTemplateSpecializationType(TypeLocBuilder &TLB,
@@ -2446,7 +2421,14 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
auto [AssociatedDecl, Final] =
TemplateArgs.getAssociatedDecl(T->getDepth());
UnsignedOrNone PackIndex = std::nullopt;
- if (T->isParameterPack()) {
+ if (T->isParameterPack() ||
+ // In concept parameter mapping for fold expressions, packs that aren't
+ // expanded in place are treated as having non-pack dependency, so that
+ // a PackExpansionType won't prevent expanding the packs outside the
+ // TreeTransform. However, we still need to unpack the arguments during
+ // any template argument substitution, so we check the associated
+ // declaration instead.
+ (T->getDecl() && T->getDecl()->isTemplateParameterPack())) {
assert(Arg.getKind() == TemplateArgument::Pack &&
"Missing argument pack");
@@ -4340,10 +4322,10 @@ bool Sema::SubstTemplateArguments(
bool Sema::SubstTemplateArgumentsInParameterMapping(
ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc,
const MultiLevelTemplateArgumentList &TemplateArgs,
- TemplateArgumentListInfo &Out, bool BuildPackExpansionTypes) {
+ TemplateArgumentListInfo &Out) {
TemplateInstantiator Instantiator(
TemplateInstantiator::ForParameterMappingSubstitution, *this, BaseLoc,
- TemplateArgs, BuildPackExpansionTypes);
+ TemplateArgs);
return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index bc923c80b7132..5a1e5fedba9da 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -5179,7 +5179,7 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
if (In.getArgument().isPackExpansion()) {
UnexpandedInfo Info;
TemplateArgumentLoc Prepared;
- if (PreparePackForExpansion(In, Uneval, Prepared, Info))
+ if (getDerived().PreparePackForExpansion(In, Uneval, Prepared, Info))
return true;
if (!Info.Expand) {
Outputs.addArgument(Prepared);
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 289059ea86eb9..89ddcbaf11583 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -491,4 +491,20 @@ void test() {
}
+namespace GH177245 {
+
+template <class _Fun, class... _As>
+concept __callable = requires (_Fun __fun, _As...) { __fun(); };
+
+template <class... _Args>
+struct __mdispatch {
+ template <class... _Ts>
+ requires (__callable<_Args, _Ts...> && ...)
+ void operator()();
+};
+
+static_assert(!__callable<__mdispatch<int>>);
+
+}
+
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/179153
More information about the llvm-branch-commits
mailing list