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