[clang] [HLSL] Default and Relaxed Availability Diagnostics (PR #92703)

Helena Kotas via cfe-commits cfe-commits at lists.llvm.org
Sun May 19 11:05:09 PDT 2024


https://github.com/hekota created https://github.com/llvm/llvm-project/pull/92703

None

>From 22b67d30ca087d6a912183039c87fd1790eedfe4 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 23 Apr 2024 00:49:28 -0700
Subject: [PATCH 01/21] Add environment parameter to clang availability
 attribute

---
 clang/include/clang/Basic/Attr.td             |  33 +++++-
 clang/include/clang/Basic/AttrDocs.td         |   2 +
 .../clang/Basic/DiagnosticParseKinds.td       |   2 +
 .../clang/Basic/DiagnosticSemaKinds.td        |   5 +-
 clang/include/clang/Parse/Parser.h            |   3 +
 clang/include/clang/Sema/ParsedAttr.h         |  40 ++++---
 clang/include/clang/Sema/Sema.h               |   5 +-
 clang/lib/AST/DeclBase.cpp                    |  27 ++++-
 clang/lib/Headers/hlsl/hlsl_intrinsics.h      |  13 ++-
 clang/lib/Index/CommentToXML.cpp              |   3 +
 clang/lib/Parse/ParseDecl.cpp                 |  20 +++-
 clang/lib/Sema/SemaAPINotes.cpp               |   3 +-
 clang/lib/Sema/SemaAvailability.cpp           | 109 +++++++++++++-----
 clang/lib/Sema/SemaDecl.cpp                   |   2 +-
 clang/lib/Sema/SemaDeclAttr.cpp               |  34 ++++--
 15 files changed, 232 insertions(+), 69 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index dc87a8c6f022d..1b07f4eb40809 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -956,7 +956,7 @@ def Availability : InheritableAttr {
               VersionArgument<"deprecated">, VersionArgument<"obsoleted">,
               BoolArgument<"unavailable">, StringArgument<"message">,
               BoolArgument<"strict">, StringArgument<"replacement">,
-              IntArgument<"priority">];
+              IntArgument<"priority">, IdentifierArgument<"environment">];
   let AdditionalMembers =
 [{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) {
     return llvm::StringSwitch<llvm::StringRef>(Platform)
@@ -976,7 +976,7 @@ def Availability : InheritableAttr {
              .Case("xros", "visionOS")
              .Case("xros_app_extension", "visionOS (App Extension)")
              .Case("swift", "Swift")
-             .Case("shadermodel", "HLSL ShaderModel")
+             .Case("shadermodel", "HLSL Shader Model")
              .Case("ohos", "OpenHarmony OS")
              .Default(llvm::StringRef());
 }
@@ -1016,7 +1016,34 @@ static llvm::StringRef canonicalizePlatformName(llvm::StringRef Platform) {
              .Case("visionos_app_extension", "xros_app_extension")
              .Case("ShaderModel", "shadermodel")
              .Default(Platform);
-} }];
+}
+static llvm::StringRef getPrettyEnviromentName(llvm::StringRef Environment) {
+    return llvm::StringSwitch<llvm::StringRef>(Environment)
+             .Case("pixel", "pixel shader")
+             .Case("vertex", "vertex shader")
+             .Case("geometry", "geometry shader")
+             .Case("hull", "hull shader")
+             .Case("domain", "domain shader")
+             .Case("compute", "compute shader")
+             .Case("mesh", "mesh shader")
+             .Case("amplification", "amplification shader")
+             .Case("library", "shader library")
+             .Default(Environment);
+}
+static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environment) {
+    return llvm::StringSwitch<llvm::Triple::EnvironmentType>(Environment)
+             .Case("pixel", llvm::Triple::Pixel)
+             .Case("vertex", llvm::Triple::Vertex)
+             .Case("geometry", llvm::Triple::Geometry)
+             .Case("hull", llvm::Triple::Hull)
+             .Case("domain", llvm::Triple::Domain)
+             .Case("compute", llvm::Triple::Compute)
+             .Case("mesh", llvm::Triple::Mesh)
+             .Case("amplification", llvm::Triple::Amplification)
+             .Case("library", llvm::Triple::Library)
+             .Default(llvm::Triple::UnknownEnvironment);
+}
+}];
   let HasCustomParsing = 1;
   let InheritEvenIfAlreadyPresent = 1;
   let Subjects = SubjectList<[Named]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index a0bbe5861c572..a81163df35ca8 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1593,6 +1593,8 @@ replacement=\ *string-literal*
   a warning about use of a deprecated declaration. The Fix-It will replace
   the deprecated declaration with the new declaration specified.
 
+// HEKOTA TODO add docs here
+
 Multiple availability attributes can be placed on a declaration, which may
 correspond to different platforms. For most platforms, the availability
 attribute with the platform corresponding to the target platform will be used;
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 66405095d51de..631dc8880fcfc 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1103,6 +1103,8 @@ def err_zero_version : Error<
   "version number must have non-zero major, minor, or sub-minor version">;
 def err_availability_expected_platform : Error<
   "expected a platform name, e.g., 'macos'">;
+def err_availability_expected_environment : Error<
+  "expected an environment name, e.g., 'pixel'">;
 
 // objc_bridge_related attribute
 def err_objcbridge_related_expected_related_class : Error<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1a2d8bf4e4eb1..2647d84d4041d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3819,6 +3819,9 @@ def note_cannot_use_trivial_abi_reason : Note<
 // Availability attribute
 def warn_availability_unknown_platform : Warning<
   "unknown platform %0 in availability macro">, InGroup<Availability>;
+def warn_availability_unknown_environment : Warning<
+  "unknown environment %0 in availability macro">, InGroup<Availability>;
+
 def warn_availability_version_ordering : Warning<
   "feature cannot be %select{introduced|deprecated|obsoleted}0 in %1 version "
   "%2 before it was %select{introduced|deprecated|obsoleted}3 in version %4; "
@@ -3851,7 +3854,7 @@ def warn_availability_fuchsia_unavailable_minor : Warning<
   InGroup<Availability>;
 
 def warn_unguarded_availability :
-  Warning<"%0 is only available on %1 %2 or newer">,
+  Warning<"%0 %select{is only|is not}5 available %select{|in %4 environment }3on %1 %2 %select{or newer|}5">,
   InGroup<UnguardedAvailability>, DefaultIgnore;
 def warn_unguarded_availability_new :
   Warning<warn_unguarded_availability.Summary>,
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 72b2f958a5e62..a6cb96ca4a37c 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -151,6 +151,9 @@ class Parser : public CodeCompletionHandler {
   /// Identifier for "replacement".
   IdentifierInfo *Ident_replacement;
 
+  /// Identifier for "environment".
+  IdentifierInfo *Ident_environment;
+
   /// Identifiers used by the 'external_source_symbol' attribute.
   IdentifierInfo *Ident_language, *Ident_defined_in,
       *Ident_generated_declaration, *Ident_USR;
diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h
index 25a5fa05b21c7..7ad39d1a4942f 100644
--- a/clang/include/clang/Sema/ParsedAttr.h
+++ b/clang/include/clang/Sema/ParsedAttr.h
@@ -40,6 +40,7 @@ class LangOptions;
 class Sema;
 class Stmt;
 class TargetInfo;
+struct IdentifierLoc;
 
 /// Represents information about a change in availability for
 /// an entity, which is part of the encoding of the 'availability'
@@ -68,12 +69,14 @@ struct AvailabilityData {
   AvailabilityChange Changes[NumAvailabilitySlots];
   SourceLocation StrictLoc;
   const Expr *Replacement;
+  const IdentifierLoc *EnvironmentLoc;
 
   AvailabilityData(const AvailabilityChange &Introduced,
                    const AvailabilityChange &Deprecated,
                    const AvailabilityChange &Obsoleted,
-                   SourceLocation Strict, const Expr *ReplaceExpr)
-    : StrictLoc(Strict), Replacement(ReplaceExpr) {
+                   SourceLocation Strict, const Expr *ReplaceExpr,
+                   const IdentifierLoc *EnvironmentLoc)
+    : StrictLoc(Strict), Replacement(ReplaceExpr), EnvironmentLoc(EnvironmentLoc) {
     Changes[IntroducedSlot] = Introduced;
     Changes[DeprecatedSlot] = Deprecated;
     Changes[ObsoletedSlot] = Obsoleted;
@@ -234,7 +237,7 @@ class ParsedAttr final
              const AvailabilityChange &deprecated,
              const AvailabilityChange &obsoleted, SourceLocation unavailable,
              const Expr *messageExpr, Form formUsed, SourceLocation strict,
-             const Expr *replacementExpr)
+             const Expr *replacementExpr, const IdentifierLoc *environmentLoc)
       : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc, formUsed),
         NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true),
         IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
@@ -243,8 +246,9 @@ class ParsedAttr final
         Info(ParsedAttrInfo::get(*this)) {
     ArgsUnion PVal(Parm);
     memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion));
-    new (getAvailabilityData()) detail::AvailabilityData(
-        introduced, deprecated, obsoleted, strict, replacementExpr);
+    new (getAvailabilityData())
+        detail::AvailabilityData(introduced, deprecated, obsoleted, strict,
+                                 replacementExpr, environmentLoc);
   }
 
   /// Constructor for objc_bridge_related attributes.
@@ -445,6 +449,12 @@ class ParsedAttr final
     return getAvailabilityData()->Replacement;
   }
 
+  const IdentifierLoc *getEnvironment() const {
+    assert(getParsedKind() == AT_Availability &&
+           "Not an availability attribute");
+    return getAvailabilityData()->EnvironmentLoc;
+  }
+
   const ParsedType &getMatchingCType() const {
     assert(getParsedKind() == AT_TypeTagForDatatype &&
            "Not a type_tag_for_datatype attribute");
@@ -759,11 +769,13 @@ class AttributePool {
                      const AvailabilityChange &obsoleted,
                      SourceLocation unavailable, const Expr *MessageExpr,
                      ParsedAttr::Form form, SourceLocation strict,
-                     const Expr *ReplacementExpr) {
+                     const Expr *ReplacementExpr,
+                     IdentifierLoc *EnvironmentLoc) {
     void *memory = allocate(AttributeFactory::AvailabilityAllocSize);
-    return add(new (memory) ParsedAttr(
-        attrName, attrRange, scopeName, scopeLoc, Param, introduced, deprecated,
-        obsoleted, unavailable, MessageExpr, form, strict, ReplacementExpr));
+    return add(new (memory) ParsedAttr(attrName, attrRange, scopeName, scopeLoc,
+                                       Param, introduced, deprecated, obsoleted,
+                                       unavailable, MessageExpr, form, strict,
+                                       ReplacementExpr, EnvironmentLoc));
   }
 
   ParsedAttr *create(IdentifierInfo *attrName, SourceRange attrRange,
@@ -993,10 +1005,12 @@ class ParsedAttributes : public ParsedAttributesView {
                      const AvailabilityChange &obsoleted,
                      SourceLocation unavailable, const Expr *MessageExpr,
                      ParsedAttr::Form form, SourceLocation strict,
-                     const Expr *ReplacementExpr) {
-    ParsedAttr *attr = pool.create(
-        attrName, attrRange, scopeName, scopeLoc, Param, introduced, deprecated,
-        obsoleted, unavailable, MessageExpr, form, strict, ReplacementExpr);
+                     const Expr *ReplacementExpr,
+                     IdentifierLoc *EnvironmentLoc) {
+    ParsedAttr *attr =
+        pool.create(attrName, attrRange, scopeName, scopeLoc, Param, introduced,
+                    deprecated, obsoleted, unavailable, MessageExpr, form,
+                    strict, ReplacementExpr, EnvironmentLoc);
     addAtEnd(attr);
     return attr;
   }
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1e89dfc58d92b..d2686e9f8bd79 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -39,6 +39,7 @@
 #include "clang/Basic/Cuda.h"
 #include "clang/Basic/DarwinSDKInfo.h"
 #include "clang/Basic/ExpressionTraits.h"
+#include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/Module.h"
 #include "clang/Basic/OpenCLOptions.h"
 #include "clang/Basic/PragmaKinds.h"
@@ -3623,7 +3624,9 @@ class Sema final : public SemaBase {
                         VersionTuple Introduced, VersionTuple Deprecated,
                         VersionTuple Obsoleted, bool IsUnavailable,
                         StringRef Message, bool IsStrict, StringRef Replacement,
-                        AvailabilityMergeKind AMK, int Priority);
+                        AvailabilityMergeKind AMK, int Priority,
+                        IdentifierInfo *IIEnvironment);
+  
   TypeVisibilityAttr *
   mergeTypeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI,
                           TypeVisibilityAttr::VisibilityType Vis);
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 434926324c96c..87177d00c83a4 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -666,12 +666,27 @@ static AvailabilityResult CheckAvailability(ASTContext &Context,
   // Make sure that this declaration has already been introduced.
   if (!A->getIntroduced().empty() &&
       EnclosingVersion < A->getIntroduced()) {
-    if (Message) {
-      Message->clear();
-      llvm::raw_string_ostream Out(*Message);
-      VersionTuple VTI(A->getIntroduced());
-      Out << "introduced in " << PrettyPlatformName << ' '
-          << VTI << HintMessage;
+    IdentifierInfo *IIEnv = A->getEnvironment();
+    StringRef TargetEnv = Context.getTargetInfo().getTriple().getEnvironmentName();
+    StringRef EnvName = AvailabilityAttr::getPrettyEnviromentName(TargetEnv);
+    // Matching environment or no environment on attribute
+    if (!IIEnv || (!TargetEnv.empty() && IIEnv->getName() == TargetEnv)) {
+      if (Message) {
+        Message->clear();
+        llvm::raw_string_ostream Out(*Message);
+        VersionTuple VTI(A->getIntroduced());
+        Out << "introduced in " << PrettyPlatformName << " "
+            << VTI << EnvName << HintMessage;
+      }
+    }
+    // Non-matching environment or no environment on target
+    else {
+      if (Message) {
+        Message->clear();
+        llvm::raw_string_ostream Out(*Message);
+        Out << "not available on " << PrettyPlatformName << " "
+            << EnvName << HintMessage;
+      }
     }
 
     return A->getStrict() ? AR_Unavailable : AR_NotYetIntroduced;
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index 06409c6fc7741..ca0582ef39a27 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -18,14 +18,19 @@ namespace hlsl {
 
 #define _HLSL_BUILTIN_ALIAS(builtin)                                           \
   __attribute__((clang_builtin_alias(builtin)))
-#define _HLSL_AVAILABILITY(environment, version)                               \
-  __attribute__((availability(environment, introduced = version)))
+#define _HLSL_AVAILABILITY(platform, version)                               \
+  __attribute__((availability(platform, introduced = version)))
+#define _HLSL_AVAILABILITY_STAGE(platform, version, stage)                        \
+  __attribute__((availability(platform, introduced = version, environment = stage)))
 
 #ifdef __HLSL_ENABLE_16_BIT
-#define _HLSL_16BIT_AVAILABILITY(environment, version)                         \
-  __attribute__((availability(environment, introduced = version)))
+#define _HLSL_16BIT_AVAILABILITY(platform, version)                         \
+  __attribute__((availability(platform, introduced = version)))
+#define _HLSL_16BIT_AVAILABILITY_STAGE(platform, version, stage)                  \
+  __attribute__((availability(platform, introduced = version, environment = stage)))
 #else
 #define _HLSL_16BIT_AVAILABILITY(environment, version)
+#define _HLSL_16BIT_AVAILABILITY_STAGE(environment, version, stage)
 #endif
 
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/Index/CommentToXML.cpp b/clang/lib/Index/CommentToXML.cpp
index 295f3f228ff79..5a484bdd15eaa 100644
--- a/clang/lib/Index/CommentToXML.cpp
+++ b/clang/lib/Index/CommentToXML.cpp
@@ -1052,6 +1052,9 @@ void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
       }
       if (AA->getUnavailable())
         Result << "<Unavailable/>";
+
+      // HEKOTA TODO add Environment here
+      
       Result << "</Availability>";
     }
   }
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 5f26b5a9e46be..55facfb009932 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -1224,6 +1224,7 @@ void Parser::ParseAvailabilityAttribute(
   enum { Introduced, Deprecated, Obsoleted, Unknown };
   AvailabilityChange Changes[Unknown];
   ExprResult MessageExpr, ReplacementExpr;
+  IdentifierLoc *EnvironmentLoc = nullptr;
 
   // Opening '('.
   BalancedDelimiterTracker T(*this, tok::l_paren);
@@ -1271,6 +1272,7 @@ void Parser::ParseAvailabilityAttribute(
     Ident_message = PP.getIdentifierInfo("message");
     Ident_strict = PP.getIdentifierInfo("strict");
     Ident_replacement = PP.getIdentifierInfo("replacement");
+    Ident_environment = PP.getIdentifierInfo("environment");
   }
 
   // Parse the optional "strict", the optional "replacement" and the set of
@@ -1318,6 +1320,13 @@ void Parser::ParseAvailabilityAttribute(
       continue;
     }
 
+    if (Keyword == Ident_environment) {
+      if (EnvironmentLoc != nullptr) {
+        Diag(KeywordLoc, diag::err_availability_redundant)
+            << Keyword << SourceRange(EnvironmentLoc->Loc);
+      }
+    }
+
     if (Tok.isNot(tok::equal)) {
       Diag(Tok, diag::err_expected_after) << Keyword << tok::equal;
       SkipUntil(tok::r_paren, StopAtSemi);
@@ -1339,6 +1348,15 @@ void Parser::ParseAvailabilityAttribute(
         continue;
       }
     }
+    if (Keyword == Ident_environment) {
+      if (Tok.isNot(tok::identifier)) {
+        Diag(Tok, diag::err_availability_expected_environment);
+        SkipUntil(tok::r_paren, StopAtSemi);
+        return;
+      }
+      EnvironmentLoc = ParseIdentifierLoc();
+      continue;
+    }
 
     // Special handling of 'NA' only when applied to introduced or
     // deprecated.
@@ -1420,7 +1438,7 @@ void Parser::ParseAvailabilityAttribute(
                SourceRange(AvailabilityLoc, T.getCloseLocation()), ScopeName,
                ScopeLoc, Platform, Changes[Introduced], Changes[Deprecated],
                Changes[Obsoleted], UnavailableLoc, MessageExpr.get(), Form,
-               StrictLoc, ReplacementExpr.get());
+               StrictLoc, ReplacementExpr.get(), EnvironmentLoc);
 }
 
 /// Parse the contents of the "external_source_symbol" attribute.
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 4c445f28bba8c..b904fa0b04448 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -268,7 +268,8 @@ static void ProcessAPINotes(Sema &S, Decl *D,
               ASTAllocateString(S.Context, Info.UnavailableMsg),
               /*Strict=*/false,
               /*Replacement=*/StringRef(),
-              /*Priority=*/Sema::AP_Explicit);
+              /*Priority=*/Sema::AP_Explicit,
+              /*Environment=*/nullptr);
         },
         [](const Decl *D) {
           return llvm::find_if(D->attrs(), [](const Attr *next) -> bool {
diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index 846a31a796730..2a24cef17b469 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -14,19 +14,35 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Basic/DiagnosticSema.h"
+#include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/DelayedDiagnostic.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/Sema.h"
+#include "llvm/ADT/StringRef.h"
 #include <optional>
 
 using namespace clang;
 using namespace sema;
 
+static bool hasMatchingEnvironmentOrNone(const ASTContext &Context, const AvailabilityAttr *AA) {
+  IdentifierInfo *IIEnvironment = AA->getEnvironment();
+  auto Environment = Context.getTargetInfo().getTriple().getEnvironment();
+  if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment) 
+    return true;
+  
+  llvm::Triple::EnvironmentType ET =
+      AvailabilityAttr::getEnvironmentType(IIEnvironment->getName());
+  return Environment == ET;
+}
+  
 static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
                                                   const Decl *D) {
+  AvailabilityAttr const *PartialMatch = nullptr;
   // Check each AvailabilityAttr to find the one for this platform.
+  // For multiple attributes with the same platform try to find one for this
+  // environment.
   for (const auto *A : D->attrs()) {
     if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) {
       // FIXME: this is copied from CheckAvailability. We should try to
@@ -45,11 +61,15 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
       StringRef TargetPlatform = Context.getTargetInfo().getPlatformName();
 
       // Match the platform name.
-      if (RealizedPlatform == TargetPlatform)
-        return Avail;
+      if (RealizedPlatform == TargetPlatform) {
+        // Find the best matching attribute for this environment
+        if (hasMatchingEnvironmentOrNone(Context, Avail))
+          return Avail;
+        PartialMatch = Avail;
+      }
     }
   }
-  return nullptr;
+  return PartialMatch;
 }
 
 /// The diagnostic we should emit for \c D, and the declaration that
@@ -118,8 +138,9 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
 /// the context of \c Ctx. For example, we should emit an unavailable diagnostic
 /// in a deprecated context, but not the other way around.
 static bool
-ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K,
-                                    VersionTuple DeclVersion, Decl *Ctx,
+ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K, 
+                                    VersionTuple DeclVersion, 
+                                    const IdentifierInfo* DeclEnv, Decl *Ctx,
                                     const NamedDecl *OffendingDecl) {
   assert(K != AR_Available && "Expected an unavailable declaration here!");
 
@@ -139,7 +160,7 @@ ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K,
   auto CheckContext = [&](const Decl *C) {
     if (K == AR_NotYetIntroduced) {
       if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))
-        if (AA->getIntroduced() >= DeclVersion)
+        if (AA->getIntroduced() >= DeclVersion && AA->getEnvironment() == DeclEnv )
           return true;
     } else if (K == AR_Deprecated) {
       if (C->isDeprecated())
@@ -343,10 +364,14 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
   unsigned available_here_select_kind;
 
   VersionTuple DeclVersion;
-  if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl))
+  const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl);
+  const IdentifierInfo *IIEnv = nullptr;
+  if (AA) {
     DeclVersion = AA->getIntroduced();
+    IIEnv = AA->getEnvironment();
+  }
 
-  if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx,
+  if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx,
                                            OffendingDecl))
     return;
 
@@ -354,8 +379,7 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
 
   // The declaration can have multiple availability attributes, we are looking
   // at one of them.
-  const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl);
-  if (A && A->isInherited()) {
+  if (AA && AA->isInherited()) {
     for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl;
          Redecl = Redecl->getPreviousDecl()) {
       const AvailabilityAttr *AForRedecl =
@@ -375,9 +399,9 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
     // not specified for deployment targets >= to iOS 11 or equivalent or
     // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
     // later.
-    const AvailabilityAttr *AA =
-        getAttrForPlatform(S.getASTContext(), OffendingDecl);
+    assert(AA != nullptr && "expecting valid availability attribute");
     VersionTuple Introduced = AA->getIntroduced();
+    bool EnvironmentMatches = hasMatchingEnvironmentOrNone(S.getASTContext(), AA);
 
     bool UseNewWarning = shouldDiagnoseAvailabilityByDefault(
         S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
@@ -385,16 +409,25 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
     unsigned Warning = UseNewWarning ? diag::warn_unguarded_availability_new
                                      : diag::warn_unguarded_availability;
 
-    std::string PlatformName(AvailabilityAttr::getPrettyPlatformName(
-        S.getASTContext().getTargetInfo().getPlatformName()));
+    const TargetInfo &TI = S.getASTContext().getTargetInfo();
+    std::string PlatformName(
+        AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
+    std::string TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
+        TI.getTriple().getEnvironmentName()));
+    VersionTuple UseVersion =
+        EnvironmentMatches ? Introduced : TI.getTriple().getOSVersion();
+    bool UseEnvironment =
+        (AA->getEnvironment() != nullptr && !TargetEnvironment.empty());
 
     S.Diag(Loc, Warning) << OffendingDecl << PlatformName
-                         << Introduced.getAsString();
+                         << UseVersion.getAsString() << UseEnvironment
+                         << TargetEnvironment << !EnvironmentMatches;
 
-    S.Diag(OffendingDecl->getLocation(),
-           diag::note_partial_availability_specified_here)
-        << OffendingDecl << PlatformName << Introduced.getAsString()
-        << S.Context.getTargetInfo().getPlatformMinVersion().getAsString();
+    if (EnvironmentMatches)
+      S.Diag(OffendingDecl->getLocation(),
+             diag::note_partial_availability_specified_here)
+          << OffendingDecl << PlatformName << UseVersion.getAsString()
+          << S.Context.getTargetInfo().getPlatformMinVersion().getAsString();
 
     if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) {
       if (const auto *TD = dyn_cast<TagDecl>(Enclosing))
@@ -771,17 +804,22 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
 
     const AvailabilityAttr *AA =
       getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
+    bool EnvironmentMatches =
+        hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA);
     VersionTuple Introduced = AA->getIntroduced();
 
-    if (AvailabilityStack.back() >= Introduced)
+    if (EnvironmentMatches && AvailabilityStack.back() >= Introduced)
       return;
 
     // If the context of this function is less available than D, we should not
     // emit a diagnostic.
-    if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx,
+    if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, 
+                                             AA->getEnvironment(), Ctx,
                                              OffendingDecl))
       return;
 
