Repository: shader-slang/slang (2025-06-25T20:50:28Z)

Issue:
__alignOf<T> fails to compile on CPU if T is a template type with multiple generic parameters
This is a funny one, I'll submit a PR to fix this one soon.

So basically, `__alignOf<T>()` gets lowered as a call to this macro:
```c
    #define SLANG_ALIGN_OF(T) __alignof__(T)
```

If you try to call `__alignOf<float2>()`, it lowers to `SLANG_ALIGN_OF(Vector<float, 2>)`. And the C preprocessor of course has no notion of templates, so it thinks that that is 2 parameters, causing the downstream compiler to issue an error.

IMO the whole define is unnecessary; instead of compiler-specific builtins, we could just use `alignof(x)` which exists since C++11.


Discussion:

Patch:
diff --git a/include/slang.h b/include/slang.h
index ba247aadd1..977480bb39 100644
--- a/include/slang.h
+++ b/include/slang.h
@@ -263,7 +263,6 @@ convention for interface methods.
     #define SLANG_NO_INLINE __attribute__((noinline))
     #define SLANG_FORCE_INLINE inline __attribute__((always_inline))
     #define SLANG_BREAKPOINT(id) __builtin_trap();
-    #define SLANG_ALIGN_OF(T) __alignof__(T)
 #endif // SLANG_GCC_FAMILY
 
 #if SLANG_GCC_FAMILY || defined(__clang__)
@@ -280,7 +279,6 @@ convention for interface methods.
     #define SLANG_NO_INLINE __declspec(noinline)
     #define SLANG_FORCE_INLINE __forceinline
     #define SLANG_BREAKPOINT(id) __debugbreak();
-    #define SLANG_ALIGN_OF(T) __alignof(T)
 
     #define SLANG_INT64(x) (x##i64)
     #define SLANG_UINT64(x) (x##ui64)
diff --git a/prelude/slang-cpp-prelude.h b/prelude/slang-cpp-prelude.h
index 6530654c1b..f9c77cd911 100644
--- a/prelude/slang-cpp-prelude.h
+++ b/prelude/slang-cpp-prelude.h
@@ -223,7 +223,6 @@ Any platforms not detected by the above logic are now now explicitly zeroed out.
 
 // GCC Specific
 #if SLANG_GCC_FAMILY
-#define SLANG_ALIGN_OF(T) __alignof__(T)
 
 #define SLANG_BREAKPOINT(id) __builtin_trap()
 
@@ -234,7 +233,6 @@ Any platforms not detected by the above logic are now now explicitly zeroed out.
 
 // Microsoft VC specific
 #if SLANG_VC
-#define SLANG_ALIGN_OF(T) __alignof(T)
 
 #define SLANG_BREAKPOINT(id) __debugbreak();
 
diff --git a/prelude/slang-cuda-prelude.h b/prelude/slang-cuda-prelude.h
index a1d3da082c..f0c0531688 100644
--- a/prelude/slang-cuda-prelude.h
+++ b/prelude/slang-cuda-prelude.h
@@ -38,10 +38,6 @@
 #define SLANG_OFFSET_OF(type, member) (size_t)((char*)&(((type*)0)->member) - (char*)0)
 #endif
 
-#ifndef SLANG_ALIGN_OF
-#define SLANG_ALIGN_OF(type) __alignof__(type)
-#endif
-
 // Must be large enough to cause overflow and therefore infinity
 #ifndef SLANG_INFINITY
 #define SLANG_INFINITY ((float)(1e+300 * 1e+300))
