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