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

source code of clang/include/clang/AST/ExprConcepts.h