[clang] [HLSL] Strict Availability Diagnostics (PR #93860)

Helena Kotas via cfe-commits cfe-commits at lists.llvm.org
Tue Jun 11 17:18:45 PDT 2024


https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/93860

>From 532f10f77c862f6d429366f0d6903773da8fa79b Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 30 May 2024 11:40:27 -0700
Subject: [PATCH 1/3] Implement HLSL strict diagnostic mode

---
 clang/include/clang/Basic/Attr.td             |   5 -
 clang/include/clang/Basic/LangOptions.def     |   2 +
 clang/include/clang/Driver/Options.td         |   9 ++
 clang/lib/AST/DeclBase.cpp                    |   2 +-
 clang/lib/Sema/SemaAvailability.cpp           |  76 ++++++----
 clang/lib/Sema/SemaHLSL.cpp                   |  54 +++++--
 .../avail-diag-strict-compute.hlsl            | 129 ++++++++++++++++
 .../Availability/avail-diag-strict-lib.hlsl   | 142 ++++++++++++++++++
 8 files changed, 369 insertions(+), 50 deletions(-)
 create mode 100644 clang/test/SemaHLSL/Availability/avail-diag-strict-compute.hlsl
 create mode 100644 clang/test/SemaHLSL/Availability/avail-diag-strict-lib.hlsl

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 2665b7353ca4a..6ac2e3b50ee54 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1060,11 +1060,6 @@ static llvm::StringRef canonicalizePlatformName(llvm::StringRef Platform) {
              .Case("ShaderModel", "shadermodel")
              .Default(Platform);
 }
-static llvm::StringRef getPrettyEnviromentName(llvm::Triple::EnvironmentType EnvironmentType) {
-  if (EnvironmentType >= llvm::Triple::Pixel && EnvironmentType <= llvm::Triple::Amplification)
-    return llvm::Triple::getEnvironmentTypeName(EnvironmentType);
-  return "";
-}
 static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environment) {
     return llvm::StringSwitch<llvm::Triple::EnvironmentType>(Environment)
              .Case("pixel", llvm::Triple::Pixel)
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 4061451b2150a..443b49d0e2779 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -276,6 +276,8 @@ LANGOPT(RenderScript      , 1, 0, "RenderScript")
 
 LANGOPT(HLSL, 1, 0, "HLSL")
 ENUM_LANGOPT(HLSLVersion, HLSLLangStd, 16, HLSL_Unset, "HLSL Version")
+LANGOPT(HLSLStrictAvailability, 1, 0,
+        "Strict availability diagnostic mode for HLSL built-in functions.")
 
 LANGOPT(CUDAIsDevice      , 1, 0, "compiling for CUDA device")
 LANGOPT(CUDAAllowVariadicFunctions, 1, 0, "allowing variadic functions in CUDA device code")
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 1637a114fcce1..cb380450d0425 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -196,6 +196,10 @@ def m_Group : OptionGroup<"<m group>">, Group<CompileOnly_Group>,
               DocName<"Target-dependent compilation options">,
               Visibility<[ClangOption, CLOption]>;
 
+def hlsl_Group : OptionGroup<"<HLSL group>">, Group<f_Group>,
+                   DocName<"HLSL options">,
+                   Visibility<[ClangOption]>;
+
 // Feature groups - these take command line options that correspond directly to
 // target specific features and can be translated directly from command line
 // options.
@@ -7863,6 +7867,11 @@ def finclude_default_header : Flag<["-"], "finclude-default-header">,
 def fdeclare_opencl_builtins : Flag<["-"], "fdeclare-opencl-builtins">,
   HelpText<"Add OpenCL builtin function declarations (experimental)">;
 
+def fhlsl_strict_availability : Flag<["-"], "fhlsl-strict-availability">,
+  HelpText<"Enables strict availability diagnostic mode for HLSL built-in functions.">,
+  Group<hlsl_Group>,
+  MarshallingInfoFlag<LangOpts<"HLSLStrictAvailability">>;
+
 def fpreserve_vec3_type : Flag<["-"], "fpreserve-vec3-type">,
   HelpText<"Preserve 3-component vector type">,
   MarshallingInfoFlag<CodeGenOpts<"PreserveVec3Type">>,
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index ffb22194bce52..f481019eff51a 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -669,7 +669,7 @@ static AvailabilityResult CheckAvailability(ASTContext &Context,
     IdentifierInfo *IIEnv = A->getEnvironment();
     StringRef TargetEnv =
         Context.getTargetInfo().getTriple().getEnvironmentName();
-    StringRef EnvName = AvailabilityAttr::getPrettyEnviromentName(
+    StringRef EnvName = llvm::Triple::getEnvironmentTypeName(
         Context.getTargetInfo().getTriple().getEnvironment());
     // Matching environment or no environment on attribute
     if (!IIEnv || (!TargetEnv.empty() && IIEnv->getName() == TargetEnv)) {
diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index 330cd602297d4..0e448c8b13985 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -229,9 +229,7 @@ shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);
     break;
   case llvm::Triple::ShaderModel:
-    // FIXME: This will be updated when HLSL strict diagnostic mode
-    // is implemented (issue #90096)
-    return false;
+    return Context.getLangOpts().HLSLStrictAvailability;
   default:
     // New targets should always warn about availability.
     return Triple.getVendor() == llvm::Triple::Apple;
@@ -410,13 +408,10 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
     const TargetInfo &TI = S.getASTContext().getTargetInfo();
     std::string PlatformName(
         AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
-    llvm::StringRef TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
-        TI.getTriple().getEnvironment()));
+    llvm::StringRef TargetEnvironment(
+        llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment()));
     llvm::StringRef AttrEnvironment =
-        AA->getEnvironment() ? AvailabilityAttr::getPrettyEnviromentName(
-                                   AvailabilityAttr::getEnvironmentType(
-                                       AA->getEnvironment()->getName()))
-                             : "";
+        AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
     bool UseEnvironment =
         (!AttrEnvironment.empty() && !TargetEnvironment.empty());
 
@@ -834,34 +829,55 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
                                              OffendingDecl))
       return;
 
