https://github.com/inbelic updated https://github.com/llvm/llvm-project/pull/182101
>From bf87d3cbdc7118754c85db1022561e3241e80486 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Tue, 17 Feb 2026 11:54:04 -0800 Subject: [PATCH 1/4] [SemaHLSL] Emit a warning if local resource refer to non-unique global resources --- .../clang/Basic/DiagnosticSemaKinds.td | 3 + clang/include/clang/Sema/SemaHLSL.h | 18 +++++ clang/lib/Sema/SemaHLSL.cpp | 68 ++++++++++++++++++- .../SemaHLSL/local_resource_bindings.hlsl | 56 +++++++++++++++ .../local_resource_bindings_errs.hlsl | 49 +++++++++++++ 5 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 clang/test/SemaHLSL/local_resource_bindings.hlsl create mode 100644 clang/test/SemaHLSL/local_resource_bindings_errs.hlsl diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 68016ec4d58a3..7c1958457a881 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13585,6 +13585,9 @@ def err_hlsl_incomplete_resource_array_in_function_param: Error< "incomplete resource array in a function parameter">; def err_hlsl_assign_to_global_resource: Error< "assignment to global resource variable %0 is not allowed">; +def warn_hlsl_assigning_local_resource_is_not_unique + : Warning<"assignment of %0 to local resource %1 is not to the same " + "unique global resource">; def err_hlsl_push_constant_unique : Error<"cannot have more than one push constant block">; diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 020a4dc44ee7f..86748f15b9e76 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -132,6 +132,8 @@ class SemaHLSL : public SemaBase { bool ActOnUninitializedVarDecl(VarDecl *D); void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU); void CheckEntryPoint(FunctionDecl *FD); + + // Return true if everything is ok; returns false if there was an error. bool CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr, SourceLocation Loc); @@ -234,6 +236,12 @@ class SemaHLSL : public SemaBase { // List of all resource bindings ResourceBindings Bindings; + // Map of local resource variables to their assigned global resources. + // + // The binding can be a nullptr, in which case, the variable has yet to be + // initialized or assigned to. + llvm::DenseMap<const VarDecl *, const DeclBindingInfo *> Assigns; + // Global declaration collected for the $Globals default constant // buffer which will be created at the end of the translation unit. llvm::SmallVector<Decl *> DefaultCBufferDecls; @@ -313,6 +321,16 @@ class SemaHLSL : public SemaBase { bool initGlobalResourceDecl(VarDecl *VD); bool initGlobalResourceArrayDecl(VarDecl *VD); + + // Infer a common global binding info for an Expr + // + // Returns std::nullopt if the expr refers to non-unique global bindings. + // Returns nullptr if it refer to any global binding, otherwise it returns + // a reference to the global binding info. + std::optional<const DeclBindingInfo *> inferGlobalBinding(Expr *E); + + // Returns true if no diagnostic is reported + bool trackLocalResource(VarDecl *VDecl, Expr *E); }; } // namespace clang diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 911dba40d3bde..73e5935d79bb7 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -4686,7 +4686,67 @@ bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) { return false; } -// Return true if everything is ok; returns false if there was an error. +std::optional<const DeclBindingInfo *> SemaHLSL::inferGlobalBinding(Expr *E) { + if (auto *Ternary = dyn_cast<ConditionalOperator>(E)) { + auto TrueInfo = inferGlobalBinding(Ternary->getTrueExpr()); + auto FalseInfo = inferGlobalBinding(Ternary->getFalseExpr()); + if (!TrueInfo || !FalseInfo) + return std::nullopt; + if (*TrueInfo != *FalseInfo) + return std::nullopt; + return TrueInfo; + } + + if (auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) + E = ASE->getBase()->IgnoreParenImpCasts(); + + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) + if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { + const Type *Ty = VD->getType()->getUnqualifiedDesugaredType(); + if (Ty->isArrayType()) + Ty = Ty->getArrayElementTypeNoTypeQual(); + + if (const auto *AttrResType = + HLSLAttributedResourceType::findHandleTypeOnResource(Ty)) { + ResourceClass RC = AttrResType->getAttrs().ResourceClass; + return Bindings.getDeclBindingInfo(VD, RC); + } + } + + return nullptr; +} + +bool SemaHLSL::trackLocalResource(VarDecl *VD, Expr *E) { + std::optional<const DeclBindingInfo *> ExprBinding = inferGlobalBinding(E); + if (!ExprBinding) { + SemaRef.Diag(E->getBeginLoc(), + diag::warn_hlsl_assigning_local_resource_is_not_unique) + << E << VD; + return false; + } + + if (*ExprBinding == nullptr) + return true; // No binding could be inferred to track, return without error + + auto PrevBinding = Assigns.find(VD); + if (PrevBinding == Assigns.end()) { + // No previous binding recorded, simply record the new assignment + Assigns.insert({VD, *ExprBinding}); + return true; + } + + // Otherwise, warn if the assignment implies different resource bindings + if (*ExprBinding != PrevBinding->second) { + SemaRef.Diag(E->getBeginLoc(), + diag::warn_hlsl_assigning_local_resource_is_not_unique) + << E << VD; + SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD; + return false; + } + + return true; +} + bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr, SourceLocation Loc) { assert((LHSExpr->getType()->isHLSLResourceRecord() || @@ -4709,6 +4769,8 @@ bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD; return false; } + + trackLocalResource(VD, RHSExpr); } } return true; @@ -5315,6 +5377,10 @@ QualType SemaHLSL::checkMatrixComponent(Sema &S, QualType baseType, } bool SemaHLSL::handleInitialization(VarDecl *VDecl, Expr *&Init) { + // If initializing a local resource, track the resource binding it is using + if (VDecl->getType()->isHLSLResourceRecord() && !VDecl->hasGlobalStorage()) + trackLocalResource(VDecl, Init); + const HLSLVkConstantIdAttr *ConstIdAttr = VDecl->getAttr<HLSLVkConstantIdAttr>(); if (!ConstIdAttr) diff --git a/clang/test/SemaHLSL/local_resource_bindings.hlsl b/clang/test/SemaHLSL/local_resource_bindings.hlsl new file mode 100644 index 0000000000000..3059452b49ef0 --- /dev/null +++ b/clang/test/SemaHLSL/local_resource_bindings.hlsl @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -verify %s + +// expected-no-diagnostics + +RWBuffer<int> In : register(u0); +RWStructuredBuffer<int> Out0 : register(u1); +RWStructuredBuffer<int> Out1 : register(u2); +RWStructuredBuffer<int> OutArr[]; + +cbuffer c { + bool cond; +}; + +void no_initial_assignment(int idx) { + RWStructuredBuffer<int> Out; + if (cond) { + Out = Out1; + } + Out[idx] = In[idx]; +} + +void same_assignment(int idx) { + RWStructuredBuffer<int> Out = Out1; + if (cond) { + Out = Out1; + } + Out[idx] = In[idx]; +} + +void conditional_initialization_with_index(int idx) { + RWStructuredBuffer<int> Out = cond ? OutArr[0] : OutArr[1]; + Out[idx] = In[idx]; +} + +void conditional_assignment_with_index(int idx) { + RWStructuredBuffer<int> Out; + if (cond) { + Out = OutArr[0]; + } else { + Out = OutArr[1]; + } + Out[idx] = In[idx]; +} + +void reassignment(int idx) { + RWStructuredBuffer<int> Out = Out0; + if (cond) { + Out = Out0; + } + Out[idx] = In[idx]; +} + +void conditional_result_in_same(int idx) { + RWStructuredBuffer<int> Out = cond ? Out0 : Out0; + Out[idx] = In[idx]; +} diff --git a/clang/test/SemaHLSL/local_resource_bindings_errs.hlsl b/clang/test/SemaHLSL/local_resource_bindings_errs.hlsl new file mode 100644 index 0000000000000..3be8268ec0505 --- /dev/null +++ b/clang/test/SemaHLSL/local_resource_bindings_errs.hlsl @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -verify %s + +RWBuffer<int> In : register(u0); +RWStructuredBuffer<int> Out0 : register(u1); +RWStructuredBuffer<int> Out1 : register(u2); +RWStructuredBuffer<int> OutArr[]; + +cbuffer c { + bool cond; +}; + +void conditional_initialization(int idx) { + // expected-warning@+1 {{assignment of 'cond ? Out0 : Out1' to local resource 'Out' is not to the same unique global resource}} + RWStructuredBuffer<int> Out = cond ? Out0 : Out1; + Out[idx] = In[idx]; +} + +void branched_assignment(int idx) { + RWStructuredBuffer<int> Out = Out0; // expected-note {{variable 'Out' is declared here}} + if (cond) { + // expected-warning@+1 {{assignment of 'Out1' to local resource 'Out' is not to the same unique global resource}} + Out = Out1; + } + Out[idx] = In[idx]; +} + +void branched_assignment_with_array(int idx) { + RWStructuredBuffer<int> Out = Out0; // expected-note {{variable 'Out' is declared here}} + if (cond) { + // expected-warning@+1 {{assignment of 'OutArr[0]' to local resource 'Out' is not to the same unique global resource}} + Out = OutArr[0]; + } + Out[idx] = In[idx]; +} + +void conditional_assignment(int idx) { + RWStructuredBuffer<int> Out; + // expected-warning@+1 {{assignment of 'cond ? Out0 : Out1' to local resource 'Out' is not to the same unique global resource}} + Out = cond ? Out0 : Out1; + Out[idx] = In[idx]; +} + +static RWStructuredBuffer<int> StaticOut; + +void static_conditional_assignment(int idx) { + // expected-warning@+1 {{assignment of 'cond ? Out0 : Out1' to local resource 'StaticOut' is not to the same unique global resource}} + StaticOut = cond ? Out0 : Out1; + StaticOut[idx] = In[idx]; +} >From 5287da122980a3884664a71f2281c51c1ebe1983 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Thu, 19 Feb 2026 14:59:45 -0800 Subject: [PATCH 2/4] review: add assignment to uninitialized test - no warnings generated --- clang/test/SemaHLSL/local_resource_bindings.hlsl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clang/test/SemaHLSL/local_resource_bindings.hlsl b/clang/test/SemaHLSL/local_resource_bindings.hlsl index 3059452b49ef0..a7f28018516a6 100644 --- a/clang/test/SemaHLSL/local_resource_bindings.hlsl +++ b/clang/test/SemaHLSL/local_resource_bindings.hlsl @@ -19,6 +19,12 @@ void no_initial_assignment(int idx) { Out[idx] = In[idx]; } +void assignment_to_uninitialized(int idx) { + RWStructuredBuffer<int> Out; + Out = Out; + Out[idx] = In[idx]; +} + void same_assignment(int idx) { RWStructuredBuffer<int> Out = Out1; if (cond) { >From c09420c976f23a83b0b3bc0adcfeffe56846d725 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Thu, 19 Feb 2026 15:01:02 -0800 Subject: [PATCH 3/4] review: remove unused return value --- clang/include/clang/Sema/SemaHLSL.h | 3 +-- clang/lib/Sema/SemaHLSL.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 86748f15b9e76..a6a38531ac284 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -329,8 +329,7 @@ class SemaHLSL : public SemaBase { // a reference to the global binding info. std::optional<const DeclBindingInfo *> inferGlobalBinding(Expr *E); - // Returns true if no diagnostic is reported - bool trackLocalResource(VarDecl *VDecl, Expr *E); + void trackLocalResource(VarDecl *VDecl, Expr *E); }; } // namespace clang diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 73e5935d79bb7..804ea70aaddce 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -4716,23 +4716,23 @@ std::optional<const DeclBindingInfo *> SemaHLSL::inferGlobalBinding(Expr *E) { return nullptr; } -bool SemaHLSL::trackLocalResource(VarDecl *VD, Expr *E) { +void SemaHLSL::trackLocalResource(VarDecl *VD, Expr *E) { std::optional<const DeclBindingInfo *> ExprBinding = inferGlobalBinding(E); if (!ExprBinding) { SemaRef.Diag(E->getBeginLoc(), diag::warn_hlsl_assigning_local_resource_is_not_unique) << E << VD; - return false; + return; // Expr use multiple resources } if (*ExprBinding == nullptr) - return true; // No binding could be inferred to track, return without error + return; // No binding could be inferred to track, return without error auto PrevBinding = Assigns.find(VD); if (PrevBinding == Assigns.end()) { // No previous binding recorded, simply record the new assignment Assigns.insert({VD, *ExprBinding}); - return true; + return; } // Otherwise, warn if the assignment implies different resource bindings @@ -4741,10 +4741,10 @@ bool SemaHLSL::trackLocalResource(VarDecl *VD, Expr *E) { diag::warn_hlsl_assigning_local_resource_is_not_unique) << E << VD; SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD; - return false; + return; } - return true; + return; } bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, >From 5a09a3694ce0e44a3d6383fdfcac2b5deacc215a Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Mon, 2 Mar 2026 16:55:41 +0000 Subject: [PATCH 4/4] self-review: fix conversion errors --- .../SemaHLSL/local_resource_bindings.hlsl | 38 +++++++++---------- .../local_resource_bindings_errs.hlsl | 30 +++++++-------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/clang/test/SemaHLSL/local_resource_bindings.hlsl b/clang/test/SemaHLSL/local_resource_bindings.hlsl index a7f28018516a6..58e7bf47d2d0d 100644 --- a/clang/test/SemaHLSL/local_resource_bindings.hlsl +++ b/clang/test/SemaHLSL/local_resource_bindings.hlsl @@ -1,45 +1,45 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -verify %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -finclude-default-header -verify %s // expected-no-diagnostics -RWBuffer<int> In : register(u0); -RWStructuredBuffer<int> Out0 : register(u1); -RWStructuredBuffer<int> Out1 : register(u2); -RWStructuredBuffer<int> OutArr[]; +RWBuffer<uint> In : register(u0); +RWStructuredBuffer<uint> Out0 : register(u1); +RWStructuredBuffer<uint> Out1 : register(u2); +RWStructuredBuffer<uint> OutArr[]; cbuffer c { bool cond; }; -void no_initial_assignment(int idx) { - RWStructuredBuffer<int> Out; +void no_initial_assignment(uint idx) { + RWStructuredBuffer<uint> Out; if (cond) { Out = Out1; } Out[idx] = In[idx]; } -void assignment_to_uninitialized(int idx) { - RWStructuredBuffer<int> Out; +void assignment_to_uninitialized(uint idx) { + RWStructuredBuffer<uint> Out; Out = Out; Out[idx] = In[idx]; } -void same_assignment(int idx) { - RWStructuredBuffer<int> Out = Out1; +void same_assignment(uint idx) { + RWStructuredBuffer<uint> Out = Out1; if (cond) { Out = Out1; } Out[idx] = In[idx]; } -void conditional_initialization_with_index(int idx) { - RWStructuredBuffer<int> Out = cond ? OutArr[0] : OutArr[1]; +void conditional_initialization_with_index(uint idx) { + RWStructuredBuffer<uint> Out = cond ? OutArr[0] : OutArr[1]; Out[idx] = In[idx]; } -void conditional_assignment_with_index(int idx) { - RWStructuredBuffer<int> Out; +void conditional_assignment_with_index(uint idx) { + RWStructuredBuffer<uint> Out; if (cond) { Out = OutArr[0]; } else { @@ -48,15 +48,15 @@ void conditional_assignment_with_index(int idx) { Out[idx] = In[idx]; } -void reassignment(int idx) { - RWStructuredBuffer<int> Out = Out0; +void reassignment(uint idx) { + RWStructuredBuffer<uint> Out = Out0; if (cond) { Out = Out0; } Out[idx] = In[idx]; } -void conditional_result_in_same(int idx) { - RWStructuredBuffer<int> Out = cond ? Out0 : Out0; +void conditional_result_in_same(uint idx) { + RWStructuredBuffer<uint> Out = cond ? Out0 : Out0; Out[idx] = In[idx]; } diff --git a/clang/test/SemaHLSL/local_resource_bindings_errs.hlsl b/clang/test/SemaHLSL/local_resource_bindings_errs.hlsl index 3be8268ec0505..7404e444390b1 100644 --- a/clang/test/SemaHLSL/local_resource_bindings_errs.hlsl +++ b/clang/test/SemaHLSL/local_resource_bindings_errs.hlsl @@ -1,22 +1,22 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -verify %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -finclude-default-header -verify %s -RWBuffer<int> In : register(u0); -RWStructuredBuffer<int> Out0 : register(u1); -RWStructuredBuffer<int> Out1 : register(u2); -RWStructuredBuffer<int> OutArr[]; +RWBuffer<uint> In : register(u0); +RWStructuredBuffer<uint> Out0 : register(u1); +RWStructuredBuffer<uint> Out1 : register(u2); +RWStructuredBuffer<uint> OutArr[]; cbuffer c { bool cond; }; -void conditional_initialization(int idx) { +void conditional_initialization(uint idx) { // expected-warning@+1 {{assignment of 'cond ? Out0 : Out1' to local resource 'Out' is not to the same unique global resource}} - RWStructuredBuffer<int> Out = cond ? Out0 : Out1; + RWStructuredBuffer<uint> Out = cond ? Out0 : Out1; Out[idx] = In[idx]; } -void branched_assignment(int idx) { - RWStructuredBuffer<int> Out = Out0; // expected-note {{variable 'Out' is declared here}} +void branched_assignment(uint idx) { + RWStructuredBuffer<uint> Out = Out0; // expected-note {{variable 'Out' is declared here}} if (cond) { // expected-warning@+1 {{assignment of 'Out1' to local resource 'Out' is not to the same unique global resource}} Out = Out1; @@ -24,8 +24,8 @@ void branched_assignment(int idx) { Out[idx] = In[idx]; } -void branched_assignment_with_array(int idx) { - RWStructuredBuffer<int> Out = Out0; // expected-note {{variable 'Out' is declared here}} +void branched_assignment_with_array(uint idx) { + RWStructuredBuffer<uint> Out = Out0; // expected-note {{variable 'Out' is declared here}} if (cond) { // expected-warning@+1 {{assignment of 'OutArr[0]' to local resource 'Out' is not to the same unique global resource}} Out = OutArr[0]; @@ -33,16 +33,16 @@ void branched_assignment_with_array(int idx) { Out[idx] = In[idx]; } -void conditional_assignment(int idx) { - RWStructuredBuffer<int> Out; +void conditional_assignment(uint idx) { + RWStructuredBuffer<uint> Out; // expected-warning@+1 {{assignment of 'cond ? Out0 : Out1' to local resource 'Out' is not to the same unique global resource}} Out = cond ? Out0 : Out1; Out[idx] = In[idx]; } -static RWStructuredBuffer<int> StaticOut; +static RWStructuredBuffer<uint> StaticOut; -void static_conditional_assignment(int idx) { +void static_conditional_assignment(uint idx) { // expected-warning@+1 {{assignment of 'cond ? Out0 : Out1' to local resource 'StaticOut' is not to the same unique global resource}} StaticOut = cond ? Out0 : Out1; StaticOut[idx] = In[idx]; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
