https://github.com/Icohedron created 
https://github.com/llvm/llvm-project/pull/212846

This PR adds availability attributes to texture sample methods that require 
implicit derivatives (fixes https://github.com/llvm/llvm-project/issues/198885)

To make these availability attributes actually get checked, 
`DiagnoseHLSLAvailability::HandleFunctionOrMethodRef` in `SemaHLSL.cpp` has 
been changed to check availability attributes regardless of whether or not a 
function has a body/definition (fixes 
https://github.com/llvm/llvm-project/issues/212842).

Assisted by: Claude Opus 5

>From 64503dc8e74a91f94e1a6adde4bb4a9e793009db Mon Sep 17 00:00:00 2001
From: Deric Cheung <[email protected]>
Date: Wed, 29 Jul 2026 11:32:09 -0700
Subject: [PATCH 1/2] Check availability attributes regardless of if the
 function has a definition

---
 clang/lib/Sema/SemaHLSL.cpp                     | 17 ++++++-----------
 .../avail-diag-default-compute.hlsl             |  8 ++++++++
 2 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e26b18552d362..0af1cce24fbde 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2987,19 +2987,14 @@ void 
DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD,
   assert((isa<DeclRefExpr>(RefExpr) || isa<MemberExpr>(RefExpr)) &&
          "expected DeclRefExpr or MemberExpr");
 
-  // has a definition -> add to stack to be scanned
-  const FunctionDecl *FDWithBody = nullptr;
-  if (FD->hasBody(FDWithBody)) {
-    if (!WasAlreadyScannedInCurrentStage(FDWithBody))
-      DeclsToScan.push_back(FDWithBody);
-    return;
-  }
-
-  // no body -> diagnose availability
-  const AvailabilityAttr *AA = FindAvailabilityAttr(FD);
-  if (AA)
+  if (const AvailabilityAttr *AA = FindAvailabilityAttr(FD))
     CheckDeclAvailability(
         FD, AA, SourceRange(RefExpr->getBeginLoc(), RefExpr->getEndLoc()));
+
+  // has a definition -> add to stack to be scanned
+  const FunctionDecl *FDWithBody = nullptr;
+  if (FD->hasBody(FDWithBody) && !WasAlreadyScannedInCurrentStage(FDWithBody))
+    DeclsToScan.push_back(FDWithBody);
 }
 
 void DiagnoseHLSLAvailability::RunOnTranslationUnit(
diff --git a/clang/test/SemaHLSL/Availability/avail-diag-default-compute.hlsl 
b/clang/test/SemaHLSL/Availability/avail-diag-default-compute.hlsl
index 1424fe63242ae..75b5f160a3cb1 100644
--- a/clang/test/SemaHLSL/Availability/avail-diag-default-compute.hlsl
+++ b/clang/test/SemaHLSL/Availability/avail-diag-default-compute.hlsl
@@ -15,6 +15,11 @@ __attribute__((availability(shadermodel, introduced = 5.0, 
environment = pixel))
 __attribute__((availability(shadermodel, introduced = 6.5, environment = 
mesh)))
 float fz(float); // #fz
 
+// A function that has a definition is diagnosed based on its availability
+// attribute as well; having a body does not make it available everywhere.
+__attribute__((availability(shadermodel, introduced = 6.5)))
+float fdef(float f) { return f; } // #fdef
+
 float also_alive(float f) {
   // expected-error@#also_alive_fx_call {{'fx' is only available on Shader 
Model 6.5 or newer}}
   // expected-note@#fx {{'fx' has been marked as being introduced in Shader 
Model 6.5 here, but the deployment target is Shader Model 6.0}}
@@ -115,4 +120,7 @@ void main() {
   float c = C.makeF();
   float d = test((float)1.0);
   float e = test((half)1.0);
+  // expected-error@#main_fdef_call {{'fdef' is only available on Shader Model 
6.5 or newer}}
+  // expected-note@#fdef {{'fdef' has been marked as being introduced in 
Shader Model 6.5 here, but the deployment target is Shader Model 6.0}}
+  float g = fdef(f); // #main_fdef_call
 }

>From e1470028ad9c4040c3d5472629a49a0b99e37001 Mon Sep 17 00:00:00 2001
From: Deric Cheung <[email protected]>
Date: Wed, 29 Jul 2026 11:53:26 -0700
Subject: [PATCH 2/2] Add availability attributes to samplers that require
 implicit derivatives

