xref: /llvm-project/clang/lib/Sema/HLSLExternalSemaSource.cpp (revision 7a4b3b4927bd92e8494dca9dffaf5aeeca4658ba)
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