[clang] [llvm] [HLSL] Implement '__builtin_hlsl_is_intangible' type trait (PR #104544)

Helena Kotas via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 4 12:08:13 PDT 2024


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

>From 6d5f8991a4ef9e79bc1bed30addf7b29b7ed0d2e Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 15 Aug 2024 19:03:29 -0700
Subject: [PATCH 01/16] Implement `__builtin_is_intangible`

---
 clang/include/clang/Basic/TokenKinds.def |  3 ++
 clang/include/clang/Sema/SemaHLSL.h      |  3 ++
 clang/lib/Sema/SemaExprCXX.cpp           |  8 ++++
 clang/lib/Sema/SemaHLSL.cpp              | 49 ++++++++++++++++++++++++
 4 files changed, 63 insertions(+)

diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index d683106bb0e298..f4fc7c321d9c5a 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -660,6 +660,9 @@ KEYWORD(out                         , KEYHLSL)
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) KEYWORD(Name, KEYHLSL)
 #include "clang/Basic/HLSLIntangibleTypes.def"
 
+// HLSL Type traits
+TYPE_TRAIT_1(__builtin_is_intangible, IsIntangibleType, KEYHLSL)
+
 // OpenMP Type Traits
 UNARY_EXPR_OR_TYPE_TRAIT(__builtin_omp_required_simd_align, OpenMPRequiredSimdAlign, KEYALL)
 
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index d60cb2a57d4918..13e75a79ec6bf0 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -62,6 +62,9 @@ class SemaHLSL : public SemaBase {
   void handleParamModifierAttr(Decl *D, const ParsedAttr &AL);
 
   bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
+
+  // HLSL Type trait implementations
+  bool IsIntangibleType(QualType T1) const;
 };
 
 } // namespace clang
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 5356bcf172f752..f3f8d511a6e568 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -39,6 +39,7 @@
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaCUDA.h"
+#include "clang/Sema/SemaHLSL.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaLambda.h"
 #include "clang/Sema/SemaObjC.h"
@@ -5683,6 +5684,13 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
         return true;
     return false;
   }
+  case UTT_IsIntangibleType:
+    if (!T->isVoidType() && !T->isIncompleteArrayType())
+      if (Self.RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), T,
+                                   diag::err_incomplete_type))
+        return true;
+    DiagnoseVLAInCXXTypeTrait(Self, TInfo, tok::kw___builtin_is_intangible);
+    return Self.HLSL().IsIntangibleType(T);
   }
 }
 
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e3e926465e799e..5978c14399ba32 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -12,6 +12,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
 #include "clang/Basic/DiagnosticSema.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/TargetInfo.h"
@@ -1154,3 +1155,51 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
   }
   return false;
 }
+
+bool SemaHLSL::IsIntangibleType(QualType Ty) const {
+  if (Ty.isNull())
+    return false;
+
+  Ty = Ty.getCanonicalType().getUnqualifiedType();
+  if (Ty->isBuiltinType())
+    return Ty->isHLSLSpecificType();
+
+  llvm::SmallVector<QualType, 8> TypesToScan;
+  TypesToScan.push_back(Ty);
+  while (!TypesToScan.empty()) {
+    QualType T = TypesToScan.pop_back_val();
+    assert(T == T.getCanonicalType().getUnqualifiedType() && "expected sugar-free type");
+    assert(!isa<MatrixType>(T) && "Matrix types not yet supported in HLSL");
+
+    if (const auto *AT = dyn_cast<ConstantArrayType>(T)) {
+      QualType ElTy = AT->getElementType().getCanonicalType().getUnqualifiedType();
+      if (ElTy->isBuiltinType())
+        return ElTy->isHLSLSpecificType();
+      TypesToScan.push_back(ElTy);
+      continue; 
+    }
+
+    if (const auto *VT = dyn_cast<VectorType>(T)) {
+      QualType ElTy = VT->getElementType().getCanonicalType().getUnqualifiedType();
+      assert(ElTy->isBuiltinType() && "vectors can only contain builtin types");
+      if (ElTy->isHLSLSpecificType())
+        return true;
+      continue;
+    }
+
+    if (const auto *RT = dyn_cast<RecordType>(T)) {
+      const RecordDecl *RD = RT->getDecl();
+      for (const auto *FD : RD->fields()) {
+        QualType FieldTy = FD->getType().getCanonicalType().getUnqualifiedType();
+        if (FieldTy->isBuiltinType()) {
+          if (FieldTy->isHLSLSpecificType())
+            return true;
+        } else {
+          TypesToScan.push_back(FieldTy); 
+        }
+      }
+      continue;
+    }
+  }
+  return false;
+}

>From d21ca2e2891acbd6c89864b57d864d881a8a8b96 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 15 Aug 2024 20:52:24 -0700
Subject: [PATCH 02/16] add caching

---
 clang/include/clang/Sema/SemaHLSL.h |  5 ++++-
 clang/lib/Sema/SemaExprCXX.cpp      |  1 +
 clang/lib/Sema/SemaHLSL.cpp         | 22 +++++++++++++++++-----
 3 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 13e75a79ec6bf0..663dea12880d1b 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -64,7 +64,10 @@ class SemaHLSL : public SemaBase {
   bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
 
   // HLSL Type trait implementations
-  bool IsIntangibleType(QualType T1) const;
+  bool IsIntangibleType(const QualType T1);
+
+private:
+  llvm::DenseMap<const Type *, bool> IsIntangibleTypeCache;
 };
 
 } // namespace clang
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f3f8d511a6e568..d3964f5da01e00 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5099,6 +5099,7 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
   case UTT_IsDestructible:
   case UTT_IsNothrowDestructible:
   case UTT_IsTriviallyDestructible:
+  case UTT_IsIntangibleType:
     if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType())
       return true;
 
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 5978c14399ba32..e23240a380528d 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -28,7 +28,7 @@
 
 using namespace clang;
 
-SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {}
+SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S), IsIntangibleTypeCache(8) {}
 
 Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
                                  SourceLocation KwLoc, IdentifierInfo *Ident,
@@ -1156,10 +1156,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
   return false;
 }
 
