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