summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorArielG-NV <159081215+ArielG-NV@users.noreply.github.com>2025-08-08 13:19:25 -0700
committerGitHub <noreply@github.com>2025-08-08 20:19:25 +0000
commit07f21ee31b5f427edb72d5578f713b3da3f3b96f (patch)
tree777df480b51f488a296bcf0c231afc3cfff2afdc /source
parent719772c01a8ee8afa81cded249d6a51e33e17d8d (diff)
Error if super-type capabilities are a super-set of sub-type (#7452)
Fixes: #7410 Changes: 1. super-type capabilities must be a super-set of sub-type capabilities (and support the same shader stages/targets) * InheritanceDecl visits super-type to inherit it's capabilities; validate InheritanceDecl capabilities against sub-type * visit all container decl's with a default case * clean up functionDeclBase visitor * Simplify `diagnoseUndeclaredCapability` by moving logic into capability checking (more correct*) 3. added changed behavior to documentation 4. fixed some incorrect capabilities 5. **we do not** diagnose capability errors on interface requirement-to-implementation if both lack explicit capability requirements. This change is to work around a slangpy regression (test case for the failing situation is in `tests\language-feature\capability\capability-interface-extension-1.slang`), Note: maybe for slang-2026 we don't do this? 6. requirement & implementation must support the same shader stage/target. This was changed because otherwise we can have cases where `X` inherits from `Y`, but `Y` is only expected to be used in `glsl` whilst `X` is expected to be used in `hlsl | glsl` 7. removed `tests/language-feature/capability/capabilitySimplification3.slang` because it tests nothing special (redundant) Note: not using rebase due to separate branches depending on this PR --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
Diffstat (limited to 'source')
-rw-r--r--source/slang/glsl.meta.slang24
-rw-r--r--source/slang/hlsl.meta.slang85
-rw-r--r--source/slang/slang-capabilities.capdef12
-rw-r--r--source/slang/slang-capability.cpp131
-rw-r--r--source/slang/slang-capability.h32
-rw-r--r--source/slang/slang-check-decl.cpp335
-rw-r--r--source/slang/slang-check-stmt.cpp4
-rw-r--r--source/slang/slang-diagnostic-defs.h27
-rw-r--r--source/slang/slang-lower-to-ir.cpp2
-rw-r--r--source/slang/slang-parser.cpp4
-rw-r--r--source/slang/slang.natvis3
11 files changed, 446 insertions, 213 deletions
diff --git a/source/slang/glsl.meta.slang b/source/slang/glsl.meta.slang
index 782c09bb9..eeaf2a58c 100644
--- a/source/slang/glsl.meta.slang
+++ b/source/slang/glsl.meta.slang
@@ -4778,9 +4778,9 @@ public property uint3 gl_LaunchSizeEXT
// casting conflict due to the spirv implementation of PrimitiveIndex().
internal in uint __gl_PrimitiveID : SV_PrimitiveID;
-public property int gl_PrimitiveID
+public property int gl_PrimitiveID
{
- [require(cuda_glsl_hlsl_spirv)]
+ [require(cuda_glsl_hlsl_spirv, raytracing_allstages)]
get
{
__stage_switch
@@ -4796,9 +4796,9 @@ public property int gl_PrimitiveID
}
}
-public property int gl_InstanceID
+public property int gl_InstanceID
{
- [require(cuda_glsl_hlsl_spirv)]
+ [require(cuda_glsl_hlsl_spirv, raytracing_allstages)]
get
{
__stage_switch
@@ -8473,7 +8473,7 @@ public vector<T, N> subgroupPartitionedInclusiveMulNV(vector<T, N> value, uvec4
__generic<T : __BuiltinArithmeticType>
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public T subgroupPartitionedInclusiveMinNV(T value, uvec4 ballot)
{
return WaveMultiPrefixInclusiveMin(value, ballot);
@@ -8481,7 +8481,7 @@ public T subgroupPartitionedInclusiveMinNV(T value, uvec4 ballot)
__generic<T : __BuiltinArithmeticType, let N : int>
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public vector<T, N> subgroupPartitionedInclusiveMinNV(vector<T, N> value, uvec4 ballot)
{
return WaveMultiPrefixInclusiveMin(value, ballot);
@@ -8489,7 +8489,7 @@ public vector<T, N> subgroupPartitionedInclusiveMinNV(vector<T, N> value, uvec4
__generic<T : __BuiltinArithmeticType>
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public T subgroupPartitionedInclusiveMaxNV(T value, uvec4 ballot)
{
return WaveMultiPrefixInclusiveMax(value, ballot);
@@ -8497,7 +8497,7 @@ public T subgroupPartitionedInclusiveMaxNV(T value, uvec4 ballot)
__generic<T : __BuiltinArithmeticType, let N : int>
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public vector<T, N> subgroupPartitionedInclusiveMaxNV(vector<T, N> value, uvec4 ballot)
{
return WaveMultiPrefixInclusiveMax(value, ballot);
@@ -8585,7 +8585,7 @@ public vector<T, N> subgroupPartitionedExclusiveMulNV(vector<T, N> value, uvec4
__generic<T : __BuiltinArithmeticType>
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public T subgroupPartitionedExclusiveMinNV(T value, uvec4 ballot)
{
return WaveMultiPrefixExclusiveMin(value, ballot);
@@ -8593,7 +8593,7 @@ public T subgroupPartitionedExclusiveMinNV(T value, uvec4 ballot)
__generic<T : __BuiltinArithmeticType, let N : int>
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public vector<T, N> subgroupPartitionedExclusiveMinNV(vector<T, N> value, uvec4 ballot)
{
return WaveMultiPrefixExclusiveMin(value, ballot);
@@ -8601,7 +8601,7 @@ public vector<T, N> subgroupPartitionedExclusiveMinNV(vector<T, N> value, uvec4
__generic<T : __BuiltinArithmeticType>
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public T subgroupPartitionedExclusiveMaxNV(T value, uvec4 ballot)
{
return WaveMultiPrefixExclusiveMax(value, ballot);
@@ -8609,7 +8609,7 @@ public T subgroupPartitionedExclusiveMaxNV(T value, uvec4 ballot)
__generic<T : __BuiltinArithmeticType, let N : int>
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public vector<T, N> subgroupPartitionedExclusiveMaxNV(vector<T, N> value, uvec4 ballot)
{
return WaveMultiPrefixExclusiveMax(value, ballot);
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index 2d9543716..07f59ac46 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -4947,6 +4947,7 @@ T __getElement<T, U, I>(U collection, I index);
/// @category stage_io Stage IO types
__generic<T, let N : int>
+[require(glsl_hlsl_spirv, geometry)]
[require(glsl_hlsl_spirv, hull)]
__magic_type(HLSLInputPatchType)
__intrinsic_type($(kIROp_HLSLInputPatchType))
@@ -5017,6 +5018,7 @@ This type is supported natively when targeting HLSL.
*/
__magic_type(HLSL$(item.name)Type)
__intrinsic_type($(item.op))
+[require(byteaddressbuffer_rw)]
struct $(item.name)
{
// Note(tfoley): supports all operations from `ByteAddressBuffer`
@@ -5025,7 +5027,7 @@ struct $(item.name)
/// Get the number of bytes in the buffer.
///@param[out] dim The number of bytes in the buffer.
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_spirv_wgsl, structuredbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_spirv_wgsl)]
void GetDimensions(out uint dim)
{
__target_switch
@@ -5054,7 +5056,7 @@ struct $(item.name)
/// When targeting non-HLSL, the status is always 0.
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv_wgsl, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv_wgsl)]
uint Load(int location)
{
__target_switch
@@ -5067,7 +5069,7 @@ struct $(item.name)
[__NoSideEffect]
[ForceInline]
- [require(hlsl, byteaddressbuffer_rw)]
+ [require(hlsl)]
uint Load(int location, out uint status)
{
__target_switch
@@ -5093,7 +5095,7 @@ struct $(item.name)
/// When targeting non-HLSL, the status is always 0.
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
uint2 Load2(uint location)
{
__target_switch
@@ -5106,7 +5108,7 @@ struct $(item.name)
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
uint2 Load2Aligned(uint location, uint alignment)
{
__target_switch
@@ -5123,7 +5125,7 @@ struct $(item.name)
///@return `uint2` Two 32-bit unsigned integers loaded from the buffer.
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
uint2 Load2Aligned(uint location)
{
__target_switch
@@ -5136,7 +5138,7 @@ struct $(item.name)
[__NoSideEffect]
[ForceInline]
- [require(hlsl, byteaddressbuffer_rw)]
+ [require(hlsl)]
uint2 Load2(uint location, out uint status)
{
__target_switch
@@ -5161,7 +5163,7 @@ struct $(item.name)
/// When targeting non-HLSL, the status is always 0.
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
uint3 Load3(uint location)
{
__target_switch
@@ -5174,7 +5176,7 @@ struct $(item.name)
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
uint3 Load3Aligned(uint location, uint alignment)
{
__target_switch
@@ -5191,7 +5193,7 @@ struct $(item.name)
///@return `uint3` Three 32-bit unsigned integer value loaded from the buffer.
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
uint3 Load3Aligned(uint location)
{
__target_switch
@@ -5204,7 +5206,7 @@ struct $(item.name)
[__NoSideEffect]
[ForceInline]
- [require(hlsl, byteaddressbuffer_rw)]
+ [require(hlsl)]
uint3 Load3(uint location, out uint status)
{
__target_switch
@@ -5228,7 +5230,7 @@ struct $(item.name)
/// If any values were taken from an unmapped tile, `CheckAccessFullyMapped` returns FALSE.
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
uint4 Load4(uint location)
{
__target_switch
@@ -5241,7 +5243,7 @@ struct $(item.name)
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
uint4 Load4Aligned(uint location, uint alignment)
{
__target_switch
@@ -5258,7 +5260,7 @@ struct $(item.name)
///@return `uint4` Four 32-bit unsigned integer value loaded from the buffer.
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
uint4 Load4Aligned(uint location)
{
__target_switch
@@ -5271,7 +5273,7 @@ struct $(item.name)
[__NoSideEffect]
[ForceInline]
- [require(hlsl, byteaddressbuffer_rw)]
+ [require(hlsl)]
uint4 Load4(uint location, out uint status)
{
__target_switch
@@ -5282,7 +5284,7 @@ struct $(item.name)
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
T Load<T>(uint location)
{
return __byteAddressBufferLoad<T>(this, location, 0);
@@ -5290,7 +5292,7 @@ struct $(item.name)
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
T LoadAligned<T>(uint location, uint alignment)
{
return __byteAddressBufferLoad<T>(this, location, alignment);
@@ -5303,7 +5305,7 @@ struct $(item.name)
///Currently, this function only supports when `T` is scalar, vector, or matrix type.
[__NoSideEffect]
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
T LoadAligned<T>(uint location)
{
return __byteAddressBufferLoad<T>(this, location, __naturalStrideOf<T>());
@@ -5420,6 +5422,7 @@ ${{{{
/// maps to `atomicAdd`.
[__requiresNVAPI]
[ForceInline]
+ [require(sm_5_0)]
void InterlockedAddF16(uint byteAddress, half value, out half originalValue)
{
__target_switch
@@ -5459,6 +5462,7 @@ ${{{{
/// maps to `atomicAdd`.
[__requiresNVAPI]
[ForceInline]
+ [require(sm_5_0)]
void InterlockedAddF16Emulated(uint byteAddress, half value, out half originalValue)
{
__target_switch
@@ -5735,7 +5739,7 @@ ${{{{
/// @param value The operand of the atomic operation.
/// @param original_value The original value at `dest` before the $(op.internalName) operation.
[ForceInline]
- [require(cuda_glsl_hlsl_metal_spirv, atomic_glsl_hlsl_cuda_metal, byteaddressbuffer_rw)]
+ [require(cuda_glsl_hlsl_metal_spirv, atomic_glsl_hlsl_cuda_metal)]
void Interlocked$(op.name)(
UINT dest,
UINT value,
@@ -5751,7 +5755,7 @@ ${{{{
}
[ForceInline]
- [require(cuda_glsl_hlsl_metal_spirv, atomic_glsl_hlsl_cuda_metal, byteaddressbuffer_rw)]
+ [require(cuda_glsl_hlsl_metal_spirv, atomic_glsl_hlsl_cuda_metal)]
void Interlocked$(op.name)(
UINT dest,
UINT value)
@@ -5778,7 +5782,7 @@ ${{{{
/// translates to `InterlockedCompareExchange`.
/// For CUDA, this function maps to `atomicCAS`.
[ForceInline]
- [require(cuda_glsl_hlsl_metal_spirv, atomic_glsl_hlsl_cuda_metal, byteaddressbuffer_rw)]
+ [require(cuda_glsl_hlsl_metal_spirv, atomic_glsl_hlsl_cuda_metal)]
void InterlockedCompareExchange(
UINT dest,
UINT compare_value,
@@ -5803,7 +5807,7 @@ ${{{{
/// translates to `InterlockedCompareStore`.
/// For CUDA, this function maps to `atomicCAS`.
[ForceInline]
- [require(cuda_glsl_hlsl_metal_spirv, atomic_glsl_hlsl_cuda_metal, byteaddressbuffer_rw)]
+ [require(cuda_glsl_hlsl_metal_spirv, atomic_glsl_hlsl_cuda_metal)]
void InterlockedCompareStore(
UINT dest,
UINT compare_value,
@@ -5824,7 +5828,7 @@ ${{{{
///@param address The input address in bytes, which must be a multiple of 4.
///@param alignment Specifies the alignment of the location, which must be a multiple of 4.
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
void Store(uint address, uint value)
{
__target_switch
@@ -5841,7 +5845,7 @@ ${{{{
///@param value Two input values.
///@param alignment Specifies the alignment of the location, which must be a multiple of 4.
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
void Store2(uint address, uint2 value)
{
__target_switch
@@ -5854,7 +5858,7 @@ ${{{{
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
void Store2(uint address, uint2 value, uint alignment)
{
__target_switch
@@ -5870,7 +5874,7 @@ ${{{{
///@param address The input address in bytes, which must be a multiple of 8.
///@param value Two input values.
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
void Store2Aligned(uint address, uint2 value)
{
__target_switch
@@ -5886,7 +5890,7 @@ ${{{{
///@param value Three input values.
///@param alignment Specifies the alignment of the location, which must be a multiple of 4.
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
void Store3(uint address, uint3 value)
{
__target_switch
@@ -5898,7 +5902,7 @@ ${{{{
}
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
void Store3(uint address, uint3 value, uint alignment)
{
__target_switch
@@ -5914,7 +5918,7 @@ ${{{{
///@param address The input address in bytes, which must be a multiple of 12.
///@param value Three input values.
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_spirv)]
void Store3Aligned(uint address, uint3 value)
{
__target_switch
@@ -5930,7 +5934,7 @@ ${{{{
///@param value Four input values.
///@param alignment Specifies the alignment of the location, which must be a multiple of 4.
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_spirv)]
void Store4(uint address, uint4 value)
{
__target_switch
@@ -5943,7 +5947,7 @@ ${{{{
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
void Store4(uint address, uint4 value, uint alignment)
{
__target_switch
@@ -5959,7 +5963,7 @@ ${{{{
///@param address The input address in bytes, which must be a multiple of 16.
///@param value Four input values.
[ForceInline]
- [require(cpp_cuda_glsl_hlsl_metal_spirv, byteaddressbuffer_rw)]
+ [require(cpp_cuda_glsl_hlsl_metal_spirv)]
void Store4Aligned(uint address, uint4 value)
{
__target_switch
@@ -7392,7 +7396,6 @@ float16_t asfloat16(uint16_t value)
}
[__readNone]
-[require(cuda_glsl_hlsl_spirv, shader5_sm_5_0)]
vector<float16_t,N> asfloat16<let N : int>(vector<uint16_t,N> value)
{
__target_switch
@@ -7414,7 +7417,6 @@ matrix<float16_t,R,C> asfloat16<let R : int, let C : int>(matrix<uint16_t,R,C> v
[__unsafeForceInlineEarly]
[__readNone]
-[require(cuda_hlsl_metal_spirv, shader5_sm_5_0)]
int16_t asint16(float16_t value)
{
__target_switch
@@ -7430,8 +7432,7 @@ int16_t asint16(float16_t value)
}
[__unsafeForceInlineEarly]
-[__readNone]
-[require(cuda_hlsl_metal_spirv, shader5_sm_5_0)]
+[__readNone]
vector<int16_t,N> asint16<let N : int>(vector<float16_t,N> value)
{
__target_switch
@@ -7444,7 +7445,6 @@ vector<int16_t,N> asint16<let N : int>(vector<float16_t,N> value)
[__unsafeForceInlineEarly]
[__readNone]
-[require(cuda_hlsl_spirv, shader5_sm_5_0)]
matrix<int16_t,R,C> asint16<let R : int, let C : int>(matrix<float16_t,R,C> value)
{
__target_switch
@@ -7455,8 +7455,7 @@ matrix<int16_t,R,C> asint16<let R : int, let C : int>(matrix<float16_t,R,C> valu
}
[__readNone]
-[__unsafeForceInlineEarly]
-[require(cuda_hlsl_metal_spirv, shader5_sm_5_0)]
+[__unsafeForceInlineEarly]
float16_t asfloat16(int16_t value)
{
__target_switch
@@ -7473,7 +7472,6 @@ float16_t asfloat16(int16_t value)
[__unsafeForceInlineEarly]
[__readNone]
-[require(cuda_hlsl_metal_spirv, shader5_sm_5_0)]
vector<float16_t,N> asfloat16<let N : int>(vector<int16_t,N> value)
{
__target_switch
@@ -7489,7 +7487,6 @@ vector<float16_t,N> asfloat16<let N : int>(vector<int16_t,N> value)
[__unsafeForceInlineEarly]
[__readNone]
-[require(cuda_hlsl_spirv, shader5_sm_5_0)]
matrix<float16_t,R,C> asfloat16<let R : int, let C : int>(matrix<int16_t,R,C> value)
{
__target_switch
@@ -10270,7 +10267,7 @@ vector<T, N> fwidth_coarse(vector<T, N> x)
__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
[__readNone]
-[require(glsl_hlsl_spirv, fragmentprocessing)]
+[require(glsl_hlsl_spirv, fragmentprocessing_derivativecontrol)]
matrix<T, N, M> fwidth_coarse(matrix<T, N, M> x)
{
__target_switch
@@ -10332,7 +10329,7 @@ vector<T, N> fwidth_fine(vector<T, N> x)
__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
[__readNone]
-[require(glsl_hlsl_spirv, fragmentprocessing)]
+[require(glsl_hlsl_spirv, fragmentprocessing_derivativecontrol)]
matrix<T, N, M> fwidth_fine(matrix<T, N, M> x)
{
__target_switch
@@ -26175,8 +26172,10 @@ CoopVec<T, N> atan<T : __BuiltinFloatingPointType, let N : int>(CoopVec<T, N> yO
// [ForceInline]
[require(cooperative_vector)]
+[require(cooperative_vector)]
[require(hlsl_coopvec_poc)]
[require(optix_coopvec)]
+[require(GL_ARB_gpu_shader5)]
CoopVec<T, N> fma<T : __BuiltinFloatingPointType, let N : int>(CoopVec<T, N> a, CoopVec<T, N> b, CoopVec<T, N> c)
{
// TODO: Investigate, why does this fail if it's not inlined
diff --git a/source/slang/slang-capabilities.capdef b/source/slang/slang-capabilities.capdef
index ec821ef21..95f1335da 100644
--- a/source/slang/slang-capabilities.capdef
+++ b/source/slang/slang-capabilities.capdef
@@ -152,7 +152,7 @@ def glsl_spirv_1_6 : glsl_spirv_1_5;
// We have multiple capabilities for the various SPIR-V versions,
// arranged so that they inherit from one another to represent which versions
-// provide a super-set of the features of earlier ones (e.g., SPIR-V 1.4 is
+// provide a superset of the features of earlier ones (e.g., SPIR-V 1.4 is
// expressed as inheriting from SPIR-V 1.3).
def _spirv_1_0 : spirv;
@@ -887,6 +887,7 @@ def _GL_NV_shader_invocation_reorder : _GLSL_460;
def _GL_NV_shader_subgroup_partitioned : _GLSL_140;
def _GL_NV_shader_texture_footprint : _GLSL_450;
def _GL_NV_cluster_acceleration_structure : _GLSL_460;
+def _GL_NV_cooperative_vector : _GLSL_450;
// GLSL extension and SPV extension associations.
@@ -1134,6 +1135,10 @@ alias GL_NV_shader_texture_footprint = _GL_NV_shader_texture_footprint | spvImag
/// [EXT]
alias GL_NV_cluster_acceleration_structure = _GL_NV_cluster_acceleration_structure | spvRayTracingClusterAccelerationStructureNV;
+/// Represents the GL_NV_cooperative_vector extension.
+/// [EXT]
+alias GL_NV_cooperative_vector = _GL_NV_cooperative_vector | spvCooperativeVectorNV + spvCooperativeVectorTrainingNV;
+
// Define feature names not reliant on shader stages
/// NVAPI capability for HLSL
@@ -1190,11 +1195,10 @@ alias bufferreference_int64 = bufferreference + GL_EXT_shader_explicit_arithmeti
/// Note that cpp and cuda are supported via a fallback non-cooperative implementation
/// No HLSL shader model bound yet
/// [Compound]
-alias cooperative_vector = _sm_6_9 | cpp | _cuda_sm_9_0 | spvCooperativeVectorNV;
+alias cooperative_vector = _sm_6_9 | cpp | _cuda_sm_9_0 | spvCooperativeVectorNV | _GL_NV_cooperative_vector;
/// Capabilities needed to train cooperative vectors
/// [Compound]
-alias cooperative_vector_training = spvCooperativeVectorTrainingNV;
-
+alias cooperative_vector_training = spvCooperativeVectorTrainingNV | _GL_NV_cooperative_vector;
/// Capabilities needed to use cooperative matrices
/// [Compound]
alias cooperative_matrix = spvCooperativeMatrixKHR;
diff --git a/source/slang/slang-capability.cpp b/source/slang/slang-capability.cpp
index a2fef9f8a..a965ecd93 100644
--- a/source/slang/slang-capability.cpp
+++ b/source/slang/slang-capability.cpp
@@ -269,6 +269,22 @@ CapabilityAtomSet CapabilityAtomSet::newSetWithoutImpliedAtoms() const
//// CapabiltySet
+CapabilityAtomSet getTargetAtomsInSet(const CapabilitySet& set)
+{
+ CapabilityAtomSet out;
+ for (auto i : set.getCapabilityTargetSets())
+ out.add((UInt)i.first);
+ return out;
+}
+
+CapabilityAtomSet getStageAtomsInSet(const CapabilityTargetSet& set)
+{
+ CapabilityAtomSet out;
+ for (auto i : set.getShaderStageSets())
+ out.add((UInt)i.first);
+ return out;
+}
+
CapabilityAtom getTargetAtomInSet(const CapabilityAtomSet& atomSet)
{
auto targetSet = getAtomSetOfTargets();
@@ -959,56 +975,117 @@ CapabilitySet::AtomSets::Iterator CapabilitySet::getAtomSets() const
return CapabilitySet::AtomSets::Iterator(&this->getCapabilityTargetSets()).begin();
}
-bool CapabilitySet::checkCapabilityRequirement(
+void CapabilitySet::checkCapabilityRequirement(
+ CheckCapabilityRequirementOptions options,
CapabilitySet const& available,
CapabilitySet const& required,
- CapabilityAtomSet& outFailedAvailableSet)
+ CapabilityAtomSet& outFailedAvailableSet,
+ CheckCapabilityRequirementResult& result)
{
- // Requirements x are met by available disjoint capabilities (a | b) iff
+ // 'required' capabilities x are met by 'available' disjoint capabilities (a | b) iff
// both 'a' satisfies x and 'b' satisfies x.
// If we have a caller function F() decorated with:
// [require(hlsl, _sm_6_3)] [require(spirv, _spv_ray_tracing)] void F() { g(); }
// We'd better make sure that `g()` can be compiled with both (hlsl+_sm_6_3) and
// (spirv+_spv_ray_tracing) capability sets. In this method, F()'s capability declaration is
// represented by `available`, and g()'s capability is represented by `required`. We will check
- // that for every capability conjunction X of F(), there is one capability conjunction Y in g()
+ // that for every capability conjunction X of F(), there is a capability conjunction Y in g()
// such that X implies Y.
//
- // if empty there is no body, all capabilities are supported.
- if (required.isEmpty())
- return true;
+ // If empty, all capabilities are supported.
+ // Either, we require no capabilities (return true)
+ // or we have no capability requirements (return true)
+ if (required.isEmpty() || available.isEmpty())
+ {
+ result = CheckCapabilityRequirementResult::AvailableIsASuperSetToRequired;
+ return;
+ }
+ // invalid isn't a fail because the capabilities already threw an error.
if (required.isInvalid())
{
outFailedAvailableSet.add((UInt)CapabilityAtom::Invalid);
- return false;
+ result = CheckCapabilityRequirementResult::AvailableIsASuperSetToRequired;
+ return;
}
- // If F's capability is empty, we can satisfy any non-empty requirements.
- //
- if (available.isEmpty() && !required.isEmpty())
- return false;
+ auto availableTargetSets = available.getCapabilityTargetSets();
+ auto requiredTargetSets = required.getCapabilityTargetSets();
+ if (options == CheckCapabilityRequirementOptions::MustHaveEqualAbstractAtoms)
+ {
+ // If we have a mismatch in capability-target count we clearly have a
+ // mismatch and will fail
+ auto availableTargetSetsCount = availableTargetSets.getCount();
+ auto requiredTargetSetsCount = requiredTargetSets.getCount();
+ if (availableTargetSetsCount != requiredTargetSetsCount)
+ {
+ auto availableTargets = getTargetAtomsInSet(available);
+ auto requiredTargets = getTargetAtomsInSet(required);
+ if (requiredTargetSetsCount > availableTargetSetsCount)
+ {
+ result = CheckCapabilityRequirementResult::AvailableIsNotASuperSetToRequired;
+ requiredTargets.subtractWith((UIntSet)availableTargets);
+ outFailedAvailableSet.add((UIntSet)requiredTargets);
+ }
+ else
+ {
+ result = CheckCapabilityRequirementResult::RequiredIsMissingAbstractAtoms;
+ availableTargets.subtractWith((UIntSet)requiredTargets);
+ outFailedAvailableSet.add((UIntSet)availableTargets);
+ }
+ return;
+ }
+ }
- // if all sets in `available` are not a super-set to at least 1 `required` set, then we have an
- // err
- for (auto& availableTarget : available.m_targetSets)
+ // if all sets in `available` are not a superset to `required` then we have an
+ // error.
+ for (auto& availableTarget : availableTargetSets)
{
- auto reqTarget = required.m_targetSets.tryGetValue(availableTarget.first);
+ auto reqTarget = requiredTargetSets.tryGetValue(availableTarget.first);
if (!reqTarget)
{
outFailedAvailableSet.add((UInt)availableTarget.first);
- return false;
+ result = CheckCapabilityRequirementResult::RequiredIsMissingAbstractAtoms;
+ return;
}
- for (auto& availableStage : availableTarget.second.shaderStageSets)
+ if (options == CheckCapabilityRequirementOptions::MustHaveEqualAbstractAtoms)
{
- auto reqStage = reqTarget->shaderStageSets.tryGetValue(availableStage.first);
+ // If we have a mismatch in capability-stage count we clearly have a
+ // mismatch and will fail
+ auto availableStageSetsCount = availableTarget.second.getShaderStageSets().getCount();
+ auto requiredStageSetsCount = reqTarget->getShaderStageSets().getCount();
+ if (availableStageSetsCount != requiredStageSetsCount)
+ {
+ auto availableStages = getStageAtomsInSet(availableTarget.second);
+ auto requiredStages = getStageAtomsInSet(*reqTarget);
+
+ if (requiredStageSetsCount > availableStageSetsCount)
+ {
+ result = CheckCapabilityRequirementResult::AvailableIsNotASuperSetToRequired;
+ requiredStages.subtractWith((UIntSet)availableStages);
+ outFailedAvailableSet.add((UIntSet)requiredStages);
+ }
+ else
+ {
+ result = CheckCapabilityRequirementResult::RequiredIsMissingAbstractAtoms;
+ availableStages.subtractWith((UIntSet)requiredStages);
+ outFailedAvailableSet.add((UIntSet)availableStages);
+ }
+ return;
+ }
+ }
+
+ for (auto& availableStage : availableTarget.second.getShaderStageSets())
+ {
+ auto reqStage = reqTarget->getShaderStageSets().tryGetValue(availableStage.first);
if (!reqStage)
{
outFailedAvailableSet.add((UInt)availableStage.first);
- return false;
+ result = CheckCapabilityRequirementResult::RequiredIsMissingAbstractAtoms;
+ return;
}
const CapabilityAtomSet* lastBadStage = nullptr;
@@ -1020,7 +1097,7 @@ bool CapabilitySet::checkCapabilityRequirement(
{
const auto& reqStageSet = reqStage->atomSet.value();
if (availableStageSet.contains(reqStageSet))
- break;
+ continue;
else
lastBadStage = &reqStageSet;
}
@@ -1031,13 +1108,19 @@ bool CapabilitySet::checkCapabilityRequirement(
outFailedAvailableSet,
*lastBadStage,
availableStageSet);
- return false;
+
+ // Not a failiure if nothing is missing
+ if (outFailedAvailableSet.isEmpty())
+ continue;
+ result = CheckCapabilityRequirementResult::AvailableIsNotASuperSetToRequired;
+ return;
}
}
}
}
- return true;
+ result = CheckCapabilityRequirementResult::AvailableIsASuperSetToRequired;
+ return;
}
/// Converts spirv version atom to the glsl_spirv equivlent. If not possible, Invalid is returned
@@ -1097,7 +1180,7 @@ UnownedStringSlice capabilityNameToStringWithoutPrefix(CapabilityName capability
return name;
}
-void printDiagnosticArg(StringBuilder& sb, const CapabilityAtomSet atomSet)
+void printDiagnosticArg(StringBuilder& sb, const CapabilityAtomSet& atomSet)
{
bool isFirst = true;
for (auto atom : atomSet.newSetWithoutImpliedAtoms())
diff --git a/source/slang/slang-capability.h b/source/slang/slang-capability.h
index 4bf0704a0..43f933620 100644
--- a/source/slang/slang-capability.h
+++ b/source/slang/slang-capability.h
@@ -36,7 +36,7 @@ namespace Slang
// The situation is slightly more complicated for a function. A function
// might require a specific set of atomic feature, and that is the simple
// case. In this simple case, we know that a target can run a function
-// if the features of the target are a super-set of those required by
+// if the features of the target are a superset of those required by
// the function.
//
// In the more general case, we might have a function that can be used
@@ -98,6 +98,27 @@ struct CapabilityTargetSet
/// 2. `this` has completly disjoint shader stages from other.
bool tryJoin(const CapabilityTargetSets& other);
void unionWith(const CapabilityTargetSet& other);
+
+ const CapabilityStageSets& getShaderStageSets() const { return shaderStageSets; }
+};
+
+enum class CheckCapabilityRequirementOptions
+{
+ // `available` can have a subset of the abstract atoms `required` has
+ AvailableCanHaveSubsetOfAbstractAtoms,
+ // `available` and `required` both must have equal abstract stage & target atoms
+ MustHaveEqualAbstractAtoms,
+};
+
+enum class CheckCapabilityRequirementResult
+{
+ // `available` is a superset to `required`
+ AvailableIsASuperSetToRequired,
+ // `available` is not a superset to `required`
+ AvailableIsNotASuperSetToRequired,
+ // `available` has abstract atoms that `required` is missing.
+ // Only possible with CheckCapabilityRequirementOptions::MustHaveEqualAbstractAtoms
+ RequiredIsMissingAbstractAtoms,
};
struct CapabilitySet
@@ -186,12 +207,14 @@ public:
CapabilitySet const& targetCaps,
bool& isEqual) const;
- /// Find any capability sets which are in 'available' but not in 'required'. Return false if
+ /// Identify capability sets which are in 'available' but not in 'required'. Return false if
/// this situation occurs.
- static bool checkCapabilityRequirement(
+ static void checkCapabilityRequirement(
+ CheckCapabilityRequirementOptions options,
CapabilitySet const& available,
CapabilitySet const& required,
- CapabilityAtomSet& outFailedAvailableSet);
+ CapabilityAtomSet& outFailedAvailableSet,
+ CheckCapabilityRequirementResult& result);
// For each element in `elementsToPermutateWith`, create and add a different conjunction
// permutation by adding to `setToPermutate`.
@@ -370,6 +393,7 @@ bool isSpirvExtensionAtom(CapabilityAtom name);
void printDiagnosticArg(StringBuilder& sb, CapabilityAtom atom);
void printDiagnosticArg(StringBuilder& sb, CapabilityName name);
+void printDiagnosticArg(StringBuilder& sb, const CapabilityAtomSet& atomSet);
const CapabilityAtomSet& getAtomSetOfTargets();
const CapabilityAtomSet& getAtomSetOfStages();
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 72b5c19db..0a9853012 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -995,12 +995,10 @@ struct SemanticsDeclCapabilityVisitor : public SemanticsDeclVisitorBase,
CapabilitySet getDeclaredCapabilitySet(Decl* decl);
-
void visitDecl(Decl*) {}
void visitDeclGroup(DeclGroup*) {}
void checkVarDeclCommon(VarDeclBase* varDecl);
- void visitAggTypeDeclBase(AggTypeDeclBase* decl);
- void visitNamespaceDeclBase(NamespaceDeclBase* decl);
+ void visitContainerDecl(ContainerDecl* decl);
void visitVarDecl(VarDecl* varDecl) { checkVarDeclCommon(varDecl); }
@@ -1013,7 +1011,8 @@ struct SemanticsDeclCapabilityVisitor : public SemanticsDeclVisitorBase,
void diagnoseUndeclaredCapability(
Decl* decl,
const DiagnosticInfo& diagnosticInfo,
- const CapabilityAtomSet& failedAtomsInsideAvailableSet);
+ const CapabilityAtomSet& failedAtomsInsideAvailableSet,
+ bool printProvenance);
};
@@ -14382,26 +14381,29 @@ struct CapabilityDeclReferenceVisitor
auto targetCaseCount = stmt->targetCases.getCount();
for (Index targetCaseIndex = 0; targetCaseIndex < targetCaseCount; targetCaseIndex++)
{
- // We may recieve a `default:` case for a `__target_switch`. If this is the case,
- // we must resolve the target capability for a non empty set of
- // `calling_functions_targets`:
- // ``` default_target = calling_functions_targets-{other_case_targets} ```
+ // The logic here is to collect a list of `case` statment capabilities
+ // so that down-the-line we can specialize according to the compile-capabilities.
//
- // * `calling_functions_capability` = `requirement attribute` of the calling
- // function; if missing
- // we can assume it is `any_target`
+ // The additional goal we have is to merge all case-capabilities into 1 set
+ // so that we can propegate them to the parent-function so that a user may break-down
+ // a functon into `stage`/`target` specific code.
//
- // * `{other_case_targets}` = set of all capabilities all `case` statments target
- // inside the `__target_switch`
-
- // If we do not handle `default:`, the codegen will fail when trying to find a
- // specific codegen target not handled explicitly by a `case` statment. We must also
- // ensure the `default` case is last so we have priority to hit `case` statments and
- // can preprocess `case` statments before the `default` case.
+ // A few important details
+ // 1. Case statments (other than `default:`) may have overlapping capabilities. This is
+ // to allow "more specialized" `case` statments to support specializing code on
+ // higher-feature-levels to support writing 1 function to handle cases such as sm_5_0
+ // and sm_6_0 support all in 1 function.
+ //
+ // 2. All `case:` statments are explicit with their own-capabilities, `default:`
+ // statments are not. `default:` statments have the value `CapabilityName::Invalid`. If
+ // we find a `default` statment we assign it all shader target/stage capabilities that
+ // the other `case` statments did not specify which is legal for the current calling
+ // function (based on the parent function `require` decl).
CapabilitySet targetCap;
if (CapabilityName(stmt->targetCases[targetCaseIndex]->capability) ==
CapabilityName::Invalid)
{
+ // swap the `default` case to the end so that we process it last
if (targetCaseCount - 1 != targetCaseIndex)
{
for (Index i = targetCaseIndex; i < targetCaseCount - 1; i++)
@@ -14594,30 +14596,22 @@ CapabilitySet SemanticsDeclCapabilityVisitor::getDeclaredCapabilitySet(Decl* dec
return declaredCaps;
}
-void SemanticsDeclCapabilityVisitor::visitAggTypeDeclBase(AggTypeDeclBase* decl)
-{
- decl->inferredCapabilityRequirements = getDeclaredCapabilitySet(decl);
-}
-
-void SemanticsDeclCapabilityVisitor::visitNamespaceDeclBase(NamespaceDeclBase* decl)
+void SemanticsDeclCapabilityVisitor::visitContainerDecl(ContainerDecl* decl)
{
+ // Any potential child must get it's capabilities from `getDeclaredCapabilitySet`.
decl->inferredCapabilityRequirements = getDeclaredCapabilitySet(decl);
}
-template<typename ProcessFunc, typename ParentDiagnosticFunc>
-static inline void _dispatchCapabilitiesVisitorOfFunctionDecl(
- SemanticsVisitor* visitor,
- FunctionDeclBase* funcDecl,
- const ProcessFunc& processFunc,
- const ParentDiagnosticFunc& parentDiagnosticFunc)
+void SemanticsDeclCapabilityVisitor::visitFunctionDeclBase(FunctionDeclBase* funcDecl)
{
- visitor->setParentFuncOfVisitor(funcDecl);
+ setParentFuncOfVisitor(funcDecl);
+ // visit the members of our funcDecl
for (auto member : funcDecl->getDirectMemberDecls())
{
- visitor->ensureDecl(member, DeclCheckState::CapabilityChecked);
+ ensureDecl(member, DeclCheckState::CapabilityChecked);
_propagateRequirement(
- visitor,
+ this,
funcDecl->inferredCapabilityRequirements,
funcDecl,
member,
@@ -14625,22 +14619,35 @@ static inline void _dispatchCapabilitiesVisitorOfFunctionDecl(
member->loc);
}
+ // visit the body of our funcDecl, propagate capabilities.
visitReferencedDecls(
- *visitor,
+ *this,
funcDecl->body,
funcDecl->loc,
funcDecl->findModifier<RequireCapabilityAttribute>(),
- processFunc,
- parentDiagnosticFunc);
+ [this, funcDecl](SyntaxNode* node, const CapabilitySet& nodeCaps, SourceLoc refLoc)
+ {
+ _propagateRequirement(
+ this,
+ funcDecl->inferredCapabilityRequirements,
+ funcDecl,
+ node,
+ nodeCaps,
+ refLoc);
+ },
+ [this, funcDecl](DiagnosticCategory category)
+ { _propagateSeeDefinitionOf(this, funcDecl, category); });
+ // non-static function join's capabilities with parent
+ // to become a superset of the parent.
if (!isEffectivelyStatic(funcDecl))
{
auto parentAggTypeDecl = getParentAggTypeDecl(funcDecl);
if (parentAggTypeDecl)
{
- visitor->ensureDecl(parentAggTypeDecl, DeclCheckState::CapabilityChecked);
+ ensureDecl(parentAggTypeDecl, DeclCheckState::CapabilityChecked);
_propagateRequirement(
- visitor,
+ this,
funcDecl->inferredCapabilityRequirements,
funcDecl,
parentAggTypeDecl,
@@ -14648,34 +14655,13 @@ static inline void _dispatchCapabilitiesVisitorOfFunctionDecl(
funcDecl->loc);
}
}
-}
-
-void SemanticsDeclCapabilityVisitor::visitFunctionDeclBase(FunctionDeclBase* funcDecl)
-{
- // If the function is an entrypoint and specifies a target stage, add the capabilities to
- // our function capabilities.
- _dispatchCapabilitiesVisitorOfFunctionDecl(
- this,
- funcDecl,
- [this, funcDecl](SyntaxNode* node, const CapabilitySet& nodeCaps, SourceLoc refLoc)
- {
- _propagateRequirement(
- this,
- funcDecl->inferredCapabilityRequirements,
- funcDecl,
- node,
- nodeCaps,
- refLoc);
- },
- [this, funcDecl](DiagnosticCategory category)
- { _propagateSeeDefinitionOf(this, funcDecl, category); });
+ // Get require of decl + add parents
auto declaredCaps = getDeclaredCapabilitySet(funcDecl);
-
auto vis = getDeclVisibility(funcDecl);
- // If 0 capabilities were annotated on a function, capabilities are inferred from the
- // function body
+ // If 0 capabilities were annotated on this function,
+ // capabilities are inferred from the children.
if (declaredCaps.isEmpty())
{
declaredCaps = funcDecl->inferredCapabilityRequirements;
@@ -14687,30 +14673,37 @@ void SemanticsDeclCapabilityVisitor::visitFunctionDeclBase(FunctionDeclBase* fun
addModifier(funcDecl, declaredCapModifier);
if (vis == DeclVisibility::Public)
{
- // For public decls, we need to enforce that the function
- // only uses capabilities that it declares.
- // At a minimum we will propagate shader requirements to our
- // function from calling children in all cases so the parent
- // can enforce shader targets correctly and propagate to `main`
+ // We need to enforce that the function-body
+ // only uses capabilities that the function-decl declares.
+ //
+ // A small exception to this rule is that the body must
+ // implement all shader stages/targets of the functionDecl
+ // requirements. The body can support *more* stages/targets,
+ // these will just be not accessible (which is fine since a user
+ // may only need hlsl support for a function, using an STD-LIB function
+ // implemented for all targets/stages).
CapabilityAtomSet failedAvailableCapabilityConjunction;
- if (!CapabilitySet::checkCapabilityRequirement(
- declaredCaps,
- funcDecl->inferredCapabilityRequirements,
- failedAvailableCapabilityConjunction))
- {
- diagnoseUndeclaredCapability(
- funcDecl,
- Diagnostics::useOfUndeclaredCapability,
- failedAvailableCapabilityConjunction);
- funcDecl->inferredCapabilityRequirements = declaredCaps;
- }
- else
- funcDecl->inferredCapabilityRequirements.nonDestructiveJoin(declaredCaps);
+ CheckCapabilityRequirementResult checkCapabilityResult;
+ CapabilitySet::checkCapabilityRequirement(
+ CheckCapabilityRequirementOptions::AvailableCanHaveSubsetOfAbstractAtoms,
+ declaredCaps,
+ funcDecl->inferredCapabilityRequirements,
+ failedAvailableCapabilityConjunction,
+ checkCapabilityResult);
+ diagnoseUndeclaredCapability(
+ funcDecl,
+ Diagnostics::useOfUndeclaredCapability,
+ failedAvailableCapabilityConjunction,
+ true);
+
+ // declared capabilities must be a superset.
+ funcDecl->inferredCapabilityRequirements = declaredCaps;
}
else
{
// For internal decls, their inferred capability should be joined
- // with the declared capabilities.
+ // with the declared capabilities since we are assuming the stdlib
+ // is not wrong.
funcDecl->inferredCapabilityRequirements.join(declaredCaps);
}
}
@@ -14718,8 +14711,29 @@ void SemanticsDeclCapabilityVisitor::visitFunctionDeclBase(FunctionDeclBase* fun
void SemanticsDeclCapabilityVisitor::visitInheritanceDecl(InheritanceDecl* inheritanceDecl)
{
- // Check that the implementation of an interface requirement is not using more capabilities
- // than what's declared on the interface method.
+ auto inheritanceParentDecl = inheritanceDecl->parentDecl;
+ ensureDecl(inheritanceParentDecl, DeclCheckState::CapabilityChecked);
+
+ // Propegate capabilities of inheritance `base` to
+ // `InheritanceDecl`
+ visitReferencedDecls(
+ *this,
+ inheritanceDecl->base,
+ inheritanceDecl->loc,
+ nullptr,
+ [this, inheritanceDecl](SyntaxNode* node, const CapabilitySet& nodeCaps, SourceLoc refLoc)
+ {
+ _propagateRequirement(
+ this,
+ inheritanceDecl->inferredCapabilityRequirements,
+ inheritanceDecl,
+ node,
+ nodeCaps,
+ refLoc);
+ },
+ [this, inheritanceDecl](DiagnosticCategory category)
+ { _propagateSeeDefinitionOf(this, inheritanceDecl, category); });
+
if (inheritanceDecl->witnessTable)
{
for (auto& kv : inheritanceDecl->witnessTable->m_requirementDictionary)
@@ -14727,29 +14741,116 @@ void SemanticsDeclCapabilityVisitor::visitInheritanceDecl(InheritanceDecl* inher
if (kv.value.getFlavor() != RequirementWitness::Flavor::declRef)
continue;
auto requirementDecl = kv.key;
- auto implDecl = kv.value.getDeclRef();
- if (!implDecl)
+ auto implDeclRef = kv.value.getDeclRef();
+ if (!implDeclRef)
continue;
- if (getModuleDecl(implDecl.getDecl())->languageVersion == SLANG_LANGUAGE_VERSION_LEGACY)
+ if (getModuleDecl(implDeclRef.getDecl())->languageVersion ==
+ SLANG_LANGUAGE_VERSION_LEGACY)
break;
ensureDecl(requirementDecl, DeclCheckState::CapabilityChecked);
- ensureDecl(implDecl.declRefBase, DeclCheckState::CapabilityChecked);
+ ensureDecl(implDeclRef.declRefBase, DeclCheckState::CapabilityChecked);
+
+ // Only if capabilities are opted-into, should we error.
+ auto implDecl = implDeclRef.getDecl();
+ if (!requirementDecl->hasModifier<ExplicitlyDeclaredCapabilityModifier>() &&
+ !implDecl->hasModifier<ExplicitlyDeclaredCapabilityModifier>())
+ continue;
CapabilityAtomSet failedAvailableCapabilityConjunction;
- if (!CapabilitySet::checkCapabilityRequirement(
- requirementDecl->inferredCapabilityRequirements,
- implDecl.getDecl()->inferredCapabilityRequirements,
- failedAvailableCapabilityConjunction))
+ CheckCapabilityRequirementResult checkCapabilityResult;
+ CapabilitySet::checkCapabilityRequirement(
+ CheckCapabilityRequirementOptions::MustHaveEqualAbstractAtoms,
+ requirementDecl->inferredCapabilityRequirements,
+ implDecl->inferredCapabilityRequirements,
+ failedAvailableCapabilityConjunction,
+ checkCapabilityResult);
+
+ if (checkCapabilityResult ==
+ CheckCapabilityRequirementResult::AvailableIsNotASuperSetToRequired)
{
diagnoseUndeclaredCapability(
- implDecl.getDecl(),
+ implDecl,
Diagnostics::useOfUndeclaredCapabilityOfInterfaceRequirement,
+ failedAvailableCapabilityConjunction,
+ false);
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ requirementDecl,
+ Diagnostics::seeDeclarationOf,
+ requirementDecl);
+ }
+ else if (
+ checkCapabilityResult ==
+ CheckCapabilityRequirementResult::RequiredIsMissingAbstractAtoms)
+ {
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ implDecl,
+ Diagnostics::requirmentHasSubsetOfAbstractAtomsToImplementation,
+ implDecl,
failedAvailableCapabilityConjunction);
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ requirementDecl,
+ Diagnostics::seeDeclarationOf,
+ requirementDecl);
}
}
}
+
+ // validate that super-type is a super set of capabilities
+ CapabilityAtomSet failedAvailableCapabilityConjunction;
+ CheckCapabilityRequirementResult checkCapabilityResult;
+ CapabilitySet::checkCapabilityRequirement(
+ CheckCapabilityRequirementOptions::MustHaveEqualAbstractAtoms,
+ inheritanceDecl->inferredCapabilityRequirements,
+ inheritanceParentDecl->inferredCapabilityRequirements,
+ failedAvailableCapabilityConjunction,
+ checkCapabilityResult);
+
+ if (checkCapabilityResult ==
+ CheckCapabilityRequirementResult::AvailableIsNotASuperSetToRequired)
+ {
+ diagnoseUndeclaredCapability(
+ inheritanceParentDecl,
+ Diagnostics::useOfUndeclaredCapabilityOfInheritanceDecl,
+ failedAvailableCapabilityConjunction,
+ false);
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ inheritanceDecl->base,
+ Diagnostics::seeDeclarationOf,
+ inheritanceDecl->base);
+ }
+ else if (
+ checkCapabilityResult == CheckCapabilityRequirementResult::RequiredIsMissingAbstractAtoms)
+ {
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ inheritanceParentDecl,
+ Diagnostics::subTypeHasSubsetOfAbstractAtomsToSuperType,
+ inheritanceParentDecl,
+ failedAvailableCapabilityConjunction);
+ maybeDiagnose(
+ getSink(),
+ getOptionSet(),
+ DiagnosticCategory::Capability,
+ inheritanceDecl->base,
+ Diagnostics::seeDeclarationOf,
+ inheritanceDecl->base);
+ }
}
DeclVisibility getDeclVisibility(Decl* decl)
@@ -15044,13 +15145,11 @@ void diagnoseCapabilityProvenance(
void SemanticsDeclCapabilityVisitor::diagnoseUndeclaredCapability(
Decl* decl,
const DiagnosticInfo& diagnosticInfo,
- const CapabilityAtomSet& failedAtomsInsideAvailableSet)
+ const CapabilityAtomSet& failedAtomsInsideAvailableSet,
+ bool printProvenance)
{
if (decl->inferredCapabilityRequirements.isEmpty())
return;
- if (failedAtomsInsideAvailableSet.isEmpty() ||
- failedAtomsInsideAvailableSet.contains((UInt)CapabilityAtom::Invalid))
- return;
// There are two causes for why type checking failed on failedAvailableSet.
// The first scenario is that failedAvailableSet defines a set of capabilities on a
@@ -15075,7 +15174,7 @@ void SemanticsDeclCapabilityVisitor::diagnoseUndeclaredCapability(
getSink(),
this->getOptionSet(),
DiagnosticCategory::Capability,
- decl->loc,
+ decl,
Diagnostics::declHasDependenciesNotCompatibleOnTarget,
decl,
outFailedAtom);
@@ -15090,16 +15189,19 @@ void SemanticsDeclCapabilityVisitor::diagnoseUndeclaredCapability(
getAtomSetOfTargets(),
failedAtomSet);
- HashSet<Decl*> printedDecls;
- for (auto atom : targetsNotUsedSet)
+ if (printProvenance)
{
- CapabilityAtom formattedAtom = asAtom(atom);
- diagnoseCapabilityProvenance(
- this->getOptionSet(),
- getSink(),
- decl,
- formattedAtom,
- printedDecls);
+ HashSet<Decl*> printedDecls;
+ for (auto atom : targetsNotUsedSet)
+ {
+ CapabilityAtom formattedAtom = asAtom(atom);
+ diagnoseCapabilityProvenance(
+ this->getOptionSet(),
+ getSink(),
+ decl,
+ formattedAtom,
+ printedDecls);
+ }
}
return;
}
@@ -15130,7 +15232,7 @@ void SemanticsDeclCapabilityVisitor::diagnoseUndeclaredCapability(
getSink(),
this->getOptionSet(),
DiagnosticCategory::Capability,
- decl->loc,
+ decl,
Diagnostics::declHasDependenciesNotCompatibleOnStage,
decl,
formattedAtom);
@@ -15141,18 +15243,21 @@ void SemanticsDeclCapabilityVisitor::diagnoseUndeclaredCapability(
getSink(),
this->getOptionSet(),
DiagnosticCategory::Capability,
- decl->loc,
+ decl,
diagnosticInfo,
decl,
formattedAtom);
}
- // Print provenances.
- diagnoseCapabilityProvenance(
- this->getOptionSet(),
- getSink(),
- decl,
- formattedAtom,
- printedDecls);
+ if (printProvenance)
+ {
+ // Print provenances.
+ diagnoseCapabilityProvenance(
+ this->getOptionSet(),
+ getSink(),
+ decl,
+ formattedAtom,
+ printedDecls);
+ }
}
}
diff --git a/source/slang/slang-check-stmt.cpp b/source/slang/slang-check-stmt.cpp
index 8c9e24d48..4c45db1d7 100644
--- a/source/slang/slang-check-stmt.cpp
+++ b/source/slang/slang-check-stmt.cpp
@@ -442,14 +442,14 @@ void SemanticsStmtVisitor::visitTargetSwitchStmt(TargetSwitchStmt* stmt)
bool isStage = isStageAtom((CapabilityName)caseStmt->capability, canonicalStage);
if (as<StageSwitchStmt>(stmt))
{
- if (!isStage && caseStmt->capability != 0)
+ if (!isStage && caseStmt->capability != (int32_t)CapabilityName::Invalid)
{
getSink()->diagnose(
caseStmt->capabilityToken.loc,
Diagnostics::unknownStageName,
caseStmt->capabilityToken);
}
- caseStmt->capability = (int)canonicalStage;
+ caseStmt->capability = (int32_t)canonicalStage;
}
else
{
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 8ec910f15..8d20483a4 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -1157,24 +1157,30 @@ DIAGNOSTIC(
missingCapabilityRequirementOnPublicDecl,
"public symbol '$0' is missing capability requirement declaration, the symbol is assumed to "
"require inferred capabilities '$1'.")
-DIAGNOSTIC(36104, Error, useOfUndeclaredCapability, "'$0' uses undeclared capability '$1'.")
+DIAGNOSTIC(36104, Error, useOfUndeclaredCapability, "'$0' uses undeclared capability '$1'")
DIAGNOSTIC(
36104,
Error,
useOfUndeclaredCapabilityOfInterfaceRequirement,
- "'$0' uses capability '$1' that is missing from the interface requirement.")
+ "'$0' uses capability '$1' that is incompatable with the interface requirement")
+DIAGNOSTIC(
+ 36104,
+ Error,
+ useOfUndeclaredCapabilityOfInheritanceDecl,
+ "'$0' uses capability '$1' that is incompatable with the supertype")
DIAGNOSTIC(36105, Error, unknownCapability, "unknown capability name '$0'.")
DIAGNOSTIC(36106, Error, expectCapability, "expect a capability name.")
DIAGNOSTIC(
36107,
Error,
entryPointUsesUnavailableCapability,
- "entrypoint '$0' uses features that are not available in '$2' stage for '$1' target.")
+ "entrypoint '$0' uses features that are not available in '$2' stage for '$1' compilation "
+ "target.")
DIAGNOSTIC(
36108,
Error,
declHasDependenciesNotCompatibleOnTarget,
- "'$0' has dependencies that are not compatible on the required target '$1'.")
+ "'$0' has dependencies that are not compatible on the required compilation target '$1'.")
DIAGNOSTIC(36109, Error, invalidTargetSwitchCase, "'$0' cannot be used as a target_switch case.")
DIAGNOSTIC(
36110,
@@ -1211,7 +1217,18 @@ DIAGNOSTIC(
36117,
Error,
declHasDependenciesNotCompatibleOnStage,
- "'$0' uses features that are not available in '$1' stage.")
+ "'$0' requires support for stage '$1', but stage is unsupported.")
+DIAGNOSTIC(
+ 36118,
+ Error,
+ subTypeHasSubsetOfAbstractAtomsToSuperType,
+ "subtype '$0' must have the same target/stage support as the supertype; '$0' is missing '$1'")
+DIAGNOSTIC(
+ 36118,
+ Error,
+ requirmentHasSubsetOfAbstractAtomsToImplementation,
+ "requirement '$0' must have the same target/stage support as the implementation; '$0' is "
+ "missing '$1'")
// Attributes
DIAGNOSTIC(31000, Warning, unknownAttributeName, "unknown attribute '$0'")
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index 526e2f952..52d24ff7d 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -7186,7 +7186,7 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor>
if (!builder->getBlock()->getTerminator())
builder->emitBranch(breakLabel);
}
- if (targetCase->capability == 0)
+ if (targetCase->capability == (int32_t)CapabilityName::Invalid)
{
info.defaultLabel = caseBlock;
}
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index 7e9740b53..1302975df 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -5754,7 +5754,7 @@ static Stmt* parseTargetSwitchStmtImpl(Parser* parser, TargetSwitchStmt* stmt)
Diagnostics::unknownTargetName,
caseName.getContent());
}
- targetCase->capability = int32_t(cap);
+ targetCase->capability = (int32_t)cap;
targetCase->capabilityToken = caseName;
targetCase->loc = caseName.loc;
targetCase->body = bodyStmt;
@@ -8743,7 +8743,7 @@ Expr* Parser::ParseLeafExpression()
/// Parse an argument to an application of a generic
static Expr* _parseGenericArg(Parser* parser)
{
- // The grammar for generic arguments needs to be a super-set of the
+ // The grammar for generic arguments needs to be a superset of the
// grammar for types and for expressions, because we do not know
// which to expect at each argument position during parsing.
//
diff --git a/source/slang/slang.natvis b/source/slang/slang.natvis
index 38ec85b62..3dd3ef1af 100644
--- a/source/slang/slang.natvis
+++ b/source/slang/slang.natvis
@@ -286,7 +286,8 @@
<ExpandedItem Condition="astNodeType == Slang::ASTNodeType::ContinueStmt">(Slang::ContinueStmt*)&amp;astNodeType</ExpandedItem>
<ExpandedItem Condition="astNodeType == Slang::ASTNodeType::ReturnStmt">(Slang::ReturnStmt*)&amp;astNodeType</ExpandedItem>
<ExpandedItem Condition="astNodeType == Slang::ASTNodeType::ExpressionStmt">(Slang::ExpressionStmt*)&amp;astNodeType</ExpandedItem>
- <Item Name="[Stmt]">(Slang::Stmt*)this,!</Item>
+ <ExpandedItem Condition="astNodeType == Slang::ASTNodeType::TargetSwitchStmt">(Slang::TargetSwitchStmt*)&amp;astNodeType</ExpandedItem>
+ <Item Name="[Stmt]">(Slang::Stmt*)this,!</Item>
</Expand>
</Type>
<Type Name="Slang::Name">