diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h --- a/clang/include/clang/AST/ASTConcept.h +++ b/clang/include/clang/AST/ASTConcept.h @@ -21,6 +21,7 @@ #include #include 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 @@ 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 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -283,12 +283,16 @@ 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 + mutable llvm::ContextualFoldingSet CanonTemplateTemplateParms; TemplateTemplateParmDecl * diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -537,6 +537,10 @@ } 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 @@ } 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 --- 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 TemplateDecl; class TemplateTemplateParmDecl; class TemplateTypeParmDecl; +class ConceptDecl; class UnresolvedSetImpl; class VarTemplateDecl; class VarTemplatePartialSpecializationDecl; @@ -81,20 +83,24 @@ /// 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 Params, SourceLocation RAngleLoc, - Expr *RequiresClause); + TemplateParameterList(const ASTContext& C, SourceLocation TemplateLoc, + SourceLocation LAngleLoc, ArrayRef Params, + SourceLocation RAngleLoc, Expr *RequiresClause); size_t numTrailingObjects(OverloadToken) const { return NumParams; } size_t numTrailingObjects(OverloadToken) const { - return HasRequiresClause; + return HasRequiresClause ? 1 : 0; } public: @@ -158,14 +164,22 @@ 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() : nullptr; + return HasRequiresClause ? getTrailingObjects()[0] : nullptr; } /// The constraint-expression of the associated requires-clause. const Expr *getRequiresClause() const { - return HasRequiresClause ? *getTrailingObjects() : nullptr; + return HasRequiresClause ? getTrailingObjects()[0] : nullptr; } /// \brief All associated constraints derived from this template parameter @@ -208,15 +222,16 @@ >::type storage; public: - FixedSizeTemplateParameterListStorage(SourceLocation TemplateLoc, + FixedSizeTemplateParameterListStorage(const ASTContext &C, + SourceLocation TemplateLoc, SourceLocation LAngleLoc, ArrayRef Params, SourceLocation RAngleLoc, Expr *RequiresClause) : FixedSizeStorageOwner( (assert(N == Params.size()), - assert(HasRequiresClause == static_cast(RequiresClause)), - new (static_cast(&storage)) TemplateParameterList( + assert(HasRequiresClause == (RequiresClause != nullptr)), + new (static_cast(&storage)) TemplateParameterList(C, TemplateLoc, LAngleLoc, Params, RAngleLoc, RequiresClause))) {} }; @@ -1148,9 +1163,12 @@ /// \code /// template class vector; /// \endcode -class TemplateTypeParmDecl : public TypeDecl { +class TemplateTypeParmDecl final : public TypeDecl, + private llvm::TrailingObjects { /// 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 @@ /// 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; @@ -1165,8 +1199,12 @@ TemplateTypeParmDecl(DeclContext *DC, SourceLocation KeyLoc, SourceLocation IdLoc, IdentifierInfo *Id, - bool Typename) - : TypeDecl(TemplateTypeParm, DC, IdLoc, Id, KeyLoc), Typename(Typename) {} + bool Typename, bool HasTypeConstraint, + Optional 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 @@ SourceLocation NameLoc, unsigned D, unsigned P, IdentifierInfo *Id, bool Typename, - bool ParameterPack); + bool ParameterPack, + bool HasTypeConstraint = false, + Optional 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 @@ /// 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 + /// struct X { + /// template ...Convertibles> + /// struct Y { /* ... */ }; + /// }; + /// \endcode + /// + /// The parameter pack \c Convertibles has (convertible_to && ...) 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 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() : + 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 &AC) const { + if (HasTypeConstraint) + AC.push_back(getTypeConstraint()->getImmediatelyDeclaredConstraint()); + } + SourceRange getSourceRange() const override LLVM_READONLY; // Implement isa/cast/dyncast/etc. @@ -1424,6 +1543,33 @@ 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 &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 @@ ConstraintExpr->getEndLoc()); } + bool isTypeConcept() const { + return isa(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 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4841,7 +4841,7 @@ /// /// 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 { friend class ASTStmtReader; @@ -4850,30 +4850,6 @@ using SubstitutionDiagnostic = std::pair; 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 @@ /// 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 ConvertedArgs, const ConstraintSatisfaction *Satisfaction); @@ -4896,8 +4872,8 @@ 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 ConvertedArgs, @@ -4906,30 +4882,13 @@ 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 getTemplateArguments() const { return ArrayRef(getTrailingObjects(), NumTemplateArgs); } - const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const { - return ArgsAsWritten; - } - /// \brief Set new template arguments for this concept specialization. - void setTemplateArguments(const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef Converted); + void setTemplateArguments(ArrayRef Converted); /// \brief Whether or not the concept with the given arguments was satisfied /// when the expression was created. @@ -4949,15 +4908,14 @@ 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 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -304,6 +304,11 @@ 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 @@ // D is the "T" in something like // template