From 07f21ee31b5f427edb72d5578f713b3da3f3b96f Mon Sep 17 00:00:00 2001
From: ArielG-NV <159081215+ArielG-NV@users.noreply.github.com>
Date: Fri, 8 Aug 2025 13:19:25 -0700
Subject: 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>
---
docs/command-line-slangc-reference.md | 1 +
docs/user-guide/05-capabilities.md | 193 +++++++++++-
.../user-guide/a3-02-reference-capability-atoms.md | 3 +
docs/user-guide/toc.html | 7 +-
source/slang/glsl.meta.slang | 24 +-
source/slang/hlsl.meta.slang | 85 +++---
source/slang/slang-capabilities.capdef | 12 +-
source/slang/slang-capability.cpp | 131 ++++++--
source/slang/slang-capability.h | 32 +-
source/slang/slang-check-decl.cpp | 335 ++++++++++++++-------
source/slang/slang-check-stmt.cpp | 4 +-
source/slang/slang-diagnostic-defs.h | 27 +-
source/slang/slang-lower-to-ir.cpp | 2 +-
source/slang/slang-parser.cpp | 4 +-
source/slang/slang.natvis | 3 +-
tests/diagnostics/discard-in-rt.slang | 4 +-
.../wave-rotate/wave-rotate-clustered.slang | 4 +-
tests/hlsl-intrinsic/wave-rotate/wave-rotate.slang | 8 +-
.../capability/capability-inheritance-1.slang | 77 +++++
.../capability-interface-requirement-1.slang | 56 ++++
.../language-feature/capability/capability2.slang | 27 +-
.../capability/capabilitySimplification1.slang | 30 +-
.../capability/capabilitySimplification3.slang | 17 --
.../capability/explicit-shader-stage-2.slang | 2 +-
24 files changed, 815 insertions(+), 273 deletions(-)
create mode 100644 tests/language-feature/capability/capability-inheritance-1.slang
create mode 100644 tests/language-feature/capability/capability-interface-requirement-1.slang
delete mode 100644 tests/language-feature/capability/capabilitySimplification3.slang
diff --git a/docs/command-line-slangc-reference.md b/docs/command-line-slangc-reference.md
index 95a13a263..cf858a54c 100644
--- a/docs/command-line-slangc-reference.md
+++ b/docs/command-line-slangc-reference.md
@@ -1360,6 +1360,7 @@ A capability describes an optional feature that a target may or may not support.
* `GL_NV_shader_subgroup_partitioned` : enables the GL_NV_shader_subgroup_partitioned extension
* `GL_NV_shader_texture_footprint` : enables the GL_NV_shader_texture_footprint extension
* `GL_NV_cluster_acceleration_structure` : enables the GL_NV_cluster_acceleration_structure extension
+* `GL_NV_cooperative_vector` : enables the GL_NV_cooperative_vector extension
* `nvapi`
* `raytracing`
* `ser`
diff --git a/docs/user-guide/05-capabilities.md b/docs/user-guide/05-capabilities.md
index b398ba3a5..1c8503899 100644
--- a/docs/user-guide/05-capabilities.md
+++ b/docs/user-guide/05-capabilities.md
@@ -61,10 +61,9 @@ their corresponding group.
If two capability requirements contain different atoms that are conflicting with each other, these two requirements are considered __incompatible__.
For example, requirement `spvShaderClockKHR + fragment` and requirement `spvShaderClockKHR + vertex` are incompatible, because `fragment` conflicts with `vertex`.
-## Requirements in Parent Scope
+## Capabilities Between Parent and Members
-The capability requirement of a decl is always merged with the requirements declared in its parents. If the decl declares requirements for additional compilation targets, they are added
-to the requirement set as a separate disjunction.
+The capability requirement of a member is always merged with the requirements declared in its parent(s). If the member declares requirements for additional compilation targets, they are added to the requirement set as a separate disjunction.
For example, given:
```csharp
[require(glsl)]
@@ -79,7 +78,7 @@ struct MyType
`MyType.method` will have requirement `glsl | hlsl + hlsl_nvapi | spirv`.
The `[require]` attribute can also be used on module declarations, so that the requirement will
-apply to all decls within the module. For example:
+apply to all members within the module. For example:
```csharp
[require(glsl)]
[require(hlsl)]
@@ -92,7 +91,124 @@ public void myFunc()
}
```
-## Inference of Capability Requirements
+## Capabilities Between Subtype and Supertype
+
+For inheritance/implementing-interfaces the story is a bit different.
+We require that the subtype (`Foo1`) have a subset of capabilities to the supertype (`IFoo1`).
+
+For example:
+```csharp
+[require(sm_4_0)]
+interface IFoo1
+{
+}
+[require(sm_6_0)]
+struct Foo1 : IFoo1
+{
+}
+```
+We error here since `Foo1` is not a subset to `IFoo1`. `Foo1` has `sm_6_0`, which includes capabilities `sm_4_0` does not have.
+
+```csharp
+[require(sm_6_0)]
+interface IFoo2
+{
+}
+[require(sm_4_0)]
+interface IFoo1
+{
+}
+[require(sm_4_0)]
+struct Foo1 : IFoo1, IFoo2
+{
+}
+```
+We do not error here since `IFoo2` and `IFoo1` are supersets to `Foo1`.
+
+Additionally, any supertype to subtype relationship must share the same shader stage and shader target support.
+
+```csharp
+// Error, Foo1 is missing `spirv`
+[require(hlsl)]
+[require(spirv)]
+interface IFoo1
+{
+}
+[require(hlsl)]
+struct Foo1 : IFoo1
+{
+}
+
+// Error, IFoo1 is missing `hlsl`
+[require(hlsl)]
+interface IFoo1
+{
+}
+[require(hlsl)]
+[require(spirv)]
+struct Foo1 : IFoo1
+{
+}
+```
+
+## Capabilities Between Requirement and Implementation
+
+We require that all requirement capabilities are supersets of their implementation (only required if capabilities are explicitly annotated).
+
+```csharp
+public interface IAtomicAddable_Pass
+{
+ public static void atomicAdd(RWByteAddressBuffer buf, uint addr, This value);
+}
+public extension int64_t : IAtomicAddable_Pass
+{
+ public static void atomicAdd(RWByteAddressBuffer buf, uint addr, int64_t value) { buf.InterlockedAddI64(addr, value); }
+}
+
+public interface IAtomicAddable_Error
+{
+ [require(glsl, sm_4_0)]
+ public static void atomicAdd(RWByteAddressBuffer buf, uint addr, This value);
+}
+public extension uint : IAtomicAddable_Error
+{
+ // Error: implementation has superset of capabilites, sm_6_0 vs. sm_4_0
+ // Note: sm_6_0 is inferred from `InterlockedAddI64`
+ public static void atomicAdd(RWByteAddressBuffer buf, uint addr, int64_t value) { buf.InterlockedAddI64(addr, value); }
+}
+```
+
+Requirment and implementation must also share the same shader stage and shader target support.
+
+```csharp
+public interface IAtomicAddable_Error
+{
+ [require(glsl)]
+ [require(hlsl)]
+ public static void atomicAdd(RWByteAddressBuffer buf, uint addr, This value);
+}
+public extension uint : IAtomicAddable_Error
+{
+ [require(glsl)] // Error, missing `hlsl`
+ public static void atomicAdd(RWByteAddressBuffer buf, uint addr, int64_t value) { buf.InterlockedAddI64(addr, value); }
+}
+
+public interface IAtomicAddable_Error
+{
+ [require(glsl)]
+ public static void atomicAdd(RWByteAddressBuffer buf, uint addr, This value);
+}
+public extension uint : IAtomicAddable_Error
+{
+ [require(glsl)]
+ [require(hlsl)] // Error, has additional capability `hlsl`
+ public static void atomicAdd(RWByteAddressBuffer buf, uint addr, int64_t value) { buf.InterlockedAddI64(addr, value); }
+}
+```
+
+## Capabilities of Functions
+
+### Inference of Capability Requirements
By default, Slang will infer the capability requirements of a function given its definition, as long as the function has `internal` or `private` visibility. For example, given:
```csharp
@@ -110,7 +226,7 @@ Slang will automatically deduce that `myFunc` has capability
```
Since `discard` statement requires capability `fragment`.
-## Inference on target_switch
+### Inference on target_switch
A `__target_switch` statement will introduce disjunctions in its inferred capability requirement. For example:
```csharp
@@ -126,10 +242,71 @@ void myFunc()
The capability requirement of `myFunc` is `(spirv | hlsl)`, meaning that the function can be called from a context where either `spirv` or `hlsl` capability
is available.
-## Capability Aliases
+### Capability Incompatabilities
+
+The function declaration must be a superset of the capabilities the function body uses **for any shader stage/target the function declaration implicitly/explicitly requires**.
+
+```csharp
+[require(sm_5_0)]
+public void requires_sm_5_0()
+{
+
+}
+[require(sm_4_0)]
+public void logic_sm_5_0_error() // Error, missing `sm_5_0` support
+{
+ requires_sm_5_0();
+}
+
+public void logic_sm_5_0__pass() // Pass, no requirements
+{
+ requires_sm_5_0();
+}
+
+[require(hlsl, vertex)]
+public void logic_vertex()
+{
+
+}
+[require(hlsl, fragment)]
+public void logic_fragment()
+{
+
+}
+[require(hlsl, vertex, fragment)]
+public void logic_stage_pass_1() // Pass, `vertex` and `fragment` supported
+{
+ __stage_switch
+ {
+ case vertex:
+ logic_vertex();
+ case fragment:
+ logic_fragment();
+ }
+}
+
+[require(hlsl, vertex, fragment, mesh, hull, domain)]
+public void logic_many_stages()
+{
+
+}
+[require(hlsl, vertex, fragment)]
+public void logic_stage_pass_2() // Pass, function only requires that the body implements the stages `vertex` & `fragment`, the rest are irelevant
+{
+ logic_many_stages();
+}
+
+[require(hlsl, any_hit)]
+public void logic_stage_fail_1() // Error, function requires `any_hit`, body does not support `any_hit`
+{
+ logic_many_stages();
+}
+```
+
+## Capability Aliases
To make it easy to specify capabilities on different platforms, Slang also defines many aliases that can be used in `[require]` attributes.
-For example, Slang declares:
+For example, Slang declares in `slang-capabilities.capdef`:
```
alias sm_6_6 = _sm_6_6
| glsl_spirv_1_5 + sm_6_5
diff --git a/docs/user-guide/a3-02-reference-capability-atoms.md b/docs/user-guide/a3-02-reference-capability-atoms.md
index d46861d48..ea460da34 100644
--- a/docs/user-guide/a3-02-reference-capability-atoms.md
+++ b/docs/user-guide/a3-02-reference-capability-atoms.md
@@ -810,6 +810,9 @@ Extensions
`spvVulkanMemoryModelKHR`
> Represents the SPIR-V capability for vulkan memory model.
+`GL_NV_cooperative_vector`
+> Represents the GL_NV_cooperative_vector extension.
+
Compound Capabilities
----------------------
*Capabilities to specify capabilities created by other capabilities (`raytracing`, `meshshading`...)*
diff --git a/docs/user-guide/toc.html b/docs/user-guide/toc.html
index f559fec31..b32ad71da 100644
--- a/docs/user-guide/toc.html
+++ b/docs/user-guide/toc.html
@@ -68,9 +68,10 @@
- Capability Atoms and Capability Requirements
- Conflicting Capabilities
-- Requirements in Parent Scope
-- Inference of Capability Requirements
-- Inference on target_switch
+- Capabilities Between Parent and Members
+- Capabilities Between Subtype and Supertype
+- Capabilities Between Requirement and Implementation
+- Capabilities of Functions
- Capability Aliases
- Validation of Capability Requirements
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 subgroupPartitionedInclusiveMulNV(vector value, uvec4
__generic
[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
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public vector subgroupPartitionedInclusiveMinNV(vector value, uvec4 ballot)
{
return WaveMultiPrefixInclusiveMin(value, ballot);
@@ -8489,7 +8489,7 @@ public vector subgroupPartitionedInclusiveMinNV(vector value, uvec4
__generic
[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
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public vector subgroupPartitionedInclusiveMaxNV(vector value, uvec4 ballot)
{
return WaveMultiPrefixInclusiveMax(value, ballot);
@@ -8585,7 +8585,7 @@ public vector subgroupPartitionedExclusiveMulNV(vector value, uvec4
__generic
[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
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public vector subgroupPartitionedExclusiveMinNV(vector value, uvec4 ballot)
{
return WaveMultiPrefixExclusiveMin(value, ballot);
@@ -8601,7 +8601,7 @@ public vector subgroupPartitionedExclusiveMinNV(vector value, uvec4
__generic
[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
[ForceInline]
-[require(cuda_glsl_spirv, subgroup_partitioned)]
+[require(glsl_spirv, subgroup_partitioned)]
public vector subgroupPartitionedExclusiveMaxNV(vector 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(U collection, I index);
/// @category stage_io Stage IO types
__generic
+[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(uint location)
{
return __byteAddressBufferLoad(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(uint location, uint alignment)
{
return __byteAddressBufferLoad(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(uint location)
{
return __byteAddressBufferLoad(this, location, __naturalStrideOf());
@@ -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 asfloat16(vector value)
{
__target_switch
@@ -7414,7 +7417,6 @@ matrix asfloat16(matrix 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 asint16(vector value)
{
__target_switch
@@ -7444,7 +7445,6 @@ vector asint16(vector value)
[__unsafeForceInlineEarly]
[__readNone]
-[require(cuda_hlsl_spirv, shader5_sm_5_0)]
matrix asint16(matrix value)
{
__target_switch
@@ -7455,8 +7455,7 @@ matrix asint16(matrix 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 asfloat16(vector value)
{
__target_switch
@@ -7489,7 +7487,6 @@ vector asfloat16(vector value)
[__unsafeForceInlineEarly]
[__readNone]
-[require(cuda_hlsl_spirv, shader5_sm_5_0)]
matrix asfloat16(matrix value)
{
__target_switch
@@ -10270,7 +10267,7 @@ vector fwidth_coarse(vector x)
__generic
[__readNone]
-[require(glsl_hlsl_spirv, fragmentprocessing)]
+[require(glsl_hlsl_spirv, fragmentprocessing_derivativecontrol)]
matrix fwidth_coarse(matrix x)
{
__target_switch
@@ -10332,7 +10329,7 @@ vector fwidth_fine(vector x)
__generic
[__readNone]
-[require(glsl_hlsl_spirv, fragmentprocessing)]
+[require(glsl_hlsl_spirv, fragmentprocessing_derivativecontrol)]
matrix fwidth_fine(matrix x)
{
__target_switch
@@ -26175,8 +26172,10 @@ CoopVec atan(CoopVec yO
// [ForceInline]
[require(cooperative_vector)]
+[require(cooperative_vector)]
[require(hlsl_coopvec_poc)]
[require(optix_coopvec)]
+[require(GL_ARB_gpu_shader5)]
CoopVec fma(CoopVec a, CoopVec b, CoopVec 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
-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(),
- 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() &&
+ !implDecl->hasModifier())
+ 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 printedDecls;
- for (auto atom : targetsNotUsedSet)
+ if (printProvenance)
{
- CapabilityAtom formattedAtom = asAtom(atom);
- diagnoseCapabilityProvenance(
- this->getOptionSet(),
- getSink(),
- decl,
- formattedAtom,
- printedDecls);
+ HashSet 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(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
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 @@
(Slang::ContinueStmt*)&astNodeType
(Slang::ReturnStmt*)&astNodeType
(Slang::ExpressionStmt*)&astNodeType
- - (Slang::Stmt*)this,!
+ (Slang::TargetSwitchStmt*)&astNodeType
+ - (Slang::Stmt*)this,!
diff --git a/tests/diagnostics/discard-in-rt.slang b/tests/diagnostics/discard-in-rt.slang
index 93c3d1038..4af4430a4 100644
--- a/tests/diagnostics/discard-in-rt.slang
+++ b/tests/diagnostics/discard-in-rt.slang
@@ -1,6 +1,6 @@
//TEST:SIMPLE(filecheck=CHECK): -target spirv
-// CHECK: 'closestHit' uses features that are not available in 'closesthit' stage.
+// CHECK: error 36117:{{.*}}'closestHit'{{.*}}'closesthit'
// CHECK: see using of 'discard'
struct PrimaryRayPayload{}
@@ -13,7 +13,7 @@ void closestHit(
discard;
}
-// CHECK: 'closestHit1' uses features that are not available in 'closesthit' stage
+// CHECK: error 36107:{{.*}}'closestHit1'{{.*}}'closesthit'{{.* }}'spirv'
// CHECK: see using of 'discard'
[shader("closesthit")]
void closestHit1(
diff --git a/tests/hlsl-intrinsic/wave-rotate/wave-rotate-clustered.slang b/tests/hlsl-intrinsic/wave-rotate/wave-rotate-clustered.slang
index a8ca2d66f..81601e9be 100644
--- a/tests/hlsl-intrinsic/wave-rotate/wave-rotate-clustered.slang
+++ b/tests/hlsl-intrinsic/wave-rotate/wave-rotate-clustered.slang
@@ -1,8 +1,8 @@
//TEST_CATEGORY(wave, compute)
//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly
-//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl -Xslang... -capability GL_KHR_shader_subgroup_rotate -X.
+//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl -profile sm_6_0 -Xslang... -capability GL_KHR_shader_subgroup_rotate -X.
//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly -xslang -DUSE_GLSL_SYNTAX -allow-glsl
-//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl -allow-glsl -Xslang... -DUSE_GLSL_SYNTAX -capability GL_KHR_shader_subgroup_rotate -X.
+//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl -profile sm_6_0 -allow-glsl -Xslang... -DUSE_GLSL_SYNTAX -capability GL_KHR_shader_subgroup_rotate -X.
#if defined(USE_GLSL_SYNTAX)
#define __clusteredRotate subgroupClusteredRotate
diff --git a/tests/hlsl-intrinsic/wave-rotate/wave-rotate.slang b/tests/hlsl-intrinsic/wave-rotate/wave-rotate.slang
index 5dc319254..353afbb35 100644
--- a/tests/hlsl-intrinsic/wave-rotate/wave-rotate.slang
+++ b/tests/hlsl-intrinsic/wave-rotate/wave-rotate.slang
@@ -1,11 +1,11 @@
-//TEST_CATEGORY(wave, compute)
-//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly
-//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl -Xslang... -capability GL_KHR_shader_subgroup_rotate -X.
+// TEST_CATEGORY(wave, compute)
+// TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly
+// TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-slang -compute -vk -shaderobj -emit-spirv-via-glsl -profile sm_6_0 -Xslang... -capability GL_KHR_shader_subgroup_rotate -X.
//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-metal -compute -shaderobj -xslang -DMETAL
//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-directly -xslang -DUSE_GLSL_SYNTAX -allow-glsl
-//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -emit-spirv-via-glsl -allow-glsl -Xslang... -DUSE_GLSL_SYNTAX -capability GL_KHR_shader_subgroup_rotate -X.
+//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-slang -compute -vk -shaderobj -emit-spirv-via-glsl -allow-glsl -profile sm_6_0 -Xslang... -DUSE_GLSL_SYNTAX -capability GL_KHR_shader_subgroup_rotate -X.
//TEST:COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-metal -compute -shaderobj -xslang -DMETAL -xslang -DUSE_GLSL_SYNTAX -allow-glsl
diff --git a/tests/language-feature/capability/capability-inheritance-1.slang b/tests/language-feature/capability/capability-inheritance-1.slang
new file mode 100644
index 000000000..b8104b350
--- /dev/null
+++ b/tests/language-feature/capability/capability-inheritance-1.slang
@@ -0,0 +1,77 @@
+//TEST:SIMPLE(filecheck=CHECK): -target hlsl -entry computeMain -stage compute
+
+// Test if InheritanceDecl fail to conform to parent capabilities
+
+// mismatch of target abstract atoms
+[require(hlsl)]
+interface IFoo0
+{
+}
+[require(hlsl)]
+[require(spirv)]
+// CHECK-DAG: ([[# @LINE+1]]): error 36108{{.*}}spirv
+struct Foo0 : IFoo0
+{
+}
+
+// mismatch of target abstract atoms
+[require(hlsl)]
+[require(spirv)]
+interface IFoo1
+{
+}
+[require(hlsl)]
+// CHECK-DAG: ([[# @LINE+1]]): error 36118{{.*}}spirv
+struct Foo1 : IFoo1
+{
+}
+
+// subtype is superset
+[require(sm_4_0)]
+interface IFoo2
+{
+}
+[require(sm_5_0)]
+// CHECK-DAG: ([[# @LINE+1]]): error 36104{{.*}}sm_5_0
+struct Foo2 : IFoo2
+{
+}
+
+// subtype is subset, no error
+[require(sm_5_0)]
+interface IFoo3
+{
+}
+[require(sm_4_0)]
+struct Foo3 : IFoo3
+{
+}
+
+// mismatch of stage abstract atoms
+[require(hlsl, compute)]
+interface IFoo4
+{
+}
+[require(hlsl, fragment)]
+// CHECK-DAG: ([[# @LINE+1]]): error 36118{{.*}}compute
+struct Foo4 : IFoo4
+{
+}
+
+// mismatch of stage abstract atoms
+[require(fragment)]
+[require(vertex)]
+interface IFoo5
+{
+}
+[require(fragment)]
+// CHECK-DAG: ([[# @LINE+1]]): error 36118{{.*}}vertex
+struct Foo5 : IFoo5
+{
+}
+
+[require(hlsl)]
+[numthreads(1,1,1)]
+void computeMain()
+{
+}
diff --git a/tests/language-feature/capability/capability-interface-requirement-1.slang b/tests/language-feature/capability/capability-interface-requirement-1.slang
new file mode 100644
index 000000000..687ea51c9
--- /dev/null
+++ b/tests/language-feature/capability/capability-interface-requirement-1.slang
@@ -0,0 +1,56 @@
+//TEST:SIMPLE(filecheck=CHECK): -target hlsl -entry computeMain -stage compute
+
+// Test for requirment/implementation conformance given mismatch
+
+public interface IAtomicAddable_Error1
+{
+ [require(sm_5_0, glsl)]
+ [require(sm_5_0, hlsl)]
+ public void atomicAdd(RWByteAddressBuffer buf, uint addr, int64_t value);
+}
+public struct AtomicAddable_Error1 : IAtomicAddable_Error1
+{
+ [require(sm_5_0, glsl)]
+ // CHECK: ([[# @LINE+1]]): error 36118: {{.*}}hlsl
+ public void atomicAdd(RWByteAddressBuffer buf, uint addr, int64_t value) { }
+}
+
+// ([[# @LINE-9]]): note: see declaration of 'atomicAdd'
+
+public interface IAtomicAddable_Error2
+{
+ [require(sm_5_0, glsl)]
+ public void atomicAdd(RWByteAddressBuffer buf, uint addr, int64_t value);
+}
+public struct AtomicAddable_Error2 : IAtomicAddable_Error2
+{
+ [require(sm_5_0, glsl)]
+ [require(sm_5_0, hlsl)]
+ // CHECK: ([[# @LINE+1]]): error 36108: {{.*}}hlsl
+ public void atomicAdd(RWByteAddressBuffer buf, uint addr, int64_t value) { }
+}
+
+// ([[# @LINE-9]]): note: see declaration of 'atomicAdd'
+
+// Test that we do not error (should be implicitly inferring capabilities)
+// CHECK-NOT: error
+
+public interface IAtomicAddable3
+{
+ public void atomicAdd(RWByteAddressBuffer buf, uint addr, int value);
+}
+
+public struct AtomicAddable_Error4 : IAtomicAddable3
+{
+ public void atomicAdd(RWByteAddressBuffer buf, uint addr, int value) { buf.InterlockedAdd(addr, value); }
+}
+
+public struct AtomicAddable_Error5 : IAtomicAddable3
+{
+ public void atomicAdd(RWByteAddressBuffer buf, uint addr, int value) { buf.InterlockedAddI64(addr, value); }
+}
+
+[numthreads(1,1,1)]
+void computeMain()
+{
+}
diff --git a/tests/language-feature/capability/capability2.slang b/tests/language-feature/capability/capability2.slang
index 6125c21d3..354b87dd5 100644
--- a/tests/language-feature/capability/capability2.slang
+++ b/tests/language-feature/capability/capability2.slang
@@ -12,6 +12,11 @@ interface IFoo
void method2();
}
+interface IFoo2
+{
+ void method3();
+}
+
[require(spvGroupNonUniformArithmetic)]
void useNonUniformArithmetic()
{}
@@ -39,20 +44,32 @@ struct Impl1 : IFoo
}
}
-struct Impl2 : IFoo
+struct Impl2 : IFoo, IFoo2
{
- // CHECK: error 36104: {{.*}}spvGroupNonUniformArithmetic
+ // error here because explicit requirement is on `method1`
+ // CHECK: error 36104:{{.*}}spvGroupNonUniformArithmetic
void method1()
{
- useRayQueryKHR(); // OK.
- useNonUniformArithmetic(); // error.
+ useRayQueryKHR();
+ useNonUniformArithmetic();
}
+ // error here because capabilities are explicitly tagged on
+ // the requirement parent `IFoo`
// CHECK: error 36104: {{.*}}spvGroupNonUniformArithmetic
void method2()
{
useAtomicFloat16();
- useNonUniformArithmetic(); // error.
+ useNonUniformArithmetic();
+ }
+
+ // do not error here because capabilities are not explicitly tagged
+ // on the requirement parent `IFoo2` or requirment method
+ // CHECK-NOT: error
+ void method3()
+ {
+ useAtomicFloat16();
+ useNonUniformArithmetic();
}
}
diff --git a/tests/language-feature/capability/capabilitySimplification1.slang b/tests/language-feature/capability/capabilitySimplification1.slang
index 440ac1ced..0e6303412 100644
--- a/tests/language-feature/capability/capabilitySimplification1.slang
+++ b/tests/language-feature/capability/capabilitySimplification1.slang
@@ -1,24 +1,13 @@
//TEST:SIMPLE(filecheck=CHECK): -target glsl -entry computeMain -stage compute -profile sm_5_0
//TEST:SIMPLE(filecheck=CHECK_IGNORE_CAPS): -target glsl -emit-spirv-directly -entry computeMain -stage compute -profile sm_5_0 -ignore-capabilities
-
// CHECK_IGNORE_CAPS-NOT: error 36107
-// CHECK: error 36107
-// CHECK-SAME: entrypoint 'computeMain' uses features that are not available in 'compute' stage for 'glsl' target.
-// CHECK: capabilitySimplification1.slang(21): note: see using of 'WaveMultiPrefixCountBits'
-// CHECK-NOT: see using of 'WaveMultiPrefixCountBits'
-// CHECK: {{.*}}.meta.slang({{.*}}): note: see definition of 'WaveMultiPrefixCountBits'
-// CHECK: {{.*}}.meta.slang({{.*}}): note: see declaration of 'require'
-
-void nestedSafeCall()
-{
- AllMemoryBarrier();
-}
-
-void nestedBadCall()
+[numthreads(1, 1, 1)]
+// CHECK: ([[# @LINE+1]]): error 36107: {{.*}}computeMain{{.*}}compute{{.*}}glsl
+void computeMain()
{
- WaveMultiPrefixCountBits(true, 0);
+ nestedCall();
}
void nestedCall()
@@ -27,8 +16,13 @@ void nestedCall()
nestedBadCall();
}
-[numthreads(1,1,1)]
-void computeMain()
+void nestedSafeCall()
{
- nestedCall();
+ AllMemoryBarrier();
}
+
+void nestedBadCall()
+{
+ // CHECK: ([[# @LINE+1]]): note: see using of 'WaveMultiPrefixCountBits'
+ WaveMultiPrefixCountBits(true, 0);
+}
\ No newline at end of file
diff --git a/tests/language-feature/capability/capabilitySimplification3.slang b/tests/language-feature/capability/capabilitySimplification3.slang
deleted file mode 100644
index 150f926a9..000000000
--- a/tests/language-feature/capability/capabilitySimplification3.slang
+++ /dev/null
@@ -1,17 +0,0 @@
-//TEST:SIMPLE(filecheck=CHECK): -target glsl -entry computeMain -stage compute -profile sm_5_0
-//TEST:SIMPLE(filecheck=CHECK_IGNORE_CAPS): -target glsl -emit-spirv-directly -entry computeMain -stage compute -profile sm_5_0 -ignore-capabilities
-
-
-// CHECK_IGNORE_CAPS-NOT: error 36107
-
-// CHECK: error 36107: entrypoint 'computeMain' uses features that are not available in 'compute' stage for 'glsl' target.
-// CHECK: capabilitySimplification3.slang(16): note: see using of 'WaveMultiPrefixCountBits'
-// CHECK-NOT: see using of 'WaveMultiPrefixCountBits'
-// CHECK: {{.*}}.meta.slang({{.*}}): note: see definition of 'WaveMultiPrefixCountBits'
-// CHECK: {{.*}}.meta.slang({{.*}}): note: see declaration of 'require'
-
-[numthreads(1,1,1)]
-void computeMain()
-{
- WaveMultiPrefixCountBits(true, 0);
-}
diff --git a/tests/language-feature/capability/explicit-shader-stage-2.slang b/tests/language-feature/capability/explicit-shader-stage-2.slang
index a01cff7c2..8ef3e158c 100644
--- a/tests/language-feature/capability/explicit-shader-stage-2.slang
+++ b/tests/language-feature/capability/explicit-shader-stage-2.slang
@@ -1,7 +1,7 @@
//TEST:SIMPLE(filecheck=CHECK): -target hlsl -entry main -allow-glsl -profile sm_5_0
//TEST:SIMPLE(filecheck=CHECK_IGNORE_CAPS): -target hlsl -entry main -allow-glsl -profile sm_5_0 -ignore-capabilities
-//CHECK: error 36107: entrypoint 'main' uses features that are not available in 'fragment' stage for 'hlsl' target.
+// CHECK: error 36107: entrypoint 'main' uses features that are not available in 'fragment' stage for 'hlsl'
//CHECK_IGNORE_CAPS-NOT: error 36100
[shader("fragment")]
float4 main()
--
cgit v1.2.3