summaryrefslogtreecommitdiffstats
path: root/tests/language-feature
diff options
context:
space:
mode:
authorJulius Ikkala <julius.ikkala@gmail.com>2025-06-28 05:39:24 +0300
committerGitHub <noreply@github.com>2025-06-28 02:39:24 +0000
commit7349dc5cff49cf22c82eb912813e47f30cd7a757 (patch)
tree4d7b3e14f119e7bb48623e52c890b461fd3d9701 /tests/language-feature
parenta13dda4f214274a10d39f37c79622fc3e62da310 (diff)
Minimal optional constraints (#7422)
* Parse optional witness syntax * Allow failing optional constraint * Make `is` work with optional constraint * Allow using optional constraint in checked if statements * Fix tests * Make it work with structs * Fix MSVC build error * Disallow using `as` with optional constraints * Update test to match split is/as errors * Add tests * Fix uninitialized variables in tests * Add tests of incorrect uses & fix related bugs * Mention optional constraints in docs * format code * Fix type unification with NoneWitness * Fix formatting --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> Co-authored-by: Nathan V. Morrical <natemorrical@gmail.com>
Diffstat (limited to 'tests/language-feature')
-rw-r--r--tests/language-feature/generics/where-optional-1.slang11
-rw-r--r--tests/language-feature/generics/where-optional-2.slang63
-rw-r--r--tests/language-feature/generics/where-optional-3.slang95
-rw-r--r--tests/language-feature/generics/where-optional-4.slang15
-rw-r--r--tests/language-feature/generics/where-optional-5.slang17
-rw-r--r--tests/language-feature/interface-as-rhs-error.slang4
6 files changed, 203 insertions, 2 deletions
diff --git a/tests/language-feature/generics/where-optional-1.slang b/tests/language-feature/generics/where-optional-1.slang
new file mode 100644
index 000000000..da4bdaacb
--- /dev/null
+++ b/tests/language-feature/generics/where-optional-1.slang
@@ -0,0 +1,11 @@
+//DIAGNOSTIC_TEST:SIMPLE(filecheck=CHECK):
+interface IThing
+{
+ void thing();
+}
+
+void f<T>(T t) where optional T: IThing
+{
+ // Unchecked optional constraint is an error.
+ t.thing(); // CHECK: error 30403
+}
diff --git a/tests/language-feature/generics/where-optional-2.slang b/tests/language-feature/generics/where-optional-2.slang
new file mode 100644
index 000000000..67679bd8d
--- /dev/null
+++ b/tests/language-feature/generics/where-optional-2.slang
@@ -0,0 +1,63 @@
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -shaderobj
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -vk -shaderobj
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -cpu -shaderobj
+
+//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<int> outputBuffer;
+
+interface IThing
+{
+ [mutating]
+ int thing(int index);
+}
+
+struct MyThing: IThing
+{
+ int val;
+
+ [mutating]
+ int thing(int index)
+ {
+ val++;
+ outputBuffer[index] = val;
+ return val;
+ }
+}
+
+struct NotMyThing
+{
+ int val;
+}
+
+void f<T>(inout T t, int index) where optional T: IThing
+{
+ if (T is IThing)
+ {
+ outputBuffer[index+1] = 2 * t.thing(index);
+ }
+ else
+ {
+ outputBuffer[index] = 0;
+ outputBuffer[index+1] = 0;
+ }
+}
+
+[numthreads(1, 1, 1)]
+void computeMain(int3 dispatchThreadID: SV_DispatchThreadID)
+{
+ MyThing mt = MyThing(0);
+ NotMyThing nt = NotMyThing(1);
+
+ // CHECK: 1
+ // CHECK-NEXT: 2
+ f<MyThing>(mt, 0);
+ // CHECK-NEXT: 0
+ // CHECK-NEXT: 0
+ f<NotMyThing>(nt, 2);
+ // CHECK: 2
+ // CHECK-NEXT: 4
+ f(mt, 4);
+ // CHECK-NEXT: 0
+ // CHECK-NEXT: 0
+ f(nt, 6);
+}
diff --git a/tests/language-feature/generics/where-optional-3.slang b/tests/language-feature/generics/where-optional-3.slang
new file mode 100644
index 000000000..f8b3a4907
--- /dev/null
+++ b/tests/language-feature/generics/where-optional-3.slang
@@ -0,0 +1,95 @@
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -shaderobj
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -vk -shaderobj
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -cpu -shaderobj
+
+//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<int> outputBuffer;
+
+interface IReleaseable
+{
+ [mutating]
+ void release();
+}
+
+struct Container<K, V>
+ where optional K : IReleaseable
+ where optional V : IReleaseable
+{
+ K k[2];
+ V v[2];
+
+ [mutating]
+ void erase(int index)
+ {
+ if (K is IReleaseable)
+ k[index].release();
+ if (V is IReleaseable)
+ v[index].release();
+ }
+}
+
+struct HeavyEntry: IReleaseable
+{
+ int index;
+ int value;
+
+ [mutating]
+ void release()
+ {
+ outputBuffer[index] = value;
+ }
+};
+
+struct LightEntry
+{
+ int value;
+};
+
+[numthreads(1, 1, 1)]
+void computeMain(int3 dispatchThreadID: SV_DispatchThreadID)
+{
+ { // Neither is IReleaseable
+ var c = Container<LightEntry, LightEntry>();
+ c.k[0] = LightEntry(1);
+ c.k[1] = LightEntry(2);
+ c.v[0] = LightEntry(3);
+ c.v[1] = LightEntry(4);
+ c.erase(0);
+ c.erase(1);
+ }
+ { // K is IReleaseable
+ var c = Container<HeavyEntry, LightEntry>();
+ c.k[0] = HeavyEntry(0,1);
+ c.k[1] = HeavyEntry(1,2);
+ c.v[0] = LightEntry(3);
+ c.v[1] = LightEntry(4);
+ // CHECK: 1
+ c.erase(0);
+ // CHECK-NEXT: 2
+ c.erase(1);
+ }
+ { // V is IReleaseable
+ var c = Container<LightEntry, HeavyEntry>();
+ c.k[0] = LightEntry(1);
+ c.k[1] = LightEntry(2);
+ c.v[0] = HeavyEntry(2,3);
+ c.v[1] = HeavyEntry(3,4);
+ // CHECK-NEXT: 3
+ c.erase(0);
+ // CHECK-NEXT: 4
+ c.erase(1);
+ }
+ { // K and V are IReleaseable
+ var c = Container<HeavyEntry, HeavyEntry>();
+ c.k[0] = HeavyEntry(4,5);
+ c.k[1] = HeavyEntry(6,7);
+ c.v[0] = HeavyEntry(5,6);
+ c.v[1] = HeavyEntry(7,8);
+ // CHECK-NEXT: 5
+ // CHECK-NEXT: 6
+ c.erase(0);
+ // CHECK-NEXT: 7
+ // CHECK-NEXT: 8
+ c.erase(1);
+ }
+}
diff --git a/tests/language-feature/generics/where-optional-4.slang b/tests/language-feature/generics/where-optional-4.slang
new file mode 100644
index 000000000..6d72186d9
--- /dev/null
+++ b/tests/language-feature/generics/where-optional-4.slang
@@ -0,0 +1,15 @@
+//DIAGNOSTIC_TEST:SIMPLE(filecheck=CHECK):
+interface IThing
+{
+ void thing();
+}
+
+void g<T>(T t) where T: IThing
+{
+}
+
+void f<T>(T t) where optional T: IThing
+{
+ // Error: cannot upgrade optional to non-optional witness in unchecked context.
+ g<T>(t); // CHECK: error 38029
+}
diff --git a/tests/language-feature/generics/where-optional-5.slang b/tests/language-feature/generics/where-optional-5.slang
new file mode 100644
index 000000000..3ce8041ad
--- /dev/null
+++ b/tests/language-feature/generics/where-optional-5.slang
@@ -0,0 +1,17 @@
+//DIAGNOSTIC_TEST:SIMPLE(filecheck=CHECK):
+interface IThing
+{
+ void thing();
+}
+
+void f<T, U>(T t, U u)
+ where optional T: IThing
+ where optional U: IThing
+{
+ // Error: cannot upgrade optional to non-optional witness in unchecked context.
+ if (U is IThing)
+ {
+ // U being IThing doesn't justify using T as such!
+ t.thing(); // CHECK: error 30403
+ }
+}
diff --git a/tests/language-feature/interface-as-rhs-error.slang b/tests/language-feature/interface-as-rhs-error.slang
index 9ad71afde..7293b5134 100644
--- a/tests/language-feature/interface-as-rhs-error.slang
+++ b/tests/language-feature/interface-as-rhs-error.slang
@@ -21,13 +21,13 @@ struct AnotherType
// These should produce errors - interface types as RHS
bool testIsOperatorWithInterface<T>()
{
- //CHECK: ([[# @LINE+1]]): error 30301: 'is' and 'as' operators do not support interface types as the right-hand side
+ //CHECK: ([[# @LINE+1]]): error 30301: cannot use 'is' operator with an interface type as the right-hand side
return (T is IMyInterface);
}
void testAsOperatorWithInterface<T>(T value)
{
- //CHECK: ([[# @LINE+1]]): error 30301: 'is' and 'as' operators do not support interface types as the right-hand side
+ //CHECK: ([[# @LINE+1]]): error 30302: cannot use 'as' operator with an interface type as the right-hand side
let result = value as IMyInterface;
}