+    // HEKOTA: use  different error message when !environmentMatches ?
+    // 
     // 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
@@ -793,18 +831,27 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
             ? diag::warn_unguarded_availability_new
             : diag::warn_unguarded_availability;
 
-    std::string PlatformName(AvailabilityAttr::getPrettyPlatformName(
-        SemaRef.getASTContext().getTargetInfo().getPlatformName()));
+    const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
+    std::string PlatformName(
+        AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
+    std::string TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
+        TI.getTriple().getEnvironmentName()));
+    VersionTuple UseVersion =
+        EnvironmentMatches ? Introduced : TI.getTriple().getOSVersion();
+    bool useEnvironment =
+        (AA->getEnvironment() != nullptr && !TargetEnvironment.empty());
 
     SemaRef.Diag(Range.getBegin(), DiagKind)
-        << Range << D << PlatformName << Introduced.getAsString();
-
-    SemaRef.Diag(OffendingDecl->getLocation(),
-                 diag::note_partial_availability_specified_here)
-        << OffendingDecl << PlatformName << Introduced.getAsString()
-        << SemaRef.Context.getTargetInfo()
-               .getPlatformMinVersion()
-               .getAsString();
+        << Range << D << PlatformName << UseVersion.getAsString()
+        << useEnvironment << TargetEnvironment << !EnvironmentMatches;
+
+    if (EnvironmentMatches)
+      SemaRef.Diag(OffendingDecl->getLocation(),
+                   diag::note_partial_availability_specified_here)
+          << OffendingDecl << PlatformName << Introduced.getAsString()
+          << SemaRef.Context.getTargetInfo()
+                 .getPlatformMinVersion()
+                 .getAsString();
 
     auto FixitDiag =
         SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index af6b3f21f15a6..a9188e69f50ee 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2916,7 +2916,7 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
         D, *AA, AA->getPlatform(), AA->isImplicit(), AA->getIntroduced(),
         AA->getDeprecated(), AA->getObsoleted(), AA->getUnavailable(),
         AA->getMessage(), AA->getStrict(), AA->getReplacement(), AMK,
-        AA->getPriority());
+        AA->getPriority(), AA->getEnvironment());
   else if (const auto *VA = dyn_cast<VisibilityAttr>(Attr))
     NewAttr = S.mergeVisibilityAttr(D, *VA, VA->getVisibility());
   else if (const auto *VA = dyn_cast<TypeVisibilityAttr>(Attr))
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 363ae93cb62df..b8f704155f793 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -26,6 +26,7 @@
 #include "clang/Basic/Cuda.h"
 #include "clang/Basic/DarwinSDKInfo.h"
 #include "clang/Basic/HLSLRuntime.h"
+#include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
@@ -51,6 +52,7 @@
 #include "llvm/Support/Error.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Triple.h"
 #include <optional>
 
 using namespace clang;
@@ -2494,7 +2496,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
     bool Implicit, VersionTuple Introduced, VersionTuple Deprecated,
     VersionTuple Obsoleted, bool IsUnavailable, StringRef Message,
     bool IsStrict, StringRef Replacement, AvailabilityMergeKind AMK,
-    int Priority) {
+    int Priority, IdentifierInfo *Environment) {
   VersionTuple MergedIntroduced = Introduced;
   VersionTuple MergedDeprecated = Deprecated;
   VersionTuple MergedObsoleted = Obsoleted;
@@ -2528,6 +2530,12 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
         continue;
       }
 
+      IdentifierInfo *OldEnvironment = OldAA->getEnvironment();
+      if (OldEnvironment != Environment) {
+        ++i;
+        continue;
+      }
+
       // If there is an existing availability attribute for this platform that
       // has a lower priority use the existing one and discard the new
       // attribute.
@@ -2646,7 +2654,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
       !OverrideOrImpl) {
     auto *Avail = ::new (Context) AvailabilityAttr(
         Context, CI, Platform, Introduced, Deprecated, Obsoleted, IsUnavailable,
-        Message, IsStrict, Replacement, Priority);
+        Message, IsStrict, Replacement, Priority, Environment);
     Avail->setImplicit(Implicit);
     return Avail;
   }
@@ -2708,10 +2716,22 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   int PriorityModifier = AL.isPragmaClangAttribute()
                              ? Sema::AP_PragmaClangAttribute
                              : Sema::AP_Explicit;
+
+  const IdentifierLoc *EnvironmentLoc = AL.getEnvironment();
+  IdentifierInfo *IIEnvironment = nullptr;
+  if (EnvironmentLoc) {
+    IIEnvironment = EnvironmentLoc->Ident;
+    if (AvailabilityAttr::getEnvironmentType(
+            EnvironmentLoc->Ident->getName()) ==
+        llvm::Triple::EnvironmentType::UnknownEnvironment)
+      S.Diag(Platform->Loc, diag::warn_availability_unknown_environment)
+          << EnvironmentLoc->Ident;
+  }
+
   AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(
       ND, AL, II, false /*Implicit*/, Introduced.Version, Deprecated.Version,
       Obsoleted.Version, IsUnavailable, Str, IsStrict, Replacement,
-      Sema::AMK_None, PriorityModifier);
+      Sema::AMK_None, PriorityModifier, IIEnvironment);
   if (NewAttr)
     D->addAttr(NewAttr);
 
@@ -2768,7 +2788,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
           ND, AL, NewII, true /*Implicit*/, NewIntroduced, NewDeprecated,
           NewObsoleted, IsUnavailable, Str, IsStrict, Replacement,
           Sema::AMK_None,
-          PriorityModifier + Sema::AP_InferredFromOtherPlatform);
+          PriorityModifier + Sema::AP_InferredFromOtherPlatform, IIEnvironment);
       if (NewAttr)
         D->addAttr(NewAttr);
     }
@@ -2810,7 +2830,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
           ND, AL, NewII, true /*Implicit*/, NewIntroduced, NewDeprecated,
           NewObsoleted, IsUnavailable, Str, IsStrict, Replacement,
           Sema::AMK_None,
-          PriorityModifier + Sema::AP_InferredFromOtherPlatform);
+          PriorityModifier + Sema::AP_InferredFromOtherPlatform, IIEnvironment);
       if (NewAttr)
         D->addAttr(NewAttr);
     }
@@ -2843,7 +2863,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
           MinMacCatalystVersion(Deprecated.Version),
           MinMacCatalystVersion(Obsoleted.Version), IsUnavailable, Str,
           IsStrict, Replacement, Sema::AMK_None,
-          PriorityModifier + Sema::AP_InferredFromOtherPlatform);
+          PriorityModifier + Sema::AP_InferredFromOtherPlatform, IIEnvironment);
       if (NewAttr)
         D->addAttr(NewAttr);
     } else if (II->getName() == "macos" && GetSDKInfo() &&
@@ -2886,7 +2906,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
               VersionOrEmptyVersion(NewObsoleted), /*IsUnavailable=*/false, Str,
               IsStrict, Replacement, Sema::AMK_None,
               PriorityModifier + Sema::AP_InferredFromOtherPlatform +
-                  Sema::AP_InferredFromOtherPlatform);
+                  Sema::AP_InferredFromOtherPlatform, IIEnvironment);
           if (NewAttr)
             D->addAttr(NewAttr);
         }

>From 36eb95468b95e3dd545f0eb7eb3eddfeb68db97e Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 23 Apr 2024 13:20:19 -0700
Subject: [PATCH 02/21] apply clang-format

---
 clang/include/clang/Sema/ParsedAttr.h    |  8 +++----
 clang/include/clang/Sema/Sema.h          | 16 ++++++--------
 clang/lib/AST/DeclBase.cpp               | 11 +++++-----
 clang/lib/Headers/hlsl/hlsl_intrinsics.h | 14 ++++++------
 clang/lib/Index/CommentToXML.cpp         |  2 +-
 clang/lib/Sema/SemaAvailability.cpp      | 27 ++++++++++++------------
 clang/lib/Sema/SemaDeclAttr.cpp          | 11 +++++-----
 7 files changed, 46 insertions(+), 43 deletions(-)

diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h
index 7ad39d1a4942f..a2b35e71795cd 100644
--- a/clang/include/clang/Sema/ParsedAttr.h
+++ b/clang/include/clang/Sema/ParsedAttr.h
@@ -73,10 +73,10 @@ struct AvailabilityData {
 
   AvailabilityData(const AvailabilityChange &Introduced,
                    const AvailabilityChange &Deprecated,
-                   const AvailabilityChange &Obsoleted,
-                   SourceLocation Strict, const Expr *ReplaceExpr,
-                   const IdentifierLoc *EnvironmentLoc)
-    : StrictLoc(Strict), Replacement(ReplaceExpr), EnvironmentLoc(EnvironmentLoc) {
+                   const AvailabilityChange &Obsoleted, SourceLocation Strict,
+                   const Expr *ReplaceExpr, const IdentifierLoc *EnvironmentLoc)
+      : StrictLoc(Strict), Replacement(ReplaceExpr),
+        EnvironmentLoc(EnvironmentLoc) {
     Changes[IntroducedSlot] = Introduced;
     Changes[DeprecatedSlot] = Deprecated;
     Changes[ObsoletedSlot] = Obsoleted;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f84c023d6f78e..64b06b42f17ab 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3619,15 +3619,13 @@ class Sema final : public SemaBase {
   bool CheckAttrTarget(const ParsedAttr &CurrAttr);
   bool CheckAttrNoArgs(const ParsedAttr &CurrAttr);
 
-  AvailabilityAttr *
-  mergeAvailabilityAttr(NamedDecl *D, const AttributeCommonInfo &CI,
-                        IdentifierInfo *Platform, bool Implicit,
-                        VersionTuple Introduced, VersionTuple Deprecated,
-                        VersionTuple Obsoleted, bool IsUnavailable,
-                        StringRef Message, bool IsStrict, StringRef Replacement,
-                        AvailabilityMergeKind AMK, int Priority,
-                        IdentifierInfo *IIEnvironment);
-  
+  AvailabilityAttr *mergeAvailabilityAttr(
+      NamedDecl *D, const AttributeCommonInfo &CI, IdentifierInfo *Platform,
+      bool Implicit, VersionTuple Introduced, VersionTuple Deprecated,
+      VersionTuple Obsoleted, bool IsUnavailable, StringRef Message,
+      bool IsStrict, StringRef Replacement, AvailabilityMergeKind AMK,
+      int Priority, IdentifierInfo *IIEnvironment);
+
   TypeVisibilityAttr *
   mergeTypeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI,
                           TypeVisibilityAttr::VisibilityType Vis);
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index fbb7f2b3ac30a..9b9e49ec7f761 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -667,7 +667,8 @@ static AvailabilityResult CheckAvailability(ASTContext &Context,
   if (!A->getIntroduced().empty() &&
       EnclosingVersion < A->getIntroduced()) {
     IdentifierInfo *IIEnv = A->getEnvironment();
-    StringRef TargetEnv = Context.getTargetInfo().getTriple().getEnvironmentName();
+    StringRef TargetEnv =
+        Context.getTargetInfo().getTriple().getEnvironmentName();
     StringRef EnvName = AvailabilityAttr::getPrettyEnviromentName(TargetEnv);
     // Matching environment or no environment on attribute
     if (!IIEnv || (!TargetEnv.empty() && IIEnv->getName() == TargetEnv)) {
@@ -675,8 +676,8 @@ static AvailabilityResult CheckAvailability(ASTContext &Context,
         Message->clear();
         llvm::raw_string_ostream Out(*Message);
         VersionTuple VTI(A->getIntroduced());
-        Out << "introduced in " << PrettyPlatformName << " "
-            << VTI << EnvName << HintMessage;
+        Out << "introduced in " << PrettyPlatformName << " " << VTI << EnvName
+            << HintMessage;
       }
     }
     // Non-matching environment or no environment on target
@@ -684,8 +685,8 @@ static AvailabilityResult CheckAvailability(ASTContext &Context,
       if (Message) {
         Message->clear();
         llvm::raw_string_ostream Out(*Message);
-        Out << "not available on " << PrettyPlatformName << " "
-            << EnvName << HintMessage;
+        Out << "not available on " << PrettyPlatformName << " " << EnvName
+            << HintMessage;
       }
     }
 
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index ca0582ef39a27..6880e26600d0b 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -18,16 +18,18 @@ namespace hlsl {
 
 #define _HLSL_BUILTIN_ALIAS(builtin)                                           \
   __attribute__((clang_builtin_alias(builtin)))
-#define _HLSL_AVAILABILITY(platform, version)                               \
+#define _HLSL_AVAILABILITY(platform, version)                                  \
   __attribute__((availability(platform, introduced = version)))
-#define _HLSL_AVAILABILITY_STAGE(platform, version, stage)                        \
-  __attribute__((availability(platform, introduced = version, environment = stage)))
+#define _HLSL_AVAILABILITY_STAGE(platform, version, stage)                     \
+  __attribute__((                                                              \
+      availability(platform, introduced = version, environment = stage)))
 
 #ifdef __HLSL_ENABLE_16_BIT
-#define _HLSL_16BIT_AVAILABILITY(platform, version)                         \
+#define _HLSL_16BIT_AVAILABILITY(platform, version)                            \
   __attribute__((availability(platform, introduced = version)))
-#define _HLSL_16BIT_AVAILABILITY_STAGE(platform, version, stage)                  \
-  __attribute__((availability(platform, introduced = version, environment = stage)))
+#define _HLSL_16BIT_AVAILABILITY_STAGE(platform, version, stage)               \
+  __attribute__((                                                              \
+      availability(platform, introduced = version, environment = stage)))
 #else
 #define _HLSL_16BIT_AVAILABILITY(environment, version)
 #define _HLSL_16BIT_AVAILABILITY_STAGE(environment, version, stage)
diff --git a/clang/lib/Index/CommentToXML.cpp b/clang/lib/Index/CommentToXML.cpp
index 5a484bdd15eaa..498476063f062 100644
--- a/clang/lib/Index/CommentToXML.cpp
+++ b/clang/lib/Index/CommentToXML.cpp
@@ -1054,7 +1054,7 @@ void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
         Result << "<Unavailable/>";
 
       // HEKOTA TODO add Environment here
-      
+
       Result << "</Availability>";
     }
   }
diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index 2a24cef17b469..c6b2ec6fa59be 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -26,17 +26,18 @@
 using namespace clang;
 using namespace sema;
 
-static bool hasMatchingEnvironmentOrNone(const ASTContext &Context, const AvailabilityAttr *AA) {
+static bool hasMatchingEnvironmentOrNone(const ASTContext &Context,
+                                         const AvailabilityAttr *AA) {
   IdentifierInfo *IIEnvironment = AA->getEnvironment();
   auto Environment = Context.getTargetInfo().getTriple().getEnvironment();
-  if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment) 
+  if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment)
     return true;
-  
+
   llvm::Triple::EnvironmentType ET =
       AvailabilityAttr::getEnvironmentType(IIEnvironment->getName());
   return Environment == ET;
 }
-  
+
 static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
                                                   const Decl *D) {
   AvailabilityAttr const *PartialMatch = nullptr;
@@ -137,11 +138,9 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
 /// whether we should emit a diagnostic for \c K and \c DeclVersion in
 /// the context of \c Ctx. For example, we should emit an unavailable diagnostic
 /// in a deprecated context, but not the other way around.
-static bool
-ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K, 
-                                    VersionTuple DeclVersion, 
-                                    const IdentifierInfo* DeclEnv, Decl *Ctx,
-                                    const NamedDecl *OffendingDecl) {
+static bool ShouldDiagnoseAvailabilityInContext(
+    Sema &S, AvailabilityResult K, VersionTuple DeclVersion,
+    const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) {
   assert(K != AR_Available && "Expected an unavailable declaration here!");
 
   // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic.
@@ -160,7 +159,8 @@ ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K,
   auto CheckContext = [&](const Decl *C) {
     if (K == AR_NotYetIntroduced) {
       if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))
-        if (AA->getIntroduced() >= DeclVersion && AA->getEnvironment() == DeclEnv )
+        if (AA->getIntroduced() >= DeclVersion &&
+            AA->getEnvironment() == DeclEnv)
           return true;
     } else if (K == AR_Deprecated) {
       if (C->isDeprecated())
@@ -401,7 +401,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
     // later.
     assert(AA != nullptr && "expecting valid availability attribute");
     VersionTuple Introduced = AA->getIntroduced();
-    bool EnvironmentMatches = hasMatchingEnvironmentOrNone(S.getASTContext(), AA);
+    bool EnvironmentMatches =
+        hasMatchingEnvironmentOrNone(S.getASTContext(), AA);
 
     bool UseNewWarning = shouldDiagnoseAvailabilityByDefault(
         S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
@@ -813,13 +814,13 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
 
     // If the context of this function is less available than D, we should not
     // emit a diagnostic.
-    if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, 
+    if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced,
                                              AA->getEnvironment(), Ctx,
                                              OffendingDecl))
       return;
 
     // HEKOTA: use  different error message when !environmentMatches ?
-    // 
+    //
     // 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
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index b8f704155f793..8a8e297261678 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2787,8 +2787,8 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
       AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(
           ND, AL, NewII, true /*Implicit*/, NewIntroduced, NewDeprecated,
           NewObsoleted, IsUnavailable, Str, IsStrict, Replacement,
-          Sema::AMK_None,
-          PriorityModifier + Sema::AP_InferredFromOtherPlatform, IIEnvironment);
+          Sema::AMK_None, PriorityModifier + Sema::AP_InferredFromOtherPlatform,
+          IIEnvironment);
       if (NewAttr)
         D->addAttr(NewAttr);
     }
@@ -2829,8 +2829,8 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
       AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(
           ND, AL, NewII, true /*Implicit*/, NewIntroduced, NewDeprecated,
           NewObsoleted, IsUnavailable, Str, IsStrict, Replacement,
-          Sema::AMK_None,
-          PriorityModifier + Sema::AP_InferredFromOtherPlatform, IIEnvironment);
+          Sema::AMK_None, PriorityModifier + Sema::AP_InferredFromOtherPlatform,
+          IIEnvironment);
       if (NewAttr)
         D->addAttr(NewAttr);
     }
@@ -2906,7 +2906,8 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
               VersionOrEmptyVersion(NewObsoleted), /*IsUnavailable=*/false, Str,
               IsStrict, Replacement, Sema::AMK_None,
               PriorityModifier + Sema::AP_InferredFromOtherPlatform +
-                  Sema::AP_InferredFromOtherPlatform, IIEnvironment);
+                  Sema::AP_InferredFromOtherPlatform,
+              IIEnvironment);
           if (NewAttr)
             D->addAttr(NewAttr);
         }

>From 0a66eb21674bfd68d70ce0424d77f17c7e4cce58 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 25 Apr 2024 16:57:32 -0700
Subject: [PATCH 03/21] Add tests and documentation, adjust diag messages

---
 clang/include/clang/Basic/Attr.td             |  2 +-
 clang/include/clang/Basic/AttrDocs.td         |  5 ++-
 .../clang/Basic/DiagnosticParseKinds.td       |  2 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +-
 clang/lib/Index/CommentToXML.cpp              |  7 ++-
 clang/lib/Sema/SemaAvailability.cpp           | 12 ++---
 clang/test/Parser/attr-availability.c         |  4 ++
 clang/test/SemaHLSL/AvailabilityMarkup.hlsl   |  8 ++--
 .../SemaHLSL/AvailabilityMarkupStages.hlsl    | 44 +++++++++++++++++++
 .../SemaHLSL/WaveBuiltinAvailability.hlsl     |  4 +-
 10 files changed, 72 insertions(+), 18 deletions(-)
 create mode 100644 clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index fb5b6d466c6ff..251a2cf04e37d 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -976,7 +976,7 @@ def Availability : InheritableAttr {
              .Case("xros", "visionOS")
              .Case("xros_app_extension", "visionOS (App Extension)")
              .Case("swift", "Swift")
-             .Case("shadermodel", "HLSL Shader Model")
+             .Case("shadermodel", "Shader Model")
              .Case("ohos", "OpenHarmony OS")
              .Default(llvm::StringRef());
 }
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index a81163df35ca8..9c2f3f9e833a5 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1593,7 +1593,10 @@ replacement=\ *string-literal*
   a warning about use of a deprecated declaration. The Fix-It will replace
   the deprecated declaration with the new declaration specified.
 
-// HEKOTA TODO add docs here
+environment=\ *identifier*
+  Target environment in which this declaration is available. If present, 
+  the availability attribute applies only to targets with the same platform
+  and environment. 
 
 Multiple availability attributes can be placed on a declaration, which may
 correspond to different platforms. For most platforms, the availability
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 624e7b0ecf272..31309b8cd7067 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1104,7 +1104,7 @@ def err_zero_version : Error<
 def err_availability_expected_platform : Error<
   "expected a platform name, e.g., 'macos'">;
 def err_availability_expected_environment : Error<
-  "expected an environment name, e.g., 'pixel'">;
+  "expected an environment name, e.g., 'compute'">;
 
 // objc_bridge_related attribute
 def err_objcbridge_related_expected_related_class : Error<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8ac94349d79aa..f3a9f1b90db59 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5860,7 +5860,7 @@ def note_availability_specified_here : Note<
   "%0 has been explicitly marked "
   "%select{unavailable|deleted|deprecated}1 here">;
 def note_partial_availability_specified_here : Note<
-  "%0 has been marked as being introduced in %1 %2 here, "
+  "%0 has been marked as being introduced in %1 %2 %select{|in %5 environment }4here, "
   "but the deployment target is %1 %3">;
 def note_implicitly_deleted : Note<
   "explicitly defaulted function was implicitly deleted here">;
diff --git a/clang/lib/Index/CommentToXML.cpp b/clang/lib/Index/CommentToXML.cpp
index 498476063f062..3372fbba43831 100644
--- a/clang/lib/Index/CommentToXML.cpp
+++ b/clang/lib/Index/CommentToXML.cpp
@@ -12,6 +12,7 @@
 #include "clang/AST/Comment.h"
 #include "clang/AST/CommentVisitor.h"
 #include "clang/Basic/FileManager.h"
+#include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Format/Format.h"
 #include "clang/Index/USRGeneration.h"
@@ -1053,8 +1054,10 @@ void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
       if (AA->getUnavailable())
         Result << "<Unavailable/>";
 
-      // HEKOTA TODO add Environment here
-
+      IdentifierInfo *Environment = AA->getEnvironment();
+      if (Environment) {
+        Result << "<Environment>" << Environment->getName() << "</Environment>";
+      }
       Result << "</Availability>";
     }
   }
diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index c6b2ec6fa59be..e0d3383bdfca9 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -428,7 +428,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
       S.Diag(OffendingDecl->getLocation(),
              diag::note_partial_availability_specified_here)
           << OffendingDecl << PlatformName << UseVersion.getAsString()
-          << S.Context.getTargetInfo().getPlatformMinVersion().getAsString();
+          << S.Context.getTargetInfo().getPlatformMinVersion().getAsString()
+          << UseEnvironment << TargetEnvironment;
 
     if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) {
       if (const auto *TD = dyn_cast<TagDecl>(Enclosing))
@@ -819,8 +820,6 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
                                              OffendingDecl))
       return;
 
-    // HEKOTA: use  different error message when !environmentMatches ?
-    //
     // 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
@@ -839,12 +838,12 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
         TI.getTriple().getEnvironmentName()));
     VersionTuple UseVersion =
         EnvironmentMatches ? Introduced : TI.getTriple().getOSVersion();
-    bool useEnvironment =
+    bool UseEnvironment =
         (AA->getEnvironment() != nullptr && !TargetEnvironment.empty());
 
     SemaRef.Diag(Range.getBegin(), DiagKind)
         << Range << D << PlatformName << UseVersion.getAsString()
