1 //===--- ASTConcept.h - Concepts Related AST Data Structures ----*- 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 /// \brief This file provides AST data structures related to concepts. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_CLANG_AST_ASTCONCEPT_H 15 #define LLVM_CLANG_AST_ASTCONCEPT_H 16 17 #include "clang/AST/DeclarationName.h" 18 #include "clang/AST/NestedNameSpecifier.h" 19 #include "clang/AST/TemplateBase.h" 20 #include "clang/Basic/SourceLocation.h" 21 #include "llvm/ADT/FoldingSet.h" 22 #include "llvm/ADT/PointerUnion.h" 23 #include "llvm/ADT/SmallVector.h" 24 #include <utility> 25 26 namespace clang { 27 28 class ConceptDecl; 29 class Expr; 30 class NamedDecl; 31 struct PrintingPolicy; 32 33 /// The result of a constraint satisfaction check, containing the necessary 34 /// information to diagnose an unsatisfied constraint. 35 class ConstraintSatisfaction : public llvm::FoldingSetNode { 36 // The template-like entity that 'owns' the constraint checked here (can be a 37 // constrained entity or a concept). 38 const NamedDecl *ConstraintOwner = nullptr; 39 llvm::SmallVector<TemplateArgument, 4> TemplateArgs; 40 41 public: 42 43 ConstraintSatisfaction() = default; 44 45 ConstraintSatisfaction(const NamedDecl *ConstraintOwner, 46 ArrayRef<TemplateArgument> TemplateArgs) 47 : ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs) {} 48 49 using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>; 50 using Detail = llvm::PointerUnion<Expr *, SubstitutionDiagnostic *>; 51 52 bool IsSatisfied = false; 53 bool ContainsErrors = false; 54 55 /// \brief The substituted constraint expr, if the template arguments could be 56 /// substituted into them, or a diagnostic if substitution resulted in an 57 /// invalid expression. 58 llvm::SmallVector<Detail, 4> Details; 59 60 void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) { 61 Profile(ID, C, ConstraintOwner, TemplateArgs); 62 } 63 64 static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C, 65 const NamedDecl *ConstraintOwner, 66 ArrayRef<TemplateArgument> TemplateArgs); 67 68 bool HasSubstitutionFailure() { 69 for (const auto &Detail : Details) 70 if (Detail.dyn_cast<SubstitutionDiagnostic *>()) 71 return true; 72 return false; 73 } 74 }; 75 76 /// Pairs of unsatisfied atomic constraint expressions along with the 77 /// substituted constraint expr, if the template arguments could be 78 /// substituted into them, or a diagnostic if substitution resulted in 79 /// an invalid expression. 80 using UnsatisfiedConstraintRecord = 81 llvm::PointerUnion<Expr *, std::pair<SourceLocation, StringRef> *>; 82 83 /// \brief The result of a constraint satisfaction check, containing the 84 /// necessary information to diagnose an unsatisfied constraint. 85 /// 86 /// This is safe to store in an AST node, as opposed to ConstraintSatisfaction. 87 struct ASTConstraintSatisfaction final : 88 llvm::TrailingObjects<ASTConstraintSatisfaction, 89 UnsatisfiedConstraintRecord> { 90 std::size_t NumRecords; 91 bool IsSatisfied : 1; 92 bool ContainsErrors : 1; 93 94 const UnsatisfiedConstraintRecord *begin() const { 95 return getTrailingObjects<UnsatisfiedConstraintRecord>(); 96 } 97 98 const UnsatisfiedConstraintRecord *end() const { 99 return getTrailingObjects<UnsatisfiedConstraintRecord>() + NumRecords; 100 } 101 102 ASTConstraintSatisfaction(const ASTContext &C, 103 const ConstraintSatisfaction &Satisfaction); 104 ASTConstraintSatisfaction(const ASTContext &C, 105 const ASTConstraintSatisfaction &Satisfaction); 106 107 static ASTConstraintSatisfaction * 108 Create(const ASTContext &C, const ConstraintSatisfaction &Satisfaction); 109 static ASTConstraintSatisfaction * 110 Rebuild(const ASTContext &C, const ASTConstraintSatisfaction &Satisfaction); 111 }; 112 113 /// A reference to a concept and its template args, as it appears in the code. 114 /// 115 /// Examples: 116 /// template <int X> requires is_even<X> int half = X/2; 117 /// ~~~~~~~~~~ (in ConceptSpecializationExpr) 118 /// 119 /// std::input_iterator auto I = Container.begin(); 120 /// ~~~~~~~~~~~~~~~~~~~ (in AutoTypeLoc) 121 /// 122 /// template <std::derives_from<Expr> T> void dump(); 123 /// ~~~~~~~~~~~~~~~~~~~~~~~ (in TemplateTypeParmDecl) 124 class ConceptReference { 125 // \brief The optional nested name specifier used when naming the concept. 126 NestedNameSpecifierLoc NestedNameSpec; 127 128 /// \brief The location of the template keyword, if specified when naming the 129 /// concept. 130 SourceLocation TemplateKWLoc; 131 132 /// \brief The concept name used. 133 DeclarationNameInfo ConceptName; 134 135 /// \brief The declaration found by name lookup when the expression was 136 /// created. 137 /// Can differ from NamedConcept when, for example, the concept was found 138 /// through a UsingShadowDecl. 139 NamedDecl *FoundDecl; 140 141 /// \brief The concept named. 142 ConceptDecl *NamedConcept; 143 144 /// \brief The template argument list source info used to specialize the 145 /// concept. 146 const ASTTemplateArgumentListInfo *ArgsAsWritten; 147 148 ConceptReference(NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, 149 DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, 150 ConceptDecl *NamedConcept, 151 const ASTTemplateArgumentListInfo *ArgsAsWritten) 152 : NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc), 153 ConceptName(ConceptNameInfo), FoundDecl(FoundDecl), 154 NamedConcept(NamedConcept), ArgsAsWritten(ArgsAsWritten) {} 155 156 public: 157 static ConceptReference * 158 Create(const ASTContext &C, NestedNameSpecifierLoc NNS, 159 SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo, 160 NamedDecl *FoundDecl, ConceptDecl *NamedConcept, 161 const ASTTemplateArgumentListInfo *ArgsAsWritten); 162 163 const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const { 164 return NestedNameSpec; 165 } 166 167 const DeclarationNameInfo &getConceptNameInfo() const { return ConceptName; } 168 169 SourceLocation getConceptNameLoc() const { 170 return getConceptNameInfo().getLoc(); 171 } 172 173 SourceLocation getTemplateKWLoc() const { return TemplateKWLoc; } 174 175 SourceLocation getLocation() const { return getConceptNameLoc(); } 176 177 SourceLocation getBeginLoc() const LLVM_READONLY { 178 // Note that if the qualifier is null the template KW must also be null. 179 if (auto QualifierLoc = getNestedNameSpecifierLoc()) 180 return QualifierLoc.getBeginLoc(); 181 return getConceptNameInfo().getBeginLoc(); 182 } 183 184 SourceLocation getEndLoc() const LLVM_READONLY { 185 return getTemplateArgsAsWritten() && 186 getTemplateArgsAsWritten()->getRAngleLoc().isValid() 187 ? getTemplateArgsAsWritten()->getRAngleLoc() 188 : getConceptNameInfo().getEndLoc(); 189 } 190 191 SourceRange getSourceRange() const LLVM_READONLY { 192 return SourceRange(getBeginLoc(), getEndLoc()); 193 } 194 195 NamedDecl *getFoundDecl() const { 196 return FoundDecl; 197 } 198 199 ConceptDecl *getNamedConcept() const { 200 return NamedConcept; 201 } 202 203 const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const { 204 return ArgsAsWritten; 205 } 206 207 /// \brief Whether or not template arguments were explicitly specified in the 208 /// concept reference (they might not be in type constraints, for example) 209 bool hasExplicitTemplateArgs() const { 210 return ArgsAsWritten != nullptr; 211 } 212 213 void print(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const; 214 void dump() const; 215 void dump(llvm::raw_ostream &) const; 216 }; 217 218 /// Models the abbreviated syntax to constrain a template type parameter: 219 /// template <convertible_to<string> T> void print(T object); 220 /// ~~~~~~~~~~~~~~~~~~~~~~ 221 /// Semantically, this adds an "immediately-declared constraint" with extra arg: 222 /// requires convertible_to<T, string> 223 /// 224 /// In the C++ grammar, a type-constraint is also used for auto types: 225 /// convertible_to<string> auto X = ...; 226 /// We do *not* model these as TypeConstraints, but AutoType(Loc) directly. 227 class TypeConstraint { 228 /// \brief The immediately-declared constraint expression introduced by this 229 /// type-constraint. 230 Expr *ImmediatelyDeclaredConstraint = nullptr; 231 ConceptReference *ConceptRef; 232 233 public: 234 TypeConstraint(ConceptReference *ConceptRef, 235 Expr *ImmediatelyDeclaredConstraint) 236 : ImmediatelyDeclaredConstraint(ImmediatelyDeclaredConstraint), 237 ConceptRef(ConceptRef) {} 238 239 /// \brief Get the immediately-declared constraint expression introduced by 240 /// this type-constraint, that is - the constraint expression that is added to 241 /// the associated constraints of the enclosing declaration in practice. 242 Expr *getImmediatelyDeclaredConstraint() const { 243 return ImmediatelyDeclaredConstraint; 244 } 245 246 ConceptReference *getConceptReference() const { return ConceptRef; } 247 248 // FIXME: Instead of using these concept related functions the callers should 249 // directly work with the corresponding ConceptReference. 250 ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); } 251 252 SourceLocation getConceptNameLoc() const { 253 return ConceptRef->getConceptNameLoc(); 254 } 255 256 bool hasExplicitTemplateArgs() const { 257 return ConceptRef->hasExplicitTemplateArgs(); 258 } 259 260 const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const { 261 return ConceptRef->getTemplateArgsAsWritten(); 262 } 263 264 SourceLocation getTemplateKWLoc() const { 265 return ConceptRef->getTemplateKWLoc(); 266 } 267 268 NamedDecl *getFoundDecl() const { return ConceptRef->getFoundDecl(); } 269 270 const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const { 271 return ConceptRef->getNestedNameSpecifierLoc(); 272 } 273 274 const DeclarationNameInfo &getConceptNameInfo() const { 275 return ConceptRef->getConceptNameInfo(); 276 } 277 278 void print(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const { 279 ConceptRef->print(OS, Policy); 280 } 281 }; 282 283 } // clang 284 285 #endif // LLVM_CLANG_AST_ASTCONCEPT_H 286