diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -605,8 +605,8 @@ } void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) { - if (const auto *E = D->getPlaceholderTypeConstraint()) - Visit(E); + if (const auto *TC = D->getPlaceholderTypeConstraint()) + Visit(TC->getImmediatelyDeclaredConstraint()); if (D->hasDefaultArgument()) Visit(D->getDefaultArgument(), SourceRange(), D->getDefaultArgStorage().getInheritedFrom(), diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1415,7 +1415,7 @@ protected TemplateParmPosition, private llvm::TrailingObjects, - Expr *> { + TypeConstraint> { friend class ASTDeclReader; friend TrailingObjects; @@ -1428,12 +1428,20 @@ // down here to save memory. /// Whether this non-type template parameter is a parameter pack. - bool ParameterPack; + bool ParameterPack : 1; + + /// Whether this template type parameter has a type-constraint construct. + bool HasTypeConstraint : 1; + + /// Whether the type constraint has been initialized. This can be false if the + /// constraint was not initialized yet or if there was an error forming the + /// type constraint. + bool TypeConstraintInitialized : 1; /// Whether this non-type template parameter is an "expanded" /// parameter pack, meaning that its type is a pack expansion and we /// already know the set of types that expansion expands to. - bool ExpandedParameterPack = false; + bool ExpandedParameterPack : 1; /// The number of types in an expanded parameter pack. unsigned NumExpandedTypes = 0; @@ -1445,15 +1453,17 @@ NonTypeTemplateParmDecl(DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, unsigned D, unsigned P, - IdentifierInfo *Id, QualType T, - bool ParameterPack, TypeSourceInfo *TInfo) + IdentifierInfo *Id, QualType T, bool ParameterPack, + bool HasTypeConstraint, TypeSourceInfo *TInfo) : DeclaratorDecl(NonTypeTemplateParm, DC, IdLoc, Id, T, TInfo, StartLoc), - TemplateParmPosition(D, P), ParameterPack(ParameterPack) {} + TemplateParmPosition(D, P), ParameterPack(ParameterPack), + HasTypeConstraint(HasTypeConstraint), TypeConstraintInitialized(false), + ExpandedParameterPack(false) {} NonTypeTemplateParmDecl(DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, unsigned D, unsigned P, IdentifierInfo *Id, QualType T, - TypeSourceInfo *TInfo, + bool HasTypeConstraint, TypeSourceInfo *TInfo, ArrayRef ExpandedTypes, ArrayRef ExpandedTInfos); @@ -1461,12 +1471,14 @@ static NonTypeTemplateParmDecl * Create(const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, unsigned D, unsigned P, IdentifierInfo *Id, - QualType T, bool ParameterPack, TypeSourceInfo *TInfo); + QualType T, bool ParameterPack, bool HasTypeConstraint, + TypeSourceInfo *TInfo); static NonTypeTemplateParmDecl * Create(const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, unsigned D, unsigned P, IdentifierInfo *Id, - QualType T, TypeSourceInfo *TInfo, ArrayRef ExpandedTypes, + QualType T, bool HasTypeConstraint, TypeSourceInfo *TInfo, + ArrayRef ExpandedTypes, ArrayRef ExpandedTInfos); static NonTypeTemplateParmDecl *CreateDeserialized(ASTContext &C, @@ -1586,33 +1598,33 @@ return TypesAndInfos[I].second; } - /// Return the constraint introduced by the placeholder type of this non-type - /// template parameter (if any). - Expr *getPlaceholderTypeConstraint() const { - return hasPlaceholderTypeConstraint() ? *getTrailingObjects() : - nullptr; + /// Returns the type constraint associated with this template parameter (if + /// any). + const TypeConstraint *getPlaceholderTypeConstraint() const { + return TypeConstraintInitialized ? getTrailingObjects() : + nullptr; } - void setPlaceholderTypeConstraint(Expr *E) { - *getTrailingObjects() = E; - } + void setPlaceholderTypeConstraint(NestedNameSpecifierLoc NNS, + DeclarationNameInfo NameInfo, NamedDecl *FoundDecl, + ConceptDecl *CD, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + Expr *ImmediatelyDeclaredConstraint); - /// Determine whether this non-type template parameter's type has a - /// placeholder with a type-constraint. + /// Determine whether this template parameter has a type-constraint. bool hasPlaceholderTypeConstraint() const { - auto *AT = getType()->getContainedAutoType(); - return AT && AT->isConstrained(); + return HasTypeConstraint; } /// \brief Get the associated-constraints of this template parameter. - /// This will either be a vector of size 1 containing the immediately-declared - /// constraint introduced by the placeholder type, or an empty vector. + /// This will either be the immediately-introduced constraint or empty. /// - /// Use this instead of getPlaceholderImmediatelyDeclaredConstraint for - /// concepts APIs that accept an ArrayRef of constraint expressions. + /// Use this instead of getPlaceholderTypeConstraint for concepts APIs that + /// accept an ArrayRef of constraint expressions. void getAssociatedConstraints(llvm::SmallVectorImpl &AC) const { - if (Expr *E = getPlaceholderTypeConstraint()) - AC.push_back(E); + if (HasTypeConstraint) + AC.push_back( + getPlaceholderTypeConstraint()->getImmediatelyDeclaredConstraint()); } // Implement isa/cast/dyncast/etc. diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -479,6 +479,7 @@ bool TraverseDeclTemplateParameterLists(T *D); bool TraverseTemplateTypeParamDeclConstraints(const TemplateTypeParmDecl *D); + bool TraverseTemplateTypeParamDeclConstraints(const NonTypeTemplateParmDecl *D); bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL, unsigned Count); @@ -1925,6 +1926,14 @@ return true; } +template +bool RecursiveASTVisitor::TraverseTemplateTypeParamDeclConstraints( + const NonTypeTemplateParmDecl *D) { + if (const auto *TC = D->getPlaceholderTypeConstraint()) + TRY_TO(TraverseTypeConstraint(TC)); + return true; +} + DEF_TRAVERSE_DECL(TemplateTypeParmDecl, { // D is the "T" in something like "template class vector;" if (D->getTypeForDecl()) @@ -2283,6 +2292,7 @@ DEF_TRAVERSE_DECL(ImplicitParamDecl, { TRY_TO(TraverseVarHelper(D)); }) DEF_TRAVERSE_DECL(NonTypeTemplateParmDecl, { + TRY_TO(TraverseTemplateTypeParamDeclConstraints(D)); // A non-type template parameter, e.g. "S" in template class Foo ... TRY_TO(TraverseDeclaratorHelper(D)); if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7942,23 +7942,24 @@ bool ActOnTypeConstraint(const CXXScopeSpec &SS, TemplateIdAnnotation *TypeConstraint, - TemplateTypeParmDecl *ConstrainedParameter, + NamedDecl *ConstrainedParameter, SourceLocation EllipsisLoc); bool BuildTypeConstraint(const CXXScopeSpec &SS, TemplateIdAnnotation *TypeConstraint, - TemplateTypeParmDecl *ConstrainedParameter, + NamedDecl *ConstrainedParameter, SourceLocation EllipsisLoc, bool AllowUnexpandedPack); - bool AttachTypeConstraint(NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo, ConceptDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs, TemplateTypeParmDecl *ConstrainedParameter, SourceLocation EllipsisLoc); - - bool AttachTypeConstraint(AutoTypeLoc TL, - NonTypeTemplateParmDecl *ConstrainedParameter, + bool AttachTypeConstraint(NestedNameSpecifierLoc NS, + DeclarationNameInfo NameInfo, + ConceptDecl *NamedConcept, + const TemplateArgumentListInfo *TemplateArgs, + NonTypeTemplateParmDecl *NTTP, SourceLocation EllipsisLoc); bool RequireStructuralType(QualType T, SourceLocation Loc); @@ -7971,7 +7972,7 @@ unsigned Depth, unsigned Position, SourceLocation EqualLoc, - Expr *DefaultArg); + Expr *DefaultArg, bool HasTypeConstraint); NamedDecl *ActOnTemplateTemplateParameter(Scope *S, SourceLocation TmpLoc, TemplateParameterList *Params, @@ -9915,6 +9916,10 @@ bool SubstTypeConstraint(TemplateTypeParmDecl *Inst, const TypeConstraint *TC, const MultiLevelTemplateArgumentList &TemplateArgs, bool EvaluateConstraint); + bool SubstTypeConstraint(NonTypeTemplateParmDecl *Inst, + const TypeConstraint *TC, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceLocation EllipsisLoc, bool EvaluateConstraint); bool InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD, ParmVarDecl *Param); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -696,6 +696,11 @@ if (const auto *NTTP = dyn_cast(*P)) { ID.AddInteger(1); ID.AddBoolean(NTTP->isParameterPack()); + const TypeConstraint *TC = NTTP->getPlaceholderTypeConstraint(); + ID.AddBoolean(TC != nullptr); + if (TC) + TC->getImmediatelyDeclaredConstraint()->Profile(ID, C, + /*Canonical=*/true); ID.AddPointer(NTTP->getType().getCanonicalType().getAsOpaquePtr()); if (NTTP->isExpandedParameterPack()) { ID.AddBoolean(true); @@ -822,31 +827,29 @@ getTrivialTypeSourceInfo(ExpandedTypes.back())); } - Param = NonTypeTemplateParmDecl::Create(*this, getTranslationUnitDecl(), - SourceLocation(), - SourceLocation(), - NTTP->getDepth(), - NTTP->getPosition(), nullptr, - T, - TInfo, - ExpandedTypes, - ExpandedTInfos); + Param = NonTypeTemplateParmDecl::Create( + *this, getTranslationUnitDecl(), SourceLocation(), SourceLocation(), + NTTP->getDepth(), NTTP->getPosition(), nullptr, T, + NTTP->hasPlaceholderTypeConstraint(), TInfo, ExpandedTypes, + ExpandedTInfos); } else { - Param = NonTypeTemplateParmDecl::Create(*this, getTranslationUnitDecl(), - SourceLocation(), - SourceLocation(), - NTTP->getDepth(), - NTTP->getPosition(), nullptr, - T, - NTTP->isParameterPack(), - TInfo); + Param = NonTypeTemplateParmDecl::Create( + *this, getTranslationUnitDecl(), SourceLocation(), SourceLocation(), + NTTP->getDepth(), NTTP->getPosition(), nullptr, T, + NTTP->isParameterPack(), NTTP->hasPlaceholderTypeConstraint(), + TInfo); } - if (AutoType *AT = T->getContainedAutoType()) { - if (AT->isConstrained()) { - Param->setPlaceholderTypeConstraint( - canonicalizeImmediatelyDeclaredConstraint( - *this, NTTP->getPlaceholderTypeConstraint(), T)); - } + if (const auto *TC = NTTP->getPlaceholderTypeConstraint()) { + Expr *NewIDC = canonicalizeImmediatelyDeclaredConstraint( + *this, TC->getImmediatelyDeclaredConstraint(), NTTP->getType()); + Param->setPlaceholderTypeConstraint( + NestedNameSpecifierLoc(), + DeclarationNameInfo(TC->getNamedConcept()->getDeclName(), + SourceLocation()), + /*FoundDecl=*/nullptr, + // Actually canonicalizing a TemplateArgumentLoc is difficult so we + // simply omit the ArgsAsWritten + TC->getNamedConcept(), /*ArgsAsWritten=*/nullptr, NewIDC); } CanonParams.push_back(Param); @@ -6305,7 +6308,11 @@ if (auto *TX = dyn_cast(X)) { auto *TY = cast(Y); return TX->isParameterPack() == TY->isParameterPack() && - TX->getASTContext().hasSameType(TX->getType(), TY->getType()); + TX->getASTContext().hasSameType(TX->getType(), TY->getType()) && + TX->hasPlaceholderTypeConstraint() == + TY->hasPlaceholderTypeConstraint() && + isSameTypeConstraint(TX->getPlaceholderTypeConstraint(), + TY->getPlaceholderTypeConstraint()); } auto *TX = cast(X); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -5648,14 +5648,43 @@ return std::move(Err); NonTypeTemplateParmDecl *ToD = nullptr; - if (GetImportedOrCreateDecl(ToD, D, Importer.getToContext(), - Importer.getToContext().getTranslationUnitDecl(), - ToInnerLocStart, ToLocation, D->getDepth(), - D->getPosition(), - ToDeclName.getAsIdentifierInfo(), ToType, - D->isParameterPack(), ToTypeSourceInfo)) + if (GetImportedOrCreateDecl( + ToD, D, Importer.getToContext(), + Importer.getToContext().getTranslationUnitDecl(), ToInnerLocStart, + ToLocation, D->getDepth(), D->getPosition(), + ToDeclName.getAsIdentifierInfo(), ToType, D->isParameterPack(), + D->hasPlaceholderTypeConstraint(), ToTypeSourceInfo)) return ToD; + // Import the type-constraint + if (const TypeConstraint *TC = D->getPlaceholderTypeConstraint()) { + + Error Err = Error::success(); + auto ToNNS = importChecked(Err, TC->getNestedNameSpecifierLoc()); + auto ToName = importChecked(Err, TC->getConceptNameInfo().getName()); + auto ToNameLoc = importChecked(Err, TC->getConceptNameInfo().getLoc()); + auto ToFoundDecl = importChecked(Err, TC->getFoundDecl()); + auto ToNamedConcept = importChecked(Err, TC->getNamedConcept()); + auto ToIDC = importChecked(Err, TC->getImmediatelyDeclaredConstraint()); + if (Err) + return std::move(Err); + + TemplateArgumentListInfo ToTAInfo; + const auto *ASTTemplateArgs = TC->getTemplateArgsAsWritten(); + if (ASTTemplateArgs) + if (Error Err = + ImportTemplateArgumentListInfo(*ASTTemplateArgs, ToTAInfo)) + return std::move(Err); + + ToD->setPlaceholderTypeConstraint( + ToNNS, DeclarationNameInfo(ToName, ToNameLoc), ToFoundDecl, + ToNamedConcept, + ASTTemplateArgs ? ASTTemplateArgumentListInfo::Create( + Importer.getToContext(), ToTAInfo) + : nullptr, + ToIDC); + } + if (D->hasDefaultArgument()) { ExpectedExpr ToDefaultArgOrErr = import(D->getDefaultArgument()); if (!ToDefaultArgOrErr) diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1789,6 +1789,8 @@ void DeclPrinter::VisitNonTypeTemplateParmDecl( const NonTypeTemplateParmDecl *NTTP) { + if (const TypeConstraint *TC = NTTP->getPlaceholderTypeConstraint()) + TC->print(Out, Policy); StringRef Name; if (IdentifierInfo *II = NTTP->getIdentifier()) Name = diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -190,8 +190,8 @@ if (const auto *TC = TTP->getTypeConstraint()) AC.push_back(TC->getImmediatelyDeclaredConstraint()); } else if (const auto *NTTP = dyn_cast(Param)) { - if (const Expr *E = NTTP->getPlaceholderTypeConstraint()) - AC.push_back(E); + if (const auto *TC = NTTP->getPlaceholderTypeConstraint()) + AC.push_back(TC->getImmediatelyDeclaredConstraint()); } } if (HasRequiresClause) @@ -528,6 +528,10 @@ if (const auto *NTTP = dyn_cast(D)) { ID.AddInteger(0); ID.AddBoolean(NTTP->isParameterPack()); + ID.AddBoolean(NTTP->hasPlaceholderTypeConstraint()); + if (const TypeConstraint *TC = NTTP->getPlaceholderTypeConstraint()) + TC->getImmediatelyDeclaredConstraint()->Profile(ID, C, + /*Canonical=*/true); NTTP->getType().getCanonicalType().Profile(ID); continue; } @@ -717,10 +721,12 @@ NonTypeTemplateParmDecl::NonTypeTemplateParmDecl( DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, unsigned D, - unsigned P, IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, - ArrayRef ExpandedTypes, ArrayRef ExpandedTInfos) + unsigned P, IdentifierInfo *Id, QualType T, bool HasTypeConstraint, + TypeSourceInfo *TInfo, ArrayRef ExpandedTypes, + ArrayRef ExpandedTInfos) : DeclaratorDecl(NonTypeTemplateParm, DC, IdLoc, Id, T, TInfo, StartLoc), TemplateParmPosition(D, P), ParameterPack(true), + HasTypeConstraint(HasTypeConstraint), TypeConstraintInitialized(false), ExpandedParameterPack(true), NumExpandedTypes(ExpandedTypes.size()) { if (!ExpandedTypes.empty() && !ExpandedTInfos.empty()) { auto TypesAndInfos = @@ -737,53 +743,53 @@ SourceLocation StartLoc, SourceLocation IdLoc, unsigned D, unsigned P, IdentifierInfo *Id, QualType T, bool ParameterPack, - TypeSourceInfo *TInfo) { - AutoType *AT = - C.getLangOpts().CPlusPlus20 ? T->getContainedAutoType() : nullptr; - return new (C, DC, - additionalSizeToAlloc, - Expr *>(0, - AT && AT->isConstrained() ? 1 : 0)) + bool HasTypeConstraint, TypeSourceInfo *TInfo) { + return new ( + C, DC, + additionalSizeToAlloc, + TypeConstraint>(0, HasTypeConstraint ? 1 : 0)) NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, T, ParameterPack, - TInfo); + HasTypeConstraint, TInfo); } NonTypeTemplateParmDecl *NonTypeTemplateParmDecl::Create( const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, unsigned D, unsigned P, IdentifierInfo *Id, - QualType T, TypeSourceInfo *TInfo, ArrayRef ExpandedTypes, + QualType T, bool HasTypeConstraint, TypeSourceInfo *TInfo, + ArrayRef ExpandedTypes, ArrayRef ExpandedTInfos) { - AutoType *AT = TInfo->getType()->getContainedAutoType(); return new (C, DC, additionalSizeToAlloc, - Expr *>( - ExpandedTypes.size(), AT && AT->isConstrained() ? 1 : 0)) - NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, T, TInfo, - ExpandedTypes, ExpandedTInfos); + TypeConstraint>(ExpandedTypes.size(), + HasTypeConstraint ? 1 : 0)) + NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, T, + HasTypeConstraint, TInfo, ExpandedTypes, + ExpandedTInfos); } NonTypeTemplateParmDecl * NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID, bool HasTypeConstraint) { - return new (C, ID, additionalSizeToAlloc, - Expr *>(0, - HasTypeConstraint ? 1 : 0)) - NonTypeTemplateParmDecl(nullptr, SourceLocation(), SourceLocation(), - 0, 0, nullptr, QualType(), false, nullptr); + return new ( + C, ID, + additionalSizeToAlloc, + TypeConstraint>(0, HasTypeConstraint ? 1 : 0)) + NonTypeTemplateParmDecl(nullptr, SourceLocation(), SourceLocation(), 0, 0, + nullptr, QualType(), false, HasTypeConstraint, + nullptr); } NonTypeTemplateParmDecl * NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID, unsigned NumExpandedTypes, bool HasTypeConstraint) { - auto *NTTP = - new (C, ID, additionalSizeToAlloc, - Expr *>( - NumExpandedTypes, HasTypeConstraint ? 1 : 0)) - NonTypeTemplateParmDecl(nullptr, SourceLocation(), SourceLocation(), - 0, 0, nullptr, QualType(), nullptr, None, - None); + auto *NTTP = new (C, ID, + additionalSizeToAlloc, + TypeConstraint>( + NumExpandedTypes, HasTypeConstraint ? 1 : 0)) + NonTypeTemplateParmDecl(nullptr, SourceLocation(), SourceLocation(), 0, 0, + nullptr, QualType(), HasTypeConstraint, nullptr, + None, None); NTTP->NumExpandedTypes = NumExpandedTypes; return NTTP; } @@ -801,6 +807,22 @@ : SourceLocation(); } +void NonTypeTemplateParmDecl::setPlaceholderTypeConstraint( + NestedNameSpecifierLoc NNS, DeclarationNameInfo NameInfo, + NamedDecl *FoundDecl, ConceptDecl *CD, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + Expr *ImmediatelyDeclaredConstraint) { + assert(HasTypeConstraint && + "HasTypeConstraint=true must be passed at construction in order to " + "call setTypeConstraint"); + assert(!TypeConstraintInitialized && + "TypeConstraint was already initialized!"); + new (getTrailingObjects()) + TypeConstraint(NNS, NameInfo, FoundDecl, CD, ArgsAsWritten, + ImmediatelyDeclaredConstraint); + TypeConstraintInitialized = true; +} + //===----------------------------------------------------------------------===// // TemplateTemplateParmDecl Method Implementations //===----------------------------------------------------------------------===// @@ -1422,7 +1444,8 @@ C.getTrivialTypeSourceInfo(QualType(T->getTypeForDecl(), 0)); auto *N = NonTypeTemplateParmDecl::Create( C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/1, - /*Id=*/nullptr, TI->getType(), /*ParameterPack=*/true, TI); + /*Id=*/nullptr, TI->getType(), /*ParameterPack=*/true, + /*HasTypeConstraint=*/false, TI); N->setImplicit(true); // @@ -1448,7 +1471,8 @@ QualType(TemplateTypeParm->getTypeForDecl(), 0)); auto *NonTypeTemplateParm = NonTypeTemplateParmDecl::Create( C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/2, - /*Id=*/nullptr, TInfo->getType(), /*ParameterPack=*/false, TInfo); + /*Id=*/nullptr, TInfo->getType(), /*ParameterPack=*/false, + /*HasTypeConstraint=*/false, TInfo); NamedDecl *Params[] = {TemplateTemplateParm, TemplateTypeParm, NonTypeTemplateParm}; @@ -1463,7 +1487,8 @@ TypeSourceInfo *TInfo = C.getTrivialTypeSourceInfo(C.getSizeType()); auto *Index = NonTypeTemplateParmDecl::Create( C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/0, - /*Id=*/nullptr, TInfo->getType(), /*ParameterPack=*/false, TInfo); + /*Id=*/nullptr, TInfo->getType(), /*ParameterPack=*/false, + /*HasTypeConstraint=*/false, TInfo); // typename ...T auto *Ts = TemplateTypeParmDecl::Create( diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -399,6 +399,11 @@ } Hash.AddBoolean(D->isParameterPack()); + const TypeConstraint *TC = D->getPlaceholderTypeConstraint(); + Hash.AddBoolean(TC != nullptr); + if (TC) + AddStmt(TC->getImmediatelyDeclaredConstraint()); + Inherited::VisitNonTypeTemplateParmDecl(D); } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -2124,6 +2124,15 @@ void TextNodeDumper::VisitNonTypeTemplateParmDecl( const NonTypeTemplateParmDecl *D) { + if (const auto *TC = D->getPlaceholderTypeConstraint()) { + OS << " "; + dumpBareDeclRef(TC->getNamedConcept()); + if (TC->getNamedConcept() != TC->getFoundDecl()) { + OS << " ("; + dumpBareDeclRef(TC->getFoundDecl()); + OS << ")"; + } + } dumpType(D->getType()); OS << " depth " << D->getDepth() << " index " << D->getIndex(); if (D->isParameterPack()) diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp --- a/clang/lib/Index/IndexDecl.cpp +++ b/clang/lib/Index/IndexDecl.cpp @@ -707,6 +707,9 @@ } else if (const auto *NTTP = dyn_cast(TP)) { if (NTTP->hasDefaultArgument()) IndexCtx.indexBody(NTTP->getDefaultArgument(), Parent); + if (auto *C = NTTP->getPlaceholderTypeConstraint()) + IndexCtx.handleReference(C->getNamedConcept(), C->getConceptNameLoc(), + Parent, NTTP->getLexicalDeclContext()); } else if (const auto *TTPD = dyn_cast(TP)) { if (TTPD->hasDefaultArgument()) handleTemplateArgumentLoc(TTPD->getDefaultArgument(), Parent, diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" #include "clang/Parse/ParseDiagnostic.h" @@ -680,7 +681,7 @@ D.setInvalidType(true); NamedDecl *ErrorParam = Actions.ActOnNonTypeTemplateParameter( getCurScope(), D, Depth, Position, /*EqualLoc=*/SourceLocation(), - /*DefaultArg=*/nullptr); + /*DefaultArg=*/nullptr, /*HasTypeConstraint=*/false); ErrorParam->setInvalidDecl(true); SkipUntil(tok::comma, tok::greater, tok::greatergreater, StopAtSemi | StopBeforeMatch); @@ -862,8 +863,7 @@ TypeConstraint != nullptr); if (TypeConstraint) { - Actions.ActOnTypeConstraint(TypeConstraintSS, TypeConstraint, - cast(NewDecl), + Actions.ActOnTypeConstraint(TypeConstraintSS, TypeConstraint, NewDecl, EllipsisLoc); } @@ -989,8 +989,20 @@ /// template-parameter: /// ... /// parameter-declaration -NamedDecl * -Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) { +NamedDecl *Parser::ParseNonTypeTemplateParameter(unsigned Depth, + unsigned Position) { + // Consume the 'type-constraint'. + CXXScopeSpec TypeConstraintSS; + TemplateIdAnnotation *TypeConstraint = nullptr; + if (isTypeConstraintAnnotation()) { + ParseOptionalCXXScopeSpecifier(TypeConstraintSS, /*ObjectType=*/nullptr, + /*ObjectHasErrors=*/false, + /*EnteringContext*/ false); + TypeConstraint = + static_cast(Tok.getAnnotationValue()); + ConsumeAnnotationToken(); + } + // Parse the declaration-specifiers (i.e., the type). // FIXME: The type should probably be restricted in some way... Not all // declarators (parts of declarators?) are accepted for parameters. @@ -1038,9 +1050,16 @@ } // Create the parameter. - return Actions.ActOnNonTypeTemplateParameter(getCurScope(), ParamDecl, - Depth, Position, EqualLoc, - DefaultArg.get()); + NamedDecl *NewDecl = Actions.ActOnNonTypeTemplateParameter( + getCurScope(), ParamDecl, Depth, Position, EqualLoc, DefaultArg.get(), TypeConstraint); + + if (TypeConstraint) { + // FIXME: diagnose if the constrained type is not auto. + Actions.ActOnTypeConstraint(TypeConstraintSS, TypeConstraint, NewDecl, + ParamDecl.getEllipsisLoc()); + } + + return NewDecl; } void Parser::DiagnoseMisplacedEllipsis(SourceLocation EllipsisLoc, diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -420,7 +420,7 @@ auto *SizeParam = NonTypeTemplateParmDecl::Create( AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1, &AST.Idents.get("element_count", tok::TokenKind::identifier), AST.IntTy, - false, AST.getTrivialTypeSourceInfo(AST.IntTy)); + false, false, AST.getTrivialTypeSourceInfo(AST.IntTy)); Expr *LiteralExpr = IntegerLiteral::Create(AST, llvm::APInt(AST.getIntWidth(AST.IntTy), 4), AST.IntTy, SourceLocation()); diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -3095,9 +3095,15 @@ HasDefaultArg = TTP->hasDefaultArgument(); } else if (NonTypeTemplateParmDecl *NTTP = dyn_cast(*P)) { + if (const auto *TC = NTTP->getPlaceholderTypeConstraint()) { + llvm::raw_string_ostream OS(PlaceholderStr); + TC->print(OS, Policy); + OS.flush(); + } if (NTTP->getIdentifier()) - PlaceholderStr = std::string(NTTP->getIdentifier()->deuglifiedName()); + PlaceholderStr += std::string(NTTP->getIdentifier()->deuglifiedName()); NTTP->getType().getAsStringInternal(PlaceholderStr, Policy); + HasDefaultArg = NTTP->hasDefaultArgument(); } else { assert(isa(*P)); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -37,6 +37,7 @@ #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ErrorHandling.h" #include using namespace clang; @@ -1109,15 +1110,17 @@ bool Sema::ActOnTypeConstraint(const CXXScopeSpec &SS, TemplateIdAnnotation *TypeConstr, - TemplateTypeParmDecl *ConstrainedParameter, + NamedDecl *ConstrainedParameter, SourceLocation EllipsisLoc) { + assert(isa(ConstrainedParameter) || + isa(ConstrainedParameter)); return BuildTypeConstraint(SS, TypeConstr, ConstrainedParameter, EllipsisLoc, false); } bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS, TemplateIdAnnotation *TypeConstr, - TemplateTypeParmDecl *ConstrainedParameter, + NamedDecl *ConstrainedParameter, SourceLocation EllipsisLoc, bool AllowUnexpandedPack) { TemplateName TN = TypeConstr->Template.get(); @@ -1156,11 +1159,23 @@ } } } + + if (auto *TypeDecl = dyn_cast(ConstrainedParameter)) return AttachTypeConstraint( SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(), ConceptName, CD, - TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr, - ConstrainedParameter, EllipsisLoc); + TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr, TypeDecl, + EllipsisLoc); + + if (auto *NonTypeDecl = + dyn_cast(ConstrainedParameter)) + return AttachTypeConstraint( + SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(), + ConceptName, CD, + TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr, NonTypeDecl, + EllipsisLoc); + + llvm_unreachable("new constrained entity?"); } template @@ -1251,9 +1266,15 @@ return false; } -bool Sema::AttachTypeConstraint(AutoTypeLoc TL, NonTypeTemplateParmDecl *NTTP, +bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS, + DeclarationNameInfo NameInfo, + ConceptDecl *NamedConcept, + const TemplateArgumentListInfo *TemplateArgs, + NonTypeTemplateParmDecl *NTTP, SourceLocation EllipsisLoc) { - if (NTTP->getType() != TL.getType() || + if (AutoTypeLoc TL = + NTTP->getTypeSourceInfo()->getTypeLoc().getContainedAutoTypeLoc(); + NTTP->getType() != TL.getType() || TL.getAutoKeyword() != AutoTypeKeyword::Auto) { Diag(NTTP->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), diag::err_unsupported_placeholder_constraint) @@ -1266,20 +1287,32 @@ BuildDeclRefExpr(NTTP, NTTP->getType(), VK_PRValue, NTTP->getLocation()); if (!Ref) return true; + + // C++2a [temp.param]p4: + // [...] If Q is of the form C, then let E' be + // C. Otherwise, let E' be C. [...] + const ASTTemplateArgumentListInfo *ArgsAsWritten = + TemplateArgs ? ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs) + : nullptr; + ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint( - *this, TL.getNestedNameSpecifierLoc(), TL.getConceptNameInfo(), - TL.getNamedConcept(), TL.getLAngleLoc(), TL.getRAngleLoc(), + *this, NS, NameInfo, NamedConcept, + TemplateArgs ? TemplateArgs->getLAngleLoc() : SourceLocation(), + TemplateArgs ? TemplateArgs->getRAngleLoc() : SourceLocation(), BuildDecltypeType(Ref), NTTP->getLocation(), [&](TemplateArgumentListInfo &ConstraintArgs) { - for (unsigned I = 0, C = TL.getNumArgs(); I != C; ++I) - ConstraintArgs.addArgument(TL.getArgLoc(I)); + if (TemplateArgs) + for (const auto &ArgLoc : TemplateArgs->arguments()) + ConstraintArgs.addArgument(ArgLoc); }, EllipsisLoc); - if (ImmediatelyDeclaredConstraint.isInvalid() || - !ImmediatelyDeclaredConstraint.isUsable()) + if (ImmediatelyDeclaredConstraint.isInvalid()) return true; - NTTP->setPlaceholderTypeConstraint(ImmediatelyDeclaredConstraint.get()); + NTTP->setPlaceholderTypeConstraint(NS, NameInfo, + /*FoundDecl=*/NamedConcept, NamedConcept, + ArgsAsWritten, + ImmediatelyDeclaredConstraint.get()); return false; } @@ -1474,7 +1507,8 @@ unsigned Depth, unsigned Position, SourceLocation EqualLoc, - Expr *Default) { + Expr *Default, + bool HasTypeConstraint) { TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); // Check that we have valid decl-specifiers specified. @@ -1554,14 +1588,9 @@ NonTypeTemplateParmDecl *Param = NonTypeTemplateParmDecl::Create( Context, Context.getTranslationUnitDecl(), D.getBeginLoc(), D.getIdentifierLoc(), Depth, Position, ParamName, T, IsParameterPack, - TInfo); + HasTypeConstraint, TInfo); Param->setAccess(AS_public); - if (AutoTypeLoc TL = TInfo->getTypeLoc().getContainedAutoTypeLoc()) - if (TL.isConstrained()) - if (AttachTypeConstraint(TL, Param, D.getEllipsisLoc())) - Invalid = true; - if (Invalid) Param->setInvalidDecl(); @@ -7019,15 +7048,19 @@ TemplateDeductionInfo Info(DeductionArg->getExprLoc(), Param->getDepth() + 1); ParamType = QualType(); + // In template parameter list, immediately-declared constraint of the auto + // type is also an associated constraint, and will be checked along with + // the other associated constraints after checking the template argument + // list. + assert(!TSI->getTypeLoc() + .getType() + ->getContainedAutoType() + ->isConstrained() && + "the auto type should not be constrained when used in template " + "parameters"); TemplateDeductionResult Result = DeduceAutoType(TSI->getTypeLoc(), DeductionArg, ParamType, Info, - /*DependentDeduction=*/true, - // We do not check constraints right now because the - // immediately-declared constraint of the auto type is - // also an associated constraint, and will be checked - // along with the other associated constraints after - // checking the template argument list. - /*IgnoreConstraints=*/true); + /*DependentDeduction=*/true); if (Result == TDK_AlreadyDiagnosed) { if (ParamType.isNull()) return ExprError(); @@ -7933,12 +7966,26 @@ : Kind), TemplateArgLoc)) return false; - } else if (Kind != Sema::TPL_TemplateTemplateArgumentMatch) { + } + + if (Kind != Sema::TPL_TemplateTemplateArgumentMatch && + !isa(Old)) { const Expr *NewC = nullptr, *OldC = nullptr; + + if (isa(New)) { if (const auto *TC = cast(New)->getTypeConstraint()) NewC = TC->getImmediatelyDeclaredConstraint(); if (const auto *TC = cast(Old)->getTypeConstraint()) OldC = TC->getImmediatelyDeclaredConstraint(); + } else if (isa(New)) { + if (const auto *TC = cast(New) + ->getPlaceholderTypeConstraint()) + NewC = TC->getImmediatelyDeclaredConstraint(); + if (const auto *TC = cast(Old) + ->getPlaceholderTypeConstraint()) + OldC = TC->getImmediatelyDeclaredConstraint(); + } else + llvm_unreachable("unexpected template parameter type"); auto Diagnose = [&] { S.Diag(NewC ? NewC->getBeginLoc() : New->getBeginLoc(), diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2423,6 +2423,36 @@ : SourceLocation()); } +bool Sema::SubstTypeConstraint( + NonTypeTemplateParmDecl *Inst, const TypeConstraint *TC, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceLocation EllipsisLoc, bool EvaluateConstraints) { + const ASTTemplateArgumentListInfo *TemplArgInfo = + TC->getTemplateArgsAsWritten(); + + if (!EvaluateConstraints) { + Inst->setPlaceholderTypeConstraint( + TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(), + TC->getNamedConcept(), TC->getNamedConcept(), TemplArgInfo, + TC->getImmediatelyDeclaredConstraint()); + return false; + } + + TemplateArgumentListInfo InstArgs; + + if (TemplArgInfo) { + InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc); + InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc); + if (SubstTemplateArguments(TemplArgInfo->arguments(), TemplateArgs, + InstArgs)) + return true; + } + + return AttachTypeConstraint(TC->getNestedNameSpecifierLoc(), + TC->getConceptNameInfo(), TC->getNamedConcept(), + &InstArgs, Inst, EllipsisLoc); +} + ParmVarDecl * Sema::SubstParmVarDecl(ParmVarDecl *OldParm, const MultiLevelTemplateArgumentList &TemplateArgs, diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2907,23 +2907,25 @@ Param = NonTypeTemplateParmDecl::Create( SemaRef.Context, Owner, D->getInnerLocStart(), D->getLocation(), D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), - D->getPosition(), D->getIdentifier(), T, DI, ExpandedParameterPackTypes, + D->getPosition(), D->getIdentifier(), T, + D->hasPlaceholderTypeConstraint(), DI, ExpandedParameterPackTypes, ExpandedParameterPackTypesAsWritten); else Param = NonTypeTemplateParmDecl::Create( SemaRef.Context, Owner, D->getInnerLocStart(), D->getLocation(), D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), - D->getPosition(), D->getIdentifier(), T, D->isParameterPack(), DI); - - if (AutoTypeLoc AutoLoc = DI->getTypeLoc().getContainedAutoTypeLoc()) - if (AutoLoc.isConstrained()) - if (SemaRef.AttachTypeConstraint( - AutoLoc, Param, - IsExpandedParameterPack - ? DI->getTypeLoc().getAs() - .getEllipsisLoc() - : SourceLocation())) - Invalid = true; + D->getPosition(), D->getIdentifier(), T, D->isParameterPack(), + D->hasPlaceholderTypeConstraint(), DI); + + if (auto *TC = D->getPlaceholderTypeConstraint()) + if (SemaRef.SubstTypeConstraint(Param, TC, TemplateArgs, + IsExpandedParameterPack + ? DI->getTypeLoc() + .getAs() + .getEllipsisLoc() + : SourceLocation(), + EvaluateConstraints)) + return nullptr; Param->setAccess(AS_public); Param->setImplicit(D->isImplicit()); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2509,8 +2509,19 @@ // TemplateParmPosition. D->setDepth(Record.readInt()); D->setPosition(Record.readInt()); - if (D->hasPlaceholderTypeConstraint()) - D->setPlaceholderTypeConstraint(Record.readExpr()); + if (D->hasPlaceholderTypeConstraint()) { + NestedNameSpecifierLoc NNS = Record.readNestedNameSpecifierLoc(); + DeclarationNameInfo DN = Record.readDeclarationNameInfo(); + ConceptDecl *NamedConcept = Record.readDeclAs(); + const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; + if (Record.readBool()) + ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); + Expr *ImmediatelyDeclaredConstraint = Record.readExpr(); + D->setPlaceholderTypeConstraint(NNS, DN, /*FoundDecl=*/nullptr, + NamedConcept, ArgsAsWritten, + ImmediatelyDeclaredConstraint); + } + if (D->isExpandedParameterPack()) { auto TypesAndInfos = D->getTrailingObjects>(); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1708,8 +1708,8 @@ // For an expanded parameter pack, record the number of expansion types here // so that it's easier for deserialization to allocate the right amount of // memory. - Expr *TypeConstraint = D->getPlaceholderTypeConstraint(); - Record.push_back(!!TypeConstraint); + const TypeConstraint *TC = D->getPlaceholderTypeConstraint(); + Record.push_back(TC != nullptr); if (D->isExpandedParameterPack()) Record.push_back(D->getNumExpansionTypes()); @@ -1717,8 +1717,15 @@ // TemplateParmPosition. Record.push_back(D->getDepth()); Record.push_back(D->getPosition()); - if (TypeConstraint) - Record.AddStmt(TypeConstraint); + if (TC) { + Record.AddNestedNameSpecifierLoc(TC->getNestedNameSpecifierLoc()); + Record.AddDeclarationNameInfo(TC->getConceptNameInfo()); + Record.AddDeclRef(TC->getNamedConcept()); + Record.push_back(TC->getTemplateArgsAsWritten() != nullptr); + if (TC->getTemplateArgsAsWritten()) + Record.AddASTTemplateArgumentListInfo(TC->getTemplateArgsAsWritten()); + Record.AddStmt(TC->getImmediatelyDeclaredConstraint()); + } if (D->isExpandedParameterPack()) { for (unsigned I = 0, N = D->getNumExpansionTypes(); I != N; ++I) { diff --git a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp --- a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp +++ b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp @@ -26,6 +26,12 @@ template... Vs> static void foo(Vs... v); }; + + template + struct R { + template auto... Vs> + static void foo(); + }; }; int main() { @@ -33,4 +39,6 @@ T::foo(1, 2, 3); // expected-error{{no matching function for call to 'foo'}} T::S::foo(1, 'a'); T::S::foo('a', true); + T::R::foo<1, 'a'>(); + T::R::foo<'a', true>(); } diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -933,6 +933,11 @@ } bool CursorVisitor::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { + if (const auto *TC = D->getPlaceholderTypeConstraint()) { + if (VisitTypeConstraint(*TC)) + return true; + } + if (VisitDeclaratorDecl(D)) return true; @@ -5356,17 +5361,22 @@ // There is no parameter name, which makes this tricky. Try to come up // with something useful that isn't too long. - if (TemplateTypeParmDecl *TTP = dyn_cast(Param)) + if (TemplateTypeParmDecl *TTP = dyn_cast(Param)) { if (const auto *TC = TTP->getTypeConstraint()) { TC->getConceptNameInfo().printName(OS, Policy); if (TC->hasExplicitTemplateArgs()) OS << "<...>"; } else OS << (TTP->wasDeclaredWithTypename() ? "typename" : "class"); - else if (NonTypeTemplateParmDecl *NTTP = - dyn_cast(Param)) + } else if (NonTypeTemplateParmDecl *NTTP = + dyn_cast(Param)) { + if (const auto *TC = NTTP->getPlaceholderTypeConstraint()) { + TC->getConceptNameInfo().printName(OS, Policy); + if (TC->hasExplicitTemplateArgs()) + OS << "<...>"; + } OS << NTTP->getType().getAsString(Policy); - else + } else OS << "template<...> class"; }