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