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