-    // We would like to emit the diagnostic even if -Wunguarded-availability is
-    // not specified for deployment targets >= to iOS 11 or equivalent or
-    // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
-    // later.
-    bool UseNewDiagKind = shouldDiagnoseAvailabilityByDefault(
-        SemaRef.Context,
-        SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced);
-
     const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
+
+    // In HLSL, emit diagnostic here during parsing only if the diagnostic
+    // mode is set to strict (-fhlsl-strict-availability), and either the decl
+    // availability is not restricted to a specific environment/shader stage,
+    // or the target stage is known (= it is not shader library).
+    const LangOptions &LandOpts = SemaRef.getLangOpts();
+    if (LandOpts.HLSL) {
+      if (!LandOpts.HLSLStrictAvailability ||
+          (AA->getEnvironment() != nullptr &&
+           TI.getTriple().getEnvironment() ==
+               llvm::Triple::EnvironmentType::Library))
+        return;
+    }
+
     std::string PlatformName(
         AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
-    llvm::StringRef TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
-        TI.getTriple().getEnvironment()));
+    llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName());
     llvm::StringRef AttrEnvironment =
-        AA->getEnvironment() ? AvailabilityAttr::getPrettyEnviromentName(
-                                   AvailabilityAttr::getEnvironmentType(
-                                       AA->getEnvironment()->getName()))
-                             : "";
+        AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
     bool UseEnvironment =
         (!AttrEnvironment.empty() && !TargetEnvironment.empty());
 
