1 //===--- HLSLExternalSemaSource.cpp - HLSL Sema Source --------------------===// 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 // 10 //===----------------------------------------------------------------------===// 11 12 #include "clang/Sema/HLSLExternalSemaSource.h" 13 #include "clang/AST/ASTContext.h" 14 #include "clang/AST/Attr.h" 15 #include "clang/AST/Decl.h" 16 #include "clang/AST/DeclCXX.h" 17 #include "clang/AST/Expr.h" 18 #include "clang/AST/Type.h" 19 #include "clang/Basic/SourceLocation.h" 20 #include "clang/Sema/Lookup.h" 21 #include "clang/Sema/Sema.h" 22 #include "clang/Sema/SemaHLSL.h" 23 #include "llvm/ADT/SmallVector.h" 24 #include "llvm/Frontend/HLSL/HLSLResource.h" 25 #include "llvm/Support/ErrorHandling.h" 26 27 #include <functional> 28 29 using namespace clang; 30 using namespace llvm::hlsl; 31 32 static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name); 33 34 namespace { 35 36 struct TemplateParameterListBuilder; 37 38 class BuiltinTypeDeclBuilder { 39 ClassTemplateDecl *Template = nullptr; 40 ClassTemplateDecl *PrevTemplate = nullptr; 41 NamespaceDecl *HLSLNamespace = nullptr; 42 llvm::StringMap<FieldDecl *> Fields; 43 44 public: 45 Sema &SemaRef; 46 CXXRecordDecl *Record = nullptr; 47 friend struct TemplateParameterListBuilder; 48 49 BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R) 50 : SemaRef(SemaRef), Record(R) { 51 Record->startDefinition(); 52 Template = Record->getDescribedClassTemplate(); 53 } 54 55 BuiltinTypeDeclBuilder(Sema &SemaRef, NamespaceDecl *Namespace, 56 StringRef Name) 57 : HLSLNamespace(Namespace), SemaRef(SemaRef) { 58 ASTContext &AST = SemaRef.getASTContext(); 59 IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); 60 61 LookupResult Result(SemaRef, &II, SourceLocation(), Sema::LookupTagName); 62 CXXRecordDecl *PrevDecl = nullptr; 63 if (SemaRef.LookupQualifiedName(Result, HLSLNamespace)) { 64 // Declaration already exists (from precompiled headers) 65 NamedDecl *Found = Result.getFoundDecl(); 66 if (auto *TD = dyn_cast<ClassTemplateDecl>(Found)) { 67 PrevDecl = TD->getTemplatedDecl(); 68 PrevTemplate = TD; 69 } else 70 PrevDecl = dyn_cast<CXXRecordDecl>(Found); 71 assert(PrevDecl && "Unexpected lookup result type."); 72 } 73 74 if (PrevDecl && PrevDecl->isCompleteDefinition()) { 75 Record = PrevDecl; 76 Template = PrevTemplate; 77 return; 78 } 79 80 Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace, 81 SourceLocation(), SourceLocation(), &II, 82 PrevDecl, true); 83 Record->setImplicit(true); 84 Record->setLexicalDeclContext(HLSLNamespace); 85 Record->setHasExternalLexicalStorage(); 86 87 // Don't let anyone derive from built-in types. 88 Record->addAttr(FinalAttr::CreateImplicit(AST, SourceRange(), 89 FinalAttr::Keyword_final)); 90 } 91 92 ~BuiltinTypeDeclBuilder() { 93 if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace) 94 HLSLNamespace->addDecl(Record); 95 } 96 97 CXXRecordDecl *finalizeForwardDeclaration() { 98 // Force the QualType to be generated for the record declaration. In most 99 // cases this will happen naturally when something uses the type the 100 // QualType gets lazily created. Unfortunately, with our injected types if a 101 // type isn't used in a translation unit the QualType may not get 102 // automatically generated before a PCH is generated. To resolve this we 103 // just force that the QualType is generated after we create a forward 104 // declaration. 105 (void)Record->getASTContext().getRecordType(Record); 106 return Record; 107 } 108 109 BuiltinTypeDeclBuilder & 110 addMemberVariable(StringRef Name, QualType Type, llvm::ArrayRef<Attr *> Attrs, 111 AccessSpecifier Access = AccessSpecifier::AS_private) { 112 assert(!Record->isCompleteDefinition() && "record is already complete"); 113 assert(Record->isBeingDefined() && 114 "Definition must be started before adding members!"); 115 ASTContext &AST = Record->getASTContext(); 116 117 IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); 118 TypeSourceInfo *MemTySource = 119 AST.getTrivialTypeSourceInfo(Type, SourceLocation()); 120 auto *Field = FieldDecl::Create( 121 AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource, 122 nullptr, false, InClassInitStyle::ICIS_NoInit); 123 Field->setAccess(Access); 124 Field->setImplicit(true); 125 for (Attr *A : Attrs) { 126 if (A) 127 Field->addAttr(A); 128 } 129 130 Record->addDecl(Field); 131 Fields[Name] = Field; 132 return *this; 133 } 134 135 BuiltinTypeDeclBuilder & 136 addHandleMember(ResourceClass RC, ResourceKind RK, bool IsROV, bool RawBuffer, 137 AccessSpecifier Access = AccessSpecifier::AS_private) { 138 assert(!Record->isCompleteDefinition() && "record is already complete"); 139 140 ASTContext &Ctx = SemaRef.getASTContext(); 141 TypeSourceInfo *ElementTypeInfo = 142 Ctx.getTrivialTypeSourceInfo(getHandleElementType(), SourceLocation()); 143 144 // add handle member with resource type attributes 145 QualType AttributedResTy = QualType(); 146 SmallVector<const Attr *> Attrs = { 147 HLSLResourceClassAttr::CreateImplicit(Ctx, RC), 148 IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr, 149 RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr, 150 ElementTypeInfo 151 ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo) 152 : nullptr}; 153 Attr *ResourceAttr = HLSLResourceAttr::CreateImplicit(Ctx, RK); 154 if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs, 155 AttributedResTy)) 156 addMemberVariable("__handle", AttributedResTy, {ResourceAttr}, Access); 157 return *this; 158 } 159 160 BuiltinTypeDeclBuilder &addDefaultHandleConstructor() { 161 if (Record->isCompleteDefinition()) 162 return *this; 163 ASTContext &AST = Record->getASTContext(); 164 165 QualType ConstructorType = 166 AST.getFunctionType(AST.VoidTy, {}, FunctionProtoType::ExtProtoInfo()); 167 168 CanQualType CanTy = Record->getTypeForDecl()->getCanonicalTypeUnqualified(); 169 DeclarationName Name = AST.DeclarationNames.getCXXConstructorName(CanTy); 170 CXXConstructorDecl *Constructor = CXXConstructorDecl::Create( 171 AST, Record, SourceLocation(), 172 DeclarationNameInfo(Name, SourceLocation()), ConstructorType, 173 AST.getTrivialTypeSourceInfo(ConstructorType, SourceLocation()), 174 ExplicitSpecifier(), false, true, false, 175 ConstexprSpecKind::Unspecified); 176 177 Constructor->setBody(CompoundStmt::Create( 178 AST, {}, FPOptionsOverride(), SourceLocation(), SourceLocation())); 179 Constructor->setAccess(AccessSpecifier::AS_public); 180 Record->addDecl(Constructor); 181 return *this; 182 } 183 184 BuiltinTypeDeclBuilder &addArraySubscriptOperators() { 185 ASTContext &AST = Record->getASTContext(); 186 DeclarationName Subscript = 187 AST.DeclarationNames.getCXXOperatorName(OO_Subscript); 188 189 addHandleAccessFunction(Subscript, /*IsConst=*/true, /*IsRef=*/true); 190 addHandleAccessFunction(Subscript, /*IsConst=*/false, /*IsRef=*/true); 191 return *this; 192 } 193 194 BuiltinTypeDeclBuilder &addLoadMethods() { 195 if (Record->isCompleteDefinition()) 196 return *this; 197 198 ASTContext &AST = Record->getASTContext(); 199 IdentifierInfo &II = AST.Idents.get("Load", tok::TokenKind::identifier); 200 DeclarationName Load(&II); 201 // TODO: We also need versions with status for CheckAccessFullyMapped. 202 addHandleAccessFunction(Load, /*IsConst=*/false, /*IsRef=*/false); 203 204 return *this; 205 } 206 207 FieldDecl *getResourceHandleField() { 208 auto I = Fields.find("__handle"); 209 assert(I != Fields.end() && 210 I->second->getType()->isHLSLAttributedResourceType() && 211 "record does not have resource handle field"); 212 return I->second; 213 } 214 215 QualType getFirstTemplateTypeParam() { 216 assert(Template && "record it not a template"); 217 if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>( 218 Template->getTemplateParameters()->getParam(0))) { 219 return QualType(TTD->getTypeForDecl(), 0); 220 } 221 return QualType(); 222 } 223 224 QualType getHandleElementType() { 225 if (Template) 226 return getFirstTemplateTypeParam(); 227 // TODO: Should we default to VoidTy? Using `i8` is arguably ambiguous. 228 return SemaRef.getASTContext().Char8Ty; 229 } 230 231 BuiltinTypeDeclBuilder &startDefinition() { 232 assert(!Record->isCompleteDefinition() && "record is already complete"); 233 Record->startDefinition(); 234 return *this; 235 } 236 237 BuiltinTypeDeclBuilder &completeDefinition() { 238 assert(!Record->isCompleteDefinition() && "record is already complete"); 239 assert(Record->isBeingDefined() && 240 "Definition must be started before completing it."); 241 242 Record->completeDefinition(); 243 return *this; 244 } 245 246 Expr *getConstantIntExpr(int value) { 247 ASTContext &AST = SemaRef.getASTContext(); 248 return IntegerLiteral::Create( 249 AST, llvm::APInt(AST.getTypeSize(AST.IntTy), value, true), AST.IntTy, 250 SourceLocation()); 251 } 252 253 TemplateParameterListBuilder addTemplateArgumentList(); 254 BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef<StringRef> Names, 255 ConceptDecl *CD); 256 257 // Builtin types methods 258 BuiltinTypeDeclBuilder &addIncrementCounterMethod(); 259 BuiltinTypeDeclBuilder &addDecrementCounterMethod(); 260 BuiltinTypeDeclBuilder &addHandleAccessFunction(DeclarationName &Name, 261 bool IsConst, bool IsRef); 262 BuiltinTypeDeclBuilder &addAppendMethod(); 263 BuiltinTypeDeclBuilder &addConsumeMethod(); 264 }; 265 266 struct TemplateParameterListBuilder { 267 BuiltinTypeDeclBuilder &Builder; 268 llvm::SmallVector<NamedDecl *> Params; 269 270 TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {} 271 272 ~TemplateParameterListBuilder() { finalizeTemplateArgs(); } 273 274 TemplateParameterListBuilder & 275 addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) { 276 assert(!Builder.Record->isCompleteDefinition() && 277 "record is already complete"); 278 ASTContext &AST = Builder.SemaRef.getASTContext(); 279 unsigned Position = static_cast<unsigned>(Params.size()); 280 auto *Decl = TemplateTypeParmDecl::Create( 281 AST, Builder.Record->getDeclContext(), SourceLocation(), 282 SourceLocation(), /* TemplateDepth */ 0, Position, 283 &AST.Idents.get(Name, tok::TokenKind::identifier), 284 /* Typename */ true, 285 /* ParameterPack */ false, 286 /* HasTypeConstraint*/ false); 287 if (!DefaultValue.isNull()) 288 Decl->setDefaultArgument(AST, 289 Builder.SemaRef.getTrivialTemplateArgumentLoc( 290 DefaultValue, QualType(), SourceLocation())); 291 292 Params.emplace_back(Decl); 293 return *this; 294 } 295 296 // The concept specialization expression (CSE) constructed in 297 // constructConceptSpecializationExpr is constructed so that it 298 // matches the CSE that is constructed when parsing the below C++ code: 299 // 300 // template<typename T> 301 // concept is_typed_resource_element_compatible = 302 // __builtin_hlsl_typed_resource_element_compatible<T> 303 // 304 // template<typename element_type> requires 305 // is_typed_resource_element_compatible<element_type> 306 // struct RWBuffer { 307 // element_type Val; 308 // }; 309 // 310 // int fn() { 311 // RWBuffer<int> Buf; 312 // } 313 // 314 // When dumping the AST and filtering for "RWBuffer", the resulting AST 315 // structure is what we're trying to construct below, specifically the 316 // CSE portion. 317 ConceptSpecializationExpr * 318 constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD) { 319 ASTContext &Context = S.getASTContext(); 320 SourceLocation Loc = Builder.Record->getBeginLoc(); 321 DeclarationNameInfo DNI(CD->getDeclName(), Loc); 322 NestedNameSpecifierLoc NNSLoc; 323 DeclContext *DC = Builder.Record->getDeclContext(); 324 TemplateArgumentListInfo TALI(Loc, Loc); 325 326 // Assume that the concept decl has just one template parameter 327 // This parameter should have been added when CD was constructed 328 // in getTypedBufferConceptDecl 329 assert(CD->getTemplateParameters()->size() == 1 && 330 "unexpected concept decl parameter count"); 331 TemplateTypeParmDecl *ConceptTTPD = dyn_cast<TemplateTypeParmDecl>( 332 CD->getTemplateParameters()->getParam(0)); 333 334 // this TemplateTypeParmDecl is the template for the resource, and is 335 // used to construct a template argumentthat will be used 336 // to construct the ImplicitConceptSpecializationDecl 337 TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create( 338 Context, // AST context 339 Builder.Record->getDeclContext(), // DeclContext 340 SourceLocation(), SourceLocation(), 341 /*D=*/0, // Depth in the template parameter list 342 /*P=*/0, // Position in the template parameter list 343 /*Id=*/nullptr, // Identifier for 'T' 344 /*Typename=*/true, // Indicates this is a 'typename' or 'class' 345 /*ParameterPack=*/false, // Not a parameter pack 346 /*HasTypeConstraint=*/false // Has no type constraint 347 ); 348 349 T->setDeclContext(DC); 350 351 QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD); 352 353 // this is the 2nd template argument node, on which 354 // the concept constraint is actually being applied: 'element_type' 355 TemplateArgument ConceptTA = TemplateArgument(ConceptTType); 356 357 QualType CSETType = Context.getTypeDeclType(T); 358 359 // this is the 1st template argument node, which represents 360 // the abstract type that a concept would refer to: 'T' 361 TemplateArgument CSETA = TemplateArgument(CSETType); 362 363 ImplicitConceptSpecializationDecl *ImplicitCSEDecl = 364 ImplicitConceptSpecializationDecl::Create( 365 Context, Builder.Record->getDeclContext(), Loc, {CSETA}); 366 367 // Constraint satisfaction is used to construct the 368 // ConceptSpecailizationExpr, and represents the 2nd Template Argument, 369 // located at the bottom of the sample AST above. 370 const ConstraintSatisfaction CS(CD, {ConceptTA}); 371 TemplateArgumentLoc TAL = S.getTrivialTemplateArgumentLoc( 372 ConceptTA, QualType(), SourceLocation()); 373 374 TALI.addArgument(TAL); 375 const ASTTemplateArgumentListInfo *ATALI = 376 ASTTemplateArgumentListInfo::Create(Context, TALI); 377 378 // In the concept reference, ATALI is what adds the extra 379 // TemplateArgument node underneath CSE 380 ConceptReference *CR = 381 ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI); 382 383 ConceptSpecializationExpr *CSE = 384 ConceptSpecializationExpr::Create(Context, CR, ImplicitCSEDecl, &CS); 385 386 return CSE; 387 } 388 389 BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr) { 390 if (Params.empty()) 391 return Builder; 392 393 ASTContext &AST = Builder.SemaRef.Context; 394 ConceptSpecializationExpr *CSE = 395 CD ? constructConceptSpecializationExpr(Builder.SemaRef, CD) : nullptr; 396 auto *ParamList = TemplateParameterList::Create( 397 AST, SourceLocation(), SourceLocation(), Params, SourceLocation(), CSE); 398 Builder.Template = ClassTemplateDecl::Create( 399 AST, Builder.Record->getDeclContext(), SourceLocation(), 400 DeclarationName(Builder.Record->getIdentifier()), ParamList, 401 Builder.Record); 402 403 Builder.Record->setDescribedClassTemplate(Builder.Template); 404 Builder.Template->setImplicit(true); 405 Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext()); 406 407 // NOTE: setPreviousDecl before addDecl so new decl replace old decl when 408 // make visible. 409 Builder.Template->setPreviousDecl(Builder.PrevTemplate); 410 Builder.Record->getDeclContext()->addDecl(Builder.Template); 411 Params.clear(); 412 413 QualType T = Builder.Template->getInjectedClassNameSpecialization(); 414 T = AST.getInjectedClassNameType(Builder.Record, T); 415 416 return Builder; 417 } 418 }; 419 420 // Builder for methods of builtin types. Allows adding methods to builtin types 421 // using the builder pattern like this: 422 // 423 // BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType) 424 // .addParam("param_name", Type, InOutModifier) 425 // .callBuiltin("builtin_name", BuiltinParams...) 426 // .finalizeMethod(); 427 // 428 // The builder needs to have all of the method parameters before it can create 429 // a CXXMethodDecl. It collects them in addParam calls and when a first 430 // method that builds the body is called or when access to 'this` is needed it 431 // creates the CXXMethodDecl and ParmVarDecls instances. These can then be 432 // referenced from the body building methods. Destructor or an explicit call to 433 // finalizeMethod() will complete the method definition. 434 // 435 // The callBuiltin helper method accepts constants via `Expr *` or placeholder 436 // value arguments to indicate which function arguments to forward to the 437 // builtin. 438 // 439 // If the method that is being built has a non-void return type the 440 // finalizeMethod will create a return statent with the value of the last 441 // statement (unless the last statement is already a ReturnStmt). 442 struct BuiltinTypeMethodBuilder { 443 struct MethodParam { 444 const IdentifierInfo &NameII; 445 QualType Ty; 446 HLSLParamModifierAttr::Spelling Modifier; 447 MethodParam(const IdentifierInfo &NameII, QualType Ty, 448 HLSLParamModifierAttr::Spelling Modifier) 449 : NameII(NameII), Ty(Ty), Modifier(Modifier) {} 450 }; 451 452 BuiltinTypeDeclBuilder &DeclBuilder; 453 DeclarationNameInfo NameInfo; 454 QualType ReturnTy; 455 CXXMethodDecl *Method; 456 bool IsConst; 457 llvm::SmallVector<MethodParam> Params; 458 llvm::SmallVector<Stmt *> StmtsList; 459 460 // Argument placeholders, inspired by std::placeholder. These are the indices 461 // of arguments to forward to `callBuiltin` and other method builder methods. 462 // Additional special values are: 463 // Handle - refers to the resource handle. 464 // LastStmt - refers to the last statement in the method body; referencing 465 // LastStmt will remove the statement from the method body since 466 // it will be linked from the new expression being constructed. 467 enum class PlaceHolder { _0, _1, _2, _3, Handle = 128, LastStmt }; 468 469 Expr *convertPlaceholder(PlaceHolder PH) { 470 if (PH == PlaceHolder::Handle) 471 return getResourceHandleExpr(); 472 473 if (PH == PlaceHolder::LastStmt) { 474 assert(!StmtsList.empty() && "no statements in the list"); 475 Stmt *LastStmt = StmtsList.pop_back_val(); 476 assert(isa<ValueStmt>(LastStmt) && 477 "last statement does not have a value"); 478 return cast<ValueStmt>(LastStmt)->getExprStmt(); 479 } 480 481 ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); 482 ParmVarDecl *ParamDecl = Method->getParamDecl(static_cast<unsigned>(PH)); 483 return DeclRefExpr::Create( 484 AST, NestedNameSpecifierLoc(), SourceLocation(), ParamDecl, false, 485 DeclarationNameInfo(ParamDecl->getDeclName(), SourceLocation()), 486 ParamDecl->getType(), VK_PRValue); 487 } 488 Expr *convertPlaceholder(Expr *E) { return E; } 489 490 public: 491 BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name, 492 QualType ReturnTy, bool IsConst = false) 493 : DeclBuilder(DB), NameInfo(DeclarationNameInfo(Name, SourceLocation())), 494 ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {} 495 496 BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef Name, 497 QualType ReturnTy, bool IsConst = false) 498 : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) { 499 const IdentifierInfo &II = 500 DB.SemaRef.getASTContext().Idents.get(Name, tok::TokenKind::identifier); 501 NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation()); 502 } 503 504 BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty, 505 HLSLParamModifierAttr::Spelling Modifier = 506 HLSLParamModifierAttr::Keyword_in) { 507 assert(Method == nullptr && "Cannot add param, method already created"); 508 const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get( 509 Name, tok::TokenKind::identifier); 510 Params.emplace_back(II, Ty, Modifier); 511 return *this; 512 } 513 514 private: 515 void createMethodDecl() { 516 assert(Method == nullptr && "Method already created"); 517 518 // create method type 519 ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); 520 SmallVector<QualType> ParamTypes; 521 for (MethodParam &MP : Params) 522 ParamTypes.emplace_back(MP.Ty); 523 524 FunctionProtoType::ExtProtoInfo ExtInfo; 525 if (IsConst) 526 ExtInfo.TypeQuals.addConst(); 527 528 QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo); 529 530 // create method decl 531 auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation()); 532 Method = 533 CXXMethodDecl::Create(AST, DeclBuilder.Record, SourceLocation(), 534 NameInfo, MethodTy, TSInfo, SC_None, false, false, 535 ConstexprSpecKind::Unspecified, SourceLocation()); 536 537 // create params & set them to the function prototype 538 SmallVector<ParmVarDecl *> ParmDecls; 539 auto FnProtoLoc = 540 Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>(); 541 for (int I = 0, E = Params.size(); I != E; I++) { 542 MethodParam &MP = Params[I]; 543 ParmVarDecl *Parm = ParmVarDecl::Create( 544 AST, Method->getDeclContext(), SourceLocation(), SourceLocation(), 545 &MP.NameII, MP.Ty, 546 AST.getTrivialTypeSourceInfo(MP.Ty, SourceLocation()), SC_None, 547 nullptr); 548 if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) { 549 auto *Mod = 550 HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier); 551 Parm->addAttr(Mod); 552 } 553 ParmDecls.push_back(Parm); 554 FnProtoLoc.setParam(I, Parm); 555 } 556 Method->setParams({ParmDecls}); 557 } 558 559 public: 560 ~BuiltinTypeMethodBuilder() { finalizeMethod(); } 561 562 BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete; 563 BuiltinTypeMethodBuilder & 564 operator=(const BuiltinTypeMethodBuilder &Other) = delete; 565 566 Expr *getResourceHandleExpr() { 567 // The first statement added to a method or access to 'this' creates the 568 // declaration. 569 if (!Method) 570 createMethodDecl(); 571 572 ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); 573 CXXThisExpr *This = CXXThisExpr::Create( 574 AST, SourceLocation(), Method->getFunctionObjectParameterType(), true); 575 FieldDecl *HandleField = DeclBuilder.getResourceHandleField(); 576 return MemberExpr::CreateImplicit(AST, This, false, HandleField, 577 HandleField->getType(), VK_LValue, 578 OK_Ordinary); 579 } 580 581 template <typename... Ts> 582 BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName, 583 QualType ReturnType, Ts... ArgSpecs) { 584 std::array<Expr *, sizeof...(ArgSpecs)> Args{ 585 convertPlaceholder(std::forward<Ts>(ArgSpecs))...}; 586 587 // The first statement added to a method or access to 'this` creates the 588 // declaration. 589 if (!Method) 590 createMethodDecl(); 591 592 ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); 593 FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName); 594 DeclRefExpr *DRE = DeclRefExpr::Create( 595 AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false, 596 FD->getNameInfo(), AST.BuiltinFnTy, VK_PRValue); 597 598 if (ReturnType.isNull()) 599 ReturnType = FD->getReturnType(); 600 601 Expr *Call = CallExpr::Create(AST, DRE, Args, ReturnType, VK_PRValue, 602 SourceLocation(), FPOptionsOverride()); 603 StmtsList.push_back(Call); 604 return *this; 605 } 606 607 template <typename TLHS, typename TRHS> 608 BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS) { 609 Expr *LHSExpr = convertPlaceholder(LHS); 610 Expr *RHSExpr = convertPlaceholder(RHS); 611 Stmt *AssignStmt = BinaryOperator::Create( 612 DeclBuilder.SemaRef.getASTContext(), LHSExpr, RHSExpr, BO_Assign, 613 LHSExpr->getType(), ExprValueKind::VK_PRValue, 614 ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride()); 615 StmtsList.push_back(AssignStmt); 616 return *this; 617 } 618 619 template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr) { 620 Expr *PtrExpr = convertPlaceholder(Ptr); 621 Expr *Deref = 622 UnaryOperator::Create(DeclBuilder.SemaRef.getASTContext(), PtrExpr, 623 UO_Deref, PtrExpr->getType()->getPointeeType(), 624 VK_PRValue, OK_Ordinary, SourceLocation(), 625 /*CanOverflow=*/false, FPOptionsOverride()); 626 StmtsList.push_back(Deref); 627 return *this; 628 } 629 630 BuiltinTypeDeclBuilder &finalizeMethod() { 631 assert(!DeclBuilder.Record->isCompleteDefinition() && 632 "record is already complete"); 633 assert( 634 Method != nullptr && 635 "method decl not created; are you missing a call to build the body?"); 636 637 if (!Method->hasBody()) { 638 ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); 639 assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) && 640 "nothing to return from non-void method"); 641 if (ReturnTy != AST.VoidTy) { 642 if (Expr *LastExpr = dyn_cast<Expr>(StmtsList.back())) { 643 assert(AST.hasSameUnqualifiedType(LastExpr->getType(), 644 ReturnTy.getNonReferenceType()) && 645 "Return type of the last statement must match the return type " 646 "of the method"); 647 if (!isa<ReturnStmt>(LastExpr)) { 648 StmtsList.pop_back(); 649 StmtsList.push_back( 650 ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr)); 651 } 652 } 653 } 654 655 Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(), 656 SourceLocation(), SourceLocation())); 657 Method->setLexicalDeclContext(DeclBuilder.Record); 658 Method->setAccess(AccessSpecifier::AS_public); 659 Method->addAttr(AlwaysInlineAttr::CreateImplicit( 660 AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline)); 661 DeclBuilder.Record->addDecl(Method); 662 } 663 return DeclBuilder; 664 } 665 }; 666 667 } // namespace 668 669 TemplateParameterListBuilder BuiltinTypeDeclBuilder::addTemplateArgumentList() { 670 return TemplateParameterListBuilder(*this); 671 } 672 673 BuiltinTypeDeclBuilder & 674 BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names, 675 ConceptDecl *CD = nullptr) { 676 if (Record->isCompleteDefinition()) { 677 assert(Template && "existing record it not a template"); 678 assert(Template->getTemplateParameters()->size() == Names.size() && 679 "template param count mismatch"); 680 return *this; 681 } 682 683 TemplateParameterListBuilder Builder = this->addTemplateArgumentList(); 684 for (StringRef Name : Names) 685 Builder.addTypeParameter(Name); 686 return Builder.finalizeTemplateArgs(CD); 687 } 688 689 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() { 690 using PH = BuiltinTypeMethodBuilder::PlaceHolder; 691 return BuiltinTypeMethodBuilder(*this, "IncrementCounter", 692 SemaRef.getASTContext().UnsignedIntTy) 693 .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(), 694 PH::Handle, getConstantIntExpr(1)) 695 .finalizeMethod(); 696 } 697 698 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() { 699 using PH = BuiltinTypeMethodBuilder::PlaceHolder; 700 return BuiltinTypeMethodBuilder(*this, "DecrementCounter", 701 SemaRef.getASTContext().UnsignedIntTy) 702 .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(), 703 PH::Handle, getConstantIntExpr(-1)) 704 .finalizeMethod(); 705 } 706 707 BuiltinTypeDeclBuilder & 708 BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name, 709 bool IsConst, bool IsRef) { 710 assert(!Record->isCompleteDefinition() && "record is already complete"); 711 ASTContext &AST = SemaRef.getASTContext(); 712 using PH = BuiltinTypeMethodBuilder::PlaceHolder; 713 714 QualType ElemTy = getHandleElementType(); 715 // TODO: Map to an hlsl_device address space. 716 QualType ElemPtrTy = AST.getPointerType(ElemTy); 717 QualType ReturnTy = ElemTy; 718 if (IsConst) 719 ReturnTy.addConst(); 720 if (IsRef) 721 ReturnTy = AST.getLValueReferenceType(ReturnTy); 722 723 return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst) 724 .addParam("Index", AST.UnsignedIntTy) 725 .callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle, 726 PH::_0) 727 .dereference(PH::LastStmt) 728 .finalizeMethod(); 729 } 730 731 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() { 732 using PH = BuiltinTypeMethodBuilder::PlaceHolder; 733 ASTContext &AST = SemaRef.getASTContext(); 734 QualType ElemTy = getHandleElementType(); 735 return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy) 736 .addParam("value", ElemTy) 737 .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy, 738 PH::Handle, getConstantIntExpr(1)) 739 .callBuiltin("__builtin_hlsl_resource_getpointer", 740 AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt) 741 .dereference(PH::LastStmt) 742 .assign(PH::LastStmt, PH::_0) 743 .finalizeMethod(); 744 } 745 746 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() { 747 using PH = BuiltinTypeMethodBuilder::PlaceHolder; 748 ASTContext &AST = SemaRef.getASTContext(); 749 QualType ElemTy = getHandleElementType(); 750 return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy) 751 .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy, 752 PH::Handle, getConstantIntExpr(-1)) 753 .callBuiltin("__builtin_hlsl_resource_getpointer", 754 AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt) 755 .dereference(PH::LastStmt) 756 .finalizeMethod(); 757 } 758 759 HLSLExternalSemaSource::~HLSLExternalSemaSource() {} 760 761 void HLSLExternalSemaSource::InitializeSema(Sema &S) { 762 SemaPtr = &S; 763 ASTContext &AST = SemaPtr->getASTContext(); 764 // If the translation unit has external storage force external decls to load. 765 if (AST.getTranslationUnitDecl()->hasExternalLexicalStorage()) 766 (void)AST.getTranslationUnitDecl()->decls_begin(); 767 768 IdentifierInfo &HLSL = AST.Idents.get("hlsl", tok::TokenKind::identifier); 769 LookupResult Result(S, &HLSL, SourceLocation(), Sema::LookupNamespaceName); 770 NamespaceDecl *PrevDecl = nullptr; 771 if (S.LookupQualifiedName(Result, AST.getTranslationUnitDecl())) 772 PrevDecl = Result.getAsSingle<NamespaceDecl>(); 773 HLSLNamespace = NamespaceDecl::Create( 774 AST, AST.getTranslationUnitDecl(), /*Inline=*/false, SourceLocation(), 775 SourceLocation(), &HLSL, PrevDecl, /*Nested=*/false); 776 HLSLNamespace->setImplicit(true); 777 HLSLNamespace->setHasExternalLexicalStorage(); 778 AST.getTranslationUnitDecl()->addDecl(HLSLNamespace); 779 780 // Force external decls in the HLSL namespace to load from the PCH. 781 (void)HLSLNamespace->getCanonicalDecl()->decls_begin(); 782 defineTrivialHLSLTypes(); 783 defineHLSLTypesWithForwardDeclarations(); 784 785 // This adds a `using namespace hlsl` directive. In DXC, we don't put HLSL's 786 // built in types inside a namespace, but we are planning to change that in 787 // the near future. In order to be source compatible older versions of HLSL 788 // will need to implicitly use the hlsl namespace. For now in clang everything 789 // will get added to the namespace, and we can remove the using directive for 790 // future language versions to match HLSL's evolution. 791 auto *UsingDecl = UsingDirectiveDecl::Create( 792 AST, AST.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), 793 NestedNameSpecifierLoc(), SourceLocation(), HLSLNamespace, 794 AST.getTranslationUnitDecl()); 795 796 AST.getTranslationUnitDecl()->addDecl(UsingDecl); 797 } 798 799 void HLSLExternalSemaSource::defineHLSLVectorAlias() { 800 ASTContext &AST = SemaPtr->getASTContext(); 801 802 llvm::SmallVector<NamedDecl *> TemplateParams; 803 804 auto *TypeParam = TemplateTypeParmDecl::Create( 805 AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 0, 806 &AST.Idents.get("element", tok::TokenKind::identifier), false, false); 807 TypeParam->setDefaultArgument( 808 AST, SemaPtr->getTrivialTemplateArgumentLoc( 809 TemplateArgument(AST.FloatTy), QualType(), SourceLocation())); 810 811 TemplateParams.emplace_back(TypeParam); 812 813 auto *SizeParam = NonTypeTemplateParmDecl::Create( 814 AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1, 815 &AST.Idents.get("element_count", tok::TokenKind::identifier), AST.IntTy, 816 false, AST.getTrivialTypeSourceInfo(AST.IntTy)); 817 llvm::APInt Val(AST.getIntWidth(AST.IntTy), 4); 818 TemplateArgument Default(AST, llvm::APSInt(std::move(Val)), AST.IntTy, 819 /*IsDefaulted=*/true); 820 SizeParam->setDefaultArgument( 821 AST, SemaPtr->getTrivialTemplateArgumentLoc(Default, AST.IntTy, 822 SourceLocation(), SizeParam)); 823 TemplateParams.emplace_back(SizeParam); 824 825 auto *ParamList = 826 TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(), 827 TemplateParams, SourceLocation(), nullptr); 828 829 IdentifierInfo &II = AST.Idents.get("vector", tok::TokenKind::identifier); 830 831 QualType AliasType = AST.getDependentSizedExtVectorType( 832 AST.getTemplateTypeParmType(0, 0, false, TypeParam), 833 DeclRefExpr::Create( 834 AST, NestedNameSpecifierLoc(), SourceLocation(), SizeParam, false, 835 DeclarationNameInfo(SizeParam->getDeclName(), SourceLocation()), 836 AST.IntTy, VK_LValue), 837 SourceLocation()); 838 839 auto *Record = TypeAliasDecl::Create(AST, HLSLNamespace, SourceLocation(), 840 SourceLocation(), &II, 841 AST.getTrivialTypeSourceInfo(AliasType)); 842 Record->setImplicit(true); 843 844 auto *Template = 845 TypeAliasTemplateDecl::Create(AST, HLSLNamespace, SourceLocation(), 846 Record->getIdentifier(), ParamList, Record); 847 848 Record->setDescribedAliasTemplate(Template); 849 Template->setImplicit(true); 850 Template->setLexicalDeclContext(Record->getDeclContext()); 851 HLSLNamespace->addDecl(Template); 852 } 853 854 void HLSLExternalSemaSource::defineTrivialHLSLTypes() { 855 defineHLSLVectorAlias(); 856 } 857 858 /// Set up common members and attributes for buffer types 859 static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S, 860 ResourceClass RC, ResourceKind RK, 861 bool IsROV, bool RawBuffer) { 862 return BuiltinTypeDeclBuilder(S, Decl) 863 .addHandleMember(RC, RK, IsROV, RawBuffer) 864 .addDefaultHandleConstructor(); 865 } 866 867 // This function is responsible for constructing the constraint expression for 868 // this concept: 869 // template<typename T> concept is_typed_resource_element_compatible = 870 // __is_typed_resource_element_compatible<T>; 871 static Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc, 872 TemplateTypeParmDecl *T) { 873 ASTContext &Context = S.getASTContext(); 874 875 // Obtain the QualType for 'bool' 876 QualType BoolTy = Context.BoolTy; 877 878 // Create a QualType that points to this TemplateTypeParmDecl 879 QualType TType = Context.getTypeDeclType(T); 880 881 // Create a TypeSourceInfo for the template type parameter 'T' 882 TypeSourceInfo *TTypeSourceInfo = 883 Context.getTrivialTypeSourceInfo(TType, NameLoc); 884 885 TypeTraitExpr *TypedResExpr = TypeTraitExpr::Create( 886 Context, BoolTy, NameLoc, UTT_IsTypedResourceElementCompatible, 887 {TTypeSourceInfo}, NameLoc, true); 888 889 return TypedResExpr; 890 } 891 892 // This function is responsible for constructing the constraint expression for 893 // this concept: 894 // template<typename T> concept is_structured_resource_element_compatible = 895 // !__is_intangible<T> && sizeof(T) >= 1; 896 static Expr *constructStructuredBufferConstraintExpr(Sema &S, 897 SourceLocation NameLoc, 898 TemplateTypeParmDecl *T) { 899 ASTContext &Context = S.getASTContext(); 900 901 // Obtain the QualType for 'bool' 902 QualType BoolTy = Context.BoolTy; 903 904 // Create a QualType that points to this TemplateTypeParmDecl 905 QualType TType = Context.getTypeDeclType(T); 906 907 // Create a TypeSourceInfo for the template type parameter 'T' 908 TypeSourceInfo *TTypeSourceInfo = 909 Context.getTrivialTypeSourceInfo(TType, NameLoc); 910 911 TypeTraitExpr *IsIntangibleExpr = 912 TypeTraitExpr::Create(Context, BoolTy, NameLoc, UTT_IsIntangibleType, 913 {TTypeSourceInfo}, NameLoc, true); 914 915 // negate IsIntangibleExpr 916 UnaryOperator *NotIntangibleExpr = UnaryOperator::Create( 917 Context, IsIntangibleExpr, UO_LNot, BoolTy, VK_LValue, OK_Ordinary, 918 NameLoc, false, FPOptionsOverride()); 919 920 // element types also may not be of 0 size 921 UnaryExprOrTypeTraitExpr *SizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr( 922 UETT_SizeOf, TTypeSourceInfo, BoolTy, NameLoc, NameLoc); 923 924 // Create a BinaryOperator that checks if the size of the type is not equal to 925 // 1 Empty structs have a size of 1 in HLSL, so we need to check for that 926 IntegerLiteral *rhs = IntegerLiteral::Create( 927 Context, llvm::APInt(Context.getTypeSize(Context.getSizeType()), 1, true), 928 Context.getSizeType(), NameLoc); 929 930 BinaryOperator *SizeGEQOneExpr = 931 BinaryOperator::Create(Context, SizeOfExpr, rhs, BO_GE, BoolTy, VK_LValue, 932 OK_Ordinary, NameLoc, FPOptionsOverride()); 933 934 // Combine the two constraints 935 BinaryOperator *CombinedExpr = BinaryOperator::Create( 936 Context, NotIntangibleExpr, SizeGEQOneExpr, BO_LAnd, BoolTy, VK_LValue, 937 OK_Ordinary, NameLoc, FPOptionsOverride()); 938 939 return CombinedExpr; 940 } 941 942 static ConceptDecl *constructBufferConceptDecl(Sema &S, NamespaceDecl *NSD, 943 bool isTypedBuffer) { 944 ASTContext &Context = S.getASTContext(); 945 DeclContext *DC = NSD->getDeclContext(); 946 SourceLocation DeclLoc = SourceLocation(); 947 948 IdentifierInfo &ElementTypeII = Context.Idents.get("element_type"); 949 TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create( 950 Context, NSD->getDeclContext(), DeclLoc, DeclLoc, 951 /*D=*/0, 952 /*P=*/0, 953 /*Id=*/&ElementTypeII, 954 /*Typename=*/true, 955 /*ParameterPack=*/false); 956 957 T->setDeclContext(DC); 958 T->setReferenced(); 959 960 // Create and Attach Template Parameter List to ConceptDecl 961 TemplateParameterList *ConceptParams = TemplateParameterList::Create( 962 Context, DeclLoc, DeclLoc, {T}, DeclLoc, nullptr); 963 964 DeclarationName DeclName; 965 Expr *ConstraintExpr = nullptr; 966 967 if (isTypedBuffer) { 968 DeclName = DeclarationName( 969 &Context.Idents.get("__is_typed_resource_element_compatible")); 970 ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T); 971 } else { 972 DeclName = DeclarationName( 973 &Context.Idents.get("__is_structured_resource_element_compatible")); 974 ConstraintExpr = constructStructuredBufferConstraintExpr(S, DeclLoc, T); 975 } 976 977 // Create a ConceptDecl 978 ConceptDecl *CD = 979 ConceptDecl::Create(Context, NSD->getDeclContext(), DeclLoc, DeclName, 980 ConceptParams, ConstraintExpr); 981 982 // Attach the template parameter list to the ConceptDecl 983 CD->setTemplateParameters(ConceptParams); 984 985 // Add the concept declaration to the Translation Unit Decl 986 NSD->getDeclContext()->addDecl(CD); 987 988 return CD; 989 } 990 991 void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { 992 CXXRecordDecl *Decl; 993 ConceptDecl *TypedBufferConcept = constructBufferConceptDecl( 994 *SemaPtr, HLSLNamespace, /*isTypedBuffer*/ true); 995 ConceptDecl *StructuredBufferConcept = constructBufferConceptDecl( 996 *SemaPtr, HLSLNamespace, /*isTypedBuffer*/ false); 997 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer") 998 .addSimpleTemplateParams({"element_type"}, TypedBufferConcept) 999 .finalizeForwardDeclaration(); 1000 1001 onCompletion(Decl, [this](CXXRecordDecl *Decl) { 1002 setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, 1003 ResourceKind::TypedBuffer, /*IsROV=*/false, 1004 /*RawBuffer=*/false) 1005 .addArraySubscriptOperators() 1006 .addLoadMethods() 1007 .completeDefinition(); 1008 }); 1009 1010 Decl = 1011 BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer") 1012 .addSimpleTemplateParams({"element_type"}, StructuredBufferConcept) 1013 .finalizeForwardDeclaration(); 1014 onCompletion(Decl, [this](CXXRecordDecl *Decl) { 1015 setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, 1016 ResourceKind::TypedBuffer, /*IsROV=*/true, 1017 /*RawBuffer=*/false) 1018 .addArraySubscriptOperators() 1019 .addLoadMethods() 1020 .completeDefinition(); 1021 }); 1022 1023 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "StructuredBuffer") 1024 .addSimpleTemplateParams({"element_type"}, StructuredBufferConcept) 1025 .finalizeForwardDeclaration(); 1026 onCompletion(Decl, [this](CXXRecordDecl *Decl) { 1027 setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, ResourceKind::RawBuffer, 1028 /*IsROV=*/false, /*RawBuffer=*/true) 1029 .addArraySubscriptOperators() 1030 .addLoadMethods() 1031 .completeDefinition(); 1032 }); 1033 1034 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWStructuredBuffer") 1035 .addSimpleTemplateParams({"element_type"}, StructuredBufferConcept) 1036 .finalizeForwardDeclaration(); 1037 onCompletion(Decl, [this](CXXRecordDecl *Decl) { 1038 setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, 1039 /*IsROV=*/false, /*RawBuffer=*/true) 1040 .addArraySubscriptOperators() 1041 .addLoadMethods() 1042 .addIncrementCounterMethod() 1043 .addDecrementCounterMethod() 1044 .completeDefinition(); 1045 }); 1046 1047 Decl = 1048 BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "AppendStructuredBuffer") 1049 .addSimpleTemplateParams({"element_type"}, StructuredBufferConcept) 1050 .finalizeForwardDeclaration(); 1051 onCompletion(Decl, [this](CXXRecordDecl *Decl) { 1052 setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, 1053 /*IsROV=*/false, /*RawBuffer=*/true) 1054 .addAppendMethod() 1055 .completeDefinition(); 1056 }); 1057 1058 Decl = 1059 BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConsumeStructuredBuffer") 1060 .addSimpleTemplateParams({"element_type"}, StructuredBufferConcept) 1061 .finalizeForwardDeclaration(); 1062 onCompletion(Decl, [this](CXXRecordDecl *Decl) { 1063 setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, 1064 /*IsROV=*/false, /*RawBuffer=*/true) 1065 .addConsumeMethod() 1066 .completeDefinition(); 1067 }); 1068 1069 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, 1070 "RasterizerOrderedStructuredBuffer") 1071 .addSimpleTemplateParams({"element_type"}, StructuredBufferConcept) 1072 .finalizeForwardDeclaration(); 1073 onCompletion(Decl, [this](CXXRecordDecl *Decl) { 1074 setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, 1075 /*IsROV=*/true, /*RawBuffer=*/true) 1076 .addArraySubscriptOperators() 1077 .addLoadMethods() 1078 .addIncrementCounterMethod() 1079 .addDecrementCounterMethod() 1080 .completeDefinition(); 1081 }); 1082 1083 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ByteAddressBuffer") 1084 .finalizeForwardDeclaration(); 1085 onCompletion(Decl, [this](CXXRecordDecl *Decl) { 1086 setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, ResourceKind::RawBuffer, 1087 /*IsROV=*/false, 1088 /*RawBuffer=*/true) 1089 .completeDefinition(); 1090 }); 1091 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWByteAddressBuffer") 1092 .finalizeForwardDeclaration(); 1093 onCompletion(Decl, [this](CXXRecordDecl *Decl) { 1094 setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, 1095 /*IsROV=*/false, 1096 /*RawBuffer=*/true) 1097 .completeDefinition(); 1098 }); 1099 Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, 1100 "RasterizerOrderedByteAddressBuffer") 1101 .finalizeForwardDeclaration(); 1102 onCompletion(Decl, [this](CXXRecordDecl *Decl) { 1103 setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, 1104 /*IsROV=*/true, 1105 /*RawBuffer=*/true) 1106 .completeDefinition(); 1107 }); 1108 } 1109 1110 void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record, 1111 CompletionFunction Fn) { 1112 if (!Record->isCompleteDefinition()) 1113 Completions.insert(std::make_pair(Record->getCanonicalDecl(), Fn)); 1114 } 1115 1116 void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) { 1117 if (!isa<CXXRecordDecl>(Tag)) 1118 return; 1119 auto Record = cast<CXXRecordDecl>(Tag); 1120 1121 // If this is a specialization, we need to get the underlying templated 1122 // declaration and complete that. 1123 if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(Record)) 1124 Record = TDecl->getSpecializedTemplate()->getTemplatedDecl(); 1125 Record = Record->getCanonicalDecl(); 1126 auto It = Completions.find(Record); 1127 if (It == Completions.end()) 1128 return; 1129 It->second(Record); 1130 } 1131 1132 static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) { 1133 IdentifierInfo &II = 1134 S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); 1135 DeclarationNameInfo NameInfo = 1136 DeclarationNameInfo(DeclarationName(&II), SourceLocation()); 1137 LookupResult R(S, NameInfo, Sema::LookupOrdinaryName); 1138 // AllowBuiltinCreation is false but LookupDirect will create 1139 // the builtin when searching the global scope anyways... 1140 S.LookupName(R, S.getCurScope()); 1141 // FIXME: If the builtin function was user-declared in global scope, 1142 // this assert *will* fail. Should this call LookupBuiltin instead? 1143 assert(R.isSingleResult() && 1144 "Since this is a builtin it should always resolve!"); 1145 return cast<FunctionDecl>(R.getFoundDecl()); 1146 } 1147