[clang] [HLSL][Sema] Validate that occupied register numbers never exceed UINT32_MAX (PR #174028)
Joshua Batista via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 7 13:37:16 PST 2026
https://github.com/bob80905 updated https://github.com/llvm/llvm-project/pull/174028
>From e895abde4b01be94499ed343f1e40c6f32102404 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 30 Dec 2025 13:46:14 -0800
Subject: [PATCH 01/12] first attempt
---
.../clang/Basic/DiagnosticSemaKinds.td | 1 +
clang/lib/Sema/SemaHLSL.cpp | 102 ++++++++++++++++++
...esource_binding_attr_error_uint32_max.hlsl | 31 ++++++
3 files changed, 134 insertions(+)
create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f3c13841c4bf0..37dfa0892926e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13338,6 +13338,7 @@ def warn_hlsl_register_type_c_packoffset: Warning<"binding type 'c' ignored in b
def warn_hlsl_deprecated_register_type_b: Warning<"binding type 'b' only applies to constant buffers. The 'bool constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError;
def warn_hlsl_deprecated_register_type_i: Warning<"binding type 'i' ignored. The 'integer constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError;
def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
+def err_hlsl_register_number_too_large : Error<"register number should not exceed UINT32_MAX, 4294967295">;
def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
def err_hlsl_space_on_global_constant : Error<"register space cannot be specified on global constants">;
def warn_hlsl_implicit_binding : Warning<"resource has implicit register binding">, InGroup<HLSLImplicitBinding>, DefaultIgnore;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index a6de1cd550212..7760b1f791374 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2361,6 +2361,99 @@ static bool DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
return ValidateMultipleRegisterAnnotations(S, D, RegType);
}
+bool ExceedsUInt32Max(llvm::StringRef S) {
+ constexpr size_t MaxDigits = 10; // UINT32_MAX = 4294967295
+ if (S.size() > MaxDigits)
+ return true;
+
+ if (S.size() < MaxDigits)
+ return false;
+
+ return S.compare("4294967295") > 0;
+}
+
+// return false if the slot count exceeds the limit, true otherwise
+static bool AccumulateHLSLResourceSlots(QualType Ty, llvm::APInt &SlotCount,
+ const llvm::APInt &Limit,
+ ASTContext &Ctx,
+ uint64_t Multiplier = 1) {
+ Ty = Ty.getCanonicalType();
+ const Type *T = Ty.getTypePtr();
+
+ // Early exit if already overflowed
+ if (SlotCount.ugt(Limit))
+ return false;
+
+ // Case 1: array type
+ if (const auto *AT = dyn_cast<ArrayType>(T)) {
+ uint64_t Count = 1;
+
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) {
+ Count = CAT->getSize().getZExtValue();
+ }
+ // TODO: how do we handle non constant resource arrays?
+
+ QualType ElemTy = AT->getElementType();
+
+ return AccumulateHLSLResourceSlots(ElemTy, SlotCount, Limit, Ctx,
+ Multiplier * Count);
+ }
+
+ // Case 2: resource leaf
+ if (T->isHLSLResourceRecord()) {
+ llvm::APInt Add(SlotCount.getBitWidth(), Multiplier);
+ SlotCount += Add;
+ return SlotCount.ule(Limit);
+ }
+
+ // Case 3: struct / record
+ if (const auto *RT = dyn_cast<RecordType>(T)) {
+ const RecordDecl *RD = RT->getDecl();
+ for (const FieldDecl *Field : RD->fields()) {
+ if (!AccumulateHLSLResourceSlots(Field->getType(), SlotCount, Limit, Ctx,
+ Multiplier))
+ return false;
+ }
+ return true;
+ }
+
+ // Case 4: everything else
+ return true;
+}
+
+// return true if there is something invalid, false otherwise
+bool ValidateRegisterNumber(StringRef SlotNumStr, Decl *TheDecl,
+ ASTContext &Ctx) {
+ if (ExceedsUInt32Max(SlotNumStr))
+ return true;
+
+ llvm::APInt SlotNum;
+ if (SlotNumStr.getAsInteger(10, SlotNum))
+ return false;
+ SlotNum = SlotNum.zext(64);
+
+ // uint32_max isn't 64 bits, but this int should
+ // have a 64 bit width in case it is compared to
+ // another 64 bit-width value. Assert failure otherwise.
+ llvm::APInt Limit(64, UINT32_MAX);
+ VarDecl *VD = dyn_cast<VarDecl>(TheDecl);
+ if (VD) {
+ AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit, Ctx);
+ return SlotNum.ugt(Limit);
+ }
+ // handle the cbuffer case
+ HLSLBufferDecl *HBD = dyn_cast<HLSLBufferDecl>(TheDecl);
+ if (HBD) {
+ // resources cannot be put within a cbuffer, so no need
+ // to analyze the structure since the register number
+ // won't be pushed any higher.
+ return SlotNum.ugt(Limit);
+ }
+
+ // we don't expect any other decl type, so fail
+ return true;
+}
+
void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
if (VarDecl *VD = dyn_cast<VarDecl>(TheDecl)) {
QualType Ty = VD->getType();
@@ -2420,6 +2513,15 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
return;
}
StringRef SlotNumStr = Slot.substr(1);
+
+ // Validate register number. It should not exceed UINT32_MAX,
+ // including if the resource type is an array that starts
+ // before UINT32_MAX, but ends afterwards.
+ if (ValidateRegisterNumber(SlotNumStr, TheDecl, getASTContext())) {
+ Diag(SlotLoc, diag::err_hlsl_register_number_too_large);
+ return;
+ }
+
unsigned N;
if (SlotNumStr.getAsInteger(10, N)) {
Diag(SlotLoc, diag::err_hlsl_unsupported_register_number);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
new file mode 100644
index 0000000000000..81a51fb601e54
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+
+// test semantic validation for register numbers that exceed UINT32_MAX
+
+struct S {
+ RWBuffer<float> A[4];
+ RWBuffer<int> B[10];
+};
+
+// test that S.A carries the register number over the limit and emits the error
+// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+S s : register(u4294967294); // UINT32_MAX - 1
+
+// test the error is also triggered when analyzing S.B
+// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+S s2 : register(u4294967289);
+
+// test a standard resource array
+// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+RWBuffer<float> Buf[10] : register(u4294967294);
+
+// test directly an excessively high register number.
+// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+RWBuffer<float> A : register(u9995294967294);
+
+// test a struct within a cbuffer
+// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+cbuffer MyCB : register(b9995294967294) {
+ float F[4];
+ int I[10];
+};
>From ec47d3bce03bdc87421c4713aa57562c92758220 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 30 Dec 2025 13:57:39 -0800
Subject: [PATCH 02/12] add some more tests
---
...resource_binding_attr_error_uint32_max.hlsl | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
index 81a51fb601e54..70a226337ef6a 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
@@ -7,6 +7,11 @@ struct S {
RWBuffer<int> B[10];
};
+// do some more nesting
+struct S2 {
+ S a[3];
+};
+
// test that S.A carries the register number over the limit and emits the error
// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
S s : register(u4294967294); // UINT32_MAX - 1
@@ -15,9 +20,17 @@ S s : register(u4294967294); // UINT32_MAX - 1
// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
S s2 : register(u4294967289);
+
+// test the error is also triggered when analyzing S2.a[1].B
+// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+S2 s3 : register(u4294967275);
+
+// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+RWBuffer<float> Buf[10][10] : register(u4294967234);
+
// test a standard resource array
// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
-RWBuffer<float> Buf[10] : register(u4294967294);
+RWBuffer<float> Buf2[10] : register(u4294967294);
// test directly an excessively high register number.
// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
@@ -29,3 +42,6 @@ cbuffer MyCB : register(b9995294967294) {
float F[4];
int I[10];
};
+
+// no errors expected, all 100 register numbers are occupied here
+RWBuffer<float> Buf3[10][10] : register(u4294967194);
>From 58472282591f66876fa67552da748c49a50abb8d Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 30 Dec 2025 14:07:09 -0800
Subject: [PATCH 03/12] adjust comment
---
clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
index 70a226337ef6a..d2e089391143c 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
@@ -36,7 +36,7 @@ RWBuffer<float> Buf2[10] : register(u4294967294);
// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
RWBuffer<float> A : register(u9995294967294);
-// test a struct within a cbuffer
+// test a cbuffer directly with an excessively high register number.
// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
cbuffer MyCB : register(b9995294967294) {
float F[4];
>From ed1996e8ed23ce8084ba39cf951ce1cdff31df92 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 31 Dec 2025 00:39:59 -0800
Subject: [PATCH 04/12] address Damyan
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/lib/Sema/SemaHLSL.cpp | 59 +++++++------------
...esource_binding_attr_error_uint32_max.hlsl | 20 ++++---
3 files changed, 35 insertions(+), 46 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 37dfa0892926e..5cbbc7d130c99 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13338,7 +13338,7 @@ def warn_hlsl_register_type_c_packoffset: Warning<"binding type 'c' ignored in b
def warn_hlsl_deprecated_register_type_b: Warning<"binding type 'b' only applies to constant buffers. The 'bool constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError;
def warn_hlsl_deprecated_register_type_i: Warning<"binding type 'i' ignored. The 'integer constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError;
def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
-def err_hlsl_register_number_too_large : Error<"register number should not exceed UINT32_MAX, 4294967295">;
+def err_hlsl_register_number_too_large : Error<"register number should not exceed 4294967295">;
def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
def err_hlsl_space_on_global_constant : Error<"register space cannot be specified on global constants">;
def warn_hlsl_implicit_binding : Warning<"resource has implicit register binding">, InGroup<HLSLImplicitBinding>, DefaultIgnore;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 7760b1f791374..157c6c0f6def7 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2361,27 +2361,15 @@ static bool DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
return ValidateMultipleRegisterAnnotations(S, D, RegType);
}
-bool ExceedsUInt32Max(llvm::StringRef S) {
- constexpr size_t MaxDigits = 10; // UINT32_MAX = 4294967295
- if (S.size() > MaxDigits)
- return true;
-
- if (S.size() < MaxDigits)
- return false;
-
- return S.compare("4294967295") > 0;
-}
-
// return false if the slot count exceeds the limit, true otherwise
-static bool AccumulateHLSLResourceSlots(QualType Ty, llvm::APInt &SlotCount,
- const llvm::APInt &Limit,
- ASTContext &Ctx,
+static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount,
+ const uint64_t &Limit, ASTContext &Ctx,
uint64_t Multiplier = 1) {
Ty = Ty.getCanonicalType();
const Type *T = Ty.getTypePtr();
// Early exit if already overflowed
- if (SlotCount.ugt(Limit))
+ if (SlotCount > Limit)
return false;
// Case 1: array type
@@ -2391,19 +2379,17 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, llvm::APInt &SlotCount,
if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) {
Count = CAT->getSize().getZExtValue();
}
- // TODO: how do we handle non constant resource arrays?
QualType ElemTy = AT->getElementType();
-
return AccumulateHLSLResourceSlots(ElemTy, SlotCount, Limit, Ctx,
Multiplier * Count);
}
// Case 2: resource leaf
if (T->isHLSLResourceRecord()) {
- llvm::APInt Add(SlotCount.getBitWidth(), Multiplier);
- SlotCount += Add;
- return SlotCount.ule(Limit);
+
+ SlotCount += Multiplier;
+ return SlotCount <= Limit;
}
// Case 3: struct / record
@@ -2422,24 +2408,17 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, llvm::APInt &SlotCount,
}
// return true if there is something invalid, false otherwise
-bool ValidateRegisterNumber(StringRef SlotNumStr, Decl *TheDecl,
- ASTContext &Ctx) {
- if (ExceedsUInt32Max(SlotNumStr))
- return true;
-
- llvm::APInt SlotNum;
+static bool ValidateRegisterNumber(StringRef SlotNumStr, Decl *TheDecl,
+ ASTContext &Ctx) {
+ uint64_t SlotNum;
if (SlotNumStr.getAsInteger(10, SlotNum))
return false;
- SlotNum = SlotNum.zext(64);
- // uint32_max isn't 64 bits, but this int should
- // have a 64 bit width in case it is compared to
- // another 64 bit-width value. Assert failure otherwise.
- llvm::APInt Limit(64, UINT32_MAX);
+ uint64_t Limit = UINT32_MAX;
VarDecl *VD = dyn_cast<VarDecl>(TheDecl);
if (VD) {
AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit, Ctx);
- return SlotNum.ugt(Limit);
+ return SlotNum > Limit;
}
// handle the cbuffer case
HLSLBufferDecl *HBD = dyn_cast<HLSLBufferDecl>(TheDecl);
@@ -2447,7 +2426,7 @@ bool ValidateRegisterNumber(StringRef SlotNumStr, Decl *TheDecl,
// resources cannot be put within a cbuffer, so no need
// to analyze the structure since the register number
// won't be pushed any higher.
- return SlotNum.ugt(Limit);
+ return SlotNum > Limit;
}
// we don't expect any other decl type, so fail
@@ -2514,6 +2493,13 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
}
StringRef SlotNumStr = Slot.substr(1);
+ unsigned N;
+ // validate that the stringref has a non-empty number
+ if (SlotNumStr.empty() || !llvm::all_of(SlotNumStr, llvm::isDigit)) {
+ Diag(SlotLoc, diag::err_hlsl_unsupported_register_number);
+ return;
+ }
+
// Validate register number. It should not exceed UINT32_MAX,
// including if the resource type is an array that starts
// before UINT32_MAX, but ends afterwards.
@@ -2522,11 +2508,8 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
return;
}
- unsigned N;
- if (SlotNumStr.getAsInteger(10, N)) {
- Diag(SlotLoc, diag::err_hlsl_unsupported_register_number);
- return;
- }
+ // this shouldn't fail now that it's validated.
+ assert(!SlotNumStr.getAsInteger(10, N));
SlotNum = N;
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
index d2e089391143c..9f2f4b0a7d816 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
@@ -13,31 +13,31 @@ struct S2 {
};
// test that S.A carries the register number over the limit and emits the error
-// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+// expected-error at +1 {{register number should not exceed 4294967295}}
S s : register(u4294967294); // UINT32_MAX - 1
// test the error is also triggered when analyzing S.B
-// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+// expected-error at +1 {{register number should not exceed 4294967295}}
S s2 : register(u4294967289);
// test the error is also triggered when analyzing S2.a[1].B
-// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+// expected-error at +1 {{register number should not exceed 4294967295}}
S2 s3 : register(u4294967275);
-// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+// expected-error at +1 {{register number should not exceed 4294967295}}
RWBuffer<float> Buf[10][10] : register(u4294967234);
// test a standard resource array
-// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+// expected-error at +1 {{register number should not exceed 4294967295}}
RWBuffer<float> Buf2[10] : register(u4294967294);
// test directly an excessively high register number.
-// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+// expected-error at +1 {{register number should not exceed 4294967295}}
RWBuffer<float> A : register(u9995294967294);
// test a cbuffer directly with an excessively high register number.
-// expected-error at +1 {{register number should not exceed UINT32_MAX, 4294967295}}
+// expected-error at +1 {{register number should not exceed 4294967295}}
cbuffer MyCB : register(b9995294967294) {
float F[4];
int I[10];
@@ -45,3 +45,9 @@ cbuffer MyCB : register(b9995294967294) {
// no errors expected, all 100 register numbers are occupied here
RWBuffer<float> Buf3[10][10] : register(u4294967194);
+
+// expected-error at +1 {{register number should be an integer}}
+RWBuffer<float> Buf4[10][10] : register(ud);
+
+// expected-error at +1 {{expected <numeric_constant>}}
+RWBuffer<float> BadBuf[10][10] : register(u);
>From e023e3d4df672bbfa92189b45160a7cced98e1d6 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 31 Dec 2025 10:50:30 -0800
Subject: [PATCH 05/12] try to fix macos issue
---
clang/lib/Sema/SemaHLSL.cpp | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 157c6c0f6def7..207fd7abb827e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2408,7 +2408,7 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount,
}
// return true if there is something invalid, false otherwise
-static bool ValidateRegisterNumber(StringRef SlotNumStr, Decl *TheDecl,
+static bool ValidateRegisterNumber(const StringRef SlotNumStr, Decl *TheDecl,
ASTContext &Ctx) {
uint64_t SlotNum;
if (SlotNumStr.getAsInteger(10, SlotNum))
@@ -2491,7 +2491,7 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
Diag(SlotLoc, diag::warn_hlsl_deprecated_register_type_i);
return;
}
- StringRef SlotNumStr = Slot.substr(1);
+ const StringRef SlotNumStr = Slot.substr(1);
unsigned N;
// validate that the stringref has a non-empty number
@@ -2508,8 +2508,10 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
return;
}
- // this shouldn't fail now that it's validated.
- assert(!SlotNumStr.getAsInteger(10, N));
+ if (SlotNumStr.getAsInteger(10, N)) {
+ Diag(SlotLoc, diag::err_hlsl_unsupported_register_number);
+ return;
+ }
SlotNum = N;
}
>From a7c86da26c8732ec4b0d2aacd563be85df1a3823 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 31 Dec 2025 12:33:58 -0800
Subject: [PATCH 06/12] address Finn
---
clang/lib/Sema/SemaHLSL.cpp | 34 +++++++++++--------
...esource_binding_attr_error_uint32_max.hlsl | 7 ++--
2 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 207fd7abb827e..e30f5b4e5d8dc 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2376,9 +2376,8 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount,
if (const auto *AT = dyn_cast<ArrayType>(T)) {
uint64_t Count = 1;
- if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) {
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(AT))
Count = CAT->getSize().getZExtValue();
- }
QualType ElemTy = AT->getElementType();
return AccumulateHLSLResourceSlots(ElemTy, SlotCount, Limit, Ctx,
@@ -2395,11 +2394,11 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount,
// Case 3: struct / record
if (const auto *RT = dyn_cast<RecordType>(T)) {
const RecordDecl *RD = RT->getDecl();
- for (const FieldDecl *Field : RD->fields()) {
+ for (const FieldDecl *Field : RD->fields())
if (!AccumulateHLSLResourceSlots(Field->getType(), SlotCount, Limit, Ctx,
Multiplier))
return false;
- }
+
return true;
}
@@ -2409,24 +2408,32 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount,
// return true if there is something invalid, false otherwise
static bool ValidateRegisterNumber(const StringRef SlotNumStr, Decl *TheDecl,
- ASTContext &Ctx) {
+ ASTContext &Ctx, unsigned &Result) {
uint64_t SlotNum;
if (SlotNumStr.getAsInteger(10, SlotNum))
return false;
uint64_t Limit = UINT32_MAX;
- VarDecl *VD = dyn_cast<VarDecl>(TheDecl);
- if (VD) {
+ if (VarDecl *VD = dyn_cast<VarDecl>(TheDecl)) {
AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit, Ctx);
- return SlotNum > Limit;
+
+ bool TooHigh = SlotNum > Limit;
+ if (!TooHigh)
+ SlotNumStr.getAsInteger(10, Result);
+ return TooHigh;
}
// handle the cbuffer case
HLSLBufferDecl *HBD = dyn_cast<HLSLBufferDecl>(TheDecl);
if (HBD) {
+ SlotNumStr.getAsInteger(10, Result);
// resources cannot be put within a cbuffer, so no need
// to analyze the structure since the register number
// won't be pushed any higher.
- return SlotNum > Limit;
+ bool TooHigh = SlotNum > Limit;
+ if (!TooHigh)
+ SlotNumStr.getAsInteger(10, Result);
+
+ return TooHigh;
}
// we don't expect any other decl type, so fail
@@ -2494,7 +2501,8 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
const StringRef SlotNumStr = Slot.substr(1);
unsigned N;
- // validate that the stringref has a non-empty number
+
+ // validate that the slot number is a non-empty number
if (SlotNumStr.empty() || !llvm::all_of(SlotNumStr, llvm::isDigit)) {
Diag(SlotLoc, diag::err_hlsl_unsupported_register_number);
return;
@@ -2503,15 +2511,11 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
// Validate register number. It should not exceed UINT32_MAX,
// including if the resource type is an array that starts
// before UINT32_MAX, but ends afterwards.
- if (ValidateRegisterNumber(SlotNumStr, TheDecl, getASTContext())) {
+ if (ValidateRegisterNumber(SlotNumStr, TheDecl, getASTContext(), N)) {
Diag(SlotLoc, diag::err_hlsl_register_number_too_large);
return;
}
- if (SlotNumStr.getAsInteger(10, N)) {
- Diag(SlotLoc, diag::err_hlsl_unsupported_register_number);
- return;
- }
SlotNum = N;
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
index 9f2f4b0a7d816..7ed084aec2cfd 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
@@ -2,6 +2,10 @@
// test semantic validation for register numbers that exceed UINT32_MAX
+
+// expected-error at +1 {{expected <numeric_constant>}}
+RWBuffer<float> BadBuf[10][10] : register(u);
+
struct S {
RWBuffer<float> A[4];
RWBuffer<int> B[10];
@@ -48,6 +52,3 @@ RWBuffer<float> Buf3[10][10] : register(u4294967194);
// expected-error at +1 {{register number should be an integer}}
RWBuffer<float> Buf4[10][10] : register(ud);
-
-// expected-error at +1 {{expected <numeric_constant>}}
-RWBuffer<float> BadBuf[10][10] : register(u);
>From 6ad8d1157fc230d78001a1086283608df5104ad5 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 31 Dec 2025 12:45:53 -0800
Subject: [PATCH 07/12] one quick move
---
clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 3 +++
.../test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl | 4 ----
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index afd7d407c34c2..66b42825c5cf7 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -34,6 +34,9 @@ cbuffer D : register(b 2, space3) {}
// expected-error at +1 {{expected <numeric_constant>}}
cbuffer E : register(u-1) {};
+// expected-error at +1 {{expected <numeric_constant>}}
+cbuffer F : register(u) {};
+
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
static MyTemplatedSRV<float> U : register(u5);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
index 7ed084aec2cfd..6f35aed0f2ccb 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
@@ -2,10 +2,6 @@
// test semantic validation for register numbers that exceed UINT32_MAX
-
-// expected-error at +1 {{expected <numeric_constant>}}
-RWBuffer<float> BadBuf[10][10] : register(u);
-
struct S {
RWBuffer<float> A[4];
RWBuffer<int> B[10];
>From 17b4664618ac4db77816cb80a9927659c58a6976 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 31 Dec 2025 13:29:42 -0800
Subject: [PATCH 08/12] fix off by one error, and add test
---
clang/lib/Sema/SemaHLSL.cpp | 41 +++++++++++--------
...esource_binding_attr_error_uint32_max.hlsl | 12 ++++--
2 files changed, 34 insertions(+), 19 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e30f5b4e5d8dc..db96b49f93a9f 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2386,9 +2386,14 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount,
// Case 2: resource leaf
if (T->isHLSLResourceRecord()) {
+ // Validate highest slot used
+ uint64_t EndSlot = SlotCount + Multiplier - 1;
+ if (EndSlot > Limit)
+ return false;
- SlotCount += Multiplier;
- return SlotCount <= Limit;
+ // Advance SlotCount past the consumed range
+ SlotCount = EndSlot + 1;
+ return true;
}
// Case 3: struct / record
@@ -2411,29 +2416,33 @@ static bool ValidateRegisterNumber(const StringRef SlotNumStr, Decl *TheDecl,
ASTContext &Ctx, unsigned &Result) {
uint64_t SlotNum;
if (SlotNumStr.getAsInteger(10, SlotNum))
- return false;
+ return true;
- uint64_t Limit = UINT32_MAX;
+ const uint64_t Limit = UINT32_MAX;
if (VarDecl *VD = dyn_cast<VarDecl>(TheDecl)) {
- AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit, Ctx);
+ uint64_t BaseSlot = SlotNum;
+
+ if (!AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit, Ctx))
+ return true;
+
+ // After AccumulateHLSLResourceSlots runs, SlotNum is now
+ // the first free slot; last used was SlotNum - 1
+ if (BaseSlot > Limit)
+ return true;
- bool TooHigh = SlotNum > Limit;
- if (!TooHigh)
- SlotNumStr.getAsInteger(10, Result);
- return TooHigh;
+ SlotNumStr.getAsInteger(10, Result);
+ return false;
}
// handle the cbuffer case
- HLSLBufferDecl *HBD = dyn_cast<HLSLBufferDecl>(TheDecl);
- if (HBD) {
- SlotNumStr.getAsInteger(10, Result);
+ if (dyn_cast<HLSLBufferDecl>(TheDecl)) {
// resources cannot be put within a cbuffer, so no need
// to analyze the structure since the register number
// won't be pushed any higher.
- bool TooHigh = SlotNum > Limit;
- if (!TooHigh)
- SlotNumStr.getAsInteger(10, Result);
+ if (SlotNum > Limit)
+ return true;
- return TooHigh;
+ SlotNumStr.getAsInteger(10, Result);
+ return false;
}
// we don't expect any other decl type, so fail
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
index 6f35aed0f2ccb..780d35aa0b346 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
@@ -43,8 +43,14 @@ cbuffer MyCB : register(b9995294967294) {
int I[10];
};
-// no errors expected, all 100 register numbers are occupied here
-RWBuffer<float> Buf3[10][10] : register(u4294967194);
// expected-error at +1 {{register number should be an integer}}
-RWBuffer<float> Buf4[10][10] : register(ud);
+RWBuffer<float> Buf3[10][10] : register(ud);
+
+// this should work
+RWBuffer<float> GoodBuf : register(u4294967295);
+
+// no errors expected, all 100 register numbers are occupied here
+RWBuffer<float> GoodBufArray[10][10] : register(u4294967194);
+
+
>From 79d8366e6910100b6ef3919d9d27ce28d364ba59 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 6 Jan 2026 15:11:06 -0800
Subject: [PATCH 09/12] address tex, fix var names, account for bases, and for
different register classes
---
clang/lib/Sema/SemaHLSL.cpp | 51 +++++++++++++------
...esource_binding_attr_error_uint32_max.hlsl | 34 +++++++++++++
2 files changed, 70 insertions(+), 15 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index db96b49f93a9f..90fa4da9b612e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2362,14 +2362,16 @@ static bool DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
}
// return false if the slot count exceeds the limit, true otherwise
-static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount,
- const uint64_t &Limit, ASTContext &Ctx,
- uint64_t Multiplier = 1) {
+static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &StartSlot,
+ const uint64_t &Limit,
+ const ResourceClass ResClass,
+ ASTContext &Ctx,
+ uint64_t ArrayCount = 1) {
Ty = Ty.getCanonicalType();
const Type *T = Ty.getTypePtr();
// Early exit if already overflowed
- if (SlotCount > Limit)
+ if (StartSlot > Limit)
return false;
// Case 1: array type
@@ -2380,29 +2382,44 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount,
Count = CAT->getSize().getZExtValue();
QualType ElemTy = AT->getElementType();
- return AccumulateHLSLResourceSlots(ElemTy, SlotCount, Limit, Ctx,
- Multiplier * Count);
+ return AccumulateHLSLResourceSlots(ElemTy, StartSlot, Limit, ResClass, Ctx,
+ ArrayCount * Count);
}
// Case 2: resource leaf
- if (T->isHLSLResourceRecord()) {
+ if (auto ResTy = dyn_cast<HLSLAttributedResourceType>(T)) {
+ // First ensure this resource counts towards the corresponding
+ // register type limit.
+ if (ResTy->getAttrs().ResourceClass != ResClass)
+ return true;
+
// Validate highest slot used
- uint64_t EndSlot = SlotCount + Multiplier - 1;
+ uint64_t EndSlot = StartSlot + ArrayCount - 1;
if (EndSlot > Limit)
return false;
// Advance SlotCount past the consumed range
- SlotCount = EndSlot + 1;
+ StartSlot = EndSlot + 1;
return true;
}
// Case 3: struct / record
if (const auto *RT = dyn_cast<RecordType>(T)) {
const RecordDecl *RD = RT->getDecl();
- for (const FieldDecl *Field : RD->fields())
- if (!AccumulateHLSLResourceSlots(Field->getType(), SlotCount, Limit, Ctx,
- Multiplier))
+
+ if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+ for (const CXXBaseSpecifier &Base : CXXRD->bases()) {
+ if (!AccumulateHLSLResourceSlots(Base.getType(), StartSlot, Limit,
+ ResClass, Ctx, ArrayCount))
+ return false;
+ }
+ }
+
+ for (const FieldDecl *Field : RD->fields()) {
+ if (!AccumulateHLSLResourceSlots(Field->getType(), StartSlot, Limit,
+ ResClass, Ctx, ArrayCount))
return false;
+ }
return true;
}
@@ -2413,16 +2430,19 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount,
// return true if there is something invalid, false otherwise
static bool ValidateRegisterNumber(const StringRef SlotNumStr, Decl *TheDecl,
- ASTContext &Ctx, unsigned &Result) {
+ ASTContext &Ctx, RegisterType RegTy,
+ unsigned &Result) {
uint64_t SlotNum;
if (SlotNumStr.getAsInteger(10, SlotNum))
return true;
const uint64_t Limit = UINT32_MAX;
+
if (VarDecl *VD = dyn_cast<VarDecl>(TheDecl)) {
uint64_t BaseSlot = SlotNum;
- if (!AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit, Ctx))
+ if (!AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit,
+ getResourceClass(RegTy), Ctx))
return true;
// After AccumulateHLSLResourceSlots runs, SlotNum is now
@@ -2520,7 +2540,8 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
// Validate register number. It should not exceed UINT32_MAX,
// including if the resource type is an array that starts
// before UINT32_MAX, but ends afterwards.
- if (ValidateRegisterNumber(SlotNumStr, TheDecl, getASTContext(), N)) {
+ if (ValidateRegisterNumber(SlotNumStr, TheDecl, getASTContext(), RegType,
+ N)) {
Diag(SlotLoc, diag::err_hlsl_register_number_too_large);
return;
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
index 780d35aa0b346..61c21c3916afb 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
@@ -43,6 +43,40 @@ cbuffer MyCB : register(b9995294967294) {
int I[10];
};
+struct MySRV {
+ __hlsl_resource_t [[hlsl::resource_class(SRV)]] x;
+};
+
+struct MySampler {
+ __hlsl_resource_t [[hlsl::resource_class(Sampler)]] x;
+};
+
+struct MyUAV {
+ __hlsl_resource_t [[hlsl::resource_class(UAV)]] x;
+};
+
+// test that different resource classes don't contribute to the
+// maximum limit
+struct MyResources {
+ MySRV TheSRV[10]; // t
+ MySampler TheSampler[20]; // s
+ MyUAV TheUAV[40]; // u
+}
+
+// no failures here, since only the SRV contributes to the count,
+// and the count + 10 does not exceed uint32 max.
+MyResources M : register(t4294967284);
+
+// three failures, since each of the three resources exceed the limit
+// expected-error at +3 {{register number should not exceed 4294967295}}
+// expected-error at +2 {{register number should not exceed 4294967295}}
+// expected-error at +1 {{register number should not exceed 4294967295}}
+MyResources M2 : register(t4294967294) : register(s4294967294) : register(u4294967294);
+
+// one failure here, just because the final UAV exceeds the limit.
+// expected-error at +1 {{register number should not exceed 4294967295}}
+MyResources M3 : register(t2) : register(s3) : register(u4294967280);
+
// expected-error at +1 {{register number should be an integer}}
RWBuffer<float> Buf3[10][10] : register(ud);
>From 6cea29f90a742da2dfbc132569c2e93f577082df Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 6 Jan 2026 16:45:00 -0800
Subject: [PATCH 10/12] fix bug with c and i register types
---
clang/lib/Sema/SemaHLSL.cpp | 7 +++++++
.../SemaHLSL/resource_binding_attr_error_uint32_max.hlsl | 2 +-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 90fa4da9b612e..ff836d50534c6 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2437,6 +2437,13 @@ static bool ValidateRegisterNumber(const StringRef SlotNumStr, Decl *TheDecl,
return true;
const uint64_t Limit = UINT32_MAX;
+ if (SlotNum > Limit)
+ return true;
+
+ // after verifying the number doesn't exceed uint32max, we don't need
+ // to look further into c or i register types
+ if (RegTy == RegisterType::C || RegTy == RegisterType::I)
+ return false;
if (VarDecl *VD = dyn_cast<VarDecl>(TheDecl)) {
uint64_t BaseSlot = SlotNum;
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
index 61c21c3916afb..464eb12669a9c 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl
@@ -61,7 +61,7 @@ struct MyResources {
MySRV TheSRV[10]; // t
MySampler TheSampler[20]; // s
MyUAV TheUAV[40]; // u
-}
+};
// no failures here, since only the SRV contributes to the count,
// and the count + 10 does not exceed uint32 max.
>From c0a937cac5c7bbacb2f88d5f853f46a1896b891d Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 6 Jan 2026 23:36:06 -0800
Subject: [PATCH 11/12] return a result for c and i register types
---
clang/lib/Sema/SemaHLSL.cpp | 6 ++++--
clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 6 ++++++
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index ff836d50534c6..0b6f1d8075985 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2442,8 +2442,10 @@ static bool ValidateRegisterNumber(const StringRef SlotNumStr, Decl *TheDecl,
// after verifying the number doesn't exceed uint32max, we don't need
// to look further into c or i register types
- if (RegTy == RegisterType::C || RegTy == RegisterType::I)
+ if (RegTy == RegisterType::C || RegTy == RegisterType::I) {
+ SlotNumStr.getAsInteger(10, Result);
return false;
+ }
if (VarDecl *VD = dyn_cast<VarDecl>(TheDecl)) {
uint64_t BaseSlot = SlotNum;
@@ -2460,7 +2462,7 @@ static bool ValidateRegisterNumber(const StringRef SlotNumStr, Decl *TheDecl,
SlotNumStr.getAsInteger(10, Result);
return false;
}
- // handle the cbuffer case
+ // handle the cbuffer/tbuffer case
if (dyn_cast<HLSLBufferDecl>(TheDecl)) {
// resources cannot be put within a cbuffer, so no need
// to analyze the structure since the register number
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 66b42825c5cf7..49cee68347414 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -37,6 +37,12 @@ cbuffer E : register(u-1) {};
// expected-error at +1 {{expected <numeric_constant>}}
cbuffer F : register(u) {};
+// expected-error at +1 {{binding type 'u' only applies to UAV resources}}
+cbuffer G : register(u13) {};
+
+// expected-error at +1 {{binding type 'c' only applies to numeric variables in the global scope}}
+cbuffer H : register(c0) {};
+
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
static MyTemplatedSRV<float> U : register(u5);
>From 3e809db5c0e881bd7df16209208a3af96d9b3f77 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 7 Jan 2026 13:34:27 -0800
Subject: [PATCH 12/12] address Tex
---
.../SemaHLSL/resource_binding_attr_error_resource.hlsl | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
index ea43e27b5b5ac..f81bdd690f8ee 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
@@ -25,7 +25,6 @@ struct MyCBuffer {
__hlsl_resource_t [[hlsl::resource_class(CBuffer)]] x;
};
-
// expected-error at +1 {{binding type 'i' ignored. The 'integer constant' binding type is no longer supported}}
MySRV invalid : register(i2);
@@ -41,6 +40,13 @@ MyTemplatedSRV<int> c : register(b2);
// expected-error at +1 {{binding type 's' only applies to sampler state}}
MyUAV d : register(s2, space1);
+// expected-error at +1 {{binding type 't' only applies to SRV resources}}
+cbuffer cbuf : register(t0) {}
+
+// expected-error at +1 {{binding type 'b' only applies to constant buffer resources}}
+tbuffer tbuf : register(b0) {};
+
+
// empty binding prefix cases:
// expected-error at +1 {{expected identifier}}
MyTemplatedSRV<int> e: register();
More information about the cfe-commits
mailing list