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