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