Author: Richard Smith Date: 2021-05-12T18:45:34-07:00 New Revision: e1aa528d3aaf5fcf9c50d1e34b39dbde1e63801d
URL: https://github.com/llvm/llvm-project/commit/e1aa528d3aaf5fcf9c50d1e34b39dbde1e63801d DIFF: https://github.com/llvm/llvm-project/commit/e1aa528d3aaf5fcf9c50d1e34b39dbde1e63801d.diff LOG: Handle unexpanded packs appearing in type-constraints. For a type-constraint in a lambda signature, this makes the lambda contain an unexpanded pack; for requirements in a requires-expressions it makes the requires-expression contain an unexpanded pack; otherwise it's invalid. Added: Modified: clang/include/clang/AST/DeclTemplate.h clang/include/clang/AST/RecursiveASTVisitor.h clang/include/clang/Sema/Sema.h clang/lib/AST/DeclTemplate.cpp clang/lib/AST/Type.cpp clang/lib/Sema/SemaExprCXX.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaType.cpp clang/test/SemaTemplate/concepts.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 0cd8f00c623b..cbaa287f225a 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -160,9 +160,7 @@ class TemplateParameterList final /// Determine whether this template parameter list contains an /// unexpanded parameter pack. - bool containsUnexpandedParameterPack() const { - return ContainsUnexpandedParameterPack; - } + bool containsUnexpandedParameterPack() const; /// Determine whether this template parameter list contains a parameter pack. bool hasParameterPack() const { diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 9499564e46e0..4770a2fd42a2 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -481,6 +481,8 @@ template <typename Derived> class RecursiveASTVisitor { template <typename T> bool TraverseDeclTemplateParameterLists(T *D); + bool TraverseTemplateTypeParamDeclConstraints(const TemplateTypeParmDecl *D); + bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL, unsigned Count); bool TraverseArrayTypeLocHelper(ArrayTypeLoc TL); @@ -658,8 +660,14 @@ bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) { // As a syntax visitor, by default we want to ignore declarations for // implicit declarations (ones not typed explicitly by the user). - if (!getDerived().shouldVisitImplicitCode() && D->isImplicit()) + if (!getDerived().shouldVisitImplicitCode() && D->isImplicit()) { + // For an implicit template type parameter, its type constraints are not + // implicit and are not represented anywhere else. We still need to visit + // them. + if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D)) + return TraverseTemplateTypeParamDeclConstraints(TTPD); return true; + } switch (D->getKind()) { #define ABSTRACT_DECL(DECL) @@ -1779,10 +1787,9 @@ DEF_TRAVERSE_DECL(BuiltinTemplateDecl, { TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); }) -DEF_TRAVERSE_DECL(TemplateTypeParmDecl, { - // D is the "T" in something like "template<typename T> class vector;" - if (D->getTypeForDecl()) - TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0))); +template <typename Derived> +bool RecursiveASTVisitor<Derived>::TraverseTemplateTypeParamDeclConstraints( + const TemplateTypeParmDecl *D) { if (const auto *TC = D->getTypeConstraint()) { if (Expr *IDC = TC->getImmediatelyDeclaredConstraint()) { TRY_TO(TraverseStmt(IDC)); @@ -1794,6 +1801,14 @@ DEF_TRAVERSE_DECL(TemplateTypeParmDecl, { TRY_TO(TraverseConceptReference(*TC)); } } + return true; +} + +DEF_TRAVERSE_DECL(TemplateTypeParmDecl, { + // D is the "T" in something like "template<typename T> class vector;" + if (D->getTypeForDecl()) + TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0))); + TRY_TO(TraverseTemplateTypeParamDeclConstraints(D)); if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) TRY_TO(TraverseTypeLoc(D->getDefaultArgumentInfo()->getTypeLoc())); }) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 53ff0f1e634d..135da703c4df 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7455,6 +7455,11 @@ class Sema final { TemplateIdAnnotation *TypeConstraint, TemplateTypeParmDecl *ConstrainedParameter, SourceLocation EllipsisLoc); + bool BuildTypeConstraint(const CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstraint, + TemplateTypeParmDecl *ConstrainedParameter, + SourceLocation EllipsisLoc, + bool AllowUnexpandedPack); bool AttachTypeConstraint(NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo, diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index ba4a5359f937..e5b1833a02f2 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -69,12 +69,6 @@ TemplateParameterList::TemplateParameterList(const ASTContext& C, TTP->getTemplateParameters()->containsUnexpandedParameterPack()) ContainsUnexpandedParameterPack = true; } else if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(P)) { - // FIXME: The type parameter might have a constraint that has not been - // attached yet. If so, we can compute the wrong value for - // 'ContainsUnexpandedParameterPack' here. Note that this only happens - // for implicit parameters, for which the ParmVarDecl will correctly - // track that it contains an unexpanded parameter pack, so this should - // not be problematic in practice. if (const TypeConstraint *TC = TTP->getTypeConstraint()) { if (TC->getImmediatelyDeclaredConstraint() ->containsUnexpandedParameterPack()) @@ -95,6 +89,30 @@ TemplateParameterList::TemplateParameterList(const ASTContext& C, } } +bool TemplateParameterList::containsUnexpandedParameterPack() const { + if (ContainsUnexpandedParameterPack) + return true; + if (!HasConstrainedParameters) + return false; + + // An implicit constrained parameter might have had a use of an unexpanded + // pack added to it after the template parameter list was created. All + // implicit parameters are at the end of the parameter list. + for (const NamedDecl *Param : llvm::reverse(asArray())) { + if (!Param->isImplicit()) + break; + + if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) { + const auto *TC = TTP->getTypeConstraint(); + if (TC && TC->getImmediatelyDeclaredConstraint() + ->containsUnexpandedParameterPack()) + return true; + } + } + + return false; +} + TemplateParameterList * TemplateParameterList::Create(const ASTContext &C, SourceLocation TemplateLoc, SourceLocation LAngleLoc, diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 611c30d9c767..d58cb43b6e7b 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -4397,8 +4397,8 @@ AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword, if (TypeConstraintConcept) { TemplateArgument *ArgBuffer = getArgBuffer(); for (const TemplateArgument &Arg : TypeConstraintArgs) { - addDependence(toTypeDependence( - Arg.getDependence() & TemplateArgumentDependence::UnexpandedPack)); + addDependence( + toSyntacticDependence(toTypeDependence(Arg.getDependence()))); new (ArgBuffer++) TemplateArgument(Arg); } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 4a4c8116d6f1..b31c484b1170 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8629,8 +8629,9 @@ Sema::ActOnCompoundRequirement( /*ParameterPack=*/false, /*HasTypeConstraint=*/true); - if (ActOnTypeConstraint(SS, TypeConstraint, TParam, - /*EllpsisLoc=*/SourceLocation())) + if (BuildTypeConstraint(SS, TypeConstraint, TParam, + /*EllpsisLoc=*/SourceLocation(), + /*AllowUnexpandedPack=*/true)) // Just produce a requirement with no type requirements. return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, {}); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index dfd4297bbc8b..ae368ba75291 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1105,8 +1105,17 @@ bool Sema::ActOnTypeConstraint(const CXXScopeSpec &SS, TemplateIdAnnotation *TypeConstr, TemplateTypeParmDecl *ConstrainedParameter, SourceLocation EllipsisLoc) { - ConceptDecl *CD = - cast<ConceptDecl>(TypeConstr->Template.get().getAsTemplateDecl()); + return BuildTypeConstraint(SS, TypeConstr, ConstrainedParameter, EllipsisLoc, + false); +} + +bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstr, + TemplateTypeParmDecl *ConstrainedParameter, + SourceLocation EllipsisLoc, + bool AllowUnexpandedPack) { + TemplateName TN = TypeConstr->Template.get(); + ConceptDecl *CD = cast<ConceptDecl>(TN.getAsTemplateDecl()); // C++2a [temp.param]p4: // [...] The concept designated by a type-constraint shall be a type @@ -1126,15 +1135,24 @@ bool Sema::ActOnTypeConstraint(const CXXScopeSpec &SS, return true; } + DeclarationNameInfo ConceptName(DeclarationName(TypeConstr->Name), + TypeConstr->TemplateNameLoc); + TemplateArgumentListInfo TemplateArgs; if (TypeConstr->LAngleLoc.isValid()) { TemplateArgs = makeTemplateArgumentListInfo(*this, *TypeConstr); + + if (EllipsisLoc.isInvalid() && !AllowUnexpandedPack) { + for (TemplateArgumentLoc Arg : TemplateArgs.arguments()) { + if (DiagnoseUnexpandedParameterPack(Arg, UPPC_TypeConstraint)) + return true; + } + } } return AttachTypeConstraint( SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(), - DeclarationNameInfo(DeclarationName(TypeConstr->Name), - TypeConstr->TemplateNameLoc), CD, + ConceptName, CD, TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr, ConstrainedParameter, EllipsisLoc); } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 9801dd6ffb66..e1c5d3367425 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -3223,31 +3223,52 @@ InventTemplateParameter(TypeProcessingState &state, QualType T, // extract its type constraints to attach to the template parameter. AutoTypeLoc AutoLoc = TrailingTSI->getTypeLoc().getContainedAutoTypeLoc(); TemplateArgumentListInfo TAL(AutoLoc.getLAngleLoc(), AutoLoc.getRAngleLoc()); - for (unsigned Idx = 0; Idx < AutoLoc.getNumArgs(); ++Idx) + bool Invalid = false; + for (unsigned Idx = 0; Idx < AutoLoc.getNumArgs(); ++Idx) { + if (D.getEllipsisLoc().isInvalid() && !Invalid && + S.DiagnoseUnexpandedParameterPack(AutoLoc.getArgLoc(Idx), + Sema::UPPC_TypeConstraint)) + Invalid = true; TAL.addArgument(AutoLoc.getArgLoc(Idx)); + } - S.AttachTypeConstraint(AutoLoc.getNestedNameSpecifierLoc(), - AutoLoc.getConceptNameInfo(), - AutoLoc.getNamedConcept(), - AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr, - InventedTemplateParam, D.getEllipsisLoc()); + if (!Invalid) { + S.AttachTypeConstraint( + AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(), + AutoLoc.getNamedConcept(), + AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr, + InventedTemplateParam, D.getEllipsisLoc()); + } } else { // The 'auto' appears in the decl-specifiers; we've not finished forming // TypeSourceInfo for it yet. TemplateIdAnnotation *TemplateId = D.getDeclSpec().getRepAsTemplateId(); TemplateArgumentListInfo TemplateArgsInfo; + bool Invalid = false; if (TemplateId->LAngleLoc.isValid()) { ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); S.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo); + + if (D.getEllipsisLoc().isInvalid()) { + for (TemplateArgumentLoc Arg : TemplateArgsInfo.arguments()) { + if (S.DiagnoseUnexpandedParameterPack(Arg, + Sema::UPPC_TypeConstraint)) { + Invalid = true; + break; + } + } + } + } + if (!Invalid) { + S.AttachTypeConstraint( + D.getDeclSpec().getTypeSpecScope().getWithLocInContext(S.Context), + DeclarationNameInfo(DeclarationName(TemplateId->Name), + TemplateId->TemplateNameLoc), + cast<ConceptDecl>(TemplateId->Template.get().getAsTemplateDecl()), + TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr, + InventedTemplateParam, D.getEllipsisLoc()); } - S.AttachTypeConstraint( - D.getDeclSpec().getTypeSpecScope().getWithLocInContext(S.Context), - DeclarationNameInfo(DeclarationName(TemplateId->Name), - TemplateId->TemplateNameLoc), - cast<ConceptDecl>(TemplateId->Template.get().getAsTemplateDecl()), - TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr, - InventedTemplateParam, D.getEllipsisLoc()); } } diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index 753a84c35e1f..01a2618d0ab1 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -74,3 +74,55 @@ namespace PR50306 { template void f<char>(); // OK template void f<int>(); // expected-note {{in instantiation of}} } + +namespace PackInTypeConstraint { + template<typename T, typename U> concept C = sizeof(T) == sizeof(int); // expected-note 3{{}} + + template<typename ...T, C<T> U> void h1(); // expected-error {{type constraint contains unexpanded parameter pack 'T'}} + template<typename ...T, C<T> ...U> void h2(); + template<typename ...T> void h3(C<T> auto); // expected-error {{type constraint contains unexpanded parameter pack 'T'}} + template<typename ...T> void h4(C<T> auto...); + + template<typename ...T> void f1() { + []<C<T> U>(U u){}(T()); // expected-error {{unexpanded parameter pack 'T'}} + } + template<typename ...T> void f2() { + ([]<C<T> U>(U u){}(T()), ...); // expected-error {{no match}} expected-note 2{{}} + } + template void f2<int, int, int>(); // OK + template void f2<int, char, double>(); // expected-note {{in instantiation of}} + void f3() { + ([]<typename ...T, C<T> U>(U u){}(0), // expected-error {{type constraint contains unexpanded parameter pack 'T'}} + ...); // expected-error {{does not contain any unexpanded}} + } + + template<typename ...T> void g1() { + [](C<T> auto){}(T()); // expected-error {{expression contains unexpanded parameter pack 'T'}} + } + template<typename ...T> void g2() { + ([](C<T> auto){}(T()), ...); // expected-error {{no matching function}} expected-note {{constraints not satisfied}} expected-note {{because}} + } + template void g2<int, int, int>(); // OK + template void g2<int, char, double>(); // expected-note {{in instantiation of}} + void g3() { + ([]<typename ...T>(C<T> auto){}(1), // expected-error {{type constraint contains unexpanded parameter pack 'T'}} + ...); // expected-error {{does not contain any unexpanded}} + } + + template<typename ...T> void g4() { + []() -> C<T> auto{ return T(); }(); // expected-error {{expression contains unexpanded parameter pack 'T'}} + } + template<typename ...T> void g5() { + ([]() -> C<T> auto{ // expected-error-re {{deduced type {{.*}} does not satisfy}} + return T(); + }(), ...); + } + template void g5<int, int, int>(); // OK + template void g5<int, char, double>(); // expected-note {{in instantiation of}} + void g6() { + ([]<typename ...T>() -> C<T> auto{ // expected-error {{declaration type contains unexpanded parameter pack 'T'}} + return T(); // expected-error {{expression contains unexpanded parameter pack 'T'}} + }(), + ...); // expected-error {{does not contain any unexpanded}} + } +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits