[llvm-branch-commits] [clang] [HLSL][NFC] Use method builder to create default resource constructor (PR #131384)

Helena Kotas via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Mar 14 12:59:48 PDT 2025


https://github.com/hekota created https://github.com/llvm/llvm-project/pull/131384

Updates the `BuiltinTypeMethodBuilder` to support creating constructors and use it to create the default resource constructor. This enables us to have a shared code for implementing both builtin methods and constructors and will come in handy when we add more constructors in the future.

>From 47b41c88a60a7f376070b9ff779ec955eebf523a Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 12 Mar 2025 17:20:51 -0700
Subject: [PATCH 1/2] [HLSL] Create default resource constructor with
 BuiltinTypeMethodBuilder

---
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 126 ++++++++++--------
 1 file changed, 72 insertions(+), 54 deletions(-)

diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index db0ed3434d837..a52c6a49264c8 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -89,21 +89,24 @@ struct TemplateParameterListBuilder {
 // statement (unless the last statement is already a ReturnStmt).
 struct BuiltinTypeMethodBuilder {
 private:
-  struct MethodParam {
+  struct Param {
     const IdentifierInfo &NameII;
     QualType Ty;
     HLSLParamModifierAttr::Spelling Modifier;
-    MethodParam(const IdentifierInfo &NameII, QualType Ty,
-                HLSLParamModifierAttr::Spelling Modifier)
+    Param(const IdentifierInfo &NameII, QualType Ty,
+          HLSLParamModifierAttr::Spelling Modifier)
         : NameII(NameII), Ty(Ty), Modifier(Modifier) {}
   };
 
   BuiltinTypeDeclBuilder &DeclBuilder;
-  DeclarationNameInfo NameInfo;
+  DeclarationName Name;
   QualType ReturnTy;
+  // method or constructor declaration (CXXConstructorDecl derives from
+  // CXXMethodDecl)
   CXXMethodDecl *Method;
   bool IsConst;
-  llvm::SmallVector<MethodParam> Params;
+  bool IsConstructor;
+  llvm::SmallVector<Param> Params;
   llvm::SmallVector<Stmt *> StmtsList;
 
   // Argument placeholders, inspired by std::placeholder. These are the indices
@@ -122,12 +125,14 @@ struct BuiltinTypeMethodBuilder {
   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);
+                           QualType ReturnTy, bool IsConst = false,
+                           bool IsConstructor = false)
+      : DeclBuilder(DB), Name(Name), ReturnTy(ReturnTy), Method(nullptr),
+        IsConst(IsConst), IsConstructor(IsConstructor) {}
+
+  BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef NameStr,
+                           QualType ReturnTy, bool IsConst = false,
+                           bool IsConstructor = false);
   BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;
 
   ~BuiltinTypeMethodBuilder() { finalizeMethod(); }
@@ -148,7 +153,14 @@ struct BuiltinTypeMethodBuilder {
   Expr *getResourceHandleExpr();
 
 private:
-  void createMethodDecl();
+  void createDecl();
+
+  // Makes sure the declaration is created; should be called before any
+  // statement added or when access to 'this' is needed.
+  void ensureCompleteDecl() {
+    if (!Method)
+      createDecl();
+  }
 };
 
 TemplateParameterListBuilder::~TemplateParameterListBuilder() {
@@ -323,13 +335,26 @@ Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) {
 }
 
 BuiltinTypeMethodBuilder::BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB,
-                                                   StringRef Name,
+                                                   StringRef NameStr,
                                                    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());
+                                                   bool IsConst,
+                                                   bool IsConstructor)
+    : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst),
+      IsConstructor(IsConstructor) {
+
+  assert((!NameStr.empty() || IsConstructor) && "method needs a name");
+  assert(((IsConstructor && !IsConst) || !IsConstructor) &&
+         "constructor cannot be const");
+
+  ASTContext &AST = DB.SemaRef.getASTContext();
+  if (IsConstructor) {
+    Name = AST.DeclarationNames.getCXXConstructorName(
+        DB.Record->getTypeForDecl()->getCanonicalTypeUnqualified());
+  } else {
+    const IdentifierInfo &II =
+        AST.Idents.get(NameStr, tok::TokenKind::identifier);
+    Name = DeclarationName(&II);
+  }
 }
 
 BuiltinTypeMethodBuilder &
@@ -342,13 +367,13 @@ BuiltinTypeMethodBuilder::addParam(StringRef Name, QualType Ty,
   return *this;
 }
 
