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