Author: Steven Perron
Date: 2026-03-30T14:53:09Z
New Revision: 58419354a8b3a3f981507d13c3324511eb4833ca

URL: 
https://github.com/llvm/llvm-project/commit/58419354a8b3a3f981507d13c3324511eb4833ca
DIFF: 
https://github.com/llvm/llvm-project/commit/58419354a8b3a3f981507d13c3324511eb4833ca.diff

LOG: [HLSL] Implement Texture2D::mips[][] (#186143)

We implement the Textur2D::mips[][] method. We follow the design in DXC.
There is a new member called `mips` with type mips_type. The member will
contain a copy of the handle for the texture.
    
The type `mips_type` will have a member function `operator[]` that takes
a level, and returns a `mips_slice_type`. The slice will contain the
handle and the level. It also has an operator[] member function that
take a coordinate. It will do a load from the handle with the level and
coordinate, and return that value.
    
Assisted-by: Gemini

Added: 
    clang/test/CodeGenHLSL/resources/Texture2D-Mips.hlsl
    clang/test/SemaHLSL/Texture2D-mips-errors.hlsl

Modified: 
    clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
    clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
    clang/lib/Sema/HLSLExternalSemaSource.cpp
    clang/test/CodeGenHLSL/resources/Texture2D-Gather.hlsl
    clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl
    clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl
    clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl
    clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl
    clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl
    clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
    clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl
    clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl

Removed: 
    


################################################################################
diff  --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index a99b9446fd65f..3c84cd28732c9 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -16,6 +16,7 @@
 #include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclFriend.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/HLSLResource.h"
@@ -187,6 +188,7 @@ struct BuiltinTypeMethodBuilder {
     _5,
     Handle = 128,
     CounterHandle,
+    This,
     LastStmt
   };
 
@@ -226,12 +228,22 @@ struct BuiltinTypeMethodBuilder {
   template <typename TLHS, typename TRHS>
   BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS);
   template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr);
+  template <typename V, typename S>
+  BuiltinTypeMethodBuilder &concat(V Vec, S Scalar, QualType ResultTy);
 
   template <typename T>
   BuiltinTypeMethodBuilder &accessHandleFieldOnResource(T ResourceRecord);
