[clang] [Clang] Drop unrelated template arguments in substituted parameter mapping (PR #163221)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 14 08:32:57 PDT 2025
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/163221
>From f0d65ac5c86967923fb3adf5cad197ed932b5fa0 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 14 Oct 2025 00:40:54 +0800
Subject: [PATCH 1/3] [Clang] Drop unrelated template arguments in substituted
parameter mapping
The unused template arguments living in different levels would confuse
the substitution otherwise.
Fixes a regression on trunk.
---
clang/include/clang/Sema/Template.h | 4 +--
clang/lib/Sema/SemaConcept.cpp | 39 ++++++++++++++--------------
clang/test/SemaTemplate/concepts.cpp | 25 ++++++++++++++++++
3 files changed, 47 insertions(+), 21 deletions(-)
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 60c7d275f1aaf..4acb3a84c4f38 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -205,8 +205,8 @@ enum class TemplateSubstitutionKind : char {
/// Add a new outmost level to the multi-level template argument
/// list.
- /// A 'Final' substitution means that Subst* nodes won't be built
- /// for the replacements.
+ /// A 'Final' substitution means that these Args are not needed to be
+ /// resugared later.
void addOuterTemplateArguments(Decl *AssociatedDecl, ArgList Args,
bool Final) {
assert(!NumRetainedOuterLevels &&
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 9cbd1bd772f6f..46e0d01c8bfa9 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -565,7 +565,7 @@ std::optional<MultiLevelTemplateArgumentList>
ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
const NormalizedConstraintWithParamMapping &Constraint,
MultiLevelTemplateArgumentList MLTAL,
- llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost) {
+ llvm::SmallVector<TemplateArgument> &SubstitutedOutermost) {
if (!Constraint.hasParameterMapping())
return std::move(MLTAL);
@@ -606,38 +606,39 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
Constraint.mappingOccurenceList();
// The empty MLTAL situation should only occur when evaluating non-dependent
// constraints.
- if (!MLTAL.getNumSubstitutedLevels())
- MLTAL.addOuterTemplateArguments(TD, {}, /*Final=*/false);
- SubstitutedOuterMost =
- llvm::to_vector_of<TemplateArgument>(MLTAL.getOutermost());
+ if (MLTAL.getNumSubstitutedLevels())
+ SubstitutedOutermost =
+ llvm::to_vector_of<TemplateArgument>(MLTAL.getOutermost());
unsigned Offset = 0;
for (unsigned I = 0, MappedIndex = 0; I < Used.size(); I++) {
TemplateArgument Arg;
if (Used[I])
Arg = S.Context.getCanonicalTemplateArgument(
CTAI.SugaredConverted[MappedIndex++]);
- if (I < SubstitutedOuterMost.size()) {
- SubstitutedOuterMost[I] = Arg;
+ if (I < SubstitutedOutermost.size()) {
+ SubstitutedOutermost[I] = Arg;
Offset = I + 1;
} else {
- SubstitutedOuterMost.push_back(Arg);
- Offset = SubstitutedOuterMost.size();
+ SubstitutedOutermost.push_back(Arg);
+ Offset = SubstitutedOutermost.size();
}
}
- if (Offset < SubstitutedOuterMost.size())
- SubstitutedOuterMost.erase(SubstitutedOuterMost.begin() + Offset);
+ if (Offset < SubstitutedOutermost.size())
+ SubstitutedOutermost.erase(SubstitutedOutermost.begin() + Offset);
- MLTAL.replaceOutermostTemplateArguments(TD, SubstitutedOuterMost);
- return std::move(MLTAL);
+ MultiLevelTemplateArgumentList SubstitutedTemplateArgs;
+ SubstitutedTemplateArgs.addOuterTemplateArguments(TD, SubstitutedOutermost,
+ /*Final=*/false);
+ return std::move(SubstitutedTemplateArgs);
}
ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
const AtomicConstraint &Constraint,
const MultiLevelTemplateArgumentList &MLTAL) {
- llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ llvm::SmallVector<TemplateArgument> SubstitutedOutermost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
- SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost);
+ SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOutermost);
if (!SubstitutedArgs) {
Satisfaction.IsSatisfied = false;
return ExprEmpty();
@@ -785,13 +786,13 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
FoldExpandedConstraint::FoldOperatorKind::And;
unsigned EffectiveDetailEndIndex = Satisfaction.Details.size();
- llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ llvm::SmallVector<TemplateArgument> SubstitutedOutermost;
// FIXME: Is PackSubstitutionIndex correct?
llvm::SaveAndRestore _(PackSubstitutionIndex, S.ArgPackSubstIndex);
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
SubstitutionInTemplateArguments(
static_cast<const NormalizedConstraintWithParamMapping &>(Constraint),
- MLTAL, SubstitutedOuterMost);
+ MLTAL, SubstitutedOutermost);
if (!SubstitutedArgs) {
Satisfaction.IsSatisfied = false;
return ExprError();
@@ -879,9 +880,9 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
const MultiLevelTemplateArgumentList &MLTAL, unsigned Size) {
const ConceptReference *ConceptId = Constraint.getConceptId();
- llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ llvm::SmallVector<TemplateArgument> SubstitutedOutermost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
- SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost);
+ SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOutermost);
if (!SubstitutedArgs) {
Satisfaction.IsSatisfied = false;
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index 3fbe7c0ac650f..aaa20f6240e76 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1416,6 +1416,31 @@ concept IsEntitySpec =
}
+namespace case8 {
+
+template <class T>
+struct type_identity {
+ using type = T;
+};
+
+template <typename Inner>
+struct Cat {};
+
+template <typename T>
+concept CatConcept = requires {
+ []<class Inner>(type_identity<Cat<Inner>>) {}(type_identity<T>{});
+};
+
+template <typename Dummy>
+struct Feeder {
+ template <CatConcept Dummy2>
+ void feed() noexcept {}
+};
+
+void main() { Feeder<int>{}.feed<Cat<int>>(); }
+
+}
+
}
namespace GH162125 {
>From 51bf929ee2cf70143a56a7f1a17042df396a9368 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 14 Oct 2025 14:57:15 +0800
Subject: [PATCH 2/3] Revert capitalize changes
---
clang/lib/Sema/SemaConcept.cpp | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 46e0d01c8bfa9..87dd68269d44a 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -565,7 +565,7 @@ std::optional<MultiLevelTemplateArgumentList>
ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
const NormalizedConstraintWithParamMapping &Constraint,
MultiLevelTemplateArgumentList MLTAL,
- llvm::SmallVector<TemplateArgument> &SubstitutedOutermost) {
+ llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost) {
if (!Constraint.hasParameterMapping())
return std::move(MLTAL);
@@ -607,7 +607,7 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
// The empty MLTAL situation should only occur when evaluating non-dependent
// constraints.
if (MLTAL.getNumSubstitutedLevels())
- SubstitutedOutermost =
+ SubstitutedOuterMost =
llvm::to_vector_of<TemplateArgument>(MLTAL.getOutermost());
unsigned Offset = 0;
for (unsigned I = 0, MappedIndex = 0; I < Used.size(); I++) {
@@ -615,19 +615,19 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
if (Used[I])
Arg = S.Context.getCanonicalTemplateArgument(
CTAI.SugaredConverted[MappedIndex++]);
- if (I < SubstitutedOutermost.size()) {
- SubstitutedOutermost[I] = Arg;
+ if (I < SubstitutedOuterMost.size()) {
+ SubstitutedOuterMost[I] = Arg;
Offset = I + 1;
} else {
- SubstitutedOutermost.push_back(Arg);
- Offset = SubstitutedOutermost.size();
+ SubstitutedOuterMost.push_back(Arg);
+ Offset = SubstitutedOuterMost.size();
}
}
- if (Offset < SubstitutedOutermost.size())
- SubstitutedOutermost.erase(SubstitutedOutermost.begin() + Offset);
+ if (Offset < SubstitutedOuterMost.size())
+ SubstitutedOuterMost.erase(SubstitutedOuterMost.begin() + Offset);
MultiLevelTemplateArgumentList SubstitutedTemplateArgs;
- SubstitutedTemplateArgs.addOuterTemplateArguments(TD, SubstitutedOutermost,
+ SubstitutedTemplateArgs.addOuterTemplateArguments(TD, SubstitutedOuterMost,
/*Final=*/false);
return std::move(SubstitutedTemplateArgs);
}
@@ -636,9 +636,9 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
const AtomicConstraint &Constraint,
const MultiLevelTemplateArgumentList &MLTAL) {
- llvm::SmallVector<TemplateArgument> SubstitutedOutermost;
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
- SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOutermost);
+ SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost);
if (!SubstitutedArgs) {
Satisfaction.IsSatisfied = false;
return ExprEmpty();
@@ -786,13 +786,13 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
FoldExpandedConstraint::FoldOperatorKind::And;
unsigned EffectiveDetailEndIndex = Satisfaction.Details.size();
- llvm::SmallVector<TemplateArgument> SubstitutedOutermost;
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
// FIXME: Is PackSubstitutionIndex correct?
llvm::SaveAndRestore _(PackSubstitutionIndex, S.ArgPackSubstIndex);
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
SubstitutionInTemplateArguments(
static_cast<const NormalizedConstraintWithParamMapping &>(Constraint),
- MLTAL, SubstitutedOutermost);
+ MLTAL, SubstitutedOuterMost);
if (!SubstitutedArgs) {
Satisfaction.IsSatisfied = false;
return ExprError();
@@ -880,9 +880,9 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
const MultiLevelTemplateArgumentList &MLTAL, unsigned Size) {
const ConceptReference *ConceptId = Constraint.getConceptId();
- llvm::SmallVector<TemplateArgument> SubstitutedOutermost;
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
- SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOutermost);
+ SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost);
if (!SubstitutedArgs) {
Satisfaction.IsSatisfied = false;
>From 4b56f34d982938e57de9f971643f0f7bad194a2d Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 14 Oct 2025 23:32:37 +0800
Subject: [PATCH 3/3] Fix wording
---
clang/include/clang/Sema/Template.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 4acb3a84c4f38..e963439b05c98 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -205,7 +205,7 @@ enum class TemplateSubstitutionKind : char {
/// Add a new outmost level to the multi-level template argument
/// list.
- /// A 'Final' substitution means that these Args are not needed to be
+ /// A 'Final' substitution means that these Args don't need to be
/// resugared later.
void addOuterTemplateArguments(Decl *AssociatedDecl, ArgList Args,
bool Final) {
More information about the cfe-commits
mailing list