xref: /freebsd/contrib/llvm-project/clang/include/clang/AST/ExprConcepts.h (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
1 //===- ExprConcepts.h - C++2a Concepts expressions --------------*- 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 /// Defines Expressions and AST nodes for C++2a concepts.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_CLANG_AST_EXPRCONCEPTS_H
15 #define LLVM_CLANG_AST_EXPRCONCEPTS_H
16 
17 #include "clang/AST/ASTContext.h"
18 #include "clang/AST/ASTConcept.h"
19 #include "clang/AST/Decl.h"
20 #include "clang/AST/DeclarationName.h"
21 #include "clang/AST/DeclTemplate.h"
22 #include "clang/AST/Expr.h"
23 #include "clang/AST/NestedNameSpecifier.h"
24 #include "clang/AST/TemplateBase.h"
25 #include "clang/AST/Type.h"
26 #include "clang/Basic/SourceLocation.h"
27 #include "llvm/Support/TrailingObjects.h"
28 #include <utility>
29 #include <string>
30 
31 namespace clang {
32 class ASTStmtReader;
33 class ASTStmtWriter;
34 
35 /// \brief Represents the specialization of a concept - evaluates to a prvalue
36 /// of type bool.
37 ///
38 /// According to C++2a [expr.prim.id]p3 an id-expression that denotes the
39 /// specialization of a concept results in a prvalue of type bool.
40 class ConceptSpecializationExpr final : public Expr, public ConceptReference,
41       private llvm::TrailingObjects<ConceptSpecializationExpr,
42                                     TemplateArgument> {
43   friend class ASTStmtReader;
44   friend TrailingObjects;
45 public:
46   using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
47 
48 protected:
49   /// \brief The number of template arguments in the tail-allocated list of
50   /// converted template arguments.
51   unsigned NumTemplateArgs;
52 
53   /// \brief Information about the satisfaction of the named concept with the
54   /// given arguments. If this expression is value dependent, this is to be
55   /// ignored.
56   ASTConstraintSatisfaction *Satisfaction;
57 
58   ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS,
59                             SourceLocation TemplateKWLoc,
60                             DeclarationNameInfo ConceptNameInfo,
61                             NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
62                             const ASTTemplateArgumentListInfo *ArgsAsWritten,
63                             ArrayRef<TemplateArgument> ConvertedArgs,
64                             const ConstraintSatisfaction *Satisfaction);
65 
66   ConceptSpecializationExpr(const ASTContext &C, ConceptDecl *NamedConcept,
67                             ArrayRef<TemplateArgument> ConvertedArgs,
68                             const ConstraintSatisfaction *Satisfaction,
69                             bool Dependent,
70                             bool ContainsUnexpandedParameterPack);
71 
72   ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);
73 
74 public:
75 
76   static ConceptSpecializationExpr *
77   Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
78          SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
79          NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
80          const ASTTemplateArgumentListInfo *ArgsAsWritten,
81          ArrayRef<TemplateArgument> ConvertedArgs,
82          const ConstraintSatisfaction *Satisfaction);
83 
84   static ConceptSpecializationExpr *
85   Create(const ASTContext &C, ConceptDecl *NamedConcept,
86          ArrayRef<TemplateArgument> ConvertedArgs,
87          const ConstraintSatisfaction *Satisfaction,
88          bool Dependent,
89          bool ContainsUnexpandedParameterPack);
90 
91   static ConceptSpecializationExpr *
92   Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
93 
94   ArrayRef<TemplateArgument> getTemplateArguments() const {
95     return ArrayRef<TemplateArgument>(getTrailingObjects<TemplateArgument>(),
96                                       NumTemplateArgs);
97   }
98 
99   /// \brief Set new template arguments for this concept specialization.
100   void setTemplateArguments(ArrayRef<TemplateArgument> Converted);
101 
102   /// \brief Whether or not the concept with the given arguments was satisfied
103   /// when the expression was created.
104   /// The expression must not be dependent.
105   bool isSatisfied() const {
106     assert(!isValueDependent()
107            && "isSatisfied called on a dependent ConceptSpecializationExpr");
108     return Satisfaction->IsSatisfied;
109   }
110 
111   /// \brief Get elaborated satisfaction info about the template arguments'
112   /// satisfaction of the named concept.
113   /// The expression must not be dependent.
114   const ASTConstraintSatisfaction &getSatisfaction() const {
115     assert(!isValueDependent()
116            && "getSatisfaction called on dependent ConceptSpecializationExpr");
117     return *Satisfaction;
118   }
119 
120   static bool classof(const Stmt *T) {
121     return T->getStmtClass() == ConceptSpecializationExprClass;
122   }
123 
124   SourceLocation getBeginLoc() const LLVM_READONLY {
125     return ConceptName.getBeginLoc();
126   }
127 
128   SourceLocation getEndLoc() const LLVM_READONLY {
129     return ArgsAsWritten->RAngleLoc;
130   }
131 
132   // Iterators
133   child_range children() {
134     return child_range(child_iterator(), child_iterator());
135   }
136   const_child_range children() const {
137     return const_child_range(const_child_iterator(), const_child_iterator());
138   }
139 };
140 
141 namespace concepts {
142 
143 /// \brief A static requirement that can be used in a requires-expression to
144 /// check properties of types and expression.
145 class Requirement {
146 public:
147   // Note - simple and compound requirements are both represented by the same
148   // class (ExprRequirement).
149   enum RequirementKind { RK_Type, RK_Simple, RK_Compound, RK_Nested };
150 private:
151   const RequirementKind Kind;
152   // FIXME: use RequirementDependence to model dependence?
153   bool Dependent : 1;
154   bool ContainsUnexpandedParameterPack : 1;
155   bool Satisfied : 1;
156 public:
157   struct SubstitutionDiagnostic {
158     StringRef SubstitutedEntity;
159     // FIXME: Store diagnostics semantically and not as prerendered strings.
160     //  Fixing this probably requires serialization of PartialDiagnostic
161     //  objects.
162     SourceLocation DiagLoc;
163     StringRef DiagMessage;
164   };
165 
166   Requirement(RequirementKind Kind, bool IsDependent,
167               bool ContainsUnexpandedParameterPack, bool IsSatisfied = true) :
168       Kind(Kind), Dependent(IsDependent),
169       ContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack),
170       Satisfied(IsSatisfied) {}
171 
172   RequirementKind getKind() const { return Kind; }
173 
174   bool isSatisfied() const {
175     assert(!Dependent &&
176            "isSatisfied can only be called on non-dependent requirements.");
177     return Satisfied;
178   }
179 
180   void setSatisfied(bool IsSatisfied) {
181     assert(!Dependent &&
182            "setSatisfied can only be called on non-dependent requirements.");
183     Satisfied = IsSatisfied;
184   }
185 
186   void setDependent(bool IsDependent) { Dependent = IsDependent; }
187   bool isDependent() const { return Dependent; }
188 
189   void setContainsUnexpandedParameterPack(bool Contains) {
190     ContainsUnexpandedParameterPack = Contains;
191   }
192   bool containsUnexpandedParameterPack() const {
193     return ContainsUnexpandedParameterPack;
194   }
195 };
196 
197 /// \brief A requires-expression requirement which queries the existence of a
198 /// type name or type template specialization ('type' requirements).
199 class TypeRequirement : public Requirement {
200 public:
201   enum SatisfactionStatus {
202       SS_Dependent,
203       SS_SubstitutionFailure,
204       SS_Satisfied
205   };
206 private:
207   llvm::PointerUnion<SubstitutionDiagnostic *, TypeSourceInfo *> Value;
208   SatisfactionStatus Status;
209 public:
210   friend ASTStmtReader;
211   friend ASTStmtWriter;
212 
213   /// \brief Construct a type requirement from a type. If the given type is not
214   /// dependent, this indicates that the type exists and the requirement will be
215   /// satisfied. Otherwise, the SubstitutionDiagnostic constructor is to be
216   /// used.
217   TypeRequirement(TypeSourceInfo *T);
218 
219   /// \brief Construct a type requirement when the nested name specifier is
220   /// invalid due to a bad substitution. The requirement is unsatisfied.
221   TypeRequirement(SubstitutionDiagnostic *Diagnostic) :
222       Requirement(RK_Type, false, false, false), Value(Diagnostic),
223       Status(SS_SubstitutionFailure) {}
224 
225   SatisfactionStatus getSatisfactionStatus() const { return Status; }
226   void setSatisfactionStatus(SatisfactionStatus Status) {
227     this->Status = Status;
228   }
229 
230   bool isSubstitutionFailure() const {
231     return Status == SS_SubstitutionFailure;
232   }
233 
234   SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
235     assert(Status == SS_SubstitutionFailure &&
236            "Attempted to get substitution diagnostic when there has been no "
237            "substitution failure.");
238     return Value.get<SubstitutionDiagnostic *>();
239   }
240 
241   TypeSourceInfo *getType() const {
242     assert(!isSubstitutionFailure() &&
243            "Attempted to get type when there has been a substitution failure.");
244     return Value.get<TypeSourceInfo *>();
245   }
246 
247   static bool classof(const Requirement *R) {
248     return R->getKind() == RK_Type;
249   }
250 };
251 
252 /// \brief A requires-expression requirement which queries the validity and
253 /// properties of an expression ('simple' and 'compound' requirements).
254 class ExprRequirement : public Requirement {
255 public:
256   enum SatisfactionStatus {
257       SS_Dependent,
258       SS_ExprSubstitutionFailure,
259       SS_NoexceptNotMet,
260       SS_TypeRequirementSubstitutionFailure,
261       SS_ConstraintsNotSatisfied,
262       SS_Satisfied
263   };
264   class ReturnTypeRequirement {
265       llvm::PointerIntPair<
266           llvm::PointerUnion<TemplateParameterList *, SubstitutionDiagnostic *>,
267           1, bool>
268           TypeConstraintInfo;
269   public:
270       friend ASTStmtReader;
271       friend ASTStmtWriter;
272 
273       /// \brief No return type requirement was specified.
274       ReturnTypeRequirement() : TypeConstraintInfo(nullptr, 0) {}
275 
276       /// \brief A return type requirement was specified but it was a
277       /// substitution failure.
278       ReturnTypeRequirement(SubstitutionDiagnostic *SubstDiag) :
279           TypeConstraintInfo(SubstDiag, 0) {}
280 
281       /// \brief A 'type constraint' style return type requirement.
282       /// \param TPL an invented template parameter list containing a single
283       /// type parameter with a type-constraint.
284       // TODO: Can we maybe not save the whole template parameter list and just
285       //  the type constraint? Saving the whole TPL makes it easier to handle in
286       //  serialization but is less elegant.
287       ReturnTypeRequirement(TemplateParameterList *TPL);
288 
289       bool isDependent() const {
290         return TypeConstraintInfo.getInt();
291       }
292 
293       bool containsUnexpandedParameterPack() const {
294         if (!isTypeConstraint())
295           return false;
296         return getTypeConstraintTemplateParameterList()
297                 ->containsUnexpandedParameterPack();
298       }
299 
300       bool isEmpty() const {
301         return TypeConstraintInfo.getPointer().isNull();
302       }
303 
304       bool isSubstitutionFailure() const {
305         return !isEmpty() &&
306             TypeConstraintInfo.getPointer().is<SubstitutionDiagnostic *>();
307       }
308 
309       bool isTypeConstraint() const {
310         return !isEmpty() &&
311             TypeConstraintInfo.getPointer().is<TemplateParameterList *>();
312       }
313 
314       SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
315         assert(isSubstitutionFailure());
316         return TypeConstraintInfo.getPointer().get<SubstitutionDiagnostic *>();
317       }
318 
319       const TypeConstraint *getTypeConstraint() const;
320 
321       TemplateParameterList *getTypeConstraintTemplateParameterList() const {
322         assert(isTypeConstraint());
323         return TypeConstraintInfo.getPointer().get<TemplateParameterList *>();
324       }
325   };
326 private:
327   llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> Value;
328   SourceLocation NoexceptLoc; // May be empty if noexcept wasn't specified.
329   ReturnTypeRequirement TypeReq;
330   ConceptSpecializationExpr *SubstitutedConstraintExpr;
331   SatisfactionStatus Status;
332 public:
333   friend ASTStmtReader;
334   friend ASTStmtWriter;
335 
336   /// \brief Construct a compound requirement.
337   /// \param E the expression which is checked by this requirement.
338   /// \param IsSimple whether this was a simple requirement in source.
339   /// \param NoexceptLoc the location of the noexcept keyword, if it was
340   /// specified, otherwise an empty location.
341   /// \param Req the requirement for the type of the checked expression.
342   /// \param Status the satisfaction status of this requirement.
343   ExprRequirement(
344       Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
345       ReturnTypeRequirement Req, SatisfactionStatus Status,
346       ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr);
347 
348   /// \brief Construct a compound requirement whose expression was a
349   /// substitution failure. The requirement is not satisfied.
350   /// \param E the diagnostic emitted while instantiating the original
351   /// expression.
352   /// \param IsSimple whether this was a simple requirement in source.
353   /// \param NoexceptLoc the location of the noexcept keyword, if it was
354   /// specified, otherwise an empty location.
355   /// \param Req the requirement for the type of the checked expression (omit
356   /// if no requirement was specified).
357   ExprRequirement(SubstitutionDiagnostic *E, bool IsSimple,
358                   SourceLocation NoexceptLoc, ReturnTypeRequirement Req = {});
359 
360   bool isSimple() const { return getKind() == RK_Simple; }
361   bool isCompound() const { return getKind() == RK_Compound; }
362 
363   bool hasNoexceptRequirement() const { return NoexceptLoc.isValid(); }
364   SourceLocation getNoexceptLoc() const { return NoexceptLoc; }
365 
366   SatisfactionStatus getSatisfactionStatus() const { return Status; }
367 
368   bool isExprSubstitutionFailure() const {
369     return Status == SS_ExprSubstitutionFailure;
370   }
371 
372   const ReturnTypeRequirement &getReturnTypeRequirement() const {
373     return TypeReq;
374   }
375 
376   ConceptSpecializationExpr *
377   getReturnTypeRequirementSubstitutedConstraintExpr() const {
378     assert(Status >= SS_TypeRequirementSubstitutionFailure);
379     return SubstitutedConstraintExpr;
380   }
381 
382   SubstitutionDiagnostic *getExprSubstitutionDiagnostic() const {
383     assert(isExprSubstitutionFailure() &&
384            "Attempted to get expression substitution diagnostic when there has "
385            "been no expression substitution failure");
386     return Value.get<SubstitutionDiagnostic *>();
387   }
388 
389   Expr *getExpr() const {
390     assert(!isExprSubstitutionFailure() &&
391            "ExprRequirement has no expression because there has been a "
392            "substitution failure.");
393     return Value.get<Expr *>();
394   }
395 
396   static bool classof(const Requirement *R) {
397     return R->getKind() == RK_Compound || R->getKind() == RK_Simple;
398   }
399 };
400 
401 /// \brief A requires-expression requirement which is satisfied when a general
402 /// constraint expression is satisfied ('nested' requirements).
403 class NestedRequirement : public Requirement {
404   llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> Value;
405   const ASTConstraintSatisfaction *Satisfaction = nullptr;
406 
407 public:
408   friend ASTStmtReader;
409   friend ASTStmtWriter;
410 
411   NestedRequirement(SubstitutionDiagnostic *SubstDiag) :
412       Requirement(RK_Nested, /*Dependent=*/false,
413                   /*ContainsUnexpandedParameterPack*/false,
414                   /*Satisfied=*/false), Value(SubstDiag) {}
415 
416   NestedRequirement(Expr *Constraint) :
417       Requirement(RK_Nested, /*Dependent=*/true,
418                   Constraint->containsUnexpandedParameterPack()),
419       Value(Constraint) {
420     assert(Constraint->isInstantiationDependent() &&
421            "Nested requirement with non-dependent constraint must be "
422            "constructed with a ConstraintSatisfaction object");
423   }
424 
425   NestedRequirement(ASTContext &C, Expr *Constraint,
426                     const ConstraintSatisfaction &Satisfaction) :
427       Requirement(RK_Nested, Constraint->isInstantiationDependent(),
428                   Constraint->containsUnexpandedParameterPack(),
429                   Satisfaction.IsSatisfied),
430       Value(Constraint),
431       Satisfaction(ASTConstraintSatisfaction::Create(C, Satisfaction)) {}
432 
433   bool isSubstitutionFailure() const {
434     return Value.is<SubstitutionDiagnostic *>();
435   }
436 
437   SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
438     assert(isSubstitutionFailure() &&
439            "getSubstitutionDiagnostic() may not be called when there was no "
440            "substitution failure.");
441     return Value.get<SubstitutionDiagnostic *>();
442   }
443 
444   Expr *getConstraintExpr() const {
445     assert(!isSubstitutionFailure() && "getConstraintExpr() may not be called "
446                                        "on nested requirements with "
447                                        "substitution failures.");
448     return Value.get<Expr *>();
449   }
450 
451   const ASTConstraintSatisfaction &getConstraintSatisfaction() const {
452     assert(!isSubstitutionFailure() && "getConstraintSatisfaction() may not be "
453                                        "called on nested requirements with "
454                                        "substitution failures.");
455     return *Satisfaction;
456   }
457 
458   static bool classof(const Requirement *R) {
459     return R->getKind() == RK_Nested;
460   }
461 };
462 
463 } // namespace concepts
464 
465 /// C++2a [expr.prim.req]:
466 ///     A requires-expression provides a concise way to express requirements on
467 ///     template arguments. A requirement is one that can be checked by name
468 ///     lookup (6.4) or by checking properties of types and expressions.
469 ///     [...]
470 ///     A requires-expression is a prvalue of type bool [...]
471 class RequiresExpr final : public Expr,
472     llvm::TrailingObjects<RequiresExpr, ParmVarDecl *,
473                           concepts::Requirement *> {
474   friend TrailingObjects;
475   friend class ASTStmtReader;
476 
477   unsigned NumLocalParameters;
478   unsigned NumRequirements;
479   RequiresExprBodyDecl *Body;
480   SourceLocation RBraceLoc;
481 
482   unsigned numTrailingObjects(OverloadToken<ParmVarDecl *>) const {
483     return NumLocalParameters;
484   }
485 
486   unsigned numTrailingObjects(OverloadToken<concepts::Requirement *>) const {
487     return NumRequirements;
488   }
489 
490   RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc,
491                RequiresExprBodyDecl *Body,
492                ArrayRef<ParmVarDecl *> LocalParameters,
493                ArrayRef<concepts::Requirement *> Requirements,
494                SourceLocation RBraceLoc);
495   RequiresExpr(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters,
496                unsigned NumRequirements);
497 
498 public:
499   static RequiresExpr *
500   Create(ASTContext &C, SourceLocation RequiresKWLoc,
501          RequiresExprBodyDecl *Body, ArrayRef<ParmVarDecl *> LocalParameters,
502          ArrayRef<concepts::Requirement *> Requirements,
503          SourceLocation RBraceLoc);
504   static RequiresExpr *
505   Create(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters,
506          unsigned NumRequirements);
507 
508   ArrayRef<ParmVarDecl *> getLocalParameters() const {
509     return {getTrailingObjects<ParmVarDecl *>(), NumLocalParameters};
510   }
511 
512   RequiresExprBodyDecl *getBody() const { return Body; }
513 
514   ArrayRef<concepts::Requirement *> getRequirements() const {
515     return {getTrailingObjects<concepts::Requirement *>(), NumRequirements};
516   }
517 
518   /// \brief Whether or not the requires clause is satisfied.
519   /// The expression must not be dependent.
520   bool isSatisfied() const {
521     assert(!isValueDependent()
522            && "isSatisfied called on a dependent RequiresExpr");
523     return RequiresExprBits.IsSatisfied;
524   }
525 
526   SourceLocation getRequiresKWLoc() const {
527     return RequiresExprBits.RequiresKWLoc;
528   }
529 
530   SourceLocation getRBraceLoc() const { return RBraceLoc; }
531 
532   static bool classof(const Stmt *T) {
533     return T->getStmtClass() == RequiresExprClass;
534   }
535 
536   SourceLocation getBeginLoc() const LLVM_READONLY {
537     return RequiresExprBits.RequiresKWLoc;
538   }
539   SourceLocation getEndLoc() const LLVM_READONLY {
540     return RBraceLoc;
541   }
542 
543   // Iterators
544   child_range children() {
545     return child_range(child_iterator(), child_iterator());
546   }
547   const_child_range children() const {
548     return const_child_range(const_child_iterator(), const_child_iterator());
549   }
550 };
551 
552 } // namespace clang
553 
554 #endif // LLVM_CLANG_AST_EXPRCONCEPTS_H
555