-        << useEnvironment << TargetEnvironment << !EnvironmentMatches;
+        << UseEnvironment << TargetEnvironment << !EnvironmentMatches;
 
     if (EnvironmentMatches)
       SemaRef.Diag(OffendingDecl->getLocation(),
@@ -852,7 +851,8 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
           << OffendingDecl << PlatformName << Introduced.getAsString()
           << SemaRef.Context.getTargetInfo()
                  .getPlatformMinVersion()
-                 .getAsString();
+                 .getAsString()
+          << UseEnvironment << TargetEnvironment;
 
     auto FixitDiag =
         SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
diff --git a/clang/test/Parser/attr-availability.c b/clang/test/Parser/attr-availability.c
index aab0f2f3a852a..8ba120f944a73 100644
--- a/clang/test/Parser/attr-availability.c
+++ b/clang/test/Parser/attr-availability.c
@@ -30,6 +30,10 @@ void f11(void) __attribute__((availability(macosx,message=u"b"))); // expected-w
 
 void f12(void) __attribute__((availability(macosx,message="a" u"b"))); // expected-warning {{encoding prefix 'u' on an unevaluated string literal has no effect}}
 
+void f13(void) __attribute__((availability(shadermodel, introduced = 6.0, environment="pixel"))); // expected-error {{expected an environment name, e.g., 'compute'}}
+
+void f14(void) __attribute__((availability(shadermodel, introduced = 6.0, environment=pixel, environment=compute))); // expected-error {{redundant 'environment' availability change; only the last specified change will be used}}
+
 enum E{
     gorf __attribute__((availability(macosx,introduced=8.5, message = 10.0))), // expected-error {{expected string literal for optional message in 'availability' attribute}}
     garf __attribute__((availability(macosx,introduced=8.5, message))), // expected-error {{expected '=' after 'message'}}
diff --git a/clang/test/SemaHLSL/AvailabilityMarkup.hlsl b/clang/test/SemaHLSL/AvailabilityMarkup.hlsl
index b883957af0871..ece5ca9d3ea9f 100644
--- a/clang/test/SemaHLSL/AvailabilityMarkup.hlsl
+++ b/clang/test/SemaHLSL/AvailabilityMarkup.hlsl
@@ -10,13 +10,13 @@ __attribute__((availability(shadermodel, introduced = 5.0)))
 unsigned fn5_0();
 
 void fn() {
-    // expected-warning@#fn6_0_site {{'fn6_0' is only available on HLSL ShaderModel 6.0 or newer}}
-    // expected-note@#fn6_0 {{'fn6_0' has been marked as being introduced in HLSL ShaderModel 6.0 here, but the deployment target is HLSL ShaderModel 5.0}}
+    // expected-warning@#fn6_0_site {{'fn6_0' is only available on Shader Model 6.0 or newer}}
+    // expected-note@#fn6_0 {{'fn6_0' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
     // expected-note@#fn6_0_site {{enclose 'fn6_0' in a __builtin_available check to silence this warning}}
     unsigned A = fn6_0(); // #fn6_0_site
 
-    // expected-warning@#fn5_1_site {{'fn5_1' is only available on HLSL ShaderModel 5.1 or newer}}
-    // expected-note@#fn5_1 {{'fn5_1' has been marked as being introduced in HLSL ShaderModel 5.1 here, but the deployment target is HLSL ShaderModel 5.0}}
+    // expected-warning@#fn5_1_site {{'fn5_1' is only available on Shader Model 5.1 or newer}}
+    // expected-note@#fn5_1 {{'fn5_1' has been marked as being introduced in Shader Model 5.1 here, but the deployment target is Shader Model 5.0}}
     // expected-note@#fn5_1_site {{enclose 'fn5_1' in a __builtin_available check to silence this warning}}
     unsigned B = fn5_1(); // #fn5_1_site
 
diff --git a/clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl b/clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl
new file mode 100644
index 0000000000000..ae3c260242339
--- /dev/null
+++ b/clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -triple dxil-unknown-shadermodel5.0-compute -verify %s
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0)))
+unsigned fn6_0_all(); // #fn6_0_all_def
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 5.0)))
+unsigned fn5_0_all();
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = mesh)))
+unsigned fn6_0_stages1();  // #fn6_0_stages1_def
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned fn6_0_stages2(); // #fn6_0_stages2_def
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned fn5_0_stages();
+
+void fn() {
+
+    // expected-warning@#fn6_0_all_call {{'fn6_0_all' is only available on Shader Model 6.0 or newer}}
+    // expected-note@#fn6_0_all_def {{'fn6_0_all' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#fn6_0_all_call {{enclose 'fn6_0_all' in a __builtin_available check to silence this warning}}
+    unsigned A = fn6_0_all(); // #fn6_0_all_call
+
+    unsigned B = fn5_0_all();
+
+    // expected-warning@#fn6_0_stages1_call {{'fn6_0_stages1' is only available in compute shader environment on Shader Model 6.0 or newer}}
+    // expected-note@#fn6_0_stages1_def {{'fn6_0_stages1' has been marked as being introduced in Shader Model 6.0 in compute shader environment here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#fn6_0_stages1_call {{enclose 'fn6_0_stages1' in a __builtin_available check to silence this warning}}
+    unsigned C = fn6_0_stages1(); // #fn6_0_stages1_call
+
+    // expected-warning@#fn6_0_stages2_call {{'fn6_0_stages2' is not available in compute shader environment on Shader Model 5.0}}
+    // expected-note@#fn6_0_stages2_call {{enclose 'fn6_0_stages2' in a __builtin_available check to silence this warning}}
+    unsigned E = fn6_0_stages2(); // #fn6_0_stages2_call
+
+    unsigned D = fn5_0_stages();
+}
diff --git a/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl b/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
index 0e45edc6a4c86..185b79be37be5 100644
--- a/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
+++ b/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
@@ -2,8 +2,8 @@
 // WaveActiveCountBits is unavailable before ShaderModel 6.0.
 
 unsigned foo(bool b) {
-    // expected-warning@#site {{'WaveActiveCountBits' is only available on HLSL ShaderModel 6.0 or newer}}
-    // expected-note at hlsl/hlsl_intrinsics.h:* {{'WaveActiveCountBits' has been marked as being introduced in HLSL ShaderModel 6.0 here, but the deployment target is HLSL ShaderModel 5.0}}
+    // expected-warning@#site {{'WaveActiveCountBits' is only available on Shader Model 6.0 or newer}}
+    // expected-note at hlsl/hlsl_intrinsics.h:* {{'WaveActiveCountBits' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
     // expected-note@#site {{enclose 'WaveActiveCountBits' in a __builtin_available check to silence this warning}}
     return hlsl::WaveActiveCountBits(b); // #site
 }

>From eac0b91459cb2bcac0fc0e071bb1294171e2af14 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 30 Apr 2024 12:52:27 -0700
Subject: [PATCH 04/21] Remove trailing whitespace

---
 clang/include/clang/Basic/AttrDocs.td | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 9c2f3f9e833a5..dbadaee7e0c4e 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1594,9 +1594,9 @@ replacement=\ *string-literal*
   the deprecated declaration with the new declaration specified.
 
 environment=\ *identifier*
-  Target environment in which this declaration is available. If present, 
+  Target environment in which this declaration is available. If present,
   the availability attribute applies only to targets with the same platform
-  and environment. 
+  and environment.
 
 Multiple availability attributes can be placed on a declaration, which may
 correspond to different platforms. For most platforms, the availability

>From 65052906a9e01b80cc4cfba8b89d6e45ecc29537 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 7 May 2024 15:55:11 -0700
Subject: [PATCH 05/21] Modify diagnostic message when function is not
 available for target environment for any version

---
 .../clang/Basic/DiagnosticSemaKinds.td        | 10 +-
 clang/lib/Sema/SemaAvailability.cpp           | 98 +++++++++++--------
 .../SemaHLSL/AvailabilityMarkupStages.hlsl    |  3 +-
 3 files changed, 66 insertions(+), 45 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index bd45b38364bb6..35210fea07b1a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3862,11 +3862,17 @@ def warn_availability_fuchsia_unavailable_minor : Warning<
   InGroup<Availability>;
 
 def warn_unguarded_availability :
-  Warning<"%0 %select{is only|is not}5 available %select{|in %4 environment }3on %1 %2 %select{or newer|}5">,
+  Warning<"%0 is only available %select{|in %4 environment }3on %1 %2 or newer">,
+  InGroup<UnguardedAvailability>, DefaultIgnore;
+def warn_unguarded_availability_unavailable :
+  Warning<"%0 is unavailable">,
   InGroup<UnguardedAvailability>, DefaultIgnore;
 def warn_unguarded_availability_new :
   Warning<warn_unguarded_availability.Summary>,
   InGroup<UnguardedAvailabilityNew>;
+def warn_unguarded_availability_unavailable_new :
+  Warning<warn_unguarded_availability_unavailable.Summary>,
+  InGroup<UnguardedAvailabilityNew>;
 def note_decl_unguarded_availability_silence : Note<
   "annotate %select{%1|anonymous %1}0 with an availability attribute to silence this warning">;
 def note_unguarded_available_silence : Note<
@@ -5859,7 +5865,7 @@ def note_availability_specified_here : Note<
   "%select{unavailable|deleted|deprecated}1 here">;
 def note_partial_availability_specified_here : Note<
   "%0 has been marked as being introduced in %1 %2 %select{|in %5 environment }4here, "
-  "but the deployment target is %1 %3">;
+  "but the deployment target is %1 %3%select{| %6 environment }4">;
 def note_implicitly_deleted : Note<
   "explicitly defaulted function was implicitly deleted here">;
 def warn_not_enough_argument : Warning<
diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index e0d3383bdfca9..fb2e4f48d93e6 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -401,35 +401,44 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
     // later.
     assert(AA != nullptr && "expecting valid availability attribute");
     VersionTuple Introduced = AA->getIntroduced();
-    bool EnvironmentMatches =
+    bool EnvironmentMatchesOrNone =
         hasMatchingEnvironmentOrNone(S.getASTContext(), AA);
 
-    bool UseNewWarning = shouldDiagnoseAvailabilityByDefault(
-        S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
-        Introduced);
-    unsigned Warning = UseNewWarning ? diag::warn_unguarded_availability_new
-                                     : diag::warn_unguarded_availability;
-
     const TargetInfo &TI = S.getASTContext().getTargetInfo();
     std::string PlatformName(
         AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
-    std::string TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
+    llvm::StringRef TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
         TI.getTriple().getEnvironmentName()));
-    VersionTuple UseVersion =
-        EnvironmentMatches ? Introduced : TI.getTriple().getOSVersion();
+    llvm::StringRef AttrEnvironment =
+        AA->getEnvironment() ? AvailabilityAttr::getPrettyEnviromentName(
+                                   AA->getEnvironment()->getName())
+                             : "";
     bool UseEnvironment =
-        (AA->getEnvironment() != nullptr && !TargetEnvironment.empty());
+        (!AttrEnvironment.empty() && !TargetEnvironment.empty());
+
+    bool UseNewWarning = shouldDiagnoseAvailabilityByDefault(
+        S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
+        Introduced);
 
-    S.Diag(Loc, Warning) << OffendingDecl << PlatformName
-                         << UseVersion.getAsString() << UseEnvironment
-                         << TargetEnvironment << !EnvironmentMatches;
+    if (EnvironmentMatchesOrNone) {
+      unsigned DiagKind = UseNewWarning ? diag::warn_unguarded_availability_new
+                                        : diag::warn_unguarded_availability;
 
-    if (EnvironmentMatches)
-      S.Diag(OffendingDecl->getLocation(),
-             diag::note_partial_availability_specified_here)
-          << OffendingDecl << PlatformName << UseVersion.getAsString()
-          << S.Context.getTargetInfo().getPlatformMinVersion().getAsString()
+      S.Diag(Loc, DiagKind)
+          << OffendingDecl << PlatformName << Introduced.getAsString()
           << UseEnvironment << TargetEnvironment;
+    } else {
+      unsigned DiagKind =
+          UseNewWarning ? diag::warn_unguarded_availability_unavailable_new
+                        : diag::warn_unguarded_availability_unavailable;
+      S.Diag(Loc, DiagKind) << Loc << OffendingDecl;
+    }
+
+    S.Diag(OffendingDecl->getLocation(),
+           diag::note_partial_availability_specified_here)
+        << OffendingDecl << PlatformName << Introduced.getAsString()
+        << S.Context.getTargetInfo().getPlatformMinVersion().getAsString()
+        << UseEnvironment << AttrEnvironment << TargetEnvironment;
 
     if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) {
       if (const auto *TD = dyn_cast<TagDecl>(Enclosing))
@@ -806,11 +815,11 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
 
     const AvailabilityAttr *AA =
       getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
-    bool EnvironmentMatches =
+    bool EnvironmentMatchesOrNone =
         hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA);
     VersionTuple Introduced = AA->getIntroduced();
 
-    if (EnvironmentMatches && AvailabilityStack.back() >= Introduced)
+    if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced)
       return;
 
     // If the context of this function is less available than D, we should not
@@ -824,35 +833,40 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
     // not specified for deployment targets >= to iOS 11 or equivalent or
     // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
     // later.
-    unsigned DiagKind =
-        shouldDiagnoseAvailabilityByDefault(
-            SemaRef.Context,
-            SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced)
-            ? diag::warn_unguarded_availability_new
-            : diag::warn_unguarded_availability;
+    bool UseNewDiagKind = shouldDiagnoseAvailabilityByDefault(
+        SemaRef.Context,
+        SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced);
 
     const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
     std::string PlatformName(
         AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
-    std::string TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
+    llvm::StringRef TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
         TI.getTriple().getEnvironmentName()));
-    VersionTuple UseVersion =
-        EnvironmentMatches ? Introduced : TI.getTriple().getOSVersion();
+    llvm::StringRef AttrEnvironment =
+        AA->getEnvironment() ? AvailabilityAttr::getPrettyEnviromentName(
+                                   AA->getEnvironment()->getName())
+                             : "";
     bool UseEnvironment =
-        (AA->getEnvironment() != nullptr && !TargetEnvironment.empty());
-
-    SemaRef.Diag(Range.getBegin(), DiagKind)
-        << Range << D << PlatformName << UseVersion.getAsString()
-        << UseEnvironment << TargetEnvironment << !EnvironmentMatches;
+        (!AttrEnvironment.empty() && !TargetEnvironment.empty());
 
-    if (EnvironmentMatches)
-      SemaRef.Diag(OffendingDecl->getLocation(),
-                   diag::note_partial_availability_specified_here)
-          << OffendingDecl << PlatformName << Introduced.getAsString()
-          << SemaRef.Context.getTargetInfo()
-                 .getPlatformMinVersion()
-                 .getAsString()
+    if (EnvironmentMatchesOrNone) {
+      unsigned DiagKind = UseNewDiagKind ? diag::warn_unguarded_availability_new
+                                         : diag::warn_unguarded_availability;
+      SemaRef.Diag(Range.getBegin(), DiagKind)
+          << Range << D << PlatformName << Introduced.getAsString()
           << UseEnvironment << TargetEnvironment;
+    } else {
+      unsigned DiagKind =
+          UseNewDiagKind ? diag::warn_unguarded_availability_unavailable_new
+                         : diag::warn_unguarded_availability_unavailable;
+      SemaRef.Diag(Range.getBegin(), DiagKind) << Range << D;
+    }
+
+    SemaRef.Diag(OffendingDecl->getLocation(),
+                 diag::note_partial_availability_specified_here)
+        << OffendingDecl << PlatformName << Introduced.getAsString()
+        << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString()
+        << UseEnvironment << AttrEnvironment << TargetEnvironment;
 
     auto FixitDiag =
         SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
diff --git a/clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl b/clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl
index ae3c260242339..3572cd85dd4ef 100644
--- a/clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl
+++ b/clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl
@@ -36,7 +36,8 @@ void fn() {
     // expected-note@#fn6_0_stages1_call {{enclose 'fn6_0_stages1' in a __builtin_available check to silence this warning}}
     unsigned C = fn6_0_stages1(); // #fn6_0_stages1_call
 
-    // expected-warning@#fn6_0_stages2_call {{'fn6_0_stages2' is not available in compute shader environment on Shader Model 5.0}}
+    // expected-warning@#fn6_0_stages2_call {{'fn6_0_stages2' is unavailable}}
+    // expected-note@#fn6_0_stages2_def {{'fn6_0_stages2' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 compute shader environment}}
     // expected-note@#fn6_0_stages2_call {{enclose 'fn6_0_stages2' in a __builtin_available check to silence this warning}}
     unsigned E = fn6_0_stages2(); // #fn6_0_stages2_call
 

>From 22d2d24c927a867a3a9a93c1f754b61a9baa299e Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 8 May 2024 14:26:13 -0700
Subject: [PATCH 06/21] Move tests into Sema, add coverage for strict flag

- merge AvailabilityMarkup.hlsl and AvailabilityMarkupStages.hlsl into attr-availability-shadermodel.hlsl and move to Sema test directory
- add coverage for strict mode which uses different code path to generate the warnings
- add missing space in diag message
---
 clang/lib/AST/DeclBase.cpp                    |   4 +-
 .../Sema/attr-availability-shadermodel.hlsl   | 139 ++++++++++++++++++
 clang/test/SemaHLSL/AvailabilityMarkup.hlsl   |  25 ----
 .../SemaHLSL/AvailabilityMarkupStages.hlsl    |  45 ------
 4 files changed, 141 insertions(+), 72 deletions(-)
 create mode 100644 clang/test/Sema/attr-availability-shadermodel.hlsl
 delete mode 100644 clang/test/SemaHLSL/AvailabilityMarkup.hlsl
 delete mode 100644 clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl

diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index f769c9ac277a7..65d5eeb6354eb 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -676,8 +676,8 @@ static AvailabilityResult CheckAvailability(ASTContext &Context,
         Message->clear();
         llvm::raw_string_ostream Out(*Message);
         VersionTuple VTI(A->getIntroduced());
-        Out << "introduced in " << PrettyPlatformName << " " << VTI << EnvName
-            << HintMessage;
+        Out << "introduced in " << PrettyPlatformName << " " << VTI << " "
+            << EnvName << HintMessage;
       }
     }
     // Non-matching environment or no environment on target
diff --git a/clang/test/Sema/attr-availability-shadermodel.hlsl b/clang/test/Sema/attr-availability-shadermodel.hlsl
new file mode 100644
index 0000000000000..ef896e99a3c66
--- /dev/null
+++ b/clang/test/Sema/attr-availability-shadermodel.hlsl
@@ -0,0 +1,139 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel5.0-compute -fsyntax-only -verify %s
+
+// Platform shader model, no environment parameter
+__attribute__((availability(shadermodel, introduced = 6.0)))
+unsigned fn6_0(); // #fn6_0
+
+__attribute__((availability(shadermodel, introduced = 5.1)))
+unsigned fn5_1(); // #fn5_1
+
+__attribute__((availability(shadermodel, introduced = 5.0)))
+unsigned fn5_0();
+
+// Platform shader model, no environment parameter
+// - with strict flag (executes different code path)
+__attribute__((availability(shadermodel, strict, introduced = 6.0)))
+unsigned fn6_0_s(); // #fn6_0_s
+
+__attribute__((availability(shadermodel, strict, introduced = 5.1)))
+unsigned fn5_1_s(); // #fn5_1_s
+
+__attribute__((availability(shadermodel, strict, introduced = 5.0)))
+unsigned fn5_0_s();
+
+// Platform shader model, environment parameter restricting earlier version,
+// available in all environments in higher versions
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0)))
+unsigned fn6_0_mix(); // #fn6_0_mix
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 5.0)))
+unsigned fn5_0_mix();
+
+// Platform shader model, environment parameter restricting earlier version,
+// available in all environments in higher versions
+// - with strict flag (executes different code path)
+__attribute__((availability(shadermodel, strict, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, strict, introduced = 6.0)))
+unsigned fn6_0_mix_s(); // #fn6_0_mix_s
+
+__attribute__((availability(shadermodel, strict, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, strict, introduced = 5.0)))
+unsigned fn5_0_mix_s();
+
+// Platform shader model, environment parameter restricting earlier version,
+// never available in all environments in higher versions
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = mesh)))
+unsigned fn6_0_stages1();  // #fn6_0_stages1
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned fn6_0_stages2(); // #fn6_0_stages2
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned fn5_0_stages();
+
+// Platform shader model, environment parameter restricting earlier version,
+// never available in all environments in higher versions
+// - with strict flag (executes different code path)
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = mesh)))
+unsigned fn6_0_stages1_s();  // #fn6_0_stages1_s
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned fn6_0_stages2_s(); // #fn6_0_stages2_s
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned fn5_0_stages_s();
+
+int main() {
+    // expected-warning@#fn6_0_call {{'fn6_0' is only available on Shader Model 6.0 or newer}}
+    // expected-note@#fn6_0 {{'fn6_0' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#fn6_0_call {{enclose 'fn6_0' in a __builtin_available check to silence this warning}}
+    unsigned A1 = fn6_0(); // #fn6_0_call
+
+    // expected-warning@#fn5_1_call {{'fn5_1' is only available on Shader Model 5.1 or newer}}
+    // expected-note@#fn5_1 {{'fn5_1' has been marked as being introduced in Shader Model 5.1 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#fn5_1_call {{enclose 'fn5_1' in a __builtin_available check to silence this warning}}
+    unsigned B1 = fn5_1(); // #fn5_1_call
+
+    unsigned C1 = fn5_0();
+
+    // expected-error@#fn6_0_s_call {{'fn6_0_s' is unavailable: introduced in Shader Model 6.0 compute shader}}
+    // expected-note@#fn6_0_s {{'fn6_0_s' has been explicitly marked unavailable here}}
+    unsigned A2 = fn6_0_s(); // #fn6_0_s_call
+
+    // expected-error@#fn5_1_s_call {{'fn5_1_s' is unavailable: introduced in Shader Model 5.1 compute shader}}
+    // expected-note@#fn5_1_s {{'fn5_1_s' has been explicitly marked unavailable here}}
+    unsigned B2 = fn5_1_s(); // #fn5_1_s_call
+
+    unsigned C2 = fn5_0_s();
+
+    // expected-warning@#fn6_0_mix_call {{'fn6_0_mix' is only available on Shader Model 6.0 or newer}}
+    // expected-note@#fn6_0_mix {{'fn6_0_mix' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#fn6_0_mix_call {{enclose 'fn6_0_mix' in a __builtin_available check to silence this warning}}
+    unsigned A3 = fn6_0_mix(); // #fn6_0_mix_call
+
+    unsigned B3 = fn5_0_mix();
+
+    // expected-error@#fn6_0_mix_s_call {{'fn6_0_mix_s' is unavailable: introduced in Shader Model 6.0 compute shader}}
+    // expected-note@#fn6_0_mix_s {{'fn6_0_mix_s' has been explicitly marked unavailable here}}
+    unsigned A4 = fn6_0_mix_s(); // #fn6_0_mix_s_call
+
+    unsigned B4 = fn5_0_mix_s();
+
+    // expected-warning@#fn6_0_stages1_call {{'fn6_0_stages1' is only available in compute shader environment on Shader Model 6.0 or newer}}
+    // expected-note@#fn6_0_stages1 {{'fn6_0_stages1' has been marked as being introduced in Shader Model 6.0 in compute shader environment here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#fn6_0_stages1_call {{enclose 'fn6_0_stages1' in a __builtin_available check to silence this warning}}
+    unsigned A5 = fn6_0_stages1(); // #fn6_0_stages1_call
+
+    // expected-warning@#fn6_0_stages2_call {{'fn6_0_stages2' is unavailable}}
+    // expected-note@#fn6_0_stages2 {{'fn6_0_stages2' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 compute shader environment}}
+    // expected-note@#fn6_0_stages2_call {{enclose 'fn6_0_stages2' in a __builtin_available check to silence this warning}}
+    unsigned B5 = fn6_0_stages2(); // #fn6_0_stages2_call
+
+    unsigned C5 = fn5_0_stages();
+
+    // expected-warning@#fn6_0_stages1_s_call {{'fn6_0_stages1_s' is only available in compute shader environment on Shader Model 6.0 or newer}}
+    // expected-note@#fn6_0_stages1_s {{'fn6_0_stages1_s' has been marked as being introduced in Shader Model 6.0 in compute shader environment here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#fn6_0_stages1_s_call {{enclose 'fn6_0_stages1_s' in a __builtin_available check to silence this warning}}
+    unsigned A6 = fn6_0_stages1_s(); // #fn6_0_stages1_s_call
+
+    // expected-warning@#fn6_0_stages2_s_call {{'fn6_0_stages2_s' is unavailable}}
+    // expected-note@#fn6_0_stages2_s {{'fn6_0_stages2_s' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 compute shader environment}}
+    // expected-note@#fn6_0_stages2_s_call {{enclose 'fn6_0_stages2_s' in a __builtin_available check to silence this warning}}
+    unsigned B6 = fn6_0_stages2_s(); // #fn6_0_stages2_s_call
+
+    unsigned C6 = fn5_0_stages_s();
+
+    return 0;
+}
diff --git a/clang/test/SemaHLSL/AvailabilityMarkup.hlsl b/clang/test/SemaHLSL/AvailabilityMarkup.hlsl
deleted file mode 100644
index ece5ca9d3ea9f..0000000000000
--- a/clang/test/SemaHLSL/AvailabilityMarkup.hlsl
+++ /dev/null
@@ -1,25 +0,0 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel5.0-library -verify %s
-
-__attribute__((availability(shadermodel, introduced = 6.0)))
-unsigned fn6_0(); // #fn6_0
-
-__attribute__((availability(shadermodel, introduced = 5.1)))
-unsigned fn5_1(); // #fn5_1
-
-__attribute__((availability(shadermodel, introduced = 5.0)))
-unsigned fn5_0();
-
-void fn() {
-    // expected-warning@#fn6_0_site {{'fn6_0' is only available on Shader Model 6.0 or newer}}
-    // expected-note@#fn6_0 {{'fn6_0' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn6_0_site {{enclose 'fn6_0' in a __builtin_available check to silence this warning}}
-    unsigned A = fn6_0(); // #fn6_0_site
-
-    // expected-warning@#fn5_1_site {{'fn5_1' is only available on Shader Model 5.1 or newer}}
-    // expected-note@#fn5_1 {{'fn5_1' has been marked as being introduced in Shader Model 5.1 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn5_1_site {{enclose 'fn5_1' in a __builtin_available check to silence this warning}}
-    unsigned B = fn5_1(); // #fn5_1_site
-
-    unsigned C = fn5_0();
-}
-
diff --git a/clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl b/clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl
deleted file mode 100644
index 3572cd85dd4ef..0000000000000
--- a/clang/test/SemaHLSL/AvailabilityMarkupStages.hlsl
+++ /dev/null
@@ -1,45 +0,0 @@
-// RUN: %clang_cc1 -triple dxil-unknown-shadermodel5.0-compute -verify %s
-
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 6.0)))
-unsigned fn6_0_all(); // #fn6_0_all_def
-
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 5.0)))
-unsigned fn5_0_all();
-
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = compute)))
-__attribute__((availability(shadermodel, introduced = 5.0, environment = mesh)))
-unsigned fn6_0_stages1();  // #fn6_0_stages1_def
-
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
-unsigned fn6_0_stages2(); // #fn6_0_stages2_def
-
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 5.0, environment = compute)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
-unsigned fn5_0_stages();
-
-void fn() {
-
-    // expected-warning@#fn6_0_all_call {{'fn6_0_all' is only available on Shader Model 6.0 or newer}}
-    // expected-note@#fn6_0_all_def {{'fn6_0_all' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn6_0_all_call {{enclose 'fn6_0_all' in a __builtin_available check to silence this warning}}
-    unsigned A = fn6_0_all(); // #fn6_0_all_call
-
-    unsigned B = fn5_0_all();
-
-    // expected-warning@#fn6_0_stages1_call {{'fn6_0_stages1' is only available in compute shader environment on Shader Model 6.0 or newer}}
-    // expected-note@#fn6_0_stages1_def {{'fn6_0_stages1' has been marked as being introduced in Shader Model 6.0 in compute shader environment here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn6_0_stages1_call {{enclose 'fn6_0_stages1' in a __builtin_available check to silence this warning}}
-    unsigned C = fn6_0_stages1(); // #fn6_0_stages1_call
-
-    // expected-warning@#fn6_0_stages2_call {{'fn6_0_stages2' is unavailable}}
-    // expected-note@#fn6_0_stages2_def {{'fn6_0_stages2' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 compute shader environment}}
-    // expected-note@#fn6_0_stages2_call {{enclose 'fn6_0_stages2' in a __builtin_available check to silence this warning}}
-    unsigned E = fn6_0_stages2(); // #fn6_0_stages2_call
-
-    unsigned D = fn5_0_stages();
-}

