xref: /freebsd/contrib/llvm-project/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===--- HLSLBuiltinTypeDeclBuilder.cpp - HLSL Builtin Type Decl Builder --===//
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 // Helper classes for creating HLSL builtin class types. Used by external HLSL
10 // sema source.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "HLSLBuiltinTypeDeclBuilder.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/Attr.h"
17 #include "clang/AST/Decl.h"
18 #include "clang/AST/DeclCXX.h"
19 #include "clang/AST/Expr.h"
20 #include "clang/AST/Type.h"
21 #include "clang/Basic/SourceLocation.h"
22 #include "clang/Sema/Lookup.h"
23 #include "clang/Sema/Sema.h"
24 #include "clang/Sema/SemaHLSL.h"
25 #include "llvm/ADT/SmallVector.h"
26 
27 using namespace llvm::hlsl;
28 
29 namespace clang {
30 
31 namespace hlsl {
32 
33 namespace {
34 
lookupBuiltinFunction(Sema & S,StringRef Name)35 static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) {
36   IdentifierInfo &II =
37       S.getASTContext().Idents.get(Name, tok::TokenKind::identifier);
38   DeclarationNameInfo NameInfo =
39       DeclarationNameInfo(DeclarationName(&II), SourceLocation());
40   LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
41   // AllowBuiltinCreation is false but LookupDirect will create
42   // the builtin when searching the global scope anyways...
43   S.LookupName(R, S.getCurScope());
44   // FIXME: If the builtin function was user-declared in global scope,
45   // this assert *will* fail. Should this call LookupBuiltin instead?
46   assert(R.isSingleResult() &&
47          "Since this is a builtin it should always resolve!");
48   return cast<FunctionDecl>(R.getFoundDecl());
49 }
50 } // namespace
51 
52 // Builder for template arguments of builtin types. Used internally
53 // by BuiltinTypeDeclBuilder.
54 struct TemplateParameterListBuilder {
55   BuiltinTypeDeclBuilder &Builder;
56   llvm::SmallVector<NamedDecl *> Params;
57 
TemplateParameterListBuilderclang::hlsl::TemplateParameterListBuilder58   TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {}
59   ~TemplateParameterListBuilder();
60 
61   TemplateParameterListBuilder &
62   addTypeParameter(StringRef Name, QualType DefaultValue = QualType());
63 
64   ConceptSpecializationExpr *
65   constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD);
66 
67   BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr);
68 };
69 
70 // Builder for methods or constructors of builtin types. Allows creating methods
71 // or constructors of builtin types using the builder pattern like this:
72 //
73 //   BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType)
74 //       .addParam("param_name", Type, InOutModifier)
75 //       .callBuiltin("builtin_name", BuiltinParams...)
76 //       .finalize();
77 //
78 // The builder needs to have all of the parameters before it can create
79 // a CXXMethodDecl or CXXConstructorDecl. It collects them in addParam calls and
80 // when a first method that builds the body is called or when access to 'this`
81 // is needed it creates the CXXMethodDecl/CXXConstructorDecl and ParmVarDecls
82 // instances. These can then be referenced from the body building methods.
83 // Destructor or an explicit call to finalize() will complete the method
84 // definition.
85 //
86 // The callBuiltin helper method accepts constants via `Expr *` or placeholder
87 // value arguments to indicate which function arguments to forward to the
88 // builtin.
89 //
90 // If the method that is being built has a non-void return type the
91 // finalize() will create a return statement with the value of the last
92 // statement (unless the last statement is already a ReturnStmt or the return
93 // value is void).
94 struct BuiltinTypeMethodBuilder {
95 private:
96   struct Param {
97     const IdentifierInfo &NameII;
98     QualType Ty;
99     HLSLParamModifierAttr::Spelling Modifier;
Paramclang::hlsl::BuiltinTypeMethodBuilder::Param100     Param(const IdentifierInfo &NameII, QualType Ty,
101           HLSLParamModifierAttr::Spelling Modifier)
102         : NameII(NameII), Ty(Ty), Modifier(Modifier) {}
103   };
104 
105   BuiltinTypeDeclBuilder &DeclBuilder;
106   DeclarationName Name;
107   QualType ReturnTy;
108   // method or constructor declaration
109   // (CXXConstructorDecl derives from CXXMethodDecl)
110   CXXMethodDecl *Method;
111   bool IsConst;
112   bool IsCtor;
113   llvm::SmallVector<Param> Params;
114   llvm::SmallVector<Stmt *> StmtsList;
115 
116   // Argument placeholders, inspired by std::placeholder. These are the indices
117   // of arguments to forward to `callBuiltin` and other method builder methods.
118   // Additional special values are:
119   //   Handle   - refers to the resource handle.
120   //   LastStmt - refers to the last statement in the method body; referencing
121   //              LastStmt will remove the statement from the method body since
122   //              it will be linked from the new expression being constructed.
123   enum class PlaceHolder { _0, _1, _2, _3, _4, Handle = 128, LastStmt };
124 
125   Expr *convertPlaceholder(PlaceHolder PH);
convertPlaceholderclang::hlsl::BuiltinTypeMethodBuilder126   Expr *convertPlaceholder(Expr *E) { return E; }
127 
128 public:
129   friend BuiltinTypeDeclBuilder;
130 
BuiltinTypeMethodBuilderclang::hlsl::BuiltinTypeMethodBuilder131   BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name,
132                            QualType ReturnTy, bool IsConst = false,
133                            bool IsCtor = false)
134       : DeclBuilder(DB), Name(Name), ReturnTy(ReturnTy), Method(nullptr),
135         IsConst(IsConst), IsCtor(IsCtor) {}
136 
137   BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef NameStr,
138                            QualType ReturnTy, bool IsConst = false,
139                            bool IsCtor = false);
140   BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;
141 
~BuiltinTypeMethodBuilderclang::hlsl::BuiltinTypeMethodBuilder142   ~BuiltinTypeMethodBuilder() { finalize(); }
143 
144   BuiltinTypeMethodBuilder &
145   operator=(const BuiltinTypeMethodBuilder &Other) = delete;
146 
147   BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty,
148                                      HLSLParamModifierAttr::Spelling Modifier =
149                                          HLSLParamModifierAttr::Keyword_in);
150   template <typename... Ts>
151   BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName,
152                                         QualType ReturnType, Ts... ArgSpecs);
153   template <typename TLHS, typename TRHS>
154   BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS);
155   template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr);
156   BuiltinTypeDeclBuilder &finalize();
157   Expr *getResourceHandleExpr();
158 
159 private:
160   void createDecl();
161 
162   // Makes sure the declaration is created; should be called before any
163   // statement added to the body or when access to 'this' is needed.
ensureCompleteDeclclang::hlsl::BuiltinTypeMethodBuilder164   void ensureCompleteDecl() {
165     if (!Method)
166       createDecl();
167   }
168 };
169 
~TemplateParameterListBuilder()170 TemplateParameterListBuilder::~TemplateParameterListBuilder() {
171   finalizeTemplateArgs();
172 }
173 
174 TemplateParameterListBuilder &
addTypeParameter(StringRef Name,QualType DefaultValue)175 TemplateParameterListBuilder::addTypeParameter(StringRef Name,
176                                                QualType DefaultValue) {
177   assert(!Builder.Record->isCompleteDefinition() &&
178          "record is already complete");
179   ASTContext &AST = Builder.SemaRef.getASTContext();
180   unsigned Position = static_cast<unsigned>(Params.size());
181   auto *Decl = TemplateTypeParmDecl::Create(
182       AST, Builder.Record->getDeclContext(), SourceLocation(), SourceLocation(),
183       /* TemplateDepth */ 0, Position,
184       &AST.Idents.get(Name, tok::TokenKind::identifier),
185       /* Typename */ true,
186       /* ParameterPack */ false,
187       /* HasTypeConstraint*/ false);
188   if (!DefaultValue.isNull())
189     Decl->setDefaultArgument(AST,
190                              Builder.SemaRef.getTrivialTemplateArgumentLoc(
191                                  DefaultValue, QualType(), SourceLocation()));
192 
193   Params.emplace_back(Decl);
194   return *this;
195 }
196 
197 // The concept specialization expression (CSE) constructed in
198 // constructConceptSpecializationExpr is constructed so that it
199 // matches the CSE that is constructed when parsing the below C++ code:
200 //
201 // template<typename T>
202 // concept is_typed_resource_element_compatible =
203 // __builtin_hlsl_typed_resource_element_compatible<T>
204 //
205 // template<typename element_type> requires
206 // is_typed_resource_element_compatible<element_type>
207 // struct RWBuffer {
208 //     element_type Val;
209 // };
210 //
211 // int fn() {
212 //     RWBuffer<int> Buf;
213 // }
214 //
215 // When dumping the AST and filtering for "RWBuffer", the resulting AST
216 // structure is what we're trying to construct below, specifically the
217 // CSE portion.
218 ConceptSpecializationExpr *
constructConceptSpecializationExpr(Sema & S,ConceptDecl * CD)219 TemplateParameterListBuilder::constructConceptSpecializationExpr(
220     Sema &S, ConceptDecl *CD) {
221   ASTContext &Context = S.getASTContext();
222   SourceLocation Loc = Builder.Record->getBeginLoc();
223   DeclarationNameInfo DNI(CD->getDeclName(), Loc);
224   NestedNameSpecifierLoc NNSLoc;
225   DeclContext *DC = Builder.Record->getDeclContext();
226   TemplateArgumentListInfo TALI(Loc, Loc);
227 
228   // Assume that the concept decl has just one template parameter
229   // This parameter should have been added when CD was constructed
230   // in getTypedBufferConceptDecl
231   assert(CD->getTemplateParameters()->size() == 1 &&
232          "unexpected concept decl parameter count");
233   TemplateTypeParmDecl *ConceptTTPD =
234       dyn_cast<TemplateTypeParmDecl>(CD->getTemplateParameters()->getParam(0));
235 
236   // this TemplateTypeParmDecl is the template for the resource, and is
237   // used to construct a template argumentthat will be used
238   // to construct the ImplicitConceptSpecializationDecl
239   TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
240       Context,                          // AST context
241       Builder.Record->getDeclContext(), // DeclContext
242       SourceLocation(), SourceLocation(),
243       /*D=*/0,                    // Depth in the template parameter list
244       /*P=*/0,                    // Position in the template parameter list
245       /*Id=*/nullptr,             // Identifier for 'T'
246       /*Typename=*/true,          // Indicates this is a 'typename' or 'class'
247       /*ParameterPack=*/false,    // Not a parameter pack
248       /*HasTypeConstraint=*/false // Has no type constraint
249   );
250 
251   T->setDeclContext(DC);
252 
253   QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD);
254 
255   // this is the 2nd template argument node, on which
256   // the concept constraint is actually being applied: 'element_type'
257   TemplateArgument ConceptTA = TemplateArgument(ConceptTType);
258 
259   QualType CSETType = Context.getTypeDeclType(T);
260 
261   // this is the 1st template argument node, which represents
262   // the abstract type that a concept would refer to: 'T'
263   TemplateArgument CSETA = TemplateArgument(CSETType);
264 
265   ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
266       ImplicitConceptSpecializationDecl::Create(
267           Context, Builder.Record->getDeclContext(), Loc, {CSETA});
268 
269   // Constraint satisfaction is used to construct the
270   // ConceptSpecailizationExpr, and represents the 2nd Template Argument,
271   // located at the bottom of the sample AST above.
272   const ConstraintSatisfaction CS(CD, {ConceptTA});
273   TemplateArgumentLoc TAL =
274       S.getTrivialTemplateArgumentLoc(ConceptTA, QualType(), SourceLocation());
275 
276   TALI.addArgument(TAL);
277   const ASTTemplateArgumentListInfo *ATALI =
278       ASTTemplateArgumentListInfo::Create(Context, TALI);
279 
280   // In the concept reference, ATALI is what adds the extra
281   // TemplateArgument node underneath CSE
282   ConceptReference *CR =
283       ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI);
284 
285   ConceptSpecializationExpr *CSE =
286       ConceptSpecializationExpr::Create(Context, CR, ImplicitCSEDecl, &CS);
287 
288   return CSE;
289 }
290 
291 BuiltinTypeDeclBuilder &
finalizeTemplateArgs(ConceptDecl * CD)292 TemplateParameterListBuilder::finalizeTemplateArgs(ConceptDecl *CD) {
293   if (Params.empty())
294     return Builder;
295 
296   ASTContext &AST = Builder.SemaRef.Context;
297   ConceptSpecializationExpr *CSE =
298       CD ? constructConceptSpecializationExpr(Builder.SemaRef, CD) : nullptr;
299   auto *ParamList = TemplateParameterList::Create(
300       AST, SourceLocation(), SourceLocation(), Params, SourceLocation(), CSE);
301   Builder.Template = ClassTemplateDecl::Create(
302       AST, Builder.Record->getDeclContext(), SourceLocation(),
303       DeclarationName(Builder.Record->getIdentifier()), ParamList,
304       Builder.Record);
305 
306   Builder.Record->setDescribedClassTemplate(Builder.Template);
307   Builder.Template->setImplicit(true);
308   Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext());
309 
310   // NOTE: setPreviousDecl before addDecl so new decl replace old decl when
311   // make visible.
312   Builder.Template->setPreviousDecl(Builder.PrevTemplate);
313   Builder.Record->getDeclContext()->addDecl(Builder.Template);
314   Params.clear();
315 
316   QualType T = Builder.Template->getInjectedClassNameSpecialization();
317   T = AST.getInjectedClassNameType(Builder.Record, T);
318 
319   return Builder;
320 }
321 
convertPlaceholder(PlaceHolder PH)322 Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) {
323   if (PH == PlaceHolder::Handle)
324     return getResourceHandleExpr();
325 
326   if (PH == PlaceHolder::LastStmt) {
327     assert(!StmtsList.empty() && "no statements in the list");
328     Stmt *LastStmt = StmtsList.pop_back_val();
329     assert(isa<ValueStmt>(LastStmt) && "last statement does not have a value");
330     return cast<ValueStmt>(LastStmt)->getExprStmt();
331   }
332 
333   ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
334   ParmVarDecl *ParamDecl = Method->getParamDecl(static_cast<unsigned>(PH));
335   return DeclRefExpr::Create(
336       AST, NestedNameSpecifierLoc(), SourceLocation(), ParamDecl, false,
337       DeclarationNameInfo(ParamDecl->getDeclName(), SourceLocation()),
338       ParamDecl->getType(), VK_PRValue);
339 }
340 
BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder & DB,StringRef NameStr,QualType ReturnTy,bool IsConst,bool IsCtor)341 BuiltinTypeMethodBuilder::BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB,
342                                                    StringRef NameStr,
343                                                    QualType ReturnTy,
344                                                    bool IsConst, bool IsCtor)
345     : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst),
346       IsCtor(IsCtor) {
347 
348   assert((!NameStr.empty() || IsCtor) && "method needs a name");
349   assert(((IsCtor && !IsConst) || !IsCtor) && "constructor cannot be const");
350 
351   ASTContext &AST = DB.SemaRef.getASTContext();
352   if (IsCtor) {
353     Name = AST.DeclarationNames.getCXXConstructorName(
354         DB.Record->getTypeForDecl()->getCanonicalTypeUnqualified());
355   } else {
356     const IdentifierInfo &II =
357         AST.Idents.get(NameStr, tok::TokenKind::identifier);
358     Name = DeclarationName(&II);
359   }
360 }
361 
362 BuiltinTypeMethodBuilder &
addParam(StringRef Name,QualType Ty,HLSLParamModifierAttr::Spelling Modifier)363 BuiltinTypeMethodBuilder::addParam(StringRef Name, QualType Ty,
364                                    HLSLParamModifierAttr::Spelling Modifier) {
365   assert(Method == nullptr && "Cannot add param, method already created");
366   const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get(
367       Name, tok::TokenKind::identifier);
368   Params.emplace_back(II, Ty, Modifier);
369   return *this;
370 }
371 
createDecl()372 void BuiltinTypeMethodBuilder::createDecl() {
373   assert(Method == nullptr && "Method or constructor is already created");
374 
375   // create method or constructor type
376   ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
377   SmallVector<QualType> ParamTypes;
378   for (Param &MP : Params)
379     ParamTypes.emplace_back(MP.Ty);
380 
381   FunctionProtoType::ExtProtoInfo ExtInfo;
382   if (IsConst)
383     ExtInfo.TypeQuals.addConst();
384 
385   QualType FuncTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo);
386 
387   // create method or constructor decl
388   auto *TSInfo = AST.getTrivialTypeSourceInfo(FuncTy, SourceLocation());
389   DeclarationNameInfo NameInfo = DeclarationNameInfo(Name, SourceLocation());
390   if (IsCtor)
391     Method = CXXConstructorDecl::Create(
392         AST, DeclBuilder.Record, SourceLocation(), NameInfo, FuncTy, TSInfo,
393         ExplicitSpecifier(), false, true, false,
394         ConstexprSpecKind::Unspecified);
395   else
396     Method =
397         CXXMethodDecl::Create(AST, DeclBuilder.Record, SourceLocation(),
398                               NameInfo, FuncTy, TSInfo, SC_None, false, false,
399                               ConstexprSpecKind::Unspecified, SourceLocation());
400 
401   // create params & set them to the function prototype
402   SmallVector<ParmVarDecl *> ParmDecls;
403   unsigned CurScopeDepth = DeclBuilder.SemaRef.getCurScope()->getDepth();
404   auto FnProtoLoc =
405       Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
406   for (int I = 0, E = Params.size(); I != E; I++) {
407     Param &MP = Params[I];
408     ParmVarDecl *Parm = ParmVarDecl::Create(
409         AST, Method->getDeclContext(), SourceLocation(), SourceLocation(),
410         &MP.NameII, MP.Ty,
411         AST.getTrivialTypeSourceInfo(MP.Ty, SourceLocation()), SC_None,
412         nullptr);
413     if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) {
414       auto *Mod =
415           HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier);
416       Parm->addAttr(Mod);
417     }
418     Parm->setScopeInfo(CurScopeDepth, I);
419     ParmDecls.push_back(Parm);
420     FnProtoLoc.setParam(I, Parm);
421   }
422   Method->setParams({ParmDecls});
423 }
424 
getResourceHandleExpr()425 Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() {
426   ensureCompleteDecl();
427 
428   ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
429   CXXThisExpr *This = CXXThisExpr::Create(
430       AST, SourceLocation(), Method->getFunctionObjectParameterType(), true);
431   FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
432   return MemberExpr::CreateImplicit(AST, This, false, HandleField,
433                                     HandleField->getType(), VK_LValue,
434                                     OK_Ordinary);
435 }
436 
437 template <typename... Ts>
438 BuiltinTypeMethodBuilder &
callBuiltin(StringRef BuiltinName,QualType ReturnType,Ts...ArgSpecs)439 BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName,
440                                       QualType ReturnType, Ts... ArgSpecs) {
441   std::array<Expr *, sizeof...(ArgSpecs)> Args{
442       convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
443 
444   ensureCompleteDecl();
445 
446   ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
447   FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName);
448   DeclRefExpr *DRE = DeclRefExpr::Create(
449       AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false,
450       FD->getNameInfo(), AST.BuiltinFnTy, VK_PRValue);
451 
452   auto *ImpCast = ImplicitCastExpr::Create(
453       AST, AST.getPointerType(FD->getType()), CK_BuiltinFnToFnPtr, DRE, nullptr,
454       VK_PRValue, FPOptionsOverride());
455 
456   if (ReturnType.isNull())
457     ReturnType = FD->getReturnType();
458 
459   Expr *Call = CallExpr::Create(AST, ImpCast, Args, ReturnType, VK_PRValue,
460                                 SourceLocation(), FPOptionsOverride());
461   StmtsList.push_back(Call);
462   return *this;
463 }
464 
465 template <typename TLHS, typename TRHS>
assign(TLHS LHS,TRHS RHS)466 BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::assign(TLHS LHS, TRHS RHS) {
467   Expr *LHSExpr = convertPlaceholder(LHS);
468   Expr *RHSExpr = convertPlaceholder(RHS);
469   Stmt *AssignStmt = BinaryOperator::Create(
470       DeclBuilder.SemaRef.getASTContext(), LHSExpr, RHSExpr, BO_Assign,
471       LHSExpr->getType(), ExprValueKind::VK_PRValue,
472       ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride());
473   StmtsList.push_back(AssignStmt);
474   return *this;
475 }
476 
477 template <typename T>
dereference(T Ptr)478 BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) {
479   Expr *PtrExpr = convertPlaceholder(Ptr);
480   Expr *Deref =
481       UnaryOperator::Create(DeclBuilder.SemaRef.getASTContext(), PtrExpr,
482                             UO_Deref, PtrExpr->getType()->getPointeeType(),
483                             VK_PRValue, OK_Ordinary, SourceLocation(),
484                             /*CanOverflow=*/false, FPOptionsOverride());
485   StmtsList.push_back(Deref);
486   return *this;
487 }
488 
finalize()489 BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalize() {
490   assert(!DeclBuilder.Record->isCompleteDefinition() &&
491          "record is already complete");
492 
493   ensureCompleteDecl();
494 
495   if (!Method->hasBody()) {
496     ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
497     assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) &&
498            "nothing to return from non-void method");
499     if (ReturnTy != AST.VoidTy) {
500       if (Expr *LastExpr = dyn_cast<Expr>(StmtsList.back())) {
501         assert(AST.hasSameUnqualifiedType(LastExpr->getType(),
502                                           ReturnTy.getNonReferenceType()) &&
503                "Return type of the last statement must match the return type "
504                "of the method");
505         if (!isa<ReturnStmt>(LastExpr)) {
506           StmtsList.pop_back();
507           StmtsList.push_back(
508               ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr));
509         }
510       }
511     }
512 
513     Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(),
514                                          SourceLocation(), SourceLocation()));
515     Method->setLexicalDeclContext(DeclBuilder.Record);
516     Method->setAccess(AccessSpecifier::AS_public);
517     Method->addAttr(AlwaysInlineAttr::CreateImplicit(
518         AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline));
519     DeclBuilder.Record->addDecl(Method);
520   }
521   return DeclBuilder;
522 }
523 
BuiltinTypeDeclBuilder(Sema & SemaRef,CXXRecordDecl * R)524 BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R)
525     : SemaRef(SemaRef), Record(R) {
526   Record->startDefinition();
527   Template = Record->getDescribedClassTemplate();
528 }
529 
BuiltinTypeDeclBuilder(Sema & SemaRef,NamespaceDecl * Namespace,StringRef Name)530 BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef,
531                                                NamespaceDecl *Namespace,
532                                                StringRef Name)
533     : SemaRef(SemaRef), HLSLNamespace(Namespace) {
534   ASTContext &AST = SemaRef.getASTContext();
535   IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
536 
537   LookupResult Result(SemaRef, &II, SourceLocation(), Sema::LookupTagName);
538   CXXRecordDecl *PrevDecl = nullptr;
539   if (SemaRef.LookupQualifiedName(Result, HLSLNamespace)) {
540     // Declaration already exists (from precompiled headers)
541     NamedDecl *Found = Result.getFoundDecl();
542     if (auto *TD = dyn_cast<ClassTemplateDecl>(Found)) {
543       PrevDecl = TD->getTemplatedDecl();
544       PrevTemplate = TD;
545     } else
546       PrevDecl = dyn_cast<CXXRecordDecl>(Found);
547     assert(PrevDecl && "Unexpected lookup result type.");
548   }
549 
550   if (PrevDecl && PrevDecl->isCompleteDefinition()) {
551     Record = PrevDecl;
552     Template = PrevTemplate;
553     return;
554   }
555 
556   Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace,
557                                  SourceLocation(), SourceLocation(), &II,
558                                  PrevDecl, true);
559   Record->setImplicit(true);
560   Record->setLexicalDeclContext(HLSLNamespace);
561   Record->setHasExternalLexicalStorage();
562 
563   // Don't let anyone derive from built-in types.
564   Record->addAttr(
565       FinalAttr::CreateImplicit(AST, SourceRange(), FinalAttr::Keyword_final));
566 }
567 
~BuiltinTypeDeclBuilder()568 BuiltinTypeDeclBuilder::~BuiltinTypeDeclBuilder() {
569   if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)
570     HLSLNamespace->addDecl(Record);
571 }
572 
finalizeForwardDeclaration()573 CXXRecordDecl *BuiltinTypeDeclBuilder::finalizeForwardDeclaration() {
574   // Force the QualType to be generated for the record declaration. In most
575   // cases this will happen naturally when something uses the type the
576   // QualType gets lazily created. Unfortunately, with our injected types if a
577   // type isn't used in a translation unit the QualType may not get
578   // automatically generated before a PCH is generated. To resolve this we
579   // just force that the QualType is generated after we create a forward
580   // declaration.
581   (void)Record->getASTContext().getRecordType(Record);
582   return Record;
583 }
584 
585 BuiltinTypeDeclBuilder &
addMemberVariable(StringRef Name,QualType Type,llvm::ArrayRef<Attr * > Attrs,AccessSpecifier Access)586 BuiltinTypeDeclBuilder::addMemberVariable(StringRef Name, QualType Type,
587                                           llvm::ArrayRef<Attr *> Attrs,
588                                           AccessSpecifier Access) {
589   assert(!Record->isCompleteDefinition() && "record is already complete");
590   assert(Record->isBeingDefined() &&
591          "Definition must be started before adding members!");
592   ASTContext &AST = Record->getASTContext();
593 
594   IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
595   TypeSourceInfo *MemTySource =
596       AST.getTrivialTypeSourceInfo(Type, SourceLocation());
597   auto *Field = FieldDecl::Create(
598       AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource,
599       nullptr, false, InClassInitStyle::ICIS_NoInit);
600   Field->setAccess(Access);
601   Field->setImplicit(true);
602   for (Attr *A : Attrs) {
603     if (A)
604       Field->addAttr(A);
605   }
606 
607   Record->addDecl(Field);
608   Fields[Name] = Field;
609   return *this;
610 }
611 
addHandleMember(ResourceClass RC,bool IsROV,bool RawBuffer,AccessSpecifier Access)612 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember(
613     ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) {
614   assert(!Record->isCompleteDefinition() && "record is already complete");
615 
616   ASTContext &Ctx = SemaRef.getASTContext();
617   TypeSourceInfo *ElementTypeInfo =
618       Ctx.getTrivialTypeSourceInfo(getHandleElementType(), SourceLocation());
619 
620   // add handle member with resource type attributes
621   QualType AttributedResTy = QualType();
622   SmallVector<const Attr *> Attrs = {
623       HLSLResourceClassAttr::CreateImplicit(Ctx, RC),
624       IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr,
625       RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr,
626       ElementTypeInfo
627           ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo)
628           : nullptr};
629   if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs,
630                                        AttributedResTy))
631     addMemberVariable("__handle", AttributedResTy, {}, Access);
632   return *this;
633 }
634 
635 // Adds default constructor to the resource class:
636 // Resource::Resource()
addDefaultHandleConstructor()637 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
638   if (Record->isCompleteDefinition())
639     return *this;
640 
641   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
642   QualType HandleType = getResourceHandleField()->getType();
643   return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy,
644                                   false, true)
645       .callBuiltin("__builtin_hlsl_resource_uninitializedhandle", HandleType,
646                    PH::Handle)
647       .assign(PH::Handle, PH::LastStmt)
648       .finalize();
649 }
650 
651 BuiltinTypeDeclBuilder &
addHandleConstructorFromBinding()652 BuiltinTypeDeclBuilder::addHandleConstructorFromBinding() {
653   if (Record->isCompleteDefinition())
654     return *this;
655 
656   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
657   ASTContext &AST = SemaRef.getASTContext();
658   QualType HandleType = getResourceHandleField()->getType();
659 
660   return BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true)
661       .addParam("registerNo", AST.UnsignedIntTy)
662       .addParam("spaceNo", AST.UnsignedIntTy)
663       .addParam("range", AST.IntTy)
664       .addParam("index", AST.UnsignedIntTy)
665       .addParam("name", AST.getPointerType(AST.CharTy.withConst()))
666       .callBuiltin("__builtin_hlsl_resource_handlefrombinding", HandleType,
667                    PH::Handle, PH::_0, PH::_1, PH::_2, PH::_3, PH::_4)
668       .assign(PH::Handle, PH::LastStmt)
669       .finalize();
670 }
671 
672 BuiltinTypeDeclBuilder &
addHandleConstructorFromImplicitBinding()673 BuiltinTypeDeclBuilder::addHandleConstructorFromImplicitBinding() {
674   if (Record->isCompleteDefinition())
675     return *this;
676 
677   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
678   ASTContext &AST = SemaRef.getASTContext();
679   QualType HandleType = getResourceHandleField()->getType();
680 
681   return BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true)
682       .addParam("spaceNo", AST.UnsignedIntTy)
683       .addParam("range", AST.IntTy)
684       .addParam("index", AST.UnsignedIntTy)
685       .addParam("orderId", AST.UnsignedIntTy)
686       .addParam("name", AST.getPointerType(AST.CharTy.withConst()))
687       .callBuiltin("__builtin_hlsl_resource_handlefromimplicitbinding",
688                    HandleType, PH::Handle, PH::_0, PH::_1, PH::_2, PH::_3,
689                    PH::_4)
690       .assign(PH::Handle, PH::LastStmt)
691       .finalize();
692 }
693 
addArraySubscriptOperators()694 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {
695   ASTContext &AST = Record->getASTContext();
696   DeclarationName Subscript =
697       AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
698 
699   addHandleAccessFunction(Subscript, /*IsConst=*/true, /*IsRef=*/true);
700   if (getResourceAttrs().ResourceClass == llvm::dxil::ResourceClass::UAV)
701     addHandleAccessFunction(Subscript, /*IsConst=*/false, /*IsRef=*/true);
702 
703   return *this;
704 }
705 
addLoadMethods()706 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() {
707   if (Record->isCompleteDefinition())
708     return *this;
709 
710   ASTContext &AST = Record->getASTContext();
711   IdentifierInfo &II = AST.Idents.get("Load", tok::TokenKind::identifier);
712   DeclarationName Load(&II);
713   // TODO: We also need versions with status for CheckAccessFullyMapped.
714   addHandleAccessFunction(Load, /*IsConst=*/false, /*IsRef=*/false);
715 
716   return *this;
717 }
718 
getResourceHandleField() const719 FieldDecl *BuiltinTypeDeclBuilder::getResourceHandleField() const {
720   auto I = Fields.find("__handle");
721   assert(I != Fields.end() &&
722          I->second->getType()->isHLSLAttributedResourceType() &&
723          "record does not have resource handle field");
724   return I->second;
725 }
726 
getFirstTemplateTypeParam()727 QualType BuiltinTypeDeclBuilder::getFirstTemplateTypeParam() {
728   assert(Template && "record it not a template");
729   if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
730           Template->getTemplateParameters()->getParam(0))) {
731     return QualType(TTD->getTypeForDecl(), 0);
732   }
733   return QualType();
734 }
735 
getHandleElementType()736 QualType BuiltinTypeDeclBuilder::getHandleElementType() {
737   if (Template)
738     return getFirstTemplateTypeParam();
739   // TODO: Should we default to VoidTy? Using `i8` is arguably ambiguous.
740   return SemaRef.getASTContext().Char8Ty;
741 }
742 
743 HLSLAttributedResourceType::Attributes
getResourceAttrs() const744 BuiltinTypeDeclBuilder::getResourceAttrs() const {
745   QualType HandleType = getResourceHandleField()->getType();
746   return cast<HLSLAttributedResourceType>(HandleType)->getAttrs();
747 }
748 
749 // BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::startDefinition() {
750 //   assert(!Record->isCompleteDefinition() && "record is already complete");
751 //   Record->startDefinition();
752 //   return *this;
753 // }
754 
completeDefinition()755 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::completeDefinition() {
756   assert(!Record->isCompleteDefinition() && "record is already complete");
757   assert(Record->isBeingDefined() &&
758          "Definition must be started before completing it.");
759 
760   Record->completeDefinition();
761   return *this;
762 }
763 
getConstantIntExpr(int value)764 Expr *BuiltinTypeDeclBuilder::getConstantIntExpr(int value) {
765   ASTContext &AST = SemaRef.getASTContext();
766   return IntegerLiteral::Create(
767       AST, llvm::APInt(AST.getTypeSize(AST.IntTy), value, true), AST.IntTy,
768       SourceLocation());
769 }
770 
771 BuiltinTypeDeclBuilder &
addSimpleTemplateParams(ArrayRef<StringRef> Names,ConceptDecl * CD=nullptr)772 BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
773                                                 ConceptDecl *CD = nullptr) {
774   if (Record->isCompleteDefinition()) {
775     assert(Template && "existing record it not a template");
776     assert(Template->getTemplateParameters()->size() == Names.size() &&
777            "template param count mismatch");
778     return *this;
779   }
780 
781   TemplateParameterListBuilder Builder = TemplateParameterListBuilder(*this);
782   for (StringRef Name : Names)
783     Builder.addTypeParameter(Name);
784   return Builder.finalizeTemplateArgs(CD);
785 }
786 
addIncrementCounterMethod()787 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
788   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
789   return BuiltinTypeMethodBuilder(*this, "IncrementCounter",
790                                   SemaRef.getASTContext().UnsignedIntTy)
791       .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
792                    PH::Handle, getConstantIntExpr(1))
793       .finalize();
794 }
795 
addDecrementCounterMethod()796 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
797   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
798   return BuiltinTypeMethodBuilder(*this, "DecrementCounter",
799                                   SemaRef.getASTContext().UnsignedIntTy)
800       .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
801                    PH::Handle, getConstantIntExpr(-1))
802       .finalize();
803 }
804 
805 BuiltinTypeDeclBuilder &
addHandleAccessFunction(DeclarationName & Name,bool IsConst,bool IsRef)806 BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name,
807                                                 bool IsConst, bool IsRef) {
808   assert(!Record->isCompleteDefinition() && "record is already complete");
809   ASTContext &AST = SemaRef.getASTContext();
810   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
811 
812   QualType ElemTy = getHandleElementType();
813   QualType AddrSpaceElemTy =
814       AST.getAddrSpaceQualType(ElemTy, LangAS::hlsl_device);
815   QualType ElemPtrTy = AST.getPointerType(AddrSpaceElemTy);
816   QualType ReturnTy;
817 
818   if (IsRef) {
819     ReturnTy = AddrSpaceElemTy;
820     if (IsConst)
821       ReturnTy.addConst();
822     ReturnTy = AST.getLValueReferenceType(ReturnTy);
823   } else {
824     ReturnTy = ElemTy;
825     if (IsConst)
826       ReturnTy.addConst();
827   }
828 
829   return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst)
830       .addParam("Index", AST.UnsignedIntTy)
831       .callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle,
832                    PH::_0)
833       .dereference(PH::LastStmt)
834       .finalize();
835 }
836 
addAppendMethod()837 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
838   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
839   ASTContext &AST = SemaRef.getASTContext();
840   QualType ElemTy = getHandleElementType();
841   QualType AddrSpaceElemTy =
842       AST.getAddrSpaceQualType(ElemTy, LangAS::hlsl_device);
843   return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy)
844       .addParam("value", ElemTy)
845       .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
846                    PH::Handle, getConstantIntExpr(1))
847       .callBuiltin("__builtin_hlsl_resource_getpointer",
848                    AST.getPointerType(AddrSpaceElemTy), PH::Handle,
849                    PH::LastStmt)
850       .dereference(PH::LastStmt)
851       .assign(PH::LastStmt, PH::_0)
852       .finalize();
853 }
854 
addConsumeMethod()855 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
856   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
857   ASTContext &AST = SemaRef.getASTContext();
858   QualType ElemTy = getHandleElementType();
859   QualType AddrSpaceElemTy =
860       AST.getAddrSpaceQualType(ElemTy, LangAS::hlsl_device);
861   return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy)
862       .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
863                    PH::Handle, getConstantIntExpr(-1))
864       .callBuiltin("__builtin_hlsl_resource_getpointer",
865                    AST.getPointerType(AddrSpaceElemTy), PH::Handle,
866                    PH::LastStmt)
867       .dereference(PH::LastStmt)
868       .finalize();
869 }
870 
871 } // namespace hlsl
872 } // namespace clang
873