https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/109518
>From 1deb74f00b518eb595dbe49be3234a05c3753c6f Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn0...@gmail.com> Date: Sat, 21 Sep 2024 12:48:27 +0800 Subject: [PATCH 1/3] [Clang][Sema] Retain the expanding index for unevaluated type constraints --- clang/include/clang/Sema/Sema.h | 1 + clang/lib/Sema/SemaTemplate.cpp | 6 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 115 +++++++++++++++++- clang/lib/Sema/SemaType.cpp | 8 +- .../SemaCXX/fold_lambda_with_variadics.cpp | 54 ++++++++ 5 files changed, 177 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index b86861ce7e8cfa..f711fe952d35e9 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -11252,6 +11252,7 @@ class Sema final : public SemaBase { ConceptDecl *NamedConcept, NamedDecl *FoundDecl, const TemplateArgumentListInfo *TemplateArgs, TemplateTypeParmDecl *ConstrainedParameter, + QualType ConstrainedType, SourceLocation EllipsisLoc); bool AttachTypeConstraint(AutoTypeLoc TL, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 16f4542d785715..bdfb0f4c95afb8 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1134,7 +1134,8 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS, SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(), ConceptName, CD, /*FoundDecl=*/USD ? cast<NamedDecl>(USD) : CD, TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr, - ConstrainedParameter, EllipsisLoc); + ConstrainedParameter, Context.getTypeDeclType(ConstrainedParameter), + EllipsisLoc); } template <typename ArgumentLocAppender> @@ -1191,6 +1192,7 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS, ConceptDecl *NamedConcept, NamedDecl *FoundDecl, const TemplateArgumentListInfo *TemplateArgs, TemplateTypeParmDecl *ConstrainedParameter, + QualType ConstrainedType, SourceLocation EllipsisLoc) { // C++2a [temp.param]p4: // [...] If Q is of the form C<A1, ..., An>, then let E' be @@ -1199,7 +1201,7 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS, TemplateArgs ? ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs) : nullptr; - QualType ParamAsArgument(ConstrainedParameter->getTypeForDecl(), 0); + QualType ParamAsArgument = ConstrainedType; ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint( *this, NS, NameInfo, NamedConcept, FoundDecl, diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 7481c700019dc8..d62ac5c1de3cc9 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1646,6 +1646,21 @@ namespace { SubstTemplateTypeParmPackTypeLoc TL, bool SuppressObjCLifetime); + QualType + TransformSubstTemplateTypeParmType(TypeLocBuilder &TLB, + SubstTemplateTypeParmTypeLoc TL) { + if (SemaRef.CodeSynthesisContexts.back().Kind != + Sema::CodeSynthesisContext::ConstraintSubstitution) + return inherited::TransformSubstTemplateTypeParmType(TLB, TL); + + auto PackIndex = TL.getTypePtr()->getPackIndex(); + std::optional<Sema::ArgumentPackSubstitutionIndexRAII> SubstIndex; + if (SemaRef.ArgumentPackSubstitutionIndex == -1 && PackIndex) + SubstIndex.emplace(SemaRef, *PackIndex); + + return inherited::TransformSubstTemplateTypeParmType(TLB, TL); + } + CXXRecordDecl::LambdaDependencyKind ComputeLambdaDependency(LambdaScopeInfo *LSI) { if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(getSema()); @@ -3056,6 +3071,46 @@ namespace { } // namespace +namespace { + +struct ExpandPackedTypeConstraints + : TreeTransform<ExpandPackedTypeConstraints> { + + using inherited = TreeTransform<ExpandPackedTypeConstraints>; + + ExpandPackedTypeConstraints(Sema &SemaRef) : inherited(SemaRef) {} + + using inherited::TransformTemplateTypeParmType; + + QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB, + TemplateTypeParmTypeLoc TL, bool) { + const TemplateTypeParmType *T = TL.getTypePtr(); + if (!T->isParameterPack()) { + TemplateTypeParmTypeLoc NewTL = + TLB.push<TemplateTypeParmTypeLoc>(TL.getType()); + NewTL.setNameLoc(TL.getNameLoc()); + return TL.getType(); + } + + assert(SemaRef.ArgumentPackSubstitutionIndex != -1); + + QualType Result = SemaRef.Context.getSubstTemplateTypeParmType( + TL.getType(), T->getDecl(), T->getIndex(), + SemaRef.ArgumentPackSubstitutionIndex); + SubstTemplateTypeParmTypeLoc NewTL = + TLB.push<SubstTemplateTypeParmTypeLoc>(Result); + NewTL.setNameLoc(TL.getNameLoc()); + return Result; + } + + bool SubstTemplateArguments(ArrayRef<TemplateArgumentLoc> Args, + TemplateArgumentListInfo &Out) { + return inherited::TransformTemplateArguments(Args.begin(), Args.end(), Out); + } +}; + +} // namespace + bool Sema::SubstTypeConstraint( TemplateTypeParmDecl *Inst, const TypeConstraint *TC, const MultiLevelTemplateArgumentList &TemplateArgs, @@ -3064,9 +3119,62 @@ bool Sema::SubstTypeConstraint( TC->getTemplateArgsAsWritten(); if (!EvaluateConstraints) { - Inst->setTypeConstraint(TC->getConceptReference(), - TC->getImmediatelyDeclaredConstraint()); - return false; + bool ShouldExpandExplicitTemplateArgs = + TemplArgInfo && ArgumentPackSubstitutionIndex != -1 && + llvm::any_of(TemplArgInfo->arguments(), [](auto &Arg) { + return Arg.getArgument().containsUnexpandedParameterPack(); + }); + + // We want to transform the packs into Subst* nodes for type constraints + // inside a pack expansion. For example, + // + // template <class... Ts> void foo() { + // bar([](C<Ts> auto value) {}...); + // } + // + // As we expand Ts in the process of instantiating foo(), and retain + // the original template depths of Ts until the constraint evaluation, we + // would otherwise have no chance to expand Ts by the time of evaluating + // C<auto, Ts>. + // + // So we form a Subst* node for Ts along with a proper substitution index + // here, and substitute the node with a complete MLTAL later in evaluation. + if (ShouldExpandExplicitTemplateArgs) { + TemplateArgumentListInfo InstArgs; + InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc); + InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc); + if (ExpandPackedTypeConstraints(*this).SubstTemplateArguments( + TemplArgInfo->arguments(), InstArgs)) + return true; + + // The type of the original parameter. + auto *ConstraintExpr = TC->getImmediatelyDeclaredConstraint(); + QualType ConstrainedType; + + if (auto *FE = dyn_cast<CXXFoldExpr>(ConstraintExpr)) { + assert(FE->getLHS()); + ConstraintExpr = FE->getLHS(); + } + auto *CSE = cast<ConceptSpecializationExpr>(ConstraintExpr); + assert(!CSE->getTemplateArguments().empty() && + "Empty template arguments?"); + ConstrainedType = CSE->getTemplateArguments()[0].getAsType(); + assert(!ConstrainedType.isNull() && + "Failed to extract the original ConstrainedType?"); + + return AttachTypeConstraint( + TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(), + TC->getNamedConcept(), + /*FoundDecl=*/TC->getConceptReference()->getFoundDecl(), &InstArgs, + Inst, ConstrainedType, + Inst->isParameterPack() + ? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint()) + ->getEllipsisLoc() + : SourceLocation()); + } + Inst->setTypeConstraint(TC->getConceptReference(), + TC->getImmediatelyDeclaredConstraint()); + return false; } TemplateArgumentListInfo InstArgs; @@ -3082,6 +3190,7 @@ bool Sema::SubstTypeConstraint( TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(), TC->getNamedConcept(), /*FoundDecl=*/TC->getConceptReference()->getFoundDecl(), &InstArgs, Inst, + Context.getTypeDeclType(Inst), Inst->isParameterPack() ? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint()) ->getEllipsisLoc() diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 950bd6db0359d1..62a7357efcf9cc 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -3035,7 +3035,9 @@ InventTemplateParameter(TypeProcessingState &state, QualType T, AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(), AutoLoc.getNamedConcept(), /*FoundDecl=*/AutoLoc.getFoundDecl(), AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr, - InventedTemplateParam, D.getEllipsisLoc()); + InventedTemplateParam, + S.Context.getTypeDeclType(InventedTemplateParam), + D.getEllipsisLoc()); } } else { // The 'auto' appears in the decl-specifiers; we've not finished forming @@ -3072,7 +3074,9 @@ InventTemplateParameter(TypeProcessingState &state, QualType T, /*FoundDecl=*/ USD ? cast<NamedDecl>(USD) : CD, TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr, - InventedTemplateParam, D.getEllipsisLoc()); + InventedTemplateParam, + S.Context.getTypeDeclType(InventedTemplateParam), + D.getEllipsisLoc()); } } } diff --git a/clang/test/SemaCXX/fold_lambda_with_variadics.cpp b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp index 14e242f009dc51..2257a4c2d975a8 100644 --- a/clang/test/SemaCXX/fold_lambda_with_variadics.cpp +++ b/clang/test/SemaCXX/fold_lambda_with_variadics.cpp @@ -179,3 +179,57 @@ void foo() { } } // namespace GH99877 + +namespace GH101754 { + +template <typename... Ts> struct Overloaded : Ts... { + using Ts::operator()...; +}; + +template <typename... Ts> Overloaded(Ts...) -> Overloaded<Ts...>; + +template <class T, class U> +concept same_as = __is_same(T, U); // #same_as + +template <typename... Ts> constexpr auto foo() { + return Overloaded{[](same_as<Ts> auto value) { return value; }...}; // #lambda +} + +static_assert(foo<int, double>()(123) == 123); +static_assert(foo<int, double>()(2.718) == 2.718); + +static_assert(foo<int, double>()('c')); +// expected-error@-1 {{no matching function}} + +// expected-note@#lambda {{constraints not satisfied}} +// expected-note@#lambda {{'same_as<char, int>' evaluated to false}} +// expected-note@#same_as {{evaluated to false}} + +// expected-note@#lambda {{constraints not satisfied}} +// expected-note@#lambda {{'same_as<char, double>' evaluated to false}} +// expected-note@#same_as {{evaluated to false}} + +template <class T, class U, class V> +concept C = same_as<T, U> && same_as<U, V>; // #C + +template <typename... Ts> constexpr auto bar() { + return ([]<class Up>() { + return Overloaded{[](C<Up, Ts> auto value) { // #bar + return value; + }...}; + }.template operator()<Ts>(), ...); +} +static_assert(bar<int, float>()(3.14f)); // OK, bar() returns the last overload i.e. <float>. + +static_assert(bar<int, float>()(123)); +// expected-error@-1 {{no matching function}} +// expected-note@#bar {{constraints not satisfied}} +// expected-note@#bar {{'C<int, float, int>' evaluated to false}} +// expected-note@#C {{evaluated to false}} + +// expected-note@#bar {{constraints not satisfied}} +// expected-note@#bar {{'C<int, float, float>' evaluated to false}} +// expected-note@#C {{evaluated to false}} +// expected-note@#same_as 2{{evaluated to false}} + +} // namespace GH101754 >From cf025f0ad63035b5812471ed8c902e9660fbfc28 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sat, 21 Sep 2024 22:47:10 +0800 Subject: [PATCH 2/3] Avoid nested SubstTemplateTypeParmTypes --- clang/lib/Sema/SemaTemplateInstantiate.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index d62ac5c1de3cc9..34635ef3860803 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -3103,6 +3103,18 @@ struct ExpandPackedTypeConstraints return Result; } + QualType TransformSubstTemplateTypeParmType(TypeLocBuilder &TLB, + SubstTemplateTypeParmTypeLoc TL) { + const SubstTemplateTypeParmType *T = TL.getTypePtr(); + if (T->getPackIndex()) { + SubstTemplateTypeParmTypeLoc TypeLoc = + TLB.push<SubstTemplateTypeParmTypeLoc>(TL.getType()); + TypeLoc.setNameLoc(TL.getNameLoc()); + return TypeLoc.getType(); + } + return inherited::TransformSubstTemplateTypeParmType(TLB, TL); + } + bool SubstTemplateArguments(ArrayRef<TemplateArgumentLoc> Args, TemplateArgumentListInfo &Out) { return inherited::TransformTemplateArguments(Args.begin(), Args.end(), Out); >From 190125736bbaa1b79f77e226d864c944a15f5bf6 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sat, 21 Sep 2024 23:15:34 +0800 Subject: [PATCH 3/3] Release note --- clang/docs/ReleaseNotes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index f4535db7356194..c069bb0b4fa597 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -407,6 +407,8 @@ Bug Fixes to C++ Support - Clang now uses the correct set of template argument lists when comparing the constraints of out-of-line definitions and member templates explicitly specialized for a given implicit instantiation of a class template. (#GH102320) +- Fixed an issue in constraint evaluation, where type constraints on the lambda expression + containing outer unexpanded parameters were not correctly expanded. (#GH101754) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits