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/SmallVector.h"
22#include <optional>
23#include <string>
24#include <utility>
25
26namespace clang {
27class Sema;
28
29struct AtomicConstraint {
30 const Expr *ConstraintExpr;
31 std::optional<ArrayRef<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(Arg: (*ParameterMapping)[I].getArgument())
48 .Profile(ID&: IDA, Context: C);
49 C.getCanonicalTemplateArgument(Arg: (*Other.ParameterMapping)[I].getArgument())
50 .Profile(ID&: IDB, Context: 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.
81struct 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(a&: Constraint, b&: 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
146private:
147 static std::optional<NormalizedConstraint>
148 fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef<const Expr *> E);
149 static std::optional<NormalizedConstraint>
150 fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E);
151};
152
153} // clang
154
155#endif // LLVM_CLANG_SEMA_SEMACONCEPT_H
156

source code of clang/include/clang/Sema/SemaConcept.h