>From 66ad8f1470c769cae4507e8021bfb42f058fd787 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 9 May 2024 16:52:14 -0700
Subject: [PATCH 07/21] Fix test - add numthreads attribute

---
 clang/test/Sema/attr-availability-shadermodel.hlsl | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/test/Sema/attr-availability-shadermodel.hlsl b/clang/test/Sema/attr-availability-shadermodel.hlsl
index ef896e99a3c66..3339656354f29 100644
--- a/clang/test/Sema/attr-availability-shadermodel.hlsl
+++ b/clang/test/Sema/attr-availability-shadermodel.hlsl
@@ -75,6 +75,7 @@ __attribute__((availability(shadermodel, introduced = 5.0, environment = compute
 __attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
 unsigned fn5_0_stages_s();
 
+[numthreads(4,1,1)]
 int main() {
     // expected-warning@#fn6_0_call {{'fn6_0' is only available on Shader Model 6.0 or newer}}
     // expected-note@#fn6_0 {{'fn6_0' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}

>From 47dc8b7fb5d9f32fc3de61a2cd7e54b9fe6627fa Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 7 May 2024 12:26:02 -0700
Subject: [PATCH 08/21] Default and relaxed mode implementation (WIP)

---
 clang/include/clang/Basic/DiagnosticGroups.td |   3 +
 .../clang/Basic/DiagnosticSemaKinds.td        |   4 +
 clang/include/clang/Sema/SemaHLSL.h           |   1 +
 clang/lib/Sema/Sema.cpp                       |   3 +
 clang/lib/Sema/SemaAvailability.cpp           |   3 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 177 ++++++++++++++++++
 6 files changed, 190 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 2beb1d45124b4..ae78b5a7ee3f5 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1513,6 +1513,9 @@ def HLSLMixPackOffset : DiagGroup<"mix-packoffset">;
 // Warnings for DXIL validation
 def DXILValidation : DiagGroup<"dxil-validation">;
 
+// Warning for HLSL availability
+def HLSLAvailability : DiagGroup<"hlsl-availability">;
+
 // Warnings and notes related to const_var_decl_type attribute checks
 def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">;
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 01c0e9f22d5e5..da454c1e03a32 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12210,6 +12210,10 @@ def err_hlsl_param_qualifier_mismatch :
 def warn_hlsl_impcast_vector_truncation : Warning<
   "implicit conversion truncates vector: %0 to %1">, InGroup<Conversion>;
 
+def warn_hlsl_availability : Warning<
+  "%0 %select{is only|is not}5 available %select{|in %4 environment }3on %1 %2 %select{or newer|}5">,
+  InGroup<HLSLAvailability>, DefaultError;
+
 // Layout randomization diagnostics.
 def err_non_designated_init_used : Error<
   "a randomized struct can only be initialized with a designated initializer">;
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 34acaf19517f2..eac1f7c07c85d 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -49,6 +49,7 @@ class SemaHLSL : public SemaBase {
   void DiagnoseAttrStageMismatch(
       const Attr *A, HLSLShaderAttr::ShaderType Stage,
       std::initializer_list<HLSLShaderAttr::ShaderType> AllowedStages);
+  void DiagnoseAvailabilityViolations(TranslationUnitDecl *TU);
 };
 
 } // namespace clang
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index a1e32d391ed0c..03390228ca112 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1351,6 +1351,9 @@ void Sema::ActOnEndOfTranslationUnit() {
     Consumer.CompleteExternalDeclaration(D);
   }
 
+  if (LangOpts.HLSL)
+    HLSL().DiagnoseAvailabilityViolations(getASTContext().getTranslationUnitDecl());
+
   // If there were errors, disable 'unused' warnings since they will mostly be
   // noise. Don't warn for a use from a module: either we should warn on all
   // file-scope declarations in modules or not at all, but whether the
diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index fb2e4f48d93e6..fdc8d310ae865 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -228,7 +228,8 @@ shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
     break;
   case llvm::Triple::ShaderModel:
     // Always enable availability diagnostics for shader models.
-    return true;
+    // HEKOTA TODO
+    return false;
   default:
     // New targets should always warn about availability.
     return Triple.getVendor() == llvm::Triple::Apple;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 6a12c417e2f3a..7393d9a05eaeb 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -9,6 +9,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Sema/SemaHLSL.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Basic/DiagnosticSema.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/TargetInfo.h"
@@ -16,6 +19,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/TargetParser/Triple.h"
 #include <iterator>
@@ -290,3 +294,176 @@ void SemaHLSL::DiagnoseAttrStageMismatch(
       << A << HLSLShaderAttr::ConvertShaderTypeToStr(Stage)
       << (AllowedStages.size() != 1) << join(StageStrings, ", ");
 }
+
+namespace {
+
+/// HEKOTA TODO: UDPATE
+/// This class implements HLSL availability diagnostics
+///
+/// This is done by traversing all CallExpr nodes that are reachable from exported functions (either library exports or entry functions).
+/// If the callee of an CallExpr is in HLSL namespace and has availability annotation that signifies that the API is unavailable 
+/// for the target shader model and stage, the compiler emits an error in default or strict diagnostic mode (-fhlsl-strict-diagnostics)
+/// or a warning in relaxed mode (-Wno-error=hlsl-availability).
+class DiagnoseHLSLAvailability : public RecursiveASTVisitor<DiagnoseHLSLAvailability> {
+  // HEKOTAS this is probably not needed
+  // typedef RecursiveASTVisitor<DiagnoseHLSLAvailability> Base;
+
+  Sema &SemaRef;
+
+  // Stack of functions to be scaned
+  llvm::SmallVector<const FunctionDecl *, 8> DeclsToScan;
+  // Set of functions already scaned
+  llvm::SmallPtrSet<const FunctionDecl *, 8> ScannedDecls;
+
+  void HandleFunctionOrMethodRef(FunctionDecl *FD, Expr *RefExpr);
+  void CheckDeclAvailability(NamedDecl *D, SourceRange Range);
+  const AvailabilityAttr *FindAvailabilityAttr(const Decl *D);
+  bool HasMatchingEnvironmentOrNone(const AvailabilityAttr *AA);
+
+public:
+  DiagnoseHLSLAvailability(Sema &SemaRef) : SemaRef(SemaRef) {}
+
+  void RunOnTranslationUnit(const TranslationUnitDecl *TU);
+
+  bool VisitDeclRefExpr(DeclRefExpr *DRE) {
+    FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(DRE->getDecl());
+    if (FD)
+      HandleFunctionOrMethodRef(FD, DRE);
+    return true;
+  }
+
+  bool VisitMemberExpr(MemberExpr *ME) {
+    FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(ME->getMemberDecl());
+    if (FD)
+      HandleFunctionOrMethodRef(FD, ME);
+    return true;
+  }
+
+  // HEKOTA what is this?
+  //bool VisitTypeLoc(TypeLoc Ty);
+};
+
+void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD, Expr *RefExpr) {
+  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 (!ScannedDecls.contains(FDWithBody))
+      DeclsToScan.push_back(FDWithBody);
+    return;
+  }
+
+  // no definition -> diagnose availability
+  CheckDeclAvailability(FD, SourceRange(RefExpr->getBeginLoc(), RefExpr->getEndLoc()));
+}
+
+void DiagnoseHLSLAvailability::RunOnTranslationUnit(const TranslationUnitDecl *TU) {
+  // Add all shader entry functions and library exports to the stack
+  // of functions to be scanned
+  for (auto &D : TU->decls()) {
+    const FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(D);
+    // HEKOTA TODO detect also library exports
+    if (!FD || !FD->hasAttr<HLSLShaderAttr>())
+      continue;
+
+    DeclsToScan.push_back(FD);
+  }
+
+  while (!DeclsToScan.empty()) {
+    // Take one decl from the stack and check it by traversing its AST.
+    // For any CallExpr found during the traversal add it's callee to the top of the stack 
+    // to be processed next. Functions already processed are stored in ScannedDecls.
+    const FunctionDecl *FD = DeclsToScan.back();
+    DeclsToScan.pop_back();
+
+    // Decl was already scanned
+    if (ScannedDecls.contains(FD))
+      continue;
+    ScannedDecls.insert(FD);
+
+    Stmt *Body = FD->getBody();
+    assert(Body && "full definition with body expected here");
+
+    TraverseStmt(Body);
+  }
+}
+
+bool DiagnoseHLSLAvailability::HasMatchingEnvironmentOrNone(const AvailabilityAttr *AA) {
+  IdentifierInfo *IIEnvironment = AA->getEnvironment();
+  if (!IIEnvironment)
+    return true;
+
+  auto TargetEnvironment = SemaRef.getASTContext().getTargetInfo().getTriple().getEnvironment();
+  if (TargetEnvironment == llvm::Triple::UnknownEnvironment)
+    return true;
+
+  llvm::Triple::EnvironmentType ET =
+      AvailabilityAttr::getEnvironmentType(IIEnvironment->getName());
+  return TargetEnvironment == ET;
+}
+
+const AvailabilityAttr *DiagnoseHLSLAvailability::FindAvailabilityAttr(const Decl *D) {
+  AvailabilityAttr const *PartialMatch = nullptr;
+  // Check each AvailabilityAttr to find the one for this platform.
+  // For multiple attributes with the same platform try to find one for this
+  // environment.
+  for (const auto *A : D->attrs()) {
+    if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) {
+      StringRef AttrPlatform = Avail->getPlatform()->getName();
+      StringRef TargetPlatform = SemaRef.getASTContext().getTargetInfo().getPlatformName();
+
+      // Match the platform name.
+      if (AttrPlatform == TargetPlatform) {
+        // Find the best matching attribute for this environment
+        if (HasMatchingEnvironmentOrNone(Avail))
+          return Avail;
+        PartialMatch = Avail;
+      }
+    }
+  }
+  return PartialMatch;
+}
+
+void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D, SourceRange Range) {
+  const AvailabilityAttr *AA = FindAvailabilityAttr(D);
+  bool EnvironmentMatches = HasMatchingEnvironmentOrNone(AA);
+  VersionTuple Introduced = AA->getIntroduced();
+  VersionTuple TargetVersion = SemaRef.Context.getTargetInfo().getPlatformMinVersion();
+
+  if (TargetVersion >= Introduced && EnvironmentMatches)
+    return;
+
+  const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
+  std::string PlatformName(
+      AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
+  std::string TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
+      TI.getTriple().getEnvironmentName()));
+  VersionTuple UseVersion =
+      EnvironmentMatches ? Introduced : TI.getTriple().getOSVersion();
+  bool UseEnvironment =
+      (AA->getEnvironment() != nullptr && !TargetEnvironment.empty());
+
+  SemaRef.Diag(Range.getBegin(), diag::warn_hlsl_availability)
+      << Range << D << PlatformName << UseVersion.getAsString()
+      << UseEnvironment << TargetEnvironment << !EnvironmentMatches;
+
+  if (EnvironmentMatches)
+    SemaRef.Diag(D->getLocation(),
+                  diag::note_partial_availability_specified_here)
+        << D << PlatformName << Introduced.getAsString()
+        << SemaRef.Context.getTargetInfo()
+                .getPlatformMinVersion()
+                .getAsString()
+        << UseEnvironment << TargetEnvironment;
+
+  // SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
+  //   << Range << D
+  //   << /*__builtin_available*/ 1;
+}
+
+} // namespace
+
+void SemaHLSL::DiagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
+  DiagnoseHLSLAvailability(SemaRef).RunOnTranslationUnit(TU);
+}
\ No newline at end of file

>From ba3e986526fe417f9de6d7c3d6bfe21c721431ff Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 8 May 2024 14:51:51 -0700
Subject: [PATCH 09/21] Do not offer to silence the warning or fixits for HLSL

---
 clang/lib/Sema/SemaAvailability.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index fdc8d310ae865..775ecab6f5d19 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -15,6 +15,7 @@
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Basic/DiagnosticSema.h"
 #include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LangOptions.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/DelayedDiagnostic.h"
@@ -441,6 +442,10 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
         << S.Context.getTargetInfo().getPlatformMinVersion().getAsString()
         << UseEnvironment << AttrEnvironment << TargetEnvironment;
 
+    // Do not offer to silence the warning or fixits for HLSL
+    if (S.getLangOpts().HLSL)
+      return;
+
     if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) {
       if (const auto *TD = dyn_cast<TagDecl>(Enclosing))
         if (TD->getDeclName().isEmpty()) {
@@ -869,6 +874,10 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
         << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString()
         << UseEnvironment << AttrEnvironment << TargetEnvironment;
 
+    // Do not offer to silence the warning or fixits for HLSL
+    if (SemaRef.getLangOpts().HLSL)
+      return;
+
     auto FixitDiag =
         SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
         << Range << D

>From b5834219f8262ccd26a0d5b5314f163b2467665e Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Mon, 13 May 2024 19:00:21 -0700
Subject: [PATCH 10/21] Default and relaxed mode implementation (WIP - part 2)

---
 clang/include/clang/Basic/Attr.td             |  21 +++
 .../clang/Basic/DiagnosticSemaKinds.td        |   7 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 141 +++++++++++++-----
 .../AvailabilityDefaultLibrary.hlsl           | 102 +++++++++++++
 .../AvailabilityDefaultStage.hlsl             |  98 ++++++++++++
 .../AvailabilityRelaxedLibrary.hlsl           | 102 +++++++++++++
 .../AvailabilityRelaxedStage.hlsl             |  98 ++++++++++++
 7 files changed, 528 insertions(+), 41 deletions(-)
 create mode 100644 clang/test/SemaHLSL/Availability/AvailabilityDefaultLibrary.hlsl
 create mode 100644 clang/test/SemaHLSL/Availability/AvailabilityDefaultStage.hlsl
 create mode 100644 clang/test/SemaHLSL/Availability/AvailabilityRelaxedLibrary.hlsl
 create mode 100644 clang/test/SemaHLSL/Availability/AvailabilityRelaxedStage.hlsl

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index ff49b1a7ec64c..850e79e6b2f75 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1073,6 +1073,7 @@ static llvm::StringRef getPrettyEnviromentName(llvm::StringRef Environment) {
              .Case("library", "shader library")
              .Default(Environment);
 }
+
 static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environment) {
     return llvm::StringSwitch<llvm::Triple::EnvironmentType>(Environment)
              .Case("pixel", llvm::Triple::Pixel)
@@ -4475,6 +4476,26 @@ def HLSLShader : InheritableAttr {
                   "Miss", "Callable", "Mesh", "Amplification"]>
   ];
   let Documentation = [HLSLSV_ShaderTypeAttrDocs];
+  let AdditionalMembers =
+[{static llvm::Triple::EnvironmentType getTypeAsEnvironment(HLSLShaderAttr::ShaderType ShaderType) {
+    switch (ShaderType) {
+      case HLSLShaderAttr::Pixel:         return llvm::Triple::EnvironmentType::Pixel;
+      case HLSLShaderAttr::Vertex:        return llvm::Triple::EnvironmentType::Vertex;
+      case HLSLShaderAttr::Geometry:      return llvm::Triple::EnvironmentType::Geometry;
+      case HLSLShaderAttr::Hull:          return llvm::Triple::EnvironmentType::Hull;
+      case HLSLShaderAttr::Domain:        return llvm::Triple::EnvironmentType::Domain;
+      case HLSLShaderAttr::Compute:       return llvm::Triple::EnvironmentType::Compute;
+      case HLSLShaderAttr::RayGeneration: return llvm::Triple::EnvironmentType::RayGeneration;
+      case HLSLShaderAttr::Intersection:  return llvm::Triple::EnvironmentType::Intersection;
+      case HLSLShaderAttr::AnyHit:        return llvm::Triple::EnvironmentType::AnyHit;
+      case HLSLShaderAttr::ClosestHit:    return llvm::Triple::EnvironmentType::ClosestHit;
+      case HLSLShaderAttr::Miss:          return llvm::Triple::EnvironmentType::Miss;
+      case HLSLShaderAttr::Callable:      return llvm::Triple::EnvironmentType::Callable;
+      case HLSLShaderAttr::Mesh:          return llvm::Triple::EnvironmentType::Mesh;
+      case HLSLShaderAttr::Amplification: return llvm::Triple::EnvironmentType::Amplification;
+    }
+  }
+}];
 }
 
 def HLSLResource : InheritableAttr {
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index da454c1e03a32..6ff62a54327d3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12211,9 +12211,12 @@ def warn_hlsl_impcast_vector_truncation : Warning<
   "implicit conversion truncates vector: %0 to %1">, InGroup<Conversion>;
 
 def warn_hlsl_availability : Warning<
-  "%0 %select{is only|is not}5 available %select{|in %4 environment }3on %1 %2 %select{or newer|}5">,
+  "%0 is only available %select{|in %4 environment }3on %1 %2 or newer">,
   InGroup<HLSLAvailability>, DefaultError;
-
+def warn_hlsl_availability_unavailable :
+  Warning<err_unavailable.Summary>,
+  InGroup<HLSLAvailability>, DefaultError;
+  
 // Layout randomization diagnostics.
 def err_non_designated_init_used : Error<
   "a randomized struct can only be initialized with a designated initializer">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 7393d9a05eaeb..f571f72623bf9 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -312,18 +312,54 @@ class DiagnoseHLSLAvailability : public RecursiveASTVisitor<DiagnoseHLSLAvailabi
 
   // Stack of functions to be scaned
   llvm::SmallVector<const FunctionDecl *, 8> DeclsToScan;
-  // Set of functions already scaned
-  llvm::SmallPtrSet<const FunctionDecl *, 8> ScannedDecls;
 
+  // List of functions that were already scanned and in which environment.
+  //
+  // Maps FunctionDecl to a unsigned number that represents a set of shader
+  // environments the function has been scanned for.
+  // Since HLSLShaderAttr::ShaderType enum is generated from Attr.td and is
+  // defined without any assigned values, it is guaranteed to be numbered
+  // sequentially from 0 up and we can use it to 'index' individual bits
+  // in the set.
+  // The N'th bit in the set will be set if the function has been scanned
+  // in shader environment whose ShaderType integer value equals N.
+  // For example, if a function has been scanned in compute and pixel stage
+  // environment, the value will be 0x21 (100001 binary) because
+  // (int)HLSLShaderAttr::ShaderType::Pixel == 1 and
+  // (int)HLSLShaderAttr::ShaderType::Compute == 5.
+  llvm::DenseMap<const FunctionDecl *, unsigned> ScannedDecls;
+
+  // Do not access these directly, use the get/set methods below to make
+  // sure the values are in sync
+  HLSLShaderAttr::ShaderType CurrentShaderType;
+  llvm::Triple::EnvironmentType CurrentShaderTypeAsEnvironment;
+
+  void setCurrentShaderType(HLSLShaderAttr::ShaderType ShaderType) {
+    CurrentShaderType = ShaderType;
+    CurrentShaderTypeAsEnvironment =
+        HLSLShaderAttr::getTypeAsEnvironment(ShaderType);
+  }
+  HLSLShaderAttr::ShaderType getCurrentShaderType() {
+    return CurrentShaderType;
+  }
+  llvm::Triple::EnvironmentType getCurrentShaderTypeAsEnvironment() {
+    return CurrentShaderTypeAsEnvironment;
+  }
+
+  // Scanning methods
   void HandleFunctionOrMethodRef(FunctionDecl *FD, Expr *RefExpr);
   void CheckDeclAvailability(NamedDecl *D, SourceRange Range);
   const AvailabilityAttr *FindAvailabilityAttr(const Decl *D);
   bool HasMatchingEnvironmentOrNone(const AvailabilityAttr *AA);
+  bool WasAlreadyScanned(const FunctionDecl *FD);
+  void AddToScannedFunctions(const FunctionDecl *FD);
 
 public:
   DiagnoseHLSLAvailability(Sema &SemaRef) : SemaRef(SemaRef) {}
 
+  // AST Traversal methods
   void RunOnTranslationUnit(const TranslationUnitDecl *TU);
+  void RunOnFunction(const FunctionDecl *FD);
 
   bool VisitDeclRefExpr(DeclRefExpr *DRE) {
     FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(DRE->getDecl());
@@ -338,18 +374,28 @@ class DiagnoseHLSLAvailability : public RecursiveASTVisitor<DiagnoseHLSLAvailabi
       HandleFunctionOrMethodRef(FD, ME);
     return true;
   }
-
-  // HEKOTA what is this?
-  //bool VisitTypeLoc(TypeLoc Ty);
 };
 
+// Returns true if the function has already been scanned in the current
+// environment
+bool DiagnoseHLSLAvailability::WasAlreadyScanned(const FunctionDecl *FD) {
+  const unsigned &ScannedStages = ScannedDecls.getOrInsertDefault(FD);
+  return (ScannedStages & (1 << getCurrentShaderType()));
+}
+
+// Marks the function as scanned in the current environment
+void DiagnoseHLSLAvailability::AddToScannedFunctions(const FunctionDecl *FD) {
+  unsigned &Set = ScannedDecls.getOrInsertDefault(FD);
+  Set &= 1 << getCurrentShaderType();
+}
+
 void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD, Expr *RefExpr) {
   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 (!ScannedDecls.contains(FDWithBody))
+    if (!WasAlreadyScanned(FDWithBody))
       DeclsToScan.push_back(FDWithBody);
     return;
   }