-void BuiltinTypeMethodBuilder::createMethodDecl() {
-  assert(Method == nullptr && "Method already created");
+void BuiltinTypeMethodBuilder::createDecl() {
+  assert(Method == nullptr && "Method or constructor is already created");
 
-  // create method type
+  // create method or constructor type
   ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
   SmallVector<QualType> ParamTypes;
-  for (MethodParam &MP : Params)
+  for (Param &MP : Params)
     ParamTypes.emplace_back(MP.Ty);
 
   FunctionProtoType::ExtProtoInfo ExtInfo;
@@ -357,18 +382,26 @@ void BuiltinTypeMethodBuilder::createMethodDecl() {
 
   QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo);
 
-  // create method decl
+  // create method or constructor decl
   auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation());
-  Method = CXXMethodDecl::Create(
-      AST, DeclBuilder.Record, SourceLocation(), NameInfo, MethodTy, TSInfo,
-      SC_None, false, false, ConstexprSpecKind::Unspecified, SourceLocation());
+  DeclarationNameInfo NameInfo = DeclarationNameInfo(Name, SourceLocation());
+  if (IsConstructor)
+    Method = CXXConstructorDecl::Create(
+        AST, DeclBuilder.Record, SourceLocation(), NameInfo, MethodTy, TSInfo,
+        ExplicitSpecifier(), false, true, false,
+        ConstexprSpecKind::Unspecified);
+  else
+    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];
+    Param &MP = Params[I];
     ParmVarDecl *Parm = ParmVarDecl::Create(
         AST, Method->getDeclContext(), SourceLocation(), SourceLocation(),
         &MP.NameII, MP.Ty,
@@ -386,10 +419,7 @@ void BuiltinTypeMethodBuilder::createMethodDecl() {
 }
 
 Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() {
-  // The first statement added to a method or access to 'this' creates the
-  // declaration.
-  if (!Method)
-    createMethodDecl();
+  ensureCompleteDecl();
 
   ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
   CXXThisExpr *This = CXXThisExpr::Create(
@@ -407,10 +437,7 @@ BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName,
   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();
+  ensureCompleteDecl();
 
   ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
   FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName);
@@ -454,8 +481,8 @@ BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) {
 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?");
+
+  ensureCompleteDecl();
 
   if (!Method->hasBody()) {
     ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
@@ -600,27 +627,18 @@ BuiltinTypeDeclBuilder::addHandleMember(ResourceClass RC, ResourceKind RK,
   return *this;
 }
 
+// Adds default constructor to the resource class:
+// Resource::Resource()
 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;
+  // FIXME: initialize handle to poison value; this can be added after
+  // resource constructor from binding is implemented, otherwise the handle
+  // value will get overwritten.
+  return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy,
+                                  false, true)
+      .finalizeMethod();
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {

>From 4301a06b4dc2f96de3ea76e23685fa8b4b99e534 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 12 Mar 2025 17:51:18 -0700
Subject: [PATCH 2/2] rename finalizeMethod to finalize

---
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 27 ++++++++++---------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index a52c6a49264c8..058525d77d99e 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -71,22 +71,23 @@ struct TemplateParameterListBuilder {
 //   BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType)
 //       .addParam("param_name", Type, InOutModifier)
 //       .callBuiltin("builtin_name", BuiltinParams...)
-//       .finalizeMethod();
+//       .finalize();
 //
 // 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.
+// finalize() 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).
+// finalize() will create a return statement with the value of the last
+// statement (unless the last statement is already a ReturnStmt or the return
+// value is void).
 struct BuiltinTypeMethodBuilder {
 private:
   struct Param {
@@ -135,7 +136,7 @@ struct BuiltinTypeMethodBuilder {
                            bool IsConstructor = false);
   BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;
 
-  ~BuiltinTypeMethodBuilder() { finalizeMethod(); }
+  ~BuiltinTypeMethodBuilder() { finalize(); }
 
   BuiltinTypeMethodBuilder &
   operator=(const BuiltinTypeMethodBuilder &Other) = delete;
@@ -149,7 +150,7 @@ struct BuiltinTypeMethodBuilder {
   template <typename TLHS, typename TRHS>
   BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS);
   template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr);
-  BuiltinTypeDeclBuilder &finalizeMethod();
+  BuiltinTypeDeclBuilder &finalize();
   Expr *getResourceHandleExpr();
 
 private:
@@ -478,7 +479,7 @@ BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) {
   return *this;
 }
 
-BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalizeMethod() {
+BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalize() {
   assert(!DeclBuilder.Record->isCompleteDefinition() &&
          "record is already complete");
 
@@ -638,7 +639,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
   // value will get overwritten.
   return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy,
                                   false, true)
-      .finalizeMethod();
+      .finalize();
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {
@@ -732,7 +733,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
                                   SemaRef.getASTContext().UnsignedIntTy)
       .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
                    PH::Handle, getConstantIntExpr(1))
-      .finalizeMethod();
+      .finalize();
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
@@ -741,7 +742,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
                                   SemaRef.getASTContext().UnsignedIntTy)
       .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
                    PH::Handle, getConstantIntExpr(-1))
-      .finalizeMethod();
+      .finalize();
 }
 
 BuiltinTypeDeclBuilder &
@@ -765,7 +766,7 @@ BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name,
       .callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle,
                    PH::_0)
       .dereference(PH::LastStmt)
-      .finalizeMethod();
+      .finalize();
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
@@ -780,7 +781,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
                    AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
       .dereference(PH::LastStmt)
       .assign(PH::LastStmt, PH::_0)
-      .finalizeMethod();
+      .finalize();
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
@@ -793,7 +794,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
       .callBuiltin("__builtin_hlsl_resource_getpointer",
                    AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
       .dereference(PH::LastStmt)
-      .finalizeMethod();
+      .finalize();
 }
 
 } // namespace hlsl



More information about the llvm-branch-commits mailing list