//===- 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,
 
         ImplicitConceptSpecializationDecl *SpecDecl,
 
         const ConstraintSatisfaction *Satisfaction, bool Dependent,
 
         bool ContainsUnexpandedParameterPack);
 
 
 
  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