https://github.com/cor3ntin created https://github.com/llvm/llvm-project/pull/200185
Since the big concepts rewrite of clang 22, we were incorrectly dealing with fold expanded constraints. We would always inject all arguments in the parameter mapping, instead of the arguments of the current expansion. In the code under test here, this would then lead the parameter of the constraint expression to produce 2 expansions instead of one. It is somewhat interesting that this did not create more issues. Fixes #199569 >From 5dd9ce1e5d32fc2551b22c3b2f3094b346ae1990 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <[email protected]> Date: Thu, 28 May 2026 15:56:40 +0200 Subject: [PATCH] [Clang] Fix evaluation of fold expanded constraints. Since the big concepts rewrite of clang 22, we were incorrectly dealing with fold expanded constraints. We would always inject all arguments in the parameter mapping, instead of the arguments of the current expansion. In the code under test here, this would then lead the parameter of the constraint expression to produce 2 expansions instead of one. It is somewhat interesting that this did not create more issues. Fixes #199569 --- clang/docs/ReleaseNotes.rst | 1 + clang/include/clang/Sema/Sema.h | 2 +- clang/lib/Sema/SemaConcept.cpp | 27 +++++++++++------ clang/lib/Sema/SemaTemplateDeduction.cpp | 4 +-- clang/lib/Sema/SemaTemplateInstantiate.cpp | 5 ++-- clang/lib/Sema/TreeTransform.h | 34 ++++++++++++++-------- clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 16 ++++++++++ 7 files changed, 63 insertions(+), 26 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index cef93e25f1e7d..a4ca1e7be59c1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -642,6 +642,7 @@ Bug Fixes to C++ Support - Concepts appearing in the require-clause of a member function no longer have access to non-public members of that class, or to a current class object. (#GH115838) (#GH194803) (#GH197067) - We no longer caches invalid variable specializations. (#GH132592) +- Fix a regression in the checking of constraints involving fold expressions. (#GH199569) - Fixed an incorrect template argument deduction when matching packs of template template parameters when one of its parameters is also a pack. (#GH181166) - Clang no longer errors on overloads with different ref-qualifiers and constraints. (#GH120812) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index e71794b2d92c9..aadbac9a51026 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13570,7 +13570,7 @@ class Sema final : public SemaBase { bool SubstTemplateArgumentsInParameterMapping( ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc, const MultiLevelTemplateArgumentList &TemplateArgs, - TemplateArgumentListInfo &Out); + TemplateArgumentListInfo &Out, UnsignedOrNone ExpandedIndex); /// Retrieve the template argument list(s) that should be used to /// instantiate the definition of the given declaration. diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index ea03c3f408986..9fc8a3f3350c7 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -481,6 +481,7 @@ class ConstraintSatisfactionChecker { UnsignedOrNone PackSubstitutionIndex; ConstraintSatisfaction &Satisfaction; bool BuildExpression; + bool RemovePacksForFoldExpr = false; // The closest concept declaration when evaluating atomic constraints. ConceptDecl *ParentConcept = nullptr; @@ -538,10 +539,12 @@ class ConstraintSatisfactionChecker { SourceLocation TemplateNameLoc, UnsignedOrNone PackSubstitutionIndex, ConstraintSatisfaction &Satisfaction, - bool BuildExpression) + bool BuildExpression, + bool RemovePacksForFoldExpr) : S(SemaRef), Template(Template), TemplateNameLoc(TemplateNameLoc), PackSubstitutionIndex(PackSubstitutionIndex), - Satisfaction(Satisfaction), BuildExpression(BuildExpression) {} + Satisfaction(Satisfaction), BuildExpression(BuildExpression), + RemovePacksForFoldExpr(RemovePacksForFoldExpr) {} ExprResult Evaluate(const NormalizedConstraint &Constraint, const MultiLevelTemplateArgumentList &MLTAL); @@ -669,7 +672,8 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( if (S.SubstTemplateArgumentsInParameterMapping( Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL, - SubstArgs)) { + SubstArgs, + RemovePacksForFoldExpr ? PackSubstitutionIndex : std::nullopt)) { Satisfaction.IsSatisfied = false; return std::nullopt; } @@ -897,13 +901,15 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( } for (unsigned I = 0; I < *NumExpansions; I++) { - Sema::ArgPackSubstIndexRAII SubstIndex(S, I); + Satisfaction.IsSatisfied = false; Satisfaction.ContainsErrors = false; + ExprResult Expr = ConstraintSatisfactionChecker(S, Template, TemplateNameLoc, UnsignedOrNone(I), Satisfaction, - /*BuildExpression=*/false) + /*BuildExpression=*/false, + /*RemovePacksForFoldExpr=*/true) .Evaluate(Constraint.getNormalizedPattern(), *SubstitutedArgs); if (BuildExpression) { if (Out.isUnset() || !Expr.isUsable()) @@ -1230,7 +1236,8 @@ static bool CheckConstraintSatisfaction( ExprResult Res = ConstraintSatisfactionChecker( S, Template, TemplateIDRange.getBegin(), S.ArgPackSubstIndex, Satisfaction, - /*BuildExpression=*/ConvertedExpr != nullptr) + /*BuildExpression=*/ConvertedExpr != nullptr, + /*RemovePacksForFoldExpr=*/false) .Evaluate(*C, TemplateArgsLists); if (Res.isInvalid()) @@ -2186,8 +2193,9 @@ bool SubstituteParameterMappings::substitute( TemplateArgumentListInfo SubstArgs; llvm::SaveAndRestore<decltype(SemaRef.CurrentCachedTemplateArgs)> DoNotCacheDependentArgs(SemaRef.CurrentCachedTemplateArgs, nullptr); - if (SemaRef.SubstTemplateArgumentsInParameterMapping( - N.getParameterMapping(), N.getBeginLoc(), *MLTAL, SubstArgs)) + if (SemaRef.SubstTemplateArgumentsInParameterMapping(N.getParameterMapping(), + N.getBeginLoc(), *MLTAL, + SubstArgs, std::nullopt)) return true; Sema::CheckTemplateArgumentInfo CTAI; auto *TD = @@ -2259,7 +2267,8 @@ bool SubstituteParameterMappings::substitute(ConceptIdConstraint &CC) { const ASTTemplateArgumentListInfo *ArgsAsWritten = CSE->getTemplateArgsAsWritten(); if (SemaRef.SubstTemplateArgumentsInParameterMapping( - ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, Out)) + ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, Out, + std::nullopt)) return true; Sema::CheckTemplateArgumentInfo CTAI; if (SemaRef.CheckTemplateArgumentList(CSE->getNamedConcept(), diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 1544fd56b82ae..c98eadbeb8e76 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5173,8 +5173,8 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, EnterExpressionEvaluationContext EECtx( S, Sema::ExpressionEvaluationContext::Unevaluated, ImplicitConceptSpecializationDecl::Create( - S.getASTContext(), Concept->getDeclContext(), Concept->getLocation(), - CTAI.SugaredConverted)); + S.getASTContext(), Concept->getDeclContext(), + TypeLoc.getConceptNameLoc(), CTAI.SugaredConverted)); if (S.CheckConstraintSatisfaction( Concept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL, TypeLoc.getLocalSourceRange(), Satisfaction)) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index f168c99d1ac1a..cc2260d51a601 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -4397,11 +4397,12 @@ bool Sema::SubstTemplateArguments( bool Sema::SubstTemplateArgumentsInParameterMapping( ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc, const MultiLevelTemplateArgumentList &TemplateArgs, - TemplateArgumentListInfo &Out) { + TemplateArgumentListInfo &Out, UnsignedOrNone ExpandedIndex) { TemplateInstantiator Instantiator( TemplateInstantiator::ForParameterMappingSubstitution, *this, BaseLoc, TemplateArgs); - return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out); + return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out, + /*Uneval=*/false); } ExprResult diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index c3bf71dbf10df..41754574efa3a 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -672,10 +672,10 @@ class TreeTransform { /// routine. /// /// Returns true if an error occurred. - bool TransformTemplateArguments(const TemplateArgumentLoc *Inputs, - unsigned NumInputs, - TemplateArgumentListInfo &Outputs, - bool Uneval = false) { + bool TransformTemplateArguments( + const TemplateArgumentLoc *Inputs, unsigned NumInputs, + TemplateArgumentListInfo &Outputs, bool Uneval = false, + UnsignedOrNone ExpansionIndex = std::nullopt) { return TransformTemplateArguments(Inputs, Inputs + NumInputs, Outputs, Uneval); } @@ -694,11 +694,11 @@ class TreeTransform { /// routine. /// /// Returns true if an error occurred. - template<typename InputIterator> - bool TransformTemplateArguments(InputIterator First, - InputIterator Last, + template <typename InputIterator> + bool TransformTemplateArguments(InputIterator First, InputIterator Last, TemplateArgumentListInfo &Outputs, - bool Uneval = false); + bool Uneval = false, + UnsignedOrNone ExpansionIndex = std::nullopt); template <typename InputIterator> bool TransformConceptTemplateArguments(InputIterator First, @@ -5174,11 +5174,11 @@ class TemplateArgumentLocInventIterator { } }; -template<typename Derived> -template<typename InputIterator> +template <typename Derived> +template <typename InputIterator> bool TreeTransform<Derived>::TransformTemplateArguments( InputIterator First, InputIterator Last, TemplateArgumentListInfo &Outputs, - bool Uneval) { + bool Uneval, UnsignedOrNone ExpansionIndex) { for (TemplateArgumentLoc In : llvm::make_range(First, Last)) { TemplateArgumentLoc Out; if (In.getArgument().getKind() == TemplateArgument::Pack) { @@ -5218,7 +5218,17 @@ bool TreeTransform<Derived>::TransformTemplateArguments( std::optional<ForgetSubstitutionRAII> ForgetSubst; if (Info.ExpandUnderForgetSubstitions) ForgetSubst.emplace(getDerived()); - for (unsigned I = 0; I != *Info.NumExpansions; ++I) { + + unsigned Start, End; + if (ExpansionIndex.has_value()) { + Start = *ExpansionIndex; + End = Start + 1; + } else { + Start = 0U; + End = *Info.NumExpansions; + } + + for (unsigned I = Start; I != End; ++I) { Sema::ArgPackSubstIndexRAII SubstIndex(getSema(), I); TemplateArgumentLoc Out; diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp index 0312022912dca..48f21383b05d2 100644 --- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp +++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp @@ -598,3 +598,19 @@ static_assert(MutabilityAlias<Constant::alias>); static_assert(MutabilityAlias<Mutable::alias>); } + + +namespace GH199569 { +template <class T> constexpr bool foo(T); +template<typename... A> +concept callable = requires(A... a) { + foo(a...); +}; + +template<int> struct B {}; +template<int... N> requires((... & callable<B<N>>) != 0) +struct X {}; + +X<0, 1> x; + +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