-    unsigned DiagKind =
-        EnvironmentMatchesOrNone
-            ? (UseNewDiagKind ? diag::warn_unguarded_availability_new
-                              : diag::warn_unguarded_availability)
-            : (UseNewDiagKind
-                   ? diag::warn_unguarded_availability_unavailable_new
-                   : diag::warn_unguarded_availability_unavailable);
+    unsigned DiagKind;
+    if (SemaRef.getLangOpts().HLSL) {
+      // For HLSL, use diagnostic from HLSLAvailability group which
+      // are reported as errors in default and in strict diagnostic mode
+      // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic
+      // mode (-Wno-error=hlsl-availability)
+      DiagKind = EnvironmentMatchesOrNone
+                     ? diag::warn_hlsl_availability
+                     : diag::warn_hlsl_availability_unavailable;
+
+    } else {
+      // For iOS, emit the diagnostic even if -Wunguarded-availability is
+      // not specified for deployment targets >= to iOS 11 or equivalent or
+      // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
+      // later.
+      bool UseNewDiagKind = shouldDiagnoseAvailabilityByDefault(
+          SemaRef.Context,
+          SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced);
+
+      DiagKind = EnvironmentMatchesOrNone
+                     ? (UseNewDiagKind ? diag::warn_unguarded_availability_new
+                                       : diag::warn_unguarded_availability)
+                     : (UseNewDiagKind
+                            ? diag::warn_unguarded_availability_unavailable_new
+                            : diag::warn_unguarded_availability_unavailable);
+    }
 
     SemaRef.Diag(Range.getBegin(), DiagKind)
         << Range << D << PlatformName << Introduced.getAsString()
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 9e614ae99f37d..87a6043a064c7 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -306,7 +306,7 @@ namespace {
 /// library).
 ///
 /// This is done by traversing the AST of all shader entry point functions
-/// and of all exported functions, and any functions that are refrenced
+/// and of all exported functions, and any functions that are referenced
 /// from this AST. In other words, any functions that are reachable from
 /// the entry points.
 class DiagnoseHLSLAvailability
@@ -536,9 +536,34 @@ DiagnoseHLSLAvailability::FindAvailabilityAttr(const Decl *D) {
 void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
                                                      const AvailabilityAttr *AA,
                                                      SourceRange Range) {
-  if (ReportOnlyShaderStageIssues && !AA->getEnvironment())
-    return;
 
+  IdentifierInfo *IIEnv = AA->getEnvironment();
+
+  if (!IIEnv) {
+    // The availability attribute does not have environment -> it depends only
+    // on shader model version and not on specific the shader stage.
+
+    // Skip emitting the diagnostics if the diagnostic mode is not set to
+    // strict (-fhlsl-strict-availability) because all relevant diagnostics
+    // were already emitted in the DiagnoseUnguardedAvailability scan
+    // (SemaAvailability.cpp).
+    if (SemaRef.getLangOpts().HLSLStrictAvailability)
+      return;
+
+    // Do not report shader-stage-independent issues if scanning a function
+    // that was already scanned in a different shader stage context (they would
+    // be duplicate)
+    if (ReportOnlyShaderStageIssues)
+      return;
+
+  } else {
+    // The availability attribute has environment -> we need to know
+    // the current stage context to property diagnose it.
+    if (InUnknownShaderStageContext())
+      return;
+  }
+
+  // Check introduced version and if environment matches
   bool EnvironmentMatches = HasMatchingEnvironmentOrNone(AA);
   VersionTuple Introduced = AA->getIntroduced();
   VersionTuple TargetVersion =
@@ -547,24 +572,16 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
   if (TargetVersion >= Introduced && EnvironmentMatches)
     return;
 
-  // Do not diagnose shade-stage-specific availability when the shader stage
-  // context is unknown
-  if (InUnknownShaderStageContext() && AA->getEnvironment() != nullptr)
-    return;
-
   // Emit diagnostic message
   const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
   llvm::StringRef PlatformName(
       AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
 
   llvm::StringRef CurrentEnvStr =
-      AvailabilityAttr::getPrettyEnviromentName(GetCurrentShaderEnvironment());
+      llvm::Triple::getEnvironmentTypeName(GetCurrentShaderEnvironment());
 
-  llvm::StringRef AttrEnvStr = AA->getEnvironment()
-                                   ? AvailabilityAttr::getPrettyEnviromentName(
-                                         AvailabilityAttr::getEnvironmentType(
-                                             AA->getEnvironment()->getName()))
-                                   : "";
+  llvm::StringRef AttrEnvStr =
+      AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
   bool UseEnvironment = !AttrEnvStr.empty();
 
   if (EnvironmentMatches) {
@@ -585,5 +602,14 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
 } // namespace
 
 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
+  // because all relevant diagnostics were already emitted in the
+  // DiagnoseUnguardedAvailability scan (SemaAvailability.cpp).
+  const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
+  if (SemaRef.getLangOpts().HLSLStrictAvailability &&
+      TI.getTriple().getEnvironment() != llvm::Triple::EnvironmentType::Library)
+    return;
+
   DiagnoseHLSLAvailability(SemaRef).RunOnTranslationUnit(TU);
 }
diff --git a/clang/test/SemaHLSL/Availability/avail-diag-strict-compute.hlsl b/clang/test/SemaHLSL/Availability/avail-diag-strict-compute.hlsl
new file mode 100644
index 0000000000000..b67e10c9a9017
--- /dev/null
+++ b/clang/test/SemaHLSL/Availability/avail-diag-strict-compute.hlsl
@@ -0,0 +1,129 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute \
+// RUN: -fhlsl-strict-availability -fsyntax-only -verify %s
+
+__attribute__((availability(shadermodel, introduced = 6.5)))
+float fx(float);  // #fx
+
+__attribute__((availability(shadermodel, introduced = 6.6)))
+half fx(half);  // #fx_half
+
+__attribute__((availability(shadermodel, introduced = 5.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.5, environment = compute)))
+float fy(float); // #fy
+
+__attribute__((availability(shadermodel, introduced = 5.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.5, environment = mesh)))
+float fz(float); // #fz
+
+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}}
+  float A = fx(f); // #also_alive_fx_call
+  // expected-error@#also_alive_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
+  // expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float B = fy(f); // #also_alive_fy_call
+  // expected-error@#also_alive_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float C = fz(f); // #also_alive_fz_call
+  return 0;
+}
+
+float alive(float f) {
+  // expected-error@#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}}
+  float A = fx(f); // #alive_fx_call
+  // expected-error@#alive_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
+  // expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float B = fy(f); // #alive_fy_call
+  // expected-error@#alive_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float C = fz(f); // #alive_fz_call
+
+  return also_alive(f);
+}
+
+float also_dead(float f) {
+  // expected-error@#also_dead_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}}
+  float A = fx(f); // #also_dead_fx_call
+  // expected-error@#also_dead_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
+  // expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float B = fy(f); // #also_dead_fy_call
+  // expected-error@#also_dead_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float C = fz(f); // #also_dead_fz_call
+  return 0;
+}
+
+float dead(float f) {
+  // expected-error@#dead_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}}
+  float A = fx(f); // #dead_fx_call
+  // expected-error@#dead_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
+  // expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float B = fy(f); // #dead_fy_call
+  // expected-error@#dead_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float C = fz(f); // #dead_fz_call
+
+  return also_dead(f);
+}
+
+template<typename T>
+T aliveTemp(T f) {
+  // expected-error@#aliveTemp_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
+  // expected-note@#aliveTemp_inst {{in instantiation of function template specialization 'aliveTemp<float>' requested here}}
+  // 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}}
+  float A = fx(f); // #aliveTemp_fx_call
+  // expected-error@#aliveTemp_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
+  // expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float B = fy(f); // #aliveTemp_fy_call
+  // expected-error@#aliveTemp_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float C = fz(f); // #aliveTemp_fz_call
+  return 0;
+}
+
+template<typename T> T aliveTemp2(T f) {
+  // expected-error@#aliveTemp2_fx_call {{'fx' is only available on Shader Model 6.6 or newer}}
+  // expected-note@#fx_half {{'fx' has been marked as being introduced in Shader Model 6.6 here, but the deployment target is Shader Model 6.0}}
+  // expected-error@#aliveTemp2_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}}
+  return fx(f); // #aliveTemp2_fx_call
+}
+
+half test(half x) {
+  return aliveTemp2(x); // expected-note {{in instantiation of function template specialization 'aliveTemp2<half>' requested here}}
+}
+
+float test(float x) {
+  return aliveTemp2(x); // expected-note {{in instantiation of function template specialization 'aliveTemp2<float>' requested here}}
+}
+
+class MyClass
+{
+  float F;
+  float makeF() {
+    // expected-error@#MyClass_makeF_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}}
+    float A = fx(F); // #MyClass_makeF_fx_call
+    // expected-error@#MyClass_makeF_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
+    // expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
+    float B = fy(F); // #MyClass_makeF_fy_call
+    // expected-error@#MyClass_makeF_fz_call {{'fz' is unavailable}}
+    // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
+    float C = fz(F); // #MyClass_makeF_fz_call
+  }
+};
+
+[numthreads(4,1,1)]
+float main() {
+  float f = 3;
+  MyClass C = { 1.0f };
+  float a = alive(f);
+  float b = aliveTemp<float>(f); // #aliveTemp_inst
+  float c = C.makeF();
+  float d = test((float)1.0);
+  float e = test((half)1.0);
+  return a * b * c;
+}
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/Availability/avail-diag-strict-lib.hlsl b/clang/test/SemaHLSL/Availability/avail-diag-strict-lib.hlsl
new file mode 100644
index 0000000000000..4c9675051e570
--- /dev/null
+++ b/clang/test/SemaHLSL/Availability/avail-diag-strict-lib.hlsl
@@ -0,0 +1,142 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library \
+// RUN: -fhlsl-strict-availability -fsyntax-only -verify %s
+
+__attribute__((availability(shadermodel, introduced = 6.5)))
+float fx(float);  // #fx
+
+__attribute__((availability(shadermodel, introduced = 6.6)))
+half fx(half);  // #fx_half
+
+__attribute__((availability(shadermodel, introduced = 5.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.5, environment = compute)))
+float fy(float); // #fy
+
+__attribute__((availability(shadermodel, introduced = 5.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.5, environment = mesh)))
+float fz(float); // #fz
+
+// FIXME: all diagnostics marked as FUTURE will come alive when HLSL default
+// diagnostic mode is implemented in a future PR which will verify calls in
+// all functions that are reachable from the shader library entry points
+
+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}}
+  float A = fx(f); // #also_alive_fx_call
+  
+  // expected-error@#also_alive_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
+  // expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float B = fy(f); // #also_alive_fy_call
+
+  // expected-error@#also_alive_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float C = fz(f); // #also_alive_fz_call
+
+  return 0;
+}
+
+float alive(float f) {
+  // expected-error@#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}}
+  float A = fx(f); // #alive_fx_call
+
+  // expected-error@#alive_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
+  // expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float B = fy(f); // #alive_fy_call
+
+  // expected-error@#alive_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float C = fz(f); // #alive_fz_call
+
+  return also_alive(f);
+}
+
+float also_dead(float f) {
+  // expected-error@#also_dead_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}}
+  float A = fx(f); // #also_dead_fx_call
+  
+  // Call to environment-specific function from an unreachable function 
+  // in a shader library - no diagnostic expected.
+  float B = fy(f); // #also_dead_fy_call
+
+  // Call to environment-specific function from an unreachable function 
+  // in a shader library - no diagnostic expected.
+  float C = fz(f); // #also_dead_fz_call
+  return 0;
+}
+
+float dead(float f) {
+  // expected-error@#dead_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}}
+  float A = fx(f); // #dead_fx_call
+
+  // Call to environment-specific function from an unreachable function 
+  // in a shader library - no diagnostic expected.
+  float B = fy(f); // #dead_fy_call
+
+  // Call to environment-specific function from an unreachable function 
+  // in a shader library - no diagnostic expected.
+  float C = fz(f); // #dead_fz_call
+
+  return also_dead(f);
+}
+
+template<typename T>
+T aliveTemp(T f) {
+  // expected-error@#aliveTemp_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
+  // expected-note@#aliveTemp_inst {{in instantiation of function template specialization 'aliveTemp<float>' requested here}}
+  // 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}}
+  float A = fx(f); // #aliveTemp_fx_call
+  // expected-error@#aliveTemp_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
+  // expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float B = fy(f); // #aliveTemp_fy_call
+  // expected-error@#aliveTemp_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
+  float C = fz(f); // #aliveTemp_fz_call
+  return 0;
+}
+
+template<typename T> T aliveTemp2(T f) {
+  // expected-error@#aliveTemp2_fx_call {{'fx' is only available on Shader Model 6.6 or newer}}
+  // expected-note@#fx_half {{'fx' has been marked as being introduced in Shader Model 6.6 here, but the deployment target is Shader Model 6.0}}
+  // expected-error@#aliveTemp2_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}}
+  return fx(f); // #aliveTemp2_fx_call
+}
+
+half test(half x) {
+  return aliveTemp2(x); // expected-note {{in instantiation of function template specialization 'aliveTemp2<half>' requested here}}
+}
+
+float test(float x) {
+  return aliveTemp2(x); // expected-note {{in instantiation of function template specialization 'aliveTemp2<float>' requested here}}
+}
+
+class MyClass
+{
+  float F;
+  float makeF() {
+    // expected-error@#MyClass_makeF_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}}
+    float A = fx(F); // #MyClass_makeF_fx_call
+    // expected-error@#MyClass_makeF_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
+    // expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
+    float B = fy(F); // #MyClass_makeF_fy_call
+    // expected-error@#MyClass_makeF_fz_call {{'fz' is unavailable}}
+    // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
+    float C = fz(F); // #MyClass_makeF_fz_call
+  }
+};
+
+[shader("compute")]
+[numthreads(4,1,1)]
+float main() {
+  float f = 3;
+  MyClass C = { 1.0f };
+  float a = alive(f);float b = aliveTemp<float>(f); // #aliveTemp_inst
+  float c = C.makeF();
+  float d = test((float)1.0);
+  float e = test((half)1.0);
+  return a * b * c;
+}
\ No newline at end of file