@@ -363,12 +409,23 @@ void DiagnoseHLSLAvailability::RunOnTranslationUnit(const TranslationUnitDecl *T
   // of functions to be scanned
   for (auto &D : TU->decls()) {
     const FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(D);
+    if (!FD)
+      continue;
+
     // HEKOTA TODO detect also library exports
-    if (!FD || !FD->hasAttr<HLSLShaderAttr>())
+    auto ShaderAttr = FD->getAttr<HLSLShaderAttr>();
+    if (!ShaderAttr)
       continue;
 
-    DeclsToScan.push_back(FD);
+    setCurrentShaderType(ShaderAttr->getType());
+
+    RunOnFunction(FD);
   }
+}
+
+void DiagnoseHLSLAvailability::RunOnFunction(const FunctionDecl *FD) {
+  assert(DeclsToScan.empty() && "DeclsToScan should be empty");
+  DeclsToScan.push_back(FD);
 
   while (!DeclsToScan.empty()) {
     // Take one decl from the stack and check it by traversing its AST.
@@ -378,9 +435,10 @@ void DiagnoseHLSLAvailability::RunOnTranslationUnit(const TranslationUnitDecl *T
     DeclsToScan.pop_back();
 
     // Decl was already scanned
-    if (ScannedDecls.contains(FD))
+    if (WasAlreadyScanned(FD))
       continue;
-    ScannedDecls.insert(FD);
+
+    AddToScannedFunctions(FD);
 
     Stmt *Body = FD->getBody();
     assert(Body && "full definition with body expected here");
@@ -394,13 +452,16 @@ bool DiagnoseHLSLAvailability::HasMatchingEnvironmentOrNone(const AvailabilityAt
   if (!IIEnvironment)
     return true;
 
-  auto TargetEnvironment = SemaRef.getASTContext().getTargetInfo().getTriple().getEnvironment();
-  if (TargetEnvironment == llvm::Triple::UnknownEnvironment)
-    return true;
+  assert(getCurrentShaderTypeAsEnvironment() !=
+             llvm::Triple::UnknownEnvironment &&
+         "this should be set to the shader stage context");
 
-  llvm::Triple::EnvironmentType ET =
+  llvm::Triple::EnvironmentType AttrEnv =
       AvailabilityAttr::getEnvironmentType(IIEnvironment->getName());
-  return TargetEnvironment == ET;
+  llvm::Triple::EnvironmentType CurrentEnv =
+      getCurrentShaderTypeAsEnvironment();
+
+  return CurrentEnv == AttrEnv;
 }
 
 const AvailabilityAttr *DiagnoseHLSLAvailability::FindAvailabilityAttr(const Decl *D) {
@@ -435,31 +496,33 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D, SourceRange R
     return;
 
   const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
-  std::string PlatformName(
+  llvm::StringRef PlatformName(
       AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
-  std::string TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
-      TI.getTriple().getEnvironmentName()));
-  VersionTuple UseVersion =
-      EnvironmentMatches ? Introduced : TI.getTriple().getOSVersion();
-  bool UseEnvironment =
-      (AA->getEnvironment() != nullptr && !TargetEnvironment.empty());
-
-  SemaRef.Diag(Range.getBegin(), diag::warn_hlsl_availability)
-      << Range << D << PlatformName << UseVersion.getAsString()
-      << UseEnvironment << TargetEnvironment << !EnvironmentMatches;
-
-  if (EnvironmentMatches)
-    SemaRef.Diag(D->getLocation(),
-                  diag::note_partial_availability_specified_here)
-        << D << PlatformName << Introduced.getAsString()
-        << SemaRef.Context.getTargetInfo()
-                .getPlatformMinVersion()
-                .getAsString()
-        << UseEnvironment << TargetEnvironment;
-
-  // SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
-  //   << Range << D
-  //   << /*__builtin_available*/ 1;
+
+  StringRef CurrentShaderTypeStr(
+      HLSLShaderAttr::ConvertShaderTypeToStr(getCurrentShaderType()));
+  llvm::StringRef CurrentEnvStr =
+      AvailabilityAttr::getPrettyEnviromentName(CurrentShaderTypeStr);
+
+  llvm::StringRef AttrEnvStr = AA->getEnvironment()
+                                   ? AvailabilityAttr::getPrettyEnviromentName(
+                                         AA->getEnvironment()->getName())
+                                   : "";
+  bool UseEnvironment = !AttrEnvStr.empty();
+
+  if (EnvironmentMatches) {
+    SemaRef.Diag(Range.getBegin(), diag::warn_hlsl_availability)
+        << Range << D << PlatformName << Introduced.getAsString()
+        << UseEnvironment << CurrentEnvStr;
+  } else {
+    SemaRef.Diag(Range.getBegin(), diag::warn_hlsl_availability_unavailable)
+        << Range << D;
+  }
+
+  SemaRef.Diag(D->getLocation(), diag::note_partial_availability_specified_here)
+      << D << PlatformName << Introduced.getAsString()
+      << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString()
+      << UseEnvironment << AttrEnvStr << CurrentEnvStr;
 }
 
 } // namespace
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityDefaultLibrary.hlsl b/clang/test/SemaHLSL/Availability/AvailabilityDefaultLibrary.hlsl
new file mode 100644
index 0000000000000..de1d61dbb8677
--- /dev/null
+++ b/clang/test/SemaHLSL/Availability/AvailabilityDefaultLibrary.hlsl
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library \
+// RUN: -fsyntax-only -verify %s
+
+__attribute__((availability(shadermodel, introduced = 6.5)))
+float fx(float);  // #fx
+
+__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 shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader 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 shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float C = fz(f); // #alive_fz_call
+
+  return also_alive(f);
+}
+
+float also_dead(float f) {
+  // unreachable code - no errors expected
+  float A = fx(f);
+  float B = fy(f);
+  float C = fz(f);
+  return 0;
+}
+
+float dead(float f) {
+  // unreachable code - no errors expected
+  float A = fx(f);
+  float B = fy(f);
+  float C = fz(f);
+  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@#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 shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float C = fz(f); // #aliveTemp_fz_call
+  return 0;
+}
+
+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 shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+    float C = fz(F); // #MyClass_makeF_fz_call
+    return 0;
+  }
+};
+
+[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();
+  return a * b * c;
+}
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityDefaultStage.hlsl b/clang/test/SemaHLSL/Availability/AvailabilityDefaultStage.hlsl
new file mode 100644
index 0000000000000..c3b53a2386176
--- /dev/null
+++ b/clang/test/SemaHLSL/Availability/AvailabilityDefaultStage.hlsl
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute \
+// RUN: -fsyntax-only -verify %s
+
+__attribute__((availability(shadermodel, introduced = 6.5)))
+float fx(float);  // #fx
+
+__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 shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader 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 shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float C = fz(f); // #alive_fz_call
+
+  return also_alive(f);
+}
+
+float also_dead(float f) {
+  // unreachable code - no errors expected
+  float A = fx(f);
+  float B = fy(f);
+  float C = fz(f);
+  return 0;
+}
+
+float dead(float f) {
+  // unreachable code - no errors expected
+  float A = fx(f);
+  float B = fy(f);
+  float C = fz(f);
+
+  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@#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 shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float C = fz(f); // #aliveTemp_fz_call
+  return 0;
+}
+
+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 shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+    float C = fz(F); // #MyClass_makeF_fz_call
+    return 0;
+  }
+};
+
+[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();
+  return a * b * c;
+}
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityRelaxedLibrary.hlsl b/clang/test/SemaHLSL/Availability/AvailabilityRelaxedLibrary.hlsl
new file mode 100644
index 0000000000000..ee44a243d7129
--- /dev/null
+++ b/clang/test/SemaHLSL/Availability/AvailabilityRelaxedLibrary.hlsl
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library \
+// RUN: -fsyntax-only -verify %s
+
+__attribute__((availability(shadermodel, introduced = 6.5)))
+float fx(float);  // #fx
+
+__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-warning@#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-warning@#also_alive_fy_call {{'fy' is only available in compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float B = fy(f); // #also_alive_fy_call
+
+  // expected-warning@#also_alive_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float C = fz(f); // #also_alive_fz_call
+
+  return 0;
+}
+
+float alive(float f) {
+  // expected-warning@#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-warning@#alive_fy_call {{'fy' is only available in compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float B = fy(f); // #alive_fy_call
+
+  // expected-warning@#alive_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float C = fz(f); // #alive_fz_call
+
+  return also_alive(f);
+}
+
+float also_dead(float f) {
+  // unreachable code - no errors expected
+  float A = fx(f);
+  float B = fy(f);
+  float C = fz(f);
+  return 0;
+}
+
+float dead(float f) {
+  // unreachable code - no errors expected
+  float A = fx(f);
+  float B = fy(f);
+  float C = fz(f);
+  return also_dead(f);
+}
+
+template<typename T>
+T aliveTemp(T f) {
+  // expected-warning@#aliveTemp_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); // #aliveTemp_fx_call
+  // expected-warning@#aliveTemp_fy_call {{'fy' is only available in compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float B = fy(f); // #aliveTemp_fy_call
+  // expected-warning@#aliveTemp_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float C = fz(f); // #aliveTemp_fz_call
+  return 0;
+}
+
+class MyClass
+{
+  float F;
+  float makeF() {
+    // expected-warning@#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-warning@#MyClass_makeF_fy_call {{'fy' is only available in compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+    float B = fy(F); // #MyClass_makeF_fy_call
+    // expected-warning@#MyClass_makeF_fz_call {{'fz' is unavailable}}
+    // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+    float C = fz(F); // #MyClass_makeF_fz_call
+    return 0;
+  }
+};
+
+[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();
+  return a * b * c;
+}
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityRelaxedStage.hlsl b/clang/test/SemaHLSL/Availability/AvailabilityRelaxedStage.hlsl
new file mode 100644
index 0000000000000..a24943d431d4b
--- /dev/null
+++ b/clang/test/SemaHLSL/Availability/AvailabilityRelaxedStage.hlsl
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute \
+// RUN: -fsyntax-only -Wno-error=hlsl-availability -verify %s
+
+__attribute__((availability(shadermodel, introduced = 6.5)))
+float fx(float);  // #fx
+
+__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-warning@#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-warning@#also_alive_fy_call {{'fy' is only available in compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float B = fy(f); // #also_alive_fy_call
+  // expected-warning@#also_alive_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float C = fz(f); // #also_alive_fz_call
+  return 0;
+}
+
+float alive(float f) {
+  // expected-warning@#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-warning@#alive_fy_call {{'fy' is only available in compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float B = fy(f); // #alive_fy_call
+  // expected-warning@#alive_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float C = fz(f); // #alive_fz_call
+
+  return also_alive(f);
+}
+
+float also_dead(float f) {
+  // unreachable code - no errors expected
+  float A = fx(f);
+  float B = fy(f);
+  float C = fz(f);
+  return 0;
+}
+
+float dead(float f) {
+  // unreachable code - no errors expected
+  float A = fx(f);
+  float B = fy(f);
+  float C = fz(f);
+
+  return also_dead(f);
+}
+
+template<typename T>
+T aliveTemp(T f) {
+  // expected-warning@#aliveTemp_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); // #aliveTemp_fx_call
+  // expected-warning@#aliveTemp_fy_call {{'fy' is only available in compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float B = fy(f); // #aliveTemp_fy_call
+  // expected-warning@#aliveTemp_fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float C = fz(f); // #aliveTemp_fz_call
+  return 0;
+}
+
+class MyClass
+{
+  float F;
+  float makeF() {
+    // expected-warning@#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-warning@#MyClass_makeF_fy_call {{'fy' is only available in compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+    float B = fy(F); // #MyClass_makeF_fy_call
+    // expected-warning@#MyClass_makeF_fz_call {{'fz' is unavailable}}
+    // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+    float C = fz(F); // #MyClass_makeF_fz_call
+    return 0;
+  }
+};
+
+[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();
+  return a * b * c;
+}
\ No newline at end of file

>From 5ab4d50be5dff8b1daee735a41d89ac1509d8568 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 14 May 2024 01:36:50 -0700
Subject: [PATCH 11/21] Default & relaxed availability diagnostic (part 3)

- Add scanning of exported functions (disabled for now)
- Update comments
- Prohibit strict availability param
- Add all shader stages as valid environment
---
 clang/include/clang/Basic/Attr.td             |  33 ++++-
 clang/include/clang/Basic/DiagnosticGroups.td |   2 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/lib/Sema/SemaAvailability.cpp           |   4 +-
 clang/lib/Sema/SemaDeclAttr.cpp               |   5 +
 clang/lib/Sema/SemaHLSL.cpp                   | 121 +++++++++++-------
 .../Sema/attr-availability-shadermodel.hlsl   |  84 ++----------
 .../AvailabilityDefaultLibrary.hlsl           |   8 +-
 .../AvailabilityDefaultStage.hlsl             |   2 +-
 .../AvailabilityRelaxedLibrary.hlsl           |  10 +-
 .../AvailabilityRelaxedStage.hlsl             |   2 +-
 .../SemaHLSL/WaveBuiltinAvailability.hlsl     |   9 +-
 12 files changed, 152 insertions(+), 131 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 850e79e6b2f75..6673083ba6f62 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1068,12 +1068,37 @@ static llvm::StringRef getPrettyEnviromentName(llvm::StringRef Environment) {
              .Case("hull", "hull shader")
              .Case("domain", "domain shader")
              .Case("compute", "compute shader")
+             .Case("raygeneration", "ray generation shader")
+             .Case("intersection", "intersection shader")
+             .Case("anyhit", "anyhit shader")
+             .Case("closesthit", "closesthit shader")
+             .Case("miss", "miss shader")
+             .Case("callable", "callable shader")
              .Case("mesh", "mesh shader")
              .Case("amplification", "amplification shader")
              .Case("library", "shader library")
              .Default(Environment);
 }
-
+static llvm::StringRef getEnvironmentString(llvm::Triple::EnvironmentType EnvironmentType) {
+  switch (EnvironmentType) {
+    case llvm::Triple::EnvironmentType::Pixel: return "pixel";
+    case llvm::Triple::EnvironmentType::Vertex: return "vertex";
+    case llvm::Triple::EnvironmentType::Geometry: return "geometry";
+    case llvm::Triple::EnvironmentType::Hull: return "hull";
+    case llvm::Triple::EnvironmentType::Domain: return "domain";
+    case llvm::Triple::EnvironmentType::Compute: return "compute";
+    case llvm::Triple::EnvironmentType::RayGeneration: return "ray generation";
+    case llvm::Triple::EnvironmentType::Intersection: return "intersection";
+    case llvm::Triple::EnvironmentType::AnyHit: return "any hit";
+    case llvm::Triple::EnvironmentType::ClosestHit: return "closest hit";
+    case llvm::Triple::EnvironmentType::Miss: return "miss";
+    case llvm::Triple::EnvironmentType::Callable: return "callable";
+    case llvm::Triple::EnvironmentType::Mesh: return "mesh";
+    case llvm::Triple::EnvironmentType::Amplification: return "amplification";
+    case llvm::Triple::EnvironmentType::Library: return "library";
+    default: return "";
+  }
+}
 static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environment) {
     return llvm::StringSwitch<llvm::Triple::EnvironmentType>(Environment)
              .Case("pixel", llvm::Triple::Pixel)
@@ -1082,6 +1107,12 @@ static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environm
              .Case("hull", llvm::Triple::Hull)
              .Case("domain", llvm::Triple::Domain)
              .Case("compute", llvm::Triple::Compute)
+             .Case("raygeneration", llvm::Triple::RayGeneration)
+             .Case("intersection", llvm::Triple::Intersection)
+             .Case("anyhit", llvm::Triple::AnyHit)
+             .Case("closesthit", llvm::Triple::ClosestHit)
+             .Case("miss", llvm::Triple::Miss)
+             .Case("callable", llvm::Triple::Callable)
              .Case("mesh", llvm::Triple::Mesh)
              .Case("amplification", llvm::Triple::Amplification)
              .Case("library", llvm::Triple::Library)
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index ae78b5a7ee3f5..e8b05772061f4 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1513,7 +1513,7 @@ def HLSLMixPackOffset : DiagGroup<"mix-packoffset">;
 // Warnings for DXIL validation
 def DXILValidation : DiagGroup<"dxil-validation">;
 
-// Warning for HLSL availability
+// Warning for HLSL API availability
 def HLSLAvailability : DiagGroup<"hlsl-availability">;
 
 // Warnings and notes related to const_var_decl_type attribute checks
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6ff62a54327d3..a17db812b73cf 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3862,6 +3862,9 @@ def note_protocol_method : Note<
 def warn_availability_fuchsia_unavailable_minor : Warning<
   "Fuchsia API Level prohibits specifying a minor or sub-minor version">,
   InGroup<Availability>;
+def warn_availability_hlsl_strict_not_supported : Warning<
+  "availability parameter 'strict' is not supported in HLSL">,
+  InGroup<Availability>;
 
 def warn_unguarded_availability :
   Warning<"%0 is only available %select{|in %4 environment }3on %1 %2 or newer">,
diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index 775ecab6f5d19..e914975b84ac6 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -228,8 +228,8 @@ shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);
     break;
   case llvm::Triple::ShaderModel:
-    // Always enable availability diagnostics for shader models.
-    // HEKOTA TODO
+    // FIXME: This will be updated when HLSL strict diagnostic mode
+    // is implemented (issue #90096)
     return false;
   default:
     // New targets should always warn about availability.
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 3b79f34c091cb..195364aa38391 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2713,6 +2713,11 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
     }
   }
 
+  if (S.getLangOpts().HLSL && IsStrict) {
+    S.Diag(AL.getStrictLoc(), diag::warn_availability_hlsl_strict_not_supported);
+    return;
+  }
+
   int PriorityModifier = AL.isPragmaClangAttribute()
                              ? Sema::AP_PragmaClangAttribute
                              : Sema::AP_Explicit;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index f571f72623bf9..063acb352035a 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -297,13 +297,18 @@ void SemaHLSL::DiagnoseAttrStageMismatch(
 
 namespace {
 
-/// HEKOTA TODO: UDPATE
-/// This class implements HLSL availability diagnostics
+/// This class implements HLSL availability diagnostics for default
+/// and relaxed mode
 ///
-/// This is done by traversing all CallExpr nodes that are reachable from exported functions (either library exports or entry functions).
-/// If the callee of an CallExpr is in HLSL namespace and has availability annotation that signifies that the API is unavailable 
-/// for the target shader model and stage, the compiler emits an error in default or strict diagnostic mode (-fhlsl-strict-diagnostics)
-/// or a warning in relaxed mode (-Wno-error=hlsl-availability).
+/// The goal of this diagnostic is to emit an error or warning when an
+/// unavailable API is found in a code that is reachable from the shader
+/// entry function or from an exported function (when compiling shader
+/// 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
+/// from this AST. In other words, any function that are reachable from
+/// the entry points.
 class DiagnoseHLSLAvailability : public RecursiveASTVisitor<DiagnoseHLSLAvailability> {
   // HEKOTAS this is probably not needed
   // typedef RecursiveASTVisitor<DiagnoseHLSLAvailability> Base;
@@ -331,24 +336,30 @@ class DiagnoseHLSLAvailability : public RecursiveASTVisitor<DiagnoseHLSLAvailabi
 
   // Do not access these directly, use the get/set methods below to make
   // sure the values are in sync
-  HLSLShaderAttr::ShaderType CurrentShaderType;
-  llvm::Triple::EnvironmentType CurrentShaderTypeAsEnvironment;
+  llvm::Triple::EnvironmentType CurrentShaderEnvironment;
+  unsigned CurrentShaderStageBit;
 
-  void setCurrentShaderType(HLSLShaderAttr::ShaderType ShaderType) {
-    CurrentShaderType = ShaderType;
-    CurrentShaderTypeAsEnvironment =
-        HLSLShaderAttr::getTypeAsEnvironment(ShaderType);
+  void SetShaderStageContext(HLSLShaderAttr::ShaderType ShaderType) {
+    CurrentShaderEnvironment = HLSLShaderAttr::getTypeAsEnvironment(ShaderType);
+    CurrentShaderStageBit = (1 << ShaderType);
+  }
+  void SetUnknownShaderStageContext() {
+    CurrentShaderEnvironment =
+        llvm::Triple::EnvironmentType::UnknownEnvironment;
+    CurrentShaderStageBit = (1 << 31);
   }
-  HLSLShaderAttr::ShaderType getCurrentShaderType() {
-    return CurrentShaderType;
+  llvm::Triple::EnvironmentType GetCurrentShaderEnvironment() {
+    return CurrentShaderEnvironment;
   }
-  llvm::Triple::EnvironmentType getCurrentShaderTypeAsEnvironment() {
-    return CurrentShaderTypeAsEnvironment;
+  bool InUnknownShaderStageContext() {
+    return CurrentShaderEnvironment ==
+           llvm::Triple::EnvironmentType::UnknownEnvironment;
   }
 
   // Scanning methods
   void HandleFunctionOrMethodRef(FunctionDecl *FD, Expr *RefExpr);
-  void CheckDeclAvailability(NamedDecl *D, SourceRange Range);
+  void CheckDeclAvailability(NamedDecl *D, const AvailabilityAttr *AA,
+                             SourceRange Range);
   const AvailabilityAttr *FindAvailabilityAttr(const Decl *D);
   bool HasMatchingEnvironmentOrNone(const AvailabilityAttr *AA);
   bool WasAlreadyScanned(const FunctionDecl *FD);
@@ -357,7 +368,7 @@ class DiagnoseHLSLAvailability : public RecursiveASTVisitor<DiagnoseHLSLAvailabi
 public:
   DiagnoseHLSLAvailability(Sema &SemaRef) : SemaRef(SemaRef) {}
 
-  // AST Traversal methods
+  // AST traversal methods
   void RunOnTranslationUnit(const TranslationUnitDecl *TU);
   void RunOnFunction(const FunctionDecl *FD);
 
@@ -377,16 +388,16 @@ class DiagnoseHLSLAvailability : public RecursiveASTVisitor<DiagnoseHLSLAvailabi
 };
 
 // Returns true if the function has already been scanned in the current
-// environment
+// shader environment
 bool DiagnoseHLSLAvailability::WasAlreadyScanned(const FunctionDecl *FD) {
   const unsigned &ScannedStages = ScannedDecls.getOrInsertDefault(FD);
-  return (ScannedStages & (1 << getCurrentShaderType()));
+  return ScannedStages & CurrentShaderStageBit;
 }
 
-// Marks the function as scanned in the current environment
+// Marks the function as scanned in the current shader environment
 void DiagnoseHLSLAvailability::AddToScannedFunctions(const FunctionDecl *FD) {
-  unsigned &Set = ScannedDecls.getOrInsertDefault(FD);
-  Set &= 1 << getCurrentShaderType();
+  unsigned &ScannedStages = ScannedDecls.getOrInsertDefault(FD);
+  ScannedStages |= CurrentShaderStageBit;
 }
 
 void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD, Expr *RefExpr) {
@@ -400,26 +411,38 @@ void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD, Expr
     return;
   }
 
-  // no definition -> diagnose availability
-  CheckDeclAvailability(FD, SourceRange(RefExpr->getBeginLoc(), RefExpr->getEndLoc()));
+  // no body -> diagnose availability
+  const AvailabilityAttr *AA = FindAvailabilityAttr(FD);
+  if (AA)
+    CheckDeclAvailability(
+        FD, AA, SourceRange(RefExpr->getBeginLoc(), RefExpr->getEndLoc()));
 }
 
 void DiagnoseHLSLAvailability::RunOnTranslationUnit(const TranslationUnitDecl *TU) {
-  // Add all shader entry functions and library exports to the stack
-  // of functions to be scanned
+  // Iterate over all shader entry functions and library exports, and for those
+  // that have a body (definiton), run diag scan on each, setting appropriate
+  // shader environment context based on whether it is a shader entry function
+  // or an exported function.
   for (auto &D : TU->decls()) {
     const FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(D);
-    if (!FD)
+    if (!FD || !FD->isThisDeclarationADefinition())
       continue;
 
-    // HEKOTA TODO detect also library exports
+    // shader entry point
     auto ShaderAttr = FD->getAttr<HLSLShaderAttr>();
-    if (!ShaderAttr)
+    if (ShaderAttr) {
+      SetShaderStageContext(ShaderAttr->getType());
+      RunOnFunction(FD);
       continue;
-
-    setCurrentShaderType(ShaderAttr->getType());
-
-    RunOnFunction(FD);
+    }
+    // exported library function with definition
+    // FIXME: tracking issue #92073
+#if 0
+    if (FD->getFormalLinkage() == Linkage::External) {
+      SetUnknownShaderStageContext();
+      RunOnFunction(FD);
+    }
+#endif
   }
 }
 
@@ -452,14 +475,12 @@ bool DiagnoseHLSLAvailability::HasMatchingEnvironmentOrNone(const AvailabilityAt
   if (!IIEnvironment)
     return true;
 
-  assert(getCurrentShaderTypeAsEnvironment() !=
-             llvm::Triple::UnknownEnvironment &&
-         "this should be set to the shader stage context");
+  llvm::Triple::EnvironmentType CurrentEnv = GetCurrentShaderEnvironment();
+  if (CurrentEnv == llvm::Triple::UnknownEnvironment)
+    return false;
 
   llvm::Triple::EnvironmentType AttrEnv =
       AvailabilityAttr::getEnvironmentType(IIEnvironment->getName());
-  llvm::Triple::EnvironmentType CurrentEnv =
-      getCurrentShaderTypeAsEnvironment();
 
   return CurrentEnv == AttrEnv;
 }
@@ -486,8 +507,11 @@ const AvailabilityAttr *DiagnoseHLSLAvailability::FindAvailabilityAttr(const Dec
   return PartialMatch;
 }
 
-void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D, SourceRange Range) {
-  const AvailabilityAttr *AA = FindAvailabilityAttr(D);
+// Check availability against target shader model version and current shader
+// stage and emit diagnostic
+void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
+                                                     const AvailabilityAttr *AA,
+                                                     SourceRange Range) {
   bool EnvironmentMatches = HasMatchingEnvironmentOrNone(AA);
   VersionTuple Introduced = AA->getIntroduced();
   VersionTuple TargetVersion = SemaRef.Context.getTargetInfo().getPlatformMinVersion();
@@ -495,14 +519,19 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D, SourceRange R
   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()));
 
-  StringRef CurrentShaderTypeStr(
-      HLSLShaderAttr::ConvertShaderTypeToStr(getCurrentShaderType()));
-  llvm::StringRef CurrentEnvStr =
-      AvailabilityAttr::getPrettyEnviromentName(CurrentShaderTypeStr);
+  llvm::StringRef CurrentEnvStr = AvailabilityAttr::getPrettyEnviromentName(
+      AvailabilityAttr::getEnvironmentString(GetCurrentShaderEnvironment()));
 
   llvm::StringRef AttrEnvStr = AA->getEnvironment()
                                    ? AvailabilityAttr::getPrettyEnviromentName(
@@ -529,4 +558,4 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D, SourceRange R
 
 void SemaHLSL::DiagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
   DiagnoseHLSLAvailability(SemaRef).RunOnTranslationUnit(TU);
-}
\ No newline at end of file
+}
diff --git a/clang/test/Sema/attr-availability-shadermodel.hlsl b/clang/test/Sema/attr-availability-shadermodel.hlsl
index 3339656354f29..ffec842610a49 100644
--- a/clang/test/Sema/attr-availability-shadermodel.hlsl
+++ b/clang/test/Sema/attr-availability-shadermodel.hlsl
@@ -10,17 +10,6 @@ unsigned fn5_1(); // #fn5_1
 __attribute__((availability(shadermodel, introduced = 5.0)))
 unsigned fn5_0();
 
