diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 1681ae8049a73..a7c1bb80a49db 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -451,6 +451,8 @@ Bug Fixes to C++ Support diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter. - Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326) - Mangle friend function templates with a constraint that depends on a template parameter from an enclosing template as members of the enclosing class. (#GH110247) +- 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 ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a9ce3681338d4..d616c3834c429 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -11253,6 +11253,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 99423b01114cc..c7d48b81bc034 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(USD) : CD, TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr, - ConstrainedParameter, EllipsisLoc); + ConstrainedParameter, Context.getTypeDeclType(ConstrainedParameter), + EllipsisLoc); } template @@ -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, 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 fd51fa4afcacb..e874ab563e2f8 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1655,6 +1655,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 SubstIndex; + if (SemaRef.ArgumentPackSubstitutionIndex == -1 && PackIndex) + SubstIndex.emplace(SemaRef, *PackIndex); + + return inherited::TransformSubstTemplateTypeParmType(TLB, TL); + } + CXXRecordDecl::LambdaDependencyKind ComputeLambdaDependency(LambdaScopeInfo *LSI) { if (auto TypeAlias = @@ -3078,6 +3093,58 @@ namespace { } // namespace +namespace { + +struct ExpandPackedTypeConstraints + : TreeTransform { + + using inherited = TreeTransform; + + 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(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(Result); + NewTL.setNameLoc(TL.getNameLoc()); + return Result; + } + + QualType TransformSubstTemplateTypeParmType(TypeLocBuilder &TLB, + SubstTemplateTypeParmTypeLoc TL) { + const SubstTemplateTypeParmType *T = TL.getTypePtr(); + if (T->getPackIndex()) { + SubstTemplateTypeParmTypeLoc TypeLoc = + TLB.push(TL.getType()); + TypeLoc.setNameLoc(TL.getNameLoc()); + return TypeLoc.getType(); + } + return inherited::TransformSubstTemplateTypeParmType(TLB, TL); + } + + bool SubstTemplateArguments(ArrayRef Args, + TemplateArgumentListInfo &Out) { + return inherited::TransformTemplateArguments(Args.begin(), Args.end(), Out); + } +}; + +} // namespace + bool Sema::SubstTypeConstraint( TemplateTypeParmDecl *Inst, const TypeConstraint *TC, const MultiLevelTemplateArgumentList &TemplateArgs, @@ -3086,9 +3153,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 void foo() { + // bar([](C 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. + // + // 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(ConstraintExpr)) { + assert(FE->getLHS()); + ConstraintExpr = FE->getLHS(); + } + auto *CSE = cast(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(TC->getImmediatelyDeclaredConstraint()) + ->getEllipsisLoc() + : SourceLocation()); + } + Inst->setTypeConstraint(TC->getConceptReference(), + TC->getImmediatelyDeclaredConstraint()); + return false; } TemplateArgumentListInfo InstArgs; @@ -3104,6 +3224,7 @@ bool Sema::SubstTypeConstraint( TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(), TC->getNamedConcept(), /*FoundDecl=*/TC->getConceptReference()->getFoundDecl(), &InstArgs, Inst, + Context.getTypeDeclType(Inst), Inst->isParameterPack() ? cast(TC->getImmediatelyDeclaredConstraint()) ->getEllipsisLoc() diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index a7beb9d222c3b..c44fc9c4194ca 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(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 14e242f009dc5..2257a4c2d975a 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 struct Overloaded : Ts... { + using Ts::operator()...; +}; + +template Overloaded(Ts...) -> Overloaded; + +template +concept same_as = __is_same(T, U); // #same_as + +template constexpr auto foo() { + return Overloaded{[](same_as auto value) { return value; }...}; // #lambda +} + +static_assert(foo()(123) == 123); +static_assert(foo()(2.718) == 2.718); + +static_assert(foo()('c')); +// expected-error@-1 {{no matching function}} + +// expected-note@#lambda {{constraints not satisfied}} +// expected-note@#lambda {{'same_as' evaluated to false}} +// expected-note@#same_as {{evaluated to false}} + +// expected-note@#lambda {{constraints not satisfied}} +// expected-note@#lambda {{'same_as' evaluated to false}} +// expected-note@#same_as {{evaluated to false}} + +template +concept C = same_as && same_as; // #C + +template constexpr auto bar() { + return ([]() { + return Overloaded{[](C auto value) { // #bar + return value; + }...}; + }.template operator()(), ...); +} +static_assert(bar()(3.14f)); // OK, bar() returns the last overload i.e. . + +static_assert(bar()(123)); +// expected-error@-1 {{no matching function}} +// expected-note@#bar {{constraints not satisfied}} +// expected-note@#bar {{'C' evaluated to false}} +// expected-note@#C {{evaluated to false}} + +// expected-note@#bar {{constraints not satisfied}} +// expected-note@#bar {{'C' evaluated to false}} +// expected-note@#C {{evaluated to false}} +// expected-note@#same_as 2{{evaluated to false}} + +} // namespace GH101754