diff --git a/source/core/slang-memory-arena.h b/source/core/slang-memory-arena.h
index 714918b9a4..f08541ddb7 100644
--- a/source/core/slang-memory-arena.h
+++ b/source/core/slang-memory-arena.h
@@ -382,9 +382,8 @@ inline const char* MemoryArena::allocateString(const char* chars, size_t numChar
 template<typename T>
 SLANG_FORCE_INLINE T* MemoryArena::allocate()
 {
-    void* mem = (SLANG_ALIGN_OF(T) <= kMinAlignment)
-                    ? allocate(sizeof(T))
-                    : allocateAligned(sizeof(T), SLANG_ALIGN_OF(T));
+    void* mem = (alignof(T) <= kMinAlignment) ? allocate(sizeof(T))
+                                              : allocateAligned(sizeof(T), alignof(T));
     return reinterpret_cast<T*>(mem);
 }
 
@@ -392,9 +391,8 @@ SLANG_FORCE_INLINE T* MemoryArena::allocate()
 template<typename T>
 SLANG_FORCE_INLINE T* MemoryArena::allocateArray(size_t numElems)
 {
-    return (numElems > 0)
-               ? reinterpret_cast<T*>(allocateAligned(sizeof(T) * numElems, SLANG_ALIGN_OF(T)))
-               : nullptr;
+    return (numElems > 0) ? reinterpret_cast<T*>(allocateAligned(sizeof(T) * numElems, alignof(T)))
+                          : nullptr;
 }
 
 // --------------------------------------------------------------------------
@@ -405,7 +403,7 @@ SLANG_FORCE_INLINE T* MemoryArena::allocateAndCopyArray(const T* arr, size_t num
     if (numElems > 0)
     {
         const size_t totalSize = sizeof(T) * numElems;
-        void* ptr = allocateAligned(totalSize, SLANG_ALIGN_OF(T));
+        void* ptr = allocateAligned(totalSize, alignof(T));
         ::memcpy(ptr, arr, totalSize);
         return reinterpret_cast<T*>(ptr);
     }
@@ -419,7 +417,7 @@ SLANG_FORCE_INLINE T* MemoryArena::allocateAndZeroArray(size_t numElems)
     if (numElems > 0)
     {
         const size_t totalSize = sizeof(T) * numElems;
-        void* ptr = allocateAligned(totalSize, SLANG_ALIGN_OF(T));
+        void* ptr = allocateAligned(totalSize, alignof(T));
         ::memset(ptr, 0, totalSize);
         return reinterpret_cast<T*>(ptr);
     }
diff --git a/source/core/slang-offset-container.h b/source/core/slang-offset-container.h
index 4e6920bed8..7398c3a8de 100644
--- a/source/core/slang-offset-container.h
+++ b/source/core/slang-offset-container.h
@@ -445,7 +445,7 @@ class OffsetContainer : public OffsetBase
     template<typename T>
     Offset32Ptr<T> newObject()
     {
-        void* data = allocate(sizeof(T), SLANG_ALIGN_OF(T));
+        void* data = allocate(sizeof(T), alignof(T));
         new (data) T();
         return Offset32Ptr<T>(getOffset(data));
     }
@@ -457,7 +457,7 @@ class OffsetContainer : public OffsetBase
         {
             return Offset32Array<T>();
         }
-        T* data = (T*)allocate(sizeof(T) * size, SLANG_ALIGN_OF(T));
+        T* data = (T*)allocate(sizeof(T) * size, alignof(T));
         for (size_t i = 0; i < size; ++i)
         {
             new (data + i) T();
diff --git a/source/core/slang-rtti-info.cpp b/source/core/slang-rtti-info.cpp
index 070b9c2042..b30b5ac752 100644
--- a/source/core/slang-rtti-info.cpp
+++ b/source/core/slang-rtti-info.cpp
@@ -13,11 +13,11 @@ namespace Slang
     {                                 \
         RttiInfo::Kind::Invalid, 0, 0 \
     }
-#define SLANG_RTTI_INFO_BASIC(name, type)                                    \
-    RttiInfo                                                                 \
-    {                                                                        \
-        RttiInfo::Kind::name, RttiInfo::AlignmentType(SLANG_ALIGN_OF(type)), \
-            RttiInfo::SizeType(sizeof(type))                                 \
+#define SLANG_RTTI_INFO_BASIC(name, type)                             \
+    RttiInfo                                                          \
+    {                                                                 \
+        RttiInfo::Kind::name, RttiInfo::AlignmentType(alignof(type)), \
+            RttiInfo::SizeType(sizeof(type))                          \
     }
 
 /* static */ const RttiInfo RttiInfo::g_basicTypes[Index(Kind::CountOf)] = {
diff --git a/source/core/slang-rtti-info.h b/source/core/slang-rtti-info.h
index 58e557dd01..9d4967cfeb 100644
--- a/source/core/slang-rtti-info.h
+++ b/source/core/slang-rtti-info.h
@@ -199,7 +199,7 @@ struct RttiInfo
     template<typename T>
     void init(Kind kind)
     {
-        init(kind, SLANG_ALIGN_OF(T), sizeof(T));
+        init(kind, alignof(T), sizeof(T));
     }
 
     /// Allocate memory for RttiInfo types.
@@ -475,7 +475,7 @@ struct GetRttiInfo<T[COUNT]>
     {
         FixedArrayRttiInfo info;
         info.m_kind = RttiInfo::Kind::FixedArray;
-        info.m_alignment = RttiInfo::AlignmentType(SLANG_ALIGN_OF(T));
+        info.m_alignment = RttiInfo::AlignmentType(alignof(T));
         info.m_size = RttiInfo::SizeType(sizeof(T) * COUNT);
         info.m_elementType = GetRttiInfo<T>::get();
         info.m_elementCount = COUNT;
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index 51e2f326e5..4511a71b67 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -3067,7 +3067,7 @@ int __alignOf()
     {
     case cuda :
     case cpp :
-        __intrinsic_asm "SLANG_ALIGN_OF($[0])", T;
+        __intrinsic_asm "alignof($[0])", T;
     }
 }
 
@@ -3078,8 +3078,8 @@ int __alignOf(T v)
 {
     __target_switch
     {
-    case cpp: __intrinsic_asm "SLANG_ALIGN_OF($T0)";
-    case cuda: __intrinsic_asm "SLANG_ALIGN_OF($T0)";
+    case cpp: __intrinsic_asm "alignof($T0)";
+    case cuda: __intrinsic_asm "alignof($T0)";
     }
 }
 
diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp
index 61a453f377..3982b579dc 100644
--- a/source/slang/slang-type-layout.cpp
+++ b/source/slang/slang-type-layout.cpp
@@ -463,7 +463,7 @@ struct CPULayoutRulesImpl : DefaultLayoutRulesImpl
         // the compilation.
         // If we are emitting C++, then there is no way in general to know how that C++ will be
         // compiled it could be 32 or 64 (or other) sizes. For now we just assume they are the same.
-        return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*), SLANG_ALIGN_OF(void*));
+        return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*), alignof(void*));
     }
 
     SimpleArrayLayoutInfo GetArrayLayout(SimpleLayoutInfo elementInfo, LayoutSize elementCount)