-// Platform shader model, no environment parameter
-// - with strict flag (executes different code path)
-__attribute__((availability(shadermodel, strict, introduced = 6.0)))
-unsigned fn6_0_s(); // #fn6_0_s
-
-__attribute__((availability(shadermodel, strict, introduced = 5.1)))
-unsigned fn5_1_s(); // #fn5_1_s
-
-__attribute__((availability(shadermodel, strict, introduced = 5.0)))
-unsigned fn5_0_s();
-
 // Platform shader model, environment parameter restricting earlier version,
 // available in all environments in higher versions
 __attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
@@ -31,17 +20,6 @@ __attribute__((availability(shadermodel, introduced = 2.0, environment = pixel))
 __attribute__((availability(shadermodel, introduced = 5.0)))
 unsigned fn5_0_mix();
 
-// Platform shader model, environment parameter restricting earlier version,
-// available in all environments in higher versions
-// - with strict flag (executes different code path)
-__attribute__((availability(shadermodel, strict, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, strict, introduced = 6.0)))
-unsigned fn6_0_mix_s(); // #fn6_0_mix_s
-
-__attribute__((availability(shadermodel, strict, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, strict, introduced = 5.0)))
-unsigned fn5_0_mix_s();
-
 // Platform shader model, environment parameter restricting earlier version,
 // never available in all environments in higher versions
 __attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
@@ -58,83 +36,45 @@ __attribute__((availability(shadermodel, introduced = 5.0, environment = compute
 __attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
 unsigned fn5_0_stages();
 
-// Platform shader model, environment parameter restricting earlier version,
-// never available in all environments in higher versions
-// - with strict flag (executes different code path)
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = compute)))
-__attribute__((availability(shadermodel, introduced = 5.0, environment = mesh)))
-unsigned fn6_0_stages1_s();  // #fn6_0_stages1_s
+// Strict parameter is not supported in HLSL
+__attribute__((availability(shadermodel, strict, introduced = 6.0))) // expected-warning {{availability parameter 'strict' is not supported in HLSL}}
+unsigned fn6_0_strict1();
 
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
-unsigned fn6_0_stages2_s(); // #fn6_0_stages2_s
+__attribute__((availability(shadermodel, strict, introduced = 6.0, environment = pixel))) // expected-warning {{availability parameter 'strict' is not supported in HLSL}}
+unsigned fn6_0_strict2();
 
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 5.0, environment = compute)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
-unsigned fn5_0_stages_s();
 
 [numthreads(4,1,1)]
 int main() {
-    // expected-warning@#fn6_0_call {{'fn6_0' is only available on Shader Model 6.0 or newer}}
+    // expected-error@#fn6_0_call {{'fn6_0' is only available on Shader Model 6.0 or newer}}
     // expected-note@#fn6_0 {{'fn6_0' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn6_0_call {{enclose 'fn6_0' in a __builtin_available check to silence this warning}}
     unsigned A1 = fn6_0(); // #fn6_0_call
 
-    // expected-warning@#fn5_1_call {{'fn5_1' is only available on Shader Model 5.1 or newer}}
+    // expected-error@#fn5_1_call {{'fn5_1' is only available on Shader Model 5.1 or newer}}
     // expected-note@#fn5_1 {{'fn5_1' has been marked as being introduced in Shader Model 5.1 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn5_1_call {{enclose 'fn5_1' in a __builtin_available check to silence this warning}}
     unsigned B1 = fn5_1(); // #fn5_1_call
 
     unsigned C1 = fn5_0();
 
-    // expected-error@#fn6_0_s_call {{'fn6_0_s' is unavailable: introduced in Shader Model 6.0 compute shader}}
-    // expected-note@#fn6_0_s {{'fn6_0_s' has been explicitly marked unavailable here}}
-    unsigned A2 = fn6_0_s(); // #fn6_0_s_call
-
-    // expected-error@#fn5_1_s_call {{'fn5_1_s' is unavailable: introduced in Shader Model 5.1 compute shader}}
-    // expected-note@#fn5_1_s {{'fn5_1_s' has been explicitly marked unavailable here}}
-    unsigned B2 = fn5_1_s(); // #fn5_1_s_call
-
-    unsigned C2 = fn5_0_s();
-
-    // expected-warning@#fn6_0_mix_call {{'fn6_0_mix' is only available on Shader Model 6.0 or newer}}
+    // expected-error@#fn6_0_mix_call {{'fn6_0_mix' is only available on Shader Model 6.0 or newer}}
     // expected-note@#fn6_0_mix {{'fn6_0_mix' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn6_0_mix_call {{enclose 'fn6_0_mix' in a __builtin_available check to silence this warning}}
     unsigned A3 = fn6_0_mix(); // #fn6_0_mix_call
 
     unsigned B3 = fn5_0_mix();
 
-    // expected-error@#fn6_0_mix_s_call {{'fn6_0_mix_s' is unavailable: introduced in Shader Model 6.0 compute shader}}
-    // expected-note@#fn6_0_mix_s {{'fn6_0_mix_s' has been explicitly marked unavailable here}}
-    unsigned A4 = fn6_0_mix_s(); // #fn6_0_mix_s_call
-
-    unsigned B4 = fn5_0_mix_s();
-
-    // expected-warning@#fn6_0_stages1_call {{'fn6_0_stages1' is only available in compute shader environment on Shader Model 6.0 or newer}}
+    // expected-error@#fn6_0_stages1_call {{'fn6_0_stages1' is only available in compute shader environment on Shader Model 6.0 or newer}}
     // expected-note@#fn6_0_stages1 {{'fn6_0_stages1' has been marked as being introduced in Shader Model 6.0 in compute shader environment here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn6_0_stages1_call {{enclose 'fn6_0_stages1' in a __builtin_available check to silence this warning}}
     unsigned A5 = fn6_0_stages1(); // #fn6_0_stages1_call
 
-    // expected-warning@#fn6_0_stages2_call {{'fn6_0_stages2' is unavailable}}
+    // expected-error@#fn6_0_stages2_call {{'fn6_0_stages2' is unavailable}}
     // expected-note@#fn6_0_stages2 {{'fn6_0_stages2' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 compute shader environment}}
-    // expected-note@#fn6_0_stages2_call {{enclose 'fn6_0_stages2' in a __builtin_available check to silence this warning}}
     unsigned B5 = fn6_0_stages2(); // #fn6_0_stages2_call
 
     unsigned C5 = fn5_0_stages();
 
-    // expected-warning@#fn6_0_stages1_s_call {{'fn6_0_stages1_s' is only available in compute shader environment on Shader Model 6.0 or newer}}
-    // expected-note@#fn6_0_stages1_s {{'fn6_0_stages1_s' has been marked as being introduced in Shader Model 6.0 in compute shader environment here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn6_0_stages1_s_call {{enclose 'fn6_0_stages1_s' in a __builtin_available check to silence this warning}}
-    unsigned A6 = fn6_0_stages1_s(); // #fn6_0_stages1_s_call
-
-    // expected-warning@#fn6_0_stages2_s_call {{'fn6_0_stages2_s' is unavailable}}
-    // expected-note@#fn6_0_stages2_s {{'fn6_0_stages2_s' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 compute shader environment}}
-    // expected-note@#fn6_0_stages2_s_call {{enclose 'fn6_0_stages2_s' in a __builtin_available check to silence this warning}}
-    unsigned B6 = fn6_0_stages2_s(); // #fn6_0_stages2_s_call
+    unsigned A6 = fn6_0_strict1();
 
-    unsigned C6 = fn5_0_stages_s();
+    unsigned B6 = fn6_0_strict1();
 
     return 0;
 }
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityDefaultLibrary.hlsl b/clang/test/SemaHLSL/Availability/AvailabilityDefaultLibrary.hlsl
index de1d61dbb8677..7290bdb6ce029 100644
--- a/clang/test/SemaHLSL/Availability/AvailabilityDefaultLibrary.hlsl
+++ b/clang/test/SemaHLSL/Availability/AvailabilityDefaultLibrary.hlsl
@@ -91,6 +91,12 @@ class MyClass
   }
 };
 
+// Shader entry point without body
+[shader("compute")]
+[numthreads(4,1,1)]
+float main();
+
+// Shader entry point with body
 [shader("compute")]
 [numthreads(4,1,1)]
 float main() {
@@ -99,4 +105,4 @@ float main() {
   float a = alive(f);float b = aliveTemp<float>(f); // #aliveTemp_inst
   float c = C.makeF();
   return a * b * c;
-}
\ No newline at end of file
+}
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityDefaultStage.hlsl b/clang/test/SemaHLSL/Availability/AvailabilityDefaultStage.hlsl
index c3b53a2386176..80a40adede0dd 100644
--- a/clang/test/SemaHLSL/Availability/AvailabilityDefaultStage.hlsl
+++ b/clang/test/SemaHLSL/Availability/AvailabilityDefaultStage.hlsl
@@ -95,4 +95,4 @@ float main() {
   float b = aliveTemp<float>(f); // #aliveTemp_inst
   float c = C.makeF();
   return a * b * c;
-}
\ No newline at end of file
+}
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityRelaxedLibrary.hlsl b/clang/test/SemaHLSL/Availability/AvailabilityRelaxedLibrary.hlsl
index ee44a243d7129..4d6e06faef8ac 100644
--- a/clang/test/SemaHLSL/Availability/AvailabilityRelaxedLibrary.hlsl
+++ b/clang/test/SemaHLSL/Availability/AvailabilityRelaxedLibrary.hlsl
@@ -1,5 +1,5 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library \
-// RUN: -fsyntax-only -verify %s
+// RUN: -fsyntax-only -Wno-error=hlsl-availability -verify %s
 
 __attribute__((availability(shadermodel, introduced = 6.5)))
 float fx(float);  // #fx
@@ -91,6 +91,12 @@ class MyClass
   }
 };
 
+// Shader entry point without body
+[shader("compute")]
+[numthreads(4,1,1)]
+float main();
+
+// Shader entry point with body
 [shader("compute")]
 [numthreads(4,1,1)]
 float main() {
@@ -99,4 +105,4 @@ float main() {
   float a = alive(f);float b = aliveTemp<float>(f); // #aliveTemp_inst
   float c = C.makeF();
   return a * b * c;
-}
\ No newline at end of file
+}
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityRelaxedStage.hlsl b/clang/test/SemaHLSL/Availability/AvailabilityRelaxedStage.hlsl
index a24943d431d4b..dfb0000db99cd 100644
--- a/clang/test/SemaHLSL/Availability/AvailabilityRelaxedStage.hlsl
+++ b/clang/test/SemaHLSL/Availability/AvailabilityRelaxedStage.hlsl
@@ -95,4 +95,4 @@ float main() {
   float b = aliveTemp<float>(f); // #aliveTemp_inst
   float c = C.makeF();
   return a * b * c;
-}
\ No newline at end of file
+}
diff --git a/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl b/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
index 185b79be37be5..6333c63569327 100644
--- a/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
+++ b/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
@@ -1,9 +1,10 @@
 // RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel5.0-library -verify %s
 // WaveActiveCountBits is unavailable before ShaderModel 6.0.
 
-unsigned foo(bool b) {
-    // expected-warning@#site {{'WaveActiveCountBits' is only available on Shader Model 6.0 or newer}}
+[shader("compute")]
+[numthreads(8,8,1)]
+unsigned foo() {
+    // expected-error@#site {{'WaveActiveCountBits' is only available on Shader Model 6.0 or newer}}
     // expected-note at hlsl/hlsl_intrinsics.h:* {{'WaveActiveCountBits' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#site {{enclose 'WaveActiveCountBits' in a __builtin_available check to silence this warning}}
-    return hlsl::WaveActiveCountBits(b); // #site
+    return hlsl::WaveActiveCountBits(1); // #site
 }

>From 1e2b724bb250ea70d07d20cc5173133fd1a3b89f Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 14 May 2024 09:11:46 -0700
Subject: [PATCH 12/21] Code review feedback

---
 clang/lib/Sema/SemaAvailability.cpp | 46 +++++++++++++----------------
 1 file changed, 21 insertions(+), 25 deletions(-)

diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index a607a7ba2c2f5..663b6f35b869d 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -421,19 +421,16 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
         S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
         Introduced);
 
-    if (EnvironmentMatchesOrNone) {
-      unsigned DiagKind = UseNewWarning ? diag::warn_unguarded_availability_new
-                                        : diag::warn_unguarded_availability;
-
-      S.Diag(Loc, DiagKind)
-          << OffendingDecl << PlatformName << Introduced.getAsString()
-          << UseEnvironment << TargetEnvironment;
-    } else {
-      unsigned DiagKind =
-          UseNewWarning ? diag::warn_unguarded_availability_unavailable_new
-                        : diag::warn_unguarded_availability_unavailable;
-      S.Diag(Loc, DiagKind) << Loc << OffendingDecl;
-    }
+    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);
+
+    S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName
+                          << Introduced.getAsString() << UseEnvironment
+                          << TargetEnvironment;
 
     S.Diag(OffendingDecl->getLocation(),
            diag::note_partial_availability_specified_here)
@@ -850,18 +847,17 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
     bool UseEnvironment =
         (!AttrEnvironment.empty() && !TargetEnvironment.empty());
 
-    if (EnvironmentMatchesOrNone) {
-      unsigned DiagKind = UseNewDiagKind ? diag::warn_unguarded_availability_new
-                                         : diag::warn_unguarded_availability;
-      SemaRef.Diag(Range.getBegin(), DiagKind)
-          << Range << D << PlatformName << Introduced.getAsString()
-          << UseEnvironment << TargetEnvironment;
-    } else {
-      unsigned DiagKind =
-          UseNewDiagKind ? diag::warn_unguarded_availability_unavailable_new
-                         : diag::warn_unguarded_availability_unavailable;
-      SemaRef.Diag(Range.getBegin(), DiagKind) << Range << D;
-    }
+    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);
+
+    SemaRef.Diag(Range.getBegin(), DiagKind)
+        << Range << D << PlatformName << Introduced.getAsString()
+        << UseEnvironment << TargetEnvironment;
 
     SemaRef.Diag(OffendingDecl->getLocation(),
                  diag::note_partial_availability_specified_here)

>From 0a3d1f81d1f089b94d261f37904c02da298d5722 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 14 May 2024 18:45:28 -0700
Subject: [PATCH 13/21] Add more tests, disable availability strict parameter
 use with environment

---
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/lib/Sema/SemaDeclAttr.cpp               |   5 +
 ...attr-availability-shadermodel-compute.hlsl |  79 ++++++++++
 .../attr-availability-shadermodel-mesh.hlsl   |  73 +++++++++
 .../attr-availability-shadermodel-pixel.hlsl  |  63 ++++++++
 .../Sema/attr-availability-shadermodel.hlsl   | 140 ------------------
 6 files changed, 223 insertions(+), 140 deletions(-)
 create mode 100644 clang/test/Sema/attr-availability-shadermodel-compute.hlsl
 create mode 100644 clang/test/Sema/attr-availability-shadermodel-mesh.hlsl
 create mode 100644 clang/test/Sema/attr-availability-shadermodel-pixel.hlsl
 delete mode 100644 clang/test/Sema/attr-availability-shadermodel.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 32df5cb0e07f4..69236d6ca106a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3862,6 +3862,9 @@ def note_protocol_method : Note<
 def warn_availability_fuchsia_unavailable_minor : Warning<
   "Fuchsia API Level prohibits specifying a minor or sub-minor version">,
   InGroup<Availability>;
+def warn_availability_strict_not_supported_with_environment: Warning<
+  "strict parameter with environment is not yet supported">,
+  InGroup<Availability>;
 
 def warn_unguarded_availability :
   Warning<"%0 is only available %select{|in %4 environment }3on %1 %2 or newer">,
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index db71e69227a34..504fb906342fa 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2714,6 +2714,11 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
     }
   }
 
+  if (IsStrict && AL.getEnvironment()) {
+    S.Diag(AL.getStrictLoc(), diag::warn_availability_strict_not_supported_with_environment);
+    return;
+  }
+
   int PriorityModifier = AL.isPragmaClangAttribute()
                              ? Sema::AP_PragmaClangAttribute
                              : Sema::AP_Explicit;
