//===- 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/ASTConcept.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.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/ADT/STLFunctionalExtras.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TrailingObjects.h" #include #include 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 { friend class ASTReader; friend class ASTStmtReader; private: ConceptReference *ConceptRef; /// \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, ConceptReference *ConceptRef, ImplicitConceptSpecializationDecl *SpecDecl, const ConstraintSatisfaction *Satisfaction); ConceptSpecializationExpr(const ASTContext &C, ConceptReference *ConceptRef, ImplicitConceptSpecializationDecl *SpecDecl, const ConstraintSatisfaction *Satisfaction, bool Dependent, bool ContainsUnexpandedParameterPack); ConceptSpecializationExpr(EmptyShell Empty); public: static ConceptSpecializationExpr * Create(const ASTContext &C, ConceptReference *ConceptRef, ImplicitConceptSpecializationDecl *SpecDecl, const ConstraintSatisfaction *Satisfaction); static ConceptSpecializationExpr * Create(const ASTContext &C, ConceptReference *ConceptRef, ImplicitConceptSpecializationDecl *SpecDecl, const ConstraintSatisfaction *Satisfaction, bool Dependent, bool ContainsUnexpandedParameterPack); ArrayRef getTemplateArguments() const { return SpecDecl->getTemplateArguments(); } ConceptReference *getConceptReference() const { return ConceptRef; } ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); } // FIXME: Several of the following functions can be removed. Instead the // caller can directly work with the ConceptReference. bool hasExplicitTemplateArgs() const { return ConceptRef->hasExplicitTemplateArgs(); } SourceLocation getConceptNameLoc() const { return ConceptRef->getConceptNameLoc(); } const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const { return ConceptRef->getTemplateArgsAsWritten(); } const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const { return ConceptRef->getNestedNameSpecifierLoc(); } SourceLocation getTemplateKWLoc() const { return ConceptRef->getTemplateKWLoc(); } NamedDecl *getFoundDecl() const { return ConceptRef->getFoundDecl(); } const DeclarationNameInfo &getConceptNameInfo() const { return ConceptRef->getConceptNameInfo(); } 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 { return ConceptRef->getBeginLoc(); } SourceLocation getEndLoc() const LLVM_READONLY { return ConceptRef->getEndLoc(); } SourceLocation getExprLoc() const LLVM_READONLY { return ConceptRef->getLocation(); } // 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? LLVM_PREFERRED_TYPE(bool) bool Dependent : 1; LLVM_PREFERRED_TYPE(bool) bool ContainsUnexpandedParameterPack : 1; LLVM_PREFERRED_TYPE(bool) 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 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(); } TypeSourceInfo *getType() const { assert(!isSubstitutionFailure() && "Attempted to get type when there has been a substitution failure."); return Value.get(); } 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, 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(); } bool isTypeConstraint() const { return !isEmpty() && TypeConstraintInfo.getPointer().is(); } SubstitutionDiagnostic *getSubstitutionDiagnostic() const { assert(isSubstitutionFailure()); return TypeConstraintInfo.getPointer().get(); } const TypeConstraint *getTypeConstraint() const; TemplateParameterList *getTypeConstraintTemplateParameterList() const { assert(isTypeConstraint()); return TypeConstraintInfo.getPointer().get(); } }; private: llvm::PointerUnion 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(); } Expr *getExpr() const { assert(!isExprSubstitutionFailure() && "ExprRequirement has no expression because there has been a " "substitution failure."); return Value.get(); } 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; } }; using EntityPrinter = llvm::function_ref; /// \brief create a Requirement::SubstitutionDiagnostic with only a /// SubstitutedEntity and DiagLoc using Sema's allocator. Requirement::SubstitutionDiagnostic * createSubstDiagAt(Sema &S, SourceLocation Location, EntityPrinter Printer); } // 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 { friend TrailingObjects; friend class ASTStmtReader; unsigned NumLocalParameters; unsigned NumRequirements; RequiresExprBodyDecl *Body; SourceLocation LParenLoc; SourceLocation RParenLoc; SourceLocation RBraceLoc; unsigned numTrailingObjects(OverloadToken) const { return NumLocalParameters; } unsigned numTrailingObjects(OverloadToken) const { return NumRequirements; } RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc, RequiresExprBodyDecl *Body, SourceLocation LParenLoc, ArrayRef LocalParameters, SourceLocation RParenLoc, ArrayRef Requirements, SourceLocation RBraceLoc); RequiresExpr(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters, unsigned NumRequirements); public: static RequiresExpr *Create(ASTContext &C, SourceLocation RequiresKWLoc, RequiresExprBodyDecl *Body, SourceLocation LParenLoc, ArrayRef LocalParameters, SourceLocation RParenLoc, ArrayRef Requirements, SourceLocation RBraceLoc); static RequiresExpr * Create(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters, unsigned NumRequirements); ArrayRef getLocalParameters() const { return {getTrailingObjects(), NumLocalParameters}; } RequiresExprBodyDecl *getBody() const { return Body; } ArrayRef getRequirements() const { return {getTrailingObjects(), 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 getLParenLoc() const { return LParenLoc; } SourceLocation getRParenLoc() const { return RParenLoc; } 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