[clang] [HLSL][NFC] Refactoring HLSLExternalSemaSource (PR #131032)
Helena Kotas via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 12 14:10:26 PDT 2025
https://github.com/hekota created https://github.com/llvm/llvm-project/pull/131032
Moving builder classes into separate files `HLSLBuiltinTypeDeclBuilder.cpp`/`.h` and changing a some `HLSLExternalSemaSource` methods to private.
This is a prep work before we start adding more builtin types and methods, like textures or resource constructors. For example constructors could make use of the `BuiltinTypeMethodBuilder`, but this helper class was defined in `HLSLExternalSemaSource.cpp` after the method that creates a constructor. Rather than reshuffling the code one big source file I am moving the builders into a separate cpp & header file and placing the helper classes declarations up top.
Currently the new header only exposes `BuiltinTypeDeclBuilder` to `HLSLExternalSemaSource`. In the future but we might decide to expose more helper classes as needed.
>From 7ebfc826ca27c71ef80f4a1b38b2d3a2e155b777 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 12 Mar 2025 14:09:24 -0700
Subject: [PATCH] [HLSL][NFC] Refactoring HLSLExternalSemaSource
Moving builder classes into separate files `HLSLBuiltinTypeDeclBuilder.cpp`/`.h` and changing a some `HLSLExternalSemaSource` methods to private.
This is a prep work before we start adding more builtin types and methods, like textures or resource constructors. For example constructors could make use of the `BuiltinTypeMethodBuilder`, but this helper class was defined in `HLSLExternalSemaSource.cpp` after the method that creates a constructor. Rather than reshuffling the code one big source file I am moving the builders into a separate cpp & header file and placing the helper classes declarations up top.
Currently the new header only exposes `BuiltinTypeDeclBuilder` to `HLSLExternalSemaSource`. In the future but we might decide to expose more helper classes as needed.
---
.../clang/Sema/HLSLExternalSemaSource.h | 15 +-
clang/lib/Sema/CMakeLists.txt | 1 +
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 782 ++++++++++++++++++
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 97 +++
clang/lib/Sema/HLSLExternalSemaSource.cpp | 750 +----------------
5 files changed, 890 insertions(+), 755 deletions(-)
create mode 100644 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
create mode 100644 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
diff --git a/clang/include/clang/Sema/HLSLExternalSemaSource.h b/clang/include/clang/Sema/HLSLExternalSemaSource.h
index 3c7495e66055d..9c1b16ba3950c 100644
--- a/clang/include/clang/Sema/HLSLExternalSemaSource.h
+++ b/clang/include/clang/Sema/HLSLExternalSemaSource.h
@@ -21,20 +21,15 @@ class NamespaceDecl;
class Sema;
class HLSLExternalSemaSource : public ExternalSemaSource {
+private:
Sema *SemaPtr = nullptr;
NamespaceDecl *HLSLNamespace = nullptr;
using CompletionFunction = std::function<void(CXXRecordDecl *)>;
llvm::DenseMap<CXXRecordDecl *, CompletionFunction> Completions;
- void defineHLSLVectorAlias();
- void defineTrivialHLSLTypes();
- void defineHLSLTypesWithForwardDeclarations();
-
- void onCompletion(CXXRecordDecl *Record, CompletionFunction Fn);
-
public:
- ~HLSLExternalSemaSource() override;
+ ~HLSLExternalSemaSource() override {}
/// Initialize the semantic source with the Sema instance
/// being used to perform semantic analysis on the abstract syntax
@@ -47,6 +42,12 @@ class HLSLExternalSemaSource : public ExternalSemaSource {
using ExternalASTSource::CompleteType;
/// Complete an incomplete HLSL builtin type
void CompleteType(TagDecl *Tag) override;
+
+private:
+ void defineTrivialHLSLTypes();
+ void defineHLSLVectorAlias();
+ void defineHLSLTypesWithForwardDeclarations();
+ void onCompletion(CXXRecordDecl *Record, CompletionFunction Fn);
};
} // namespace clang
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 1a351684d133e..d3fe80f659f69 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -20,6 +20,7 @@ add_clang_library(clangSema
DeclSpec.cpp
DelayedDiagnostic.cpp
HeuristicResolver.cpp
+ HLSLBuiltinTypeDeclBuilder.cpp
HLSLExternalSemaSource.cpp
IdentifierResolver.cpp
JumpDiagnostics.cpp
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
new file mode 100644
index 0000000000000..db0ed3434d837
--- /dev/null
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -0,0 +1,782 @@
+//===--- HLSLBuiltinTypeDeclBuilder.cpp - HLSL Builtin Type Decl Builder --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+#include "HLSLBuiltinTypeDeclBuilder.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaHLSL.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm::hlsl;
+
+namespace clang {
+
+namespace hlsl {
+
+namespace {
+
+static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) {
+ IdentifierInfo &II =
+ S.getASTContext().Idents.get(Name, tok::TokenKind::identifier);
+ DeclarationNameInfo NameInfo =
+ DeclarationNameInfo(DeclarationName(&II), SourceLocation());
+ LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
+ // AllowBuiltinCreation is false but LookupDirect will create
+ // the builtin when searching the global scope anyways...
+ S.LookupName(R, S.getCurScope());
+ // FIXME: If the builtin function was user-declared in global scope,
+ // this assert *will* fail. Should this call LookupBuiltin instead?
+ assert(R.isSingleResult() &&
+ "Since this is a builtin it should always resolve!");
+ return cast<FunctionDecl>(R.getFoundDecl());
+}
+} // namespace
+
+// Builder for template arguments of builtin types. Used internally
+// by BuiltinTypeDeclBuilder.
+struct TemplateParameterListBuilder {
+ BuiltinTypeDeclBuilder &Builder;
+ llvm::SmallVector<NamedDecl *> Params;
+
+ TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {}
+ ~TemplateParameterListBuilder();
+
+ TemplateParameterListBuilder &
+ addTypeParameter(StringRef Name, QualType DefaultValue = QualType());
+
+ ConceptSpecializationExpr *
+ constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD);
+
+ BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr);
+};
+
+// Builder for methods of builtin types. Allows adding methods to builtin types
+// using the builder pattern like this:
+//
+// BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType)
+// .addParam("param_name", Type, InOutModifier)
+// .callBuiltin("builtin_name", BuiltinParams...)
+// .finalizeMethod();
+//
+// The builder needs to have all of the method parameters before it can create
+// a CXXMethodDecl. It collects them in addParam calls and when a first
+// method that builds the body is called or when access to 'this` is needed it
+// creates the CXXMethodDecl and ParmVarDecls instances. These can then be
+// referenced from the body building methods. Destructor or an explicit call to
+// finalizeMethod() will complete the method definition.
+//
+// The callBuiltin helper method accepts constants via `Expr *` or placeholder
+// value arguments to indicate which function arguments to forward to the
+// builtin.
+//
+// If the method that is being built has a non-void return type the
+// finalizeMethod will create a return statent with the value of the last
+// statement (unless the last statement is already a ReturnStmt).
+struct BuiltinTypeMethodBuilder {
+private:
+ struct MethodParam {
+ const IdentifierInfo &NameII;
+ QualType Ty;
+ HLSLParamModifierAttr::Spelling Modifier;
+ MethodParam(const IdentifierInfo &NameII, QualType Ty,
+ HLSLParamModifierAttr::Spelling Modifier)
+ : NameII(NameII), Ty(Ty), Modifier(Modifier) {}
+ };
+
+ BuiltinTypeDeclBuilder &DeclBuilder;
+ DeclarationNameInfo NameInfo;
+ QualType ReturnTy;
+ CXXMethodDecl *Method;
+ bool IsConst;
+ llvm::SmallVector<MethodParam> Params;
+ llvm::SmallVector<Stmt *> StmtsList;
+
+ // Argument placeholders, inspired by std::placeholder. These are the indices
+ // of arguments to forward to `callBuiltin` and other method builder methods.
+ // Additional special values are:
+ // Handle - refers to the resource handle.
+ // LastStmt - refers to the last statement in the method body; referencing
+ // LastStmt will remove the statement from the method body since
+ // it will be linked from the new expression being constructed.
+ enum class PlaceHolder { _0, _1, _2, _3, Handle = 128, LastStmt };
+
+ Expr *convertPlaceholder(PlaceHolder PH);
+ Expr *convertPlaceholder(Expr *E) { return E; }
+
+public:
+ friend BuiltinTypeDeclBuilder;
+
+ BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name,
+ QualType ReturnTy, bool IsConst = false)
+ : DeclBuilder(DB), NameInfo(DeclarationNameInfo(Name, SourceLocation())),
+ ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {}
+
+ BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef Name,
+ QualType ReturnTy, bool IsConst = false);
+ BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;
+
+ ~BuiltinTypeMethodBuilder() { finalizeMethod(); }
+
+ BuiltinTypeMethodBuilder &
+ operator=(const BuiltinTypeMethodBuilder &Other) = delete;
+
+ BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty,
+ HLSLParamModifierAttr::Spelling Modifier =
+ HLSLParamModifierAttr::Keyword_in);
+ template <typename... Ts>
+ BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName,
+ QualType ReturnType, Ts... ArgSpecs);
+ template <typename TLHS, typename TRHS>
+ BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS);
+ template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr);
+ BuiltinTypeDeclBuilder &finalizeMethod();
+ Expr *getResourceHandleExpr();
+
+private:
+ void createMethodDecl();
+};
+
+TemplateParameterListBuilder::~TemplateParameterListBuilder() {
+ finalizeTemplateArgs();
+}
+
+TemplateParameterListBuilder &
+TemplateParameterListBuilder::addTypeParameter(StringRef Name,
+ QualType DefaultValue) {
+ assert(!Builder.Record->isCompleteDefinition() &&
+ "record is already complete");
+ ASTContext &AST = Builder.SemaRef.getASTContext();
+ unsigned Position = static_cast<unsigned>(Params.size());
+ auto *Decl = TemplateTypeParmDecl::Create(
+ AST, Builder.Record->getDeclContext(), SourceLocation(), SourceLocation(),
+ /* TemplateDepth */ 0, Position,
+ &AST.Idents.get(Name, tok::TokenKind::identifier),
+ /* Typename */ true,
+ /* ParameterPack */ false,
+ /* HasTypeConstraint*/ false);
+ if (!DefaultValue.isNull())
+ Decl->setDefaultArgument(AST,
+ Builder.SemaRef.getTrivialTemplateArgumentLoc(
+ DefaultValue, QualType(), SourceLocation()));
+
+ Params.emplace_back(Decl);
+ return *this;
+}
+
+// The concept specialization expression (CSE) constructed in
+// constructConceptSpecializationExpr is constructed so that it
+// matches the CSE that is constructed when parsing the below C++ code:
+//
+// template<typename T>
+// concept is_typed_resource_element_compatible =
+// __builtin_hlsl_typed_resource_element_compatible<T>
+//
+// template<typename element_type> requires
+// is_typed_resource_element_compatible<element_type>
+// struct RWBuffer {
+// element_type Val;
+// };
+//
+// int fn() {
+// RWBuffer<int> Buf;
+// }
+//
+// When dumping the AST and filtering for "RWBuffer", the resulting AST
+// structure is what we're trying to construct below, specifically the
+// CSE portion.
+ConceptSpecializationExpr *
+TemplateParameterListBuilder::constructConceptSpecializationExpr(
+ Sema &S, ConceptDecl *CD) {
+ ASTContext &Context = S.getASTContext();
+ SourceLocation Loc = Builder.Record->getBeginLoc();
+ DeclarationNameInfo DNI(CD->getDeclName(), Loc);
+ NestedNameSpecifierLoc NNSLoc;
+ DeclContext *DC = Builder.Record->getDeclContext();
+ TemplateArgumentListInfo TALI(Loc, Loc);
+
+ // Assume that the concept decl has just one template parameter
+ // This parameter should have been added when CD was constructed
+ // in getTypedBufferConceptDecl
+ assert(CD->getTemplateParameters()->size() == 1 &&
+ "unexpected concept decl parameter count");
+ TemplateTypeParmDecl *ConceptTTPD =
+ dyn_cast<TemplateTypeParmDecl>(CD->getTemplateParameters()->getParam(0));
+
+ // this TemplateTypeParmDecl is the template for the resource, and is
+ // used to construct a template argumentthat will be used
+ // to construct the ImplicitConceptSpecializationDecl
+ TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
+ Context, // AST context
+ Builder.Record->getDeclContext(), // DeclContext
+ SourceLocation(), SourceLocation(),
+ /*D=*/0, // Depth in the template parameter list
+ /*P=*/0, // Position in the template parameter list
+ /*Id=*/nullptr, // Identifier for 'T'
+ /*Typename=*/true, // Indicates this is a 'typename' or 'class'
+ /*ParameterPack=*/false, // Not a parameter pack
+ /*HasTypeConstraint=*/false // Has no type constraint
+ );
+
+ T->setDeclContext(DC);
+
+ QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD);
+
+ // this is the 2nd template argument node, on which
+ // the concept constraint is actually being applied: 'element_type'
+ TemplateArgument ConceptTA = TemplateArgument(ConceptTType);
+
+ QualType CSETType = Context.getTypeDeclType(T);
+
+ // this is the 1st template argument node, which represents
+ // the abstract type that a concept would refer to: 'T'
+ TemplateArgument CSETA = TemplateArgument(CSETType);
+
+ ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
+ ImplicitConceptSpecializationDecl::Create(
+ Context, Builder.Record->getDeclContext(), Loc, {CSETA});
+
+ // Constraint satisfaction is used to construct the
+ // ConceptSpecailizationExpr, and represents the 2nd Template Argument,
+ // located at the bottom of the sample AST above.
+ const ConstraintSatisfaction CS(CD, {ConceptTA});
+ TemplateArgumentLoc TAL =
+ S.getTrivialTemplateArgumentLoc(ConceptTA, QualType(), SourceLocation());
+
+ TALI.addArgument(TAL);
+ const ASTTemplateArgumentListInfo *ATALI =
+ ASTTemplateArgumentListInfo::Create(Context, TALI);
+
+ // In the concept reference, ATALI is what adds the extra
+ // TemplateArgument node underneath CSE
+ ConceptReference *CR =
+ ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI);
+
+ ConceptSpecializationExpr *CSE =
+ ConceptSpecializationExpr::Create(Context, CR, ImplicitCSEDecl, &CS);
+
+ return CSE;
+}
+
+BuiltinTypeDeclBuilder &
+TemplateParameterListBuilder::finalizeTemplateArgs(ConceptDecl *CD) {
+ if (Params.empty())
+ return Builder;
+
+ ASTContext &AST = Builder.SemaRef.Context;
+ ConceptSpecializationExpr *CSE =
+ CD ? constructConceptSpecializationExpr(Builder.SemaRef, CD) : nullptr;
+ auto *ParamList = TemplateParameterList::Create(
+ AST, SourceLocation(), SourceLocation(), Params, SourceLocation(), CSE);
+ Builder.Template = ClassTemplateDecl::Create(
+ AST, Builder.Record->getDeclContext(), SourceLocation(),
+ DeclarationName(Builder.Record->getIdentifier()), ParamList,
+ Builder.Record);
+
+ Builder.Record->setDescribedClassTemplate(Builder.Template);
+ Builder.Template->setImplicit(true);
+ Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext());
+
+ // NOTE: setPreviousDecl before addDecl so new decl replace old decl when
+ // make visible.
+ Builder.Template->setPreviousDecl(Builder.PrevTemplate);
+ Builder.Record->getDeclContext()->addDecl(Builder.Template);
+ Params.clear();
+
+ QualType T = Builder.Template->getInjectedClassNameSpecialization();
+ T = AST.getInjectedClassNameType(Builder.Record, T);
+
+ return Builder;
+}
+
+Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) {
+ if (PH == PlaceHolder::Handle)
+ return getResourceHandleExpr();
+
+ if (PH == PlaceHolder::LastStmt) {
+ assert(!StmtsList.empty() && "no statements in the list");
+ Stmt *LastStmt = StmtsList.pop_back_val();
+ assert(isa<ValueStmt>(LastStmt) && "last statement does not have a value");
+ return cast<ValueStmt>(LastStmt)->getExprStmt();
+ }
+
+ ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+ ParmVarDecl *ParamDecl = Method->getParamDecl(static_cast<unsigned>(PH));
+ return DeclRefExpr::Create(
+ AST, NestedNameSpecifierLoc(), SourceLocation(), ParamDecl, false,
+ DeclarationNameInfo(ParamDecl->getDeclName(), SourceLocation()),
+ ParamDecl->getType(), VK_PRValue);
+}
+
+BuiltinTypeMethodBuilder::BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB,
+ StringRef Name,
+ QualType ReturnTy,
+ bool IsConst)
+ : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {
+ const IdentifierInfo &II =
+ DB.SemaRef.getASTContext().Idents.get(Name, tok::TokenKind::identifier);
+ NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation());
+}
+
+BuiltinTypeMethodBuilder &
+BuiltinTypeMethodBuilder::addParam(StringRef Name, QualType Ty,
+ HLSLParamModifierAttr::Spelling Modifier) {
+ assert(Method == nullptr && "Cannot add param, method already created");
+ const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get(
+ Name, tok::TokenKind::identifier);
+ Params.emplace_back(II, Ty, Modifier);
+ return *this;
+}
+
+void BuiltinTypeMethodBuilder::createMethodDecl() {
+ assert(Method == nullptr && "Method already created");
+
+ // create method type
+ ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+ SmallVector<QualType> ParamTypes;
+ for (MethodParam &MP : Params)
+ ParamTypes.emplace_back(MP.Ty);
+
+ FunctionProtoType::ExtProtoInfo ExtInfo;
+ if (IsConst)
+ ExtInfo.TypeQuals.addConst();
+
+ QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo);
+
+ // create method decl
+ auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation());
+ Method = CXXMethodDecl::Create(
+ AST, DeclBuilder.Record, SourceLocation(), NameInfo, MethodTy, TSInfo,
+ SC_None, false, false, ConstexprSpecKind::Unspecified, SourceLocation());
+
+ // create params & set them to the function prototype
+ SmallVector<ParmVarDecl *> ParmDecls;
+ auto FnProtoLoc =
+ Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
+ for (int I = 0, E = Params.size(); I != E; I++) {
+ MethodParam &MP = Params[I];
+ ParmVarDecl *Parm = ParmVarDecl::Create(
+ AST, Method->getDeclContext(), SourceLocation(), SourceLocation(),
+ &MP.NameII, MP.Ty,
+ AST.getTrivialTypeSourceInfo(MP.Ty, SourceLocation()), SC_None,
+ nullptr);
+ if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) {
+ auto *Mod =
+ HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier);
+ Parm->addAttr(Mod);
+ }
+ ParmDecls.push_back(Parm);
+ FnProtoLoc.setParam(I, Parm);
+ }
+ Method->setParams({ParmDecls});
+}
+
+Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() {
+ // The first statement added to a method or access to 'this' creates the
+ // declaration.
+ if (!Method)
+ createMethodDecl();
+
+ ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+ CXXThisExpr *This = CXXThisExpr::Create(
+ AST, SourceLocation(), Method->getFunctionObjectParameterType(), true);
+ FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
+ return MemberExpr::CreateImplicit(AST, This, false, HandleField,
+ HandleField->getType(), VK_LValue,
+ OK_Ordinary);
+}
+
+template <typename... Ts>
+BuiltinTypeMethodBuilder &
+BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName,
+ QualType ReturnType, Ts... ArgSpecs) {
+ std::array<Expr *, sizeof...(ArgSpecs)> Args{
+ convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
+
+ // The first statement added to a method or access to 'this` creates the
+ // declaration.
+ if (!Method)
+ createMethodDecl();
+
+ ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+ FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName);
+ DeclRefExpr *DRE = DeclRefExpr::Create(
+ AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false,
+ FD->getNameInfo(), AST.BuiltinFnTy, VK_PRValue);
+
+ if (ReturnType.isNull())
+ ReturnType = FD->getReturnType();
+
+ Expr *Call = CallExpr::Create(AST, DRE, Args, ReturnType, VK_PRValue,
+ SourceLocation(), FPOptionsOverride());
+ StmtsList.push_back(Call);
+ return *this;
+}
+
+template <typename TLHS, typename TRHS>
+BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::assign(TLHS LHS, TRHS RHS) {
+ Expr *LHSExpr = convertPlaceholder(LHS);
+ Expr *RHSExpr = convertPlaceholder(RHS);
+ Stmt *AssignStmt = BinaryOperator::Create(
+ DeclBuilder.SemaRef.getASTContext(), LHSExpr, RHSExpr, BO_Assign,
+ LHSExpr->getType(), ExprValueKind::VK_PRValue,
+ ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride());
+ StmtsList.push_back(AssignStmt);
+ return *this;
+}
+
+template <typename T>
+BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) {
+ Expr *PtrExpr = convertPlaceholder(Ptr);
+ Expr *Deref =
+ UnaryOperator::Create(DeclBuilder.SemaRef.getASTContext(), PtrExpr,
+ UO_Deref, PtrExpr->getType()->getPointeeType(),
+ VK_PRValue, OK_Ordinary, SourceLocation(),
+ /*CanOverflow=*/false, FPOptionsOverride());
+ StmtsList.push_back(Deref);
+ return *this;
+}
+
+BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalizeMethod() {
+ assert(!DeclBuilder.Record->isCompleteDefinition() &&
+ "record is already complete");
+ assert(Method != nullptr &&
+ "method decl not created; are you missing a call to build the body?");
+
+ if (!Method->hasBody()) {
+ ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+ assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) &&
+ "nothing to return from non-void method");
+ if (ReturnTy != AST.VoidTy) {
+ if (Expr *LastExpr = dyn_cast<Expr>(StmtsList.back())) {
+ assert(AST.hasSameUnqualifiedType(LastExpr->getType(),
+ ReturnTy.getNonReferenceType()) &&
+ "Return type of the last statement must match the return type "
+ "of the method");
+ if (!isa<ReturnStmt>(LastExpr)) {
+ StmtsList.pop_back();
+ StmtsList.push_back(
+ ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr));
+ }
+ }
+ }
+
+ Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(),
+ SourceLocation(), SourceLocation()));
+ Method->setLexicalDeclContext(DeclBuilder.Record);
+ Method->setAccess(AccessSpecifier::AS_public);
+ Method->addAttr(AlwaysInlineAttr::CreateImplicit(
+ AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline));
+ DeclBuilder.Record->addDecl(Method);
+ }
+ return DeclBuilder;
+}
+
+BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R)
+ : SemaRef(SemaRef), Record(R) {
+ Record->startDefinition();
+ Template = Record->getDescribedClassTemplate();
+}
+
+BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef,
+ NamespaceDecl *Namespace,
+ StringRef Name)
+ : SemaRef(SemaRef), HLSLNamespace(Namespace) {
+ ASTContext &AST = SemaRef.getASTContext();
+ IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
+
+ LookupResult Result(SemaRef, &II, SourceLocation(), Sema::LookupTagName);
+ CXXRecordDecl *PrevDecl = nullptr;
+ if (SemaRef.LookupQualifiedName(Result, HLSLNamespace)) {
+ // Declaration already exists (from precompiled headers)
+ NamedDecl *Found = Result.getFoundDecl();
+ if (auto *TD = dyn_cast<ClassTemplateDecl>(Found)) {
+ PrevDecl = TD->getTemplatedDecl();
+ PrevTemplate = TD;
+ } else
+ PrevDecl = dyn_cast<CXXRecordDecl>(Found);
+ assert(PrevDecl && "Unexpected lookup result type.");
+ }
+
+ if (PrevDecl && PrevDecl->isCompleteDefinition()) {
+ Record = PrevDecl;
+ Template = PrevTemplate;
+ return;
+ }
+
+ Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace,
+ SourceLocation(), SourceLocation(), &II,
+ PrevDecl, true);
+ Record->setImplicit(true);
+ Record->setLexicalDeclContext(HLSLNamespace);
+ Record->setHasExternalLexicalStorage();
+
+ // Don't let anyone derive from built-in types.
+ Record->addAttr(
+ FinalAttr::CreateImplicit(AST, SourceRange(), FinalAttr::Keyword_final));
+}
+
+BuiltinTypeDeclBuilder::~BuiltinTypeDeclBuilder() {
+ if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)
+ HLSLNamespace->addDecl(Record);
+}
+
+CXXRecordDecl *BuiltinTypeDeclBuilder::finalizeForwardDeclaration() {
+ // Force the QualType to be generated for the record declaration. In most
+ // cases this will happen naturally when something uses the type the
+ // QualType gets lazily created. Unfortunately, with our injected types if a
+ // type isn't used in a translation unit the QualType may not get
+ // automatically generated before a PCH is generated. To resolve this we
+ // just force that the QualType is generated after we create a forward
+ // declaration.
+ (void)Record->getASTContext().getRecordType(Record);
+ return Record;
+}
+
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addMemberVariable(StringRef Name, QualType Type,
+ llvm::ArrayRef<Attr *> Attrs,
+ AccessSpecifier Access) {
+ assert(!Record->isCompleteDefinition() && "record is already complete");
+ assert(Record->isBeingDefined() &&
+ "Definition must be started before adding members!");
+ ASTContext &AST = Record->getASTContext();
+
+ IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
+ TypeSourceInfo *MemTySource =
+ AST.getTrivialTypeSourceInfo(Type, SourceLocation());
+ auto *Field = FieldDecl::Create(
+ AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource,
+ nullptr, false, InClassInitStyle::ICIS_NoInit);
+ Field->setAccess(Access);
+ Field->setImplicit(true);
+ for (Attr *A : Attrs) {
+ if (A)
+ Field->addAttr(A);
+ }
+
+ Record->addDecl(Field);
+ Fields[Name] = Field;
+ return *this;
+}
+
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addHandleMember(ResourceClass RC, ResourceKind RK,
+ bool IsROV, bool RawBuffer,
+ AccessSpecifier Access) {
+ assert(!Record->isCompleteDefinition() && "record is already complete");
+
+ ASTContext &Ctx = SemaRef.getASTContext();
+ TypeSourceInfo *ElementTypeInfo =
+ Ctx.getTrivialTypeSourceInfo(getHandleElementType(), SourceLocation());
+
+ // add handle member with resource type attributes
+ QualType AttributedResTy = QualType();
+ SmallVector<const Attr *> Attrs = {
+ HLSLResourceClassAttr::CreateImplicit(Ctx, RC),
+ IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr,
+ RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr,
+ ElementTypeInfo
+ ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo)
+ : nullptr};
+ Attr *ResourceAttr = HLSLResourceAttr::CreateImplicit(Ctx, RK);
+ if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs,
+ AttributedResTy))
+ addMemberVariable("__handle", AttributedResTy, {ResourceAttr}, Access);
+ return *this;
+}
+
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
+ if (Record->isCompleteDefinition())
+ return *this;
+ ASTContext &AST = Record->getASTContext();
+
+ QualType ConstructorType =
+ AST.getFunctionType(AST.VoidTy, {}, FunctionProtoType::ExtProtoInfo());
+
+ CanQualType CanTy = Record->getTypeForDecl()->getCanonicalTypeUnqualified();
+ DeclarationName Name = AST.DeclarationNames.getCXXConstructorName(CanTy);
+ CXXConstructorDecl *Constructor = CXXConstructorDecl::Create(
+ AST, Record, SourceLocation(),
+ DeclarationNameInfo(Name, SourceLocation()), ConstructorType,
+ AST.getTrivialTypeSourceInfo(ConstructorType, SourceLocation()),
+ ExplicitSpecifier(), false, true, false, ConstexprSpecKind::Unspecified);
+
+ Constructor->setBody(CompoundStmt::Create(
+ AST, {}, FPOptionsOverride(), SourceLocation(), SourceLocation()));
+ Constructor->setAccess(AccessSpecifier::AS_public);
+ Record->addDecl(Constructor);
+ return *this;
+}
+
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {
+ ASTContext &AST = Record->getASTContext();
+ DeclarationName Subscript =
+ AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
+
+ addHandleAccessFunction(Subscript, /*IsConst=*/true, /*IsRef=*/true);
+ addHandleAccessFunction(Subscript, /*IsConst=*/false, /*IsRef=*/true);
+ return *this;
+}
+
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() {
+ if (Record->isCompleteDefinition())
+ return *this;
+
+ ASTContext &AST = Record->getASTContext();
+ IdentifierInfo &II = AST.Idents.get("Load", tok::TokenKind::identifier);
+ DeclarationName Load(&II);
+ // TODO: We also need versions with status for CheckAccessFullyMapped.
+ addHandleAccessFunction(Load, /*IsConst=*/false, /*IsRef=*/false);
+
+ return *this;
+}
+
+FieldDecl *BuiltinTypeDeclBuilder::getResourceHandleField() {
+ auto I = Fields.find("__handle");
+ assert(I != Fields.end() &&
+ I->second->getType()->isHLSLAttributedResourceType() &&
+ "record does not have resource handle field");
+ return I->second;
+}
+
+QualType BuiltinTypeDeclBuilder::getFirstTemplateTypeParam() {
+ assert(Template && "record it not a template");
+ if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
+ Template->getTemplateParameters()->getParam(0))) {
+ return QualType(TTD->getTypeForDecl(), 0);
+ }
+ return QualType();
+}
+
+QualType BuiltinTypeDeclBuilder::getHandleElementType() {
+ if (Template)
+ return getFirstTemplateTypeParam();
+ // TODO: Should we default to VoidTy? Using `i8` is arguably ambiguous.
+ return SemaRef.getASTContext().Char8Ty;
+}
+
+// BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::startDefinition() {
+// assert(!Record->isCompleteDefinition() && "record is already complete");
+// Record->startDefinition();
+// return *this;
+// }
+
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::completeDefinition() {
+ assert(!Record->isCompleteDefinition() && "record is already complete");
+ assert(Record->isBeingDefined() &&
+ "Definition must be started before completing it.");
+
+ Record->completeDefinition();
+ return *this;
+}
+
+Expr *BuiltinTypeDeclBuilder::getConstantIntExpr(int value) {
+ ASTContext &AST = SemaRef.getASTContext();
+ return IntegerLiteral::Create(
+ AST, llvm::APInt(AST.getTypeSize(AST.IntTy), value, true), AST.IntTy,
+ SourceLocation());
+}
+
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
+ ConceptDecl *CD = nullptr) {
+ if (Record->isCompleteDefinition()) {
+ assert(Template && "existing record it not a template");
+ assert(Template->getTemplateParameters()->size() == Names.size() &&
+ "template param count mismatch");
+ return *this;
+ }
+
+ TemplateParameterListBuilder Builder = TemplateParameterListBuilder(*this);
+ for (StringRef Name : Names)
+ Builder.addTypeParameter(Name);
+ return Builder.finalizeTemplateArgs(CD);
+}
+
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
+ using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+ return BuiltinTypeMethodBuilder(*this, "IncrementCounter",
+ SemaRef.getASTContext().UnsignedIntTy)
+ .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
+ PH::Handle, getConstantIntExpr(1))
+ .finalizeMethod();
+}
+
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
+ using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+ return BuiltinTypeMethodBuilder(*this, "DecrementCounter",
+ SemaRef.getASTContext().UnsignedIntTy)
+ .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
+ PH::Handle, getConstantIntExpr(-1))
+ .finalizeMethod();
+}
+
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name,
+ bool IsConst, bool IsRef) {
+ assert(!Record->isCompleteDefinition() && "record is already complete");
+ ASTContext &AST = SemaRef.getASTContext();
+ using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+ QualType ElemTy = getHandleElementType();
+ // TODO: Map to an hlsl_device address space.
+ QualType ElemPtrTy = AST.getPointerType(ElemTy);
+ QualType ReturnTy = ElemTy;
+ if (IsConst)
+ ReturnTy.addConst();
+ if (IsRef)
+ ReturnTy = AST.getLValueReferenceType(ReturnTy);
+
+ return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst)
+ .addParam("Index", AST.UnsignedIntTy)
+ .callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle,
+ PH::_0)
+ .dereference(PH::LastStmt)
+ .finalizeMethod();
+}
+
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
+ using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+ ASTContext &AST = SemaRef.getASTContext();
+ QualType ElemTy = getHandleElementType();
+ return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy)
+ .addParam("value", ElemTy)
+ .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
+ PH::Handle, getConstantIntExpr(1))
+ .callBuiltin("__builtin_hlsl_resource_getpointer",
+ AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
+ .dereference(PH::LastStmt)
+ .assign(PH::LastStmt, PH::_0)
+ .finalizeMethod();
+}
+
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
+ using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+ ASTContext &AST = SemaRef.getASTContext();
+ QualType ElemTy = getHandleElementType();
+ return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy)
+ .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
+ PH::Handle, getConstantIntExpr(-1))
+ .callBuiltin("__builtin_hlsl_resource_getpointer",
+ AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
+ .dereference(PH::LastStmt)
+ .finalizeMethod();
+}
+
+} // namespace hlsl
+} // namespace clang
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
new file mode 100644
index 0000000000000..78357a43397fc
--- /dev/null
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -0,0 +1,97 @@
+//===--- HLSLBuiltinTypeDeclBuilder.h - HLSL Builtin Type Decl Builder ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Type.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/ADT/StringMap.h"
+
+using llvm::hlsl::ResourceClass;
+using llvm::hlsl::ResourceKind;
+
+namespace clang {
+
+class ClassTemplateDecl;
+class NamespaceDecl;
+class CXXRecordDecl;
+class FieldDecl;
+
+namespace hlsl {
+
+// Builder for builtin HLSL class types such as HLSL resource classes.
+// Allows creating declaration of builtin types using the builder pattern
+// like this:
+//
+// Decl = BuiltinTypeDeclBuilder(Sema, Namespace, "BuiltinClassName")
+// .addSimpleTemplateParams({"T"}, Concept)
+// .finalizeForwardDeclaration();
+//
+// And then completing the type like this:
+//
+// BuiltinTypeDeclBuilder(Sema, Decl)
+// .addDefaultHandleConstructor();
+// .addLoadMethods()
+// .completeDefinition();
+//
+class BuiltinTypeDeclBuilder {
+private:
+ Sema &SemaRef;
+ CXXRecordDecl *Record = nullptr;
+ ClassTemplateDecl *Template = nullptr;
+ ClassTemplateDecl *PrevTemplate = nullptr;
+ NamespaceDecl *HLSLNamespace = nullptr;
+ llvm::StringMap<FieldDecl *> Fields;
+
+public:
+ friend struct TemplateParameterListBuilder;
+ friend struct BuiltinTypeMethodBuilder;
+
+ BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R);
+ BuiltinTypeDeclBuilder(Sema &SemaRef, NamespaceDecl *Namespace,
+ StringRef Name);
+ ~BuiltinTypeDeclBuilder();
+
+ BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef<StringRef> Names,
+ ConceptDecl *CD);
+ CXXRecordDecl *finalizeForwardDeclaration();
+ //BuiltinTypeDeclBuilder &startDefinition();
+ BuiltinTypeDeclBuilder &completeDefinition();
+
+ BuiltinTypeDeclBuilder &
+ addMemberVariable(StringRef Name, QualType Type, llvm::ArrayRef<Attr *> Attrs,
+ AccessSpecifier Access = AccessSpecifier::AS_private);
+
+ BuiltinTypeDeclBuilder &
+ addHandleMember(ResourceClass RC, ResourceKind RK, bool IsROV, bool RawBuffer,
+ AccessSpecifier Access = AccessSpecifier::AS_private);
+ BuiltinTypeDeclBuilder &addArraySubscriptOperators();
+
+ // Builtin types methods
+ BuiltinTypeDeclBuilder &addDefaultHandleConstructor();
+
+ // Builtin types methods
+ BuiltinTypeDeclBuilder &addLoadMethods();
+ BuiltinTypeDeclBuilder &addIncrementCounterMethod();
+ BuiltinTypeDeclBuilder &addDecrementCounterMethod();
+ BuiltinTypeDeclBuilder &addHandleAccessFunction(DeclarationName &Name,
+ bool IsConst, bool IsRef);
+ BuiltinTypeDeclBuilder &addAppendMethod();
+ BuiltinTypeDeclBuilder &addConsumeMethod();
+
+private:
+ FieldDecl *getResourceHandleField();
+ QualType getFirstTemplateTypeParam();
+ QualType getHandleElementType();
+ Expr *getConstantIntExpr(int value);
+};
+
+} // namespace hlsl
+
+} // namespace clang
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index d34d3ef2996ac..9224f12f2d025 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -10,6 +10,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Sema/HLSLExternalSemaSource.h"
+#include "HLSLBuiltinTypeDeclBuilder.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
@@ -21,742 +22,11 @@
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaHLSL.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/Frontend/HLSL/HLSLResource.h"
-#include "llvm/Support/ErrorHandling.h"
-
-#include <functional>
using namespace clang;
using namespace llvm::hlsl;
-static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name);
-
-namespace {
-
-struct TemplateParameterListBuilder;
-
-class BuiltinTypeDeclBuilder {
- ClassTemplateDecl *Template = nullptr;
- ClassTemplateDecl *PrevTemplate = nullptr;
- NamespaceDecl *HLSLNamespace = nullptr;
- llvm::StringMap<FieldDecl *> Fields;
-
-public:
- Sema &SemaRef;
- CXXRecordDecl *Record = nullptr;
- friend struct TemplateParameterListBuilder;
-
- BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R)
- : SemaRef(SemaRef), Record(R) {
- Record->startDefinition();
- Template = Record->getDescribedClassTemplate();
- }
-
- BuiltinTypeDeclBuilder(Sema &SemaRef, NamespaceDecl *Namespace,
- StringRef Name)
- : HLSLNamespace(Namespace), SemaRef(SemaRef) {
- ASTContext &AST = SemaRef.getASTContext();
- IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
-
- LookupResult Result(SemaRef, &II, SourceLocation(), Sema::LookupTagName);
- CXXRecordDecl *PrevDecl = nullptr;
- if (SemaRef.LookupQualifiedName(Result, HLSLNamespace)) {
- // Declaration already exists (from precompiled headers)
- NamedDecl *Found = Result.getFoundDecl();
- if (auto *TD = dyn_cast<ClassTemplateDecl>(Found)) {
- PrevDecl = TD->getTemplatedDecl();
- PrevTemplate = TD;
- } else
- PrevDecl = dyn_cast<CXXRecordDecl>(Found);
- assert(PrevDecl && "Unexpected lookup result type.");
- }
-
- if (PrevDecl && PrevDecl->isCompleteDefinition()) {
- Record = PrevDecl;
- Template = PrevTemplate;
- return;
- }
-
- Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace,
- SourceLocation(), SourceLocation(), &II,
- PrevDecl, true);
- Record->setImplicit(true);
- Record->setLexicalDeclContext(HLSLNamespace);
- Record->setHasExternalLexicalStorage();
-
- // Don't let anyone derive from built-in types.
- Record->addAttr(FinalAttr::CreateImplicit(AST, SourceRange(),
- FinalAttr::Keyword_final));
- }
-
- ~BuiltinTypeDeclBuilder() {
- if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)
- HLSLNamespace->addDecl(Record);
- }
-
- CXXRecordDecl *finalizeForwardDeclaration() {
- // Force the QualType to be generated for the record declaration. In most
- // cases this will happen naturally when something uses the type the
- // QualType gets lazily created. Unfortunately, with our injected types if a
- // type isn't used in a translation unit the QualType may not get
- // automatically generated before a PCH is generated. To resolve this we
- // just force that the QualType is generated after we create a forward
- // declaration.
- (void)Record->getASTContext().getRecordType(Record);
- return Record;
- }
-
- BuiltinTypeDeclBuilder &
- addMemberVariable(StringRef Name, QualType Type, llvm::ArrayRef<Attr *> Attrs,
- AccessSpecifier Access = AccessSpecifier::AS_private) {
- assert(!Record->isCompleteDefinition() && "record is already complete");
- assert(Record->isBeingDefined() &&
- "Definition must be started before adding members!");
- ASTContext &AST = Record->getASTContext();
-
- IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
- TypeSourceInfo *MemTySource =
- AST.getTrivialTypeSourceInfo(Type, SourceLocation());
- auto *Field = FieldDecl::Create(
- AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource,
- nullptr, false, InClassInitStyle::ICIS_NoInit);
- Field->setAccess(Access);
- Field->setImplicit(true);
- for (Attr *A : Attrs) {
- if (A)
- Field->addAttr(A);
- }
-
- Record->addDecl(Field);
- Fields[Name] = Field;
- return *this;
- }
-
- BuiltinTypeDeclBuilder &
- addHandleMember(ResourceClass RC, ResourceKind RK, bool IsROV, bool RawBuffer,
- AccessSpecifier Access = AccessSpecifier::AS_private) {
- assert(!Record->isCompleteDefinition() && "record is already complete");
-
- ASTContext &Ctx = SemaRef.getASTContext();
- TypeSourceInfo *ElementTypeInfo =
- Ctx.getTrivialTypeSourceInfo(getHandleElementType(), SourceLocation());
-
- // add handle member with resource type attributes
- QualType AttributedResTy = QualType();
- SmallVector<const Attr *> Attrs = {
- HLSLResourceClassAttr::CreateImplicit(Ctx, RC),
- IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr,
- RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr,
- ElementTypeInfo
- ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo)
- : nullptr};
- Attr *ResourceAttr = HLSLResourceAttr::CreateImplicit(Ctx, RK);
- if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs,
- AttributedResTy))
- addMemberVariable("__handle", AttributedResTy, {ResourceAttr}, Access);
- return *this;
- }
-
- BuiltinTypeDeclBuilder &addDefaultHandleConstructor() {
- if (Record->isCompleteDefinition())
- return *this;
- ASTContext &AST = Record->getASTContext();
-
- QualType ConstructorType =
- AST.getFunctionType(AST.VoidTy, {}, FunctionProtoType::ExtProtoInfo());
-
- CanQualType CanTy = Record->getTypeForDecl()->getCanonicalTypeUnqualified();
- DeclarationName Name = AST.DeclarationNames.getCXXConstructorName(CanTy);
- CXXConstructorDecl *Constructor = CXXConstructorDecl::Create(
- AST, Record, SourceLocation(),
- DeclarationNameInfo(Name, SourceLocation()), ConstructorType,
- AST.getTrivialTypeSourceInfo(ConstructorType, SourceLocation()),
- ExplicitSpecifier(), false, true, false,
- ConstexprSpecKind::Unspecified);
-
- Constructor->setBody(CompoundStmt::Create(
- AST, {}, FPOptionsOverride(), SourceLocation(), SourceLocation()));
- Constructor->setAccess(AccessSpecifier::AS_public);
- Record->addDecl(Constructor);
- return *this;
- }
-
- BuiltinTypeDeclBuilder &addArraySubscriptOperators() {
- ASTContext &AST = Record->getASTContext();
- DeclarationName Subscript =
- AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
-
- addHandleAccessFunction(Subscript, /*IsConst=*/true, /*IsRef=*/true);
- addHandleAccessFunction(Subscript, /*IsConst=*/false, /*IsRef=*/true);
- return *this;
- }
-
- BuiltinTypeDeclBuilder &addLoadMethods() {
- if (Record->isCompleteDefinition())
- return *this;
-
- ASTContext &AST = Record->getASTContext();
- IdentifierInfo &II = AST.Idents.get("Load", tok::TokenKind::identifier);
- DeclarationName Load(&II);
- // TODO: We also need versions with status for CheckAccessFullyMapped.
- addHandleAccessFunction(Load, /*IsConst=*/false, /*IsRef=*/false);
-
- return *this;
- }
-
- FieldDecl *getResourceHandleField() {
- auto I = Fields.find("__handle");
- assert(I != Fields.end() &&
- I->second->getType()->isHLSLAttributedResourceType() &&
- "record does not have resource handle field");
- return I->second;
- }
-
- QualType getFirstTemplateTypeParam() {
- assert(Template && "record it not a template");
- if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
- Template->getTemplateParameters()->getParam(0))) {
- return QualType(TTD->getTypeForDecl(), 0);
- }
- return QualType();
- }
-
- QualType getHandleElementType() {
- if (Template)
- return getFirstTemplateTypeParam();
- // TODO: Should we default to VoidTy? Using `i8` is arguably ambiguous.
- return SemaRef.getASTContext().Char8Ty;
- }
-
- BuiltinTypeDeclBuilder &startDefinition() {
- assert(!Record->isCompleteDefinition() && "record is already complete");
- Record->startDefinition();
- return *this;
- }
-
- BuiltinTypeDeclBuilder &completeDefinition() {
- assert(!Record->isCompleteDefinition() && "record is already complete");
- assert(Record->isBeingDefined() &&
- "Definition must be started before completing it.");
-
- Record->completeDefinition();
- return *this;
- }
-
- Expr *getConstantIntExpr(int value) {
- ASTContext &AST = SemaRef.getASTContext();
- return IntegerLiteral::Create(
- AST, llvm::APInt(AST.getTypeSize(AST.IntTy), value, true), AST.IntTy,
- SourceLocation());
- }
-
- TemplateParameterListBuilder addTemplateArgumentList();
- BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef<StringRef> Names,
- ConceptDecl *CD);
-
- // Builtin types methods
- BuiltinTypeDeclBuilder &addIncrementCounterMethod();
- BuiltinTypeDeclBuilder &addDecrementCounterMethod();
- BuiltinTypeDeclBuilder &addHandleAccessFunction(DeclarationName &Name,
- bool IsConst, bool IsRef);
- BuiltinTypeDeclBuilder &addAppendMethod();
- BuiltinTypeDeclBuilder &addConsumeMethod();
-};
-
-struct TemplateParameterListBuilder {
- BuiltinTypeDeclBuilder &Builder;
- llvm::SmallVector<NamedDecl *> Params;
-
- TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {}
-
- ~TemplateParameterListBuilder() { finalizeTemplateArgs(); }
-
- TemplateParameterListBuilder &
- addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) {
- assert(!Builder.Record->isCompleteDefinition() &&
- "record is already complete");
- ASTContext &AST = Builder.SemaRef.getASTContext();
- unsigned Position = static_cast<unsigned>(Params.size());
- auto *Decl = TemplateTypeParmDecl::Create(
- AST, Builder.Record->getDeclContext(), SourceLocation(),
- SourceLocation(), /* TemplateDepth */ 0, Position,
- &AST.Idents.get(Name, tok::TokenKind::identifier),
- /* Typename */ true,
- /* ParameterPack */ false,
- /* HasTypeConstraint*/ false);
- if (!DefaultValue.isNull())
- Decl->setDefaultArgument(AST,
- Builder.SemaRef.getTrivialTemplateArgumentLoc(
- DefaultValue, QualType(), SourceLocation()));
-
- Params.emplace_back(Decl);
- return *this;
- }
-
- // The concept specialization expression (CSE) constructed in
- // constructConceptSpecializationExpr is constructed so that it
- // matches the CSE that is constructed when parsing the below C++ code:
- //
- // template<typename T>
- // concept is_typed_resource_element_compatible =
- // __builtin_hlsl_typed_resource_element_compatible<T>
- //
- // template<typename element_type> requires
- // is_typed_resource_element_compatible<element_type>
- // struct RWBuffer {
- // element_type Val;
- // };
- //
- // int fn() {
- // RWBuffer<int> Buf;
- // }
- //
- // When dumping the AST and filtering for "RWBuffer", the resulting AST
- // structure is what we're trying to construct below, specifically the
- // CSE portion.
- ConceptSpecializationExpr *
- constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD) {
- ASTContext &Context = S.getASTContext();
- SourceLocation Loc = Builder.Record->getBeginLoc();
- DeclarationNameInfo DNI(CD->getDeclName(), Loc);
- NestedNameSpecifierLoc NNSLoc;
- DeclContext *DC = Builder.Record->getDeclContext();
- TemplateArgumentListInfo TALI(Loc, Loc);
-
- // Assume that the concept decl has just one template parameter
- // This parameter should have been added when CD was constructed
- // in getTypedBufferConceptDecl
- assert(CD->getTemplateParameters()->size() == 1 &&
- "unexpected concept decl parameter count");
- TemplateTypeParmDecl *ConceptTTPD = dyn_cast<TemplateTypeParmDecl>(
- CD->getTemplateParameters()->getParam(0));
-
- // this TemplateTypeParmDecl is the template for the resource, and is
- // used to construct a template argumentthat will be used
- // to construct the ImplicitConceptSpecializationDecl
- TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
- Context, // AST context
- Builder.Record->getDeclContext(), // DeclContext
- SourceLocation(), SourceLocation(),
- /*D=*/0, // Depth in the template parameter list
- /*P=*/0, // Position in the template parameter list
- /*Id=*/nullptr, // Identifier for 'T'
- /*Typename=*/true, // Indicates this is a 'typename' or 'class'
- /*ParameterPack=*/false, // Not a parameter pack
- /*HasTypeConstraint=*/false // Has no type constraint
- );
-
- T->setDeclContext(DC);
-
- QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD);
-
- // this is the 2nd template argument node, on which
- // the concept constraint is actually being applied: 'element_type'
- TemplateArgument ConceptTA = TemplateArgument(ConceptTType);
-
- QualType CSETType = Context.getTypeDeclType(T);
-
- // this is the 1st template argument node, which represents
- // the abstract type that a concept would refer to: 'T'
- TemplateArgument CSETA = TemplateArgument(CSETType);
-
- ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
- ImplicitConceptSpecializationDecl::Create(
- Context, Builder.Record->getDeclContext(), Loc, {CSETA});
-
- // Constraint satisfaction is used to construct the
- // ConceptSpecailizationExpr, and represents the 2nd Template Argument,
- // located at the bottom of the sample AST above.
- const ConstraintSatisfaction CS(CD, {ConceptTA});
- TemplateArgumentLoc TAL = S.getTrivialTemplateArgumentLoc(
- ConceptTA, QualType(), SourceLocation());
-
- TALI.addArgument(TAL);
- const ASTTemplateArgumentListInfo *ATALI =
- ASTTemplateArgumentListInfo::Create(Context, TALI);
-
- // In the concept reference, ATALI is what adds the extra
- // TemplateArgument node underneath CSE
- ConceptReference *CR =
- ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI);
-
- ConceptSpecializationExpr *CSE =
- ConceptSpecializationExpr::Create(Context, CR, ImplicitCSEDecl, &CS);
-
- return CSE;
- }
-
- BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr) {
- if (Params.empty())
- return Builder;
-
- ASTContext &AST = Builder.SemaRef.Context;
- ConceptSpecializationExpr *CSE =
- CD ? constructConceptSpecializationExpr(Builder.SemaRef, CD) : nullptr;
- auto *ParamList = TemplateParameterList::Create(
- AST, SourceLocation(), SourceLocation(), Params, SourceLocation(), CSE);
- Builder.Template = ClassTemplateDecl::Create(
- AST, Builder.Record->getDeclContext(), SourceLocation(),
- DeclarationName(Builder.Record->getIdentifier()), ParamList,
- Builder.Record);
-
- Builder.Record->setDescribedClassTemplate(Builder.Template);
- Builder.Template->setImplicit(true);
- Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext());
-
- // NOTE: setPreviousDecl before addDecl so new decl replace old decl when
- // make visible.
- Builder.Template->setPreviousDecl(Builder.PrevTemplate);
- Builder.Record->getDeclContext()->addDecl(Builder.Template);
- Params.clear();
-
- QualType T = Builder.Template->getInjectedClassNameSpecialization();
- T = AST.getInjectedClassNameType(Builder.Record, T);
-
- return Builder;
- }
-};
-
-// Builder for methods of builtin types. Allows adding methods to builtin types
-// using the builder pattern like this:
-//
-// BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType)
-// .addParam("param_name", Type, InOutModifier)
-// .callBuiltin("builtin_name", BuiltinParams...)
-// .finalizeMethod();
-//
-// The builder needs to have all of the method parameters before it can create
-// a CXXMethodDecl. It collects them in addParam calls and when a first
-// method that builds the body is called or when access to 'this` is needed it
-// creates the CXXMethodDecl and ParmVarDecls instances. These can then be
-// referenced from the body building methods. Destructor or an explicit call to
-// finalizeMethod() will complete the method definition.
-//
-// The callBuiltin helper method accepts constants via `Expr *` or placeholder
-// value arguments to indicate which function arguments to forward to the
-// builtin.
-//
-// If the method that is being built has a non-void return type the
-// finalizeMethod will create a return statent with the value of the last
-// statement (unless the last statement is already a ReturnStmt).
-struct BuiltinTypeMethodBuilder {
- struct MethodParam {
- const IdentifierInfo &NameII;
- QualType Ty;
- HLSLParamModifierAttr::Spelling Modifier;
- MethodParam(const IdentifierInfo &NameII, QualType Ty,
- HLSLParamModifierAttr::Spelling Modifier)
- : NameII(NameII), Ty(Ty), Modifier(Modifier) {}
- };
-
- BuiltinTypeDeclBuilder &DeclBuilder;
- DeclarationNameInfo NameInfo;
- QualType ReturnTy;
- CXXMethodDecl *Method;
- bool IsConst;
- llvm::SmallVector<MethodParam> Params;
- llvm::SmallVector<Stmt *> StmtsList;
-
- // Argument placeholders, inspired by std::placeholder. These are the indices
- // of arguments to forward to `callBuiltin` and other method builder methods.
- // Additional special values are:
- // Handle - refers to the resource handle.
- // LastStmt - refers to the last statement in the method body; referencing
- // LastStmt will remove the statement from the method body since
- // it will be linked from the new expression being constructed.
- enum class PlaceHolder { _0, _1, _2, _3, Handle = 128, LastStmt };
-
- Expr *convertPlaceholder(PlaceHolder PH) {
- if (PH == PlaceHolder::Handle)
- return getResourceHandleExpr();
-
- if (PH == PlaceHolder::LastStmt) {
- assert(!StmtsList.empty() && "no statements in the list");
- Stmt *LastStmt = StmtsList.pop_back_val();
- assert(isa<ValueStmt>(LastStmt) &&
- "last statement does not have a value");
- return cast<ValueStmt>(LastStmt)->getExprStmt();
- }
-
- ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
- ParmVarDecl *ParamDecl = Method->getParamDecl(static_cast<unsigned>(PH));
- return DeclRefExpr::Create(
- AST, NestedNameSpecifierLoc(), SourceLocation(), ParamDecl, false,
- DeclarationNameInfo(ParamDecl->getDeclName(), SourceLocation()),
- ParamDecl->getType(), VK_PRValue);
- }
- Expr *convertPlaceholder(Expr *E) { return E; }
-
-public:
- BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name,
- QualType ReturnTy, bool IsConst = false)
- : DeclBuilder(DB), NameInfo(DeclarationNameInfo(Name, SourceLocation())),
- ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {}
-
- BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef Name,
- QualType ReturnTy, bool IsConst = false)
- : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {
- const IdentifierInfo &II =
- DB.SemaRef.getASTContext().Idents.get(Name, tok::TokenKind::identifier);
- NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation());
- }
-
- BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty,
- HLSLParamModifierAttr::Spelling Modifier =
- HLSLParamModifierAttr::Keyword_in) {
- assert(Method == nullptr && "Cannot add param, method already created");
- const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get(
- Name, tok::TokenKind::identifier);
- Params.emplace_back(II, Ty, Modifier);
- return *this;
- }
-
-private:
- void createMethodDecl() {
- assert(Method == nullptr && "Method already created");
-
- // create method type
- ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
- SmallVector<QualType> ParamTypes;
- for (MethodParam &MP : Params)
- ParamTypes.emplace_back(MP.Ty);
-
- FunctionProtoType::ExtProtoInfo ExtInfo;
- if (IsConst)
- ExtInfo.TypeQuals.addConst();
-
- QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo);
-
- // create method decl
- auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation());
- Method =
- CXXMethodDecl::Create(AST, DeclBuilder.Record, SourceLocation(),
- NameInfo, MethodTy, TSInfo, SC_None, false, false,
- ConstexprSpecKind::Unspecified, SourceLocation());
-
- // create params & set them to the function prototype
- SmallVector<ParmVarDecl *> ParmDecls;
- auto FnProtoLoc =
- Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
- for (int I = 0, E = Params.size(); I != E; I++) {
- MethodParam &MP = Params[I];
- ParmVarDecl *Parm = ParmVarDecl::Create(
- AST, Method->getDeclContext(), SourceLocation(), SourceLocation(),
- &MP.NameII, MP.Ty,
- AST.getTrivialTypeSourceInfo(MP.Ty, SourceLocation()), SC_None,
- nullptr);
- if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) {
- auto *Mod =
- HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier);
- Parm->addAttr(Mod);
- }
- ParmDecls.push_back(Parm);
- FnProtoLoc.setParam(I, Parm);
- }
- Method->setParams({ParmDecls});
- }
-
-public:
- ~BuiltinTypeMethodBuilder() { finalizeMethod(); }
-
- BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;
- BuiltinTypeMethodBuilder &
- operator=(const BuiltinTypeMethodBuilder &Other) = delete;
-
- Expr *getResourceHandleExpr() {
- // The first statement added to a method or access to 'this' creates the
- // declaration.
- if (!Method)
- createMethodDecl();
-
- ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
- CXXThisExpr *This = CXXThisExpr::Create(
- AST, SourceLocation(), Method->getFunctionObjectParameterType(), true);
- FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
- return MemberExpr::CreateImplicit(AST, This, false, HandleField,
- HandleField->getType(), VK_LValue,
- OK_Ordinary);
- }
-
- template <typename... Ts>
- BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName,
- QualType ReturnType, Ts... ArgSpecs) {
- std::array<Expr *, sizeof...(ArgSpecs)> Args{
- convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
-
- // The first statement added to a method or access to 'this` creates the
- // declaration.
- if (!Method)
- createMethodDecl();
-
- ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
- FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName);
- DeclRefExpr *DRE = DeclRefExpr::Create(
- AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false,
- FD->getNameInfo(), AST.BuiltinFnTy, VK_PRValue);
-
- if (ReturnType.isNull())
- ReturnType = FD->getReturnType();
-
- Expr *Call = CallExpr::Create(AST, DRE, Args, ReturnType, VK_PRValue,
- SourceLocation(), FPOptionsOverride());
- StmtsList.push_back(Call);
- return *this;
- }
-
- template <typename TLHS, typename TRHS>
- BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS) {
- Expr *LHSExpr = convertPlaceholder(LHS);
- Expr *RHSExpr = convertPlaceholder(RHS);
- Stmt *AssignStmt = BinaryOperator::Create(
- DeclBuilder.SemaRef.getASTContext(), LHSExpr, RHSExpr, BO_Assign,
- LHSExpr->getType(), ExprValueKind::VK_PRValue,
- ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride());
- StmtsList.push_back(AssignStmt);
- return *this;
- }
-
- template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr) {
- Expr *PtrExpr = convertPlaceholder(Ptr);
- Expr *Deref =
- UnaryOperator::Create(DeclBuilder.SemaRef.getASTContext(), PtrExpr,
- UO_Deref, PtrExpr->getType()->getPointeeType(),
- VK_PRValue, OK_Ordinary, SourceLocation(),
- /*CanOverflow=*/false, FPOptionsOverride());
- StmtsList.push_back(Deref);
- return *this;
- }
-
- BuiltinTypeDeclBuilder &finalizeMethod() {
- assert(!DeclBuilder.Record->isCompleteDefinition() &&
- "record is already complete");
- assert(
- Method != nullptr &&
- "method decl not created; are you missing a call to build the body?");
-
- if (!Method->hasBody()) {
- ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
- assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) &&
- "nothing to return from non-void method");
- if (ReturnTy != AST.VoidTy) {
- if (Expr *LastExpr = dyn_cast<Expr>(StmtsList.back())) {
- assert(AST.hasSameUnqualifiedType(LastExpr->getType(),
- ReturnTy.getNonReferenceType()) &&
- "Return type of the last statement must match the return type "
- "of the method");
- if (!isa<ReturnStmt>(LastExpr)) {
- StmtsList.pop_back();
- StmtsList.push_back(
- ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr));
- }
- }
- }
-
- Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(),
- SourceLocation(), SourceLocation()));
- Method->setLexicalDeclContext(DeclBuilder.Record);
- Method->setAccess(AccessSpecifier::AS_public);
- Method->addAttr(AlwaysInlineAttr::CreateImplicit(
- AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline));
- DeclBuilder.Record->addDecl(Method);
- }
- return DeclBuilder;
- }
-};
-
-} // namespace
-
-TemplateParameterListBuilder BuiltinTypeDeclBuilder::addTemplateArgumentList() {
- return TemplateParameterListBuilder(*this);
-}
-
-BuiltinTypeDeclBuilder &
-BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
- ConceptDecl *CD = nullptr) {
- if (Record->isCompleteDefinition()) {
- assert(Template && "existing record it not a template");
- assert(Template->getTemplateParameters()->size() == Names.size() &&
- "template param count mismatch");
- return *this;
- }
-
- TemplateParameterListBuilder Builder = this->addTemplateArgumentList();
- for (StringRef Name : Names)
- Builder.addTypeParameter(Name);
- return Builder.finalizeTemplateArgs(CD);
-}
-
-BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
- using PH = BuiltinTypeMethodBuilder::PlaceHolder;
- return BuiltinTypeMethodBuilder(*this, "IncrementCounter",
- SemaRef.getASTContext().UnsignedIntTy)
- .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
- PH::Handle, getConstantIntExpr(1))
- .finalizeMethod();
-}
-
-BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
- using PH = BuiltinTypeMethodBuilder::PlaceHolder;
- return BuiltinTypeMethodBuilder(*this, "DecrementCounter",
- SemaRef.getASTContext().UnsignedIntTy)
- .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
- PH::Handle, getConstantIntExpr(-1))
- .finalizeMethod();
-}
-
-BuiltinTypeDeclBuilder &
-BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name,
- bool IsConst, bool IsRef) {
- assert(!Record->isCompleteDefinition() && "record is already complete");
- ASTContext &AST = SemaRef.getASTContext();
- using PH = BuiltinTypeMethodBuilder::PlaceHolder;
-
- QualType ElemTy = getHandleElementType();
- // TODO: Map to an hlsl_device address space.
- QualType ElemPtrTy = AST.getPointerType(ElemTy);
- QualType ReturnTy = ElemTy;
- if (IsConst)
- ReturnTy.addConst();
- if (IsRef)
- ReturnTy = AST.getLValueReferenceType(ReturnTy);
-
- return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst)
- .addParam("Index", AST.UnsignedIntTy)
- .callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle,
- PH::_0)
- .dereference(PH::LastStmt)
- .finalizeMethod();
-}
-
-BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
- using PH = BuiltinTypeMethodBuilder::PlaceHolder;
- ASTContext &AST = SemaRef.getASTContext();
- QualType ElemTy = getHandleElementType();
- return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy)
- .addParam("value", ElemTy)
- .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
- PH::Handle, getConstantIntExpr(1))
- .callBuiltin("__builtin_hlsl_resource_getpointer",
- AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
- .dereference(PH::LastStmt)
- .assign(PH::LastStmt, PH::_0)
- .finalizeMethod();
-}
-
-BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
- using PH = BuiltinTypeMethodBuilder::PlaceHolder;
- ASTContext &AST = SemaRef.getASTContext();
- QualType ElemTy = getHandleElementType();
- return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy)
- .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
- PH::Handle, getConstantIntExpr(-1))
- .callBuiltin("__builtin_hlsl_resource_getpointer",
- AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
- .dereference(PH::LastStmt)
- .finalizeMethod();
-}
-
-HLSLExternalSemaSource::~HLSLExternalSemaSource() {}
+using clang::hlsl::BuiltinTypeDeclBuilder;
void HLSLExternalSemaSource::InitializeSema(Sema &S) {
SemaPtr = &S;
@@ -1128,19 +398,3 @@ void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) {
return;
It->second(Record);
}
-
-static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) {
- IdentifierInfo &II =
- S.getASTContext().Idents.get(Name, tok::TokenKind::identifier);
- DeclarationNameInfo NameInfo =
- DeclarationNameInfo(DeclarationName(&II), SourceLocation());
- LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
- // AllowBuiltinCreation is false but LookupDirect will create
- // the builtin when searching the global scope anyways...
- S.LookupName(R, S.getCurScope());
- // FIXME: If the builtin function was user-declared in global scope,
- // this assert *will* fail. Should this call LookupBuiltin instead?
- assert(R.isSingleResult() &&
- "Since this is a builtin it should always resolve!");
- return cast<FunctionDecl>(R.getFoundDecl());
-}
More information about the cfe-commits
mailing list