Home | History | Annotate | Line # | Download | only in AST
      1 //===- ExprConcepts.h - C++2a Concepts expressions --------------*- C++ -*-===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 /// \file
     10 /// Defines Expressions and AST nodes for C++2a concepts.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #ifndef LLVM_CLANG_AST_EXPRCONCEPTS_H
     15 #define LLVM_CLANG_AST_EXPRCONCEPTS_H
     16 
     17 #include "clang/AST/ASTContext.h"
     18 #include "clang/AST/ASTConcept.h"
     19 #include "clang/AST/Decl.h"
     20 #include "clang/AST/DeclarationName.h"
     21 #include "clang/AST/DeclTemplate.h"
     22 #include "clang/AST/Expr.h"
     23 #include "clang/AST/NestedNameSpecifier.h"
     24 #include "clang/AST/TemplateBase.h"
     25 #include "clang/AST/Type.h"
     26 #include "clang/Basic/SourceLocation.h"
     27 #include "llvm/Support/TrailingObjects.h"
     28 #include <utility>
     29 #include <string>
     30 
     31 namespace clang {
     32 class ASTStmtReader;
     33 class ASTStmtWriter;
     34 
     35 /// \brief Represents the specialization of a concept - evaluates to a prvalue
     36 /// of type bool.
     37 ///
     38 /// According to C++2a [expr.prim.id]p3 an id-expression that denotes the
     39 /// specialization of a concept results in a prvalue of type bool.
     40 class ConceptSpecializationExpr final : public Expr, public ConceptReference,
     41       private llvm::TrailingObjects<ConceptSpecializationExpr,
     42                                     TemplateArgument> {
     43   friend class ASTStmtReader;
     44   friend TrailingObjects;
     45 public:
     46   using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
     47 
     48 protected:
     49   /// \brief The number of template arguments in the tail-allocated list of
     50   /// converted template arguments.
     51   unsigned NumTemplateArgs;
     52 
     53   /// \brief Information about the satisfaction of the named concept with the
     54   /// given arguments. If this expression is value dependent, this is to be
     55   /// ignored.
     56   ASTConstraintSatisfaction *Satisfaction;
     57 
     58   ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS,
     59                             SourceLocation TemplateKWLoc,
     60                             DeclarationNameInfo ConceptNameInfo,
     61                             NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
     62                             const ASTTemplateArgumentListInfo *ArgsAsWritten,
     63                             ArrayRef<TemplateArgument> ConvertedArgs,
     64                             const ConstraintSatisfaction *Satisfaction);
     65 
     66   ConceptSpecializationExpr(const ASTContext &C, ConceptDecl *NamedConcept,
     67                             ArrayRef<TemplateArgument> ConvertedArgs,
     68                             const ConstraintSatisfaction *Satisfaction,
     69                             bool Dependent,
     70                             bool ContainsUnexpandedParameterPack);
     71 
     72   ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);
     73 
     74 public:
     75 
     76   static ConceptSpecializationExpr *
     77   Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
     78          SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
     79          NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
     80          const ASTTemplateArgumentListInfo *ArgsAsWritten,
     81          ArrayRef<TemplateArgument> ConvertedArgs,
     82          const ConstraintSatisfaction *Satisfaction);
     83 
     84   static ConceptSpecializationExpr *
     85   Create(const ASTContext &C, ConceptDecl *NamedConcept,
     86          ArrayRef<TemplateArgument> ConvertedArgs,
     87          const ConstraintSatisfaction *Satisfaction,
     88          bool Dependent,
     89          bool ContainsUnexpandedParameterPack);
     90 
     91   static ConceptSpecializationExpr *
     92   Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
     93 
     94   ArrayRef<TemplateArgument> getTemplateArguments() const {
     95     return ArrayRef<TemplateArgument>(getTrailingObjects<TemplateArgument>(),
     96                                       NumTemplateArgs);
     97   }
     98 
     99   /// \brief Set new template arguments for this concept specialization.
    100   void setTemplateArguments(ArrayRef<TemplateArgument> Converted);
    101 
    102   /// \brief Whether or not the concept with the given arguments was satisfied
    103   /// when the expression was created.
    104   /// The expression must not be dependent.
    105   bool isSatisfied() const {
    106     assert(!isValueDependent()
    107            && "isSatisfied called on a dependent ConceptSpecializationExpr");
    108     return Satisfaction->IsSatisfied;
    109   }
    110 
    111   /// \brief Get elaborated satisfaction info about the template arguments'
    112   /// satisfaction of the named concept.
    113   /// The expression must not be dependent.
    114   const ASTConstraintSatisfaction &getSatisfaction() const {
    115     assert(!isValueDependent()
    116            && "getSatisfaction called on dependent ConceptSpecializationExpr");
    117     return *Satisfaction;
    118   }
    119 
    120   static bool classof(const Stmt *T) {
    121     return T->getStmtClass() == ConceptSpecializationExprClass;
    122   }
    123 
    124   SourceLocation getBeginLoc() const LLVM_READONLY {
    125     return ConceptName.getBeginLoc();
    126   }
    127 
    128   SourceLocation getEndLoc() const LLVM_READONLY {
    129     // If the ConceptSpecializationExpr is the ImmediatelyDeclaredConstraint
    130     // of a TypeConstraint written syntactically as a constrained-parameter,
    131     // there may not be a template argument list.
    132     return ArgsAsWritten->RAngleLoc.isValid() ? ArgsAsWritten->RAngleLoc
    133                                               : ConceptName.getEndLoc();
    134   }
    135 
    136   // Iterators
    137   child_range children() {
    138     return child_range(child_iterator(), child_iterator());
    139   }
    140   const_child_range children() const {
    141     return const_child_range(const_child_iterator(), const_child_iterator());
    142   }
    143 };
    144 
    145 namespace concepts {
    146 
    147 /// \brief A static requirement that can be used in a requires-expression to
    148 /// check properties of types and expression.
    149 class Requirement {
    150 public:
    151   // Note - simple and compound requirements are both represented by the same
    152   // class (ExprRequirement).
    153   enum RequirementKind { RK_Type, RK_Simple, RK_Compound, RK_Nested };
    154 private:
    155   const RequirementKind Kind;
    156   // FIXME: use RequirementDependence to model dependence?
    157   bool Dependent : 1;
    158   bool ContainsUnexpandedParameterPack : 1;
    159   bool Satisfied : 1;
    160 public:
    161   struct SubstitutionDiagnostic {
    162     StringRef SubstitutedEntity;
    163     // FIXME: Store diagnostics semantically and not as prerendered strings.
    164     //  Fixing this probably requires serialization of PartialDiagnostic
    165     //  objects.
    166     SourceLocation DiagLoc;
    167     StringRef DiagMessage;
    168   };
    169 
    170   Requirement(RequirementKind Kind, bool IsDependent,
    171               bool ContainsUnexpandedParameterPack, bool IsSatisfied = true) :
    172       Kind(Kind), Dependent(IsDependent),
    173       ContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack),
    174       Satisfied(IsSatisfied) {}
    175 
    176   RequirementKind getKind() const { return Kind; }
    177 
    178   bool isSatisfied() const {
    179     assert(!Dependent &&
    180            "isSatisfied can only be called on non-dependent requirements.");
    181     return Satisfied;
    182   }
    183 
    184   void setSatisfied(bool IsSatisfied) {
    185     assert(!Dependent &&
    186            "setSatisfied can only be called on non-dependent requirements.");
    187     Satisfied = IsSatisfied;
    188   }
    189 
    190   void setDependent(bool IsDependent) { Dependent = IsDependent; }
    191   bool isDependent() const { return Dependent; }
    192 
    193   void setContainsUnexpandedParameterPack(bool Contains) {
    194     ContainsUnexpandedParameterPack = Contains;
    195   }
    196   bool containsUnexpandedParameterPack() const {
    197     return ContainsUnexpandedParameterPack;
    198   }
    199 };
    200 
    201 /// \brief A requires-expression requirement which queries the existence of a
    202 /// type name or type template specialization ('type' requirements).
    203 class TypeRequirement : public Requirement {
    204 public:
    205   enum SatisfactionStatus {
    206       SS_Dependent,
    207       SS_SubstitutionFailure,
    208       SS_Satisfied
    209   };
    210 private:
    211   llvm::PointerUnion<SubstitutionDiagnostic *, TypeSourceInfo *> Value;
    212   SatisfactionStatus Status;
    213 public:
    214   friend ASTStmtReader;
    215   friend ASTStmtWriter;
    216 
    217   /// \brief Construct a type requirement from a type. If the given type is not
    218   /// dependent, this indicates that the type exists and the requirement will be
    219   /// satisfied. Otherwise, the SubstitutionDiagnostic constructor is to be
    220   /// used.
    221   TypeRequirement(TypeSourceInfo *T);
    222 
    223   /// \brief Construct a type requirement when the nested name specifier is
    224   /// invalid due to a bad substitution. The requirement is unsatisfied.
    225   TypeRequirement(SubstitutionDiagnostic *Diagnostic) :
    226       Requirement(RK_Type, false, false, false), Value(Diagnostic),
    227       Status(SS_SubstitutionFailure) {}
    228 
    229   SatisfactionStatus getSatisfactionStatus() const { return Status; }
    230   void setSatisfactionStatus(SatisfactionStatus Status) {
    231     this->Status = Status;
    232   }
    233 
    234   bool isSubstitutionFailure() const {
    235     return Status == SS_SubstitutionFailure;
    236   }
    237 
    238   SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
    239     assert(Status == SS_SubstitutionFailure &&
    240            "Attempted to get substitution diagnostic when there has been no "
    241            "substitution failure.");
    242     return Value.get<SubstitutionDiagnostic *>();
    243   }
    244 
    245   TypeSourceInfo *getType() const {
    246     assert(!isSubstitutionFailure() &&
    247            "Attempted to get type when there has been a substitution failure.");
    248     return Value.get<TypeSourceInfo *>();
    249   }
    250 
    251   static bool classof(const Requirement *R) {
    252     return R->getKind() == RK_Type;
    253   }
    254 };
    255 
    256 /// \brief A requires-expression requirement which queries the validity and
    257 /// properties of an expression ('simple' and 'compound' requirements).
    258 class ExprRequirement : public Requirement {
    259 public:
    260   enum SatisfactionStatus {
    261       SS_Dependent,
    262       SS_ExprSubstitutionFailure,
    263       SS_NoexceptNotMet,
    264       SS_TypeRequirementSubstitutionFailure,
    265       SS_ConstraintsNotSatisfied,
    266       SS_Satisfied
    267   };
    268   class ReturnTypeRequirement {
    269       llvm::PointerIntPair<
    270           llvm::PointerUnion<TemplateParameterList *, SubstitutionDiagnostic *>,
    271           1, bool>
    272           TypeConstraintInfo;
    273   public:
    274       friend ASTStmtReader;
    275       friend ASTStmtWriter;
    276 
    277       /// \brief No return type requirement was specified.
    278       ReturnTypeRequirement() : TypeConstraintInfo(nullptr, 0) {}
    279 
    280       /// \brief A return type requirement was specified but it was a
    281       /// substitution failure.
    282       ReturnTypeRequirement(SubstitutionDiagnostic *SubstDiag) :
    283           TypeConstraintInfo(SubstDiag, 0) {}
    284 
    285       /// \brief A 'type constraint' style return type requirement.
    286       /// \param TPL an invented template parameter list containing a single
    287       /// type parameter with a type-constraint.
    288       // TODO: Can we maybe not save the whole template parameter list and just
    289       //  the type constraint? Saving the whole TPL makes it easier to handle in
    290       //  serialization but is less elegant.
    291       ReturnTypeRequirement(TemplateParameterList *TPL);
    292 
    293       bool isDependent() const {
    294         return TypeConstraintInfo.getInt();
    295       }
    296 
    297       bool containsUnexpandedParameterPack() const {
    298         if (!isTypeConstraint())
    299           return false;
    300         return getTypeConstraintTemplateParameterList()
    301                 ->containsUnexpandedParameterPack();
    302       }
    303 
    304       bool isEmpty() const {
    305         return TypeConstraintInfo.getPointer().isNull();
    306       }
    307 
    308       bool isSubstitutionFailure() const {
    309         return !isEmpty() &&
    310             TypeConstraintInfo.getPointer().is<SubstitutionDiagnostic *>();
    311       }
    312 
    313       bool isTypeConstraint() const {
    314         return !isEmpty() &&
    315             TypeConstraintInfo.getPointer().is<TemplateParameterList *>();
    316       }
    317 
    318       SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
    319         assert(isSubstitutionFailure());
    320         return TypeConstraintInfo.getPointer().get<SubstitutionDiagnostic *>();
    321       }
    322 
    323       const TypeConstraint *getTypeConstraint() const;
    324 
    325       TemplateParameterList *getTypeConstraintTemplateParameterList() const {
    326         assert(isTypeConstraint());
    327         return TypeConstraintInfo.getPointer().get<TemplateParameterList *>();
    328       }
    329   };
    330 private:
    331   llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> Value;
    332   SourceLocation NoexceptLoc; // May be empty if noexcept wasn't specified.
    333   ReturnTypeRequirement TypeReq;
    334   ConceptSpecializationExpr *SubstitutedConstraintExpr;
    335   SatisfactionStatus Status;
    336 public:
    337   friend ASTStmtReader;
    338   friend ASTStmtWriter;
    339 
    340   /// \brief Construct a compound requirement.
    341   /// \param E the expression which is checked by this requirement.
    342   /// \param IsSimple whether this was a simple requirement in source.
    343   /// \param NoexceptLoc the location of the noexcept keyword, if it was
    344   /// specified, otherwise an empty location.
    345   /// \param Req the requirement for the type of the checked expression.
    346   /// \param Status the satisfaction status of this requirement.
    347   ExprRequirement(
    348       Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
    349       ReturnTypeRequirement Req, SatisfactionStatus Status,
    350       ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr);
    351 
    352   /// \brief Construct a compound requirement whose expression was a
    353   /// substitution failure. The requirement is not satisfied.
    354   /// \param E the diagnostic emitted while instantiating the original
    355   /// expression.
    356   /// \param IsSimple whether this was a simple requirement in source.
    357   /// \param NoexceptLoc the location of the noexcept keyword, if it was
    358   /// specified, otherwise an empty location.
    359   /// \param Req the requirement for the type of the checked expression (omit
    360   /// if no requirement was specified).
    361   ExprRequirement(SubstitutionDiagnostic *E, bool IsSimple,
    362                   SourceLocation NoexceptLoc, ReturnTypeRequirement Req = {});
    363 
    364   bool isSimple() const { return getKind() == RK_Simple; }
    365   bool isCompound() const { return getKind() == RK_Compound; }
    366 
    367   bool hasNoexceptRequirement() const { return NoexceptLoc.isValid(); }
    368   SourceLocation getNoexceptLoc() const { return NoexceptLoc; }
    369 
    370   SatisfactionStatus getSatisfactionStatus() const { return Status; }
    371 
    372   bool isExprSubstitutionFailure() const {
    373     return Status == SS_ExprSubstitutionFailure;
    374   }
    375 
    376   const ReturnTypeRequirement &getReturnTypeRequirement() const {
    377     return TypeReq;
    378   }
    379 
    380   ConceptSpecializationExpr *
    381   getReturnTypeRequirementSubstitutedConstraintExpr() const {
    382     assert(Status >= SS_TypeRequirementSubstitutionFailure);
    383     return SubstitutedConstraintExpr;
    384   }
    385 
    386   SubstitutionDiagnostic *getExprSubstitutionDiagnostic() const {
    387     assert(isExprSubstitutionFailure() &&
    388            "Attempted to get expression substitution diagnostic when there has "
    389            "been no expression substitution failure");
    390     return Value.get<SubstitutionDiagnostic *>();
    391   }
    392 
    393   Expr *getExpr() const {
    394     assert(!isExprSubstitutionFailure() &&
    395            "ExprRequirement has no expression because there has been a "
    396            "substitution failure.");
    397     return Value.get<Expr *>();
    398   }
    399 
    400   static bool classof(const Requirement *R) {
    401     return R->getKind() == RK_Compound || R->getKind() == RK_Simple;
    402   }
    403 };
    404 
    405 /// \brief A requires-expression requirement which is satisfied when a general
    406 /// constraint expression is satisfied ('nested' requirements).
    407 class NestedRequirement : public Requirement {
    408   llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> Value;
    409   const ASTConstraintSatisfaction *Satisfaction = nullptr;
    410 
    411 public:
    412   friend ASTStmtReader;
    413   friend ASTStmtWriter;
    414 
    415   NestedRequirement(SubstitutionDiagnostic *SubstDiag) :
    416       Requirement(RK_Nested, /*Dependent=*/false,
    417                   /*ContainsUnexpandedParameterPack*/false,
    418                   /*Satisfied=*/false), Value(SubstDiag) {}
    419 
    420   NestedRequirement(Expr *Constraint) :
    421       Requirement(RK_Nested, /*Dependent=*/true,
    422                   Constraint->containsUnexpandedParameterPack()),
    423       Value(Constraint) {
    424     assert(Constraint->isInstantiationDependent() &&
    425            "Nested requirement with non-dependent constraint must be "
    426            "constructed with a ConstraintSatisfaction object");
    427   }
    428 
    429   NestedRequirement(ASTContext &C, Expr *Constraint,
    430                     const ConstraintSatisfaction &Satisfaction) :
    431       Requirement(RK_Nested, Constraint->isInstantiationDependent(),
    432                   Constraint->containsUnexpandedParameterPack(),
    433                   Satisfaction.IsSatisfied),
    434       Value(Constraint),
    435       Satisfaction(ASTConstraintSatisfaction::Create(C, Satisfaction)) {}
    436 
    437   bool isSubstitutionFailure() const {
    438     return Value.is<SubstitutionDiagnostic *>();
    439   }
    440 
    441   SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
    442     assert(isSubstitutionFailure() &&
    443            "getSubstitutionDiagnostic() may not be called when there was no "
    444            "substitution failure.");
    445     return Value.get<SubstitutionDiagnostic *>();
    446   }
    447 
    448   Expr *getConstraintExpr() const {
    449     assert(!isSubstitutionFailure() && "getConstraintExpr() may not be called "
    450                                        "on nested requirements with "
    451                                        "substitution failures.");
    452     return Value.get<Expr *>();
    453   }
    454 
    455   const ASTConstraintSatisfaction &getConstraintSatisfaction() const {
    456     assert(!isSubstitutionFailure() && "getConstraintSatisfaction() may not be "
    457                                        "called on nested requirements with "
    458                                        "substitution failures.");
    459     return *Satisfaction;
    460   }
    461 
    462   static bool classof(const Requirement *R) {
    463     return R->getKind() == RK_Nested;
    464   }
    465 };
    466 
    467 } // namespace concepts
    468 
    469 /// C++2a [expr.prim.req]:
    470 ///     A requires-expression provides a concise way to express requirements on
    471 ///     template arguments. A requirement is one that can be checked by name
    472 ///     lookup (6.4) or by checking properties of types and expressions.
    473 ///     [...]
    474 ///     A requires-expression is a prvalue of type bool [...]
    475 class RequiresExpr final : public Expr,
    476     llvm::TrailingObjects<RequiresExpr, ParmVarDecl *,
    477                           concepts::Requirement *> {
    478   friend TrailingObjects;
    479   friend class ASTStmtReader;
    480 
    481   unsigned NumLocalParameters;
    482   unsigned NumRequirements;
    483   RequiresExprBodyDecl *Body;
    484   SourceLocation RBraceLoc;
    485 
    486   unsigned numTrailingObjects(OverloadToken<ParmVarDecl *>) const {
    487     return NumLocalParameters;
    488   }
    489 
    490   unsigned numTrailingObjects(OverloadToken<concepts::Requirement *>) const {
    491     return NumRequirements;
    492   }
    493 
    494   RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc,
    495                RequiresExprBodyDecl *Body,
    496                ArrayRef<ParmVarDecl *> LocalParameters,
    497                ArrayRef<concepts::Requirement *> Requirements,
    498                SourceLocation RBraceLoc);
    499   RequiresExpr(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters,
    500                unsigned NumRequirements);
    501 
    502 public:
    503   static RequiresExpr *
    504   Create(ASTContext &C, SourceLocation RequiresKWLoc,
    505          RequiresExprBodyDecl *Body, ArrayRef<ParmVarDecl *> LocalParameters,
    506          ArrayRef<concepts::Requirement *> Requirements,
    507          SourceLocation RBraceLoc);
    508   static RequiresExpr *
    509   Create(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters,
    510          unsigned NumRequirements);
    511 
    512   ArrayRef<ParmVarDecl *> getLocalParameters() const {
    513     return {getTrailingObjects<ParmVarDecl *>(), NumLocalParameters};
    514   }
    515 
    516   RequiresExprBodyDecl *getBody() const { return Body; }
    517 
    518   ArrayRef<concepts::Requirement *> getRequirements() const {
    519     return {getTrailingObjects<concepts::Requirement *>(), NumRequirements};
    520   }
    521 
    522   /// \brief Whether or not the requires clause is satisfied.
    523   /// The expression must not be dependent.
    524   bool isSatisfied() const {
    525     assert(!isValueDependent()
    526            && "isSatisfied called on a dependent RequiresExpr");
    527     return RequiresExprBits.IsSatisfied;
    528   }
    529 
    530   SourceLocation getRequiresKWLoc() const {
    531     return RequiresExprBits.RequiresKWLoc;
    532   }
    533 
    534   SourceLocation getRBraceLoc() const { return RBraceLoc; }
    535 
    536   static bool classof(const Stmt *T) {
    537     return T->getStmtClass() == RequiresExprClass;
    538   }
    539 
    540   SourceLocation getBeginLoc() const LLVM_READONLY {
    541     return RequiresExprBits.RequiresKWLoc;
    542   }
    543   SourceLocation getEndLoc() const LLVM_READONLY {
    544     return RBraceLoc;
    545   }
    546 
    547   // Iterators
    548   child_range children() {
    549     return child_range(child_iterator(), child_iterator());
    550   }
    551   const_child_range children() const {
    552     return const_child_range(const_child_iterator(), const_child_iterator());
    553   }
    554 };
    555 
    556 } // namespace clang
    557 
    558 #endif // LLVM_CLANG_AST_EXPRCONCEPTS_H
    559