@@ -476,7 +476,7 @@ struct CPULayoutRulesImpl : DefaultLayoutRulesImpl
 
             // So it is actually a Array<T> on CPU which is a pointer and a size
             info.size = sizeof(void*) * 2;
-            info.alignment = SLANG_ALIGN_OF(void*);
+            info.alignment = alignof(void*);
 
             return info;
         }
@@ -539,7 +539,7 @@ struct CUDALayoutRulesImpl : DefaultLayoutRulesImpl
                 return SimpleLayoutInfo(
                     LayoutResourceKind::Uniform,
                     sizeof(uint8_t),
-                    SLANG_ALIGN_OF(uint8_t));
+                    alignof(uint8_t));
             }
 
         default:
@@ -1195,60 +1195,41 @@ struct CPUObjectLayoutRulesImpl : ObjectLayoutRulesImpl
         case ShaderParameterKind::ConstantBuffer:
         case ShaderParameterKind::ParameterBlock:
             // It's a pointer to the actual uniform data
-            return SimpleLayoutInfo(
-                LayoutResourceKind::Uniform,
-                sizeof(void*),
-                SLANG_ALIGN_OF(void*));
+            return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*), alignof(void*));
 
         case ShaderParameterKind::MutableTexture:
         case ShaderParameterKind::TextureUniformBuffer:
         case ShaderParameterKind::Texture:
             // It's a pointer to a texture interface
