Skip to content

[SemaHLSL] Warn when a local resource is re-assigned to non-unique global resource#182101

Merged
inbelic merged 5 commits into
llvm:mainfrom
inbelic:inbelic/local-res-warns
Mar 2, 2026
Merged

[SemaHLSL] Warn when a local resource is re-assigned to non-unique global resource#182101
inbelic merged 5 commits into
llvm:mainfrom
inbelic:inbelic/local-res-warns

Conversation

@inbelic

@inbelic inbelic commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

Generate a warning whenever a local resource is (re-)assigned such that it is not guaranteed to map to a single unique global resource.

An error is not generated during sema because simple DCE or constant folding might resolve the assignment to be to a unique global resource. Instead, an error will be reported when trying to resolve the resource access in the dxil-resource-access pass, implemented here.

Resolves #179303.

@inbelic inbelic marked this pull request as ready for review February 18, 2026 19:21
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" HLSL HLSL Language Support labels Feb 18, 2026
@llvmbot

llvmbot commented Feb 18, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-clang

Author: Finn Plummer (inbelic)

Changes

Generate a warning whenever a local resource is (re-)assigned such that it is not guaranteed to map to a single unique global resource.

An error is not generated during sema because simple DCE or constant folding might resolve the assignment to be to a unique global resource. Instead, an error will be reported when trying to resolve the resource access in the dxil-resource-access pass, implemented here.

Resolves #179303.


Full diff: https://github.com/llvm/llvm-project/pull/182101.diff

5 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3)
  • (modified) clang/include/clang/Sema/SemaHLSL.h (+18)
  • (modified) clang/lib/Sema/SemaHLSL.cpp (+67-1)
  • (added) clang/test/SemaHLSL/local_resource_bindings.hlsl (+56)
  • (added) clang/test/SemaHLSL/local_resource_bindings_errs.hlsl (+49)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 85a023435ba23..3d69eced857b6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13513,6 +13513,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 4271056ad43cc..0e4ce381eae34 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4685,7 +4685,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() ||
@@ -4708,6 +4768,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;
@@ -5312,6 +5374,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];
+}

@llvmbot

llvmbot commented Feb 18, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-hlsl

Author: Finn Plummer (inbelic)

Changes

Generate a warning whenever a local resource is (re-)assigned such that it is not guaranteed to map to a single unique global resource.

An error is not generated during sema because simple DCE or constant folding might resolve the assignment to be to a unique global resource. Instead, an error will be reported when trying to resolve the resource access in the dxil-resource-access pass, implemented here.

Resolves #179303.


Full diff: https://github.com/llvm/llvm-project/pull/182101.diff

5 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3)
  • (modified) clang/include/clang/Sema/SemaHLSL.h (+18)
  • (modified) clang/lib/Sema/SemaHLSL.cpp (+67-1)
  • (added) clang/test/SemaHLSL/local_resource_bindings.hlsl (+56)
  • (added) clang/test/SemaHLSL/local_resource_bindings_errs.hlsl (+49)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 85a023435ba23..3d69eced857b6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13513,6 +13513,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 4271056ad43cc..0e4ce381eae34 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4685,7 +4685,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() ||
@@ -4708,6 +4768,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;
@@ -5312,6 +5374,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];
+}

@github-actions

github-actions Bot commented Feb 18, 2026

Copy link
Copy Markdown

🪟 Windows x64 Test Results

  • 54634 tests passed
  • 2325 tests skipped

✅ The build succeeded and all tests passed.

@github-actions

github-actions Bot commented Feb 18, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 114570 tests passed
  • 4577 tests skipped

✅ The build succeeded and all tests passed.

Comment thread clang/lib/Sema/SemaHLSL.cpp Outdated
return nullptr;
}

bool SemaHLSL::trackLocalResource(VarDecl *VD, Expr *E) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you define the return value of this function? You also don't seem to use the return value.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah thanks for pointing that out.
Still, I would make the argument this should return void if it's unused.
Unless @inbelic has a future PR that will use this value?

Comment thread clang/test/SemaHLSL/local_resource_bindings.hlsl
@inbelic inbelic force-pushed the inbelic/local-res-warns branch from 605af71 to ee2dc97 Compare February 19, 2026 23:02
Comment thread clang/test/SemaHLSL/local_resource_bindings.hlsl Outdated
@inbelic inbelic force-pushed the inbelic/local-res-warns branch from 0c8e458 to 5a09a36 Compare March 2, 2026 16:56
@inbelic inbelic merged commit ed08557 into llvm:main Mar 2, 2026
11 checks passed
@inbelic inbelic deleted the inbelic/local-res-warns branch March 3, 2026 22:43
sahas3 pushed a commit to sahas3/llvm-project that referenced this pull request Mar 4, 2026
…obal resource (llvm#182101)

Generate a warning whenever a local resource is (re-)assigned such that
it is not guaranteed to map to a single unique global resource.

An error is not generated during sema because simple DCE or constant
folding might resolve the assignment to be to a unique global resource.
Instead, an error will be reported when trying to resolve the resource
access in the `dxil-resource-access` pass, implemented
[here](llvm#182106).

Resolves llvm#179303.
sujianIBM pushed a commit to sujianIBM/llvm-project that referenced this pull request Mar 5, 2026
…obal resource (llvm#182101)

Generate a warning whenever a local resource is (re-)assigned such that
it is not guaranteed to map to a single unique global resource.

An error is not generated during sema because simple DCE or constant
folding might resolve the assignment to be to a unique global resource.
Instead, an error will be reported when trying to resolve the resource
access in the `dxil-resource-access` pass, implemented
[here](llvm#182106).

Resolves llvm#179303.
@inbelic

inbelic commented Mar 5, 2026

Copy link
Copy Markdown
Contributor Author

@s-perron From the meeting: this pr is used to create warnings against cases that will cause code generation to create phi or selects between different resources/handles. There was an initial attempt to generate errors instead of just warnings, however, DXC does not generate an error for erroneous cases if they are removed with simple dce or by constant propogation which was not reasonably tracked in SemaHLSL. So actual errors are reported later in dxil-resource-access as part of here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category HLSL HLSL Language Support

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HLSL] Resolve local resource access to non-unique global resources

4 participants