---
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 61 +++++++++++-
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h   |  1 +
 .../Textures-derivative-availability.hlsl     | 98 +++++++++++++++++++
 3 files changed, 155 insertions(+), 5 deletions(-)
 create mode 100644 
clang/test/SemaHLSL/Resources/Textures-derivative-availability.hlsl

diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index 65b2da923d461..337e8795528c8 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -96,6 +96,32 @@ QualType getInoutParameterType(ASTContext &AST, QualType Ty) 
{
   return Ty;
 }
 
+// Attaches availability attributes to a method that requires implicit
+// derivatives. Implicit derivatives are always available in pixel
+// shaders. Shader Model 6.6 made derivatives available in compute, mesh and
+// amplification shaders as well. All other shader stages do not support
+// derivatives.
+void addDerivativeAvailabilityAttrs(ASTContext &AST, FunctionDecl *FD) {
+  struct DerivativeShaderStage {
+    StringRef Environment;
+    VersionTuple Introduced;
+  };
+  const DerivativeShaderStage Stages[] = {
+      {"pixel", VersionTuple(6, 0)},
+      {"compute", VersionTuple(6, 6)},
+      {"mesh", VersionTuple(6, 6)},
+      {"amplification", VersionTuple(6, 6)},
+  };
+
+  const IdentifierInfo *Platform = &AST.Idents.get("shadermodel");
+  for (const DerivativeShaderStage &Stage : Stages)
+    FD->addAttr(AvailabilityAttr::CreateImplicit(
+        AST, Platform, Stage.Introduced, /*Deprecated=*/VersionTuple(),
+        /*Obsoleted=*/VersionTuple(), /*Unavailable=*/false, /*Message=*/"",
+        /*Strict=*/false, /*Replacement=*/"", Sema::AP_Explicit,
+        &AST.Idents.get(Stage.Environment), /*InferredAttr=*/nullptr));
+}
+
 } // namespace
 
 // Builder for template arguments of builtin types. Used internally
@@ -1664,6 +1690,19 @@ 
BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() {
   return *this;
 }
 
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addDerivativeAvailability(StringRef MethodName) {
+  ASTContext &AST = Record->getASTContext();
+  DeclarationName Name(&AST.Idents.get(MethodName, 
tok::TokenKind::identifier));
+  for (NamedDecl *D : Record->lookup(Name)) {
+    if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
+      D = FTD->getTemplatedDecl();
+    if (auto *MD = dyn_cast<CXXMethodDecl>(D))
+      addDerivativeAvailabilityAttrs(AST, MD);
+  }
+  return *this;
+}
+
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim, bool IsArray) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
@@ -1701,7 +1740,7 @@ 
BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim, bool IsArray) {
       .finalize();
 
   // T Sample(SamplerState s, float2 location, int2 offset, float clamp)
-  return BuiltinTypeMethodBuilder(*this, "Sample", ReturnType)
+  BuiltinTypeMethodBuilder(*this, "Sample", ReturnType)
       .addParam("Sampler", SamplerStateType)
       .addParam("Location", CoordTy)
       .addParam("Offset", OffsetTy)
@@ -1711,6 +1750,9 @@ 
BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim, bool IsArray) {
                    PH::LastStmt, PH::_1, PH::_2, PH::_3)
       .returnValue(PH::LastStmt)
       .finalize();
+
+  // Sample uses implicit derivatives to calculate the mip level.
+  return addDerivativeAvailability("Sample");
 }
 
 BuiltinTypeDeclBuilder &
@@ -1754,7 +1796,7 @@ 
BuiltinTypeDeclBuilder::addSampleBiasMethods(ResourceDimension Dim,
 
   // T SampleBias(SamplerState s, float2 location, float bias, int2 offset,
   // float clamp)
-  return BuiltinTypeMethodBuilder(*this, "SampleBias", ReturnType)
+  BuiltinTypeMethodBuilder(*this, "SampleBias", ReturnType)
       .addParam("Sampler", SamplerStateType)
       .addParam("Location", CoordTy)
       .addParam("Bias", FloatTy)
@@ -1765,6 +1807,9 @@ 
BuiltinTypeDeclBuilder::addSampleBiasMethods(ResourceDimension Dim,
                    PH::Handle, PH::LastStmt, PH::_1, PH::_2, PH::_3, PH::_4)
       .returnValue(PH::LastStmt)
       .finalize();
+
+  // SampleBias uses implicit derivatives to calculate the mip level.
+  return addDerivativeAvailability("SampleBias");
 }
 
 BuiltinTypeDeclBuilder &