>From 33c54e453f978943154f121e29a3c863f0b83e06 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Mon, 3 Jun 2024 12:05:14 -0700
Subject: [PATCH 2/3] Move check to ShouldDiagnoseAvailabilityInContext

---
 clang/lib/Sema/SemaAvailability.cpp | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index 0e448c8b13985..3eaf25709e91a 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -157,6 +157,18 @@ static bool ShouldDiagnoseAvailabilityInContext(
     }
   }
 
+  // In HLSL, emit diagnostic during parsing only if the diagnostic
+  // mode is set to strict (-fhlsl-strict-availability), and either the decl
+  // availability is not restricted to a specific environment/shader stage,
+  // or the target stage is known (= it is not shader library).
+  if (S.getLangOpts().HLSL) {
+    if (!S.getLangOpts().HLSLStrictAvailability ||
+        (DeclEnv != nullptr &&
+         S.getASTContext().getTargetInfo().getTriple().getEnvironment() ==
+             llvm::Triple::EnvironmentType::Library))
+      return false;
+  }
+
   // Checks if we should emit the availability diagnostic in the context of C.
   auto CheckContext = [&](const Decl *C) {
     if (K == AR_NotYetIntroduced) {
@@ -830,20 +842,6 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
       return;
 
     const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
-
-    // In HLSL, emit diagnostic here during parsing only if the diagnostic
-    // mode is set to strict (-fhlsl-strict-availability), and either the decl
-    // availability is not restricted to a specific environment/shader stage,
-    // or the target stage is known (= it is not shader library).
-    const LangOptions &LandOpts = SemaRef.getLangOpts();
-    if (LandOpts.HLSL) {
-      if (!LandOpts.HLSLStrictAvailability ||
-          (AA->getEnvironment() != nullptr &&
-           TI.getTriple().getEnvironment() ==
-               llvm::Triple::EnvironmentType::Library))
-        return;
-    }
-
     std::string PlatformName(
         AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
     llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName());

>From 78a02c7285a9e7aa7e1bc0ebb0533a34e2e1f4f1 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 11 Jun 2024 17:18:08 -0700
Subject: [PATCH 3/3] Code review feedback

---
 clang/lib/Sema/SemaAvailability.cpp | 74 ++++++++++++-----------------
 1 file changed, 30 insertions(+), 44 deletions(-)

diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index 3eaf25709e91a..f6aa8145fc153 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -222,13 +222,16 @@ static bool ShouldDiagnoseAvailabilityInContext(
   return true;
 }
 
-static bool
-shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
-                                    const VersionTuple &DeploymentVersion,
-                                    const VersionTuple &DeclVersion) {
+static unsigned getAvailabilityDiagnosticKind(
+    const ASTContext &Context, const VersionTuple &DeploymentVersion,
+    const VersionTuple &DeclVersion, bool HasMatchingEnv) {
   const auto &Triple = Context.getTargetInfo().getTriple();
   VersionTuple ForceAvailabilityFromVersion;
   switch (Triple.getOS()) {
+  // For iOS, emit the diagnostic even if -Wunguarded-availability is
+  // not specified for deployment targets >= to iOS 11 or equivalent or
+  // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
+  // later.
   case llvm::Triple::IOS:
   case llvm::Triple::TvOS:
     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11);
@@ -240,14 +243,26 @@ shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
   case llvm::Triple::MacOSX:
     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);
     break;
+  // For HLSL, use diagnostic from HLSLAvailability group which
+  // are reported as errors by default and in strict diagnostic mode
+  // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic
+  // mode (-Wno-error=hlsl-availability)
   case llvm::Triple::ShaderModel:
-    return Context.getLangOpts().HLSLStrictAvailability;
+    return HasMatchingEnv ? diag::warn_hlsl_availability
+                          : diag::warn_hlsl_availability_unavailable;
   default:
-    // New targets should always warn about availability.
-    return Triple.getVendor() == llvm::Triple::Apple;
+    // New Apple targets should always warn about availability.
+    ForceAvailabilityFromVersion =
+        (Triple.getVendor() == llvm::Triple::Apple)
+            ? VersionTuple(/*Major=*/0, 0)
+            : VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1);
   }
