//===-- SemaConcept.h - Semantic Analysis for Constraints and Concepts ----===// // // 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 // //===----------------------------------------------------------------------===// /// // This file provides semantic analysis for C++ constraints and concepts. /// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_SEMA_SEMACONCEPT_H #define LLVM_CLANG_SEMA_SEMACONCEPT_H #include "clang/AST/ASTConcept.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/AST/DeclTemplate.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallVector.h" #include #include #include namespace clang { class Sema; struct AtomicConstraint { const Expr *ConstraintExpr; std::optional> ParameterMapping; AtomicConstraint(Sema &S, const Expr *ConstraintExpr) : ConstraintExpr(ConstraintExpr) { }; bool hasMatchingParameterMapping(ASTContext &C, const AtomicConstraint &Other) const { if (!ParameterMapping != !Other.ParameterMapping) return false; if (!ParameterMapping) return true; if (ParameterMapping->size() != Other.ParameterMapping->size()) return false; for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) { llvm::FoldingSetNodeID IDA, IDB; C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument()) .Profile(IDA, C); C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument()) .Profile(IDB, C); if (IDA != IDB) return false; } return true; } bool subsumes(ASTContext &C, const AtomicConstraint &Other) const { // C++ [temp.constr.order] p2 // - an atomic constraint A subsumes another atomic constraint B // if and only if the A and B are identical [...] // // C++ [temp.constr.atomic] p2 // Two atomic constraints are identical if they are formed from the // same expression and the targets of the parameter mappings are // equivalent according to the rules for expressions [...] // We do not actually substitute the parameter mappings into the // constraint expressions, therefore the constraint expressions are // the originals, and comparing them will suffice. if (ConstraintExpr != Other.ConstraintExpr) return false; // Check that the parameter lists are identical return hasMatchingParameterMapping(C, Other); } }; /// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is /// either an atomic constraint, a conjunction of normalized constraints or a /// disjunction of normalized constraints. struct NormalizedConstraint { friend class Sema; enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction }; using CompoundConstraint = llvm::PointerIntPair< std::pair *, 1, CompoundConstraintKind>; llvm::PointerUnion Constraint; NormalizedConstraint(AtomicConstraint *C): Constraint{C} { }; NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS, NormalizedConstraint RHS, CompoundConstraintKind Kind) : Constraint{CompoundConstraint{ new (C) std::pair{ std::move(LHS), std::move(RHS)}, Kind}} { }; NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other) { if (Other.isAtomic()) { Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint()); } else { Constraint = CompoundConstraint( new (C) std::pair{ NormalizedConstraint(C, Other.getLHS()), NormalizedConstraint(C, Other.getRHS())}, Other.getCompoundKind()); } } NormalizedConstraint(NormalizedConstraint &&Other): Constraint(Other.Constraint) { Other.Constraint = nullptr; } NormalizedConstraint &operator=(const NormalizedConstraint &Other) = delete; NormalizedConstraint &operator=(NormalizedConstraint &&Other) { if (&Other != this) { NormalizedConstraint Temp(std::move(Other)); std::swap(Constraint, Temp.Constraint); } return *this; } CompoundConstraintKind getCompoundKind() const { assert(!isAtomic() && "getCompoundKind called on atomic constraint."); return Constraint.get().getInt(); } bool isAtomic() const { return Constraint.is(); } NormalizedConstraint &getLHS() const { assert(!isAtomic() && "getLHS called on atomic constraint."); return Constraint.get().getPointer()->first; } NormalizedConstraint &getRHS() const { assert(!isAtomic() && "getRHS called on atomic constraint."); return Constraint.get().getPointer()->second; } AtomicConstraint *getAtomicConstraint() const { assert(isAtomic() && "getAtomicConstraint called on non-atomic constraint."); return Constraint.get(); } private: static std::optional fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef E); static std::optional fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E); }; } // clang #endif // LLVM_CLANG_SEMA_SEMACONCEPT_H