-  template <typename ResourceT, typename ValueT>
-  BuiltinTypeMethodBuilder &setHandleFieldOnResource(ResourceT ResourceRecord,
+  template <typename T>
+  BuiltinTypeMethodBuilder &accessFieldOnResource(T ResourceRecord,
+                                                  FieldDecl *Field);
+  template <typename ValueT>
+  BuiltinTypeMethodBuilder &setHandleFieldOnResource(LocalVar &ResourceRecord,
                                                      ValueT HandleValue);
+  template <typename ResourceT, typename ValueT>
+  BuiltinTypeMethodBuilder &setFieldOnResource(ResourceT ResourceRecord,
+                                               ValueT HandleValue,
+                                               FieldDecl *HandleField);
+  void setMipsHandleField(LocalVar &ResourceRecord);
   template <typename T>
   BuiltinTypeMethodBuilder &
   accessCounterHandleFieldOnResource(T ResourceRecord);
@@ -240,7 +252,8 @@ struct BuiltinTypeMethodBuilder {
   setCounterHandleFieldOnResource(ResourceT ResourceRecord, ValueT 
HandleValue);
   template <typename T> BuiltinTypeMethodBuilder &returnValue(T ReturnValue);
   BuiltinTypeMethodBuilder &returnThis();
-  BuiltinTypeDeclBuilder &finalize();
+  BuiltinTypeDeclBuilder &
+  finalize(AccessSpecifier Access = AccessSpecifier::AS_public);
   Expr *getResourceHandleExpr();
   Expr *getResourceCounterHandleExpr();
 
@@ -253,11 +266,6 @@ struct BuiltinTypeMethodBuilder {
     if (!Method)
       createDecl();
   }
-
-  template <typename ResourceT, typename ValueT>
-  BuiltinTypeMethodBuilder &setFieldOnResource(ResourceT ResourceRecord,
-                                               ValueT HandleValue,
-                                               FieldDecl *HandleField);
 };
 
 TemplateParameterListBuilder::~TemplateParameterListBuilder() {
@@ -414,6 +422,12 @@ Expr 
*BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) {
     return getResourceHandleExpr();
   if (PH == PlaceHolder::CounterHandle)
     return getResourceCounterHandleExpr();
+  if (PH == PlaceHolder::This) {
+    ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+    return CXXThisExpr::Create(AST, SourceLocation(),
+                               Method->getFunctionObjectParameterType(),
+                               /*IsImplicit=*/true);
+  }
 
   if (PH == PlaceHolder::LastStmt) {
     assert(!StmtsList.empty() && "no statements in the list");
@@ -609,6 +623,45 @@ BuiltinTypeMethodBuilder::declareLocalVar(LocalVar &Var) {
   return *this;
 }
 
+template <typename V, typename S>
+BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::concat(V Vec, S Scalar,
+                                                           QualType ResultTy) {
+  assert(ResultTy->isVectorType() && "The result type must be a vector type.");
+  ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+  Expr *VecExpr = convertPlaceholder(Vec);
+  auto *VecTy = VecExpr->getType()->castAs<VectorType>();
+  Expr *ScalarExpr = convertPlaceholder(Scalar);
+
+  // Save the vector to a local variable to avoid evaluating the placeholder
+  // multiple times or sharing the AST node.
+  LocalVar VecVar("vec_tmp", VecTy->desugar());
+  declareLocalVar(VecVar);
+  assign(VecVar, VecExpr);
+
+  QualType EltTy = VecTy->getElementType();
+  unsigned NumElts = VecTy->getNumElements();
+
+  SmallVector<Expr *, 4> Elts;
+  for (unsigned I = 0; I < NumElts; ++I) {
+    Elts.push_back(new (AST) ArraySubscriptExpr(
+        convertPlaceholder(VecVar), DeclBuilder.getConstantIntExpr(I), EltTy,
+        VK_PRValue, OK_Ordinary, SourceLocation()));
+  }
+  Elts.push_back(ScalarExpr);
+
+  auto *InitList =
+      new (AST) InitListExpr(AST, SourceLocation(), Elts, SourceLocation());
+  InitList->setType(ResultTy);
+
+  ExprResult Cast = DeclBuilder.SemaRef.BuildCStyleCastExpr(
+      SourceLocation(), AST.getTrivialTypeSourceInfo(ResultTy),
+      SourceLocation(), InitList);
+  assert(!Cast.isInvalid() && "Cast cannot fail!");
+  StmtsList.push_back(Cast.get());
+
+  return *this;
+}
+
 BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::returnThis() {
   ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
   CXXThisExpr *ThisExpr = CXXThisExpr::Create(
@@ -705,12 +758,67 @@ BuiltinTypeMethodBuilder::accessHandleFieldOnResource(T 
ResourceRecord) {
   return *this;
 }
 
-template <typename ResourceT, typename ValueT>
+template <typename T>
+BuiltinTypeMethodBuilder &
+BuiltinTypeMethodBuilder::accessFieldOnResource(T ResourceRecord,
+                                                FieldDecl *Field) {
+  ensureCompleteDecl();
+  Expr *Base = convertPlaceholder(ResourceRecord);
+
+  ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+  auto *Member =
+      MemberExpr::CreateImplicit(AST, Base, /*IsArrow=*/false, Field,
+                                 Field->getType(), VK_LValue, OK_Ordinary);
+  StmtsList.push_back(Member);
+  return *this;
+}
+
+void BuiltinTypeMethodBuilder::setMipsHandleField(LocalVar &ResourceRecord) {
+  FieldDecl *MipsField = DeclBuilder.Fields.lookup("mips");
+  if (!MipsField)
+    return;
+
+  ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+  QualType MipsTy = MipsField->getType();
+  const auto *RT = MipsTy->castAs<RecordType>();
+  CXXRecordDecl *MipsRecord = cast<CXXRecordDecl>(RT->getDecl());
+
+  // The mips record should have a single field that is the handle.
+  assert(MipsRecord->field_begin() != MipsRecord->field_end() &&
+         "mips_type must have at least one field");
+  assert(std::next(MipsRecord->field_begin()) == MipsRecord->field_end() &&
+         "mips_type must have exactly one field");
+  FieldDecl *MipsHandleField = *MipsRecord->field_begin();
+
+  FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
+  Expr *ResExpr = convertPlaceholder(ResourceRecord);
+  MemberExpr *HandleMemberExpr = MemberExpr::CreateImplicit(
+      AST, ResExpr, false, HandleField, HandleField->getType(), VK_LValue,
+      OK_Ordinary);
+
+  MemberExpr *MipsMemberExpr =
+      MemberExpr::CreateImplicit(AST, ResExpr, false, MipsField,
+                                 MipsField->getType(), VK_LValue, OK_Ordinary);
+  MemberExpr *MipsHandleMemberExpr = MemberExpr::CreateImplicit(
+      AST, MipsMemberExpr, false, MipsHandleField, MipsHandleField->getType(),
+      VK_LValue, OK_Ordinary);
+
+  Stmt *AssignStmt = BinaryOperator::Create(
+      AST, MipsHandleMemberExpr, HandleMemberExpr, BO_Assign,
+      MipsHandleMemberExpr->getType(), ExprValueKind::VK_LValue,
+      ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride());
+
+  StmtsList.push_back(AssignStmt);
+}
+
+template <typename ValueT>
 BuiltinTypeMethodBuilder &
-BuiltinTypeMethodBuilder::setHandleFieldOnResource(ResourceT ResourceRecord,
+BuiltinTypeMethodBuilder::setHandleFieldOnResource(LocalVar &ResourceRecord,
                                                    ValueT HandleValue) {
-  return setFieldOnResource(ResourceRecord, HandleValue,
-                            DeclBuilder.getResourceHandleField());
+  setFieldOnResource(ResourceRecord, HandleValue,
+                     DeclBuilder.getResourceHandleField());
+  setMipsHandleField(ResourceRecord);
+  return *this;
 }
 
 template <typename ResourceT, typename ValueT>
@@ -791,7 +899,8 @@ BuiltinTypeMethodBuilder 
&BuiltinTypeMethodBuilder::returnValue(T ReturnValue) {
   return *this;
 }
 
-BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalize() {
+BuiltinTypeDeclBuilder &
+BuiltinTypeMethodBuilder::finalize(AccessSpecifier Access) {
   assert(!DeclBuilder.Record->isCompleteDefinition() &&
          "record is already complete");
 
@@ -818,9 +927,11 @@ BuiltinTypeDeclBuilder 
&BuiltinTypeMethodBuilder::finalize() {
     Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(),
                                          SourceLocation(), SourceLocation()));
     Method->setLexicalDeclContext(DeclBuilder.Record);
-    Method->setAccess(AS_public);
+    Method->setAccess(Access);
+    Method->setImplicitlyInline();
     Method->addAttr(AlwaysInlineAttr::CreateImplicit(
         AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline));
+    Method->addAttr(ConvergentAttr::CreateImplicit(AST));
     if (!TemplateParamDecls.empty()) {
       TemplateParams = TemplateParameterList::Create(
           AST, SourceLocation(), SourceLocation(), TemplateParamDecls,
@@ -921,9 +1032,11 @@ BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addBufferHandles(ResourceClass RC, bool IsROV,
                                          bool RawBuffer, bool HasCounter,
                                          AccessSpecifier Access) {
-  addHandleMember(RC, ResourceDimension::Unknown, IsROV, RawBuffer, Access);
+  QualType ElementTy = getHandleElementType();
+  addHandleMember(RC, ResourceDimension::Unknown, IsROV, RawBuffer, ElementTy,
+                  Access);
   if (HasCounter)
-    addCounterHandleMember(RC, IsROV, RawBuffer, Access);
+    addCounterHandleMember(RC, IsROV, RawBuffer, ElementTy, Access);
   return *this;
 }
 
@@ -931,39 +1044,71 @@ BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addTextureHandle(ResourceClass RC, bool IsROV,
                                          ResourceDimension RD,
                                          AccessSpecifier Access) {
-  addHandleMember(RC, RD, IsROV, /*RawBuffer=*/false, Access);
+  addHandleMember(RC, RD, IsROV, /*RawBuffer=*/false, getHandleElementType(),
+                  Access);
   return *this;
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSamplerHandle() {
   addHandleMember(ResourceClass::Sampler, ResourceDimension::Unknown,
-                  /*IsROV=*/false, /*RawBuffer=*/false);
+                  /*IsROV=*/false, /*RawBuffer=*/false, 
getHandleElementType());
   return *this;
 }
 
 BuiltinTypeDeclBuilder &
-BuiltinTypeDeclBuilder::addHandleMember(ResourceClass RC, ResourceDimension RD,
-                                        bool IsROV, bool RawBuffer,
-                                        AccessSpecifier Access) {
+BuiltinTypeDeclBuilder::addFriend(CXXRecordDecl *Friend) {
+  assert(!Record->isCompleteDefinition() && "record is already complete");
+  ASTContext &AST = SemaRef.getASTContext();
+  QualType FriendTy = AST.getCanonicalTagType(Friend);
+  TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(FriendTy);
+  FriendDecl *FD =
+      FriendDecl::Create(AST, Record, SourceLocation(), TSI, SourceLocation());
+  FD->setAccess(AS_public);
+  Record->addDecl(FD);
+  return *this;
+}
+
+CXXRecordDecl *BuiltinTypeDeclBuilder::addPrivateNestedRecord(StringRef Name) {
+  assert(!Record->isCompleteDefinition() && "record is already complete");
+  ASTContext &AST = SemaRef.getASTContext();
+  IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
+  CXXRecordDecl *NestedRecord =
+      CXXRecordDecl::Create(AST, TagDecl::TagKind::Struct, Record,
+                            SourceLocation(), SourceLocation(), &II);
+  NestedRecord->setImplicit(true);
+  NestedRecord->setAccess(AccessSpecifier::AS_private);
+  NestedRecord->setLexicalDeclContext(Record);
+  Record->addDecl(NestedRecord);
+  return NestedRecord;
+}
+
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember(
+    ResourceClass RC, ResourceDimension RD, bool IsROV, bool RawBuffer,
+    QualType ElementTy, AccessSpecifier Access) {
   return addResourceMember("__handle", RC, RD, IsROV, RawBuffer,
-                           /*IsCounter=*/false, Access);
+                           /*IsCounter=*/false, ElementTy, Access);
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember(
-    ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) {
+    ResourceClass RC, bool IsROV, bool RawBuffer, QualType ElementTy,
+    AccessSpecifier Access) {
   return addResourceMember("__counter_handle", RC, ResourceDimension::Unknown,
                            IsROV, RawBuffer,
-                           /*IsCounter=*/true, Access);
+                           /*IsCounter=*/true, ElementTy, Access);
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember(
     StringRef MemberName, ResourceClass RC, ResourceDimension RD, bool IsROV,
-    bool RawBuffer, bool IsCounter, AccessSpecifier Access) {
+    bool RawBuffer, bool IsCounter, QualType ElementTy,
+    AccessSpecifier Access) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
 
   ASTContext &Ctx = SemaRef.getASTContext();
+
+  assert(!ElementTy.isNull() &&
+         "The caller should always pass in the type for the handle.");
   TypeSourceInfo *ElementTypeInfo =
-      Ctx.getTrivialTypeSourceInfo(getHandleElementType(), SourceLocation());
+      Ctx.getTrivialTypeSourceInfo(ElementTy, SourceLocation());
 
   // add handle member with resource type attributes
   QualType AttributedResTy = QualType();
@@ -988,7 +1133,8 @@ BuiltinTypeDeclBuilder 
&BuiltinTypeDeclBuilder::addResourceMember(
 
 // Adds default constructor to the resource class:
 // Resource::Resource()
-BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addDefaultHandleConstructor(AccessSpecifier Access) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
 
   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
@@ -998,7 +1144,7 @@ BuiltinTypeDeclBuilder 
&BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
       .callBuiltin("__builtin_hlsl_resource_uninitializedhandle", HandleType,
                    PH::Handle)
       .assign(PH::Handle, PH::LastStmt)
-      .finalize();
+      .finalize(Access);
 }
 
 BuiltinTypeDeclBuilder &
@@ -1185,7 +1331,8 @@ 
BuiltinTypeDeclBuilder::addCreateFromImplicitBindingWithImplicitCounter() {
       .finalize();
 }
 
-BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyConstructor() {
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addCopyConstructor(AccessSpecifier Access) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
 
   ASTContext &AST = SemaRef.getASTContext();
@@ -1197,18 +1344,18 @@ BuiltinTypeDeclBuilder 
&BuiltinTypeDeclBuilder::addCopyConstructor() {
 
   BuiltinTypeMethodBuilder MMB(*this, /*Name=*/"", AST.VoidTy,
                                /*IsConst=*/false, /*IsCtor=*/true);
-  MMB.addParam("other", ConstRecordRefType)
-      .accessHandleFieldOnResource(PH::_0)
-      .assign(PH::Handle, PH::LastStmt);
+  MMB.addParam("other", ConstRecordRefType);
 
-  if (getResourceCounterHandleField())
-    MMB.accessCounterHandleFieldOnResource(PH::_0).assign(PH::CounterHandle,
-                                                          PH::LastStmt);
+  for (auto *Field : Record->fields()) {
+    MMB.accessFieldOnResource(PH::_0, Field)
+        .setFieldOnResource(PH::This, PH::LastStmt, Field);
+  }
 
-  return MMB.finalize();
+  return MMB.finalize(Access);
 }
 
-BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyAssignmentOperator() {
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addCopyAssignmentOperator(AccessSpecifier Access) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
 
   ASTContext &AST = SemaRef.getASTContext();
@@ -1220,15 +1367,14 @@ BuiltinTypeDeclBuilder 
&BuiltinTypeDeclBuilder::addCopyAssignmentOperator() {
   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
   DeclarationName Name = AST.DeclarationNames.getCXXOperatorName(OO_Equal);
   BuiltinTypeMethodBuilder MMB(*this, Name, RecordRefType);
-  MMB.addParam("other", ConstRecordRefType)
-      .accessHandleFieldOnResource(PH::_0)
-      .assign(PH::Handle, PH::LastStmt);
+  MMB.addParam("other", ConstRecordRefType);
 
-  if (getResourceCounterHandleField())
-    MMB.accessCounterHandleFieldOnResource(PH::_0).assign(PH::CounterHandle,
-                                                          PH::LastStmt);
+  for (auto *Field : Record->fields()) {
+    MMB.accessFieldOnResource(PH::_0, Field)
+        .setFieldOnResource(PH::This, PH::LastStmt, Field);
+  }
 
-  return MMB.returnThis().finalize();
+  return MMB.returnThis().finalize(Access);
 }
 
 BuiltinTypeDeclBuilder &
@@ -1269,6 +1415,115 @@ BuiltinTypeDeclBuilder 
&BuiltinTypeDeclBuilder::addLoadMethods() {
   return *this;
 }
 
+CXXRecordDecl *BuiltinTypeDeclBuilder::addMipsSliceType(ResourceDimension Dim,
+                                                        QualType ReturnType) {
+  ASTContext &AST = Record->getASTContext();
+  uint32_t VecSize = getResourceDimensions(Dim);
+  QualType IntTy = AST.IntTy;
+  QualType IndexTy = VecSize > 1 ? AST.getExtVectorType(IntTy, VecSize) : 
IntTy;
+  QualType CoordLevelTy = AST.getExtVectorType(IntTy, VecSize + 1);
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+  // Define the mips_slice_type which is returned by mips_type::operator[].
+  // It holds the resource handle and the mip level. It has an operator[]
+  // that takes the coordinate and performs the actual resource load.
+  CXXRecordDecl *MipsSliceRecord = addPrivateNestedRecord("mips_slice_type");
+  BuiltinTypeDeclBuilder MipsSliceBuilder(SemaRef, MipsSliceRecord);
+  MipsSliceBuilder.addFriend(Record)
+      .addHandleMember(getResourceAttrs().ResourceClass, Dim,
+                       getResourceAttrs().IsROV, false, ReturnType,
+                       AccessSpecifier::AS_public)
+      .addMemberVariable("__level", IntTy, {}, AccessSpecifier::AS_public)
+      .addDefaultHandleConstructor(AccessSpecifier::AS_protected)
+      .addCopyConstructor(AccessSpecifier::AS_protected)
+      .addCopyAssignmentOperator(AccessSpecifier::AS_protected);
+
+  FieldDecl *LevelField = MipsSliceBuilder.Fields["__level"];
+  assert(LevelField && "Could not find the level field.");
+
+  DeclarationName SubscriptName =
+      AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
+
+  // operator[](intN coord) on mips_slice_type
+  BuiltinTypeMethodBuilder(MipsSliceBuilder, SubscriptName, ReturnType,
+                           /*IsConst=*/true)
+      .addParam("Coord", IndexTy)
+      .accessFieldOnResource(PH::This, LevelField)
+      .concat(PH::_0, PH::LastStmt, CoordLevelTy)
+      .callBuiltin("__builtin_hlsl_resource_load_level", ReturnType, 
PH::Handle,
+                   PH::LastStmt)
+      .finalize();
+
+  MipsSliceBuilder.completeDefinition();
+  return MipsSliceRecord;
+}
+
+CXXRecordDecl *BuiltinTypeDeclBuilder::addMipsType(ResourceDimension Dim,
+                                                   QualType ReturnType) {
+  ASTContext &AST = Record->getASTContext();
+  QualType IntTy = AST.IntTy;
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+  // First, define the mips_slice_type that will be returned by our operator[].
+  CXXRecordDecl *MipsSliceRecord = addMipsSliceType(Dim, ReturnType);
+
+  // Define the mips_type, which provides the syntax `Resource.mips[level]`.
+  // It only holds the handle, and its operator[] returns a mips_slice_type
+  // initialized with the handle and the requested mip level.
+  CXXRecordDecl *MipsRecord = addPrivateNestedRecord("mips_type");
+  BuiltinTypeDeclBuilder MipsBuilder(SemaRef, MipsRecord);
+  MipsBuilder.addFriend(Record)
+      .addHandleMember(getResourceAttrs().ResourceClass, Dim,
+                       getResourceAttrs().IsROV, false, ReturnType,
+                       AccessSpecifier::AS_public)
+      .addDefaultHandleConstructor(AccessSpecifier::AS_protected)
+      .addCopyConstructor(AccessSpecifier::AS_protected)
+      .addCopyAssignmentOperator(AccessSpecifier::AS_protected);
+
+  QualType MipsSliceTy = AST.getCanonicalTagType(MipsSliceRecord);
+
+  DeclarationName SubscriptName =
+      AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
+
+  // Locate the fields in the slice type so we can initialize them.
+  auto FieldIt = MipsSliceRecord->field_begin();
+  FieldDecl *MipsSliceHandleField = *FieldIt;
+  FieldDecl *LevelField = *++FieldIt;
+  assert(MipsSliceHandleField->getName() == "__handle" &&
+         LevelField->getName() == "__level" &&
+         "Could not find fields on mips_slice_type");
+
+  // operator[](int level) on mips_type
+  BuiltinTypeMethodBuilder::LocalVar MipsSliceVar("slice", MipsSliceTy);
+  BuiltinTypeMethodBuilder(MipsBuilder, SubscriptName, MipsSliceTy,
+                           /*IsConst=*/true)
+      .addParam("Level", IntTy)
+      .declareLocalVar(MipsSliceVar)
+      .accessHandleFieldOnResource(PH::This)
+      .setFieldOnResource(MipsSliceVar, PH::LastStmt, MipsSliceHandleField)
+      .setFieldOnResource(MipsSliceVar, PH::_0, LevelField)
+      .returnValue(MipsSliceVar)
+      .finalize();
+
+  MipsBuilder.completeDefinition();
+  return MipsRecord;
+}
+
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addMipsMember(ResourceDimension Dim) {
+  assert(!Record->isCompleteDefinition() && "record is already complete");
+  ASTContext &AST = Record->getASTContext();
+  QualType ReturnType = getHandleElementType();
+
+  CXXRecordDecl *MipsRecord = addMipsType(Dim, ReturnType);
+
+  // Add the mips field to the texture
+  QualType MipsTy = AST.getCanonicalTagType(MipsRecord);
+  addMemberVariable("mips", MipsTy, {}, AccessSpecifier::AS_public);
+
+  return *this;
+}
+
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addTextureLoadMethods(ResourceDimension Dim) {
   assert(!Record->isCompleteDefinition() && "record is already complete");

diff  --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
index 4fe6acabfac62..430dc00630676 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -87,9 +87,12 @@ class BuiltinTypeDeclBuilder {
       ResourceDimension Dim = ResourceDimension::Unknown);
 
   // Builtin types constructors
-  BuiltinTypeDeclBuilder &addDefaultHandleConstructor();
-  BuiltinTypeDeclBuilder &addCopyConstructor();
-  BuiltinTypeDeclBuilder &addCopyAssignmentOperator();
+  BuiltinTypeDeclBuilder &addDefaultHandleConstructor(
+      AccessSpecifier Access = AccessSpecifier::AS_public);
+  BuiltinTypeDeclBuilder &
+  addCopyConstructor(AccessSpecifier Access = AccessSpecifier::AS_public);
+  BuiltinTypeDeclBuilder &addCopyAssignmentOperator(
+      AccessSpecifier Access = AccessSpecifier::AS_public);
 
   // Static create methods
   BuiltinTypeDeclBuilder &addStaticInitializationFunctions(bool HasCounter);
@@ -122,23 +125,29 @@ class BuiltinTypeDeclBuilder {
   BuiltinTypeDeclBuilder &addConsumeMethod();
 
   BuiltinTypeDeclBuilder &addGetDimensionsMethodForBuffer();
+  BuiltinTypeDeclBuilder &addMipsMember(ResourceDimension Dim);
 
 private:
   BuiltinTypeDeclBuilder &addCreateFromBinding();
   BuiltinTypeDeclBuilder &addCreateFromImplicitBinding();
   BuiltinTypeDeclBuilder &addCreateFromBindingWithImplicitCounter();
   BuiltinTypeDeclBuilder &addCreateFromImplicitBindingWithImplicitCounter();
-  BuiltinTypeDeclBuilder &addResourceMember(StringRef MemberName,
-                                            ResourceClass RC,
-                                            ResourceDimension RD, bool IsROV,
-                                            bool RawBuffer, bool IsCounter,
-                                            AccessSpecifier Access);
+  BuiltinTypeDeclBuilder &
+  addResourceMember(StringRef MemberName, ResourceClass RC,
+                    ResourceDimension RD, bool IsROV, bool RawBuffer,
+                    bool IsCounter, QualType ElementTy,
+                    AccessSpecifier Access = AccessSpecifier::AS_private);
+  BuiltinTypeDeclBuilder &addFriend(CXXRecordDecl *Friend);
+  CXXRecordDecl *addPrivateNestedRecord(StringRef Name);
+  CXXRecordDecl *addMipsSliceType(ResourceDimension Dim, QualType ReturnType);
+  CXXRecordDecl *addMipsType(ResourceDimension Dim, QualType ReturnType);
   BuiltinTypeDeclBuilder &
   addHandleMember(ResourceClass RC, ResourceDimension RD, bool IsROV,
-                  bool RawBuffer,
+                  bool RawBuffer, QualType ElementTy,
                   AccessSpecifier Access = AccessSpecifier::AS_private);
   BuiltinTypeDeclBuilder &
   addCounterHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer,
+                         QualType ElementTy,
                          AccessSpecifier Access = AccessSpecifier::AS_private);
   QualType getGatherReturnType();
   FieldDecl *getResourceHandleField() const;

diff  --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp 
b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index fb40ccc674349..8f99acb4dd442 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -260,6 +260,7 @@ static BuiltinTypeDeclBuilder 
setupTextureType(CXXRecordDecl *Decl, Sema &S,
       .addTextureHandle(RC, IsROV, Dim)
       .addTextureLoadMethods(Dim)
       .addArraySubscriptOperators(Dim)
+      .addMipsMember(Dim)
       .addDefaultHandleConstructor()
       .addCopyConstructor()
       .addCopyAssignmentOperator()

diff  --git a/clang/test/CodeGenHLSL/resources/Texture2D-Gather.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-Gather.hlsl
index 54d428285d88c..2f4cb1d5b98ed 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-Gather.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-Gather.hlsl
@@ -1,11 +1,11 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,DXIL
 // RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,SPIRV
 
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
 // DXIL: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) }
 // DXIL: %"class.hlsl::SamplerComparisonState" = type { target("dx.Sampler", 
0) }
 
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
 // SPIRV: %"class.hlsl::SamplerState" = type { target("spirv.Sampler") }
 // SPIRV: %"class.hlsl::SamplerComparisonState" = type { 
target("spirv.Sampler") }
 

diff  --git a/clang/test/CodeGenHLSL/resources/Texture2D-Mips.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-Mips.hlsl
new file mode 100644
index 0000000000000..b84df2b184283
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-Mips.hlsl
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-pixel -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,DXIL
+
+Texture2D<float4> t;
+
+// CHECK: define internal {{.*}} <4 x float> @test_mips(float vector[2])(<2 x 
float> {{.*}} %loc) #1 {
+// CHECK: entry:
+// CHECK: %[[LOC_ADDR:.*]] = alloca <2 x float>
+// CHECK: %[[REF_TMP:.*]] = alloca %"struct.hlsl::Texture2D<>::mips_slice_type"
+// CHECK: store <2 x float> %loc, ptr %[[LOC_ADDR]]
+// CHECK: call void @hlsl::Texture2D<float 
vector[4]>::mips_type::operator[](int) const(ptr {{.*}} %[[REF_TMP]], ptr 
{{.*}} getelementptr {{.*}} (i8, ptr @t, i32 4), i32 noundef 0)
+// CHECK: %[[V0:.*]] = load <2 x float>, ptr %[[LOC_ADDR]]
+// CHECK: %[[CONV:.*]] = fptosi <2 x float> %[[V0]] to <2 x i32>
+// CHECK: %[[CALL:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float 
vector[4]>::mips_slice_type::operator[](int vector[2]) const(ptr {{.*}} 
%[[REF_TMP]], <2 x i32> {{.*}} %[[CONV]])
+// CHECK: ret <4 x float> %[[CALL]]
+
+[shader("pixel")]
+float4 test_mips(float2 loc : LOC) : SV_Target {
+  return t.mips[0][int2(loc)];
+}
+
+// CHECK: define linkonce_odr hidden void @hlsl::Texture2D<float 
vector[4]>::mips_type::operator[](int) const(ptr  {{.*}} %agg.result, ptr 
{{.*}} %this, i32 {{.*}} %Level)
+// CHECK: entry:
+// CHECK: %{{.*}} = alloca ptr
+// CHECK: %[[THIS_ADDR:.*]] = alloca ptr
+// CHECK: %[[LEVEL_ADDR:.*]] = alloca i32
+// CHECK: %[[SLICE:.*]] = alloca %"struct.hlsl::Texture2D<>::mips_slice_type"
+// CHECK: store ptr %agg.result, ptr %{{.*}}
+// CHECK: store ptr %this, ptr %[[THIS_ADDR]]
+// CHECK: store i32 %Level, ptr %[[LEVEL_ADDR]]
+// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// CHECK: call void @hlsl::Texture2D<float 
vector[4]>::mips_slice_type::mips_slice_type()(ptr {{.*}} %[[SLICE]])
+// CHECK: %[[HANDLE_GEP:.*]] = getelementptr {{.*}} 
%"struct.hlsl::Texture2D<>::mips_type", ptr %[[THIS1]], i32 0, i32 0
+// CHECK: %[[HANDLE:.*]] = load target("dx.Texture", <4 x float>, 0, 0, 0, 2), 
ptr %[[HANDLE_GEP]]
+// CHECK: %[[HANDLE_GEP2:.*]] = getelementptr {{.*}} 
%"struct.hlsl::Texture2D<>::mips_slice_type", ptr %[[SLICE]], i32 0, i32 0
+// CHECK: store target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE]], ptr 
%[[HANDLE_GEP2]]
+// CHECK: %[[L_VAL:.*]] = load i32, ptr %[[LEVEL_ADDR]]
+// CHECK: %[[LEVEL_GEP:.*]] = getelementptr {{.*}} 
%"struct.hlsl::Texture2D<>::mips_slice_type", ptr %[[SLICE]], i32 0, i32 1
+// CHECK: store i32 %[[L_VAL]], ptr %[[LEVEL_GEP]]
+// CHECK: call void @hlsl::Texture2D<float 
vector[4]>::mips_slice_type::mips_slice_type(hlsl::Texture2D<float 
vector[4]>::mips_slice_type const&)(ptr noundef nonnull align 4 
dereferenceable(8) %agg.result, ptr noundef nonnull align 4 dereferenceable(8) 
%[[SLICE]])
+
+// CHECK: define linkonce_odr hidden {{.*}} <4 x float> @hlsl::Texture2D<float 
vector[4]>::mips_slice_type::operator[](int vector[2]) const(ptr {{.*}} 
%[[THIS:.*]], <2 x i32> noundef %[[COORD:.*]])
+// CHECK: entry:
+// CHECK: %[[COORD_ADDR:.*]] = alloca <2 x i32>
+// CHECK: %[[VEC_TMP:.*]] = alloca <2 x i32>
+// CHECK: store <2 x i32> %[[COORD]], ptr %[[COORD_ADDR]]
+// CHECK: %[[THIS1:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[COORD_PARAM:.*]] = load <2 x i32>, ptr %[[COORD_ADDR]]
+// CHECK: store <2 x i32> %[[COORD_PARAM]], ptr %[[VEC_TMP]]
+// CHECK: %[[HANDLE_PTR:.*]] = getelementptr {{.*}} 
%"struct.hlsl::Texture2D<>::mips_slice_type", ptr %[[THIS1]], i32 0, i32 0
+// CHECK: %[[HANDLE:.*]] = load target("dx.Texture", <4 x float>, 0, 0, 0, 2), 
ptr %[[HANDLE_PTR]]
+// CHECK: %[[COORD_VAL:.*]] = load <2 x i32>, ptr %[[VEC_TMP]]
+// CHECK: %[[VECEXT:.*]] = extractelement <2 x i32> %[[COORD_VAL]], i32 0
+// CHECK: %[[VECINIT:.*]] = insertelement <3 x i32> poison, i32 %[[VECEXT]], 
i32 0
+// CHECK: %[[COORD_VAL2:.*]] = load <2 x i32>, ptr %[[VEC_TMP]]
+// CHECK: %[[VECEXT2:.*]] = extractelement <2 x i32> %[[COORD_VAL2]], i32 1
+// CHECK: %[[VECINIT3:.*]] = insertelement <3 x i32> %[[VECINIT]], i32 
%[[VECEXT2]], i32 1
+// CHECK: %[[LEVEL_PTR:.*]] = getelementptr {{.*}} 
%"struct.hlsl::Texture2D<>::mips_slice_type", ptr %[[THIS1]], i32 0, i32 1
+// CHECK: %[[LEVEL_VAL:.*]] = load i32, ptr %[[LEVEL_PTR]]
+// CHECK: %[[VECINIT4:.*]] = insertelement <3 x i32> %[[VECINIT3]], i32 
%[[LEVEL_VAL]], i32 2
+// CHECK: %[[COORD_X:.*]] = shufflevector <3 x i32> %[[VECINIT4]], <3 x i32> 
poison, <2 x i32> <i32 0, i32 1>
+// CHECK: %[[LOD:.*]] = extractelement <3 x i32> %[[VECINIT4]], i64 2
+// DXIL: %[[RES:.*]] = call {{.*}} <4 x float> 
@llvm.dx.resource.load.level.v4f32.tdx.Texture_v4f32_0_0_0_2t.v2i32.i32.v2i32(target("dx.Texture",
 <4 x float>, 0, 0, 0, 2) %[[HANDLE]], <2 x i32> %[[COORD_X]], i32 %[[LOD]], <2 
x i32> zeroinitializer)
+// CHECK: ret <4 x float> %[[RES]]

diff  --git a/clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl
index 93bed2f2b7c27..e37e182be6b44 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl
@@ -1,10 +1,10 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,DXIL
 // RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,SPIRV
 
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
 // DXIL: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) }
 
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
 // SPIRV: %"class.hlsl::SamplerState" = type { target("spirv.Sampler") }
 
 Texture2D<float4> t;

diff  --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl
index c138e7f0a6c8b..481a66c7a59a9 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl
@@ -1,10 +1,10 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,DXIL
 // RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,SPIRV
 
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
 // DXIL: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) }
 
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
 // SPIRV: %"class.hlsl::SamplerState" = type { target("spirv.Sampler") }
 
 Texture2D<float4> t;

diff  --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl
index b76b02177abbc..5262aed2816ef 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl
@@ -1,10 +1,10 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,DXIL
 // RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,SPIRV
 
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
 // DXIL: %"class.hlsl::SamplerComparisonState" = type { target("dx.Sampler", 
0) }
 
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
 // SPIRV: %"class.hlsl::SamplerComparisonState" = type { 
target("spirv.Sampler") }
 
 Texture2D<float4> t;

diff  --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl
index 279521c0bb988..1a2cd93a34168 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl
@@ -1,10 +1,10 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,DXIL
 // RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,SPIRV
 
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
 // DXIL: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) }
 
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
 // SPIRV: %"class.hlsl::SamplerState" = type { target("spirv.Sampler") }
 
 Texture2D<float4> t;

diff  --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl
index bcd025c164ac0..85fae6ea278f2 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl
@@ -1,10 +1,10 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,DXIL
 // RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s --check-prefixes=CHECK,SPIRV
 
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
 // DXIL: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) }
 
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
 // SPIRV: %"class.hlsl::SamplerState" = type { target("spirv.Sampler") }
 
 Texture2D<float4> t;

diff  --git 
a/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
index 06b4cb7ec9900..9a7819364ee83 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
@@ -5,8 +5,8 @@ SamplerState g_s : register(s0);
 Texture2D<> default_template : register(t1, space2);
 Texture2D implicit_template : register(t0, space1);
 
-// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0) }
+// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 
0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
 
 // CHECK: @{{.*}}default_template = internal global %"class.hlsl::Texture2D" 
poison, align {{[0-9]+}}
 // CHECK: @{{.*}}implicit_template = internal global %"class.hlsl::Texture2D" 
poison, align {{[0-9]+}}

diff  --git a/clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl
index 5b5bb737a7958..8ff981e4fa512 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl 
-std=hlsl202x -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s 
| FileCheck %s
 
-// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
-// CHECK: %"class.hlsl::Texture2D.0" = type { target("dx.Texture", float, 0, 
0, 0, 2) }
+// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
+// CHECK: %"class.hlsl::Texture2D.0" = type { target("dx.Texture", float, 0, 
0, 0, 2), %"struct.hlsl::Texture2D<float>::mips_type" }
 
 // CHECK: @{{.*}}t1 = internal global %"class.hlsl::Texture2D" poison, align 4
 Texture2D<> t1;

diff  --git 
a/clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl 
b/clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl
index 71ce46232d088..0367e2360242b 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl 
-finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt 
| FileCheck %s
 
-// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2) }
+// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 
0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
 
 SamplerState g_s : register(s0);
 

diff  --git a/clang/test/SemaHLSL/Texture2D-mips-errors.hlsl 
b/clang/test/SemaHLSL/Texture2D-mips-errors.hlsl
new file mode 100644
index 0000000000000..0c0fc074cae0b
--- /dev/null
+++ b/clang/test/SemaHLSL/Texture2D-mips-errors.hlsl
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm 
-disable-llvm-passes -finclude-default-header -verify %s
+
+Texture2D<float4> t;
+
+template<class T>
+float4 foo(T t) {
+  return t[int2(0, 0)];
+}
+
+[shader("pixel")]
+float4 test_mips() : SV_Target {
+  // expected-error@+4 {{'mips_type' is a private member of 
'hlsl::Texture2D<>'}}
+  // expected-note@*:* {{implicitly declared private here}}
+  // expected-error@+2 {{calling a protected constructor of class 
'hlsl::Texture2D<>::mips_type'}}
+  // expected-note@*:* {{implicitly declared protected here}}
+  Texture2D<float4>::mips_type a; 
+
+  // expected-error@+4 {{'mips_slice_type' is a private member of 
'hlsl::Texture2D<>'}}
+  // expected-note@*:* {{implicitly declared private here}}
+  // expected-error@+2 {{calling a protected constructor of class 
'hlsl::Texture2D<>::mips_slice_type'}}
+  // expected-note@*:* {{implicitly declared protected here}}
+  Texture2D<float4>::mips_slice_type b;
+
+  // expected-warning@+3 {{'auto' type specifier is a HLSL 202y extension}}
+  // expected-error@+2 {{calling a protected constructor of class 
'hlsl::Texture2D<>::mips_type'}}
+  // expected-note@*:* {{implicitly declared protected here}}
+  auto c = t.mips;
+
+  // Note: t.mips[0] correctly returns a mips_slice_type prvalue.
+  // Passing it to a template function like 'foo(t.mips[0])' currently crashes 
+  // the compiler due to an unrelated bug in HLSL template instantiation.
+  // See: https://github.com/llvm/llvm-project/issues/188556
+  // return t.mips[0][int2(0, 0)] + foo(t.mips[0]);
+  return t.mips[0][int2(0, 0)];
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to