-            return SimpleLayoutInfo(
-                LayoutResourceKind::Uniform,
-                sizeof(void*),
-                SLANG_ALIGN_OF(void*));
+            return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*), alignof(void*));
 
         case ShaderParameterKind::StructuredBuffer:
         case ShaderParameterKind::MutableStructuredBuffer:
         case ShaderParameterKind::AppendConsumeStructuredBuffer:
             // It's a ptr and a size of the amount of elements
-            return SimpleLayoutInfo(
-                LayoutResourceKind::Uniform,
-                sizeof(void*) * 2,
-                SLANG_ALIGN_OF(void*));
+            return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*) * 2, alignof(void*));
 
         case ShaderParameterKind::RawBuffer:
         case ShaderParameterKind::Buffer:
         case ShaderParameterKind::MutableRawBuffer:
         case ShaderParameterKind::MutableBuffer:
             // It's a pointer and a size in bytes
-            return SimpleLayoutInfo(
-                LayoutResourceKind::Uniform,
-                sizeof(void*) * 2,
-                SLANG_ALIGN_OF(void*));
+            return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*) * 2, alignof(void*));
 
         case ShaderParameterKind::ShaderStorageBuffer:
         case ShaderParameterKind::AccelerationStructure:
         case ShaderParameterKind::SamplerState:
             // It's a pointer
-            return SimpleLayoutInfo(
-                LayoutResourceKind::Uniform,
-                sizeof(void*),
-                SLANG_ALIGN_OF(void*));
+            return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*), alignof(void*));
 
         case ShaderParameterKind::TextureSampler:
         case ShaderParameterKind::MutableTextureSampler:
             {
                 ObjectLayoutInfo info;
-                info.layoutInfos.add(SimpleLayoutInfo(
-                    LayoutResourceKind::Uniform,
-                    sizeof(void*),
-                    SLANG_ALIGN_OF(void*)));
-                info.layoutInfos.add(SimpleLayoutInfo(
-                    LayoutResourceKind::Uniform,
-                    sizeof(void*),
-                    SLANG_ALIGN_OF(void*)));
+                info.layoutInfos.add(
+                    SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*), alignof(void*)));
+                info.layoutInfos.add(
+                    SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*), alignof(void*)));
                 return info;
             }
         case ShaderParameterKind::InputRenderTarget:
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index f2f28e3e3f..940fbfbae1 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -837,7 +837,7 @@ static T makeFromSizeVersioned(const uint8_t* src)
     const size_t dstSize = sizeof(T);
 
     // If they are the same size, and appropriate alignment we can just cast and return
-    if (srcSize == dstSize && (size_t(src) & (SLANG_ALIGN_OF(T) - 1)) == 0)
+    if (srcSize == dstSize && (size_t(src) & (alignof(T) - 1)) == 0)
     {
         return *(const T*)src;
     }


Test patch:
diff --git a/tests/bugs/gh-7522.slang b/tests/bugs/gh-7522.slang
new file mode 100644
index 0000000000..01464cdacd
--- /dev/null
+++ b/tests/bugs/gh-7522.slang
@@ -0,0 +1,15 @@
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -cpu
+
+//TEST_INPUT:ubuffer(data=[0 0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<int> outputBuffer;
+
+[numthreads(1, 1, 1)]
+void computeMain(int3 dispatchThreadID: SV_DispatchThreadID)
+{
+    // The point of this test is to just check that __alignOf isn't implemented
+    // as a C macro that misinterprets templates as multiple arguments and fails
+    // to compile :) That's why it doesn't care much about the actual values of
+    // the alignments.
+    outputBuffer[0] = __alignOf<float2>() > 0 ? 1 : 0; // CHECK: 1
+    outputBuffer[1] = __alignOf<vector<int, 2>>() > 0 ? 1 : 0; // CHECK-NEXT: 1
+}