-  return DeploymentVersion >= ForceAvailabilityFromVersion ||
-         DeclVersion >= ForceAvailabilityFromVersion;
+  if (DeploymentVersion >= ForceAvailabilityFromVersion ||
+      DeclVersion >= ForceAvailabilityFromVersion)
+    return HasMatchingEnv ? diag::warn_unguarded_availability_new
+                          : diag::warn_unguarded_availability_unavailable_new;
+  return HasMatchingEnv ? diag::warn_unguarded_availability
+                        : diag::warn_unguarded_availability_unavailable;
 }
 
 static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) {
@@ -427,16 +442,9 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
     bool UseEnvironment =
         (!AttrEnvironment.empty() && !TargetEnvironment.empty());
 
-    bool UseNewWarning = shouldDiagnoseAvailabilityByDefault(
+    unsigned DiagKind = getAvailabilityDiagnosticKind(
         S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
-        Introduced);
-
-    unsigned DiagKind =
-        EnvironmentMatchesOrNone
-            ? (UseNewWarning ? diag::warn_unguarded_availability_new
-                             : diag::warn_unguarded_availability)
-            : (UseNewWarning ? diag::warn_unguarded_availability_unavailable_new
-                             : diag::warn_unguarded_availability_unavailable);
+        Introduced, EnvironmentMatchesOrNone);
 
     S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName
                           << Introduced.getAsString() << UseEnvironment
@@ -850,32 +858,10 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
     bool UseEnvironment =
         (!AttrEnvironment.empty() && !TargetEnvironment.empty());
 
-    unsigned DiagKind;
-    if (SemaRef.getLangOpts().HLSL) {
-      // For HLSL, use diagnostic from HLSLAvailability group which
-      // are reported as errors in default and in strict diagnostic mode
-      // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic
-      // mode (-Wno-error=hlsl-availability)
-      DiagKind = EnvironmentMatchesOrNone
-                     ? diag::warn_hlsl_availability
-                     : diag::warn_hlsl_availability_unavailable;
-
-    } else {
-      // For iOS, emit the diagnostic even if -Wunguarded-availability is
-      // not specified for deployment targets >= to iOS 11 or equivalent or
-      // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
-      // later.
-      bool UseNewDiagKind = shouldDiagnoseAvailabilityByDefault(
-          SemaRef.Context,
-          SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced);
-
-      DiagKind = EnvironmentMatchesOrNone
-                     ? (UseNewDiagKind ? diag::warn_unguarded_availability_new
-                                       : diag::warn_unguarded_availability)
-                     : (UseNewDiagKind
-                            ? diag::warn_unguarded_availability_unavailable_new
-                            : diag::warn_unguarded_availability_unavailable);
-    }
+    unsigned DiagKind = getAvailabilityDiagnosticKind(
+        SemaRef.Context,
+        SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced,
+        EnvironmentMatchesOrNone);
 
     SemaRef.Diag(Range.getBegin(), DiagKind)
         << Range << D << PlatformName << Introduced.getAsString()



More information about the cfe-commits mailing list