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 // 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
137 child_range children() {
138 return child_range(child_iterator(), child_iterator());
139 }
140 const_child_range children() const {
141 return const_child_range(const_child_iterator(), const_child_iterator());
142 }
143};
144
145namespace concepts {
146
147/// \brief A static requirement that can be used in a requires-expression to
148/// check properties of types and expression.
149class Requirement {
150public:
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 };
154private:
155 const RequirementKind Kind;
156 // FIXME: use RequirementDependence to model dependence?
157 bool Dependent : 1;
158 bool ContainsUnexpandedParameterPack : 1;
159 bool Satisfied : 1;
160public:
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) :
172 Kind(Kind), Dependent(IsDependent),
173 ContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack),
174 Satisfied(IsSatisfied) {}
175
176 RequirementKind getKind() const { return Kind; }
177
178 bool isSatisfied() const {
179 assert(!Dependent &&
180 "isSatisfied can only be called on non-dependent requirements.");
181 return Satisfied;
182 }
183
184 void setSatisfied(bool IsSatisfied) {
185 assert(!Dependent &&
186 "setSatisfied can only be called on non-dependent requirements.");
187 Satisfied = IsSatisfied;
188 }
189
190 void setDependent(bool IsDependent) { Dependent = IsDependent; }
191 bool isDependent() const { return Dependent; }
192
193 void setContainsUnexpandedParameterPack(bool Contains) {
194 ContainsUnexpandedParameterPack = Contains;
195 }
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).
203class TypeRequirement : public Requirement {
204public:
205 enum SatisfactionStatus {
206 SS_Dependent,
207 SS_SubstitutionFailure,
208 SS_Satisfied
209 };
210private:
211 llvm::PointerUnion<SubstitutionDiagnostic *, TypeSourceInfo *> Value;
212 SatisfactionStatus Status;
213public:
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.
225 TypeRequirement(SubstitutionDiagnostic *Diagnostic) :
226 Requirement(RK_Type, false, false, false), Value(Diagnostic),
227 Status(SS_SubstitutionFailure) {}
228
229 SatisfactionStatus getSatisfactionStatus() const { return Status; }
230 void setSatisfactionStatus(SatisfactionStatus Status) {
231 this->Status = Status;
232 }
233
234 bool isSubstitutionFailure() const {
235 return Status == SS_SubstitutionFailure;
236 }
237
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
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
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).
258class ExprRequirement : public Requirement {
259public:
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.
278 ReturnTypeRequirement() : TypeConstraintInfo(nullptr, 0) {}
279
280 /// \brief A return type requirement was specified but it was a
281 /// substitution failure.
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
293 bool isDependent() const {
294 return TypeConstraintInfo.getInt();
295 }
296
297 bool containsUnexpandedParameterPack() const {
298 if (!isTypeConstraint())
299 return false;
300 return getTypeConstraintTemplateParameterList()
301 ->containsUnexpandedParameterPack();
302 }
303
304 bool isEmpty() const {
305 return TypeConstraintInfo.getPointer().isNull();
306 }
307
308 bool isSubstitutionFailure() const {
309 return !isEmpty() &&
310 TypeConstraintInfo.getPointer().is<SubstitutionDiagnostic *>();
311 }
312
313 bool isTypeConstraint() const {
314 return !isEmpty() &&
315 TypeConstraintInfo.getPointer().is<TemplateParameterList *>();
316 }
317
318 SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
319 assert(isSubstitutionFailure());
320 return TypeConstraintInfo.getPointer().get<SubstitutionDiagnostic *>();
321 }
322
323 const TypeConstraint *getTypeConstraint() const;
324
325 TemplateParameterList *getTypeConstraintTemplateParameterList() const {
326 assert(isTypeConstraint());
327 return TypeConstraintInfo.getPointer().get<TemplateParameterList *>();
328 }
329 };
330private:
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;
336public:
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
364 bool isSimple() const { return getKind() == RK_Simple; }
365 bool isCompound() const { return getKind() == RK_Compound; }
366
367 bool hasNoexceptRequirement() const { return NoexceptLoc.isValid(); }
368 SourceLocation getNoexceptLoc() const { return NoexceptLoc; }
369
370 SatisfactionStatus getSatisfactionStatus() const { return Status; }
371
372 bool isExprSubstitutionFailure() const {
373 return Status == SS_ExprSubstitutionFailure;
374 }
375
376 const ReturnTypeRequirement &getReturnTypeRequirement() const {
377 return TypeReq;
378 }
379
380 ConceptSpecializationExpr *
381 getReturnTypeRequirementSubstitutedConstraintExpr() const {
382 assert(Status >= SS_TypeRequirementSubstitutionFailure);
383 return SubstitutedConstraintExpr;
384 }
385
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
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
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).
407class NestedRequirement : public Requirement {
408 llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> Value;
409 const ASTConstraintSatisfaction *Satisfaction = nullptr;
410
411public:
412 friend ASTStmtReader;
413 friend ASTStmtWriter;
414
415 NestedRequirement(SubstitutionDiagnostic *SubstDiag) :
416 Requirement(RK_Nested, /*Dependent=*/false,
417 /*ContainsUnexpandedParameterPack*/false,
418 /*Satisfied=*/false), Value(SubstDiag) {}
419
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
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
437 bool isSubstitutionFailure() const {
438 return Value.is<SubstitutionDiagnostic *>();
439 }
440
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
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
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
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 [...]
475class 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
486 unsigned numTrailingObjects(OverloadToken<ParmVarDecl *>) const {
487 return NumLocalParameters;
488 }
489
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
502public:
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
512 ArrayRef<ParmVarDecl *> getLocalParameters() const {
513 return {getTrailingObjects<ParmVarDecl *>(), NumLocalParameters};
514 }
515
516 RequiresExprBodyDecl *getBody() const { return Body; }
517
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.
524 bool isSatisfied() const {
525 assert(!isValueDependent()
526 && "isSatisfied called on a dependent RequiresExpr");
527 return RequiresExprBits.IsSatisfied;
528 }
529
530 SourceLocation getRequiresKWLoc() const {
531 return RequiresExprBits.RequiresKWLoc;
532 }
533
534 SourceLocation getRBraceLoc() const { return RBraceLoc; }
535
536 static bool classof(const Stmt *T) {
537 return T->getStmtClass() == RequiresExprClass;
538 }
539
540 SourceLocation getBeginLoc() const LLVM_READONLY {
541 return RequiresExprBits.RequiresKWLoc;
542 }
543 SourceLocation getEndLoc() const LLVM_READONLY {
544 return RBraceLoc;
545 }
546
547 // Iterators
548 child_range children() {
549 return child_range(child_iterator(), child_iterator());
550 }
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