diff options
| author | Saar Raz <saar@raz.email> | 2020-01-15 02:48:42 +0200 |
|---|---|---|
| committer | Saar Raz <saar@raz.email> | 2020-01-15 04:02:39 +0200 |
| commit | ff1e0fce817e01f0288fad6a2607dd173180aabd (patch) | |
| tree | 707c0171e1619c109d41b4db69af6353a97399f9 | |
| parent | 8e780252a7284be45cf1ba224cabd884847e8e92 (diff) | |
| download | bcm5719-llvm-ff1e0fce817e01f0288fad6a2607dd173180aabd.tar.gz bcm5719-llvm-ff1e0fce817e01f0288fad6a2607dd173180aabd.zip | |
[Concepts] Type Constraints
Add support for type-constraints in template type parameters.
Also add support for template type parameters as pack expansions (where the type constraint can now contain an unexpanded parameter pack).
Differential Revision: https://reviews.llvm.org/D44352
40 files changed, 1566 insertions, 315 deletions
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h index 937a8a9b345..896d857d8c9 100644 --- a/clang/include/clang/AST/ASTConcept.h +++ b/clang/include/clang/AST/ASTConcept.h @@ -21,6 +21,7 @@ #include <string> #include <utility> namespace clang { +class ConceptDecl; /// \brief The result of a constraint satisfaction check, containing the /// necessary information to diagnose an unsatisfied constraint. @@ -75,6 +76,102 @@ struct ASTConstraintSatisfaction final : Create(const ASTContext &C, const ConstraintSatisfaction &Satisfaction); }; +/// \brief Common data class for constructs that reference concepts with +/// template arguments. +class ConceptReference { +protected: + // \brief The optional nested name specifier used when naming the concept. + NestedNameSpecifierLoc NestedNameSpec; + + /// \brief The location of the template keyword, if specified when naming the + /// concept. + SourceLocation TemplateKWLoc; + + /// \brief The concept name used. + DeclarationNameInfo ConceptName; + + /// \brief The declaration found by name lookup when the expression was + /// created. + /// Can differ from NamedConcept when, for example, the concept was found + /// through a UsingShadowDecl. + NamedDecl *FoundDecl; + + /// \brief The concept named. + ConceptDecl *NamedConcept; + + /// \brief The template argument list source info used to specialize the + /// concept. + const ASTTemplateArgumentListInfo *ArgsAsWritten; + +public: + + ConceptReference(NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, + DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten) : + NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc), + ConceptName(ConceptNameInfo), FoundDecl(FoundDecl), + NamedConcept(NamedConcept), ArgsAsWritten(ArgsAsWritten) {} + + ConceptReference() : NestedNameSpec(), TemplateKWLoc(), ConceptName(), + FoundDecl(nullptr), NamedConcept(nullptr), ArgsAsWritten(nullptr) {} + + const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const { + return NestedNameSpec; + } + + const DeclarationNameInfo &getConceptNameInfo() const { return ConceptName; } + + SourceLocation getConceptNameLoc() const { + return getConceptNameInfo().getLoc(); + } + + SourceLocation getTemplateKWLoc() const { return TemplateKWLoc; } + + NamedDecl *getFoundDecl() const { + return FoundDecl; + } + + ConceptDecl *getNamedConcept() const { + return NamedConcept; + } + + const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const { + return ArgsAsWritten; + } + + /// \brief Whether or not template arguments were explicitly specified in the + /// concept reference (they might not be in type constraints, for example) + bool hasExplicitTemplateArgs() const { + return ArgsAsWritten != nullptr; + } +}; + +class TypeConstraint : public ConceptReference { + /// \brief The immediately-declared constraint expression introduced by this + /// type-constraint. + Expr *ImmediatelyDeclaredConstraint = nullptr; + +public: + TypeConstraint(NestedNameSpecifierLoc NNS, + DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + Expr *ImmediatelyDeclaredConstraint) : + ConceptReference(NNS, /*TemplateKWLoc=*/SourceLocation(), ConceptNameInfo, + FoundDecl, NamedConcept, ArgsAsWritten), + ImmediatelyDeclaredConstraint(ImmediatelyDeclaredConstraint) {} + + /// \brief Get the immediately-declared constraint expression introduced by + /// this type-constraint, that is - the constraint expression that is added to + /// the associated constraints of the enclosing declaration in practice. + Expr *getImmediatelyDeclaredConstraint() const { + return ImmediatelyDeclaredConstraint; + } + + void print(llvm::raw_ostream &OS, PrintingPolicy Policy) const; +}; + } // clang #endif // LLVM_CLANG_AST_ASTCONCEPT_H
\ No newline at end of file diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index d5ade9340c8..fb269cef1ce 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -283,12 +283,16 @@ class ASTContext : public RefCountedBase<ASTContext> { TemplateTemplateParmDecl *getParam() const { return Parm; } - void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, Parm); } + void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) { + Profile(ID, C, Parm); + } static void Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &C, TemplateTemplateParmDecl *Parm); }; - mutable llvm::FoldingSet<CanonicalTemplateTemplateParm> + mutable llvm::ContextualFoldingSet<CanonicalTemplateTemplateParm, + const ASTContext&> CanonTemplateTemplateParms; TemplateTemplateParmDecl * diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index a672d92695d..e0ebb020e69 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -537,6 +537,10 @@ public: } void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) { + if (const auto *TC = D->getTypeConstraint()) + if (TC->hasExplicitTemplateArgs()) + for (const auto &ArgLoc : TC->getTemplateArgsAsWritten()->arguments()) + dumpTemplateArgumentLoc(ArgLoc); if (D->hasDefaultArgument()) Visit(D->getDefaultArgument(), SourceRange(), D->getDefaultArgStorage().getInheritedFrom(), @@ -544,6 +548,8 @@ public: } void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) { + 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 index 23905d12c8b..7a55d04a0f3 100755 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_AST_DECLTEMPLATE_H #define LLVM_CLANG_AST_DECLTEMPLATE_H +#include "clang/AST/ASTConcept.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" @@ -51,6 +52,7 @@ class NonTypeTemplateParmDecl; class TemplateDecl; class TemplateTemplateParmDecl; class TemplateTypeParmDecl; +class ConceptDecl; class UnresolvedSetImpl; class VarTemplateDecl; class VarTemplatePartialSpecializationDecl; @@ -81,20 +83,24 @@ class TemplateParameterList final /// pack. unsigned ContainsUnexpandedParameterPack : 1; - /// Whether this template parameter list has an associated requires-clause + /// Whether this template parameter list has a requires clause. unsigned HasRequiresClause : 1; + /// Whether any of the template parameters has constrained-parameter + /// constraint-expression. + unsigned HasConstrainedParameters : 1; + protected: - TemplateParameterList(SourceLocation TemplateLoc, SourceLocation LAngleLoc, - ArrayRef<NamedDecl *> Params, SourceLocation RAngleLoc, - Expr *RequiresClause); + TemplateParameterList(const ASTContext& C, SourceLocation TemplateLoc, + SourceLocation LAngleLoc, ArrayRef<NamedDecl *> Params, + SourceLocation RAngleLoc, Expr *RequiresClause); size_t numTrailingObjects(OverloadToken<NamedDecl *>) const { return NumParams; } size_t numTrailingObjects(OverloadToken<Expr *>) const { - return HasRequiresClause; + return HasRequiresClause ? 1 : 0; } public: @@ -158,14 +164,22 @@ public: return ContainsUnexpandedParameterPack; } + /// Determine whether this template parameter list contains a parameter pack. + bool hasParameterPack() const { + for (const NamedDecl *P : asArray()) + if (P->isParameterPack()) + return true; + return false; + } + /// The constraint-expression of the associated requires-clause. Expr *getRequiresClause() { - return HasRequiresClause ? *getTrailingObjects<Expr *>() : nullptr; + return HasRequiresClause ? getTrailingObjects<Expr *>()[0] : nullptr; } /// The constraint-expression of the associated requires-clause. const Expr *getRequiresClause() const { - return HasRequiresClause ? *getTrailingObjects<Expr *>() : nullptr; + return HasRequiresClause ? getTrailingObjects<Expr *>()[0] : nullptr; } /// \brief All associated constraints derived from this template parameter @@ -208,15 +222,16 @@ class FixedSizeTemplateParameterListStorage >::type storage; public: - FixedSizeTemplateParameterListStorage(SourceLocation TemplateLoc, + FixedSizeTemplateParameterListStorage(const ASTContext &C, + SourceLocation TemplateLoc, SourceLocation LAngleLoc, ArrayRef<NamedDecl *> Params, SourceLocation RAngleLoc, Expr *RequiresClause) : FixedSizeStorageOwner( (assert(N == Params.size()), - assert(HasRequiresClause == static_cast<bool>(RequiresClause)), - new (static_cast<void *>(&storage)) TemplateParameterList( + assert(HasRequiresClause == (RequiresClause != nullptr)), + new (static_cast<void *>(&storage)) TemplateParameterList(C, TemplateLoc, LAngleLoc, Params, RAngleLoc, RequiresClause))) {} }; @@ -1148,9 +1163,12 @@ public: /// \code /// template<typename T> class vector; /// \endcode -class TemplateTypeParmDecl : public TypeDecl { +class TemplateTypeParmDecl final : public TypeDecl, + private llvm::TrailingObjects<TemplateTypeParmDecl, TypeConstraint> { /// Sema creates these on the stack during auto type deduction. friend class Sema; + friend TrailingObjects; + friend class ASTDeclReader; /// Whether this template type parameter was declaration with /// the 'typename' keyword. @@ -1158,6 +1176,22 @@ class TemplateTypeParmDecl : public TypeDecl { /// If false, it was declared with the 'class' keyword. bool Typename : 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 constriant. + 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 : 1; + + /// The number of type parameters in an expanded parameter pack. + unsigned NumExpanded = 0; + /// The default template argument, if any. using DefArgStorage = DefaultArgStorage<TemplateTypeParmDecl, TypeSourceInfo *>; @@ -1165,8 +1199,12 @@ class TemplateTypeParmDecl : public TypeDecl { TemplateTypeParmDecl(DeclContext *DC, SourceLocation KeyLoc, SourceLocation IdLoc, IdentifierInfo *Id, - bool Typename) - : TypeDecl(TemplateTypeParm, DC, IdLoc, Id, KeyLoc), Typename(Typename) {} + bool Typename, bool HasTypeConstraint, + Optional<unsigned> NumExpanded) + : TypeDecl(TemplateTypeParm, DC, IdLoc, Id, KeyLoc), Typename(Typename), + HasTypeConstraint(HasTypeConstraint), TypeConstraintInitialized(false), + ExpandedParameterPack(NumExpanded), + NumExpanded(NumExpanded ? *NumExpanded : 0) {} public: static TemplateTypeParmDecl *Create(const ASTContext &C, DeclContext *DC, @@ -1174,15 +1212,24 @@ public: SourceLocation NameLoc, unsigned D, unsigned P, IdentifierInfo *Id, bool Typename, - bool ParameterPack); + bool ParameterPack, + bool HasTypeConstraint = false, + Optional<unsigned> NumExpanded = None); + static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C, unsigned ID); + static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C, + unsigned ID, + bool HasTypeConstraint); /// Whether this template type parameter was declared with /// the 'typename' keyword. /// - /// If not, it was declared with the 'class' keyword. - bool wasDeclaredWithTypename() const { return Typename; } + /// If not, it was either declared with the 'class' keyword or with a + /// type-constraint (see hasTypeConstraint()). + bool wasDeclaredWithTypename() const { + return Typename && !HasTypeConstraint; + } const DefArgStorage &getDefaultArgStorage() const { return DefaultArgument; } @@ -1239,6 +1286,78 @@ public: /// Returns whether this is a parameter pack. bool isParameterPack() const; + /// Whether this parameter pack is a pack expansion. + /// + /// A template type template parameter pack can be a pack expansion if its + /// type-constraint contains an unexpanded parameter pack. + bool isPackExpansion() const { + if (!isParameterPack()) + return false; + if (const TypeConstraint *TC = getTypeConstraint()) + if (TC->hasExplicitTemplateArgs()) + for (const auto &ArgLoc : TC->getTemplateArgsAsWritten()->arguments()) + if (ArgLoc.getArgument().containsUnexpandedParameterPack()) + return true; + return false; + } + + /// Whether this parameter is a template type parameter pack that has a known + /// list of different type-constraints at different positions. + /// + /// A parameter pack is an expanded parameter pack when the original + /// parameter pack's type-constraint was itself a pack expansion, and that + /// expansion has already been expanded. For example, given: + /// + /// \code + /// template<typename ...Types> + /// struct X { + /// template<convertible_to<Types> ...Convertibles> + /// struct Y { /* ... */ }; + /// }; + /// \endcode + /// + /// The parameter pack \c Convertibles has (convertible_to<Types> && ...) as + /// its type-constraint. When \c Types is supplied with template arguments by + /// instantiating \c X, the instantiation of \c Convertibles becomes an + /// expanded parameter pack. For example, instantiating + /// \c X<int, unsigned int> results in \c Convertibles being an expanded + /// parameter pack of size 2 (use getNumExpansionTypes() to get this number). + bool isExpandedParameterPack() const { return ExpandedParameterPack; } + + /// Retrieves the number of parameters in an expanded parameter pack. + unsigned getNumExpansionParameters() const { + assert(ExpandedParameterPack && "Not an expansion parameter pack"); + return NumExpanded; + } + + /// Returns the type constraint associated with this template parameter (if + /// any). + const TypeConstraint *getTypeConstraint() const { + return TypeConstraintInitialized ? getTrailingObjects<TypeConstraint>() : + nullptr; + } + + void setTypeConstraint(NestedNameSpecifierLoc NNS, + DeclarationNameInfo NameInfo, NamedDecl *FoundDecl, + ConceptDecl *CD, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + Expr *ImmediatelyDeclaredConstraint); + + /// Determine whether this template parameter has a type-constraint. + bool hasTypeConstraint() const { + return HasTypeConstraint; + } + + /// \brief Get the associated-constraints of this template parameter. + /// This will either be the immediately-introduced constraint or empty. + /// + /// Use this instead of getConstraintExpression for concepts APIs that + /// accept an ArrayRef of constraint expressions. + void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const { + if (HasTypeConstraint) + AC.push_back(getTypeConstraint()->getImmediatelyDeclaredConstraint()); + } + SourceRange getSourceRange() const override LLVM_READONLY; // Implement isa/cast/dyncast/etc. @@ -1424,6 +1543,33 @@ public: return TypesAndInfos[I].second; } + /// Return the type-constraint in the placeholder type of this non-type + /// template parameter (if any). + TypeConstraint *getPlaceholderTypeConstraint() const { + // TODO: Concepts: Implement once we have actual placeholders with type + // constraints. + return nullptr; + } + + /// Determine whether this non-type template parameter's type has a + /// placeholder with a type-constraint. + bool hasPlaceholderTypeConstraint() const { + // TODO: Concepts: Implement once we have actual placeholders with type + // constraints. + return false; + } + + /// \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. + /// + /// Use this instead of getPlaceholderImmediatelyDeclaredConstraint for + /// concepts APIs that accept an ArrayRef of constraint expressions. + void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const { + if (TypeConstraint *TC = getPlaceholderTypeConstraint()) + AC.push_back(TC->getImmediatelyDeclaredConstraint()); + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == NonTypeTemplateParm; } @@ -3086,6 +3232,10 @@ public: ConstraintExpr->getEndLoc()); } + bool isTypeConcept() const { + return isa<TemplateTypeParmDecl>(getTemplateParameters()->getParam(0)); + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == Concept; } diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 1a16aa7aace..2c29409e0ca 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4841,7 +4841,7 @@ public: /// /// According to C++2a [expr.prim.id]p3 an id-expression that denotes the /// specialization of a concept results in a prvalue of type bool. -class ConceptSpecializationExpr final : public Expr, +class ConceptSpecializationExpr final : public Expr, public ConceptReference, private llvm::TrailingObjects<ConceptSpecializationExpr, TemplateArgument> { friend class ASTStmtReader; @@ -4850,30 +4850,6 @@ public: using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>; protected: - - // \brief The optional nested name specifier used when naming the concept. - NestedNameSpecifierLoc NestedNameSpec; - - /// \brief The location of the template keyword, if specified when naming the - /// concept. - SourceLocation TemplateKWLoc; - - /// \brief The location of the concept name in the expression. - SourceLocation ConceptNameLoc; - - /// \brief The declaration found by name lookup when the expression was - /// created. - /// Can differ from NamedConcept when, for example, the concept was found - /// through a UsingShadowDecl. - NamedDecl *FoundDecl; - - /// \brief The concept named. - ConceptDecl *NamedConcept; - - /// \brief The template argument list source info used to specialize the - /// concept. - const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; - /// \brief The number of template arguments in the tail-allocated list of /// converted template arguments. unsigned NumTemplateArgs; @@ -4883,10 +4859,10 @@ protected: /// ignored. ASTConstraintSatisfaction *Satisfaction; - ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS, + ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, - SourceLocation ConceptNameLoc, NamedDecl *FoundDecl, - ConceptDecl *NamedConcept, + DeclarationNameInfo ConceptNameInfo, + NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, ArrayRef<TemplateArgument> ConvertedArgs, const ConstraintSatisfaction *Satisfaction); @@ -4896,8 +4872,8 @@ protected: public: static ConceptSpecializationExpr * - Create(ASTContext &C, NestedNameSpecifierLoc NNS, - SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc, + Create(const ASTContext &C, NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, ArrayRef<TemplateArgument> ConvertedArgs, @@ -4906,30 +4882,13 @@ public: static ConceptSpecializationExpr * Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs); - const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const { - return NestedNameSpec; - } - - NamedDecl *getFoundDecl() const { - return FoundDecl; - } - - ConceptDecl *getNamedConcept() const { - return NamedConcept; - } - ArrayRef<TemplateArgument> getTemplateArguments() const { return ArrayRef<TemplateArgument>(getTrailingObjects<TemplateArgument>(), NumTemplateArgs); } - const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const { - return ArgsAsWritten; - } - /// \brief Set new template arguments for this concept specialization. - void setTemplateArguments(const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef<TemplateArgument> Converted); + void setTemplateArguments(ArrayRef<TemplateArgument> Converted); /// \brief Whether or not the concept with the given arguments was satisfied /// when the expression was created. @@ -4949,15 +4908,14 @@ public: return *Satisfaction; } - SourceLocation getConceptNameLoc() const { return ConceptNameLoc; } - - SourceLocation getTemplateKWLoc() const { return TemplateKWLoc; } - static bool classof(const Stmt *T) { return T->getStmtClass() == ConceptSpecializationExprClass; } - SourceLocation getBeginLoc() const LLVM_READONLY { return ConceptNameLoc; } + SourceLocation getBeginLoc() const LLVM_READONLY { + return ConceptName.getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { return ArgsAsWritten->RAngleLoc; } diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 144ef221d69..f8ab8e451d8 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -304,6 +304,11 @@ public: bool TraverseSynOrSemInitListExpr(InitListExpr *S, DataRecursionQueue *Queue = nullptr); + /// Recursively visit a reference to a concept with potential arguments. + /// + /// \returns false if the visitation was terminated early, true otherwise. + bool TraverseConceptReference(const ConceptReference &C); + // ---- Methods on Attrs ---- // Visit an attribute. @@ -1773,9 +1778,8 @@ DEF_TRAVERSE_DECL(TemplateTemplateParmDecl, { // D is the "T" in something like // template <template <typename> class T> class container { }; TRY_TO(TraverseDecl(D->getTemplatedDecl())); - if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) TRY_TO(TraverseTemplateArgumentLoc(D->getDefaultArgument())); - } TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); }) @@ -1787,6 +1791,8 @@ 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))); + if (const auto *TC = D->getTypeConstraint()) + TRY_TO(TraverseConceptReference(*TC)); if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) TRY_TO(TraverseTypeLoc(D->getDefaultArgumentInfo()->getTypeLoc())); }) @@ -2330,6 +2336,18 @@ bool RecursiveASTVisitor<Derived>::TraverseSynOrSemInitListExpr( return true; } +template<typename Derived> +bool RecursiveASTVisitor<Derived>::TraverseConceptReference( + const ConceptReference &C) { + TRY_TO(TraverseNestedNameSpecifierLoc(C.getNestedNameSpecifierLoc())); + TRY_TO(TraverseDeclarationNameInfo(C.getConceptNameInfo())); + if (C.hasExplicitTemplateArgs()) + TRY_TO(TraverseTemplateArgumentLocsHelper( + C.getTemplateArgsAsWritten()->getTemplateArgs(), + C.getTemplateArgsAsWritten()->NumTemplateArgs)); + return true; +} + // If shouldVisitImplicitCode() returns false, this method traverses only the // syntactic form of InitListExpr. // If shouldVisitImplicitCode() return true, this method is called once for @@ -2688,9 +2706,7 @@ DEF_TRAVERSE_STMT(CoyieldExpr, { }) DEF_TRAVERSE_STMT(ConceptSpecializationExpr, { - TRY_TO(TraverseTemplateArgumentLocsHelper( - S->getTemplateArgsAsWritten()->getTemplateArgs(), - S->getTemplateArgsAsWritten()->NumTemplateArgs)); + TRY_TO(TraverseConceptReference(*S)); }) // These literals (all of them) do not need any action. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d27691d2112..7d8231d140e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2612,6 +2612,17 @@ def note_ambiguous_atomic_constraints_similar_expression : Note< def err_template_different_requires_clause : Error< "requires clause differs in template redeclaration">; +def err_template_different_type_constraint : Error< + "type constraint differs in template redeclaration">; +def err_template_template_parameter_not_at_least_as_constrained : Error< + "template template argument %0 is more constrained than template template " + "parameter %1">; + +def err_type_constraint_non_type_concept : Error< + "concept named in type constraint is not a type concept">; +def err_type_constraint_missing_arguments : Error< + "%0 requires more than 1 template argument; provide the remaining arguments " + "explicitly to use it here">; // C++11 char16_t/char32_t def warn_cxx98_compat_unicode_type : Warning< @@ -4819,8 +4830,8 @@ def err_unexpanded_parameter_pack : Error< "size|static assertion|fixed underlying type|enumerator value|" "using declaration|friend declaration|qualifier|initializer|default argument|" "non-type template parameter type|exception type|partial specialization|" - "__if_exists name|__if_not_exists name|lambda|block}0 contains" - "%plural{0: an|:}1 unexpanded parameter pack" + "__if_exists name|__if_not_exists name|lambda|block|type constraint}0 " + "contains%plural{0: an|:}1 unexpanded parameter pack" "%plural{0:|1: %2|2:s %2 and %3|:s %2, %3, ...}1">; def err_pack_expansion_without_parameter_packs : Error< diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index ae908bbdf3a..d58b0d987d7 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -729,7 +729,9 @@ ANNOTATION(typename) // annotation for a C typedef name, a C++ (possibly // template-id that names a type ("std::vector<int>") ANNOTATION(template_id) // annotation for a C++ template-id that names a // function template specialization (not a type), - // e.g., "std::swap<int>" + // e.g., "std::swap<int>", or a type-constraint (which + // might not have explicit template arguments), + // e.g. "C", "C<int>". ANNOTATION(non_type) // annotation for a single non-type declaration ANNOTATION(non_type_undeclared) // annotation for an undeclared identifier that // was assumed to be an ADL-only function name diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index eb2f4152e72..e320c964781 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3064,11 +3064,15 @@ private: SourceLocation &RAngleLoc); bool ParseTemplateParameterList(unsigned Depth, SmallVectorImpl<NamedDecl*> &TemplateParams); - bool isStartOfTemplateTypeParameter(); + bool isStartOfTemplateTypeParameter(bool &ScopeError); NamedDecl *ParseTemplateParameter(unsigned Depth, unsigned Position); NamedDecl *ParseTypeParameter(unsigned Depth, unsigned Position); NamedDecl *ParseTemplateTemplateParameter(unsigned Depth, unsigned Position); NamedDecl *ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position); + bool isTypeConstraintAnnotation(); + bool TryAnnotateTypeConstraint(CXXScopeSpec &SS); + NamedDecl * + ParseConstrainedTemplateTypeParameter(unsigned Depth, unsigned Position); void DiagnoseMisplacedEllipsis(SourceLocation EllipsisLoc, SourceLocation CorrectLoc, bool AlreadyHasEllipsis, @@ -3090,7 +3094,8 @@ private: CXXScopeSpec &SS, SourceLocation TemplateKWLoc, UnqualifiedId &TemplateName, - bool AllowTypeAnnotation = true); + bool AllowTypeAnnotation = true, + bool TypeConstraint = false); void AnnotateTemplateIdTokenAsType(bool IsClassName = false); bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs); ParsedTemplateArgument ParseTemplateTemplateArgument(); diff --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h index 2eed301e8ae..0874905b38a 100644 --- a/clang/include/clang/Sema/ParsedTemplate.h +++ b/clang/include/clang/Sema/ParsedTemplate.h @@ -144,6 +144,10 @@ namespace clang { /// expressions, or template names, and the source locations for important /// tokens. All of the information about template arguments is allocated /// directly after this structure. + /// A template-id annotation token can also be generated by a type-constraint + /// construct with no explicit template arguments, e.g. "template<C T>" would + /// annotate C as a TemplateIdAnnotation with no template arguments (the angle + /// locations would be invalid in this case). struct TemplateIdAnnotation final : private llvm::TrailingObjects<TemplateIdAnnotation, ParsedTemplateArgument> { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5aea6102c0e..22063cbe965 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5923,6 +5923,8 @@ public: /// /// \param OnlyNamespace If true, only considers namespaces in lookup. /// + /// \param SuppressDiagnostic If true, suppress diagnostic on error. + /// /// \returns true if an error occurred, false otherwise. bool ActOnCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, @@ -6263,12 +6265,6 @@ public: bool CheckConstraintSatisfaction(const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction); - /// Check that the associated constraints of a template declaration match the - /// associated constraints of an older declaration of which it is a - /// redeclaration. - bool CheckRedeclarationConstraintMatch(TemplateParameterList *Old, - TemplateParameterList *New); - /// \brief Ensure that the given template arguments satisfy the constraints /// associated with the given template, emitting a diagnostic if they do not. /// @@ -6301,9 +6297,8 @@ public: void DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation DiagnosticLocation, StringRef Diagnostic); - void - DiagnoseRedeclarationConstraintMismatch(const TemplateParameterList *Old, - const TemplateParameterList *New); + void DiagnoseRedeclarationConstraintMismatch(SourceLocation Old, + SourceLocation New); // ParseObjCStringLiteral - Parse Objective-C string literals. ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, @@ -6872,13 +6867,24 @@ public: TemplateDecl *AdjustDeclIfTemplate(Decl *&Decl); NamedDecl *ActOnTypeParameter(Scope *S, bool Typename, - SourceLocation EllipsisLoc, - SourceLocation KeyLoc, - IdentifierInfo *ParamName, - SourceLocation ParamNameLoc, - unsigned Depth, unsigned Position, - SourceLocation EqualLoc, - ParsedType DefaultArg); + SourceLocation EllipsisLoc, + SourceLocation KeyLoc, + IdentifierInfo *ParamName, + SourceLocation ParamNameLoc, + unsigned Depth, unsigned Position, + SourceLocation EqualLoc, + ParsedType DefaultArg, bool HasTypeConstraint); + + bool ActOnTypeConstraint(TemplateIdAnnotation *TypeConstraint, + TemplateTypeParmDecl *ConstrainedParameter, + SourceLocation EllipsisLoc); + + bool AttachTypeConstraint(NestedNameSpecifierLoc NS, + DeclarationNameInfo NameInfo, + ConceptDecl *NamedConcept, + const TemplateArgumentListInfo *TemplateArgs, + TemplateTypeParmDecl *ConstrainedParameter, + SourceLocation EllipsisLoc); QualType CheckNonTypeTemplateParameterType(TypeSourceInfo *&TSI, SourceLocation Loc); @@ -7000,8 +7006,8 @@ public: ExprResult CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - SourceLocation ConceptNameLoc, NamedDecl *FoundDecl, - ConceptDecl *NamedConcept, + const DeclarationNameInfo &ConceptNameInfo, + NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs); void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc); @@ -7160,7 +7166,8 @@ public: QualType InstantiatedParamType, Expr *Arg, TemplateArgument &Converted, CheckTemplateArgumentKind CTAK = CTAK_Specified); - bool CheckTemplateTemplateArgument(TemplateParameterList *Params, + bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, + TemplateParameterList *Params, TemplateArgumentLoc &Arg); ExprResult @@ -7353,7 +7360,10 @@ public: UPPC_Lambda, /// Block expression, - UPPC_Block + UPPC_Block, + + /// A type constraint, + UPPC_TypeConstraint }; /// Diagnose unexpanded parameter packs. @@ -7846,7 +7856,7 @@ public: sema::TemplateDeductionInfo &Info); bool isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc); + TemplateParameterList *PParam, TemplateDecl *AArg, SourceLocation Loc); void MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced, unsigned Depth, llvm::SmallBitVector &Used); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 26e76d45042..a51429264db 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -661,8 +661,9 @@ comments::FullComment *ASTContext::getCommentForDecl( return FC; } -void +void ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &C, TemplateTemplateParmDecl *Parm) { ID.AddInteger(Parm->getDepth()); ID.AddInteger(Parm->getPosition()); @@ -676,6 +677,16 @@ ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID, if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(*P)) { ID.AddInteger(0); ID.AddBoolean(TTP->isParameterPack()); + const TypeConstraint *TC = TTP->getTypeConstraint(); + ID.AddBoolean(TC != nullptr); + if (TC) + TC->getImmediatelyDeclaredConstraint()->Profile(ID, C, + /*Canonical=*/true); + if (TTP->isExpandedParameterPack()) { + ID.AddBoolean(true); + ID.AddInteger(TTP->getNumExpansionParameters()); + } else + ID.AddBoolean(false); continue; } @@ -697,8 +708,12 @@ ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID, auto *TTP = cast<TemplateTemplateParmDecl>(*P); ID.AddInteger(2); - Profile(ID, TTP); + Profile(ID, C, TTP); } + Expr *RequiresClause = Parm->getTemplateParameters()->getRequiresClause(); + ID.AddBoolean(RequiresClause != nullptr); + if (RequiresClause) + RequiresClause->Profile(ID, C, /*Canonical=*/true); } TemplateTemplateParmDecl * @@ -706,7 +721,7 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( TemplateTemplateParmDecl *TTP) const { // Check if we already have a canonical template template parameter. llvm::FoldingSetNodeID ID; - CanonicalTemplateTemplateParm::Profile(ID, TTP); + CanonicalTemplateTemplateParm::Profile(ID, *this, TTP); void *InsertPos = nullptr; CanonicalTemplateTemplateParm *Canonical = CanonTemplateTemplateParms.FindNodeOrInsertPos(ID, InsertPos); @@ -720,15 +735,79 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( for (TemplateParameterList::const_iterator P = Params->begin(), PEnd = Params->end(); P != PEnd; ++P) { - if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(*P)) - CanonParams.push_back( - TemplateTypeParmDecl::Create(*this, getTranslationUnitDecl(), - SourceLocation(), - SourceLocation(), - TTP->getDepth(), - TTP->getIndex(), nullptr, false, - TTP->isParameterPack())); - else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*P)) { + if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(*P)) { + TemplateTypeParmDecl *NewTTP = TemplateTypeParmDecl::Create(*this, + getTranslationUnitDecl(), SourceLocation(), SourceLocation(), + TTP->getDepth(), TTP->getIndex(), nullptr, false, + TTP->isParameterPack(), TTP->hasTypeConstraint(), + TTP->isExpandedParameterPack() ? + llvm::Optional<unsigned>(TTP->getNumExpansionParameters()) : None); + if (const auto *TC = TTP->getTypeConstraint()) { + // This is a bit ugly - we need to form a new immediately-declared + // constraint that references the new parameter; this would ideally + // require semantic analysis (e.g. template<C T> struct S {}; - the + // converted arguments of C<T> could be an argument pack if C is + // declared as template<typename... T> concept C = ...). + // We don't have semantic analysis here so we dig deep into the + // ready-made constraint expr and change the thing manually. + Expr *IDC = TC->getImmediatelyDeclaredConstraint(); + ConceptSpecializationExpr *CSE; + if (const auto *Fold = dyn_cast<CXXFoldExpr>(IDC)) + CSE = cast<ConceptSpecializationExpr>(Fold->getLHS()); + else + CSE = cast<ConceptSpecializationExpr>(IDC); + ArrayRef<TemplateArgument> OldConverted = CSE->getTemplateArguments(); + SmallVector<TemplateArgument, 3> NewConverted; + NewConverted.reserve(OldConverted.size()); + + QualType ParamAsArgument(NewTTP->getTypeForDecl(), 0); + if (OldConverted.front().getKind() == TemplateArgument::Pack) { + // The case: + // template<typename... T> concept C = true; + // template<C<int> T> struct S; -> constraint is C<{T, int}> + NewConverted.push_back(ParamAsArgument); + for (auto &Arg : OldConverted.front().pack_elements().drop_front(1)) + NewConverted.push_back(Arg); + TemplateArgument NewPack(NewConverted); + + NewConverted.clear(); + NewConverted.push_back(NewPack); + assert(OldConverted.size() == 1 && + "Template parameter pack should be the last parameter"); + } else { + assert(OldConverted.front().getKind() == TemplateArgument::Type && + "Unexpected first argument kind for immediately-declared " + "constraint"); + NewConverted.push_back(ParamAsArgument); + for (auto &Arg : OldConverted.drop_front(1)) + NewConverted.push_back(Arg); + } + Expr *NewIDC = ConceptSpecializationExpr::Create(*this, + NestedNameSpecifierLoc(), /*TemplateKWLoc=*/SourceLocation(), + CSE->getConceptNameInfo(), /*FoundDecl=*/CSE->getNamedConcept(), + CSE->getNamedConcept(), + // Actually canonicalizing a TemplateArgumentLoc is difficult so we + // simply omit the ArgsAsWritten + /*ArgsAsWritten=*/nullptr, NewConverted, nullptr); + + if (auto *OrigFold = dyn_cast<CXXFoldExpr>(IDC)) + NewIDC = new (*this) CXXFoldExpr(OrigFold->getType(), + SourceLocation(), NewIDC, + BinaryOperatorKind::BO_LAnd, + SourceLocation(), /*RHS=*/nullptr, + SourceLocation(), + /*NumExpansions=*/None); + + NewTTP->setTypeConstraint( + NestedNameSpecifierLoc(), + DeclarationNameInfo(TC->getNamedConcept()->getDeclName(), + SourceLocation()), /*FoundDecl=*/nullptr, + // Actually canonicalizing a TemplateArgumentLoc is difficult so we + // simply omit the ArgsAsWritten + CSE->getNamedConcept(), /*ArgsAsWritten=*/nullptr, NewIDC); + } + CanonParams.push_back(NewTTP); + } else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*P)) { QualType T = getCanonicalType(NTTP->getType()); TypeSourceInfo *TInfo = getTrivialTypeSourceInfo(T); NonTypeTemplateParmDecl *Param; @@ -767,9 +846,9 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( cast<TemplateTemplateParmDecl>(*P))); } - assert(!TTP->getTemplateParameters()->getRequiresClause() && - "Unexpected requires-clause on template template-parameter"); - Expr *const CanonRequiresClause = nullptr; + Expr *CanonRequiresClause = nullptr; + if (Expr *RequiresClause = TTP->getTemplateParameters()->getRequiresClause()) + CanonRequiresClause = RequiresClause; TemplateTemplateParmDecl *CanonTTP = TemplateTemplateParmDecl::Create(*this, getTranslationUnitDecl(), @@ -865,7 +944,8 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, : ConstantArrayTypes(this_()), FunctionProtoTypes(this_()), TemplateSpecializationTypes(this_()), DependentTemplateSpecializationTypes(this_()), - SubstTemplateTemplateParmPacks(this_()), SourceMgr(SM), LangOpts(LOpts), + SubstTemplateTemplateParmPacks(this_()), + CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts), SanitizerBL(new SanitizerBlacklist(LangOpts.SanitizerBlacklistFiles, SM)), XRayFilter(new XRayFunctionFilter(LangOpts.XRayAlwaysInstrumentFiles, LangOpts.XRayNeverInstrumentFiles, diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 8eb58b32ae1..22fb67478c9 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -5097,7 +5097,7 @@ ASTNodeImporter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { // context. This context will be fixed when the actual template declaration // is created. - // FIXME: Import default argument. + // FIXME: Import default argument and constraint expression. ExpectedSLoc BeginLocOrErr = import(D->getBeginLoc()); if (!BeginLocOrErr) @@ -5108,12 +5108,47 @@ ASTNodeImporter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { return LocationOrErr.takeError(); TemplateTypeParmDecl *ToD = nullptr; - (void)GetImportedOrCreateDecl( + if (GetImportedOrCreateDecl( ToD, D, Importer.getToContext(), Importer.getToContext().getTranslationUnitDecl(), *BeginLocOrErr, *LocationOrErr, D->getDepth(), D->getIndex(), Importer.Import(D->getIdentifier()), - D->wasDeclaredWithTypename(), D->isParameterPack()); + D->wasDeclaredWithTypename(), D->isParameterPack(), + D->hasTypeConstraint())) + return ToD; + + // Import the type-constraint + if (const TypeConstraint *TC = D->getTypeConstraint()) { + NestedNameSpecifierLoc ToNNS; + DeclarationName ToName; + SourceLocation ToNameLoc; + NamedDecl *ToFoundDecl; + ConceptDecl *ToNamedConcept; + Expr *ToIDC; + if (auto Imp = importSeq(TC->getNestedNameSpecifierLoc(), + TC->getConceptNameInfo().getName(), TC->getConceptNameInfo().getLoc(), + TC->getFoundDecl(), TC->getNamedConcept(), + TC->getImmediatelyDeclaredConstraint())) + std::tie(ToNNS, ToName, ToNameLoc, ToFoundDecl, ToNamedConcept, + ToIDC) = *Imp; + else + return Imp.takeError(); + + TemplateArgumentListInfo ToTAInfo; + const auto *ASTTemplateArgs = TC->getTemplateArgsAsWritten(); + if (ASTTemplateArgs) + if (Error Err = ImportTemplateArgumentListInfo(*ASTTemplateArgs, + ToTAInfo)) + return std::move(Err); + + ToD->setTypeConstraint(ToNNS, DeclarationNameInfo(ToName, ToNameLoc), + ToFoundDecl, ToNamedConcept, + ASTTemplateArgs ? + ASTTemplateArgumentListInfo::Create(Importer.getToContext(), + ToTAInfo) : nullptr, + ToIDC); + } + return ToD; } diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 68cc8b7a0eb..4cedcbed064 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1053,7 +1053,9 @@ void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params, if (auto TTP = dyn_cast<TemplateTypeParmDecl>(Param)) { - if (TTP->wasDeclaredWithTypename()) + if (const TypeConstraint *TC = TTP->getTypeConstraint()) + TC->print(Out, Policy); + else if (TTP->wasDeclaredWithTypename()) Out << "typename"; else Out << "class"; diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 59fa7faad92..95a2e26e0df 100755 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -43,35 +43,46 @@ using namespace clang; // TemplateParameterList Implementation //===----------------------------------------------------------------------===// -TemplateParameterList::TemplateParameterList(SourceLocation TemplateLoc, + +TemplateParameterList::TemplateParameterList(const ASTContext& C, + SourceLocation TemplateLoc, SourceLocation LAngleLoc, ArrayRef<NamedDecl *> Params, SourceLocation RAngleLoc, Expr *RequiresClause) : TemplateLoc(TemplateLoc), LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc), NumParams(Params.size()), ContainsUnexpandedParameterPack(false), - HasRequiresClause(static_cast<bool>(RequiresClause)) { + HasRequiresClause(RequiresClause != nullptr), + HasConstrainedParameters(false) { for (unsigned Idx = 0; Idx < NumParams; ++Idx) { NamedDecl *P = Params[Idx]; begin()[Idx] = P; - if (!P->isTemplateParameterPack()) { - if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) - if (NTTP->getType()->containsUnexpandedParameterPack()) - ContainsUnexpandedParameterPack = true; - - if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(P)) - if (TTP->getTemplateParameters()->containsUnexpandedParameterPack()) - ContainsUnexpandedParameterPack = true; - - // FIXME: If a default argument contains an unexpanded parameter pack, the - // template parameter list does too. + bool IsPack = P->isTemplateParameterPack(); + if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) { + if (!IsPack && NTTP->getType()->containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; + if (NTTP->hasPlaceholderTypeConstraint()) + HasConstrainedParameters = true; + } else if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(P)) { + if (!IsPack && + TTP->getTemplateParameters()->containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; + } else if (const TypeConstraint *TC = + cast<TemplateTypeParmDecl>(P)->getTypeConstraint()) { + if (TC->getImmediatelyDeclaredConstraint() + ->containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; + HasConstrainedParameters = true; } + // FIXME: If a default argument contains an unexpanded parameter pack, the + // template parameter list does too. } - if (RequiresClause) { - *getTrailingObjects<Expr *>() = RequiresClause; + + if (HasRequiresClause) { if (RequiresClause->containsUnexpandedParameterPack()) ContainsUnexpandedParameterPack = true; + *getTrailingObjects<Expr *>() = RequiresClause; } } @@ -83,7 +94,7 @@ TemplateParameterList::Create(const ASTContext &C, SourceLocation TemplateLoc, void *Mem = C.Allocate(totalSizeToAlloc<NamedDecl *, Expr *>( Params.size(), RequiresClause ? 1u : 0u), alignof(TemplateParameterList)); - return new (Mem) TemplateParameterList(TemplateLoc, LAngleLoc, Params, + return new (Mem) TemplateParameterList(C, TemplateLoc, LAngleLoc, Params, RAngleLoc, RequiresClause); } @@ -91,11 +102,23 @@ unsigned TemplateParameterList::getMinRequiredArguments() const { unsigned NumRequiredArgs = 0; for (const NamedDecl *P : asArray()) { if (P->isTemplateParameterPack()) { - if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) + if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) { if (NTTP->isExpandedParameterPack()) { NumRequiredArgs += NTTP->getNumExpansionTypes(); continue; } + } else if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(P)) { + if (TTP->isExpandedParameterPack()) { + NumRequiredArgs += TTP->getNumExpansionParameters(); + continue; + } + } else { + const auto *TP = cast<TemplateTemplateParmDecl>(P); + if (TP->isExpandedParameterPack()) { + NumRequiredArgs += TP->getNumExpansionTemplateParameters(); + continue; + } + } break; } @@ -140,14 +163,17 @@ static void AdoptTemplateParameterList(TemplateParameterList *Params, void TemplateParameterList:: getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const { - // TODO: Concepts: Collect immediately-introduced constraints. + if (HasConstrainedParameters) + for (const NamedDecl *Param : *this) + if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) + if (const auto *TC = TTP->getTypeConstraint()) + AC.push_back(TC->getImmediatelyDeclaredConstraint()); if (HasRequiresClause) AC.push_back(getRequiresClause()); } bool TemplateParameterList::hasAssociatedConstraints() const { - // TODO: Concepts: Regard immediately-introduced constraints. - return HasRequiresClause; + return HasRequiresClause || HasConstrainedParameters; } namespace clang { @@ -558,9 +584,14 @@ TemplateTypeParmDecl * TemplateTypeParmDecl::Create(const ASTContext &C, DeclContext *DC, SourceLocation KeyLoc, SourceLocation NameLoc, unsigned D, unsigned P, IdentifierInfo *Id, - bool Typename, bool ParameterPack) { + bool Typename, bool ParameterPack, + bool HasTypeConstraint, + Optional<unsigned> NumExpanded) { auto *TTPDecl = - new (C, DC) TemplateTypeParmDecl(DC, KeyLoc, NameLoc, Id, Typename); + new (C, DC, + additionalSizeToAlloc<TypeConstraint>(HasTypeConstraint ? 1 : 0)) + TemplateTypeParmDecl(DC, KeyLoc, NameLoc, Id, Typename, + HasTypeConstraint, NumExpanded); QualType TTPType = C.getTemplateTypeParmType(D, P, ParameterPack, TTPDecl); TTPDecl->setTypeForDecl(TTPType.getTypePtr()); return TTPDecl; @@ -569,7 +600,17 @@ TemplateTypeParmDecl::Create(const ASTContext &C, DeclContext *DC, TemplateTypeParmDecl * TemplateTypeParmDecl::CreateDeserialized(const ASTContext &C, unsigned ID) { return new (C, ID) TemplateTypeParmDecl(nullptr, SourceLocation(), - SourceLocation(), nullptr, false); + SourceLocation(), nullptr, false, + false, None); +} + +TemplateTypeParmDecl * +TemplateTypeParmDecl::CreateDeserialized(const ASTContext &C, unsigned ID, + bool HasTypeConstraint) { + return new (C, ID, + additionalSizeToAlloc<TypeConstraint>(HasTypeConstraint ? 1 : 0)) + TemplateTypeParmDecl(nullptr, SourceLocation(), SourceLocation(), + nullptr, false, HasTypeConstraint, None); } SourceLocation TemplateTypeParmDecl::getDefaultArgumentLoc() const { @@ -602,6 +643,20 @@ bool TemplateTypeParmDecl::isParameterPack() const { return getTypeForDecl()->castAs<TemplateTypeParmType>()->isParameterPack(); } +void TemplateTypeParmDecl::setTypeConstraint(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>()) TypeConstraint(NNS, NameInfo, + FoundDecl, CD, ArgsAsWritten, ImmediatelyDeclaredConstraint); + TypeConstraintInitialized = true; +} + //===----------------------------------------------------------------------===// // NonTypeTemplateParmDecl Method Implementations //===----------------------------------------------------------------------===// @@ -1256,7 +1311,8 @@ createMakeIntegerSeqParameterList(const ASTContext &C, DeclContext *DC) { // typename T auto *T = TemplateTypeParmDecl::Create( C, DC, SourceLocation(), SourceLocation(), /*Depth=*/1, /*Position=*/0, - /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/false); + /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/false, + /*HasTypeConstraint=*/false); T->setImplicit(true); // T ...Ints @@ -1281,7 +1337,8 @@ createMakeIntegerSeqParameterList(const ASTContext &C, DeclContext *DC) { // typename T auto *TemplateTypeParm = TemplateTypeParmDecl::Create( C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/1, - /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/false); + /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/false, + /*HasTypeConstraint=*/false); TemplateTypeParm->setImplicit(true); // T N @@ -1309,7 +1366,8 @@ createTypePackElementParameterList(const ASTContext &C, DeclContext *DC) { // typename ...T auto *Ts = TemplateTypeParmDecl::Create( C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/1, - /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/true); + /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/true, + /*HasTypeConstraint=*/false); Ts->setImplicit(true); // template <std::size_t Index, typename ...T> @@ -1339,3 +1397,15 @@ BuiltinTemplateDecl::BuiltinTemplateDecl(const ASTContext &C, DeclContext *DC, : TemplateDecl(BuiltinTemplate, DC, SourceLocation(), Name, createBuiltinTemplateParameterList(C, DC, BTK)), BTK(BTK) {} + +void TypeConstraint::print(llvm::raw_ostream &OS, PrintingPolicy Policy) const { + if (NestedNameSpec) + NestedNameSpec.getNestedNameSpecifier()->print(OS, Policy); + ConceptName.printName(OS, Policy); + if (hasExplicitTemplateArgs()) { + OS << "<"; + for (auto &ArgLoc : ArgsAsWritten->arguments()) + ArgLoc.getArgument().print(Policy, OS); + OS << ">"; + } +}
\ No newline at end of file diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index deb76820afe..422227d787b 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1765,9 +1765,9 @@ CUDAKernelCallExpr *CUDAKernelCallExpr::CreateEmpty(const ASTContext &Ctx, return new (Mem) CUDAKernelCallExpr(NumArgs, Empty); } -ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C, +ConceptSpecializationExpr::ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, - SourceLocation ConceptNameLoc, NamedDecl *FoundDecl, + DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, ArrayRef<TemplateArgument> ConvertedArgs, const ConstraintSatisfaction *Satisfaction) @@ -1776,34 +1776,31 @@ ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C, // All the flags below are set in setTemplateArguments. /*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false, /*ContainsUnexpandedParameterPacks=*/false), - NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc), - ConceptNameLoc(ConceptNameLoc), FoundDecl(FoundDecl), - NamedConcept(NamedConcept), NumTemplateArgs(ConvertedArgs.size()), + ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, + NamedConcept, ArgsAsWritten), + NumTemplateArgs(ConvertedArgs.size()), Satisfaction(Satisfaction ? ASTConstraintSatisfaction::Create(C, *Satisfaction) : nullptr) { - setTemplateArguments(ArgsAsWritten, ConvertedArgs); + setTemplateArguments(ConvertedArgs); } ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs) - : Expr(ConceptSpecializationExprClass, Empty), + : Expr(ConceptSpecializationExprClass, Empty), ConceptReference(), NumTemplateArgs(NumTemplateArgs) { } void ConceptSpecializationExpr::setTemplateArguments( - const ASTTemplateArgumentListInfo *ArgsAsWritten, ArrayRef<TemplateArgument> Converted) { assert(Converted.size() == NumTemplateArgs); - assert(!this->ArgsAsWritten && "setTemplateArguments can only be used once"); - this->ArgsAsWritten = ArgsAsWritten; std::uninitialized_copy(Converted.begin(), Converted.end(), getTrailingObjects<TemplateArgument>()); bool IsInstantiationDependent = false; bool ContainsUnexpandedParameterPack = false; - for (const TemplateArgumentLoc& LocInfo : ArgsAsWritten->arguments()) { - if (LocInfo.getArgument().isInstantiationDependent()) + for (const TemplateArgument& Arg : Converted) { + if (Arg.isInstantiationDependent()) IsInstantiationDependent = true; - if (LocInfo.getArgument().containsUnexpandedParameterPack()) + if (Arg.containsUnexpandedParameterPack()) ContainsUnexpandedParameterPack = true; if (ContainsUnexpandedParameterPack && IsInstantiationDependent) break; @@ -1821,9 +1818,10 @@ void ConceptSpecializationExpr::setTemplateArguments( } ConceptSpecializationExpr * -ConceptSpecializationExpr::Create(ASTContext &C, NestedNameSpecifierLoc NNS, +ConceptSpecializationExpr::Create(const ASTContext &C, + NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, - SourceLocation ConceptNameLoc, + DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, @@ -1832,7 +1830,7 @@ ConceptSpecializationExpr::Create(ASTContext &C, NestedNameSpecifierLoc NNS, void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( ConvertedArgs.size())); return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc, - ConceptNameLoc, FoundDecl, + ConceptNameInfo, FoundDecl, NamedConcept, ArgsAsWritten, ConvertedArgs, Satisfaction); } diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 3b89c630b45..27fdca1c4b9 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -380,6 +380,11 @@ public: } Hash.AddBoolean(D->isParameterPack()); + const TypeConstraint *TC = D->getTypeConstraint(); + Hash.AddBoolean(TC != nullptr); + if (TC) + AddStmt(TC->getImmediatelyDeclaredConstraint()); + Inherited::VisitTemplateTypeParmDecl(D); } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index fa7f4130b76..965ad17fcfa 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1694,7 +1694,16 @@ void TextNodeDumper::VisitBuiltinTemplateDecl(const BuiltinTemplateDecl *D) { } void TextNodeDumper::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) { - if (D->wasDeclaredWithTypename()) + if (const auto *TC = D->getTypeConstraint()) { + OS << " "; + dumpBareDeclRef(TC->getNamedConcept()); + if (TC->getNamedConcept() != TC->getFoundDecl()) { + OS << " ("; + dumpBareDeclRef(TC->getFoundDecl()); + OS << ")"; + } + Visit(TC->getImmediatelyDeclaredConstraint()); + } else if (D->wasDeclaredWithTypename()) OS << " typename"; else OS << " class"; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index c94ccee6028..69a3ed9cbad 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -5121,6 +5121,13 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { return !DisambiguatingWithExpression || !isStartOfObjCClassMessageMissingOpenBracket(); + // placeholder-type-specifier + case tok::annot_template_id: { + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); + return TemplateId->Kind == TNK_Concept_template && + (NextToken().is(tok::kw_auto) || NextToken().is(tok::kw_decltype)); + } + case tok::kw___declspec: case tok::kw___cdecl: case tok::kw___stdcall: diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index f4ffa08b2a1..e685d5ea8a9 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -142,11 +142,16 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType, /// /// \param OnlyNamespace If true, only considers namespaces in lookup. /// +/// /// \returns true if there was an error parsing a scope specifier -bool Parser::ParseOptionalCXXScopeSpecifier( - CXXScopeSpec &SS, ParsedType ObjectType, bool EnteringContext, - bool *MayBePseudoDestructor, bool IsTypename, IdentifierInfo **LastII, - bool OnlyNamespace, bool InUsingDeclaration) { +bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, + ParsedType ObjectType, + bool EnteringContext, + bool *MayBePseudoDestructor, + bool IsTypename, + IdentifierInfo **LastII, + bool OnlyNamespace, + bool InUsingDeclaration) { assert(getLangOpts().CPlusPlus && "Call sites of this function should be guarded by checking for C++"); @@ -476,6 +481,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier( // nested-name-specifier: // type-name '<' if (Next.is(tok::less)) { + TemplateTy Template; UnqualifiedId TemplateName; TemplateName.setIdentifier(&II, Tok.getLocation()); @@ -500,7 +506,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier( // template-id to be translated into a type annotation, // because some clients (e.g., the parsing of class template // specializations) still want to see the original template-id - // token. + // token, and it might not be a type at all (e.g. a concept name in a + // type-constraint). ConsumeToken(); if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(), TemplateName, false)) diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 35cee596bb0..1b9301b6591 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -12,6 +12,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" #include "clang/Parse/RAIIObjectsForParser.h" @@ -498,7 +499,10 @@ Parser::ParseTemplateParameterList(const unsigned Depth, /// Determine whether the parser is at the start of a template /// type parameter. -bool Parser::isStartOfTemplateTypeParameter() { +/// \param ScopeError will receive true if there was an error parsing a +/// scope specifier at the current location. +bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { + ScopeError = false; if (Tok.is(tok::kw_class)) { // "class" may be the start of an elaborated-type-specifier or a // type-parameter. Per C++ [temp.param]p3, we prefer the type-parameter. @@ -531,6 +535,40 @@ bool Parser::isStartOfTemplateTypeParameter() { } } + bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); + CXXScopeSpec SS; + ScopeError = + ParseOptionalCXXScopeSpecifier(SS, ParsedType(), + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + // If this is not a type-constraint, then + // this scope-spec is part of the typename + // of a non-type template parameter + /*IsTypename=*/true, /*LastII=*/nullptr, + // We won't find concepts in + // non-namespaces anyway, so might as well + // parse this correctly for possible type + // names. + /*OnlyNamespace=*/false); + if (ScopeError) + return false; + if (TryAnnotateTypeConstraint(SS)) + return false; + bool IsTypeConstraint = isTypeConstraintAnnotation(); + if (!IsTypeConstraint && SS.isNotEmpty()) { + // This isn't a type-constraint but we've already parsed this scope + // specifier - annotate it. + AnnotateScopeToken(SS, /*isNewAnnotation=*/!WasScopeAnnotation); + return false; + } + + if (IsTypeConstraint && + // Next token might be 'auto' or 'decltype', indicating that this + // type-constraint is in fact part of a placeholder-type-specifier of a + // non-type template parameter. + !NextToken().isOneOf(tok::kw_auto, tok::kw_decltype)) + return true; + // 'typedef' is a reasonably-common typo/thinko for 'typename', and is // ill-formed otherwise. if (Tok.isNot(tok::kw_typename) && Tok.isNot(tok::kw_typedef)) @@ -574,24 +612,37 @@ bool Parser::isStartOfTemplateTypeParameter() { /// type-parameter /// parameter-declaration /// -/// type-parameter: (see below) -/// 'class' ...[opt] identifier[opt] -/// 'class' identifier[opt] '=' type-id -/// 'typename' ...[opt] identifier[opt] -/// 'typename' identifier[opt] '=' type-id -/// 'template' '<' template-parameter-list '>' -/// 'class' ...[opt] identifier[opt] -/// 'template' '<' template-parameter-list '>' 'class' identifier[opt] -/// = id-expression +/// type-parameter: (See below) +/// type-parameter-key ...[opt] identifier[opt] +/// type-parameter-key identifier[opt] = type-id +/// (C++2a) type-constraint ...[opt] identifier[opt] +/// (C++2a) type-constraint identifier[opt] = type-id +/// 'template' '<' template-parameter-list '>' type-parameter-key +/// ...[opt] identifier[opt] +/// 'template' '<' template-parameter-list '>' type-parameter-key +/// identifier[opt] '=' id-expression +/// +/// type-parameter-key: +/// class +/// typename +/// NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { - if (isStartOfTemplateTypeParameter()) { - // Is there just a typo in the input code? ('typedef' instead of 'typename') + // We could be facing a type-constraint, which (could) start a type parameter. + // Annotate it now (we might end up not using it if we determine this + // type-constraint is in fact part of a placeholder-type-specifier of a + // non-type template parameter. + + bool ScopeError; + if (isStartOfTemplateTypeParameter(ScopeError)) { + // Is there just a typo in the input code? ('typedef' instead of + // 'typename') if (Tok.is(tok::kw_typedef)) { Diag(Tok.getLocation(), diag::err_expected_template_parameter); Diag(Tok.getLocation(), diag::note_meant_to_use_typename) << FixItHint::CreateReplacement(CharSourceRange::getCharRange( - Tok.getLocation(), Tok.getEndLoc()), + Tok.getLocation(), + Tok.getEndLoc()), "typename"); Tok.setKind(tok::kw_typename); @@ -599,7 +650,26 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { return ParseTypeParameter(Depth, Position); } - + if (ScopeError) { + // We return an invalid parameter as opposed to null to avoid having bogus + // diagnostics about an empty template parameter list. + // FIXME: Fix ParseTemplateParameterList to better handle nullptr results + // from here. + // Return a NTTP as if there was an error in a scope specifier, the user + // probably meant to write the type of a NTTP. + DeclSpec DS(getAttrFactory()); + DS.SetTypeSpecError(); + Declarator D(DS, DeclaratorContext::TemplateParamContext); + D.SetIdentifier(nullptr, Tok.getLocation()); + D.setInvalidType(true); + NamedDecl *ErrorParam = Actions.ActOnNonTypeTemplateParameter( + getCurScope(), D, Depth, Position, /*EqualLoc=*/SourceLocation(), + /*DefaultArg=*/nullptr); + ErrorParam->setInvalidDecl(true); + SkipUntil(tok::comma, tok::greater, tok::greatergreater, + StopAtSemi | StopBeforeMatch); + return ErrorParam; + } if (Tok.is(tok::kw_template)) return ParseTemplateTemplateParameter(Depth, Position); @@ -609,6 +679,56 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { return ParseNonTypeTemplateParameter(Depth, Position); } +/// Check whether the current token is a template-id annotation denoting a +/// type-constraint. +bool Parser::isTypeConstraintAnnotation() { + if (Tok.isNot(tok::annot_template_id)) + return false; + const auto *ExistingAnnot = + static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + return ExistingAnnot->Kind == TNK_Concept_template; +} + +/// Try parsing a type-constraint construct at the current location, after the +/// optional scope specifier. +/// +/// type-constraint: +/// nested-name-specifier[opt] concept-name +/// nested-name-specifier[opt] concept-name +/// '<' template-argument-list[opt] '>'[opt] +/// +/// \returns true if an error occurred, and false otherwise. +bool Parser::TryAnnotateTypeConstraint(CXXScopeSpec &SS) { + if (!getLangOpts().ConceptsTS || Tok.isNot(tok::identifier)) + return false; + + UnqualifiedId PossibleConceptName; + PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(), + Tok.getLocation()); + + TemplateTy PossibleConcept; + bool MemberOfUnknownSpecialization = false; + auto TNK = Actions.isTemplateName(getCurScope(), SS, + /*hasTemplateKeyword=*/false, + PossibleConceptName, + /*ObjectType=*/ParsedType(), + /*EnteringContext=*/false, + PossibleConcept, + MemberOfUnknownSpecialization); + assert(!MemberOfUnknownSpecialization + && "Member when we only allowed namespace scope qualifiers??"); + if (!PossibleConcept || TNK != TNK_Concept_template) + return false; + + // At this point we're sure we're dealing with a constrained parameter. It + // may or may not have a template parameter list following the concept name. + return AnnotateTemplateIdToken(PossibleConcept, TNK, SS, + /*TemplateKWLoc=*/SourceLocation(), + PossibleConceptName, + /*AllowTypeAnnotation=*/false, + /*TypeConstraint=*/true); +} + /// ParseTypeParameter - Parse a template type parameter (C++ [temp.param]). /// Other kinds of template parameters are parsed in /// ParseTemplateTemplateParameter and ParseNonTypeTemplateParameter. @@ -619,12 +739,25 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { /// 'typename' ...[opt][C++0x] identifier[opt] /// 'typename' identifier[opt] '=' type-id NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { - assert(Tok.isOneOf(tok::kw_class, tok::kw_typename) && - "A type-parameter starts with 'class' or 'typename'"); - - // Consume the 'class' or 'typename' keyword. - bool TypenameKeyword = Tok.is(tok::kw_typename); - SourceLocation KeyLoc = ConsumeToken(); + assert(Tok.isOneOf(tok::kw_class, tok::kw_typename, tok::annot_template_id) && + "A type-parameter starts with 'class', 'typename' or a " + "type-constraint"); + + TemplateIdAnnotation *TypeConstraint = nullptr; + bool TypenameKeyword = false; + SourceLocation KeyLoc; + if (Tok.is(tok::annot_template_id)) { + // Consume the 'type-constraint'. + TypeConstraint = + static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + assert(TypeConstraint->Kind == TNK_Concept_template && + "stray non-concept template-id annotation"); + KeyLoc = ConsumeAnnotationToken(); + } else { + // Consume the 'class' or 'typename' keyword. + TypenameKeyword = Tok.is(tok::kw_typename); + KeyLoc = ConsumeToken(); + } // Grab the ellipsis (if given). SourceLocation EllipsisLoc; @@ -664,9 +797,19 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { DefaultArg = ParseTypeName(/*Range=*/nullptr, DeclaratorContext::TemplateTypeArgContext).get(); - return Actions.ActOnTypeParameter(getCurScope(), TypenameKeyword, EllipsisLoc, - KeyLoc, ParamName, NameLoc, Depth, Position, - EqualLoc, DefaultArg); + NamedDecl *NewDecl = Actions.ActOnTypeParameter(getCurScope(), + TypenameKeyword, EllipsisLoc, + KeyLoc, ParamName, NameLoc, + Depth, Position, EqualLoc, + DefaultArg, + TypeConstraint != nullptr); + + if (TypeConstraint) + Actions.ActOnTypeConstraint(TypeConstraint, + cast<TemplateTypeParmDecl>(NewDecl), + EllipsisLoc); + + return NewDecl; } /// ParseTemplateTemplateParameter - Handle the parsing of template @@ -829,7 +972,7 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) { // Create the parameter. return Actions.ActOnNonTypeTemplateParameter(getCurScope(), ParamDecl, - Depth, Position, EqualLoc, + Depth, Position, EqualLoc, DefaultArg.get()); } @@ -1105,6 +1248,10 @@ Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken, /// simple-template-id is always replaced with a template-id /// annotation token. /// +/// \param TypeConstraint if true, then this is actually a type-constraint, +/// meaning that the template argument list can be omitted (and the template in +/// question must be a concept). +/// /// If an unrecoverable parse error occurs and no annotation token can be /// formed, this function returns true. /// @@ -1112,10 +1259,15 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, UnqualifiedId &TemplateName, - bool AllowTypeAnnotation) { + bool AllowTypeAnnotation, + bool TypeConstraint) { assert(getLangOpts().CPlusPlus && "Can only annotate template-ids in C++"); - assert(Template && Tok.is(tok::less) && + assert(Template && (Tok.is(tok::less) || TypeConstraint) && "Parser isn't at the beginning of a template-id"); + assert(!(TypeConstraint && AllowTypeAnnotation) && "type-constraint can't be " + "a type annotation"); + assert((!TypeConstraint || TNK == TNK_Concept_template) && "type-constraint " + "must accompany a concept name"); // Consume the template-name. SourceLocation TemplateNameLoc = TemplateName.getSourceRange().getBegin(); @@ -1123,17 +1275,19 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, // Parse the enclosed template argument list. SourceLocation LAngleLoc, RAngleLoc; TemplateArgList TemplateArgs; - bool Invalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc, - TemplateArgs, - RAngleLoc); - - if (Invalid) { - // If we failed to parse the template ID but skipped ahead to a >, we're not - // going to be able to form a token annotation. Eat the '>' if present. - TryConsumeToken(tok::greater); - // FIXME: Annotate the token stream so we don't produce the same errors - // again if we're doing this annotation as part of a tentative parse. - return true; + if (!TypeConstraint || Tok.is(tok::less)) { + bool Invalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc, + TemplateArgs, + RAngleLoc); + + if (Invalid) { + // If we failed to parse the template ID but skipped ahead to a >, we're not + // going to be able to form a token annotation. Eat the '>' if present. + TryConsumeToken(tok::greater); + // FIXME: Annotate the token stream so we don't produce the same errors + // again if we're doing this annotation as part of a tentative parse. + return true; + } } ASTTemplateArgsPtr TemplateArgsPtr(TemplateArgs); @@ -1371,8 +1525,9 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() { // Parse a non-type template argument. SourceLocation Loc = Tok.getLocation(); ExprResult ExprArg = ParseConstantExpressionInExprEvalContext(MaybeTypeCast); - if (ExprArg.isInvalid() || !ExprArg.get()) + if (ExprArg.isInvalid() || !ExprArg.get()) { return ParsedTemplateArgument(); + } return ParsedTemplateArgument(ParsedTemplateArgument::NonType, ExprArg.get(), Loc); diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index e14c63d557a..7260977c634 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -2986,7 +2986,11 @@ static void AddTemplateParameterChunks( if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*P)) { if (TTP->wasDeclaredWithTypename()) PlaceholderStr = "typename"; - else + else if (const auto *TC = TTP->getTypeConstraint()) { + llvm::raw_string_ostream OS(PlaceholderStr); + TC->print(OS, Policy); + OS.flush(); + } else PlaceholderStr = "class"; if (TTP->getIdentifier()) { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 6f22326e8fc..1184446796e 100755 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -976,20 +976,23 @@ ParsedTemplateArgument Sema::ActOnTemplateTypeArgument(TypeResult ParsedType) { /// If the type parameter has a default argument, it will be added /// later via ActOnTypeParameterDefault. NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename, - SourceLocation EllipsisLoc, - SourceLocation KeyLoc, - IdentifierInfo *ParamName, - SourceLocation ParamNameLoc, - unsigned Depth, unsigned Position, - SourceLocation EqualLoc, - ParsedType DefaultArg) { + SourceLocation EllipsisLoc, + SourceLocation KeyLoc, + IdentifierInfo *ParamName, + SourceLocation ParamNameLoc, + unsigned Depth, unsigned Position, + SourceLocation EqualLoc, + ParsedType DefaultArg, + bool HasTypeConstraint) { assert(S->isTemplateParamScope() && "Template type parameter not in template parameter scope!"); bool IsParameterPack = EllipsisLoc.isValid(); - TemplateTypeParmDecl *Param = TemplateTypeParmDecl::Create( - Context, Context.getTranslationUnitDecl(), KeyLoc, ParamNameLoc, Depth, - Position, ParamName, Typename, IsParameterPack); + TemplateTypeParmDecl *Param + = TemplateTypeParmDecl::Create(Context, Context.getTranslationUnitDecl(), + KeyLoc, ParamNameLoc, Depth, Position, + ParamName, Typename, IsParameterPack, + HasTypeConstraint); Param->setAccess(AS_public); if (Param->isParameterPack()) @@ -1036,6 +1039,125 @@ NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename, return Param; } +/// Convert the parser's template argument list representation into our form. +static TemplateArgumentListInfo +makeTemplateArgumentListInfo(Sema &S, TemplateIdAnnotation &TemplateId) { + TemplateArgumentListInfo TemplateArgs(TemplateId.LAngleLoc, + TemplateId.RAngleLoc); + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId.getTemplateArgs(), + TemplateId.NumArgs); + S.translateTemplateArguments(TemplateArgsPtr, TemplateArgs); + return TemplateArgs; +} + +bool Sema::ActOnTypeConstraint(TemplateIdAnnotation *TypeConstr, + TemplateTypeParmDecl *ConstrainedParameter, + SourceLocation EllipsisLoc) { + ConceptDecl *CD = + cast<ConceptDecl>(TypeConstr->Template.get().getAsTemplateDecl()); + + // C++2a [temp.param]p4: + // [...] The concept designated by a type-constraint shall be a type + // concept ([temp.concept]). + if (!CD->isTypeConcept()) { + Diag(TypeConstr->TemplateNameLoc, + diag::err_type_constraint_non_type_concept); + return true; + } + + bool WereArgsSpecified = TypeConstr->LAngleLoc.isValid(); + + if (!WereArgsSpecified && + CD->getTemplateParameters()->getMinRequiredArguments() > 1) { + Diag(TypeConstr->TemplateNameLoc, + diag::err_type_constraint_missing_arguments) << CD; + return true; + } + + TemplateArgumentListInfo TemplateArgs; + if (TypeConstr->LAngleLoc.isValid()) { + TemplateArgs = + makeTemplateArgumentListInfo(*this, *TypeConstr); + } + return AttachTypeConstraint( + TypeConstr->SS.isSet() ? TypeConstr->SS.getWithLocInContext(Context) : + NestedNameSpecifierLoc(), + DeclarationNameInfo(DeclarationName(TypeConstr->Name), + TypeConstr->TemplateNameLoc), CD, + TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr, + ConstrainedParameter, EllipsisLoc); +} + +/// Attach a type-constraint to a template parameter. +/// \returns true if an error occured. This can happen if the +/// immediately-declared constraint could not be formed (e.g. incorrect number +/// of arguments for the named concept). +bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS, + DeclarationNameInfo NameInfo, + ConceptDecl *NamedConcept, + const TemplateArgumentListInfo *TemplateArgs, + TemplateTypeParmDecl *ConstrainedParameter, + SourceLocation EllipsisLoc) { + // C++2a [temp.param]p4: + // [...] If Q is of the form C<A1, ..., An>, then let E' be + // C<T, A1, ..., An>. Otherwise, let E' be C<T>. [...] + const ASTTemplateArgumentListInfo *ArgsAsWritten = + TemplateArgs ? ASTTemplateArgumentListInfo::Create(Context, + *TemplateArgs) : nullptr; + + QualType ParamAsArgument(ConstrainedParameter->getTypeForDecl(), 0); + TemplateArgumentListInfo ConstraintArgs; + ConstraintArgs.addArgument( + TemplateArgumentLoc( + TemplateArgument(ParamAsArgument), + TemplateArgumentLocInfo( + Context.getTrivialTypeSourceInfo(ParamAsArgument, + ConstrainedParameter->getLocation())))); + if (TemplateArgs) { + ConstraintArgs.setRAngleLoc(TemplateArgs->getRAngleLoc()); + ConstraintArgs.setLAngleLoc(TemplateArgs->getLAngleLoc()); + for (const TemplateArgumentLoc &ArgLoc : TemplateArgs->arguments()) + ConstraintArgs.addArgument(ArgLoc); + } + + // C++2a [temp.param]p4: + // [...] This constraint-expression E is called the immediately-declared + // constraint of T. [...] + CXXScopeSpec SS; + SS.Adopt(NS); + ExprResult ImmediatelyDeclaredConstraint = CheckConceptTemplateId(SS, + /*TemplateKWLoc=*/SourceLocation(), NameInfo, /*FoundDecl=*/NamedConcept, + NamedConcept, &ConstraintArgs); + if (ImmediatelyDeclaredConstraint.isInvalid()) + return true; + + if (ConstrainedParameter->isParameterPack()) { + // C++2a [temp.param]p4: + // [...] If T is not a pack, then E is E', otherwise E is (E' && ...). + // + // We have the following case: + // + // template<typename T> concept C1 = true; + // template<C1... T> struct s1; + // + // The constraint: (C1<T> && ...) + ImmediatelyDeclaredConstraint = + BuildCXXFoldExpr(/*LParenLoc=*/SourceLocation(), + ImmediatelyDeclaredConstraint.get(), BO_LAnd, + EllipsisLoc, /*RHS=*/nullptr, + /*RParenLoc=*/SourceLocation(), + /*NumExpansions=*/None).get(); + if (ImmediatelyDeclaredConstraint.isInvalid()) + return true; + } + + ConstrainedParameter->setTypeConstraint(NS, NameInfo, + /*FoundDecl=*/NamedConcept, + NamedConcept, ArgsAsWritten, + ImmediatelyDeclaredConstraint.get()); + return false; +} + /// Check that the type of a non-type template parameter is /// well-formed. /// @@ -1868,7 +1990,23 @@ private: SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(), /*Depth*/ 0, Depth1IndexAdjustment + TTP->getIndex(), TTP->getIdentifier(), TTP->wasDeclaredWithTypename(), - TTP->isParameterPack()); + TTP->isParameterPack(), TTP->hasTypeConstraint(), + TTP->isExpandedParameterPack() ? + llvm::Optional<unsigned>(TTP->getNumExpansionParameters()) : None); + if (const auto *TC = TTP->getTypeConstraint()) { + TemplateArgumentListInfo TransformedArgs; + const auto *ArgsAsWritten = TC->getTemplateArgsAsWritten(); + if (SemaRef.Subst(ArgsAsWritten->getTemplateArgs(), + ArgsAsWritten->NumTemplateArgs, TransformedArgs, + Args)) + SemaRef.AttachTypeConstraint( + TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(), + TC->getNamedConcept(), &TransformedArgs, NewTTP, + NewTTP->isParameterPack() + ? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint()) + ->getEllipsisLoc() + : SourceLocation()); + } if (TTP->hasDefaultArgument()) { TypeSourceInfo *InstantiatedDefaultArg = SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args, @@ -2177,6 +2315,17 @@ static bool DiagnoseUnexpandedParameterPacks(Sema &S, TemplateParameterList *Params = TTP->getTemplateParameters(); for (unsigned I = 0, N = Params->size(); I != N; ++I) { NamedDecl *P = Params->getParam(I); + if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(P)) { + if (!TTP->isParameterPack()) + if (const TypeConstraint *TC = TTP->getTypeConstraint()) + if (TC->hasExplicitTemplateArgs()) + for (auto &ArgLoc : TC->getTemplateArgsAsWritten()->arguments()) + if (S.DiagnoseUnexpandedParameterPack(ArgLoc, + Sema::UPPC_TypeConstraint)) + return true; + continue; + } + if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) { if (!NTTP->isParameterPack() && S.DiagnoseUnexpandedParameterPack(NTTP->getLocation(), @@ -3711,17 +3860,6 @@ static bool isSameAsPrimaryTemplate(TemplateParameterList *Params, return true; } -/// Convert the parser's template argument list representation into our form. -static TemplateArgumentListInfo -makeTemplateArgumentListInfo(Sema &S, TemplateIdAnnotation &TemplateId) { - TemplateArgumentListInfo TemplateArgs(TemplateId.LAngleLoc, - TemplateId.RAngleLoc); - ASTTemplateArgsPtr TemplateArgsPtr(TemplateId.getTemplateArgs(), - TemplateId.NumArgs); - S.translateTemplateArguments(TemplateArgsPtr, TemplateArgs); - return TemplateArgs; -} - template<typename PartialSpecDecl> static void checkMoreSpecializedThanPrimary(Sema &S, PartialSpecDecl *Partial) { if (Partial->getDeclContext()->isDependentContext()) @@ -4233,14 +4371,14 @@ void Sema::diagnoseMissingTemplateArguments(TemplateName Name, ExprResult Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - SourceLocation ConceptNameLoc, + const DeclarationNameInfo &ConceptNameInfo, NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs) { assert(NamedConcept && "A concept template id without a template?"); llvm::SmallVector<TemplateArgument, 4> Converted; - if (CheckTemplateArgumentList(NamedConcept, ConceptNameLoc, + if (CheckTemplateArgumentList(NamedConcept, ConceptNameInfo.getLoc(), const_cast<TemplateArgumentListInfo&>(*TemplateArgs), /*PartialTemplateArgs=*/false, Converted, /*UpdateArgsWithConversion=*/false)) @@ -4259,14 +4397,14 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, {NamedConcept->getConstraintExpr()}, Converted, SourceRange(SS.isSet() ? SS.getBeginLoc() : - ConceptNameLoc, - TemplateArgs->getRAngleLoc()), - Satisfaction)) + ConceptNameInfo.getLoc(), + TemplateArgs->getRAngleLoc()), + Satisfaction)) return ExprError(); return ConceptSpecializationExpr::Create(Context, SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{}, - TemplateKWLoc, ConceptNameLoc, FoundDecl, NamedConcept, + TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept, ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs), Converted, AreArgsDependent ? nullptr : &Satisfaction); } @@ -4312,8 +4450,7 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, } if (R.getAsSingle<ConceptDecl>()) { - return CheckConceptTemplateId(SS, TemplateKWLoc, - R.getLookupNameInfo().getBeginLoc(), + return CheckConceptTemplateId(SS, TemplateKWLoc, R.getLookupNameInfo(), R.getFoundDecl(), R.getAsSingle<ConceptDecl>(), TemplateArgs); } @@ -5116,7 +5253,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: - if (CheckTemplateTemplateArgument(Params, Arg)) + if (CheckTemplateTemplateArgument(TempParm, Params, Arg)) return true; Converted.push_back(Arg.getArgument()); @@ -5156,6 +5293,12 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, /// In \c A<int,int>::B, \c NTs and \c TTs have expanded pack size 2, and \c Us /// is not a pack expansion, so returns an empty Optional. static Optional<unsigned> getExpandedPackSize(NamedDecl *Param) { + if (TemplateTypeParmDecl *TTP + = dyn_cast<TemplateTypeParmDecl>(Param)) { + if (TTP->isExpandedParameterPack()) + return TTP->getNumExpansionParameters(); + } + if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) { if (NTTP->isExpandedParameterPack()) @@ -6894,7 +7037,8 @@ static void DiagnoseTemplateParameterListArityMismatch( /// /// This routine implements the semantics of C++ [temp.arg.template]. /// It returns true if an error occurred, and false otherwise. -bool Sema::CheckTemplateTemplateArgument(TemplateParameterList *Params, +bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, + TemplateParameterList *Params, TemplateArgumentLoc &Arg) { TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern(); TemplateDecl *Template = Name.getAsTemplateDecl(); @@ -6933,6 +7077,9 @@ bool Sema::CheckTemplateTemplateArgument(TemplateParameterList *Params, // C++1z [temp.arg.template]p3: (DR 150) // A template-argument matches a template template-parameter P when P // is at least as specialized as the template-argument A. + // FIXME: We should enable RelaxedTemplateTemplateArgs by default as it is a + // defect report resolution from C++17 and shouldn't be introduced by + // concepts. if (getLangOpts().RelaxedTemplateTemplateArgs) { // Quick check for the common case: // If P contains a parameter pack, then A [...] matches P if each of A's @@ -6940,12 +7087,39 @@ bool Sema::CheckTemplateTemplateArgument(TemplateParameterList *Params, // the template-parameter-list of P. if (TemplateParameterListsAreEqual( Template->getTemplateParameters(), Params, false, - TPL_TemplateTemplateArgumentMatch, Arg.getLocation())) + TPL_TemplateTemplateArgumentMatch, Arg.getLocation()) && + // If the argument has no associated constraints, then the parameter is + // definitely at least as specialized as the argument. + // Otherwise - we need a more thorough check. + !Template->hasAssociatedConstraints()) return false; if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template, - Arg.getLocation())) + Arg.getLocation())) { + // C++2a[temp.func.order]p2 + // [...] If both deductions succeed, the partial ordering selects the + // more constrained template as described by the rules in + // [temp.constr.order]. + SmallVector<const Expr *, 3> ParamsAC, TemplateAC; + Params->getAssociatedConstraints(ParamsAC); + Template->getAssociatedConstraints(TemplateAC); + bool IsParamAtLeastAsConstrained; + if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, + IsParamAtLeastAsConstrained)) + return true; + if (!IsParamAtLeastAsConstrained) { + Diag(Arg.getLocation(), + diag::err_template_template_parameter_not_at_least_as_constrained) + << Template << Param << Arg.getSourceRange(); + Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; + Diag(Template->getLocation(), diag::note_entity_declared_at) + << Template; + MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, + TemplateAC); + return true; + } return false; + } // FIXME: Produce better diagnostics for deduction failures. } @@ -7123,7 +7297,6 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old, bool Complain, Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) { - // TODO: Concepts: Check constrained-parameter constraints here. // Check the actual kind (type, non-type, template). if (Old->getKind() != New->getKind()) { if (Complain) { @@ -7177,50 +7350,73 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old, // template parameter and one of the non-type template parameter types // is dependent, then we must wait until template instantiation time // to actually compare the arguments. - if (Kind == Sema::TPL_TemplateTemplateArgumentMatch && - (OldNTTP->getType()->isDependentType() || - NewNTTP->getType()->isDependentType())) - return true; - - if (!S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) { - if (Complain) { - unsigned NextDiag = diag::err_template_nontype_parm_different_type; - if (TemplateArgLoc.isValid()) { - S.Diag(TemplateArgLoc, - diag::err_template_arg_template_params_mismatch); - NextDiag = diag::note_template_nontype_parm_different_type; + if (Kind != Sema::TPL_TemplateTemplateArgumentMatch || + (!OldNTTP->getType()->isDependentType() && + !NewNTTP->getType()->isDependentType())) + if (!S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) { + if (Complain) { + unsigned NextDiag = diag::err_template_nontype_parm_different_type; + if (TemplateArgLoc.isValid()) { + S.Diag(TemplateArgLoc, + diag::err_template_arg_template_params_mismatch); + NextDiag = diag::note_template_nontype_parm_different_type; + } + S.Diag(NewNTTP->getLocation(), NextDiag) + << NewNTTP->getType() + << (Kind != Sema::TPL_TemplateMatch); + S.Diag(OldNTTP->getLocation(), + diag::note_template_nontype_parm_prev_declaration) + << OldNTTP->getType(); } - S.Diag(NewNTTP->getLocation(), NextDiag) - << NewNTTP->getType() - << (Kind != Sema::TPL_TemplateMatch); - S.Diag(OldNTTP->getLocation(), - diag::note_template_nontype_parm_prev_declaration) - << OldNTTP->getType(); - } - return false; - } - - return true; + return false; + } } - // For template template parameters, check the template parameter types. // The template parameter lists of template template // parameters must agree. - if (TemplateTemplateParmDecl *OldTTP + else if (TemplateTemplateParmDecl *OldTTP = dyn_cast<TemplateTemplateParmDecl>(Old)) { TemplateTemplateParmDecl *NewTTP = cast<TemplateTemplateParmDecl>(New); - return S.TemplateParameterListsAreEqual(NewTTP->getTemplateParameters(), - OldTTP->getTemplateParameters(), - Complain, + if (!S.TemplateParameterListsAreEqual(NewTTP->getTemplateParameters(), + OldTTP->getTemplateParameters(), + Complain, (Kind == Sema::TPL_TemplateMatch ? Sema::TPL_TemplateTemplateParmMatch : Kind), - TemplateArgLoc); - } + TemplateArgLoc)) + return false; + } else if (Kind != Sema::TPL_TemplateTemplateArgumentMatch) { + const Expr *NewC = nullptr, *OldC = nullptr; + if (const auto *TC = cast<TemplateTypeParmDecl>(New)->getTypeConstraint()) + NewC = TC->getImmediatelyDeclaredConstraint(); + if (const auto *TC = cast<TemplateTypeParmDecl>(Old)->getTypeConstraint()) + OldC = TC->getImmediatelyDeclaredConstraint(); + + auto Diagnose = [&] { + S.Diag(NewC ? NewC->getBeginLoc() : New->getBeginLoc(), + diag::err_template_different_type_constraint); + S.Diag(OldC ? OldC->getBeginLoc() : Old->getBeginLoc(), + diag::note_template_prev_declaration) << /*declaration*/0; + }; - // TODO: Concepts: Match immediately-introduced-constraint for type - // constraints + if (!NewC != !OldC) { + if (Complain) + Diagnose(); + return false; + } + + if (NewC) { + llvm::FoldingSetNodeID OldCID, NewCID; + OldC->Profile(OldCID, S.Context, /*Canonical=*/true); + NewC->Profile(NewCID, S.Context, /*Canonical=*/true); + if (OldCID != NewCID) { + if (Complain) + Diagnose(); + return false; + } + } + } return true; } @@ -7247,15 +7443,6 @@ void DiagnoseTemplateParameterListArityMismatch(Sema &S, << SourceRange(Old->getTemplateLoc(), Old->getRAngleLoc()); } -static void -DiagnoseTemplateParameterListRequiresClauseMismatch(Sema &S, - TemplateParameterList *New, - TemplateParameterList *Old){ - S.Diag(New->getTemplateLoc(), diag::err_template_different_requires_clause); - S.Diag(Old->getTemplateLoc(), diag::note_template_prev_declaration) - << /*declaration*/0; -} - /// Determine whether the given template parameter lists are /// equivalent. /// @@ -7348,9 +7535,17 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New, if (Kind != TPL_TemplateTemplateArgumentMatch) { const Expr *NewRC = New->getRequiresClause(); const Expr *OldRC = Old->getRequiresClause(); + + auto Diagnose = [&] { + Diag(NewRC ? NewRC->getBeginLoc() : New->getTemplateLoc(), + diag::err_template_different_requires_clause); + Diag(OldRC ? OldRC->getBeginLoc() : Old->getTemplateLoc(), + diag::note_template_prev_declaration) << /*declaration*/0; + }; + if (!NewRC != !OldRC) { if (Complain) - DiagnoseTemplateParameterListRequiresClauseMismatch(*this, New, Old); + Diagnose(); return false; } @@ -7360,7 +7555,7 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New, NewRC->Profile(NewRCID, Context, /*Canonical=*/true); if (OldRCID != NewRCID) { if (Complain) - DiagnoseTemplateParameterListRequiresClauseMismatch(*this, New, Old); + Diagnose(); return false; } } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index e626948cb5d..1b9f1b2144d 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -643,6 +643,10 @@ static TemplateParameter makeTemplateParameter(Decl *D) { /// If \p Param is an expanded parameter pack, get the number of expansions. static Optional<unsigned> getExpandedPackSize(NamedDecl *Param) { + if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) + if (TTP->isExpandedParameterPack()) + return TTP->getNumExpansionParameters(); + if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) if (NTTP->isExpandedParameterPack()) return NTTP->getNumExpansionTypes(); @@ -4542,11 +4546,12 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, // Build template<class TemplParam> void Func(FuncParam); TemplateTypeParmDecl *TemplParam = TemplateTypeParmDecl::Create( - Context, nullptr, SourceLocation(), Loc, Depth, 0, nullptr, false, false); + Context, nullptr, SourceLocation(), Loc, Depth, 0, nullptr, false, false, + false); QualType TemplArg = QualType(TemplParam->getTypeForDecl(), 0); NamedDecl *TemplParamPtr = TemplParam; FixedSizeTemplateParameterListStorage<1, false> TemplateParamsSt( - Loc, Loc, TemplParamPtr, Loc, nullptr); + Context, Loc, Loc, TemplParamPtr, Loc, nullptr); QualType FuncParam = SubstituteDeducedTypeTransform(*this, TemplArg, /*UseTypeSugar*/false) @@ -5276,9 +5281,8 @@ Sema::getMoreSpecializedPartialSpecialization( return nullptr; if (IsAtLeastAsConstrained(PS2, AC2, PS1, AC1, AtLeastAsConstrained2)) return nullptr; - if (AtLeastAsConstrained1 == AtLeastAsConstrained2) { + if (AtLeastAsConstrained1 == AtLeastAsConstrained2) return nullptr; - } return AtLeastAsConstrained1 ? PS1 : PS2; } @@ -5352,7 +5356,8 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( SFINAETrap Trap(*this); Context.getInjectedTemplateArgs(P, PArgs); - TemplateArgumentListInfo PArgList(P->getLAngleLoc(), P->getRAngleLoc()); + TemplateArgumentListInfo PArgList(P->getLAngleLoc(), + P->getRAngleLoc()); for (unsigned I = 0, N = P->size(); I != N; ++I) { // Unwrap packs that getInjectedTemplateArgs wrapped around pack // expansions, to form an "as written" argument list. diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index ce94c036eb1..64500d0a26d 100755 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2479,13 +2479,68 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl( // TODO: don't always clone when decls are refcounted. assert(D->getTypeForDecl()->isTemplateTypeParmType()); + Optional<unsigned> NumExpanded; + + if (const TypeConstraint *TC = D->getTypeConstraint()) { + if (D->isPackExpansion() && !D->isExpandedParameterPack()) { + assert(TC->getTemplateArgsAsWritten() && + "type parameter can only be an expansion when explicit arguments " + "are specified"); + // The template type parameter pack's type is a pack expansion of types. + // Determine whether we need to expand this parameter pack into separate + // types. + SmallVector<UnexpandedParameterPack, 2> Unexpanded; + for (auto &ArgLoc : TC->getTemplateArgsAsWritten()->arguments()) + SemaRef.collectUnexpandedParameterPacks(ArgLoc, Unexpanded); + + // Determine whether the set of unexpanded parameter packs can and should + // be expanded. + bool Expand = true; + bool RetainExpansion = false; + if (SemaRef.CheckParameterPacksForExpansion( + cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint()) + ->getEllipsisLoc(), + SourceRange(TC->getConceptNameLoc(), + TC->hasExplicitTemplateArgs() ? + TC->getTemplateArgsAsWritten()->getRAngleLoc() : + TC->getConceptNameInfo().getEndLoc()), + Unexpanded, TemplateArgs, Expand, RetainExpansion, NumExpanded)) + return nullptr; + } + } + TemplateTypeParmDecl *Inst = TemplateTypeParmDecl::Create( SemaRef.Context, Owner, D->getBeginLoc(), D->getLocation(), D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), D->getIndex(), - D->getIdentifier(), D->wasDeclaredWithTypename(), D->isParameterPack()); + D->getIdentifier(), D->wasDeclaredWithTypename(), D->isParameterPack(), + D->hasTypeConstraint(), NumExpanded); + Inst->setAccess(AS_public); Inst->setImplicit(D->isImplicit()); - + if (auto *TC = D->getTypeConstraint()) { + // TODO: Concepts: do not instantiate the constraint (delayed constraint + // substitution) + const ASTTemplateArgumentListInfo *TemplArgInfo + = TC->getTemplateArgsAsWritten(); + TemplateArgumentListInfo InstArgs; + + if (TemplArgInfo) { + InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc); + InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc); + if (SemaRef.Subst(TemplArgInfo->getTemplateArgs(), + TemplArgInfo->NumTemplateArgs, + InstArgs, TemplateArgs)) + return nullptr; + } + if (SemaRef.AttachTypeConstraint( + TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(), + TC->getNamedConcept(), &InstArgs, Inst, + D->isParameterPack() + ? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint()) + ->getEllipsisLoc() + : SourceLocation())) + return nullptr; + } if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { TypeSourceInfo *InstantiatedDefaultArg = SemaRef.SubstType(D->getDefaultArgumentInfo(), TemplateArgs, diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index d77542be53f..3884fdae8fe 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -3016,7 +3016,8 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, SemaRef.Context, SemaRef.Context.getTranslationUnitDecl(), /*KeyLoc*/ SourceLocation(), /*NameLoc*/ D.getBeginLoc(), TemplateParameterDepth, AutoParameterPosition, - /*Identifier*/ nullptr, false, IsParameterPack); + /*Identifier*/ nullptr, false, IsParameterPack, + /*HasTypeConstraint=*/false); CorrespondingTemplateParam->setImplicit(); LSI->TemplateParams.push_back(CorrespondingTemplateParam); // Replace the 'auto' in the function parameter with this invented diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 1d775dab67b..3b827fbc950 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3064,13 +3064,13 @@ public: /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. ExprResult RebuildConceptSpecializationExpr(NestedNameSpecifierLoc NNS, - SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc, + SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, ConceptDecl *NamedConcept, TemplateArgumentListInfo *TALI) { CXXScopeSpec SS; SS.Adopt(NNS); ExprResult Result = getSema().CheckConceptTemplateId(SS, TemplateKWLoc, - ConceptNameLoc, + ConceptNameInfo, FoundDecl, NamedConcept, TALI); if (Result.isInvalid()) @@ -11175,7 +11175,7 @@ TreeTransform<Derived>::TransformConceptSpecializationExpr( return getDerived().RebuildConceptSpecializationExpr( E->getNestedNameSpecifierLoc(), E->getTemplateKWLoc(), - E->getConceptNameLoc(), E->getFoundDecl(), E->getNamedConcept(), + E->getConceptNameInfo(), E->getFoundDecl(), E->getNamedConcept(), &TransArgs); } diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 3bcc9ad5805..96a7d5ae0a3 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2313,7 +2313,20 @@ void ASTDeclReader::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { D->setDeclaredWithTypename(Record.readInt()); - // TODO: Concepts: Immediately introduced constraint + if (Record.readInt()) { + NestedNameSpecifierLoc NNS = Record.readNestedNameSpecifierLoc(); + DeclarationNameInfo DN = Record.readDeclarationNameInfo(); + ConceptDecl *NamedConcept = cast<ConceptDecl>(Record.readDecl()); + const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; + if (Record.readInt()) + ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); + Expr *ImmediatelyDeclaredConstraint = Record.readExpr(); + D->setTypeConstraint(NNS, DN, /*FoundDecl=*/nullptr, NamedConcept, + ArgsAsWritten, ImmediatelyDeclaredConstraint); + if ((D->ExpandedParameterPack = Record.readInt())) + D->NumExpanded = Record.readInt(); + } + if (Record.readInt()) D->setDefaultArgument(readTypeSourceInfo()); } @@ -3800,9 +3813,12 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_FUNCTION_TEMPLATE: D = FunctionTemplateDecl::CreateDeserialized(Context, ID); break; - case DECL_TEMPLATE_TYPE_PARM: - D = TemplateTypeParmDecl::CreateDeserialized(Context, ID); + case DECL_TEMPLATE_TYPE_PARM: { + bool HasTypeConstraint = Record.readInt(); + D = TemplateTypeParmDecl::CreateDeserialized(Context, ID, + HasTypeConstraint); break; + } case DECL_NON_TYPE_TEMPLATE_PARM: D = NonTypeTemplateParmDecl::CreateDeserialized(Context, ID); break; diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 533502116ed..f558c26b5f1 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -730,15 +730,13 @@ void ASTStmtReader::VisitConceptSpecializationExpr( unsigned NumTemplateArgs = Record.readInt(); E->NestedNameSpec = Record.readNestedNameSpecifierLoc(); E->TemplateKWLoc = Record.readSourceLocation(); - E->ConceptNameLoc = Record.readSourceLocation(); - E->FoundDecl = readDeclAs<NamedDecl>(); + E->ConceptName = Record.readDeclarationNameInfo(); E->NamedConcept = readDeclAs<ConceptDecl>(); - const ASTTemplateArgumentListInfo *ArgsAsWritten = - Record.readASTTemplateArgumentListInfo(); + E->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); llvm::SmallVector<TemplateArgument, 4> Args; for (unsigned I = 0; I < NumTemplateArgs; ++I) Args.push_back(Record.readTemplateArgument()); - E->setTemplateArguments(ArgsAsWritten, Args); + E->setTemplateArguments(Args); ConstraintSatisfaction Satisfaction; Satisfaction.IsSatisfied = Record.readInt(); if (!Satisfaction.IsSatisfied) { diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 6b8a9d9719d..b2a8c118d40 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1637,10 +1637,26 @@ void ASTDeclWriter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { } void ASTDeclWriter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { + Record.push_back(D->hasTypeConstraint()); VisitTypeDecl(D); Record.push_back(D->wasDeclaredWithTypename()); - // TODO: Concepts - constrained parameters. + + const TypeConstraint *TC = D->getTypeConstraint(); + Record.push_back(TC != nullptr); + 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()); + Record.push_back(D->isExpandedParameterPack()); + if (D->isExpandedParameterPack()) + Record.push_back(D->getNumExpansionParameters()); + } + bool OwnsDefaultArg = D->hasDefaultArgument() && !D->defaultArgumentWasInherited(); Record.push_back(OwnsDefaultArg); @@ -1670,7 +1686,6 @@ void ASTDeclWriter::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { Code = serialization::DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK; } else { - // TODO: Concepts - constrained parameters. // Rest of NonTypeTemplateParmDecl. Record.push_back(D->isParameterPack()); bool OwnsDefaultArg = D->hasDefaultArgument() && @@ -1700,7 +1715,6 @@ void ASTDeclWriter::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { Record.AddTemplateParameterList(D->getExpansionTemplateParameters(I)); Code = serialization::DECL_EXPANDED_TEMPLATE_TEMPLATE_PARM_PACK; } else { - // TODO: Concepts - constrained parameters. // Rest of TemplateTemplateParmDecl. Record.push_back(D->isParameterPack()); bool OwnsDefaultArg = D->hasDefaultArgument() && diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index e671c78fe97..9231f3b2b9b 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -395,8 +395,7 @@ void ASTStmtWriter::VisitConceptSpecializationExpr( Record.push_back(TemplateArgs.size()); Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc()); Record.AddSourceLocation(E->getTemplateKWLoc()); - Record.AddSourceLocation(E->getConceptNameLoc()); - Record.AddDeclRef(E->getFoundDecl()); + Record.AddDeclarationNameInfo(E->getConceptNameInfo()); Record.AddDeclRef(E->getNamedConcept()); Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten()); for (const TemplateArgument &Arg : TemplateArgs) diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp new file mode 100644 index 00000000000..593336163fa --- /dev/null +++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -frelaxed-template-template-args -verify %s + +template<typename T> concept C = T::f(); +// expected-note@-1{{similar constraint}} +template<typename T> concept D = C<T> && T::g(); +template<typename T> concept F = T::f(); +// expected-note@-1{{similar constraint expressions not considered equivalent}} +template<template<C> class P> struct S1 { }; // expected-note 2{{'P' declared here}} + +template<C> struct X { }; // expected-note{{'X' declared here}} + +template<D> struct Y { }; // expected-note 2{{'Y' declared here}} +template<typename T> struct Z { }; +template<F> struct W { }; // expected-note{{'W' declared here}} + +S1<X> s11; +S1<Y> s12; // expected-error{{template template argument 'Y' is more constrained than template template parameter 'P'}} +S1<Z> s13; +S1<W> s14; // expected-error{{template template argument 'W' is more constrained than template template parameter 'P'}} + +template<template<typename> class P> struct S2 { }; // expected-note 2{{'P' declared here}} + +S2<X> s21; // expected-error{{template template argument 'X' is more constrained than template template parameter 'P'}} +S2<Y> s22; // expected-error{{template template argument 'Y' is more constrained than template template parameter 'P'}} +S2<Z> s23; + +template <template <typename...> class C> +struct S3; + +template <C T> +using N = typename T::type; + +using s31 = S3<N>; +using s32 = S3<Z>; diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp index 6f7b80e26a6..2a1a4e1b7ff 100644 --- a/clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++2a -x c++ -verify %s +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s namespace nodiag { @@ -7,6 +7,14 @@ struct A; template <typename U> requires (bool(U())) struct A; +template<typename T> +concept C1 = true; + +template <C1 T> requires (bool(T())) +struct B; +template <C1 U> requires (bool(U())) +struct B; + } // end namespace nodiag namespace diag { @@ -24,6 +32,14 @@ struct C; template <typename T> requires (!0) // expected-error{{requires clause differs in template redeclaration}} struct C; +template<typename T> +concept C1 = true; + +template <C1 T> // expected-note{{previous template declaration is here}} +struct D; +template <typename T> requires C1<T> // expected-error{{type constraint differs in template redeclaration}} +struct D; + } // end namespace diag namespace nodiag { diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.decl/p3.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.decl/p3.cpp new file mode 100644 index 00000000000..c6fe82d8de4 --- /dev/null +++ b/clang/test/CXX/temp/temp.constr/temp.constr.decl/p3.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +template<typename T> +struct X { + using Y = typename T::invalid; +}; + +template<typename T> +concept Invalid = X<T>{}; + +template<typename T> +concept False = false; // expected-note{{because 'false' evaluated to false}} + +template<typename T> +concept True = true; + +// TODO: Concepts: Uncomment trailing requires clauses here when we have correct substitution. +//template<True T> +// requires False<T> +//void g1() requires Invalid<T>; +// +//using g1i = decltype(g1<int>()); + +template<False T> // expected-note{{because 'int' does not satisfy 'False'}} + requires Invalid<T> +void g2(); // requires Invalid<T>; +// expected-note@-1{{candidate template ignored: constraints not satisfied [with T = int]}} + +using g2i = decltype(g2<int>()); +// expected-error@-1{{no matching function for call to 'g2'}}
\ No newline at end of file diff --git a/clang/test/CXX/temp/temp.param/p10-2a.cpp b/clang/test/CXX/temp/temp.param/p10-2a.cpp new file mode 100644 index 00000000000..de40f8cca5d --- /dev/null +++ b/clang/test/CXX/temp/temp.param/p10-2a.cpp @@ -0,0 +1,117 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s + +template<typename T> +concept C1 = sizeof(T) == 1; +// expected-note@-1 2{{because 'sizeof(short) == 1' (2 == 1) evaluated to false}} +// expected-note@-2 {{because 'sizeof(int) == 1' (4 == 1) evaluated to false}} + +template<C1 T> // expected-note {{because 'int' does not satisfy 'C1'}} +using A = T; + +using a1 = A<int>; // expected-error {{constraints not satisfied for alias template 'A' [with T = int]}} +using a2 = A<char>; + +template<typename T> +concept C2 = sizeof(T) == 2; +// expected-note@-1 {{because 'sizeof(char) == 2' (1 == 2) evaluated to false}} + +template<C1 T1, C2 T2> +// expected-note@-1 2{{because 'short' does not satisfy 'C1'}} +// expected-note@-2 {{because 'char' does not satisfy 'C2'}} +using B = T1; + +using b1 = B<char, short>; +using b2 = B<char, char>; // expected-error {{constraints not satisfied for alias template 'B' [with T1 = char, T2 = char]}} +using b3 = B<short, short>; // expected-error {{constraints not satisfied for alias template 'B' [with T1 = short, T2 = short]}} +using b4 = B<short, char>; // expected-error {{constraints not satisfied for alias template 'B' [with T1 = short, T2 = char]}} + +template<typename... T> +concept C3 = (sizeof(T) + ...) == 12; +// expected-note@-1 {{because 'sizeof(char [11]) == 12' (11 == 12) evaluated to false}} +// expected-note@-2 {{because 'sizeof(char [10]) == 12' (10 == 12) evaluated to false}} +// expected-note@-3 3{{because 'sizeof(int) == 12' (4 == 12) evaluated to false}} +// expected-note@-4 6{{because 'sizeof(short) == 12' (2 == 12) evaluated to false}} + +template<C3 T1, C3 T2, C3 T3> +// expected-note@-1 {{because 'char [11]' does not satisfy 'C3'}} +// expected-note@-2 {{because 'char [10]' does not satisfy 'C3'}} +using C = T2; + +using c1 = C<char[12], int[3], short[6]>; +using c2 = C<char[12], char[11], char[10]>; +// expected-error@-1 {{constraints not satisfied for alias template 'C' [with T1 = char [12], T2 = char [11], T3 = char [10]]}} +using c3 = C<char[12], char[12], char[10]>; +// expected-error@-1 {{constraints not satisfied for alias template 'C' [with T1 = char [12], T2 = char [12], T3 = char [10]]}} + +template<C3... Ts> +// expected-note@-1 {{because 'int' does not satisfy 'C3'}} +// expected-note@-2 2{{and 'int' does not satisfy 'C3'}} +// expected-note@-3 {{because 'short' does not satisfy 'C3'}} +// expected-note@-4 5{{and 'short' does not satisfy 'C3'}} +using D = int; + +using d1 = D<char[12], int[3], short[6]>; +using d2 = D<int, int, int>; +// expected-error@-1 {{constraints not satisfied for alias template 'D' [with Ts = <int, int, int>}} +using d3 = D<short, short, short, short, short, short>; +// expected-error@-1 {{constraints not satisfied for alias template 'D' [with Ts = <short, short, short, short, short, short>}} + +template<typename T> +concept C4 = sizeof(T) == 4; +// expected-note@-1 3{{because 'sizeof(char) == 4' (1 == 4) evaluated to false}} + +template<C4... Ts> +// expected-note@-1 2{{because 'char' does not satisfy 'C4'}} +// expected-note@-2 {{and 'char' does not satisfy 'C4'}} +using E = int; + +using e1 = E<int>; +using e2 = E<char, int>; // expected-error {{constraints not satisfied for alias template 'E' [with Ts = <char, int>]}} +using e3 = E<char, char>; // expected-error {{constraints not satisfied for alias template 'E' [with Ts = <char, char>]}} +using e4 = E<>; + +template<typename T, typename U> +constexpr bool is_same_v = false; + +template<typename T> +constexpr bool is_same_v<T, T> = true; + +template<typename T, typename U> +concept Same = is_same_v<T, U>; // expected-note {{because 'is_same_v<long, int>' evaluated to false}} + +template<Same<int> T> // expected-note {{because 'Same<long, int>' evaluated to false}} +using F = T; + +using f1 = F<int>; +using f2 = F<long>; // expected-error {{constraints not satisfied for alias template 'F' [with T = long]}} + +template<typename T, typename... Ts> +concept OneOf = (is_same_v<T, Ts> || ...); +// expected-note@-1 2{{because 'is_same_v<char, char [1]>' evaluated to false}} +// expected-note@-2 2{{and 'is_same_v<char, char [2]>' evaluated to false}} +// expected-note@-3 {{because 'is_same_v<short, int>' evaluated to false}} +// expected-note@-4 {{and 'is_same_v<short, long>' evaluated to false}} +// expected-note@-5 {{and 'is_same_v<short, char>' evaluated to false}} +// expected-note@-6 3{{because 'is_same_v<int, char [1]>' evaluated to false}} +// expected-note@-7 3{{and 'is_same_v<int, char [2]>' evaluated to false}} + +template<OneOf<char[1], char[2]> T, OneOf<int, long, char> U> +// expected-note@-1 2{{because 'OneOf<char, char [1], char [2]>' evaluated to false}} +// expected-note@-2 {{because 'OneOf<short, int, long, char>' evaluated to false}} +using G = T; + +using g1 = G<char[1], int>; +using g2 = G<char, int>; // expected-error{{constraints not satisfied for alias template 'G' [with T = char, U = int]}} +using g3 = G<char[1], short>; // expected-error{{constraints not satisfied for alias template 'G' [with T = char [1], U = short]}} +using g4 = G<char, short>; // expected-error{{constraints not satisfied for alias template 'G' [with T = char, U = short]}} + +template<OneOf<char[1], char[2]>... Ts> +// expected-note@-1 2{{because 'OneOf<int, char [1], char [2]>' evaluated to false}} +// expected-note@-2 {{and 'OneOf<int, char [1], char [2]>' evaluated to false}} +using H = int; + +using h1 = H<char[1], int>; +// expected-error@-1 {{constraints not satisfied for alias template 'H' [with Ts = <char [1], int>]}} +using h2 = H<int, int>; +// expected-error@-1 {{constraints not satisfied for alias template 'H' [with Ts = <int, int>]}} +using h3 = H<char[1], char[2]>;
\ No newline at end of file diff --git a/clang/test/Parser/cxx2a-constrained-template-param-with-partial-id.cpp b/clang/test/Parser/cxx2a-constrained-template-param-with-partial-id.cpp new file mode 100644 index 00000000000..2d80475e1eb --- /dev/null +++ b/clang/test/Parser/cxx2a-constrained-template-param-with-partial-id.cpp @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +template<typename T, int a> +concept C1 = true; + +template<C1 T> +// expected-error@-1 {{'C1' requires more than 1 template argument; provide the remaining arguments explicitly to use it here}} +using badA = T[10]; + +template<C1<0> T> +using A = T[10]; + +using a = A<int>; + +namespace ns { + template<typename T, typename U, typename... X> + concept C2 = true; +} + +template<ns::C2 T1, ::ns::C2 T2> +// expected-error@-1 2{{'C2' requires more than 1 template argument; provide the remaining arguments explicitly to use it here}} +requires (sizeof(T1) <= sizeof(T2)) +struct badB { }; + +template<ns::C2<int> T1, ::ns::C2<char, T1> T2> + requires (sizeof(T1) <= sizeof(T2)) +struct B { }; + +using b = B<int, int>; + +template<ns::C2... T1> +// expected-error@-1 {{'C2' requires more than 1 template argument; provide the remaining arguments explicitly to use it here}} +struct badC { }; + +template<ns::C2<int>... T1> +struct C { }; + +using c1 = C<char, char, char>; +using c2 = C<char, char, char, char>;
\ No newline at end of file diff --git a/clang/test/Parser/cxx2a-constrained-template-param.cpp b/clang/test/Parser/cxx2a-constrained-template-param.cpp new file mode 100644 index 00000000000..2ee544e970b --- /dev/null +++ b/clang/test/Parser/cxx2a-constrained-template-param.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +namespace type +{ + template<typename T> + concept C1 = true; + + template<C1 T, C1 U = int> + using A = T[10]; + + using a = A<int>; + + namespace ns { + template<typename T, int a = 0> + concept C2 = true; + } + + template<ns::C2 T1, ::type::ns::C2 T2> requires (sizeof(T1) <= sizeof(T2)) + struct B { }; + + using b = B<int, int>; + + template<ns::C2... T1> + struct C { }; + + using c1 = C<char, char, char>; + using c2 = C<char, char, char, char>; +} + +namespace non_type +{ + template<int v> + concept C1 = true; + + template<C1 v, C1 u = 0> // expected-error{{expected a type}} // expected-note{{declared here}} + // expected-error@-1 2{{concept named in type constraint is not a type concept}} + // expected-error@-2 {{expected ',' or '>' in template-parameter-list}} + int A = v; // expected-error{{'v' does not refer to a value}} +} + +namespace temp +{ + template<typename> + struct test1 { }; // expected-note{{template is declared here}} + + template<template<typename> typename T> + concept C1 = true; + + template<C1 TT, C1 UU = test1> // expected-error{{use of class template 'test1' requires template arguments}} + // expected-error@-1 2{{concept named in type constraint is not a type concept}} + using A = TT<int>; // expected-error{{expected ';' after alias declaration}} +}
\ No newline at end of file diff --git a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp new file mode 100644 index 00000000000..4daf31a292c --- /dev/null +++ b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +template<typename T, typename U> +constexpr bool is_same_v = false; + +template<typename T> +constexpr bool is_same_v<T, T> = true; + +template<typename T, typename U> +concept same_as = is_same_v<T, U>; +// expected-note@-1{{because 'is_same_v<int, _Bool>' evaluated to false}} + +template<typename T, typename... Us> +concept either = (is_same_v<T, Us> || ...); + +template<typename... Ts> +struct T { + template<same_as<Ts>... Us> + // expected-note@-1{{because 'same_as<int, _Bool>' evaluated to false}} + static void foo(Us... u, int x) { }; + // expected-note@-1{{candidate template ignored: deduced too few arguments}} + // expected-note@-2{{candidate template ignored: constraints not satisfied}} + + template<typename... Us> + struct S { + template<either<Ts, Us...>... Vs> + static void foo(Vs... v); + }; +}; + +int main() { + T<int, bool>::foo(1); // expected-error{{no matching function for call to 'foo'}} + T<int, bool>::foo(1, 2, 3); // expected-error{{no matching function for call to 'foo'}} + T<int, bool>::S<char>::foo(1, 'a'); + T<int, bool>::S<char>::foo('a', true); +} diff --git a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp index 0c054694247..beb64f8273d 100644 --- a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp +++ b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp @@ -94,12 +94,11 @@ namespace test_undeclared_nontype_parm_arg { template <typename T> struct Bar { T x; }; template <Bar<Xylophone> *P> // expected-error {{use of undeclared identifier 'Xylophone'}} -// expected-note@-1 {{template parameter is declared here}} struct Foo { }; typedef int Xylophone; Bar<Xylophone> g; -template struct Foo<&g>; // expected-error {{cannot be converted}} +template struct Foo<&g>; // expected-error {{value of type}} } #endif diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 94084e169fa..6f3490471de 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -757,6 +757,11 @@ bool CursorVisitor::VisitClassTemplatePartialSpecializationDecl( } bool CursorVisitor::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { + if (const auto *TC = D->getTypeConstraint()) + if (Visit(MakeCXCursor(TC->getImmediatelyDeclaredConstraint(), StmtParent, + TU, RegionOfInterest))) + return true; + // Visit the default argument. if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) if (TypeSourceInfo *DefArg = D->getDefaultArgumentInfo()) @@ -5050,7 +5055,12 @@ CXString clang_getCursorDisplayName(CXCursor C) { // 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<TemplateTypeParmDecl>(Param)) - OS << (TTP->wasDeclaredWithTypename()? "typename" : "class"); + 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<NonTypeTemplateParmDecl>(Param)) OS << NTTP->getType().getAsString(Policy); |

