From 1c34050a8c047fa3ebe42f0b9a468b527fe90d3a Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Sat, 18 Oct 2025 13:13:23 +0800 Subject: [PATCH 1/5] [Clang] Give empty template parameter mapping an empty MLTAL There are cases where atomic constraints are independent of template parameters, yet we still have a template parameter mapping. We don't bother translating template arguments for them. Note that we retain an empty parameter mapping rather than none at all, as the former may improve cache hit rates (We don't profile MLTAL but profile the empty template argument list instead.) --- clang/lib/Sema/SemaConcept.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 04a73181831d8..950b14c60696b 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -570,6 +570,11 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( if (!Constraint.hasParameterMapping()) return std::move(MLTAL); + // The mapping is empty, meaning no template arguments are needed for + // evaluation. + if (Constraint.getParameterMapping().empty()) + return MultiLevelTemplateArgumentList(); + TemplateDeductionInfo Info(Constraint.getBeginLoc()); Sema::InstantiatingTemplate Inst( S, Constraint.getBeginLoc(), @@ -2017,8 +2022,13 @@ void SubstituteParameterMappings::buildParameterMapping( SemaRef.MarkUsedTemplateParameters(Args->arguments(), /*Depth=*/0, OccurringIndices); } + unsigned Size = OccurringIndices.count(); + // It's OK when Size is 0. We build an empty parameter mapping when the atomic + // constraint is independent of any template parameters, so we can distinguish + // it from cases where no mapping exists at all, e.g. when there are only + // atomic constraints. TemplateArgumentLoc *TempArgs = - new (SemaRef.Context) TemplateArgumentLoc[OccurringIndices.count()]; + new (SemaRef.Context) TemplateArgumentLoc[Size]; llvm::SmallVector UsedParams; for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I) { SourceLocation Loc = ArgsAsWritten->NumTemplateArgs > I @@ -2039,7 +2049,6 @@ void SubstituteParameterMappings::buildParameterMapping( TemplateParams->getLAngleLoc(), UsedParams, /*RAngleLoc=*/SourceLocation(), /*RequiresClause=*/nullptr); - unsigned Size = OccurringIndices.count(); N.updateParameterMapping( std::move(OccurringIndices), std::move(OccurringIndicesForSubsumption), MutableArrayRef{TempArgs, Size}, UsedList); @@ -2050,6 +2059,11 @@ bool SubstituteParameterMappings::substitute( if (!N.hasParameterMapping()) buildParameterMapping(N); + // Don't bother into substituting if we have determined the mapping maps to + // nothing. + if (N.getParameterMapping().empty()) + return false; + SourceLocation InstLocBegin, InstLocEnd; llvm::ArrayRef Arguments = ArgsAsWritten->arguments(); if (Arguments.empty()) { From 9beea6e3a66dce9ed2086d5fbc1fb74b69b56a09 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Sat, 18 Oct 2025 13:53:44 +0800 Subject: [PATCH 2/5] NFC: Fix spelling: OuterMost -> Outermost --- clang/lib/Sema/SemaConcept.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 950b14c60696b..6781070cd0457 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -432,7 +432,7 @@ class ConstraintSatisfactionChecker { // XXX: It is SLOW! Use it very carefully. std::optional SubstitutionInTemplateArguments( const NormalizedConstraintWithParamMapping &Constraint, - MultiLevelTemplateArgumentList MLTAL, + const MultiLevelTemplateArgumentList &MLTAL, llvm::SmallVector &SubstitutedOuterMost); ExprResult EvaluateSlow(const AtomicConstraint &Constraint, @@ -564,8 +564,8 @@ ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint( std::optional ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( const NormalizedConstraintWithParamMapping &Constraint, - MultiLevelTemplateArgumentList MLTAL, - llvm::SmallVector &SubstitutedOuterMost) { + const MultiLevelTemplateArgumentList &MLTAL, + llvm::SmallVector &SubstitutedOutermost) { if (!Constraint.hasParameterMapping()) return std::move(MLTAL); @@ -612,7 +612,7 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( // The empty MLTAL situation should only occur when evaluating non-dependent // constraints. if (MLTAL.getNumSubstitutedLevels()) - SubstitutedOuterMost = + SubstitutedOutermost = llvm::to_vector_of(MLTAL.getOutermost()); unsigned Offset = 0; for (unsigned I = 0, MappedIndex = 0; I < Used.size(); I++) { @@ -620,19 +620,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); } @@ -641,9 +641,9 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( const AtomicConstraint &Constraint, const MultiLevelTemplateArgumentList &MLTAL) { - llvm::SmallVector SubstitutedOuterMost; + llvm::SmallVector SubstitutedOutermost; std::optional SubstitutedArgs = - SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost); + SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOutermost); if (!SubstitutedArgs) { Satisfaction.IsSatisfied = false; return ExprEmpty(); @@ -791,13 +791,13 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( FoldExpandedConstraint::FoldOperatorKind::And; unsigned EffectiveDetailEndIndex = Satisfaction.Details.size(); - llvm::SmallVector SubstitutedOuterMost; + llvm::SmallVector SubstitutedOutermost; // FIXME: Is PackSubstitutionIndex correct? llvm::SaveAndRestore _(PackSubstitutionIndex, S.ArgPackSubstIndex); std::optional SubstitutedArgs = SubstitutionInTemplateArguments( static_cast(Constraint), - MLTAL, SubstitutedOuterMost); + MLTAL, SubstitutedOutermost); if (!SubstitutedArgs) { Satisfaction.IsSatisfied = false; return ExprError(); @@ -885,9 +885,9 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( const MultiLevelTemplateArgumentList &MLTAL, unsigned Size) { const ConceptReference *ConceptId = Constraint.getConceptId(); - llvm::SmallVector SubstitutedOuterMost; + llvm::SmallVector SubstitutedOutermost; std::optional SubstitutedArgs = - SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost); + SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOutermost); if (!SubstitutedArgs) { Satisfaction.IsSatisfied = false; From 6768cec66e7937b346158b05b92f0783798b7172 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Sat, 18 Oct 2025 13:55:50 +0800 Subject: [PATCH 3/5] Add test --- clang/test/SemaTemplate/concepts.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index a54bc02226058..5b0f3d39d9648 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -1441,6 +1441,24 @@ void main() { Feeder{}.feed>(); } } +namespace case9 { + +template +concept a = requires { requires true; }; +template +concept b = a; +template +concept c = requires { b; }; +template + requires c +struct s; +template constexpr bool f() { return true; } +template constexpr bool d = f(); +struct s2; +static_assert(d>); + +} + } namespace GH162125 { From ca33e1ad4fd0f9a7b21ded24adf69eb68dff854a Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Mon, 20 Oct 2025 00:45:01 +0800 Subject: [PATCH 4/5] Revert "NFC: Fix spelling: OuterMost -> Outermost" This reverts commit 9beea6e3a66dce9ed2086d5fbc1fb74b69b56a09. --- clang/lib/Sema/SemaConcept.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 6781070cd0457..950b14c60696b 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -432,7 +432,7 @@ class ConstraintSatisfactionChecker { // XXX: It is SLOW! Use it very carefully. std::optional SubstitutionInTemplateArguments( const NormalizedConstraintWithParamMapping &Constraint, - const MultiLevelTemplateArgumentList &MLTAL, + MultiLevelTemplateArgumentList MLTAL, llvm::SmallVector &SubstitutedOuterMost); ExprResult EvaluateSlow(const AtomicConstraint &Constraint, @@ -564,8 +564,8 @@ ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint( std::optional ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( const NormalizedConstraintWithParamMapping &Constraint, - const MultiLevelTemplateArgumentList &MLTAL, - llvm::SmallVector &SubstitutedOutermost) { + MultiLevelTemplateArgumentList MLTAL, + llvm::SmallVector &SubstitutedOuterMost) { if (!Constraint.hasParameterMapping()) return std::move(MLTAL); @@ -612,7 +612,7 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( // The empty MLTAL situation should only occur when evaluating non-dependent // constraints. if (MLTAL.getNumSubstitutedLevels()) - SubstitutedOutermost = + SubstitutedOuterMost = llvm::to_vector_of(MLTAL.getOutermost()); unsigned Offset = 0; for (unsigned I = 0, MappedIndex = 0; I < Used.size(); I++) { @@ -620,19 +620,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); } @@ -641,9 +641,9 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( const AtomicConstraint &Constraint, const MultiLevelTemplateArgumentList &MLTAL) { - llvm::SmallVector SubstitutedOutermost; + llvm::SmallVector SubstitutedOuterMost; std::optional SubstitutedArgs = - SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOutermost); + SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost); if (!SubstitutedArgs) { Satisfaction.IsSatisfied = false; return ExprEmpty(); @@ -791,13 +791,13 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( FoldExpandedConstraint::FoldOperatorKind::And; unsigned EffectiveDetailEndIndex = Satisfaction.Details.size(); - llvm::SmallVector SubstitutedOutermost; + llvm::SmallVector SubstitutedOuterMost; // FIXME: Is PackSubstitutionIndex correct? llvm::SaveAndRestore _(PackSubstitutionIndex, S.ArgPackSubstIndex); std::optional SubstitutedArgs = SubstitutionInTemplateArguments( static_cast(Constraint), - MLTAL, SubstitutedOutermost); + MLTAL, SubstitutedOuterMost); if (!SubstitutedArgs) { Satisfaction.IsSatisfied = false; return ExprError(); @@ -885,9 +885,9 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( const MultiLevelTemplateArgumentList &MLTAL, unsigned Size) { const ConceptReference *ConceptId = Constraint.getConceptId(); - llvm::SmallVector SubstitutedOutermost; + llvm::SmallVector SubstitutedOuterMost; std::optional SubstitutedArgs = - SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOutermost); + SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost); if (!SubstitutedArgs) { Satisfaction.IsSatisfied = false; From 23ec5369e86f5d433625abe709137f5da6e8c8a7 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Mon, 20 Oct 2025 00:46:16 +0800 Subject: [PATCH 5/5] Address feedback --- clang/lib/Sema/SemaConcept.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 950b14c60696b..615b8fd073fff 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -2023,10 +2023,10 @@ void SubstituteParameterMappings::buildParameterMapping( /*Depth=*/0, OccurringIndices); } unsigned Size = OccurringIndices.count(); - // It's OK when Size is 0. We build an empty parameter mapping when the atomic - // constraint is independent of any template parameters, so we can distinguish - // it from cases where no mapping exists at all, e.g. when there are only - // atomic constraints. + // When the constraint is independent of any template parameters, + // we build an empty mapping so that we can distinguish these cases + // from cases where no mapping exists at all, e.g. when there are only atomic + // constraints. TemplateArgumentLoc *TempArgs = new (SemaRef.Context) TemplateArgumentLoc[Size]; llvm::SmallVector UsedParams; @@ -2059,8 +2059,7 @@ bool SubstituteParameterMappings::substitute( if (!N.hasParameterMapping()) buildParameterMapping(N); - // Don't bother into substituting if we have determined the mapping maps to - // nothing. + // If the parameter mapping is empty, there is nothing to substitute. if (N.getParameterMapping().empty()) return false;