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