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