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