diff --git a/clang/test/Sema/attr-availability-shadermodel-compute.hlsl b/clang/test/Sema/attr-availability-shadermodel-compute.hlsl
new file mode 100644
index 0000000000000..bcc55062ef873
--- /dev/null
+++ b/clang/test/Sema/attr-availability-shadermodel-compute.hlsl
@@ -0,0 +1,79 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel5.0-compute -fsyntax-only -verify %s
+
+// Platform shader model, no environment parameter
+__attribute__((availability(shadermodel, introduced = 6.0)))
+unsigned f1(); // #f1
+
+__attribute__((availability(shadermodel, introduced = 5.1)))
+unsigned f2(); // #f2
+
+__attribute__((availability(shadermodel, introduced = 5.0)))
+unsigned f3();
+
+// Platform shader model, environment parameter restricting earlier version,
+// available in all environments in higher versions
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0)))
+unsigned f4(); // #f4
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 5.0)))
+unsigned f5();
+
+// Platform shader model, environment parameter restricting earlier version,
+// never available in all environments in higher versions
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = mesh)))
+unsigned f6();  // #f6
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned f7(); // #f7
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned f8();
+
+// expected-warning@#f9_attr {{strict parameter with environment is not yet supported}}
+__attribute__((availability(shadermodel, strict, introduced = 6.0, environment = mesh)))  // #f9_attr
+unsigned f9();
+
+[numthreads(4,1,1)]
+int main() {
+    // expected-warning@#f1_call {{'f1' is only available on Shader Model 6.0 or newer}}
+    // expected-note@#f1 {{'f1' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#f1_call {{enclose 'f1' in a __builtin_available check to silence this warning}}
+    unsigned A = f1(); // #f1_call
+
+    // expected-warning@#f2_call {{'f2' is only available on Shader Model 5.1 or newer}}
+    // expected-note@#f2 {{'f2' has been marked as being introduced in Shader Model 5.1 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#f2_call {{enclose 'f2' in a __builtin_available check to silence this warning}}
+    unsigned B = f2(); // #f2_call
+
+    unsigned C = f3();
+
+    // expected-warning@#f4_call {{'f4' is only available on Shader Model 6.0 or newer}}
+    // expected-note@#f4 {{'f4' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#f4_call {{enclose 'f4' in a __builtin_available check to silence this warning}}
+    unsigned D = f4(); // #f4_call
+
+    unsigned E = f5();
+
+    // expected-warning@#f6_call {{'f6' is only available in compute shader environment on Shader Model 6.0 or newer}}
+    // expected-note@#f6 {{'f6' has been marked as being introduced in Shader Model 6.0 in compute shader environment here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#f6_call {{enclose 'f6' in a __builtin_available check to silence this warning}}
+    unsigned F = f6(); // #f6_call
+
+    // expected-warning@#f7_call {{'f7' is unavailable}}
+    // expected-note@#f7 {{'f7' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 compute shader environment}}
+    // expected-note@#f7_call {{enclose 'f7' in a __builtin_available check to silence this warning}}
+    unsigned G = f7(); // #f7_call
+
+    unsigned H = f8();
+
+    unsigned I = f9();
+
+    return 0;
+}
diff --git a/clang/test/Sema/attr-availability-shadermodel-mesh.hlsl b/clang/test/Sema/attr-availability-shadermodel-mesh.hlsl
new file mode 100644
index 0000000000000..40a7ddbb1de98
--- /dev/null
+++ b/clang/test/Sema/attr-availability-shadermodel-mesh.hlsl
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel5.0-mesh -fsyntax-only -verify %s
+
+// Platform shader model, no environment parameter
+__attribute__((availability(shadermodel, introduced = 6.0)))
+unsigned f1(); // #f1
+
+__attribute__((availability(shadermodel, introduced = 5.1)))
+unsigned f2(); // #f2
+
+__attribute__((availability(shadermodel, introduced = 5.0)))
+unsigned f3();
+
+// Platform shader model, environment parameter restricting earlier version,
+// available in all environments in higher versions
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0)))
+unsigned f4(); // #f4
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 5.0)))
+unsigned f5(); // #f5
+
+// Platform shader model, environment parameter restricting earlier version,
+// never available in all environments in higher versions
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = mesh)))
+unsigned f6();  // #f6
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned f7(); // #f7
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned f8(); // #f8
+
+[numthreads(4,1,1)]
+int main() {
+    // expected-warning@#f1_call {{'f1' is only available on Shader Model 6.0 or newer}}
+    // expected-note@#f1 {{'f1' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#f1_call {{enclose 'f1' in a __builtin_available check to silence this warning}}
+    unsigned A = f1(); // #f1_call
+
+    // expected-warning@#f2_call {{'f2' is only available on Shader Model 5.1 or newer}}
+    // expected-note@#f2 {{'f2' has been marked as being introduced in Shader Model 5.1 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#f2_call {{enclose 'f2' in a __builtin_available check to silence this warning}}
+    unsigned B = f2(); // #f2_call
+
+    unsigned C = f3();
+
+    // expected-warning@#f4_call {{'f4' is only available on Shader Model 6.0 or newer}}
+    // expected-note@#f4 {{'f4' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#f4_call {{enclose 'f4' in a __builtin_available check to silence this warning}}
+    unsigned D = f4(); // #f4_call
+
+    unsigned E = f5(); // #f5_call
+
+    unsigned F = f6(); // #f6_call
+
+    // expected-warning@#f7_call {{'f7' is only available in mesh shader environment on Shader Model 6.0 or newer}}
+    // expected-note@#f7 {{'f7' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 mesh shader environment}}
+    // expected-note@#f7_call {{enclose 'f7' in a __builtin_available check to silence this warning}}
+    unsigned G = f7(); // #f7_call
+
+    // expected-warning@#f8_call {{'f8' is only available in mesh shader environment on Shader Model 6.0 or newer}}
+    // expected-note@#f8 {{'f8' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 mesh shader environment}}
+    // expected-note@#f8_call {{enclose 'f8' in a __builtin_available check to silence this warning}}
+    unsigned H = f8(); // #f8_call
+
+    return 0;
+}
diff --git a/clang/test/Sema/attr-availability-shadermodel-pixel.hlsl b/clang/test/Sema/attr-availability-shadermodel-pixel.hlsl
new file mode 100644
index 0000000000000..59d09a9cd276f
--- /dev/null
+++ b/clang/test/Sema/attr-availability-shadermodel-pixel.hlsl
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel5.0-pixel -fsyntax-only -verify %s
+
+// Platform shader model, no environment parameter
+__attribute__((availability(shadermodel, introduced = 6.0)))
+unsigned f1(); // #f1
+
+__attribute__((availability(shadermodel, introduced = 5.1)))
+unsigned f2(); // #f2
+
+__attribute__((availability(shadermodel, introduced = 5.0)))
+unsigned f3();
+
+// Platform shader model, environment parameter restricting earlier version,
+// available in all environments in higher versions
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0)))
+unsigned f4(); // #f4
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 5.0)))
+unsigned f5();
+
+// Platform shader model, environment parameter restricting earlier version,
+// never available in all environments in higher versions
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = mesh)))
+unsigned f6();  // #f6
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned f7(); // #f7
+
+__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
+__attribute__((availability(shadermodel, introduced = 5.0, environment = compute)))
+__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
+unsigned f8();
+
+int main() {
+    // expected-warning@#f1_call {{'f1' is only available on Shader Model 6.0 or newer}}
+    // expected-note@#f1 {{'f1' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#f1_call {{enclose 'f1' in a __builtin_available check to silence this warning}}
+    unsigned A = f1(); // #f1_call
+
+    // expected-warning@#f2_call {{'f2' is only available on Shader Model 5.1 or newer}}
+    // expected-note@#f2 {{'f2' has been marked as being introduced in Shader Model 5.1 here, but the deployment target is Shader Model 5.0}}
+    // expected-note@#f2_call {{enclose 'f2' in a __builtin_available check to silence this warning}}
+    unsigned B = f2(); // #f2_call
+
+    unsigned C = f3();
+
+    unsigned D = f4(); // #f4_call
+
+    unsigned E = f5();
+
+    unsigned F = f6(); // #f6_call
+
+    unsigned G = f7(); // #f7_call
+
+    unsigned H = f8();
+
+    return 0;
+}
diff --git a/clang/test/Sema/attr-availability-shadermodel.hlsl b/clang/test/Sema/attr-availability-shadermodel.hlsl
deleted file mode 100644
index 3339656354f29..0000000000000
--- a/clang/test/Sema/attr-availability-shadermodel.hlsl
+++ /dev/null
@@ -1,140 +0,0 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel5.0-compute -fsyntax-only -verify %s
-
-// Platform shader model, no environment parameter
-__attribute__((availability(shadermodel, introduced = 6.0)))
-unsigned fn6_0(); // #fn6_0
-
-__attribute__((availability(shadermodel, introduced = 5.1)))
-unsigned fn5_1(); // #fn5_1
-
-__attribute__((availability(shadermodel, introduced = 5.0)))
-unsigned fn5_0();
-
-// Platform shader model, no environment parameter
-// - with strict flag (executes different code path)
-__attribute__((availability(shadermodel, strict, introduced = 6.0)))
-unsigned fn6_0_s(); // #fn6_0_s
-
-__attribute__((availability(shadermodel, strict, introduced = 5.1)))
-unsigned fn5_1_s(); // #fn5_1_s
-
-__attribute__((availability(shadermodel, strict, introduced = 5.0)))
-unsigned fn5_0_s();
-
-// Platform shader model, environment parameter restricting earlier version,
-// available in all environments in higher versions
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 6.0)))
-unsigned fn6_0_mix(); // #fn6_0_mix
-
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 5.0)))
-unsigned fn5_0_mix();
-
-// Platform shader model, environment parameter restricting earlier version,
-// available in all environments in higher versions
-// - with strict flag (executes different code path)
-__attribute__((availability(shadermodel, strict, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, strict, introduced = 6.0)))
-unsigned fn6_0_mix_s(); // #fn6_0_mix_s
-
-__attribute__((availability(shadermodel, strict, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, strict, introduced = 5.0)))
-unsigned fn5_0_mix_s();
-
-// Platform shader model, environment parameter restricting earlier version,
-// never available in all environments in higher versions
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = compute)))
-__attribute__((availability(shadermodel, introduced = 5.0, environment = mesh)))
-unsigned fn6_0_stages1();  // #fn6_0_stages1
-
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
-unsigned fn6_0_stages2(); // #fn6_0_stages2
-
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 5.0, environment = compute)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
-unsigned fn5_0_stages();
-
-// Platform shader model, environment parameter restricting earlier version,
-// never available in all environments in higher versions
-// - with strict flag (executes different code path)
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = compute)))
-__attribute__((availability(shadermodel, introduced = 5.0, environment = mesh)))
-unsigned fn6_0_stages1_s();  // #fn6_0_stages1_s
-
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
-unsigned fn6_0_stages2_s(); // #fn6_0_stages2_s
-
-__attribute__((availability(shadermodel, introduced = 2.0, environment = pixel)))
-__attribute__((availability(shadermodel, introduced = 5.0, environment = compute)))
-__attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
-unsigned fn5_0_stages_s();
-
-[numthreads(4,1,1)]
-int main() {
-    // expected-warning@#fn6_0_call {{'fn6_0' is only available on Shader Model 6.0 or newer}}
-    // expected-note@#fn6_0 {{'fn6_0' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn6_0_call {{enclose 'fn6_0' in a __builtin_available check to silence this warning}}
-    unsigned A1 = fn6_0(); // #fn6_0_call
-
-    // expected-warning@#fn5_1_call {{'fn5_1' is only available on Shader Model 5.1 or newer}}
-    // expected-note@#fn5_1 {{'fn5_1' has been marked as being introduced in Shader Model 5.1 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn5_1_call {{enclose 'fn5_1' in a __builtin_available check to silence this warning}}
-    unsigned B1 = fn5_1(); // #fn5_1_call
-
-    unsigned C1 = fn5_0();
-
-    // expected-error@#fn6_0_s_call {{'fn6_0_s' is unavailable: introduced in Shader Model 6.0 compute shader}}
-    // expected-note@#fn6_0_s {{'fn6_0_s' has been explicitly marked unavailable here}}
-    unsigned A2 = fn6_0_s(); // #fn6_0_s_call
-
-    // expected-error@#fn5_1_s_call {{'fn5_1_s' is unavailable: introduced in Shader Model 5.1 compute shader}}
-    // expected-note@#fn5_1_s {{'fn5_1_s' has been explicitly marked unavailable here}}
-    unsigned B2 = fn5_1_s(); // #fn5_1_s_call
-
-    unsigned C2 = fn5_0_s();
-
-    // expected-warning@#fn6_0_mix_call {{'fn6_0_mix' is only available on Shader Model 6.0 or newer}}
-    // expected-note@#fn6_0_mix {{'fn6_0_mix' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn6_0_mix_call {{enclose 'fn6_0_mix' in a __builtin_available check to silence this warning}}
-    unsigned A3 = fn6_0_mix(); // #fn6_0_mix_call
-
-    unsigned B3 = fn5_0_mix();
-
-    // expected-error@#fn6_0_mix_s_call {{'fn6_0_mix_s' is unavailable: introduced in Shader Model 6.0 compute shader}}
-    // expected-note@#fn6_0_mix_s {{'fn6_0_mix_s' has been explicitly marked unavailable here}}
-    unsigned A4 = fn6_0_mix_s(); // #fn6_0_mix_s_call
-
-    unsigned B4 = fn5_0_mix_s();
-
-    // expected-warning@#fn6_0_stages1_call {{'fn6_0_stages1' is only available in compute shader environment on Shader Model 6.0 or newer}}
-    // expected-note@#fn6_0_stages1 {{'fn6_0_stages1' has been marked as being introduced in Shader Model 6.0 in compute shader environment here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn6_0_stages1_call {{enclose 'fn6_0_stages1' in a __builtin_available check to silence this warning}}
-    unsigned A5 = fn6_0_stages1(); // #fn6_0_stages1_call
-
-    // expected-warning@#fn6_0_stages2_call {{'fn6_0_stages2' is unavailable}}
-    // expected-note@#fn6_0_stages2 {{'fn6_0_stages2' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 compute shader environment}}
-    // expected-note@#fn6_0_stages2_call {{enclose 'fn6_0_stages2' in a __builtin_available check to silence this warning}}
-    unsigned B5 = fn6_0_stages2(); // #fn6_0_stages2_call
-
-    unsigned C5 = fn5_0_stages();
-
-    // expected-warning@#fn6_0_stages1_s_call {{'fn6_0_stages1_s' is only available in compute shader environment on Shader Model 6.0 or newer}}
-    // expected-note@#fn6_0_stages1_s {{'fn6_0_stages1_s' has been marked as being introduced in Shader Model 6.0 in compute shader environment here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#fn6_0_stages1_s_call {{enclose 'fn6_0_stages1_s' in a __builtin_available check to silence this warning}}
-    unsigned A6 = fn6_0_stages1_s(); // #fn6_0_stages1_s_call
-
-    // expected-warning@#fn6_0_stages2_s_call {{'fn6_0_stages2_s' is unavailable}}
-    // expected-note@#fn6_0_stages2_s {{'fn6_0_stages2_s' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 compute shader environment}}
-    // expected-note@#fn6_0_stages2_s_call {{enclose 'fn6_0_stages2_s' in a __builtin_available check to silence this warning}}
-    unsigned B6 = fn6_0_stages2_s(); // #fn6_0_stages2_s_call
-
-    unsigned C6 = fn5_0_stages_s();
-
-    return 0;
-}

>From 48d31681232f9f5554612ecc156d3c28637c3ca6 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 14 May 2024 19:29:33 -0700
Subject: [PATCH 14/21] clang-format

---
 clang/lib/Sema/SemaDeclAttr.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 504fb906342fa..9ea5a966429da 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2715,7 +2715,8 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   }
 
   if (IsStrict && AL.getEnvironment()) {
-    S.Diag(AL.getStrictLoc(), diag::warn_availability_strict_not_supported_with_environment);
+    S.Diag(AL.getStrictLoc(),
+           diag::warn_availability_strict_not_supported_with_environment);
     return;
   }
 

>From ef7263558218f34e2fa173c444d160f741e63e4c Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 15 May 2024 14:31:34 -0700
Subject: [PATCH 15/21] Address feedback - restricting environment parameter to
 HLSL only and prohibiting strict flag in HLSL

---
 clang/include/clang/Basic/AttrDocs.td         |  2 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |  7 ++++--
 clang/lib/Sema/SemaDeclAttr.cpp               | 24 ++++++++++---------
 clang/test/Parser/attr-availability.c         |  4 +---
 ...attr-availability-shadermodel-compute.hlsl |  4 +---
 .../Sema/attr-availability-shadermodel.hlsl   | 11 +++++++++
 6 files changed, 32 insertions(+), 20 deletions(-)
 create mode 100644 clang/test/Sema/attr-availability-shadermodel.hlsl

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index a2077ce4cec08..2b27bd01ea7c8 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1596,7 +1596,7 @@ replacement=\ *string-literal*
 environment=\ *identifier*
   Target environment in which this declaration is available. If present,
   the availability attribute applies only to targets with the same platform
-  and environment.
+  and environment. The parameter is currently supported only in HLSL.
 
 Multiple availability attributes can be placed on a declaration, which may
 correspond to different platforms. For most platforms, the availability
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 69236d6ca106a..200d3e3c3625f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3862,8 +3862,11 @@ def note_protocol_method : Note<
 def warn_availability_fuchsia_unavailable_minor : Warning<
   "Fuchsia API Level prohibits specifying a minor or sub-minor version">,
   InGroup<Availability>;
-def warn_availability_strict_not_supported_with_environment: Warning<
-  "strict parameter with environment is not yet supported">,
+def warn_availability_hlsl_unavailable_strict: Warning<
+  "strict parameter is not supported in HLSL">,
+  InGroup<Availability>;
+def warn_availability_environment_only_in_hlsl: Warning<
+  "environment parameter is supported only in HLSL">,
   InGroup<Availability>;
 
 def warn_unguarded_availability :
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 9ea5a966429da..86112dcd2cadc 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2714,11 +2714,8 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
     }
   }
 
-  if (IsStrict && AL.getEnvironment()) {
-    S.Diag(AL.getStrictLoc(),
-           diag::warn_availability_strict_not_supported_with_environment);
-    return;
-  }
+  if (S.getLangOpts().HLSL && IsStrict)
+    S.Diag(AL.getStrictLoc(), diag::warn_availability_hlsl_unavailable_strict);
 
   int PriorityModifier = AL.isPragmaClangAttribute()
                              ? Sema::AP_PragmaClangAttribute