-bool SemaHLSL::IsIntangibleType(QualType Ty) const {
-  if (Ty.isNull())
-    return false;
-
+static bool calculateIsIntangibleType(QualType Ty) {
   Ty = Ty.getCanonicalType().getUnqualifiedType();
   if (Ty->isBuiltinType())
     return Ty->isHLSLSpecificType();
@@ -1203,3 +1200,18 @@ bool SemaHLSL::IsIntangibleType(QualType Ty) const {
   }
   return false;
 }
+
+bool SemaHLSL::IsIntangibleType(const clang::QualType Ty) {
+  if (Ty.isNull())
+    return false;
+
+  const auto CachedEntry = IsIntangibleTypeCache.find(Ty.getTypePtr());
+  if (CachedEntry != IsIntangibleTypeCache.end()) {
+    assert(CachedEntry->second == calculateIsIntangibleType(Ty) && "IsIntangibleType mismatch");
+    return CachedEntry->second;
+  }
+
+  bool IsIntangible = calculateIsIntangibleType(Ty);
+  IsIntangibleTypeCache[Ty.getTypePtr()] = IsIntangible;
+  return IsIntangible;
+}

>From d7e8bce2e27f894196691435d2379edfdd6cd906 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 15 Aug 2024 20:56:40 -0700
Subject: [PATCH 03/16] clang-format

---
 clang/lib/Sema/SemaHLSL.cpp | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e23240a380528d..5e5917c40bc1cc 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1165,19 +1165,22 @@ static bool calculateIsIntangibleType(QualType Ty) {
   TypesToScan.push_back(Ty);
   while (!TypesToScan.empty()) {
     QualType T = TypesToScan.pop_back_val();
-    assert(T == T.getCanonicalType().getUnqualifiedType() && "expected sugar-free type");
+    assert(T == T.getCanonicalType().getUnqualifiedType() &&
+           "expected sugar-free type");
     assert(!isa<MatrixType>(T) && "Matrix types not yet supported in HLSL");
 
     if (const auto *AT = dyn_cast<ConstantArrayType>(T)) {
-      QualType ElTy = AT->getElementType().getCanonicalType().getUnqualifiedType();
+      QualType ElTy =
+          AT->getElementType().getCanonicalType().getUnqualifiedType();
       if (ElTy->isBuiltinType())
         return ElTy->isHLSLSpecificType();
       TypesToScan.push_back(ElTy);
-      continue; 
+      continue;
     }
 
     if (const auto *VT = dyn_cast<VectorType>(T)) {
-      QualType ElTy = VT->getElementType().getCanonicalType().getUnqualifiedType();
+      QualType ElTy =
+          VT->getElementType().getCanonicalType().getUnqualifiedType();
       assert(ElTy->isBuiltinType() && "vectors can only contain builtin types");
       if (ElTy->isHLSLSpecificType())
         return true;
@@ -1187,12 +1190,13 @@ static bool calculateIsIntangibleType(QualType Ty) {
     if (const auto *RT = dyn_cast<RecordType>(T)) {
       const RecordDecl *RD = RT->getDecl();
       for (const auto *FD : RD->fields()) {
-        QualType FieldTy = FD->getType().getCanonicalType().getUnqualifiedType();
+        QualType FieldTy =
+            FD->getType().getCanonicalType().getUnqualifiedType();
         if (FieldTy->isBuiltinType()) {
           if (FieldTy->isHLSLSpecificType())
             return true;
         } else {
-          TypesToScan.push_back(FieldTy); 
+          TypesToScan.push_back(FieldTy);
         }
       }
       continue;
@@ -1207,7 +1211,8 @@ bool SemaHLSL::IsIntangibleType(const clang::QualType Ty) {
 
   const auto CachedEntry = IsIntangibleTypeCache.find(Ty.getTypePtr());
   if (CachedEntry != IsIntangibleTypeCache.end()) {
-    assert(CachedEntry->second == calculateIsIntangibleType(Ty) && "IsIntangibleType mismatch");
+    assert(CachedEntry->second == calculateIsIntangibleType(Ty) &&
+           "IsIntangibleType mismatch");
     return CachedEntry->second;
   }
 

>From 481c118d7d94f16c9cc9babbaaa794f951883088 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 15 Aug 2024 21:16:19 -0700
Subject: [PATCH 04/16] add tests

---
 .../Types/Traits/IsIntangibleType.hlsl        | 52 +++++++++++++++++++
 .../Types/Traits/IsIntangibleTypeErrors.hlsl  | 11 ++++
 2 files changed, 63 insertions(+)
 create mode 100644 clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
 create mode 100644 clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl

diff --git a/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl b/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
new file mode 100644
index 00000000000000..ae6da681d5100e
--- /dev/null
+++ b/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -verify %s
+// expected-no-diagnostics
+
+_Static_assert(__builtin_is_intangible(__hlsl_resource_t), "");
+// no need to check array of __hlsl_resource_t, arrays of sizeless types are not supported
+
+_Static_assert(!__builtin_is_intangible(int), "");
+_Static_assert(!__builtin_is_intangible(float3), "");
+_Static_assert(!__builtin_is_intangible(half[4]), "");
+
+typedef __hlsl_resource_t Res;
+_Static_assert(__builtin_is_intangible(const Res), "");
+// no need to check array of Res, arrays of sizeless types are not supported
+
+struct ABuffer {
+    const int i[10];
+    __hlsl_resource_t h;
+};
+_Static_assert(__builtin_is_intangible(ABuffer), "");
+_Static_assert(__builtin_is_intangible(ABuffer[10]), "");
+
+struct MyStruct {
+    half2 h2;
+    int3 i3;
+};
+_Static_assert(!__builtin_is_intangible(MyStruct), "");
+_Static_assert(!__builtin_is_intangible(MyStruct[10]), "");
+
+class MyClass {
+    int3 ivec;
+    float farray[12];
+    MyStruct ms;
+    ABuffer buf;
+};
+_Static_assert(__builtin_is_intangible(MyClass), "");
+_Static_assert(__builtin_is_intangible(MyClass[2]), "");
+
+union U {
+    double d[4];
+    Res buf;
+};
+_Static_assert(__builtin_is_intangible(U), "");
+_Static_assert(__builtin_is_intangible(U[100]), "");
+
+class MyClass2 {
+    int3 ivec;
+    float farray[12];
+    U u;
+};
+_Static_assert(__builtin_is_intangible(MyClass2), "");
+_Static_assert(__builtin_is_intangible(MyClass2[5]), "");
diff --git a/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl b/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
new file mode 100644
index 00000000000000..bfb654de0dcfca
--- /dev/null
+++ b/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library  -finclude-default-header -verify %s
+
+struct Undefined; // expected-note {{forward declaration of 'Undefined'}}
+_Static_assert(!__builtin_is_intangible(Undefined), ""); // expected-error{{incomplete type 'Undefined' used in type trait expression}}
+
+void fn(int X) {
+  // expected-error@#vla {{variable length arrays are not supported for the current target}}
+  // expected-error@#vla {{variable length arrays are not supported in '__builtin_is_intangible'}}
+  // expected-warning@#vla {{variable length arrays in C++ are a Clang extension}}
+  _Static_assert(!__builtin_is_intangible(int[X]), ""); // #vla
+}
\ No newline at end of file

>From 2df05761de1df378cedec49430c327a21f8ee089 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 15 Aug 2024 21:56:28 -0700
Subject: [PATCH 05/16] base classes!

---
 clang/lib/Sema/SemaHLSL.cpp                   | 21 +++++++++++--------
 .../Types/Traits/IsIntangibleType.hlsl        |  9 ++++++++
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 5e5917c40bc1cc..09d4d6e7f13c72 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -10,6 +10,7 @@
 
 #include "clang/Sema/SemaHLSL.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Type.h"
@@ -1157,7 +1158,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
 }
 
 static bool calculateIsIntangibleType(QualType Ty) {
-  Ty = Ty.getCanonicalType().getUnqualifiedType();
+  Ty = Ty->getCanonicalTypeUnqualified();
   if (Ty->isBuiltinType())
     return Ty->isHLSLSpecificType();
 
@@ -1165,13 +1166,11 @@ static bool calculateIsIntangibleType(QualType Ty) {
   TypesToScan.push_back(Ty);
   while (!TypesToScan.empty()) {
     QualType T = TypesToScan.pop_back_val();
-    assert(T == T.getCanonicalType().getUnqualifiedType() &&
-           "expected sugar-free type");
+    assert(T == T->getCanonicalTypeUnqualified() && "expected sugar-free type");
     assert(!isa<MatrixType>(T) && "Matrix types not yet supported in HLSL");
 
     if (const auto *AT = dyn_cast<ConstantArrayType>(T)) {
-      QualType ElTy =
-          AT->getElementType().getCanonicalType().getUnqualifiedType();
+      QualType ElTy = AT->getElementType()->getCanonicalTypeUnqualified();
       if (ElTy->isBuiltinType())
         return ElTy->isHLSLSpecificType();
       TypesToScan.push_back(ElTy);
@@ -1179,8 +1178,7 @@ static bool calculateIsIntangibleType(QualType Ty) {
     }
 
     if (const auto *VT = dyn_cast<VectorType>(T)) {
-      QualType ElTy =
-          VT->getElementType().getCanonicalType().getUnqualifiedType();
+      QualType ElTy = VT->getElementType()->getCanonicalTypeUnqualified();
       assert(ElTy->isBuiltinType() && "vectors can only contain builtin types");
       if (ElTy->isHLSLSpecificType())
         return true;
@@ -1190,8 +1188,7 @@ static bool calculateIsIntangibleType(QualType Ty) {
     if (const auto *RT = dyn_cast<RecordType>(T)) {
       const RecordDecl *RD = RT->getDecl();
       for (const auto *FD : RD->fields()) {
-        QualType FieldTy =
-            FD->getType().getCanonicalType().getUnqualifiedType();
+        QualType FieldTy = FD->getType()->getCanonicalTypeUnqualified();
         if (FieldTy->isBuiltinType()) {
           if (FieldTy->isHLSLSpecificType())
             return true;
@@ -1199,6 +1196,12 @@ static bool calculateIsIntangibleType(QualType Ty) {
           TypesToScan.push_back(FieldTy);
         }
       }
+
+      if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+        for (const CXXBaseSpecifier &B : CXXRD->bases()) {
+          TypesToScan.push_back(B.getType()->getCanonicalTypeUnqualified());
+        }
+      }
       continue;
     }
   }
diff --git a/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl b/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
index ae6da681d5100e..f7ef7f543bfb5e 100644
--- a/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
+++ b/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
@@ -50,3 +50,12 @@ class MyClass2 {
 };
 _Static_assert(__builtin_is_intangible(MyClass2), "");
 _Static_assert(__builtin_is_intangible(MyClass2[5]), "");
+
+class Simple {
+    int a;
+};
+
+class MyClass3 : MyClass2, Simple {
+    half h;
+};
+_Static_assert(__builtin_is_intangible(MyClass3), "");

>From 227befd3ecff7899ef0088045176cead79aa8adc Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 15 Aug 2024 22:26:44 -0700
Subject: [PATCH 06/16] Update default for incomplete type

---
 clang/lib/Sema/SemaExprCXX.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index d3964f5da01e00..94132a4823e305 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5689,7 +5689,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
     if (!T->isVoidType() && !T->isIncompleteArrayType())
       if (Self.RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), T,
                                    diag::err_incomplete_type))
-        return true;
+        return false;
     DiagnoseVLAInCXXTypeTrait(Self, TInfo, tok::kw___builtin_is_intangible);
     return Self.HLSL().IsIntangibleType(T);
   }

>From 4bbf083bd75de42c82e4b2068b541241fcd492fd Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Mon, 19 Aug 2024 15:04:58 -0700
Subject: [PATCH 07/16] Rename to `__builtin_hlsl_is_intangible`

---
 clang/include/clang/Basic/TokenKinds.def      |  2 +-
 clang/lib/Sema/SemaExprCXX.cpp                |  2 +-
 .../Types/Traits/IsIntangibleType.hlsl        | 32 +++++++++----------
 .../Types/Traits/IsIntangibleTypeErrors.hlsl  |  6 ++--
 4 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index f4fc7c321d9c5a..1b36d799f13f46 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -661,7 +661,7 @@ KEYWORD(out                         , KEYHLSL)
 #include "clang/Basic/HLSLIntangibleTypes.def"
 
 // HLSL Type traits
-TYPE_TRAIT_1(__builtin_is_intangible, IsIntangibleType, KEYHLSL)
+TYPE_TRAIT_1(__builtin_hlsl_is_intangible, IsIntangibleType, KEYHLSL)
 
 // OpenMP Type Traits
 UNARY_EXPR_OR_TYPE_TRAIT(__builtin_omp_required_simd_align, OpenMPRequiredSimdAlign, KEYALL)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 94132a4823e305..55366bfd20b100 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5690,7 +5690,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
       if (Self.RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), T,
                                    diag::err_incomplete_type))
         return false;
-    DiagnoseVLAInCXXTypeTrait(Self, TInfo, tok::kw___builtin_is_intangible);
+    DiagnoseVLAInCXXTypeTrait(Self, TInfo, tok::kw___builtin_hlsl_is_intangible);
     return Self.HLSL().IsIntangibleType(T);
   }
 }
diff --git a/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl b/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
index f7ef7f543bfb5e..39a912f99d3896 100644
--- a/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
+++ b/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
@@ -2,30 +2,30 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -verify %s
 // expected-no-diagnostics
 
-_Static_assert(__builtin_is_intangible(__hlsl_resource_t), "");
+_Static_assert(__builtin_hlsl_is_intangible(__hlsl_resource_t), "");
 // no need to check array of __hlsl_resource_t, arrays of sizeless types are not supported
 
-_Static_assert(!__builtin_is_intangible(int), "");
-_Static_assert(!__builtin_is_intangible(float3), "");
-_Static_assert(!__builtin_is_intangible(half[4]), "");
+_Static_assert(!__builtin_hlsl_is_intangible(int), "");
+_Static_assert(!__builtin_hlsl_is_intangible(float3), "");
+_Static_assert(!__builtin_hlsl_is_intangible(half[4]), "");
 
 typedef __hlsl_resource_t Res;
-_Static_assert(__builtin_is_intangible(const Res), "");
+_Static_assert(__builtin_hlsl_is_intangible(const Res), "");
 // no need to check array of Res, arrays of sizeless types are not supported
 
 struct ABuffer {
     const int i[10];
     __hlsl_resource_t h;
 };
-_Static_assert(__builtin_is_intangible(ABuffer), "");
-_Static_assert(__builtin_is_intangible(ABuffer[10]), "");
+_Static_assert(__builtin_hlsl_is_intangible(ABuffer), "");
+_Static_assert(__builtin_hlsl_is_intangible(ABuffer[10]), "");
 
 struct MyStruct {
     half2 h2;
     int3 i3;
 };
-_Static_assert(!__builtin_is_intangible(MyStruct), "");
-_Static_assert(!__builtin_is_intangible(MyStruct[10]), "");
+_Static_assert(!__builtin_hlsl_is_intangible(MyStruct), "");
+_Static_assert(!__builtin_hlsl_is_intangible(MyStruct[10]), "");
 
 class MyClass {
     int3 ivec;
@@ -33,23 +33,23 @@ class MyClass {
     MyStruct ms;
     ABuffer buf;
 };
-_Static_assert(__builtin_is_intangible(MyClass), "");
-_Static_assert(__builtin_is_intangible(MyClass[2]), "");
+_Static_assert(__builtin_hlsl_is_intangible(MyClass), "");
+_Static_assert(__builtin_hlsl_is_intangible(MyClass[2]), "");
 
 union U {
     double d[4];
     Res buf;
 };
-_Static_assert(__builtin_is_intangible(U), "");
-_Static_assert(__builtin_is_intangible(U[100]), "");
+_Static_assert(__builtin_hlsl_is_intangible(U), "");
+_Static_assert(__builtin_hlsl_is_intangible(U[100]), "");
 
 class MyClass2 {
     int3 ivec;
     float farray[12];
     U u;
 };
-_Static_assert(__builtin_is_intangible(MyClass2), "");
-_Static_assert(__builtin_is_intangible(MyClass2[5]), "");
+_Static_assert(__builtin_hlsl_is_intangible(MyClass2), "");
+_Static_assert(__builtin_hlsl_is_intangible(MyClass2[5]), "");
 
 class Simple {
     int a;
@@ -58,4 +58,4 @@ class Simple {
 class MyClass3 : MyClass2, Simple {
     half h;
 };
-_Static_assert(__builtin_is_intangible(MyClass3), "");
+_Static_assert(__builtin_hlsl_is_intangible(MyClass3), "");
diff --git a/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl b/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
index bfb654de0dcfca..bbf2a1682e3f05 100644
--- a/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
+++ b/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
@@ -1,11 +1,11 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library  -finclude-default-header -verify %s
 
 struct Undefined; // expected-note {{forward declaration of 'Undefined'}}
-_Static_assert(!__builtin_is_intangible(Undefined), ""); // expected-error{{incomplete type 'Undefined' used in type trait expression}}
+_Static_assert(!__builtin_hlsl_is_intangible(Undefined), ""); // expected-error{{incomplete type 'Undefined' used in type trait expression}}
 
 void fn(int X) {
   // expected-error@#vla {{variable length arrays are not supported for the current target}}
-  // expected-error@#vla {{variable length arrays are not supported in '__builtin_is_intangible'}}
+  // expected-error@#vla {{variable length arrays are not supported in '__builtin_hlsl_is_intangible'}}
   // expected-warning@#vla {{variable length arrays in C++ are a Clang extension}}
-  _Static_assert(!__builtin_is_intangible(int[X]), ""); // #vla
+  _Static_assert(!__builtin_hlsl_is_intangible(int[X]), ""); // #vla
 }
\ No newline at end of file

>From 6fd1bf74d532160338bf8b091a15e06369c37027 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Mon, 19 Aug 2024 17:52:07 -0700
Subject: [PATCH 08/16] clang-format

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

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 55366bfd20b100..49942944c37806 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5690,7 +5690,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
       if (Self.RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), T,
                                    diag::err_incomplete_type))
         return false;
-    DiagnoseVLAInCXXTypeTrait(Self, TInfo, tok::kw___builtin_hlsl_is_intangible);
+    DiagnoseVLAInCXXTypeTrait(Self, TInfo,
+                              tok::kw___builtin_hlsl_is_intangible);
     return Self.HLSL().IsIntangibleType(T);
   }
 }

>From 7f29d6fe8f7ca5fac9dfb475fc256b82b3b64048 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 27 Aug 2024 12:29:29 -0700
Subject: [PATCH 09/16] Address code review feedback

---
 clang/include/clang/AST/Type.h      |   7 ++
 clang/include/clang/Sema/SemaHLSL.h |   6 +-
 clang/lib/Sema/SemaExprCXX.cpp      |   1 +
 clang/lib/Sema/SemaHLSL.cpp         |  19 ++--
 particle_life.hlsl                  | 147 ++++++++++++++++++++++++++++
 5 files changed, 171 insertions(+), 9 deletions(-)
 create mode 100644 particle_life.hlsl

diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 575f3c17a3f691..d19e353967fa8b 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2656,6 +2656,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) bool is##Id##Type() const;
 #include "clang/Basic/HLSLIntangibleTypes.def"
   bool isHLSLSpecificType() const; // Any HLSL specific type
+  bool isHLSLIntangibleType() const; // Any HLSL intangible type
 
   /// Determines if this type, which must satisfy
   /// isObjCLifetimeType(), is implicitly __unsafe_unretained rather
@@ -8286,6 +8287,12 @@ inline bool Type::isHLSLSpecificType() const {
       false; // end boolean or operation
 }
 
+inline bool Type::isHLSLIntangibleType() const {
+  // All HLSL specific types are currently intangible type as well, but that
+  // might change in the future.
+  return isHLSLSpecificType();
+}
+
 inline bool Type::isTemplateTypeParmType() const {
   return isa<TemplateTypeParmType>(CanonicalType);
 }
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index e923df60b91458..cae3a502c73cf0 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -62,12 +62,12 @@ class SemaHLSL : public SemaBase {
 
   bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
 
-private:
-  llvm::DenseMap<const Type *, bool> IsIntangibleTypeCache;
-
   // HLSL Type trait implementations
   bool IsScalarizedLayoutCompatible(QualType T1, QualType T2) const;
   bool IsIntangibleType(const QualType T1);
+
+private:
+  llvm::DenseMap<const Type *, bool> IsIntangibleTypeCache;
 };
 
 } // namespace clang
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 7e179bc9939cdd..4c7c6bfdb2db9d 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5697,6 +5697,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
     return false;
   }
   case UTT_IsIntangibleType:
+    assert(Self.getLangOpts().HLSL && "intangible types are HLSL-only feature");
     if (!T->isVoidType() && !T->isIncompleteArrayType())
       if (Self.RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), T,
                                    diag::err_incomplete_type))
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e939fca1411a5d..f7cf18be195f3f 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -29,7 +29,7 @@
 
 using namespace clang;
 
-SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S), IsIntangibleTypeCache(8) {}
+SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S), IsIntangibleTypeCache() {}
 
 Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
                                  SourceLocation KwLoc, IdentifierInfo *Ident,
@@ -1528,11 +1528,10 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
 }
 
 static bool calculateIsIntangibleType(QualType Ty) {
-  Ty = Ty->getCanonicalTypeUnqualified();
-  if (Ty->isBuiltinType())
-    return Ty->isHLSLSpecificType();
+  assert(!Ty.getCanonicalType().getUnqualifiedType()->isBuiltinType() &&
+         "builtin types should be taken care of in IsIntangibleType");
 
-  llvm::SmallVector<QualType, 8> TypesToScan;
+  llvm::SmallVector<QualType> TypesToScan;
   TypesToScan.push_back(Ty);
   while (!TypesToScan.empty()) {
     QualType T = TypesToScan.pop_back_val();
@@ -1582,6 +1581,12 @@ bool SemaHLSL::IsIntangibleType(const clang::QualType Ty) {
   if (Ty.isNull())
     return false;
 
+  // check if it's a builtin type first (simple check, no need to cache it)
+  QualType CT = Ty->getCanonicalTypeUnqualified();
+  if (CT->isBuiltinType())
+    return CT->isHLSLIntangibleType();
+
+  // more complex type -> check if we already have it in the cache
   const auto CachedEntry = IsIntangibleTypeCache.find(Ty.getTypePtr());
   if (CachedEntry != IsIntangibleTypeCache.end()) {
     assert(CachedEntry->second == calculateIsIntangibleType(Ty) &&
@@ -1589,9 +1594,12 @@ bool SemaHLSL::IsIntangibleType(const clang::QualType Ty) {
     return CachedEntry->second;
   }
 
+  // calculate and add to cache
   bool IsIntangible = calculateIsIntangibleType(Ty);
   IsIntangibleTypeCache[Ty.getTypePtr()] = IsIntangible;
   return IsIntangible;
+}
+
 static void BuildFlattenedTypeList(QualType BaseTy,
                                    llvm::SmallVectorImpl<QualType> &List) {
   llvm::SmallVector<QualType, 16> WorkList;
@@ -1673,4 +1681,3 @@ bool SemaHLSL::IsScalarizedLayoutCompatible(QualType T1, QualType T2) const {
                        return SemaRef.IsLayoutCompatible(LHS, RHS);
                      });
 }
-}
diff --git a/particle_life.hlsl b/particle_life.hlsl
new file mode 100644
index 00000000000000..4cffbdb04e1b60
--- /dev/null
+++ b/particle_life.hlsl
@@ -0,0 +1,147 @@
+#define ROOT_SIGNATURE \
+    "RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)," \
+    "CBV(b0)," \
+    "SRV(t0)," \
+    "SRV(t1)," \
+    "UAV(u0)," \
+    "UAV(u1)"
+    
+
+cbuffer CONSTANTS : register(b0) {
+    uint ParticleTypeMax;
+    uint NumParticles;
+    float2 WorldSize;
+    float Friction;
+    float ForceMultipler;
+}
+
+struct Rule {
+    float force;
+    float min_distance;
+    float max_distance;
+};
+
+struct Particle {
+    float2 position;
+    float2 velocity;
+    uint type;
+};
+
+struct Vertex {
+    float2 position;
+    uint color;
+};
+
+StructuredBuffer<Rule> Rules : register(t0);
+StructuredBuffer<Particle> OldParticles : register(t1);
+RWStructuredBuffer<Particle> NewParticles : register(u0);
+RWStructuredBuffer<Vertex> Vertices : register(u1);
+
+
+float3 particle_type_to_color(uint type);
+uint float_to_abgr(float3 rgb);
+
+
+[numthreads(32, 1, 1)]
+void main(uint3 dispatch_thread_id : SV_DispatchThreadID) {
+    uint particle_id = dispatch_thread_id.x;
+
+    Particle particle = OldParticles[particle_id];
+    
+    // Accumulate forces
+    float2 force = float2(0,0);
+    float hit = 0;
+
+    for (uint i = 0; i < NumParticles; ++i) {
+        if (i == particle_id)
+            continue;
+        
+        Particle other_particle = OldParticles[i];
+
+        Rule rule = Rules[particle.type * ParticleTypeMax + other_particle.type];
+
+        float2 direction = other_particle.position - particle.position;
+
+        // wrapping
+        if (direction.x > WorldSize.x * 0.5f)
+            direction.x -= WorldSize.x;
+        if (direction.x < WorldSize.x * -0.5f)
+            direction.x += WorldSize.x;
+        if (direction.y > WorldSize.y * 0.5f)
+            direction.y -= WorldSize.y;
+        if (direction.y < WorldSize.y * -0.5f)
+            direction.y += WorldSize.y;
+
+        // apply rule   
+        float distance = length(direction);
+        direction = normalize(direction);
+
+        if (distance < rule.min_distance) {
+            float repulsive_amount = abs(rule.force) * (1.0f - (distance / rule.min_distance))  * -3.0f;
+            force += direction * repulsive_amount;
+        }
+
+        if (distance < rule.max_distance) {
+            float attract_amount = rule.force * (1.0f - (distance / rule.max_distance));
+            force += direction * attract_amount;
+            hit += 0.01f;
+        }
+    }
+
+    float2 velocity = particle.velocity;
+    velocity += force * ForceMultipler;
+    velocity *= Friction;
+
+    particle.position = particle.position + velocity;
+
+    if (particle.position.x < 0)
+        particle.position.x += WorldSize.x;
+
+    if (particle.position.x > WorldSize.x)
+        particle.position.x -= WorldSize.x;
+
+    if (particle.position.y < 0)
+        particle.position.y += WorldSize.y;
+
+    if (particle.position.y > WorldSize.y)
+        particle.position.y -= WorldSize.y;
+
+
+    particle.velocity = velocity;
+
+    Vertices[particle_id].position = particle.position;
+
+    float3 color =  particle_type_to_color(particle.type);
+
+    color = lerp(color, color * 0.1f, 1-saturate(hit));
+
+    Vertices[particle_id].color = float_to_abgr(color);
+
+    NewParticles[particle_id] = particle;
+}
+
+
+
+// from https://chilliant.com/rgb2hsv.html
+float3 hue2rgb(float H) {
+    float R = abs(H * 6 - 3) - 1;  
+    float G = 2 - abs(H * 6 - 2);  
+    float B = 2 - abs(H * 6 - 4); 
+    return saturate(float3(R,G,B));
+}
+
+float3 particle_type_to_color(uint type) {
+    float hue = (float)type / float(ParticleTypeMax);
+    return hue2rgb(hue);
+}
+
+uint float_to_abgr(float3 rgb) {
+    rgb *= 255.0;
+
+    uint r = rgb.x;
+    uint g = rgb.y;
+    uint b = rgb.z;
+    uint a = 255; 
+
+    return (a << 24) | (b << 16) | (g << 8) | r;    
+}
\ No newline at end of file

>From d666eb7999c43e2cd6ae9c0948a6e0380bb1d425 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 27 Aug 2024 12:53:48 -0700
Subject: [PATCH 10/16] Remove accidentally added file

---
 particle_life.hlsl | 147 ---------------------------------------------
 1 file changed, 147 deletions(-)
 delete mode 100644 particle_life.hlsl

diff --git a/particle_life.hlsl b/particle_life.hlsl
deleted file mode 100644
index 4cffbdb04e1b60..00000000000000
--- a/particle_life.hlsl
+++ /dev/null
@@ -1,147 +0,0 @@
-#define ROOT_SIGNATURE \
-    "RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)," \
-    "CBV(b0)," \
-    "SRV(t0)," \
-    "SRV(t1)," \
-    "UAV(u0)," \
-    "UAV(u1)"
-    
-
-cbuffer CONSTANTS : register(b0) {
-    uint ParticleTypeMax;
-    uint NumParticles;
-    float2 WorldSize;
-    float Friction;
-    float ForceMultipler;
-}
-
-struct Rule {
-    float force;
-    float min_distance;
-    float max_distance;
-};
-
-struct Particle {
-    float2 position;
-    float2 velocity;
-    uint type;
-};
-
-struct Vertex {
-    float2 position;
-    uint color;
-};
-
-StructuredBuffer<Rule> Rules : register(t0);
-StructuredBuffer<Particle> OldParticles : register(t1);
-RWStructuredBuffer<Particle> NewParticles : register(u0);
-RWStructuredBuffer<Vertex> Vertices : register(u1);
-
-
-float3 particle_type_to_color(uint type);
-uint float_to_abgr(float3 rgb);
-
-
-[numthreads(32, 1, 1)]
-void main(uint3 dispatch_thread_id : SV_DispatchThreadID) {
-    uint particle_id = dispatch_thread_id.x;
-
-    Particle particle = OldParticles[particle_id];
-    
-    // Accumulate forces
-    float2 force = float2(0,0);
-    float hit = 0;
-
-    for (uint i = 0; i < NumParticles; ++i) {
-        if (i == particle_id)
-            continue;
-        
-        Particle other_particle = OldParticles[i];
-
-        Rule rule = Rules[particle.type * ParticleTypeMax + other_particle.type];
-
-        float2 direction = other_particle.position - particle.position;
-
-        // wrapping
-        if (direction.x > WorldSize.x * 0.5f)
-            direction.x -= WorldSize.x;
-        if (direction.x < WorldSize.x * -0.5f)
-            direction.x += WorldSize.x;
-        if (direction.y > WorldSize.y * 0.5f)
-            direction.y -= WorldSize.y;
-        if (direction.y < WorldSize.y * -0.5f)
-            direction.y += WorldSize.y;
-
-        // apply rule   
-        float distance = length(direction);
-        direction = normalize(direction);
-
-        if (distance < rule.min_distance) {
-            float repulsive_amount = abs(rule.force) * (1.0f - (distance / rule.min_distance))  * -3.0f;
-            force += direction * repulsive_amount;
-        }
-
-        if (distance < rule.max_distance) {
-            float attract_amount = rule.force * (1.0f - (distance / rule.max_distance));
-            force += direction * attract_amount;
-            hit += 0.01f;
-        }
-    }
-
-    float2 velocity = particle.velocity;
-    velocity += force * ForceMultipler;
-    velocity *= Friction;
-
-    particle.position = particle.position + velocity;
-
-    if (particle.position.x < 0)
-        particle.position.x += WorldSize.x;
-
-    if (particle.position.x > WorldSize.x)
-        particle.position.x -= WorldSize.x;
-
-    if (particle.position.y < 0)
-        particle.position.y += WorldSize.y;
-
-    if (particle.position.y > WorldSize.y)
-        particle.position.y -= WorldSize.y;
-
-
-    particle.velocity = velocity;
-
-    Vertices[particle_id].position = particle.position;
-
-    float3 color =  particle_type_to_color(particle.type);
-
-    color = lerp(color, color * 0.1f, 1-saturate(hit));
-
-    Vertices[particle_id].color = float_to_abgr(color);
-
-    NewParticles[particle_id] = particle;
-}
-
-
-
-// from https://chilliant.com/rgb2hsv.html
-float3 hue2rgb(float H) {
-    float R = abs(H * 6 - 3) - 1;  
-    float G = 2 - abs(H * 6 - 2);  
-    float B = 2 - abs(H * 6 - 4); 
-    return saturate(float3(R,G,B));
-}
-
-float3 particle_type_to_color(uint type) {
-    float hue = (float)type / float(ParticleTypeMax);
-    return hue2rgb(hue);
-}
-
-uint float_to_abgr(float3 rgb) {
-    rgb *= 255.0;
-
-    uint r = rgb.x;
-    uint g = rgb.y;
-    uint b = rgb.z;
-    uint a = 255; 
-
-    return (a << 24) | (b << 16) | (g << 8) | r;    
-}
\ No newline at end of file

>From dcd2c494f9d9fa33e2631e2e19609c3d6167c5cd Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 27 Aug 2024 13:15:16 -0700
Subject: [PATCH 11/16] Simplify IsIntangible calculation, add empty new line

---
 clang/lib/Sema/SemaHLSL.cpp                   | 48 ++++++++++---------
 .../Types/Traits/IsIntangibleTypeErrors.hlsl  |  2 +-
 2 files changed, 26 insertions(+), 24 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index f7cf18be195f3f..d907462c583976 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1534,42 +1534,44 @@ static bool calculateIsIntangibleType(QualType Ty) {
   llvm::SmallVector<QualType> TypesToScan;
   TypesToScan.push_back(Ty);
   while (!TypesToScan.empty()) {
-    QualType T = TypesToScan.pop_back_val();
-    assert(T == T->getCanonicalTypeUnqualified() && "expected sugar-free type");
-    assert(!isa<MatrixType>(T) && "Matrix types not yet supported in HLSL");
+    QualType T = TypesToScan.pop_back_val()->getCanonicalTypeUnqualified();
+
+    if (T->isBuiltinType()) {
+      if (T->isHLSLIntangibleType())
+        return true;
+    }
 
     if (const auto *AT = dyn_cast<ConstantArrayType>(T)) {
-      QualType ElTy = AT->getElementType()->getCanonicalTypeUnqualified();
-      if (ElTy->isBuiltinType())
-        return ElTy->isHLSLSpecificType();
-      TypesToScan.push_back(ElTy);
+      TypesToScan.push_back(AT->getElementType());
       continue;
     }
 
     if (const auto *VT = dyn_cast<VectorType>(T)) {
-      QualType ElTy = VT->getElementType()->getCanonicalTypeUnqualified();
-      assert(ElTy->isBuiltinType() && "vectors can only contain builtin types");
-      if (ElTy->isHLSLSpecificType())
-        return true;
+      assert(!VT->getElementType()
+                  .getCanonicalType()
+                  .getUnqualifiedType()
+                  ->isHLSLIntangibleType() &&
+             "vectors can only contain builtin types that are not intangible");
+      continue;
+    }
+
+    if (const auto *MT = dyn_cast<MatrixType>(T)) {
+      assert(!MT->getElementType()
+                  .getCanonicalType()
+                  .getUnqualifiedType()
+                  ->isHLSLIntangibleType() &&
+             "matrices can only contain builtin types that are not intangible");
       continue;
     }
 
     if (const auto *RT = dyn_cast<RecordType>(T)) {
       const RecordDecl *RD = RT->getDecl();
-      for (const auto *FD : RD->fields()) {
-        QualType FieldTy = FD->getType()->getCanonicalTypeUnqualified();
-        if (FieldTy->isBuiltinType()) {
-          if (FieldTy->isHLSLSpecificType())
-            return true;
-        } else {
-          TypesToScan.push_back(FieldTy);
-        }
-      }
+      for (const auto *FD : RD->fields())
+        TypesToScan.push_back(FD->getType());
 
       if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
-        for (const CXXBaseSpecifier &B : CXXRD->bases()) {
-          TypesToScan.push_back(B.getType()->getCanonicalTypeUnqualified());
-        }
+        for (const CXXBaseSpecifier &B : CXXRD->bases())
+          TypesToScan.push_back(B.getType());
       }
       continue;
     }
diff --git a/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl b/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
index bbf2a1682e3f05..0803086749bd7d 100644
--- a/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
+++ b/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
@@ -8,4 +8,4 @@ void fn(int X) {
   // expected-error@#vla {{variable length arrays are not supported in '__builtin_hlsl_is_intangible'}}
   // expected-warning@#vla {{variable length arrays in C++ are a Clang extension}}
   _Static_assert(!__builtin_hlsl_is_intangible(int[X]), ""); // #vla
-}
\ No newline at end of file
+}

>From 97ffb26613a44e19cb1d74ef090128291d1776e9 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 3 Sep 2024 16:21:46 -0700
Subject: [PATCH 12/16] Replace DenseMap cache with 2 bits on
 RecordDeclBitfields

---
 clang/include/clang/AST/Decl.h      |  6 +++
 clang/include/clang/AST/DeclBase.h  | 15 +++++++-
 clang/include/clang/Sema/SemaHLSL.h |  5 +--
 clang/lib/AST/Decl.cpp              |  1 +
 clang/lib/Sema/SemaHLSL.cpp         | 57 ++++++++++++++++-------------
 5 files changed, 54 insertions(+), 30 deletions(-)

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 6d84bd03de810a..819b7eeeeab7fb 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -61,6 +61,7 @@ class Expr;
 class FunctionTemplateDecl;
 class FunctionTemplateSpecializationInfo;
 class FunctionTypeLoc;
+enum class IntangibleResult : unsigned char;
 class LabelStmt;
 class MemberSpecializationInfo;
 class Module;
@@ -4298,6 +4299,11 @@ class RecordDecl : public TagDecl {
 
   void reorderDecls(const SmallVectorImpl<Decl *> &Decls);
 
+  // Intangible types
+  IntangibleResult getIntangible() const { return static_cast<IntangibleResult>(RecordDeclBits.Intangible); }
+
+  void setIntangible(IntangibleResult R) { RecordDeclBits.Intangible = llvm::to_underlying(R); }
+
   /// Determines whether this declaration represents the
   /// injected class name.
   ///
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index ee662ed73d7e0e..9f309be75d0ac1 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -1411,6 +1411,15 @@ enum class DeductionCandidate : unsigned char {
   Aggregate,
 };
 
+enum class IntangibleResult : unsigned char {
+  // IsIntangible has not been computed.
+  Invalid = 0,
+  // Intangible type
+  Intangible,
+  // Not an intangible type
+  NotIntangible
+};
+
 enum class RecordArgPassingKind;
 enum class OMPDeclareReductionInitKind;
 enum class ObjCImplementationControl;
@@ -1680,9 +1689,13 @@ class DeclContext {
     LLVM_PREFERRED_TYPE(bool)
     uint64_t IsRandomized : 1;
 
+    // Indicates whether this struct is intangible
+    LLVM_PREFERRED_TYPE(IntangibleResult)
+    uint64_t Intangible : 2;
+
     /// True if a valid hash is stored in ODRHash. This should shave off some
     /// extra storage and prevent CXXRecordDecl to store unused bits.
-    uint64_t ODRHash : 26;
+    uint64_t ODRHash : 24;
   };
 
   /// Number of inherited and non-inherited bits in RecordDeclBitfields.
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index cae3a502c73cf0..ceb66fb71577a1 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -64,10 +64,7 @@ class SemaHLSL : public SemaBase {
 
   // HLSL Type trait implementations
   bool IsScalarizedLayoutCompatible(QualType T1, QualType T2) const;
-  bool IsIntangibleType(const QualType T1);
-
-private:
-  llvm::DenseMap<const Type *, bool> IsIntangibleTypeCache;
+  bool IsIntangibleType(QualType T1);
 };
 
 } // namespace clang
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 1a07125815832e..0cea6a704df301 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -5016,6 +5016,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C,
   setParamDestroyedInCallee(false);
   setArgPassingRestrictions(RecordArgPassingKind::CanPassInRegs);
   setIsRandomized(false);
+  setIntangible(IntangibleResult::Invalid);
   setODRHash(0);
 }
 
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d907462c583976..f301ccc079efd9 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -10,6 +10,7 @@
 
 #include "clang/Sema/SemaHLSL.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/RecursiveASTVisitor.h"
@@ -29,7 +30,7 @@
 
 using namespace clang;
 
-SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S), IsIntangibleTypeCache() {}
+SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {}
 
 Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
                                  SourceLocation KwLoc, IdentifierInfo *Ident,
