https://github.com/inbelic updated https://github.com/llvm/llvm-project/pull/176793
>From 007a060105b094abd17802246cd16835934d391b Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Mon, 19 Jan 2026 09:31:01 -0800 Subject: [PATCH 1/5] [SemaHLSL] Verify assignment of local resources --- .../clang/Basic/DiagnosticSemaKinds.td | 3 + clang/include/clang/Sema/SemaHLSL.h | 15 ++++ clang/lib/Sema/SemaHLSL.cpp | 76 +++++++++++++++++-- .../test/SemaHLSL/local_resource_binding.hlsl | 56 ++++++++++++++ .../SemaHLSL/local_resource_binding_errs.hlsl | 41 ++++++++++ 5 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 clang/test/SemaHLSL/local_resource_binding.hlsl create mode 100644 clang/test/SemaHLSL/local_resource_binding_errs.hlsl diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index eb7a608f798b8..87673e944fc0d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13431,6 +13431,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 err_hlsl_assigning_local_resource_is_not_unique + : Error<"assignment to local resource %0 is not to 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 99d8ed137b0c2..48daebbf61c90 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -132,6 +132,14 @@ class SemaHLSL : public SemaBase { bool ActOnUninitializedVarDecl(VarDecl *D); void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU); void CheckEntryPoint(FunctionDecl *FD); + + // Return an info if the expr has common global binding info; returns + // std::nullopt if the expr refers to non-unique global bindings, returns + // nullptr if it isn't refer to any global binding, otherwise it returns + // a reference to the global binding info. + std::optional<const DeclBindingInfo *> GetGlobalBinding(Expr *E); + + // Return true if everything is ok; returns false if there was an error. bool CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr, SourceLocation Loc); @@ -230,6 +238,13 @@ class SemaHLSL : public SemaBase { // List of all resource bindings ResourceBindings Bindings; + // Map of local resource variables to their used global resource. + // + // The Binding can be a nullptr, in which case, the variable has not be + // initialized or assigned to yet. + llvm::DenseMap<const VarDecl *, const DeclBindingInfo *> + LocalResourceBindings; + // Global declaration collected for the $Globals default constant // buffer which will be created at the end of the translation unit. llvm::SmallVector<Decl *> DefaultCBufferDecls; diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index f15b274a65a53..cd00167ceb72f 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -4398,7 +4398,35 @@ 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::GetGlobalBinding(Expr *E) { + if (auto *Ternary = dyn_cast<ConditionalOperator>(E)) { + auto TrueInfo = GetGlobalBinding(Ternary->getTrueExpr()); + auto FalseInfo = GetGlobalBinding(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::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr, SourceLocation Loc) { assert((LHSExpr->getType()->isHLSLResourceRecord() || @@ -4412,14 +4440,37 @@ bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, while (auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) E = ASE->getBase()->IgnoreParenImpCasts(); + auto RHSBinding = GetGlobalBinding(RHSExpr); + if (!RHSBinding) { + SemaRef.Diag(Loc, diag::err_hlsl_assigning_local_resource_is_not_unique) + << RHSExpr; + return false; + } + // Report error if LHS is a non-static resource declared at a global scope. if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) { if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { - if (VD->hasGlobalStorage() && VD->getStorageClass() != SC_Static) { - // assignment to global resource is not allowed - SemaRef.Diag(Loc, diag::err_hlsl_assign_to_global_resource) << VD; - SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD; - return false; + if (VD->getStorageClass() != SC_Static) { + if (VD->hasGlobalStorage()) { + // assignment to global resource is not allowed + SemaRef.Diag(Loc, diag::err_hlsl_assign_to_global_resource) << VD; + SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD; + return false; + } + + // Ensure assignment to a non-static local resource does not conflict + // with previous assignments to global resources + const DeclBindingInfo *LHSBinding = LocalResourceBindings[VD]; + if (!LHSBinding) { + // occurs when local resource was instantiated without an expression + LocalResourceBindings[VD] = *RHSBinding; + } else if (LHSBinding != *RHSBinding) { + SemaRef.Diag(Loc, + diag::err_hlsl_assigning_local_resource_is_not_unique) + << RHSExpr; + SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD; + return false; + } } } } @@ -4797,6 +4848,19 @@ bool SemaHLSL::transformInitList(const InitializedEntity &Entity, } bool SemaHLSL::handleInitialization(VarDecl *VDecl, Expr *&Init) { + // If initializing a local resource, register the binding it is using + if (VDecl->getType()->isHLSLResourceRecord() && !VDecl->hasGlobalStorage()) + if (auto *InitExpr = Init) { + if (auto Binding = GetGlobalBinding(InitExpr)) { + LocalResourceBindings.insert({VDecl, *Binding}); + } else { + SemaRef.Diag(Init->getBeginLoc(), + diag::err_hlsl_assigning_local_resource_is_not_unique) + << Init; + return false; + } + } + const HLSLVkConstantIdAttr *ConstIdAttr = VDecl->getAttr<HLSLVkConstantIdAttr>(); if (!ConstIdAttr) diff --git a/clang/test/SemaHLSL/local_resource_binding.hlsl b/clang/test/SemaHLSL/local_resource_binding.hlsl new file mode 100644 index 0000000000000..3059452b49ef0 --- /dev/null +++ b/clang/test/SemaHLSL/local_resource_binding.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_binding_errs.hlsl b/clang/test/SemaHLSL/local_resource_binding_errs.hlsl new file mode 100644 index 0000000000000..163d3c4493ca2 --- /dev/null +++ b/clang/test/SemaHLSL/local_resource_binding_errs.hlsl @@ -0,0 +1,41 @@ +// 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-error@+1 {{assignment to local resource 'cond ? Out0 : Out1' is not to 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-error@+1 {{assignment to local resource 'Out1' is not to 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-error@+1 {{assignment to local resource 'OutArr[0]' is not to same unique global resource}} + Out = OutArr[0]; + } + Out[idx] = In[idx]; +} + +void conditional_assignment(int idx) { + RWStructuredBuffer<int> Out; + // expected-error@+1 {{assignment to local resource 'cond ? Out0 : Out1' is not to same unique global resource}} + Out = cond ? Out0 : Out1; + Out[idx] = In[idx]; +} >From 4c4fcf9936aa61a1766dd6bab147c7137baf8292 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Mon, 19 Jan 2026 11:24:18 -0800 Subject: [PATCH 2/5] review: fix-up comments Co-authored-by: Deric C. <[email protected]> --- clang/include/clang/Sema/SemaHLSL.h | 4 ++-- clang/lib/Sema/SemaHLSL.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 48daebbf61c90..d12dcc596989e 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -240,8 +240,8 @@ class SemaHLSL : public SemaBase { // Map of local resource variables to their used global resource. // - // The Binding can be a nullptr, in which case, the variable has not be - // initialized or assigned to yet. + // 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 *> LocalResourceBindings; diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index cd00167ceb72f..c41e48128cef9 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -4452,7 +4452,7 @@ bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { if (VD->getStorageClass() != SC_Static) { if (VD->hasGlobalStorage()) { - // assignment to global resource is not allowed + // Assignment to a global resource is not allowed. SemaRef.Diag(Loc, diag::err_hlsl_assign_to_global_resource) << VD; SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD; return false; @@ -4462,7 +4462,7 @@ bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, // with previous assignments to global resources const DeclBindingInfo *LHSBinding = LocalResourceBindings[VD]; if (!LHSBinding) { - // occurs when local resource was instantiated without an expression + // LHSBinding is a nullptr when the local resource is instantiated without an expression. LocalResourceBindings[VD] = *RHSBinding; } else if (LHSBinding != *RHSBinding) { SemaRef.Diag(Loc, @@ -4848,7 +4848,7 @@ bool SemaHLSL::transformInitList(const InitializedEntity &Entity, } bool SemaHLSL::handleInitialization(VarDecl *VDecl, Expr *&Init) { - // If initializing a local resource, register the binding it is using + // If initializing a local resource, register the binding it is using. if (VDecl->getType()->isHLSLResourceRecord() && !VDecl->hasGlobalStorage()) if (auto *InitExpr = Init) { if (auto Binding = GetGlobalBinding(InitExpr)) { >From 5eca470288d496b2d0f64e4db6b061aed862f0e4 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Mon, 19 Jan 2026 11:26:32 -0800 Subject: [PATCH 3/5] review: fix-up diagnostic message --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/test/SemaHLSL/local_resource_binding_errs.hlsl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 87673e944fc0d..5e206d84ab7c0 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13432,7 +13432,7 @@ def err_hlsl_incomplete_resource_array_in_function_param: Error< def err_hlsl_assign_to_global_resource: Error< "assignment to global resource variable %0 is not allowed">; def err_hlsl_assigning_local_resource_is_not_unique - : Error<"assignment to local resource %0 is not to same unique global " + : Error<"assignment to local resource %0 is not to the same unique global " "resource">; def err_hlsl_push_constant_unique diff --git a/clang/test/SemaHLSL/local_resource_binding_errs.hlsl b/clang/test/SemaHLSL/local_resource_binding_errs.hlsl index 163d3c4493ca2..807886d77bc7a 100644 --- a/clang/test/SemaHLSL/local_resource_binding_errs.hlsl +++ b/clang/test/SemaHLSL/local_resource_binding_errs.hlsl @@ -10,7 +10,7 @@ cbuffer c { }; void conditional_initialization(int idx) { - // expected-error@+1 {{assignment to local resource 'cond ? Out0 : Out1' is not to same unique global resource}} + // expected-error@+1 {{assignment to local resource 'cond ? Out0 : Out1' is not to the same unique global resource}} RWStructuredBuffer<int> Out = cond ? Out0 : Out1; Out[idx] = In[idx]; } @@ -18,7 +18,7 @@ void conditional_initialization(int idx) { void branched_assignment(int idx) { RWStructuredBuffer<int> Out = Out0; // expected-note {{variable 'Out' is declared here}} if (cond) { - // expected-error@+1 {{assignment to local resource 'Out1' is not to same unique global resource}} + // expected-error@+1 {{assignment to local resource 'Out1' is not to same the unique global resource}} Out = Out1; } Out[idx] = In[idx]; @@ -27,7 +27,7 @@ void branched_assignment(int idx) { void branched_assignment_with_array(int idx) { RWStructuredBuffer<int> Out = Out0; // expected-note {{variable 'Out' is declared here}} if (cond) { - // expected-error@+1 {{assignment to local resource 'OutArr[0]' is not to same unique global resource}} + // expected-error@+1 {{assignment to local resource 'OutArr[0]' is not to the same unique global resource}} Out = OutArr[0]; } Out[idx] = In[idx]; @@ -35,7 +35,7 @@ void branched_assignment_with_array(int idx) { void conditional_assignment(int idx) { RWStructuredBuffer<int> Out; - // expected-error@+1 {{assignment to local resource 'cond ? Out0 : Out1' is not to same unique global resource}} + // expected-error@+1 {{assignment to local resource 'cond ? Out0 : Out1' is not to the same unique global resource}} Out = cond ? Out0 : Out1; Out[idx] = In[idx]; } >From 8d0f91924d97fcddf72c76781b5f3af137294533 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Mon, 19 Jan 2026 11:26:47 -0800 Subject: [PATCH 4/5] clang-format --- clang/lib/Sema/SemaHLSL.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index c41e48128cef9..021745ba26430 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -4462,7 +4462,8 @@ bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, // with previous assignments to global resources const DeclBindingInfo *LHSBinding = LocalResourceBindings[VD]; if (!LHSBinding) { - // LHSBinding is a nullptr when the local resource is instantiated without an expression. + // LHSBinding is a nullptr when the local resource is instantiated + // without an expression. LocalResourceBindings[VD] = *RHSBinding; } else if (LHSBinding != *RHSBinding) { SemaRef.Diag(Loc, >From 5c7f60d6c75f0954459a118991f73203ab430e06 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Mon, 19 Jan 2026 11:54:03 -0800 Subject: [PATCH 5/5] fix typo, whoops --- clang/test/SemaHLSL/local_resource_binding_errs.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/SemaHLSL/local_resource_binding_errs.hlsl b/clang/test/SemaHLSL/local_resource_binding_errs.hlsl index 807886d77bc7a..ad9a46b865965 100644 --- a/clang/test/SemaHLSL/local_resource_binding_errs.hlsl +++ b/clang/test/SemaHLSL/local_resource_binding_errs.hlsl @@ -18,7 +18,7 @@ void conditional_initialization(int idx) { void branched_assignment(int idx) { RWStructuredBuffer<int> Out = Out0; // expected-note {{variable 'Out' is declared here}} if (cond) { - // expected-error@+1 {{assignment to local resource 'Out1' is not to same the unique global resource}} + // expected-error@+1 {{assignment to local resource 'Out1' is not to the same unique global resource}} Out = Out1; } Out[idx] = In[idx]; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
