1 //===--- ASTConcept.h - Concepts Related AST Data Structures ----*- C++ -*-===//
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 /// \file
10 /// \brief This file provides AST data structures related to concepts.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_CLANG_AST_ASTCONCEPT_H
15 #define LLVM_CLANG_AST_ASTCONCEPT_H
16 
17 #include "clang/AST/DeclarationName.h"
18 #include "clang/AST/NestedNameSpecifier.h"
19 #include "clang/AST/TemplateBase.h"
20 #include "clang/Basic/SourceLocation.h"
21 #include "llvm/ADT/FoldingSet.h"
22 #include "llvm/ADT/PointerUnion.h"
23 #include "llvm/ADT/SmallVector.h"
24 #include <utility>
25 
26 namespace clang {
27 
28 class ConceptDecl;
29 class Expr;
30 class NamedDecl;
31 struct PrintingPolicy;
32 
33 /// The result of a constraint satisfaction check, containing the necessary
34 /// information to diagnose an unsatisfied constraint.
35 class ConstraintSatisfaction : public llvm::FoldingSetNode {
36   // The template-like entity that 'owns' the constraint checked here (can be a
37   // constrained entity or a concept).
38   const NamedDecl *ConstraintOwner = nullptr;
39   llvm::SmallVector<TemplateArgument, 4> TemplateArgs;
40 
41 public:
42 
43   ConstraintSatisfaction() = default;
44 
ConstraintSatisfaction(const NamedDecl * ConstraintOwner,ArrayRef<TemplateArgument> TemplateArgs)45   ConstraintSatisfaction(const NamedDecl *ConstraintOwner,
46                          ArrayRef<TemplateArgument> TemplateArgs) :
47       ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs.begin(),
48                                                      TemplateArgs.end()) { }
49 
50   using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
51   using Detail = llvm::PointerUnion<Expr *, SubstitutionDiagnostic *>;
52 
53   bool IsSatisfied = false;
54   bool ContainsErrors = false;
55 
56   /// \brief Pairs of unsatisfied atomic constraint expressions along with the
57   /// substituted constraint expr, if the template arguments could be
58   /// substituted into them, or a diagnostic if substitution resulted in an
59   /// invalid expression.
60   llvm::SmallVector<std::pair<const Expr *, Detail>, 4> Details;
61 
Profile(llvm::FoldingSetNodeID & ID,const ASTContext & C)62   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) {
63     Profile(ID, C, ConstraintOwner, TemplateArgs);
64   }
65 
66   static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C,
67                       const NamedDecl *ConstraintOwner,
68                       ArrayRef<TemplateArgument> TemplateArgs);
69 
HasSubstitutionFailure()70   bool HasSubstitutionFailure() {
71     for (const auto &Detail : Details)
72       if (Detail.second.dyn_cast<SubstitutionDiagnostic *>())
73         return true;
74     return false;
75   }
76 };
77 
78 /// Pairs of unsatisfied atomic constraint expressions along with the
79 /// substituted constraint expr, if the template arguments could be
80 /// substituted into them, or a diagnostic if substitution resulted in
81 /// an invalid expression.
82 using UnsatisfiedConstraintRecord =
83     std::pair<const Expr *,
84               llvm::PointerUnion<Expr *,
85                                  std::pair<SourceLocation, StringRef> *>>;
86 
87 /// \brief The result of a constraint satisfaction check, containing the
88 /// necessary information to diagnose an unsatisfied constraint.
89 ///
90 /// This is safe to store in an AST node, as opposed to ConstraintSatisfaction.
91 struct ASTConstraintSatisfaction final :
92     llvm::TrailingObjects<ASTConstraintSatisfaction,
93                           UnsatisfiedConstraintRecord> {
94   std::size_t NumRecords;
95   bool IsSatisfied : 1;
96   bool ContainsErrors : 1;
97 
beginfinal98   const UnsatisfiedConstraintRecord *begin() const {
99     return getTrailingObjects<UnsatisfiedConstraintRecord>();
100   }
101 
endfinal102   const UnsatisfiedConstraintRecord *end() const {
103     return getTrailingObjects<UnsatisfiedConstraintRecord>() + NumRecords;
104   }
105 
106   ASTConstraintSatisfaction(const ASTContext &C,
107                             const ConstraintSatisfaction &Satisfaction);
108   ASTConstraintSatisfaction(const ASTContext &C,
109                             const ASTConstraintSatisfaction &Satisfaction);
110 
111   static ASTConstraintSatisfaction *
112   Create(const ASTContext &C, const ConstraintSatisfaction &Satisfaction);
113   static ASTConstraintSatisfaction *
114   Rebuild(const ASTContext &C, const ASTConstraintSatisfaction &Satisfaction);
115 };
116 
117 /// A reference to a concept and its template args, as it appears in the code.
118 ///
119 /// Examples:
120 ///   template <int X> requires is_even<X> int half = X/2;
121 ///                             ~~~~~~~~~~ (in ConceptSpecializationExpr)
122 ///
123 ///   std::input_iterator auto I = Container.begin();
124 ///   ~~~~~~~~~~~~~~~~~~~ (in AutoTypeLoc)
125 ///
126 ///   template <std::derives_from<Expr> T> void dump();
127 ///             ~~~~~~~~~~~~~~~~~~~~~~~ (in TemplateTypeParmDecl)
128 class ConceptReference {
129   // \brief The optional nested name specifier used when naming the concept.
130   NestedNameSpecifierLoc NestedNameSpec;
131 
132   /// \brief The location of the template keyword, if specified when naming the
133   /// concept.
134   SourceLocation TemplateKWLoc;
135 
136   /// \brief The concept name used.
137   DeclarationNameInfo ConceptName;
138 
139   /// \brief The declaration found by name lookup when the expression was
140   /// created.
141   /// Can differ from NamedConcept when, for example, the concept was found
142   /// through a UsingShadowDecl.
143   NamedDecl *FoundDecl;
144 
145   /// \brief The concept named.
146   ConceptDecl *NamedConcept;
147 
148   /// \brief The template argument list source info used to specialize the
149   /// concept.
150   const ASTTemplateArgumentListInfo *ArgsAsWritten;
151 
ConceptReference(NestedNameSpecifierLoc NNS,SourceLocation TemplateKWLoc,DeclarationNameInfo ConceptNameInfo,NamedDecl * FoundDecl,ConceptDecl * NamedConcept,const ASTTemplateArgumentListInfo * ArgsAsWritten)152   ConceptReference(NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
153                    DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
154                    ConceptDecl *NamedConcept,
155                    const ASTTemplateArgumentListInfo *ArgsAsWritten)
156       : NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
157         ConceptName(ConceptNameInfo), FoundDecl(FoundDecl),
158         NamedConcept(NamedConcept), ArgsAsWritten(ArgsAsWritten) {}
159 
160 public:
161   static ConceptReference *
162   Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
163          SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
164          NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
165          const ASTTemplateArgumentListInfo *ArgsAsWritten);
166 
getNestedNameSpecifierLoc()167   const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
168     return NestedNameSpec;
169   }
170 
getConceptNameInfo()171   const DeclarationNameInfo &getConceptNameInfo() const { return ConceptName; }
172 
getConceptNameLoc()173   SourceLocation getConceptNameLoc() const {
174     return getConceptNameInfo().getLoc();
175   }
176 
getTemplateKWLoc()177   SourceLocation getTemplateKWLoc() const { return TemplateKWLoc; }
178 
getLocation()179   SourceLocation getLocation() const { return getConceptNameLoc(); }
180 
getBeginLoc()181   SourceLocation getBeginLoc() const LLVM_READONLY {
182     // Note that if the qualifier is null the template KW must also be null.
183     if (auto QualifierLoc = getNestedNameSpecifierLoc())
184       return QualifierLoc.getBeginLoc();
185     return getConceptNameInfo().getBeginLoc();
186   }
187 
getEndLoc()188   SourceLocation getEndLoc() const LLVM_READONLY {
189     return getTemplateArgsAsWritten() &&
190                    getTemplateArgsAsWritten()->getRAngleLoc().isValid()
191                ? getTemplateArgsAsWritten()->getRAngleLoc()
192                : getConceptNameInfo().getEndLoc();
193   }
194 
getSourceRange()195   SourceRange getSourceRange() const LLVM_READONLY {
196     return SourceRange(getBeginLoc(), getEndLoc());
197   }
198 
getFoundDecl()199   NamedDecl *getFoundDecl() const {
200     return FoundDecl;
201   }
202 
getNamedConcept()203   ConceptDecl *getNamedConcept() const {
204     return NamedConcept;
205   }
206 
getTemplateArgsAsWritten()207   const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
208     return ArgsAsWritten;
209   }
210 
211   /// \brief Whether or not template arguments were explicitly specified in the
212   /// concept reference (they might not be in type constraints, for example)
hasExplicitTemplateArgs()213   bool hasExplicitTemplateArgs() const {
214     return ArgsAsWritten != nullptr;
215   }
216 
217   void print(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const;
218   void dump() const;
219   void dump(llvm::raw_ostream &) const;
220 };
221 
222 /// Models the abbreviated syntax to constrain a template type parameter:
223 ///   template <convertible_to<string> T> void print(T object);
224 ///             ~~~~~~~~~~~~~~~~~~~~~~
225 /// Semantically, this adds an "immediately-declared constraint" with extra arg:
226 ///    requires convertible_to<T, string>
227 ///
228 /// In the C++ grammar, a type-constraint is also used for auto types:
229 ///    convertible_to<string> auto X = ...;
230 /// We do *not* model these as TypeConstraints, but AutoType(Loc) directly.
231 class TypeConstraint {
232   /// \brief The immediately-declared constraint expression introduced by this
233   /// type-constraint.
234   Expr *ImmediatelyDeclaredConstraint = nullptr;
235   ConceptReference *ConceptRef;
236 
237 public:
TypeConstraint(ConceptReference * ConceptRef,Expr * ImmediatelyDeclaredConstraint)238   TypeConstraint(ConceptReference *ConceptRef,
239                  Expr *ImmediatelyDeclaredConstraint)
240       : ImmediatelyDeclaredConstraint(ImmediatelyDeclaredConstraint),
241         ConceptRef(ConceptRef) {}
242 
243   /// \brief Get the immediately-declared constraint expression introduced by
244   /// this type-constraint, that is - the constraint expression that is added to
245   /// the associated constraints of the enclosing declaration in practice.
getImmediatelyDeclaredConstraint()246   Expr *getImmediatelyDeclaredConstraint() const {
247     return ImmediatelyDeclaredConstraint;
248   }
249 
getConceptReference()250   ConceptReference *getConceptReference() const { return ConceptRef; }
251 
252   // FIXME: Instead of using these concept related functions the callers should
253   // directly work with the corresponding ConceptReference.
getNamedConcept()254   ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }
255 
getConceptNameLoc()256   SourceLocation getConceptNameLoc() const {
257     return ConceptRef->getConceptNameLoc();
258   }
259 
hasExplicitTemplateArgs()260   bool hasExplicitTemplateArgs() const {
261     return ConceptRef->hasExplicitTemplateArgs();
262   }
263 
getTemplateArgsAsWritten()264   const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
265     return ConceptRef->getTemplateArgsAsWritten();
266   }
267 
getTemplateKWLoc()268   SourceLocation getTemplateKWLoc() const {
269     return ConceptRef->getTemplateKWLoc();
270   }
271 
getFoundDecl()272   NamedDecl *getFoundDecl() const { return ConceptRef->getFoundDecl(); }
273 
getNestedNameSpecifierLoc()274   const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
275     return ConceptRef->getNestedNameSpecifierLoc();
276   }
277 
getConceptNameInfo()278   const DeclarationNameInfo &getConceptNameInfo() const {
279     return ConceptRef->getConceptNameInfo();
280   }
281 
print(llvm::raw_ostream & OS,const PrintingPolicy & Policy)282   void print(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const {
283     ConceptRef->print(OS, Policy);
284   }
285 };
286 
287 } // clang
288 
289 #endif // LLVM_CLANG_AST_ASTCONCEPT_H
290