[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