@@ -2727,12 +2724,17 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   const IdentifierLoc *EnvironmentLoc = AL.getEnvironment();
   IdentifierInfo *IIEnvironment = nullptr;
   if (EnvironmentLoc) {
-    IIEnvironment = EnvironmentLoc->Ident;
-    if (AvailabilityAttr::getEnvironmentType(
-            EnvironmentLoc->Ident->getName()) ==
-        llvm::Triple::EnvironmentType::UnknownEnvironment)
-      S.Diag(Platform->Loc, diag::warn_availability_unknown_environment)
-          << EnvironmentLoc->Ident;
+    if (S.getLangOpts().HLSL) {
+      IIEnvironment = EnvironmentLoc->Ident;
+      if (AvailabilityAttr::getEnvironmentType(
+              EnvironmentLoc->Ident->getName()) ==
+          llvm::Triple::EnvironmentType::UnknownEnvironment)
+        S.Diag(EnvironmentLoc->Loc, diag::warn_availability_unknown_environment)
+            << EnvironmentLoc->Ident;
+    } else {
+      S.Diag(EnvironmentLoc->Loc,
+             diag::warn_availability_environment_only_in_hlsl);
+    }
   }
 
   AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(
diff --git a/clang/test/Parser/attr-availability.c b/clang/test/Parser/attr-availability.c
index 8ba120f944a73..0d1ef5627a74b 100644
--- a/clang/test/Parser/attr-availability.c
+++ b/clang/test/Parser/attr-availability.c
@@ -30,9 +30,7 @@ void f11(void) __attribute__((availability(macosx,message=u"b"))); // expected-w
 
 void f12(void) __attribute__((availability(macosx,message="a" u"b"))); // expected-warning {{encoding prefix 'u' on an unevaluated string literal has no effect}}
 
-void f13(void) __attribute__((availability(shadermodel, introduced = 6.0, environment="pixel"))); // expected-error {{expected an environment name, e.g., 'compute'}}
-
-void f14(void) __attribute__((availability(shadermodel, introduced = 6.0, environment=pixel, environment=compute))); // expected-error {{redundant 'environment' availability change; only the last specified change will be used}}
+void f13(void) __attribute__((availability(shadermodel, introduced = 6.0, environment=pixel))); // expected-warning {{environment parameter is supported only in HLSL}}
 
 enum E{
     gorf __attribute__((availability(macosx,introduced=8.5, message = 10.0))), // expected-error {{expected string literal for optional message in 'availability' attribute}}
diff --git a/clang/test/Sema/attr-availability-shadermodel-compute.hlsl b/clang/test/Sema/attr-availability-shadermodel-compute.hlsl
index bcc55062ef873..6347a5fe81ae8 100644
--- a/clang/test/Sema/attr-availability-shadermodel-compute.hlsl
+++ b/clang/test/Sema/attr-availability-shadermodel-compute.hlsl
@@ -36,7 +36,7 @@ __attribute__((availability(shadermodel, introduced = 5.0, environment = compute
 __attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
 unsigned f8();
 
-// expected-warning@#f9_attr {{strict parameter with environment is not yet supported}}
+// expected-warning@#f9_attr {{strict parameter is not supported in HLSL}}
 __attribute__((availability(shadermodel, strict, introduced = 6.0, environment = mesh)))  // #f9_attr
 unsigned f9();
 
@@ -73,7 +73,5 @@ int main() {
 
     unsigned H = f8();
 
-    unsigned I = f9();
-
     return 0;
 }
diff --git a/clang/test/Sema/attr-availability-shadermodel.hlsl b/clang/test/Sema/attr-availability-shadermodel.hlsl
new file mode 100644
index 0000000000000..f2402acd8f96c
--- /dev/null
+++ b/clang/test/Sema/attr-availability-shadermodel.hlsl
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.5-library -fsyntax-only -verify %s
+
+
+void f1(void) __attribute__((availability(shadermodel, introduced = 6.0, environment="pixel"))); // expected-error {{expected an environment name, e.g., 'compute'}}
+
+void f2(void) __attribute__((availability(shadermodel, introduced = 6.0, environment=pixel, environment=compute))); // expected-error {{redundant 'environment' availability change; only the last specified change will be used}}
+
+void f3(void) __attribute__((availability(shadermodel, strict, introduced = 6.0, environment = mesh)));  // expected-warning {{strict parameter is not supported in HLSL}}
+
+int main() {
+}

>From b28b689e2ee3dc77f6c0e743e0059407d2d94f1e Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 15 May 2024 18:47:44 -0700
Subject: [PATCH 16/21] Assert, avoid duplicate messages, remove strict flag
 check, clang-format, add library test with multiple stages

---
 clang/lib/Sema/SemaDeclAttr.cpp               |  5 --
 clang/lib/Sema/SemaHLSL.cpp                   | 57 ++++++++++++++-----
 .../AvailabilityMultipleStages.hlsl           | 57 +++++++++++++++++++
 3 files changed, 99 insertions(+), 20 deletions(-)
 create mode 100644 clang/test/SemaHLSL/Availability/AvailabilityMultipleStages.hlsl

diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 195364aa38391..3b79f34c091cb 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2713,11 +2713,6 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
     }
   }
 
-  if (S.getLangOpts().HLSL && IsStrict) {
-    S.Diag(AL.getStrictLoc(), diag::warn_availability_hlsl_strict_not_supported);
-    return;
-  }
-
   int PriorityModifier = AL.isPragmaClangAttribute()
                              ? Sema::AP_PragmaClangAttribute
                              : Sema::AP_Explicit;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 063acb352035a..286bd1354fd9f 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -309,7 +309,8 @@ namespace {
 /// and of all exported functions, and any functions that are refrenced
 /// from this AST. In other words, any function that are reachable from
 /// the entry points.
-class DiagnoseHLSLAvailability : public RecursiveASTVisitor<DiagnoseHLSLAvailability> {
+class DiagnoseHLSLAvailability
+    : public RecursiveASTVisitor<DiagnoseHLSLAvailability> {
   // HEKOTAS this is probably not needed
   // typedef RecursiveASTVisitor<DiagnoseHLSLAvailability> Base;
 
@@ -339,7 +340,14 @@ class DiagnoseHLSLAvailability : public RecursiveASTVisitor<DiagnoseHLSLAvailabi
   llvm::Triple::EnvironmentType CurrentShaderEnvironment;
   unsigned CurrentShaderStageBit;
 
+  // True if scanning a function that was already scanned in a different
+  // shader stage context, and therefore we should not report issues that 
+  // depend only on shader model version because they would be duplicate.
+  bool ReportOnlyShaderStageIssues;
+
   void SetShaderStageContext(HLSLShaderAttr::ShaderType ShaderType) {
+    assert((((unsigned)1) << (unsigned)ShaderType) != 0 &&
+           "ShaderType is too big for this bitmap");
     CurrentShaderEnvironment = HLSLShaderAttr::getTypeAsEnvironment(ShaderType);
     CurrentShaderStageBit = (1 << ShaderType);
   }
@@ -362,7 +370,8 @@ class DiagnoseHLSLAvailability : public RecursiveASTVisitor<DiagnoseHLSLAvailabi
                              SourceRange Range);
   const AvailabilityAttr *FindAvailabilityAttr(const Decl *D);
   bool HasMatchingEnvironmentOrNone(const AvailabilityAttr *AA);
-  bool WasAlreadyScanned(const FunctionDecl *FD);
+  bool WasAlreadyScannedInCurrentShaderStage(const FunctionDecl *FD,
+                                             bool *WasNeverScanned = nullptr);
   void AddToScannedFunctions(const FunctionDecl *FD);
 
 public:
@@ -388,9 +397,13 @@ class DiagnoseHLSLAvailability : public RecursiveASTVisitor<DiagnoseHLSLAvailabi
 };
 
 // Returns true if the function has already been scanned in the current
-// shader environment
-bool DiagnoseHLSLAvailability::WasAlreadyScanned(const FunctionDecl *FD) {
+// shader environment. WasNeverScanned will be set to true if the function
+// has never been scanned before for any shader environment.
+bool DiagnoseHLSLAvailability::WasAlreadyScannedInCurrentShaderStage(
+    const FunctionDecl *FD, bool *WasNeverScanned) {
   const unsigned &ScannedStages = ScannedDecls.getOrInsertDefault(FD);
+  if (WasNeverScanned)
+    *WasNeverScanned = (ScannedStages == 0);
   return ScannedStages & CurrentShaderStageBit;
 }
 
@@ -400,13 +413,15 @@ void DiagnoseHLSLAvailability::AddToScannedFunctions(const FunctionDecl *FD) {
   ScannedStages |= CurrentShaderStageBit;
 }
 
-void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD, Expr *RefExpr) {
-  assert((isa<DeclRefExpr>(RefExpr) || isa<MemberExpr>(RefExpr)) && "expected DeclRefExpr or MemberExpr");
+void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD,
+                                                         Expr *RefExpr) {
+  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 (!WasAlreadyScanned(FDWithBody))
+    if (!WasAlreadyScannedInCurrentShaderStage(FDWithBody))
       DeclsToScan.push_back(FDWithBody);
     return;
   }
@@ -418,7 +433,8 @@ void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD, Expr
         FD, AA, SourceRange(RefExpr->getBeginLoc(), RefExpr->getEndLoc()));
 }
 
-void DiagnoseHLSLAvailability::RunOnTranslationUnit(const TranslationUnitDecl *TU) {
+void DiagnoseHLSLAvailability::RunOnTranslationUnit(
+    const TranslationUnitDecl *TU) {
   // Iterate over all shader entry functions and library exports, and for those
   // that have a body (definiton), run diag scan on each, setting appropriate
   // shader environment context based on whether it is a shader entry function
@@ -452,15 +468,19 @@ void DiagnoseHLSLAvailability::RunOnFunction(const FunctionDecl *FD) {
 
   while (!DeclsToScan.empty()) {
     // Take one decl from the stack and check it by traversing its AST.
-    // For any CallExpr found during the traversal add it's callee to the top of the stack 
-    // to be processed next. Functions already processed are stored in ScannedDecls.
+    // For any CallExpr found during the traversal add it's callee to the top of
+    // the stack to be processed next. Functions already processed are stored in
+    // ScannedDecls.
     const FunctionDecl *FD = DeclsToScan.back();
     DeclsToScan.pop_back();
 
     // Decl was already scanned
-    if (WasAlreadyScanned(FD))
+    bool WasNeverScanned;
+    if (WasAlreadyScannedInCurrentShaderStage(FD, &WasNeverScanned))
       continue;
 
+    ReportOnlyShaderStageIssues = !WasNeverScanned;
+
     AddToScannedFunctions(FD);
 
     Stmt *Body = FD->getBody();
@@ -470,7 +490,8 @@ void DiagnoseHLSLAvailability::RunOnFunction(const FunctionDecl *FD) {
   }
 }
 
-bool DiagnoseHLSLAvailability::HasMatchingEnvironmentOrNone(const AvailabilityAttr *AA) {
+bool DiagnoseHLSLAvailability::HasMatchingEnvironmentOrNone(
+    const AvailabilityAttr *AA) {
   IdentifierInfo *IIEnvironment = AA->getEnvironment();
   if (!IIEnvironment)
     return true;
@@ -485,7 +506,8 @@ bool DiagnoseHLSLAvailability::HasMatchingEnvironmentOrNone(const AvailabilityAt
   return CurrentEnv == AttrEnv;
 }
 
-const AvailabilityAttr *DiagnoseHLSLAvailability::FindAvailabilityAttr(const Decl *D) {
+const AvailabilityAttr *
+DiagnoseHLSLAvailability::FindAvailabilityAttr(const Decl *D) {
   AvailabilityAttr const *PartialMatch = nullptr;
   // Check each AvailabilityAttr to find the one for this platform.
   // For multiple attributes with the same platform try to find one for this
@@ -493,7 +515,8 @@ const AvailabilityAttr *DiagnoseHLSLAvailability::FindAvailabilityAttr(const Dec
   for (const auto *A : D->attrs()) {
     if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) {
       StringRef AttrPlatform = Avail->getPlatform()->getName();
-      StringRef TargetPlatform = SemaRef.getASTContext().getTargetInfo().getPlatformName();
+      StringRef TargetPlatform =
+          SemaRef.getASTContext().getTargetInfo().getPlatformName();
 
       // Match the platform name.
       if (AttrPlatform == TargetPlatform) {
@@ -512,9 +535,13 @@ const AvailabilityAttr *DiagnoseHLSLAvailability::FindAvailabilityAttr(const Dec
 void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
                                                      const AvailabilityAttr *AA,
                                                      SourceRange Range) {
+  if (ReportOnlyShaderStageIssues && !AA->getEnvironment())
+    return;
+
   bool EnvironmentMatches = HasMatchingEnvironmentOrNone(AA);
   VersionTuple Introduced = AA->getIntroduced();
-  VersionTuple TargetVersion = SemaRef.Context.getTargetInfo().getPlatformMinVersion();
+  VersionTuple TargetVersion =
+      SemaRef.Context.getTargetInfo().getPlatformMinVersion();
 
   if (TargetVersion >= Introduced && EnvironmentMatches)
     return;
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityMultipleStages.hlsl b/clang/test/SemaHLSL/Availability/AvailabilityMultipleStages.hlsl
new file mode 100644
index 0000000000000..ae096ebe8be23
--- /dev/null
+++ b/clang/test/SemaHLSL/Availability/AvailabilityMultipleStages.hlsl
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library \
+// RUN: -fsyntax-only -verify %s
+
+__attribute__((availability(shadermodel, introduced = 6.5)))
+float fx(float);  // #fx
+
+__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 = compute)))
+float fz(float); // #fz
+
+
+void F(float f) {
+  // Make sure we only get this error once, even though this function is scanned twice - once
+  // in compute shader context and once in pixel shader context.
+  // expected-error@#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); // #fx_call
+  
+  // expected-error@#fy_call {{'fy' is only available in compute shader 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 shader environment here, but the deployment target is Shader Model 6.0 compute shader environment}}
+  float B = fy(f); // #fy_call
+
+  // expected-error@#fz_call {{'fz' is unavailable}}
+  // expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 5.0 in compute shader environment here, but the deployment target is Shader Model 6.0 pixel shader environment}}
+  float X = fz(f); // #fz_call
+}
+
+void deadCode(float f) {
+  // no diagnostics expected under default diagnostic mode
+  float A = fx(f);
+  float B = fy(f);
+  float X = fz(f);
+}
+
+// Pixel shader
+[shader("pixel")]
+void mainPixel() {
+  F(1.0);
+}
+
+// First Compute shader
+[shader("compute")]
+[numthreads(4,1,1)]
+void mainCompute1() {
+  F(2.0);
+}
+
+// Second compute shader to make sure we do not get duplicate messages if F is called
+// from multiple entry points.
+[shader("compute")]
+[numthreads(4,1,1)]
+void mainCompute2() {
+  F(3.0);
+}

>From 74415357604eab1ddfb2b2c52d39933d42fd9f54 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 16 May 2024 10:43:04 -0700
Subject: [PATCH 17/21] Code review feedback - unify diag message and make it
 an error

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td          | 8 ++------
 clang/lib/Sema/SemaDeclAttr.cpp                           | 7 ++++---
 clang/test/Sema/attr-availability-ios.c                   | 1 +
 .../test/Sema/attr-availability-shadermodel-compute.hlsl  | 4 ----
 clang/test/Sema/attr-availability-shadermodel.hlsl        | 2 +-
 5 files changed, 8 insertions(+), 14 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 200d3e3c3625f..762fd9829c3ce 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3862,12 +3862,8 @@ def note_protocol_method : Note<
 def warn_availability_fuchsia_unavailable_minor : Warning<
   "Fuchsia API Level prohibits specifying a minor or sub-minor version">,
   InGroup<Availability>;
-def warn_availability_hlsl_unavailable_strict: Warning<
-  "strict parameter is not supported in HLSL">,
-  InGroup<Availability>;
-def warn_availability_environment_only_in_hlsl: Warning<
-  "environment parameter is supported only in HLSL">,
-  InGroup<Availability>;
+def err_availability_unexpected_parameter: Error<
+  "unexpected parameter '%0' in availability attribute, not permitted in %select{HLSL|C/C++}1">;
 
 def warn_unguarded_availability :
   Warning<"%0 is only available %select{|in %4 environment }3on %1 %2 or newer">,
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 86112dcd2cadc..6f9746961f48c 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2715,7 +2715,8 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   }
 
   if (S.getLangOpts().HLSL && IsStrict)
-    S.Diag(AL.getStrictLoc(), diag::warn_availability_hlsl_unavailable_strict);
+    S.Diag(AL.getStrictLoc(), diag::err_availability_unexpected_parameter)
+        << "strict" << /* HLSL */ 0;
 
   int PriorityModifier = AL.isPragmaClangAttribute()
                              ? Sema::AP_PragmaClangAttribute
@@ -2732,8 +2733,8 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
         S.Diag(EnvironmentLoc->Loc, diag::warn_availability_unknown_environment)
             << EnvironmentLoc->Ident;
     } else {
-      S.Diag(EnvironmentLoc->Loc,
-             diag::warn_availability_environment_only_in_hlsl);
+      S.Diag(EnvironmentLoc->Loc, diag::err_availability_unexpected_parameter)
+          << "environment" << /* C/C++ */ 1;
     }
   }
 
diff --git a/clang/test/Sema/attr-availability-ios.c b/clang/test/Sema/attr-availability-ios.c
index b97b7e688cc61..b001e70b5ff5c 100644
--- a/clang/test/Sema/attr-availability-ios.c
+++ b/clang/test/Sema/attr-availability-ios.c
@@ -9,6 +9,7 @@ void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,
 void f5(int) __attribute__((availability(ios,introduced=2.0))) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{'f5' has been explicitly marked deprecated here}}
 void f6(int) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{'f6' has been explicitly marked deprecated here}}
 void f6(int) __attribute__((availability(iOS,introduced=2.0)));
+void f7(int) __attribute__((availability(ios,introduced=2.0, environment=e))); // expected-error {{unexpected parameter 'environment' in availability attribute, not permitted in C/C++}}
 
 void test(void) {
   f0(0); // expected-warning{{'f0' is deprecated: first deprecated in iOS 2.1}}
diff --git a/clang/test/Sema/attr-availability-shadermodel-compute.hlsl b/clang/test/Sema/attr-availability-shadermodel-compute.hlsl
index 6347a5fe81ae8..8fa696ea11649 100644
--- a/clang/test/Sema/attr-availability-shadermodel-compute.hlsl
+++ b/clang/test/Sema/attr-availability-shadermodel-compute.hlsl
@@ -36,10 +36,6 @@ __attribute__((availability(shadermodel, introduced = 5.0, environment = compute
 __attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
 unsigned f8();
 
-// expected-warning@#f9_attr {{strict parameter is not supported in HLSL}}
-__attribute__((availability(shadermodel, strict, introduced = 6.0, environment = mesh)))  // #f9_attr
-unsigned f9();
-
 [numthreads(4,1,1)]
 int main() {
     // expected-warning@#f1_call {{'f1' is only available on Shader Model 6.0 or newer}}
diff --git a/clang/test/Sema/attr-availability-shadermodel.hlsl b/clang/test/Sema/attr-availability-shadermodel.hlsl
index f2402acd8f96c..2682eb5fbb5c2 100644
--- a/clang/test/Sema/attr-availability-shadermodel.hlsl
+++ b/clang/test/Sema/attr-availability-shadermodel.hlsl
@@ -5,7 +5,7 @@ void f1(void) __attribute__((availability(shadermodel, introduced = 6.0, environ
 
 void f2(void) __attribute__((availability(shadermodel, introduced = 6.0, environment=pixel, environment=compute))); // expected-error {{redundant 'environment' availability change; only the last specified change will be used}}
 
-void f3(void) __attribute__((availability(shadermodel, strict, introduced = 6.0, environment = mesh)));  // expected-warning {{strict parameter is not supported in HLSL}}
+void f3(void) __attribute__((availability(shadermodel, strict, introduced = 6.0, environment = mesh))); // expected-error {{unexpected parameter 'strict' in availability attribute, not permitted in HLSL}}
 
 int main() {
 }

>From 055a5d71a192d217ae4d421059be21d0aaaa6777 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 16 May 2024 12:10:58 -0700
Subject: [PATCH 18/21] One more test update

---
 clang/test/Parser/attr-availability.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Parser/attr-availability.c b/clang/test/Parser/attr-availability.c
index 0d1ef5627a74b..9d84d9c1df363 100644
--- a/clang/test/Parser/attr-availability.c
+++ b/clang/test/Parser/attr-availability.c
@@ -30,7 +30,7 @@ void f11(void) __attribute__((availability(macosx,message=u"b"))); // expected-w
 
 void f12(void) __attribute__((availability(macosx,message="a" u"b"))); // expected-warning {{encoding prefix 'u' on an unevaluated string literal has no effect}}
 
-void f13(void) __attribute__((availability(shadermodel, introduced = 6.0, environment=pixel))); // expected-warning {{environment parameter is supported only in HLSL}}
+void f13(void) __attribute__((availability(shadermodel, introduced = 6.0, environment=pixel))); // expected-error {{unexpected parameter 'environment' in availability attribute, not permitted in C/C++}}
 
 enum E{
     gorf __attribute__((availability(macosx,introduced=8.5, message = 10.0))), // expected-error {{expected string literal for optional message in 'availability' attribute}}

>From a206e8377906bc77fdb3a822b3127dae601526b3 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 16 May 2024 12:36:16 -0700
Subject: [PATCH 19/21] Update new tests after merge from
 availability-environment-param

---
 .../attr-availability-shadermodel-compute.hlsl    | 15 +++++----------
 .../Sema/attr-availability-shadermodel-mesh.hlsl  | 15 +++++----------
 .../Sema/attr-availability-shadermodel-pixel.hlsl |  6 ++----
 3 files changed, 12 insertions(+), 24 deletions(-)

diff --git a/clang/test/Sema/attr-availability-shadermodel-compute.hlsl b/clang/test/Sema/attr-availability-shadermodel-compute.hlsl
index 8fa696ea11649..dd02148088af5 100644
--- a/clang/test/Sema/attr-availability-shadermodel-compute.hlsl
+++ b/clang/test/Sema/attr-availability-shadermodel-compute.hlsl
@@ -38,33 +38,28 @@ unsigned f8();
 
 [numthreads(4,1,1)]
 int main() {
-    // expected-warning@#f1_call {{'f1' is only available on Shader Model 6.0 or newer}}
+    // expected-error@#f1_call {{'f1' is only available on Shader Model 6.0 or newer}}
     // expected-note@#f1 {{'f1' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#f1_call {{enclose 'f1' in a __builtin_available check to silence this warning}}
     unsigned A = f1(); // #f1_call
 
-    // expected-warning@#f2_call {{'f2' is only available on Shader Model 5.1 or newer}}
+    // expected-error@#f2_call {{'f2' is only available on Shader Model 5.1 or newer}}
     // expected-note@#f2 {{'f2' has been marked as being introduced in Shader Model 5.1 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#f2_call {{enclose 'f2' in a __builtin_available check to silence this warning}}
     unsigned B = f2(); // #f2_call
 
     unsigned C = f3();
 
-    // expected-warning@#f4_call {{'f4' is only available on Shader Model 6.0 or newer}}
+    // expected-error@#f4_call {{'f4' is only available on Shader Model 6.0 or newer}}
     // expected-note@#f4 {{'f4' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#f4_call {{enclose 'f4' in a __builtin_available check to silence this warning}}
     unsigned D = f4(); // #f4_call
 
     unsigned E = f5();
 
-    // expected-warning@#f6_call {{'f6' is only available in compute shader environment on Shader Model 6.0 or newer}}
+    // expected-error@#f6_call {{'f6' is only available in compute shader environment on Shader Model 6.0 or newer}}
     // expected-note@#f6 {{'f6' has been marked as being introduced in Shader Model 6.0 in compute shader environment here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#f6_call {{enclose 'f6' in a __builtin_available check to silence this warning}}
     unsigned F = f6(); // #f6_call
 
-    // expected-warning@#f7_call {{'f7' is unavailable}}
+    // expected-error@#f7_call {{'f7' is unavailable}}
     // expected-note@#f7 {{'f7' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 compute shader environment}}
-    // expected-note@#f7_call {{enclose 'f7' in a __builtin_available check to silence this warning}}
     unsigned G = f7(); // #f7_call
 
     unsigned H = f8();
diff --git a/clang/test/Sema/attr-availability-shadermodel-mesh.hlsl b/clang/test/Sema/attr-availability-shadermodel-mesh.hlsl
index 40a7ddbb1de98..256f0a2837c60 100644
--- a/clang/test/Sema/attr-availability-shadermodel-mesh.hlsl
+++ b/clang/test/Sema/attr-availability-shadermodel-mesh.hlsl
@@ -38,35 +38,30 @@ unsigned f8(); // #f8
 
 [numthreads(4,1,1)]
 int main() {
-    // expected-warning@#f1_call {{'f1' is only available on Shader Model 6.0 or newer}}
+    // expected-error@#f1_call {{'f1' is only available on Shader Model 6.0 or newer}}
     // expected-note@#f1 {{'f1' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#f1_call {{enclose 'f1' in a __builtin_available check to silence this warning}}
     unsigned A = f1(); // #f1_call
 
-    // expected-warning@#f2_call {{'f2' is only available on Shader Model 5.1 or newer}}
+    // expected-error@#f2_call {{'f2' is only available on Shader Model 5.1 or newer}}
     // expected-note@#f2 {{'f2' has been marked as being introduced in Shader Model 5.1 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#f2_call {{enclose 'f2' in a __builtin_available check to silence this warning}}
     unsigned B = f2(); // #f2_call
 
     unsigned C = f3();
 
-    // expected-warning@#f4_call {{'f4' is only available on Shader Model 6.0 or newer}}
+    // expected-error@#f4_call {{'f4' is only available on Shader Model 6.0 or newer}}
     // expected-note@#f4 {{'f4' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#f4_call {{enclose 'f4' in a __builtin_available check to silence this warning}}
     unsigned D = f4(); // #f4_call
 
     unsigned E = f5(); // #f5_call
 
     unsigned F = f6(); // #f6_call
 
-    // expected-warning@#f7_call {{'f7' is only available in mesh shader environment on Shader Model 6.0 or newer}}
+    // expected-error@#f7_call {{'f7' is only available in mesh shader environment on Shader Model 6.0 or newer}}
     // expected-note@#f7 {{'f7' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 mesh shader environment}}
-    // expected-note@#f7_call {{enclose 'f7' in a __builtin_available check to silence this warning}}
     unsigned G = f7(); // #f7_call
 
-    // expected-warning@#f8_call {{'f8' is only available in mesh shader environment on Shader Model 6.0 or newer}}
+    // expected-error@#f8_call {{'f8' is only available in mesh shader environment on Shader Model 6.0 or newer}}
     // expected-note@#f8 {{'f8' has been marked as being introduced in Shader Model 6.0 in mesh shader environment here, but the deployment target is Shader Model 5.0 mesh shader environment}}
-    // expected-note@#f8_call {{enclose 'f8' in a __builtin_available check to silence this warning}}
     unsigned H = f8(); // #f8_call
 
     return 0;
diff --git a/clang/test/Sema/attr-availability-shadermodel-pixel.hlsl b/clang/test/Sema/attr-availability-shadermodel-pixel.hlsl
index 59d09a9cd276f..7cd13e653ed5a 100644
--- a/clang/test/Sema/attr-availability-shadermodel-pixel.hlsl
+++ b/clang/test/Sema/attr-availability-shadermodel-pixel.hlsl
@@ -37,14 +37,12 @@ __attribute__((availability(shadermodel, introduced = 6.0, environment = mesh)))
 unsigned f8();
 
 int main() {
-    // expected-warning@#f1_call {{'f1' is only available on Shader Model 6.0 or newer}}
+    // expected-error@#f1_call {{'f1' is only available on Shader Model 6.0 or newer}}
     // expected-note@#f1 {{'f1' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#f1_call {{enclose 'f1' in a __builtin_available check to silence this warning}}
     unsigned A = f1(); // #f1_call
 
-    // expected-warning@#f2_call {{'f2' is only available on Shader Model 5.1 or newer}}
+    // expected-error@#f2_call {{'f2' is only available on Shader Model 5.1 or newer}}
     // expected-note@#f2 {{'f2' has been marked as being introduced in Shader Model 5.1 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#f2_call {{enclose 'f2' in a __builtin_available check to silence this warning}}
     unsigned B = f2(); // #f2_call
 
     unsigned C = f3();

>From e0a3f5031921ce68a35ca6e47793100d9e43a52c Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Fri, 17 May 2024 20:01:22 -0700
Subject: [PATCH 20/21] Rename tests (default/relaxed)

---
 ...ilabilityDefaultStage.hlsl => avail-diag-default-compute.hlsl} | 0
 ...vailabilityDefaultLibrary.hlsl => avail-diag-default-lib.hlsl} | 0
 ...ilabilityRelaxedStage.hlsl => avail-diag-relaxed-compute.hlsl} | 0
 ...vailabilityRelaxedLibrary.hlsl => avail-diag-relaxed-lib.hlsl} | 0
 ...labilityMultipleStages.hlsl => avail-lib-multiple-stages.hlsl} | 0
 5 files changed, 0 insertions(+), 0 deletions(-)
 rename clang/test/SemaHLSL/Availability/{AvailabilityDefaultStage.hlsl => avail-diag-default-compute.hlsl} (100%)
 rename clang/test/SemaHLSL/Availability/{AvailabilityDefaultLibrary.hlsl => avail-diag-default-lib.hlsl} (100%)
 rename clang/test/SemaHLSL/Availability/{AvailabilityRelaxedStage.hlsl => avail-diag-relaxed-compute.hlsl} (100%)
 rename clang/test/SemaHLSL/Availability/{AvailabilityRelaxedLibrary.hlsl => avail-diag-relaxed-lib.hlsl} (100%)
 rename clang/test/SemaHLSL/Availability/{AvailabilityMultipleStages.hlsl => avail-lib-multiple-stages.hlsl} (100%)

diff --git a/clang/test/SemaHLSL/Availability/AvailabilityDefaultStage.hlsl b/clang/test/SemaHLSL/Availability/avail-diag-default-compute.hlsl
similarity index 100%
rename from clang/test/SemaHLSL/Availability/AvailabilityDefaultStage.hlsl
rename to clang/test/SemaHLSL/Availability/avail-diag-default-compute.hlsl
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityDefaultLibrary.hlsl b/clang/test/SemaHLSL/Availability/avail-diag-default-lib.hlsl
similarity index 100%
rename from clang/test/SemaHLSL/Availability/AvailabilityDefaultLibrary.hlsl
rename to clang/test/SemaHLSL/Availability/avail-diag-default-lib.hlsl
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityRelaxedStage.hlsl b/clang/test/SemaHLSL/Availability/avail-diag-relaxed-compute.hlsl
similarity index 100%
rename from clang/test/SemaHLSL/Availability/AvailabilityRelaxedStage.hlsl
rename to clang/test/SemaHLSL/Availability/avail-diag-relaxed-compute.hlsl
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityRelaxedLibrary.hlsl b/clang/test/SemaHLSL/Availability/avail-diag-relaxed-lib.hlsl
similarity index 100%
rename from clang/test/SemaHLSL/Availability/AvailabilityRelaxedLibrary.hlsl
rename to clang/test/SemaHLSL/Availability/avail-diag-relaxed-lib.hlsl
diff --git a/clang/test/SemaHLSL/Availability/AvailabilityMultipleStages.hlsl b/clang/test/SemaHLSL/Availability/avail-lib-multiple-stages.hlsl
similarity index 100%
rename from clang/test/SemaHLSL/Availability/AvailabilityMultipleStages.hlsl
rename to clang/test/SemaHLSL/Availability/avail-lib-multiple-stages.hlsl

>From 35d72822b298d77b4d73cb6b92fd218c4722e6b6 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Sun, 19 May 2024 11:03:04 -0700
Subject: [PATCH 21/21] Cleanup

---
 clang/lib/Sema/SemaAvailability.cpp           | 55 +++++++++++--------
 .../SemaHLSL/WaveBuiltinAvailability.hlsl     |  8 ---
 2 files changed, 31 insertions(+), 32 deletions(-)

diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index a3b3f4e38512c..e5cf3037aedef 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -136,6 +136,7 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
   return {Result, D};
 }
 
+
 /// whether we should emit a diagnostic for \c K and \c DeclVersion in
 /// the context of \c Ctx. For example, we should emit an unavailable diagnostic
 /// in a deprecated context, but not the other way around.
@@ -346,11 +347,14 @@ createAttributeInsertion(const NamedDecl *D, const SourceManager &SM,
 /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and
 /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl
 /// and OffendingDecl is the EnumDecl.
-static void DoEmitAvailabilityWarning(
-    Sema &S, AvailabilityResult K, Decl *Ctx, const NamedDecl *ReferringDecl,
-    const NamedDecl *OffendingDecl, StringRef Message,
-    ArrayRef<SourceLocation> Locs, const ObjCInterfaceDecl *UnknownObjCClass,
-    const ObjCPropertyDecl *ObjCProperty, bool ObjCPropertyAccess) {
+static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
+                                      Decl *Ctx, const NamedDecl *ReferringDecl,
+                                      const NamedDecl *OffendingDecl,
+                                      StringRef Message,
+                                      ArrayRef<SourceLocation> Locs,
+                                      const ObjCInterfaceDecl *UnknownObjCClass,
+                                      const ObjCPropertyDecl *ObjCProperty,
+                                      bool ObjCPropertyAccess) {
   // Diagnostics for deprecated or unavailable.
   unsigned diag, diag_message, diag_fwdclass_message;
   unsigned diag_available_here = diag::note_availability_specified_here;
@@ -506,8 +510,7 @@ static void DoEmitAvailabilityWarning(
         };
 
         switch (AL->getImplicitReason()) {
-        case UnavailableAttr::IR_None:
-          break;
+        case UnavailableAttr::IR_None: break;
 
         case UnavailableAttr::IR_ARCForbiddenType:
           flagARCError();
@@ -554,7 +557,8 @@ static void DoEmitAvailabilityWarning(
 
     CharSourceRange UseRange;
     if (!Replacement.empty())
-      UseRange = CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc));
+      UseRange =
+          CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc));
     if (UseRange.isValid()) {
       if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) {
         Selector Sel = MethodDecl->getSelector();
@@ -619,7 +623,7 @@ static void DoEmitAvailabilityWarning(
   }
 
   S.Diag(NoteLocation, diag_available_here)
-      << OffendingDecl << available_here_select_kind;
+    << OffendingDecl << available_here_select_kind;
 }
 
 void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) {
@@ -634,22 +638,26 @@ void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) {
       DD.getObjCProperty(), false);
 }
 
-static void EmitAvailabilityWarning(
-    Sema &S, AvailabilityResult AR, const NamedDecl *ReferringDecl,
-    const NamedDecl *OffendingDecl, StringRef Message,
-    ArrayRef<SourceLocation> Locs, const ObjCInterfaceDecl *UnknownObjCClass,
-    const ObjCPropertyDecl *ObjCProperty, bool ObjCPropertyAccess) {
+static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR,
+                                    const NamedDecl *ReferringDecl,
+                                    const NamedDecl *OffendingDecl,
+                                    StringRef Message,
+                                    ArrayRef<SourceLocation> Locs,
+                                    const ObjCInterfaceDecl *UnknownObjCClass,
+                                    const ObjCPropertyDecl *ObjCProperty,
+                                    bool ObjCPropertyAccess) {
   // Delay if we're currently parsing a declaration.
   if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
-    S.DelayedDiagnostics.add(DelayedDiagnostic::makeAvailability(
-        AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass, ObjCProperty,
-        Message, ObjCPropertyAccess));
+    S.DelayedDiagnostics.add(
+        DelayedDiagnostic::makeAvailability(
+            AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass,
+            ObjCProperty, Message, ObjCPropertyAccess));
     return;
   }
 
   Decl *Ctx = cast<Decl>(S.getCurLexicalContext());
-  DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl, Message,
-                            Locs, UnknownObjCClass, ObjCProperty,
+  DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl,
+                            Message, Locs, UnknownObjCClass, ObjCProperty,
                             ObjCPropertyAccess);
 }
 
@@ -810,7 +818,7 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
       return;
 
     const AvailabilityAttr *AA =
-        getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
+      getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
     bool EnvironmentMatchesOrNone =
         hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA);
     VersionTuple Introduced = AA->getIntroduced();
@@ -915,9 +923,8 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
     const char *ExtraIndentation = "    ";
     std::string FixItString;
     llvm::raw_string_ostream FixItOS(FixItString);
-    FixItOS << "if ("
-            << (SemaRef.getLangOpts().ObjC ? "@available"
-                                           : "__builtin_available")
+    FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available"
+                                                     : "__builtin_available")
             << "("
             << AvailabilityAttr::getPlatformNameSourceSpelling(
                    SemaRef.getASTContext().getTargetInfo().getPlatformName())
@@ -1030,7 +1037,7 @@ void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
                                       ObjCInterfaceDecl *ClassReceiver) {
   std::string Message;
   AvailabilityResult Result;
-  const NamedDecl *OffendingDecl;
+  const NamedDecl* OffendingDecl;
   // See if this declaration is unavailable, deprecated, or partial.
   std::tie(Result, OffendingDecl) =
       ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver);
diff --git a/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl b/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
index 649ff98e4ddae..6333c63569327 100644
--- a/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
+++ b/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
@@ -1,18 +1,10 @@
 // RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel5.0-library -verify %s
 // WaveActiveCountBits is unavailable before ShaderModel 6.0.
 
-<<<<<<< HEAD
 [shader("compute")]
 [numthreads(8,8,1)]
 unsigned foo() {
     // expected-error@#site {{'WaveActiveCountBits' is only available on Shader Model 6.0 or newer}}
     // expected-note at hlsl/hlsl_intrinsics.h:* {{'WaveActiveCountBits' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
     return hlsl::WaveActiveCountBits(1); // #site
-=======
-unsigned foo(bool b) {
-    // expected-warning@#site {{'WaveActiveCountBits' is only available on Shader Model 6.0 or newer}}
-    // expected-note at hlsl/hlsl_intrinsics.h:* {{'WaveActiveCountBits' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
-    // expected-note@#site {{enclose 'WaveActiveCountBits' in a __builtin_available check to silence this warning}}
-    return hlsl::WaveActiveCountBits(b); // #site
->>>>>>> 3f33c4c14e79e68007cf1460e4a0e606eb199da5
 }



More information about the cfe-commits mailing list