@@ -1527,11 +1528,11 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
   return false;
 }
 
-static bool calculateIsIntangibleType(QualType Ty) {
-  assert(!Ty.getCanonicalType().getUnqualifiedType()->isBuiltinType() &&
+static bool calculateIsIntangibleType(const Type *Ty) {
+  assert(!Ty->isBuiltinType() &&
          "builtin types should be taken care of in IsIntangibleType");
 
-  llvm::SmallVector<QualType> TypesToScan;
+  llvm::SmallVector<const Type *> TypesToScan;
   TypesToScan.push_back(Ty);
   while (!TypesToScan.empty()) {
     QualType T = TypesToScan.pop_back_val()->getCanonicalTypeUnqualified();
@@ -1542,7 +1543,7 @@ static bool calculateIsIntangibleType(QualType Ty) {
     }
 
     if (const auto *AT = dyn_cast<ConstantArrayType>(T)) {
-      TypesToScan.push_back(AT->getElementType());
+      TypesToScan.push_back(AT->getElementType().getTypePtr());
       continue;
     }
 
@@ -1566,12 +1567,15 @@ static bool calculateIsIntangibleType(QualType Ty) {
 
     if (const auto *RT = dyn_cast<RecordType>(T)) {
       const RecordDecl *RD = RT->getDecl();
+      if (RD->getIntangible() == IntangibleResult::Intangible)
+        return true;
+      
       for (const auto *FD : RD->fields())
-        TypesToScan.push_back(FD->getType());
+        TypesToScan.push_back(FD->getType().getTypePtr());
 
       if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
         for (const CXXBaseSpecifier &B : CXXRD->bases())
-          TypesToScan.push_back(B.getType());
+          TypesToScan.push_back(B.getType().getTypePtr());
       }
       continue;
     }
@@ -1579,27 +1583,30 @@ static bool calculateIsIntangibleType(QualType Ty) {
   return false;
 }
 
-bool SemaHLSL::IsIntangibleType(const clang::QualType Ty) {
-  if (Ty.isNull())
+bool SemaHLSL::IsIntangibleType(clang::QualType QT) {
+  if (QT.isNull())
     return false;
 
   // check if it's a builtin type first (simple check, no need to cache it)
-  QualType CT = Ty->getCanonicalTypeUnqualified();
-  if (CT->isBuiltinType())
-    return CT->isHLSLIntangibleType();
-
-  // more complex type -> check if we already have it in the cache
-  const auto CachedEntry = IsIntangibleTypeCache.find(Ty.getTypePtr());
-  if (CachedEntry != IsIntangibleTypeCache.end()) {
-    assert(CachedEntry->second == calculateIsIntangibleType(Ty) &&
-           "IsIntangibleType mismatch");
-    return CachedEntry->second;
-  }
-
-  // calculate and add to cache
-  bool IsIntangible = calculateIsIntangibleType(Ty);
-  IsIntangibleTypeCache[Ty.getTypePtr()] = IsIntangible;
-  return IsIntangible;
+  const Type *Ty = QT->getCanonicalTypeUnqualified()->getTypePtr();
+  if (Ty->isBuiltinType())
+    return Ty->isHLSLIntangibleType();
+
+  while (isa<ConstantArrayType>(Ty))
+    Ty = Ty->getArrayElementTypeNoTypeQual();
+
+  const RecordType *RT = dyn_cast<RecordType>(Ty);
+  if (!RT)
+    return false;
+
+  RecordDecl *RD = RT->getAsRecordDecl();
+  IntangibleResult Result = RD->getIntangible();
+  if (Result == IntangibleResult::Invalid) {
+    Result = calculateIsIntangibleType(Ty) ? IntangibleResult::Intangible
+                                           : IntangibleResult::NotIntangible; 
+    RD->setIntangible(Result);
+  }
+  return Result == IntangibleResult::Intangible;
 }
 
 static void BuildFlattenedTypeList(QualType BaseTy,

>From 094e208326f22ac063069cfcf0924186d4ba5845 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 3 Sep 2024 23:06:10 -0700
Subject: [PATCH 13/16] Store IsIntangible in CXXRecordDecl flags; set it when
 new field or base class is added

---
 .../clang/AST/CXXRecordDeclDefinitionBits.def |  4 ++
 clang/include/clang/AST/Decl.h                |  6 --
 clang/include/clang/AST/DeclBase.h            | 15 +---
 clang/include/clang/AST/DeclCXX.h             |  4 ++
 clang/lib/AST/Decl.cpp                        |  1 -
 clang/lib/AST/DeclCXX.cpp                     | 17 ++++-
 clang/lib/Sema/SemaHLSL.cpp                   | 70 ++-----------------
 .../Types/Traits/IsIntangibleType.hlsl        | 23 +++++-
 8 files changed, 52 insertions(+), 88 deletions(-)

diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
index cdf0804680ad0a..48830109b6b6a8 100644
--- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
+++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
@@ -249,4 +249,8 @@ FIELD(HasDeclaredCopyAssignmentWithConstParam, 1, MERGE_OR)
 /// base classes or fields have a no-return destructor
 FIELD(IsAnyDestructorNoReturn, 1, NO_MERGE)
 
+/// Whether the record type is intangible (if any base classes or fields have
+/// type that is intangible). HLSL only.
+FIELD(IsIntangible, 1, NO_MERGE)
+
 #undef FIELD
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 9ab9528baceda8..0600ecc4d14a18 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -61,7 +61,6 @@ class Expr;
 class FunctionTemplateDecl;
 class FunctionTemplateSpecializationInfo;
 class FunctionTypeLoc;
-enum class IntangibleResult : unsigned char;
 class LabelStmt;
 class MemberSpecializationInfo;
 class Module;
@@ -4300,11 +4299,6 @@ class RecordDecl : public TagDecl {
 
   void reorderDecls(const SmallVectorImpl<Decl *> &Decls);
 
-  // Intangible types
-  IntangibleResult getIntangible() const { return static_cast<IntangibleResult>(RecordDeclBits.Intangible); }
-
-  void setIntangible(IntangibleResult R) { RecordDeclBits.Intangible = llvm::to_underlying(R); }
-
   /// Determines whether this declaration represents the
   /// injected class name.
   ///
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 9f309be75d0ac1..ee662ed73d7e0e 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -1411,15 +1411,6 @@ enum class DeductionCandidate : unsigned char {
   Aggregate,
 };
 
-enum class IntangibleResult : unsigned char {
-  // IsIntangible has not been computed.
-  Invalid = 0,
-  // Intangible type
-  Intangible,
-  // Not an intangible type
-  NotIntangible
-};
-
 enum class RecordArgPassingKind;
 enum class OMPDeclareReductionInitKind;
 enum class ObjCImplementationControl;
@@ -1689,13 +1680,9 @@ class DeclContext {
     LLVM_PREFERRED_TYPE(bool)
     uint64_t IsRandomized : 1;
 
-    // Indicates whether this struct is intangible
-    LLVM_PREFERRED_TYPE(IntangibleResult)
-    uint64_t Intangible : 2;
-
     /// True if a valid hash is stored in ODRHash. This should shave off some
     /// extra storage and prevent CXXRecordDecl to store unused bits.
-    uint64_t ODRHash : 24;
+    uint64_t ODRHash : 26;
   };
 
   /// Number of inherited and non-inherited bits in RecordDeclBitfields.
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 0d72cc6a08dcb4..b46422d4a5f3c2 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1547,6 +1547,10 @@ class CXXRecordDecl : public RecordDecl {
   /// destructors are marked noreturn.
   bool isAnyDestructorNoReturn() const { return data().IsAnyDestructorNoReturn; }
 
+  /// Returns true if the class contains HLSL intangible type, either as 
+  /// a field or in base class.
+  bool isIntangible() const { return data().IsIntangible; }
+
   /// If the class is a local class [class.local], returns
   /// the enclosing function declaration.
   const FunctionDecl *isLocalClass() const {
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 0cea6a704df301..1a07125815832e 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -5016,7 +5016,6 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C,
   setParamDestroyedInCallee(false);
   setArgPassingRestrictions(RecordArgPassingKind::CanPassInRegs);
   setIsRandomized(false);
-  setIntangible(IntangibleResult::Invalid);
   setODRHash(0);
 }
 
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 9a3ede426e9143..b3721a19344a0b 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -109,7 +109,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
       ImplicitCopyAssignmentHasConstParam(true),
       HasDeclaredCopyConstructorWithConstParam(false),
       HasDeclaredCopyAssignmentWithConstParam(false),
-      IsAnyDestructorNoReturn(false), IsLambda(false),
+      IsAnyDestructorNoReturn(false), IsIntangible(false), IsLambda(false),
       IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false),
       HasODRHash(false), Definition(D) {}
 
@@ -431,6 +431,9 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
     if (BaseClassDecl->isAnyDestructorNoReturn())
       data().IsAnyDestructorNoReturn = true;
 
+    if (BaseClassDecl->isIntangible())
+      data().IsIntangible = true;
+
     // C++11 [class.copy]p18:
     //   The implicitly-declared copy assignment operator for a class X will
     //   have the form 'X& X::operator=(const X&)' if each direct base class B
@@ -1401,6 +1404,18 @@ void CXXRecordDecl::addedMember(Decl *D) {
     //   than subobjects of zero size
     if (data().Empty && !IsZeroSize)
       data().Empty = false;
+
+    if (getLangOpts().HLSL) {
+      const Type* Ty = Field->getType().getTypePtr();
+      while (isa<ConstantArrayType>(Ty))
+        Ty = Ty->getArrayElementTypeNoTypeQual();
+
+      Ty = Ty->getUnqualifiedDesugaredType();
+      if (Ty->isBuiltinType())
+        data().IsIntangible |= Ty->isHLSLIntangibleType();
+      else if (const RecordType *RT = dyn_cast<RecordType>(Ty))
+        data().IsIntangible |= RT->getAsCXXRecordDecl()->isIntangible();
+    }
   }
 
   // Handle using declarations of conversion functions.
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index a04cdae2d47c5d..93629e6b25bd24 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1612,85 +1612,29 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
   return false;
 }
 
-static bool calculateIsIntangibleType(const Type *Ty) {
-  assert(!Ty->isBuiltinType() &&
-         "builtin types should be taken care of in IsIntangibleType");
 
-  llvm::SmallVector<const Type *> TypesToScan;
-  TypesToScan.push_back(Ty);
-  while (!TypesToScan.empty()) {
-    QualType T = TypesToScan.pop_back_val()->getCanonicalTypeUnqualified();
-
-    if (T->isBuiltinType()) {
-      if (T->isHLSLIntangibleType())
-        return true;
-    }
-
-    if (const auto *AT = dyn_cast<ConstantArrayType>(T)) {
-      TypesToScan.push_back(AT->getElementType().getTypePtr());
-      continue;
-    }
-
-    if (const auto *VT = dyn_cast<VectorType>(T)) {
-      assert(!VT->getElementType()
-                  .getCanonicalType()
-                  .getUnqualifiedType()
-                  ->isHLSLIntangibleType() &&
-             "vectors can only contain builtin types that are not intangible");
-      continue;
-    }
-
-    if (const auto *MT = dyn_cast<MatrixType>(T)) {
-      assert(!MT->getElementType()
-                  .getCanonicalType()
-                  .getUnqualifiedType()
-                  ->isHLSLIntangibleType() &&
-             "matrices can only contain builtin types that are not intangible");
-      continue;
-    }
-
-    if (const auto *RT = dyn_cast<RecordType>(T)) {
-      const RecordDecl *RD = RT->getDecl();
-      if (RD->getIntangible() == IntangibleResult::Intangible)
-        return true;
-      
-      for (const auto *FD : RD->fields())
-        TypesToScan.push_back(FD->getType().getTypePtr());
-
-      if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
-        for (const CXXBaseSpecifier &B : CXXRD->bases())
-          TypesToScan.push_back(B.getType().getTypePtr());
-      }
-      continue;
-    }
-  }
-  return false;
-}
 
 bool SemaHLSL::IsIntangibleType(clang::QualType QT) {
   if (QT.isNull())
     return false;
+  
+  const Type *Ty = QT->getUnqualifiedDesugaredType();
 
   // check if it's a builtin type first (simple check, no need to cache it)
-  const Type *Ty = QT->getCanonicalTypeUnqualified()->getTypePtr();
   if (Ty->isBuiltinType())
     return Ty->isHLSLIntangibleType();
 
+  // unwrap arrays
   while (isa<ConstantArrayType>(Ty))
     Ty = Ty->getArrayElementTypeNoTypeQual();
 
-  const RecordType *RT = dyn_cast<RecordType>(Ty);
+  const RecordType *RT = dyn_cast<RecordType>(Ty->getUnqualifiedDesugaredType());
   if (!RT)
     return false;
 
-  RecordDecl *RD = RT->getAsRecordDecl();
-  IntangibleResult Result = RD->getIntangible();
-  if (Result == IntangibleResult::Invalid) {
-    Result = calculateIsIntangibleType(Ty) ? IntangibleResult::Intangible
-                                           : IntangibleResult::NotIntangible; 
-    RD->setIntangible(Result);
-  }
-  return Result == IntangibleResult::Intangible;
+  CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
+  assert(RD != nullptr && "all HLSL struct and classes should be CXXRecordDecl");
+  return RD->isIntangible();
 }
 
 static void BuildFlattenedTypeList(QualType BaseTy,
diff --git a/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl b/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
index 39a912f99d3896..92cba1dcd4bdfe 100644
--- a/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
+++ b/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
@@ -55,7 +55,24 @@ class Simple {
     int a;
 };
 
-class MyClass3 : MyClass2, Simple {
-    half h;
+template<typename T> struct TemplatedBuffer {
+    T a;
+    __hlsl_resource_t h;
+};
+_Static_assert(__builtin_hlsl_is_intangible(TemplatedBuffer<int>), "");
+
+struct MyStruct2 : TemplatedBuffer<float> {
+    float x;
+};
+_Static_assert(__builtin_hlsl_is_intangible(MyStruct2), "");
+
+struct MyStruct3 {
+    const TemplatedBuffer<float> TB[10];
+};
+_Static_assert(__builtin_hlsl_is_intangible(MyStruct3), "");
+
+template<typename T> struct SimpleTemplate {
+    T a;
 };
-_Static_assert(__builtin_hlsl_is_intangible(MyClass3), "");
+_Static_assert(__builtin_hlsl_is_intangible(SimpleTemplate<__hlsl_resource_t>), "");
+_Static_assert(!__builtin_hlsl_is_intangible(SimpleTemplate<float>), "");

>From 0e1b82c86f6baf2788de36b1dfa9e6770ee42a03 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 3 Sep 2024 23:19:31 -0700
Subject: [PATCH 14/16] clang-format & desugar

---
 clang/include/clang/AST/DeclCXX.h | 2 +-
 clang/lib/AST/DeclCXX.cpp         | 2 +-
 clang/lib/Sema/SemaHLSL.cpp       | 8 ++++----
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index b46422d4a5f3c2..88277ec5fbc667 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1547,7 +1547,7 @@ class CXXRecordDecl : public RecordDecl {
   /// destructors are marked noreturn.
   bool isAnyDestructorNoReturn() const { return data().IsAnyDestructorNoReturn; }
 
-  /// Returns true if the class contains HLSL intangible type, either as 
+  /// Returns true if the class contains HLSL intangible type, either as
   /// a field or in base class.
   bool isIntangible() const { return data().IsIntangible; }
 
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index b3721a19344a0b..e1d640fb7fd33f 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1406,7 +1406,7 @@ void CXXRecordDecl::addedMember(Decl *D) {
       data().Empty = false;
 
     if (getLangOpts().HLSL) {
-      const Type* Ty = Field->getType().getTypePtr();
+      const Type *Ty = Field->getType()->getUnqualifiedDesugaredType();
       while (isa<ConstantArrayType>(Ty))
         Ty = Ty->getArrayElementTypeNoTypeQual();
 
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 93629e6b25bd24..141be788f9deb4 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1612,8 +1612,6 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
   return false;
 }
 
-
-
 bool SemaHLSL::IsIntangibleType(clang::QualType QT) {
   if (QT.isNull())
     return false;
@@ -1628,12 +1626,14 @@ bool SemaHLSL::IsIntangibleType(clang::QualType QT) {
   while (isa<ConstantArrayType>(Ty))
     Ty = Ty->getArrayElementTypeNoTypeQual();
 
-  const RecordType *RT = dyn_cast<RecordType>(Ty->getUnqualifiedDesugaredType());
+  const RecordType *RT =
+      dyn_cast<RecordType>(Ty->getUnqualifiedDesugaredType());
   if (!RT)
     return false;
 
   CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
-  assert(RD != nullptr && "all HLSL struct and classes should be CXXRecordDecl");
+  assert(RD != nullptr &&
+         "all HLSL struct and classes should be CXXRecordDecl");
   return RD->isIntangible();
 }
 

>From 2eba03c682d1e5b05b7497982e1840f96b00f5ad Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 3 Sep 2024 23:27:27 -0700
Subject: [PATCH 15/16] one more little space

---
 clang/lib/Sema/SemaHLSL.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 141be788f9deb4..cd7c4d14c3842b 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1615,7 +1615,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
 bool SemaHLSL::IsIntangibleType(clang::QualType QT) {
   if (QT.isNull())
     return false;
-  
+
   const Type *Ty = QT->getUnqualifiedDesugaredType();
 
   // check if it's a builtin type first (simple check, no need to cache it)

>From 027d473d082a9cd1799579212fc95316ff12fe54 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 4 Sep 2024 12:07:45 -0700
Subject: [PATCH 16/16] Rename to IsHLSLIntangible; return false for variable
 length arrays after diag

---
 .../include/clang/AST/CXXRecordDeclDefinitionBits.def  |  2 +-
 clang/include/clang/AST/DeclCXX.h                      |  2 +-
 clang/lib/AST/DeclCXX.cpp                              | 10 +++++-----
 clang/lib/Sema/SemaExprCXX.cpp                         |  5 +++--
 clang/lib/Sema/SemaHLSL.cpp                            |  2 +-
 5 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
index 48830109b6b6a8..6620840df0ced2 100644
--- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
+++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
@@ -251,6 +251,6 @@ FIELD(IsAnyDestructorNoReturn, 1, NO_MERGE)
 
 /// Whether the record type is intangible (if any base classes or fields have
 /// type that is intangible). HLSL only.
-FIELD(IsIntangible, 1, NO_MERGE)
+FIELD(IsHLSLIntangible, 1, NO_MERGE)
 
 #undef FIELD
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 88277ec5fbc667..252e6e92564142 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1549,7 +1549,7 @@ class CXXRecordDecl : public RecordDecl {
 
   /// Returns true if the class contains HLSL intangible type, either as
   /// a field or in base class.
-  bool isIntangible() const { return data().IsIntangible; }
+  bool isHLSLIntangible() const { return data().IsHLSLIntangible; }
 
   /// If the class is a local class [class.local], returns
   /// the enclosing function declaration.
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index e1d640fb7fd33f..01143391edab40 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -109,7 +109,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
       ImplicitCopyAssignmentHasConstParam(true),
       HasDeclaredCopyConstructorWithConstParam(false),
       HasDeclaredCopyAssignmentWithConstParam(false),
-      IsAnyDestructorNoReturn(false), IsIntangible(false), IsLambda(false),
+      IsAnyDestructorNoReturn(false), IsHLSLIntangible(false), IsLambda(false),
       IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false),
       HasODRHash(false), Definition(D) {}
 
@@ -431,8 +431,8 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
     if (BaseClassDecl->isAnyDestructorNoReturn())
       data().IsAnyDestructorNoReturn = true;
 
-    if (BaseClassDecl->isIntangible())
-      data().IsIntangible = true;
+    if (BaseClassDecl->isHLSLIntangible())
+      data().IsHLSLIntangible = true;
 
     // C++11 [class.copy]p18:
     //   The implicitly-declared copy assignment operator for a class X will
@@ -1412,9 +1412,9 @@ void CXXRecordDecl::addedMember(Decl *D) {
 
       Ty = Ty->getUnqualifiedDesugaredType();
       if (Ty->isBuiltinType())
-        data().IsIntangible |= Ty->isHLSLIntangibleType();
+        data().IsHLSLIntangible |= Ty->isHLSLIntangibleType();
       else if (const RecordType *RT = dyn_cast<RecordType>(Ty))
-        data().IsIntangible |= RT->getAsCXXRecordDecl()->isIntangible();
+        data().IsHLSLIntangible |= RT->getAsCXXRecordDecl()->isHLSLIntangible();
     }
   }
 
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 467fc5048c164f..14feafd1e6b17f 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5703,8 +5703,9 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
       if (Self.RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), T,
                                    diag::err_incomplete_type))
         return false;
-    DiagnoseVLAInCXXTypeTrait(Self, TInfo,
-                              tok::kw___builtin_hlsl_is_intangible);
+    if (DiagnoseVLAInCXXTypeTrait(Self, TInfo,
+                                  tok::kw___builtin_hlsl_is_intangible))
+      return false;
     return Self.HLSL().IsIntangibleType(T);
   }
 }
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index cd7c4d14c3842b..65aeda4b7b613e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1634,7 +1634,7 @@ bool SemaHLSL::IsIntangibleType(clang::QualType QT) {
   CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
   assert(RD != nullptr &&
          "all HLSL struct and classes should be CXXRecordDecl");
-  return RD->isIntangible();
+  return RD->isHLSLIntangible();
 }
 
 static void BuildFlattenedTypeList(QualType BaseTy,



More information about the cfe-commits mailing list