Commit ff1e0fce authored by Saar Raz's avatar Saar Raz
Browse files

[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
parent 8e780252
Loading
Loading
Loading
Loading
+97 −0
Original line number Diff line number Diff line
@@ -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
+6 −2
Original line number Diff line number Diff line
@@ -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 *
+6 −0
Original line number Diff line number Diff line
@@ -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(),
+166 −16
Original line number Diff line number Diff line
@@ -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; }
+11 −53
Original line number Diff line number Diff line
@@ -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;
  }
Loading