@@ -1909,7 +1954,7 @@ 
BuiltinTypeDeclBuilder::addSampleCmpMethods(ResourceDimension Dim,
 
   // T SampleCmp(SamplerComparisonState s, float2 location, float 
compare_value,
   // int2 offset, float clamp)
-  return BuiltinTypeMethodBuilder(*this, "SampleCmp", ReturnType)
+  BuiltinTypeMethodBuilder(*this, "SampleCmp", ReturnType)
       .addParam("Sampler", SamplerComparisonStateType)
       .addParam("Location", CoordTy)
       .addParam("CompareValue", FloatTy)
@@ -1920,6 +1965,9 @@ 
BuiltinTypeDeclBuilder::addSampleCmpMethods(ResourceDimension Dim,
                    PH::LastStmt, PH::_1, PH::_2, PH::_3, PH::_4)
       .returnValue(PH::LastStmt)
       .finalize();
+
+  // SampleCmp uses implicit derivatives to calculate the mip level.
+  return addDerivativeAvailability("SampleCmp");
 }
 
 BuiltinTypeDeclBuilder &
@@ -2033,14 +2081,17 @@ 
BuiltinTypeDeclBuilder::addCalculateLodMethods(ResourceDimension Dim) {
       .finalize();
 
   // float CalculateLevelOfDetailUnclamped(SamplerState s, float2 location)
-  return BuiltinTypeMethodBuilder(*this, "CalculateLevelOfDetailUnclamped",
-                                  ReturnType)
+  BuiltinTypeMethodBuilder(*this, "CalculateLevelOfDetailUnclamped", 
ReturnType)
       .addParam("Sampler", SamplerStateType)
       .addParam("Location", LocationTy)
       .accessHandleFieldOnResource(PH::_0)
       .callBuiltin("__builtin_hlsl_resource_calculate_lod_unclamped",
                    ReturnType, PH::Handle, PH::LastStmt, PH::_1)
       .finalize();
+
+  // Both methods use implicit derivatives to calculate the level of detail.
+  addDerivativeAvailability("CalculateLevelOfDetail");
+  return addDerivativeAvailability("CalculateLevelOfDetailUnclamped");
 }
 
 QualType BuiltinTypeDeclBuilder::getGatherReturnType() {
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
index e809ef264198c..afb336045b1f7 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -168,6 +168,7 @@ class BuiltinTypeDeclBuilder {
                          QualType ElementTy,
                          AccessSpecifier Access = AccessSpecifier::AS_private);
   QualType getGatherReturnType();
+  BuiltinTypeDeclBuilder &addDerivativeAvailability(StringRef MethodName);
   FieldDecl *getResourceHandleField() const;
   FieldDecl *getResourceCounterHandleField() const;
   QualType getFirstTemplateTypeParam();
diff --git 
a/clang/test/SemaHLSL/Resources/Textures-derivative-availability.hlsl 
b/clang/test/SemaHLSL/Resources/Textures-derivative-availability.hlsl
new file mode 100644
index 0000000000000..9d6ce30309de9
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/Textures-derivative-availability.hlsl
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library 
-finclude-default-header \
+// RUN:   -fsyntax-only -verify=expected,sm66 -DTEXTURE=Texture2D 
-DCOORD_TYPE=float2 %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.5-library 
-finclude-default-header \
+// RUN:   -fsyntax-only -verify=expected,sm65 -DTEXTURE=Texture2D 
-DCOORD_TYPE=float2 %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library 
-finclude-default-header \
+// RUN:   -fsyntax-only -verify=expected,sm66 -DTEXTURE=Texture2DArray 
-DCOORD_TYPE=float3 %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.5-library 
-finclude-default-header \
+// RUN:   -fsyntax-only -verify=expected,sm65 -DTEXTURE=Texture2DArray 
-DCOORD_TYPE=float3 %s
+
+// Texture methods that rely on implicit derivatives are available in pixel
+// shaders since Shader Model 6.0 and in compute, mesh and amplification
+// shaders since Shader Model 6.6. They are not available in any other shader
+// stage.
+
+TEXTURE<float4> tex;
+SamplerState samp;
+SamplerComparisonState cmpSamp;
+
+// Derivatives are always available in pixel shaders; no diagnostics expected.
+[shader("pixel")]
+void PixelEntry() {
+  COORD_TYPE loc = (COORD_TYPE)0;
+  float2 lodLoc = float2(0, 0);
+
+  tex.Sample(samp, loc);
+  tex.SampleBias(samp, loc, 0.5);
+  tex.SampleCmp(cmpSamp, loc, 0.5);
+  tex.CalculateLevelOfDetail(samp, lodLoc);
+  tex.CalculateLevelOfDetailUnclamped(samp, lodLoc);
+}
+
+// Derivatives are available in compute shaders only since Shader Model 6.6.
+[shader("compute")]
+[numthreads(1, 1, 1)]
+void ComputeEntry() {
+  COORD_TYPE loc = (COORD_TYPE)0;
+  float2 lodLoc = float2(0, 0);
+
+  // sm65-error@+2 {{'Sample' is only available in compute environment on 
Shader Model 6.6 or newer}}
+  // sm65-note@* {{'Sample' has been marked as being introduced in Shader 
Model 6.6 in compute environment here, but the deployment target is Shader 
Model 6.5 compute environment}}
+  tex.Sample(samp, loc);
+
+  // sm65-error@+2 {{'SampleBias' is only available in compute environment on 
Shader Model 6.6 or newer}}
+  // sm65-note@* {{'SampleBias' has been marked as being introduced in Shader 
Model 6.6 in compute environment here}}
+  tex.SampleBias(samp, loc, 0.5);
+
+  // sm65-error@+2 {{'SampleCmp' is only available in compute environment on 
Shader Model 6.6 or newer}}
+  // sm65-note@* {{'SampleCmp' has been marked as being introduced in Shader 
Model 6.6 in compute environment here}}
+  tex.SampleCmp(cmpSamp, loc, 0.5);
+
+  // sm65-error@+2 {{'CalculateLevelOfDetail' is only available in compute 
environment on Shader Model 6.6 or newer}}
+  // sm65-note@* {{'CalculateLevelOfDetail' has been marked as being 
introduced in Shader Model 6.6 in compute environment here}}
+  tex.CalculateLevelOfDetail(samp, lodLoc);
+
+  // sm65-error@+2 {{'CalculateLevelOfDetailUnclamped' is only available in 
compute environment on Shader Model 6.6 or newer}}
+  // sm65-note@* {{'CalculateLevelOfDetailUnclamped' has been marked as being 
introduced in Shader Model 6.6 in compute environment here}}
+  tex.CalculateLevelOfDetailUnclamped(samp, lodLoc);
+}
+
+// Vertex shaders do not support derivatives in any shader model.
+[shader("vertex")]
+void VertexEntry() {
+  COORD_TYPE loc = (COORD_TYPE)0;
+  float2 lodLoc = float2(0, 0);
+
+  // expected-error@+2 {{'Sample' is unavailable}}
+  // expected-note@* {{'Sample' has been marked as being introduced in Shader 
Model}}
+  tex.Sample(samp, loc);
+
+  // expected-error@+2 {{'SampleBias' is unavailable}}
+  // expected-note@* {{'SampleBias' has been marked as being introduced in 
Shader Model}}
+  tex.SampleBias(samp, loc, 0.5);
+
+  // expected-error@+2 {{'SampleCmp' is unavailable}}
+  // expected-note@* {{'SampleCmp' has been marked as being introduced in 
Shader Model}}
+  tex.SampleCmp(cmpSamp, loc, 0.5);
+
+  // expected-error@+2 {{'CalculateLevelOfDetail' is unavailable}}
+  // expected-note@* {{'CalculateLevelOfDetail' has been marked as being 
introduced in Shader Model}}
+  tex.CalculateLevelOfDetail(samp, lodLoc);
+
+  // expected-error@+2 {{'CalculateLevelOfDetailUnclamped' is unavailable}}
+  // expected-note@* {{'CalculateLevelOfDetailUnclamped' has been marked as 
being introduced in Shader Model}}
+  tex.CalculateLevelOfDetailUnclamped(samp, lodLoc);
+}
+
+// Methods that take an explicit LOD or explicit gradients do not require
+// derivatives and are available in all shader stages; no diagnostics expected.
+[shader("vertex")]
+void ExplicitLodVertexEntry() {
+  COORD_TYPE loc = (COORD_TYPE)0;
+  float2 grad = float2(0, 0);
+
+  tex.SampleLevel(samp, loc, 0);
+  tex.SampleGrad(samp, loc, grad, grad);
+  tex.SampleCmpLevelZero(cmpSamp, loc, 0.5);
+  tex.Gather(samp, loc);
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to