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