[clang] eb06b47 - [HLSL] Diagnose dynamic indexing of struct arrays for resource access (#187132)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 8 10:00:01 PDT 2026
Author: Helena Kotas
Date: 2026-04-08T09:59:56-07:00
New Revision: eb06b4796c2a53c177516dd45ca63f871fc311bb
URL: https://github.com/llvm/llvm-project/commit/eb06b4796c2a53c177516dd45ca63f871fc311bb
DIFF: https://github.com/llvm/llvm-project/commit/eb06b4796c2a53c177516dd45ca63f871fc311bb.diff
LOG: [HLSL] Diagnose dynamic indexing of struct arrays for resource access (#187132)
Dynamic indexing of structs arrays for resource access is not supported. This change implements the diagnostic for this.
Fixes #187131
Added:
clang/test/SemaHLSL/Resources/resources-in-struct-array-error.hlsl
Modified:
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/SemaHLSL.h
clang/lib/Sema/SemaExprMember.cpp
clang/lib/Sema/SemaHLSL.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1ad4ff7098080..6d2fae551566f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13712,6 +13712,9 @@ def err_hlsl_samplecmp_requires_float
def err_hlsl_gathercmp_invalid_component
: Error<"gatherCmp%select{Red|Green|Blue|Alpha}0 operations on the Vulkan target are not supported; only GatherCmp and GatherCmpRed are allowed">;
+def err_hlsl_resource_member_array_access_not_constant
+ : Error<"index for struct array inside cbuffer that contains resources must be a constant integer expression">;
+
// Layout randomization diagnostics.
def err_non_designated_init_used : Error<
"a randomized struct can only be initialized with a designated initializer">;
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 6f62fbc635d9c..edb7a8202a80b 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -131,6 +131,7 @@ class SemaHLSL : public SemaBase {
void ActOnVariableDeclarator(VarDecl *VD);
bool ActOnUninitializedVarDecl(VarDecl *D);
void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU);
+ bool ActOnResourceMemberAccessExpr(MemberExpr *ME);
void CheckEntryPoint(FunctionDecl *FD);
// Return true if everything is ok; returns false if there was an error.
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index ff7c844f66123..a4504410cae28 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -12,6 +12,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/TypeBase.h"
@@ -1737,9 +1738,18 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base,
Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc,
FirstQualifierInScope, NameInfo, TemplateArgs, S, &ExtraArgs);
- if (!Res.isInvalid() && isa<MemberExpr>(Res.get()))
- CheckMemberAccessOfNoDeref(cast<MemberExpr>(Res.get()));
+ if (!Res.isInvalid()) {
+ if (MemberExpr *ME = dyn_cast<MemberExpr>(Res.get())) {
+ CheckMemberAccessOfNoDeref(ME);
+ if (getLangOpts().HLSL) {
+ QualType Ty = Res.get()->getType();
+ if (Ty->isHLSLResourceRecord() || Ty->isHLSLResourceRecordArray())
+ if (!HLSL().ActOnResourceMemberAccessExpr(ME))
+ Res = ExprError();
+ }
+ }
+ }
return Res;
}
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 5d7cec49c2b19..a943303149931 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3068,6 +3068,50 @@ void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
diagnoseAvailabilityViolations(TU);
}
+// For resource member access through a global struct array, verify that the
+// array index selecting the struct element is a constant integer expression.
+// Returns false if the member expression is invalid.
+bool SemaHLSL::ActOnResourceMemberAccessExpr(MemberExpr *ME) {
+ assert((ME->getType()->isHLSLResourceRecord() ||
+ ME->getType()->isHLSLResourceRecordArray()) &&
+ "expected member expr to have resource record type or array of them");
+
+ // Walk the AST from MemberExpr to the VarDecl of the parent struct instance
+ // and take note of any non-constant array indexing along the way. If the
+ // VarDecl we find is a global variable, report error if there was any
+ // non-constant array index in the resource member access along the way.
+ const Expr *NonConstIndexExpr = nullptr;
+ const Expr *E = ME->getBase();
+ while (E) {
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+ if (!NonConstIndexExpr)
+ return true;
+
+ const VarDecl *VD = cast<VarDecl>(DRE->getDecl());
+ if (!VD->hasGlobalStorage())
+ return true;
+
+ SemaRef.Diag(NonConstIndexExpr->getExprLoc(),
+ diag::err_hlsl_resource_member_array_access_not_constant);
+ return false;
+ }
+
+ if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
+ const Expr *IdxExpr = ASE->getIdx();
+ if (!IdxExpr->isIntegerConstantExpr(SemaRef.getASTContext()))
+ NonConstIndexExpr = IdxExpr;
+ E = ASE->getBase();
+ } else if (const auto *SubME = dyn_cast<MemberExpr>(E)) {
+ E = SubME->getBase();
+ } else if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
+ E = ICE->getSubExpr();
+ } else {
+ llvm_unreachable("unexpected expr type in resource member access");
+ }
+ }
+ return true;
+}
+
void SemaHLSL::diagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
// Skip running the diagnostics scan if the diagnostic mode is
// strict (-fhlsl-strict-availability) and the target shader stage is known
diff --git a/clang/test/SemaHLSL/Resources/resources-in-struct-array-error.hlsl b/clang/test/SemaHLSL/Resources/resources-in-struct-array-error.hlsl
new file mode 100644
index 0000000000000..e98a73812b773
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/resources-in-struct-array-error.hlsl
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -finclude-default-header -verify %s
+
+struct A {
+ RWBuffer<float> Buf;
+ RWBuffer<float> ManyBufs[5];
+};
+
+A array[10] : register(u10);
+
+[numthreads(4,1,1)]
+void main(uint GI : SV_GroupThreadID) {
+
+ // expected-error at +1 {{index for struct array inside cbuffer that contains resources must be a constant integer expression}}
+ array[GI].Buf[0] = 1.0f;
+
+ array[2].Buf[GI] = 2.0f; // ok
+
+ // expected-error at +1 {{index for struct array inside cbuffer that contains resources must be a constant integer expression}}
+ array[GI].ManyBufs[3][0] = 3.0f;
+
+ array[1].ManyBufs[GI][0] = 4.0f; // ok
+
+ array[1+1].ManyBufs[GI][0] = 4.0f; // ok
+
+ int x = 3;
+ array[1].ManyBufs[x][0] = 4.0f; // ok
+}
More information about the cfe-commits
mailing list