1e5dd7070Spatrick //===-- SemaConcept.h - Semantic Analysis for Constraints and Concepts ----===// 2e5dd7070Spatrick // 3*12c85518Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*12c85518Srobert // See https://llvm.org/LICENSE.txt for license information. 5*12c85518Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e5dd7070Spatrick // 7e5dd7070Spatrick //===----------------------------------------------------------------------===// 8e5dd7070Spatrick /// 9e5dd7070Spatrick // This file provides semantic analysis for C++ constraints and concepts. 10e5dd7070Spatrick /// 11e5dd7070Spatrick //===----------------------------------------------------------------------===// 12e5dd7070Spatrick 13e5dd7070Spatrick #ifndef LLVM_CLANG_SEMA_SEMACONCEPT_H 14e5dd7070Spatrick #define LLVM_CLANG_SEMA_SEMACONCEPT_H 15e5dd7070Spatrick #include "clang/AST/ASTConcept.h" 16e5dd7070Spatrick #include "clang/AST/ASTContext.h" 17e5dd7070Spatrick #include "clang/AST/Expr.h" 18e5dd7070Spatrick #include "clang/AST/DeclTemplate.h" 19e5dd7070Spatrick #include "clang/Basic/SourceLocation.h" 20e5dd7070Spatrick #include "llvm/ADT/PointerUnion.h" 21e5dd7070Spatrick #include "llvm/ADT/SmallVector.h" 22*12c85518Srobert #include <optional> 23e5dd7070Spatrick #include <string> 24e5dd7070Spatrick #include <utility> 25e5dd7070Spatrick 26e5dd7070Spatrick namespace clang { 27e5dd7070Spatrick class Sema; 28e5dd7070Spatrick 29e5dd7070Spatrick struct AtomicConstraint { 30e5dd7070Spatrick const Expr *ConstraintExpr; 31*12c85518Srobert std::optional<ArrayRef<TemplateArgumentLoc>> ParameterMapping; 32e5dd7070Spatrick AtomicConstraintAtomicConstraint33e5dd7070Spatrick AtomicConstraint(Sema &S, const Expr *ConstraintExpr) : 34e5dd7070Spatrick ConstraintExpr(ConstraintExpr) { }; 35e5dd7070Spatrick hasMatchingParameterMappingAtomicConstraint36e5dd7070Spatrick bool hasMatchingParameterMapping(ASTContext &C, 37e5dd7070Spatrick const AtomicConstraint &Other) const { 38e5dd7070Spatrick if (!ParameterMapping != !Other.ParameterMapping) 39e5dd7070Spatrick return false; 40e5dd7070Spatrick if (!ParameterMapping) 41e5dd7070Spatrick return true; 42e5dd7070Spatrick if (ParameterMapping->size() != Other.ParameterMapping->size()) 43e5dd7070Spatrick return false; 44e5dd7070Spatrick 45e5dd7070Spatrick for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) { 46e5dd7070Spatrick llvm::FoldingSetNodeID IDA, IDB; 47e5dd7070Spatrick C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument()) 48e5dd7070Spatrick .Profile(IDA, C); 49e5dd7070Spatrick C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument()) 50e5dd7070Spatrick .Profile(IDB, C); 51e5dd7070Spatrick if (IDA != IDB) 52e5dd7070Spatrick return false; 53e5dd7070Spatrick } 54e5dd7070Spatrick return true; 55e5dd7070Spatrick } 56e5dd7070Spatrick subsumesAtomicConstraint57e5dd7070Spatrick bool subsumes(ASTContext &C, const AtomicConstraint &Other) const { 58e5dd7070Spatrick // C++ [temp.constr.order] p2 59e5dd7070Spatrick // - an atomic constraint A subsumes another atomic constraint B 60e5dd7070Spatrick // if and only if the A and B are identical [...] 61e5dd7070Spatrick // 62e5dd7070Spatrick // C++ [temp.constr.atomic] p2 63e5dd7070Spatrick // Two atomic constraints are identical if they are formed from the 64e5dd7070Spatrick // same expression and the targets of the parameter mappings are 65e5dd7070Spatrick // equivalent according to the rules for expressions [...] 66e5dd7070Spatrick 67e5dd7070Spatrick // We do not actually substitute the parameter mappings into the 68e5dd7070Spatrick // constraint expressions, therefore the constraint expressions are 69e5dd7070Spatrick // the originals, and comparing them will suffice. 70e5dd7070Spatrick if (ConstraintExpr != Other.ConstraintExpr) 71e5dd7070Spatrick return false; 72e5dd7070Spatrick 73e5dd7070Spatrick // Check that the parameter lists are identical 74e5dd7070Spatrick return hasMatchingParameterMapping(C, Other); 75e5dd7070Spatrick } 76e5dd7070Spatrick }; 77e5dd7070Spatrick 78e5dd7070Spatrick /// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is 79e5dd7070Spatrick /// either an atomic constraint, a conjunction of normalized constraints or a 80e5dd7070Spatrick /// disjunction of normalized constraints. 81e5dd7070Spatrick struct NormalizedConstraint { 82e5dd7070Spatrick friend class Sema; 83e5dd7070Spatrick 84e5dd7070Spatrick enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction }; 85e5dd7070Spatrick 86e5dd7070Spatrick using CompoundConstraint = llvm::PointerIntPair< 87e5dd7070Spatrick std::pair<NormalizedConstraint, NormalizedConstraint> *, 1, 88e5dd7070Spatrick CompoundConstraintKind>; 89e5dd7070Spatrick 90e5dd7070Spatrick llvm::PointerUnion<AtomicConstraint *, CompoundConstraint> Constraint; 91e5dd7070Spatrick NormalizedConstraintNormalizedConstraint92e5dd7070Spatrick NormalizedConstraint(AtomicConstraint *C): Constraint{C} { }; NormalizedConstraintNormalizedConstraint93e5dd7070Spatrick NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS, 94e5dd7070Spatrick NormalizedConstraint RHS, CompoundConstraintKind Kind) 95e5dd7070Spatrick : Constraint{CompoundConstraint{ 96e5dd7070Spatrick new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{ 97e5dd7070Spatrick std::move(LHS), std::move(RHS)}, Kind}} { }; 98e5dd7070Spatrick NormalizedConstraintNormalizedConstraint99e5dd7070Spatrick NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other) { 100e5dd7070Spatrick if (Other.isAtomic()) { 101e5dd7070Spatrick Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint()); 102e5dd7070Spatrick } else { 103e5dd7070Spatrick Constraint = CompoundConstraint( 104e5dd7070Spatrick new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{ 105e5dd7070Spatrick NormalizedConstraint(C, Other.getLHS()), 106e5dd7070Spatrick NormalizedConstraint(C, Other.getRHS())}, 107e5dd7070Spatrick Other.getCompoundKind()); 108e5dd7070Spatrick } 109e5dd7070Spatrick } NormalizedConstraintNormalizedConstraint110e5dd7070Spatrick NormalizedConstraint(NormalizedConstraint &&Other): 111e5dd7070Spatrick Constraint(Other.Constraint) { 112e5dd7070Spatrick Other.Constraint = nullptr; 113e5dd7070Spatrick } 114e5dd7070Spatrick NormalizedConstraint &operator=(const NormalizedConstraint &Other) = delete; 115e5dd7070Spatrick NormalizedConstraint &operator=(NormalizedConstraint &&Other) { 116e5dd7070Spatrick if (&Other != this) { 117e5dd7070Spatrick NormalizedConstraint Temp(std::move(Other)); 118e5dd7070Spatrick std::swap(Constraint, Temp.Constraint); 119e5dd7070Spatrick } 120e5dd7070Spatrick return *this; 121e5dd7070Spatrick } 122e5dd7070Spatrick getCompoundKindNormalizedConstraint123e5dd7070Spatrick CompoundConstraintKind getCompoundKind() const { 124e5dd7070Spatrick assert(!isAtomic() && "getCompoundKind called on atomic constraint."); 125e5dd7070Spatrick return Constraint.get<CompoundConstraint>().getInt(); 126e5dd7070Spatrick } 127e5dd7070Spatrick isAtomicNormalizedConstraint128e5dd7070Spatrick bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); } 129e5dd7070Spatrick getLHSNormalizedConstraint130e5dd7070Spatrick NormalizedConstraint &getLHS() const { 131e5dd7070Spatrick assert(!isAtomic() && "getLHS called on atomic constraint."); 132e5dd7070Spatrick return Constraint.get<CompoundConstraint>().getPointer()->first; 133e5dd7070Spatrick } 134e5dd7070Spatrick getRHSNormalizedConstraint135e5dd7070Spatrick NormalizedConstraint &getRHS() const { 136e5dd7070Spatrick assert(!isAtomic() && "getRHS called on atomic constraint."); 137e5dd7070Spatrick return Constraint.get<CompoundConstraint>().getPointer()->second; 138e5dd7070Spatrick } 139e5dd7070Spatrick getAtomicConstraintNormalizedConstraint140e5dd7070Spatrick AtomicConstraint *getAtomicConstraint() const { 141e5dd7070Spatrick assert(isAtomic() && 142e5dd7070Spatrick "getAtomicConstraint called on non-atomic constraint."); 143e5dd7070Spatrick return Constraint.get<AtomicConstraint *>(); 144e5dd7070Spatrick } 145e5dd7070Spatrick 146e5dd7070Spatrick private: 147*12c85518Srobert static std::optional<NormalizedConstraint> 148e5dd7070Spatrick fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef<const Expr *> E); 149*12c85518Srobert static std::optional<NormalizedConstraint> 150e5dd7070Spatrick fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E); 151e5dd7070Spatrick }; 152e5dd7070Spatrick 153e5dd7070Spatrick } // clang 154e5dd7070Spatrick 155e5dd7070Spatrick #endif // LLVM_CLANG_SEMA_SEMACONCEPT_H 156