| //===- ExprConcepts.h - C++2a Concepts expressions --------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| /// \file |
| /// Defines Expressions and AST nodes for C++2a concepts. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_AST_EXPRCONCEPTS_H |
| #define LLVM_CLANG_AST_EXPRCONCEPTS_H |
| |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/ASTConcept.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/DeclarationName.h" |
| #include "clang/AST/DeclTemplate.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/AST/NestedNameSpecifier.h" |
| #include "clang/AST/TemplateBase.h" |
| #include "clang/AST/Type.h" |
| #include "clang/Basic/SourceLocation.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/TrailingObjects.h" |
| #include <utility> |
| #include <string> |
| |
| namespace clang { |
| class ASTStmtReader; |
| class ASTStmtWriter; |
| |
| /// \brief Represents the specialization of a concept - evaluates to a prvalue |
| /// of type bool. |
| /// |
| /// 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, public ConceptReference { |
| friend class ASTReader; |
| friend class ASTStmtReader; |
| |
| public: |
| using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>; |
| |
| protected: |
| /// \brief The Implicit Concept Specialization Decl, which holds the template |
| /// arguments for this specialization. |
| ImplicitConceptSpecializationDecl *SpecDecl; |
| |
| /// \brief Information about the satisfaction of the named concept with the |
| /// given arguments. If this expression is value dependent, this is to be |
| /// ignored. |
| ASTConstraintSatisfaction *Satisfaction; |
| |
| ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS, |
| SourceLocation TemplateKWLoc, |
| DeclarationNameInfo ConceptNameInfo, |
| NamedDecl *FoundDecl, ConceptDecl *NamedConcept, |
| const ASTTemplateArgumentListInfo *ArgsAsWritten, |
| ImplicitConceptSpecializationDecl *SpecDecl, |
| const ConstraintSatisfaction *Satisfaction); |
| |
| ConceptSpecializationExpr(const ASTContext &C, ConceptDecl *NamedConcept, |
| const ASTTemplateArgumentListInfo *ArgsAsWritten, |
| ImplicitConceptSpecializationDecl *SpecDecl, |
| const ConstraintSatisfaction *Satisfaction, |
| bool Dependent, |
| bool ContainsUnexpandedParameterPack); |
| ConceptSpecializationExpr(EmptyShell Empty); |
| |
| public: |
| static ConceptSpecializationExpr * |
| Create(const ASTContext &C, NestedNameSpecifierLoc NNS, |
| SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo, |
| NamedDecl *FoundDecl, ConceptDecl *NamedConcept, |
| const ASTTemplateArgumentListInfo *ArgsAsWritten, |
| ImplicitConceptSpecializationDecl *SpecDecl, |
| const ConstraintSatisfaction *Satisfaction); |
| |
| static ConceptSpecializationExpr * |
| Create(const ASTContext &C, ConceptDecl *NamedConcept, |
| const ASTTemplateArgumentListInfo *ArgsAsWritten, |
| ImplicitConceptSpecializationDecl *SpecDecl, |
| const ConstraintSatisfaction *Satisfaction, bool Dependent, |
| bool ContainsUnexpandedParameterPack); |
| |
| ArrayRef<TemplateArgument> getTemplateArguments() const { |
| return SpecDecl->getTemplateArguments(); |
| } |
| |
| const ImplicitConceptSpecializationDecl *getSpecializationDecl() const { |
| assert(SpecDecl && "Template Argument Decl not initialized"); |
| return SpecDecl; |
| } |
| |
| /// \brief Whether or not the concept with the given arguments was satisfied |
| /// when the expression was created. |
| /// The expression must not be dependent. |
| bool isSatisfied() const { |
| assert(!isValueDependent() && |
| "isSatisfied called on a dependent ConceptSpecializationExpr"); |
| return Satisfaction->IsSatisfied; |
| } |
| |
| /// \brief Get elaborated satisfaction info about the template arguments' |
| /// satisfaction of the named concept. |
| /// The expression must not be dependent. |
| const ASTConstraintSatisfaction &getSatisfaction() const { |
| assert(!isValueDependent() && |
| "getSatisfaction called on dependent ConceptSpecializationExpr"); |
| return *Satisfaction; |
| } |
| |
| static bool classof(const Stmt *T) { |
| return T->getStmtClass() == ConceptSpecializationExprClass; |
| } |
| |
| SourceLocation getBeginLoc() const LLVM_READONLY { |
| if (auto QualifierLoc = getNestedNameSpecifierLoc()) |
| return QualifierLoc.getBeginLoc(); |
| return ConceptName.getBeginLoc(); |
| } |
| |
| SourceLocation getEndLoc() const LLVM_READONLY { |
| // If the ConceptSpecializationExpr is the ImmediatelyDeclaredConstraint |
| // of a TypeConstraint written syntactically as a constrained-parameter, |
| // there may not be a template argument list. |
| return ArgsAsWritten->RAngleLoc.isValid() ? ArgsAsWritten->RAngleLoc |
| : ConceptName.getEndLoc(); |
| } |
| |
| // Iterators |
| child_range children() { |
| return child_range(child_iterator(), child_iterator()); |
| } |
| const_child_range children() const { |
| return const_child_range(const_child_iterator(), const_child_iterator()); |
| } |
| }; |
| |
| namespace concepts { |
| |
| /// \brief A static requirement that can be used in a requires-expression to |
| /// check properties of types and expression. |
| class Requirement { |
| public: |
| // Note - simple and compound requirements are both represented by the same |
| // class (ExprRequirement). |
| enum RequirementKind { RK_Type, RK_Simple, RK_Compound, RK_Nested }; |
| private: |
| const RequirementKind Kind; |
| // FIXME: use RequirementDependence to model dependence? |
| bool Dependent : 1; |
| bool ContainsUnexpandedParameterPack : 1; |
| bool Satisfied : 1; |
| public: |
| struct SubstitutionDiagnostic { |
| StringRef SubstitutedEntity; |
| // FIXME: Store diagnostics semantically and not as prerendered strings. |
| // Fixing this probably requires serialization of PartialDiagnostic |
| // objects. |
| SourceLocation DiagLoc; |
| StringRef DiagMessage; |
| }; |
| |
| Requirement(RequirementKind Kind, bool IsDependent, |
| bool ContainsUnexpandedParameterPack, bool IsSatisfied = true) : |
| Kind(Kind), Dependent(IsDependent), |
| ContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack), |
| Satisfied(IsSatisfied) {} |
| |
| RequirementKind getKind() const { return Kind; } |
| |
| bool isSatisfied() const { |
| assert(!Dependent && |
| "isSatisfied can only be called on non-dependent requirements."); |
| return Satisfied; |
| } |
| |
| void setSatisfied(bool IsSatisfied) { |
| assert(!Dependent && |
| "setSatisfied can only be called on non-dependent requirements."); |
| Satisfied = IsSatisfied; |
| } |
| |
| void setDependent(bool IsDependent) { Dependent = IsDependent; } |
| bool isDependent() const { return Dependent; } |
| |
| void setContainsUnexpandedParameterPack(bool Contains) { |
| ContainsUnexpandedParameterPack = Contains; |
| } |
| bool containsUnexpandedParameterPack() const { |
| return ContainsUnexpandedParameterPack; |
| } |
| }; |
| |
| /// \brief A requires-expression requirement which queries the existence of a |
| /// type name or type template specialization ('type' requirements). |
| class TypeRequirement : public Requirement { |
| public: |
| enum SatisfactionStatus { |
| SS_Dependent, |
| SS_SubstitutionFailure, |
| SS_Satisfied |
| }; |
| private: |
| llvm::PointerUnion<SubstitutionDiagnostic *, TypeSourceInfo *> Value; |
| SatisfactionStatus Status; |
| public: |
| friend ASTStmtReader; |
| friend ASTStmtWriter; |
| |
| /// \brief Construct a type requirement from a type. If the given type is not |
| /// dependent, this indicates that the type exists and the requirement will be |
| /// satisfied. Otherwise, the SubstitutionDiagnostic constructor is to be |
| /// used. |
| TypeRequirement(TypeSourceInfo *T); |
| |
| /// \brief Construct a type requirement when the nested name specifier is |
| /// invalid due to a bad substitution. The requirement is unsatisfied. |
| TypeRequirement(SubstitutionDiagnostic *Diagnostic) : |
| Requirement(RK_Type, false, false, false), Value(Diagnostic), |
| Status(SS_SubstitutionFailure) {} |
| |
| SatisfactionStatus getSatisfactionStatus() const { return Status; } |
| void setSatisfactionStatus(SatisfactionStatus Status) { |
| this->Status = Status; |
| } |
| |
| bool isSubstitutionFailure() const { |
| return Status == SS_SubstitutionFailure; |
| } |
| |
| SubstitutionDiagnostic *getSubstitutionDiagnostic() const { |
| assert(Status == SS_SubstitutionFailure && |
| "Attempted to get substitution diagnostic when there has been no " |
| "substitution failure."); |
| return Value.get<SubstitutionDiagnostic *>(); |
| } |
| |
| TypeSourceInfo *getType() const { |
| assert(!isSubstitutionFailure() && |
| "Attempted to get type when there has been a substitution failure."); |
| return Value.get<TypeSourceInfo *>(); |
| } |
| |
| static bool classof(const Requirement *R) { |
| return R->getKind() == RK_Type; |
| } |
| }; |
| |
| /// \brief A requires-expression requirement which queries the validity and |
| /// properties of an expression ('simple' and 'compound' requirements). |
| class ExprRequirement : public Requirement { |
| public: |
| enum SatisfactionStatus { |
| SS_Dependent, |
| SS_ExprSubstitutionFailure, |
| SS_NoexceptNotMet, |
| SS_TypeRequirementSubstitutionFailure, |
| SS_ConstraintsNotSatisfied, |
| SS_Satisfied |
| }; |
| class ReturnTypeRequirement { |
| llvm::PointerIntPair< |
| llvm::PointerUnion<TemplateParameterList *, SubstitutionDiagnostic *>, |
| 1, bool> |
| TypeConstraintInfo; |
| public: |
| friend ASTStmtReader; |
| friend ASTStmtWriter; |
| |
| /// \brief No return type requirement was specified. |
| ReturnTypeRequirement() : TypeConstraintInfo(nullptr, false) {} |
| |
| /// \brief A return type requirement was specified but it was a |
| /// substitution failure. |
| ReturnTypeRequirement(SubstitutionDiagnostic *SubstDiag) : |
| TypeConstraintInfo(SubstDiag, false) {} |
| |
| /// \brief A 'type constraint' style return type requirement. |
| /// \param TPL an invented template parameter list containing a single |
| /// type parameter with a type-constraint. |
| // TODO: Can we maybe not save the whole template parameter list and just |
| // the type constraint? Saving the whole TPL makes it easier to handle in |
| // serialization but is less elegant. |
| ReturnTypeRequirement(TemplateParameterList *TPL); |
| |
| bool isDependent() const { |
| return TypeConstraintInfo.getInt(); |
| } |
| |
| bool containsUnexpandedParameterPack() const { |
| if (!isTypeConstraint()) |
| return false; |
| return getTypeConstraintTemplateParameterList() |
| ->containsUnexpandedParameterPack(); |
| } |
| |
| bool isEmpty() const { |
| return TypeConstraintInfo.getPointer().isNull(); |
| } |
| |
| bool isSubstitutionFailure() const { |
| return !isEmpty() && |
| TypeConstraintInfo.getPointer().is<SubstitutionDiagnostic *>(); |
| } |
| |
| bool isTypeConstraint() const { |
| return !isEmpty() && |
| TypeConstraintInfo.getPointer().is<TemplateParameterList *>(); |
| } |
| |
| SubstitutionDiagnostic *getSubstitutionDiagnostic() const { |
| assert(isSubstitutionFailure()); |
| return TypeConstraintInfo.getPointer().get<SubstitutionDiagnostic *>(); |
| } |
| |
| const TypeConstraint *getTypeConstraint() const; |
| |
| TemplateParameterList *getTypeConstraintTemplateParameterList() const { |
| assert(isTypeConstraint()); |
| return TypeConstraintInfo.getPointer().get<TemplateParameterList *>(); |
| } |
| }; |
| private: |
| llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> Value; |
| SourceLocation NoexceptLoc; // May be empty if noexcept wasn't specified. |
| ReturnTypeRequirement TypeReq; |
| ConceptSpecializationExpr *SubstitutedConstraintExpr; |
| SatisfactionStatus Status; |
| public: |
| friend ASTStmtReader; |
| friend ASTStmtWriter; |
| |
| /// \brief Construct a compound requirement. |
| /// \param E the expression which is checked by this requirement. |
| /// \param IsSimple whether this was a simple requirement in source. |
| /// \param NoexceptLoc the location of the noexcept keyword, if it was |
| /// specified, otherwise an empty location. |
| /// \param Req the requirement for the type of the checked expression. |
| /// \param Status the satisfaction status of this requirement. |
| ExprRequirement( |
| Expr *E, bool IsSimple, SourceLocation NoexceptLoc, |
| ReturnTypeRequirement Req, SatisfactionStatus Status, |
| ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr); |
| |
| /// \brief Construct a compound requirement whose expression was a |
| /// substitution failure. The requirement is not satisfied. |
| /// \param E the diagnostic emitted while instantiating the original |
| /// expression. |
| /// \param IsSimple whether this was a simple requirement in source. |
| /// \param NoexceptLoc the location of the noexcept keyword, if it was |
| /// specified, otherwise an empty location. |
| /// \param Req the requirement for the type of the checked expression (omit |
| /// if no requirement was specified). |
| ExprRequirement(SubstitutionDiagnostic *E, bool IsSimple, |
| SourceLocation NoexceptLoc, ReturnTypeRequirement Req = {}); |
| |
| bool isSimple() const { return getKind() == RK_Simple; } |
| bool isCompound() const { return getKind() == RK_Compound; } |
| |
| bool hasNoexceptRequirement() const { return NoexceptLoc.isValid(); } |
| SourceLocation getNoexceptLoc() const { return NoexceptLoc; } |
| |
| SatisfactionStatus getSatisfactionStatus() const { return Status; } |
| |
| bool isExprSubstitutionFailure() const { |
| return Status == SS_ExprSubstitutionFailure; |
| } |
| |
| const ReturnTypeRequirement &getReturnTypeRequirement() const { |
| return TypeReq; |
| } |
| |
| ConceptSpecializationExpr * |
| getReturnTypeRequirementSubstitutedConstraintExpr() const { |
| assert(Status >= SS_TypeRequirementSubstitutionFailure); |
| return SubstitutedConstraintExpr; |
| } |
| |
| SubstitutionDiagnostic *getExprSubstitutionDiagnostic() const { |
| assert(isExprSubstitutionFailure() && |
| "Attempted to get expression substitution diagnostic when there has " |
| "been no expression substitution failure"); |
| return Value.get<SubstitutionDiagnostic *>(); |
| } |
| |
| Expr *getExpr() const { |
| assert(!isExprSubstitutionFailure() && |
| "ExprRequirement has no expression because there has been a " |
| "substitution failure."); |
| return Value.get<Expr *>(); |
| } |
| |
| static bool classof(const Requirement *R) { |
| return R->getKind() == RK_Compound || R->getKind() == RK_Simple; |
| } |
| }; |
| |
| /// \brief A requires-expression requirement which is satisfied when a general |
| /// constraint expression is satisfied ('nested' requirements). |
| class NestedRequirement : public Requirement { |
| Expr *Constraint = nullptr; |
| const ASTConstraintSatisfaction *Satisfaction = nullptr; |
| bool HasInvalidConstraint = false; |
| StringRef InvalidConstraintEntity; |
| |
| public: |
| friend ASTStmtReader; |
| friend ASTStmtWriter; |
| |
| NestedRequirement(Expr *Constraint) |
| : Requirement(RK_Nested, /*IsDependent=*/true, |
| Constraint->containsUnexpandedParameterPack()), |
| Constraint(Constraint) { |
| assert(Constraint->isInstantiationDependent() && |
| "Nested requirement with non-dependent constraint must be " |
| "constructed with a ConstraintSatisfaction object"); |
| } |
| |
| NestedRequirement(ASTContext &C, Expr *Constraint, |
| const ConstraintSatisfaction &Satisfaction) |
| : Requirement(RK_Nested, Constraint->isInstantiationDependent(), |
| Constraint->containsUnexpandedParameterPack(), |
| Satisfaction.IsSatisfied), |
| Constraint(Constraint), |
| Satisfaction(ASTConstraintSatisfaction::Create(C, Satisfaction)) {} |
| |
| NestedRequirement(StringRef InvalidConstraintEntity, |
| const ASTConstraintSatisfaction *Satisfaction) |
| : Requirement(RK_Nested, |
| /*IsDependent=*/false, |
| /*ContainsUnexpandedParameterPack*/ false, |
| Satisfaction->IsSatisfied), |
| Satisfaction(Satisfaction), HasInvalidConstraint(true), |
| InvalidConstraintEntity(InvalidConstraintEntity) {} |
| |
| NestedRequirement(ASTContext &C, StringRef InvalidConstraintEntity, |
| const ConstraintSatisfaction &Satisfaction) |
| : NestedRequirement(InvalidConstraintEntity, |
| ASTConstraintSatisfaction::Create(C, Satisfaction)) {} |
| |
| bool hasInvalidConstraint() const { return HasInvalidConstraint; } |
| |
| StringRef getInvalidConstraintEntity() { |
| assert(hasInvalidConstraint()); |
| return InvalidConstraintEntity; |
| } |
| |
| Expr *getConstraintExpr() const { |
| assert(!hasInvalidConstraint() && |
| "getConstraintExpr() may not be called " |
| "on nested requirements with invalid constraint."); |
| return Constraint; |
| } |
| |
| const ASTConstraintSatisfaction &getConstraintSatisfaction() const { |
| return *Satisfaction; |
| } |
| |
| static bool classof(const Requirement *R) { |
| return R->getKind() == RK_Nested; |
| } |
| }; |
| |
| } // namespace concepts |
| |
| /// C++2a [expr.prim.req]: |
| /// A requires-expression provides a concise way to express requirements on |
| /// template arguments. A requirement is one that can be checked by name |
| /// lookup (6.4) or by checking properties of types and expressions. |
| /// [...] |
| /// A requires-expression is a prvalue of type bool [...] |
| class RequiresExpr final : public Expr, |
| llvm::TrailingObjects<RequiresExpr, ParmVarDecl *, |
| concepts::Requirement *> { |
| friend TrailingObjects; |
| friend class ASTStmtReader; |
| |
| unsigned NumLocalParameters; |
| unsigned NumRequirements; |
| RequiresExprBodyDecl *Body; |
| SourceLocation RBraceLoc; |
| |
| unsigned numTrailingObjects(OverloadToken<ParmVarDecl *>) const { |
| return NumLocalParameters; |
| } |
| |
| unsigned numTrailingObjects(OverloadToken<concepts::Requirement *>) const { |
| return NumRequirements; |
| } |
| |
| RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc, |
| RequiresExprBodyDecl *Body, |
| ArrayRef<ParmVarDecl *> LocalParameters, |
| ArrayRef<concepts::Requirement *> Requirements, |
| SourceLocation RBraceLoc); |
| RequiresExpr(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters, |
| unsigned NumRequirements); |
| |
| public: |
| static RequiresExpr * |
| Create(ASTContext &C, SourceLocation RequiresKWLoc, |
| RequiresExprBodyDecl *Body, ArrayRef<ParmVarDecl *> LocalParameters, |
| ArrayRef<concepts::Requirement *> Requirements, |
| SourceLocation RBraceLoc); |
| static RequiresExpr * |
| Create(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters, |
| unsigned NumRequirements); |
| |
| ArrayRef<ParmVarDecl *> getLocalParameters() const { |
| return {getTrailingObjects<ParmVarDecl *>(), NumLocalParameters}; |
| } |
| |
| RequiresExprBodyDecl *getBody() const { return Body; } |
| |
| ArrayRef<concepts::Requirement *> getRequirements() const { |
| return {getTrailingObjects<concepts::Requirement *>(), NumRequirements}; |
| } |
| |
| /// \brief Whether or not the requires clause is satisfied. |
| /// The expression must not be dependent. |
| bool isSatisfied() const { |
| assert(!isValueDependent() |
| && "isSatisfied called on a dependent RequiresExpr"); |
| return RequiresExprBits.IsSatisfied; |
| } |
| |
| void setSatisfied(bool IsSatisfied) { |
| assert(!isValueDependent() && |
| "setSatisfied called on a dependent RequiresExpr"); |
| RequiresExprBits.IsSatisfied = IsSatisfied; |
| } |
| |
| SourceLocation getRequiresKWLoc() const { |
| return RequiresExprBits.RequiresKWLoc; |
| } |
| |
| SourceLocation getRBraceLoc() const { return RBraceLoc; } |
| |
| static bool classof(const Stmt *T) { |
| return T->getStmtClass() == RequiresExprClass; |
| } |
| |
| SourceLocation getBeginLoc() const LLVM_READONLY { |
| return RequiresExprBits.RequiresKWLoc; |
| } |
| SourceLocation getEndLoc() const LLVM_READONLY { |
| return RBraceLoc; |
| } |
| |
| // Iterators |
| child_range children() { |
| return child_range(child_iterator(), child_iterator()); |
| } |
| const_child_range children() const { |
| return const_child_range(const_child_iterator(), const_child_iterator()); |
| } |
| }; |
| |
| } // namespace clang |
| |
| #endif // LLVM_CLANG_AST_EXPRCONCEPTS_H |