From b859dc9afba0397110c67cd69bb509a100af4c9c Mon Sep 17 00:00:00 2001 From: "Sami Kiminki (NVIDIA)" <235843927+skiminki-nv@users.noreply.github.com> Date: Thu, 19 Feb 2026 21:12:48 +0200 Subject: [PATCH 01/10] Language reference: Generics Write a page about generics. Include all supported syntactic constructs with a comprehensive set of examples. --- docs/language-reference/README.md | 1 + docs/language-reference/generics.md | 691 ++++++++++++++++++++++++ docs/language-reference/types-struct.md | 31 +- 3 files changed, 708 insertions(+), 15 deletions(-) create mode 100644 docs/language-reference/generics.md diff --git a/docs/language-reference/README.md b/docs/language-reference/README.md index 95bd6e599ba..b58c49941dc 100644 --- a/docs/language-reference/README.md +++ b/docs/language-reference/README.md @@ -11,6 +11,7 @@ Contents * [Lexical Structure](lexical-structure.md) * [Preprocessor](preprocessor.md) * [Types](types.md) +* [Generics](generics.md) * [Expressions](expressions.md) * [Statements](statements.md) * [Declarations](declarations.md) diff --git a/docs/language-reference/generics.md b/docs/language-reference/generics.md new file mode 100644 index 00000000000..4d8bdcfd907 --- /dev/null +++ b/docs/language-reference/generics.md @@ -0,0 +1,691 @@ +# Generics + +Generics in Slang enable compile-time parameterization of [structures](types-struct.md), +[interfaces](types-interface.md), [type aliases (TODO)](types-alias.md), [functions and member functions](TODO.md), +[subscript operators](types-struct.md#subscript-op), and +[constructors](types-struct.md#constructor). Parameterization is allowed for types and +`uint`/`int`/`bool`-typed values. In addition, Slang supports [generic structure +extension](types-extension.md#generic-struct) covered in [type extensions](types-extension.md). + +When a generic type or function is instantiated, the compile-time parameters are bound. An instantiated +generic is a concrete type or function, which can be used as any other concrete type or +function. Conceptually, partial parameter binding can be done by defining a generic type alias for a generic +object, but this does not instantiate a generic. + +Slang does not directly support specialization or partial specialization of generics. However, [generic struct +extension](types-extension.md#generic-struct) can be used to extend generic structures much to the same +effect. + + +## Syntax + +Generic [structure](types-struct.md) syntax: +> **`'struct'`** [*`identifier`*] [*`generic-params-decl`*]
+>     [**`':'`** *`bases-clause`*]
+>     (**`'where'`** *`where-clause`*)\*
+> **`'{'`** *`member-list`* **`'}'`** + +Generic [interface](types-interface.md) syntax: +> **`'interface'`** *`identifier`* [*`generic-params-decl`*]
+>     [**`':'`** *`bases-clause`*]
+>     (**`'where'`** *`where-clause`*)\*
+> **`'{'`** *`member-list`* **`'}'`** + +Generic type alias syntax: +> **`'typealias'`** *`identifier`* [*`generic-params-decl`*]
+>     **`'='`** *`simple-type-spec`*
+>     (**`'where'`** *`where-clause`*)\* **`';'`** + +Generic function and member function declaration: (traditional syntax) +> *`simple-type-spec`* *`identifier`* [*`generic-params-decl`*]
+>     **`'('`** *`param-list`* **`')'`**
+>     (**`'where'`** *`where-clause`*)\*
+>     (**`';'`** | **`'{'`** *`body-stmt`*\* **`'}'`**) + +Generic function and member function declaration: (modern syntax) +> **`'func'`** *`identifier`* [*`generic-params-decl`*]
+>     **`'('`** *`param-list`* **`')'`**
+>     [**`'throws'`** *`simple-type-spec`*]
+>     [**`'->'`** *`simple-type-spec`*]
+>     (**`'where'`** *`where-clause`*)\*
+>     (**`';'`** | **`'{'`** *`body-stmt`*\* **`'}'`**) + +Generic [constructor](types-struct.md#constructor) declaration: +> **`'__init'`** [*`generic-params-decl`*] **`'('`** *`param-list`* **`')'`**
+>     (**`'where'`** *`where-clause`*)\*
+>     (**`';'`** | **`'{'`** *`body-stmt`*\* **`'}'`**) + +Generic [subscript operator](types-struct.md#subscript-op) declaration: +> **`'__subscript'`** [*`generic-params-decl`*] **`'('`** *`param-list`* **`')'`**
+>     **`'->'`** *`simple-type-spec`*
+>     (**`'where'`** *`where-clause`*)\*
+>     (**`';'`** | **`'{'`** *`body-stmt`*\* **`'}'`**) + +Generic parameters declaration: +> *`generic-params-decl`* =
+>     **`'<'`** [*`generic-param-decl`* (**`','`** *`generic-param-decl`*)\* ] **`'>'`** +> +> *`generic-param-decl`* =
+>     *`generic-value-param-decl`* |
+>     *`generic-value-param-trad-decl`* |
+>     *`generic-type-param-decl`* |
+>     *`generic-type-param-pack-decl`* +> +> *`generic-value-param-decl`* =
+>     **`'let'`** *`identifier`*
+>     [**`':'`** *`simple-type-spec`*]
+>     [**`'='`** *`init-expr`*]
+> +> *`generic-value-param-trad-decl`* =
+>     *`simple-type-spec`*
+>     *`identifier`*
+>     [**`'='`** *`init-expr`*]
+> +> *`generic-type-param-decl`* =
+>     [**`'typename'`**] *`identifier`*
+>     [**`':'`** *`simple-type-spec`*]
+>     [**`'='`** *`simple-type-spec`*]
+> +> *`generic-type-param-pack-decl`* =
+>     **`'each'`** *`identifier`*
+>     [**`':'`** *`simple-type-spec`*]
+ + +Generic parameter conformance clause: +> *`where-clause`* =
+>     [**`'optional'`**]
+>     *`simple-type-spec`*
+>     (*`generic-type-constraint-decl`*
+>         |*`generic-type-constraint-eq-decl`*
+>         |*`generic-type-constraint-coercion-decl`*)
+> +> *`generic-type-constraint-decl`* =
+>     **`':'`**
+>     *`simple-type-spec`*
+>     (**`','`** *`simple-type-spec`*)\*
+> +> *`generic-type-constraint-eq-decl`* =
+>     **`'=='`**
+>     *`simple-type-spec`*
+> +> *`generic-type-constraint-coercion-decl`* =
+>     **`'('`**
+>     *`simple-type-spec`*
+>     **`')'`**
+>     [**`'implicit'`**]
+ +> 📝 **Remark:** Generic enumeration declarations are also supported by the parser. However, they are not +> currently useful. See GitHub issue [#10078](https://github.com/shader-slang/slang/issues/10078). + +### Parameters + +- *`generic-params-decl`* declares a list of generic parameters. +- *`generic-param-decl`* declares a generic value, type, or type parameter pack. +- *`generic-value-param-decl`* declares a generic value parameter. +- *`generic-value-param-trad-decl`* declares a generic value parameter using traditional syntax. +- *`generic-type-param-decl`* declares a generic type parameter. +- *`generic-type-param-pack-decl`* declares a generic type parameter pack. +- *`where-clause`* is a generic parameter conformance clause. +- *`generic-type-constraint-decl`* is a generic parameter conformance clause, requiring the declared parameter + to be a subtype of one or more constraining type expressions. +- *`generic-type-constraint-eq-decl`* is a generic parameter conformance clause, requiring the declared parameter + to be equal to the constraining type expression. +- *`generic-type-constraint-coercion-decl`* is a generic parameter conformance clause, requiring the declared parameter + to be coercible. This constraint may be used only in [generic structure extensions](types-extension.md#generic-struct). + See GitHub issue [#10087](https://github.com/shader-slang/slang/issues/10087). +- *`identifier`*: see the respective syntax for a description. +- *`bases-clause`*: see the respective syntax for a description. +- *`member-list`*: see the respective syntax for a description. +- *`simple-type-spec`*: see the respective syntax for a description. +- *`param-list`*: see the respective syntax for a description. +- *`body-stmt`*: see the respective syntax for a description. + + +## Description + +A generic parameter declaration list *`generic-params-decl`* adds any number of compile-time parameters to +structures and functions. These parameterized constructs are called *generic structures* and *generic +functions*. + +A generic parameter declaration is one of: + +- Generic value parameter declaration *`generic-value-param-decl`*, which adds a value parameter with an optional + default value. The value type must be one of `bool`, `int`, `uint`. +- Generic type parameter declaration *`generic-type-param-decl`*, which adds a type parameter with optional + type constraint and default type. The keyword `typename` is optional. +- Generic type parameter pack declaration *`generic-type-param-pack-decl`*, which adds a type parameter + pack. A type parameter pack is a variable-length list of types. + +Types may be constrained by: + +- Specifying an inline type constraint in *`generic-type-param-decl`* using the form `TypeParam : + ParentType`. This adds a single conformance requirement such that `TypeParam` must be a subtype of + `ParentType`. +- Specifying one or more `where` clauses (*`where-clause`*). A `where` clause adds a single requirement using + one of the following forms: + - Conformance declaration *`generic-type-constraint-decl`* adds a requirement that the left-hand-side type + expression must conform to the right-hand-side type expression. + - Equivalence declaration *`generic-type-constraint-eq-decl`* adds a requirement that the left-hand-side + type expression must be equal to the right-hand-side type expression. + - Coercion declaration *`generic-type-constraint-coercion-decl`* adds a requirement that the parenthesized + type expression must be convertible to the left-hand-side type expression. + +Conformance and equivalence requirements may be declared as optional. When optional, expression `ParamType is +ParentType` returns `true` when `ParamType` conforms to or equals `ParentType`. When the expression is used in +an `if` statement using the form `if (ParamType is ParentType) { ... }`, then any variable of type `ParamType` may +be used as type `ParentType` in the "then" branch. + +The coercion requirement is usable only in generic structure extensions. + +Value parameters cannot be constrained. + +> 📝 **Remark 1:** In Slang, a conformance requirement `TypeParam : ParentType` means that `TypeParam` must inherit +> from `ParentType`, and `ParentType` must be an interface. + +> 📝 **Remark 2:** Slang also has the `__generic` modifier, which can be used to declare generic parameters as +> an alternative for *`generic-params-decl`*. Using *`generic-params-decl`* is recommended. + + +### Type Parameter Packs {#type-param-packs} + +A type parameter pack is declared using the `each TypeIdentifier` syntax. When a generic construct is instantiated, the +parameter pack is bound with a (possibly empty) sequence of types. + +A type parameter pack is expanded using the `expand`/`each` construct using the following syntax: + +> Expand-expression:
+>     *`expand-expr`* = **`'expand'`** *`expr`* + +> Each-expression:
+>     *`each-expr`* = **`'each'`** *`expr`* + +An each-expression evaluates to a type parameter pack, tuple, or a type-parameter-pack-typed variable. + +There must be at least one each-expression within an expand-expression. An each-expression must always be +enclosed within an expand-expression except in a generic type declaration. If there are multiple +each-expressions within an expand-expression, they must all have an equal number of type parameters. + +An expand-expression evaluates to a comma-separated value sequence whose length is the number of type +parameters of the embedded each-expressions. The sequence elements are the expand expressions with +each-expressions substituted by the Nth element of the evaluated tuple or a type-parameter-pack-typed variable. + +That is, `expand-expr` is substituted with the following sequence: + +```hlsl +expr, // every each-expr is substituted by pack element 0 +expr, // every each-expr is substituted by pack element 1 +expr, // every each-expr is substituted by pack element 2 +... +expr // every each-expr is substituted by pack element N-1 +``` + +In function parameter lists, expand/each constructs must come after all other parameters. There may be +multiple declared expand/each parameters, in which case the type parameter packs must have equal lengths. + + +## Type Checking + +Type checking of generic type parameters is performed based on their type constraints, and it is performed +before instantiation. In general, an operation on a generic type or a generic-typed variable is legal if it is +legal for all possible concrete types conforming to the declared constraints. + +The rules are as follows: +- If a generic type parameter `T` has an equality constraint `T == U`, type `T` is considered to be type `U` + for all intents and purposes. +- If a generic type parameter `T` has a type constraint `T : Parent`, type `T` is considered to be a subtype + of `Parent`. That is, all inherited members of `Parent` are accessible via generic type parameter `T`. +- If a generic type parameter `T` has a type constraint `U(T)`, type `T` may be converted to type `U`. + +Type constraints may be declared also for the associated types. For example, `where T : Parent where T.AssocT +== int` requires that `AssocT` is `int`. Note that `Parent` must declare associated type `AssocT`. (See +[interfaces](types-interface.md) for associated type declarations.) + +No assumptions are made about generic value parameters other than their declared type. + + +> 📝 **Remark:** In contrast to C++ templates, type checking of Slang generics is performed before the +> compile-time parameters are bound. In C++, type checking is performed during template instantiation. + + +## Parameter Binding + +Compile-time parameters for a generic can be bound either explicitly, implicitly, or as a combination of +both. Binding is done at the call site. + +In explicit binding, the generic parameters are listed in angle brackets after the generic type or function +identifier. Explicit binding cannot be used in constructs that do not use a named identifier at call sites +(e.g., operator overloading). + +In implicit binding, generic parameters are inferred. Inference is performed by matching the generic +parameters against the call site parameter types. It is an error if a parameter cannot be inferred from the +call site. + +In case generic parameter inference is ambiguous for a type parameter, the following rules are used +to determine the type: +- In case all inferred types are [fundamental scalar types](types-fundamental.md#scalar) or + [vector types](types-vector-and-matrix.md) of the same length, the element type with the highest promotion rank is + used. The promotion ranks from the lowest to the highest are: `int8_t`, `uint8_t`, `int16_t`, + `uint16_t`, `int32_t`, `uint32_t`, `int64_t`, `uint64_t`, `float`, `double`. + - A fundamental type is promoted to a 1-dimensional vector if necessary. +- In all other cases, an ambiguous type parameter is an error. + +It is an error when inference yields multiple options for a generic value parameter. + +Mixing explicit and implicit parameter binding is allowed. The left-most generic parameters use the provided +explicit parameter bindings and the rest are inferred. + +> 📝 **Remark:** In case the generic parameter inference is ambiguous and `bool` is inferred as a fundamental +> or element type, the behavior is currently undefined. See GitHub issue +> [#10164](https://github.com/shader-slang/slang/issues/10164) for details. + + +## Examples + +### Generic structure with type and value parameters +```hlsl +RWStructuredBuffer outputBuffer; + +struct TestStruct +{ + var arr : T[size]; +} + +[numthreads(10,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + + TestStruct obj = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + outputBuffer[id.x] = obj.arr[id.x]; +} +``` + +### Generic type alias for partial type binding +```hlsl +struct ArrayOfElements +{ + typealias ElementType = T; + ElementType elems[size]; +} + +typealias ArrayOfFiveElements = ArrayOfElements; + +RWStructuredBuffer outputBuffer; + +[numthreads(5,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + ArrayOfFiveElements myArray = + { 1.0, 2.0, 3.0, 4.0, 5.0 }; + + outputBuffer[id.x] = myArray.elems[id.x]; +} +``` + +### Generic function with type parameter +```hlsl +RWStructuredBuffer outputBuffer; + +// Note: IFunc +func performOp(IFunc binaryOp, T a, T b) -> T +{ + return binaryOp(a, b); +} + +func add2(T a, T b) -> T +{ + return a + b; +} + +struct Adder : IFunc +{ + int bias; + + int operator() (int a, int b) + { + return add2(add2(a, b), bias); + } +} + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + Adder addTwoWithBias = { 5 }; + outputBuffer[id.x] = performOp(addTwoWithBias, 234, 456); +} +``` + +### Generic constructor +```hlsl +RWStructuredBuffer outputBuffer; + +struct TestStruct +{ + int val; + + __init(T initval1, T initval2) + { + T tmp = initval1 + initval2; + val = tmp.toInt(); + } +} + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + TestStruct obj = { 123, 345 }; + outputBuffer[id.x] = obj.val; +} +``` + +### Generic subscript +```hlsl +struct TestStruct +{ + var arr : float[10]; + + // declare a 1-parameter subscript operator + __subscript (T i) -> float where T : IInteger + { + get { return arr[i.toInt()]; } + set { arr[i.toInt()] = newValue; } + } +} +``` + +### Type constraint +```hlsl +RWStructuredBuffer outputBuffer; + +func addTwo(T a, T b) -> T where T : IArithmetic +{ + return a + b; +} + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + outputBuffer[id.x] = addTwo(1, 2); + outputBuffer[id.x] += addTwo(1.1, 2.2); +} +``` + +### Optional type constraint +```hlsl +RWStructuredBuffer outputBuffer; + +func addTwoIfInts(T a, T b) -> T + where T : IArithmetic // T must be IArithmetic + where optional T : IInteger // T may also be IInteger +{ + if (T is IInteger) + return a + b; // return sum if T is IInteger + else + return a - a; // return 0 otherwise +} + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + outputBuffer[id.x] = addTwoIfInts(1, 2); + outputBuffer[id.x] += addTwoIfInts(1.1, 2.2); +} +``` + +### Type equality constraint for associated type +```hlsl +RWStructuredBuffer outputBuffer; + +interface IFace +{ + associatedtype PropertyType; + property prop : PropertyType; +} + +struct IntProperty : IFace +{ + typealias PropertyType = int; + PropertyType prop; +} + +struct FloatProperty : IFace +{ + typealias PropertyType = float; + PropertyType prop; +} + +int addTwoInts(T a, T b) where T.PropertyType == int +{ + return a.prop + b.prop; +} + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + IntProperty intObj1 = { 1 }; + IntProperty intObj2 = { 2 }; + + FloatProperty floatObj1 = { 1.0 }; + FloatProperty floatObj2 = { 2.0 }; + + outputBuffer[id.x] = addTwoInts(intObj1, intObj2); + + // FloatProperty does not conform to equivalence requirement + // "T.PropertyType == int". Hence, the following line will + // not compile. + // outputBuffer[id.x] += addTwoInts(floatObj1, floatObj2); +} +``` + +### Type coercion constraint +```hlsl +struct Foo +{ +} + +// adds method to the struct if A is convertible to int +extension Foo where int(A) +{ + int extensionMethod(int x) + { + return x + 42; + } +} +``` + +### Type parameter pack +```hlsl +RWStructuredBuffer outputBuffer; + +void sumHelper(inout int acc, int term) +{ + acc += term; +} + +int sumInts(expand each T terms) where T == int +{ + int acc = 0; + expand sumHelper(acc, each terms); + + // expands to: + // + // sumHelper(acc, term0), + // sumHelper(acc, term1), + // ... + + return acc; +} + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + outputBuffer[id.x] += sumInts(1, 2, 3, 4, 5, 6, 7); +} +``` + +### Multiple function parameter packs +```hlsl +void dotProductHelper(float a, float b, inout float ret) +{ + ret += a * b; +} + +float dotProduct + (expand each T a, expand each T b) + where T == float +{ + float r = 0.0f; + + expand dotProductHelper(each a, each b, r); + return r; +} + +RWStructuredBuffer outputBuffer; + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + float a[3] = { 4, 5, 6 }; + float b[3] = { 2, 1, 0 }; + + outputBuffer[0] = + dotProduct( + a[0], a[1], a[2], + b[0], b[1], b[2]); +} +``` + +### Generic struct extension for generic types +```hlsl +struct GenericStruct +{ +} + +extension GenericStruct where T : IFloat +{ + int isInt() { return 0; } +} + +extension GenericStruct where T : IInteger +{ + int isInt() { return 1; } +} + +RWStructuredBuffer outputBuffer; + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + GenericStruct floatObj = { }; + GenericStruct uintObj = { }; + + outputBuffer[0] = floatObj.isInt(); + outputBuffer[1] = uintObj.isInt(); +} +``` +> 📝 **Remark:** An extension cannot currently be used to override a more generic implementation. +> See GitHub issue [#10146](https://github.com/shader-slang/slang/issues/10146). + +### Explicit and implicit parameter binding + +```hlsl +// Return type Ret is listed first, since it cannot be +// inferred. +func diffSingle + (T a, U b) -> Ret +{ + return Ret(a.toInt64() - b.toInt64()); +} + +RWStructuredBuffer outputBuffer; + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + int8_t a = 3; + int16_t b = 5; + + // Return type Ret is bound explicitly. Parameter + // types T and U can be inferred from function + // arguments + outputBuffer[0] = diffSingle(a, b); +} +``` + +### Implicit parameter binding for type and value +```hlsl +ElementType sumElements + (ElementType arr[N]) +{ + ElementType acc = arr[0]; + + for (uint i = 1; i < N; ++i) + acc = acc + arr[i]; + + return acc; +} + +RWStructuredBuffer outputBuffer; + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + int elements[7] = { 1,2,3,4,5,6,7 }; + int empty[0] = { }; + + // generic parameters ElementType and N are bound implicitly + outputBuffer[0] = sumElements(elements); +} +``` + +### Implicit parameter binding, ambiguous value parameter +```hlsl +uint len(int[N] arr, int[N] arr2) +{ + return N; +} + +RWStructuredBuffer outputBuffer; + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + int arr1[7] = { }; + int arr2[6] = { }; + + // error: generic parameter N cannot be inferred + outputBuffer[0] = len(arr1, arr2); +} +``` + +### Implicit parameter binding, ambiguous type parameter +```hlsl +interface IBase +{ +} + +struct A : IBase +{ +} + +struct B : IBase +{ +} + +void testFunc(T x, T y) +{ +} + +[numthreads(1,1,1)] +void main(uint3 id : SV_DispatchThreadID) +{ + A a = { }; + B b = { }; + + // Explicit parameter type binding must be used, + // since implicit type for T is ambiguous and + // non-fundamental. + testFunc(a, b); +} +``` diff --git a/docs/language-reference/types-struct.md b/docs/language-reference/types-struct.md index bee7e929fc3..a2fe80d37b5 100644 --- a/docs/language-reference/types-struct.md +++ b/docs/language-reference/types-struct.md @@ -6,7 +6,7 @@ Struct *no-body* declaration: > *`struct-decl`* =
>     [*`modifier-list`*]
>     **`'struct'`** [*`identifier`*] [*`generic-params-decl`*]
->         [**`':'`** *`bases-clause`*] [**`'='`** *`type-expr`*] **`';'`** +>         [**`':'`** *`bases-clause`*] [**`'='`** *`simple-type-spec`*] **`';'`** Struct *with-members* declaration: > *`struct-decl`* =
@@ -20,13 +20,13 @@ Struct *link-time extern type* declaration: > *`struct-decl`* =
>     [*`modifier-list`*]
>     **`'extern'`** **`'struct'`** [*`identifier`*] [*`generic-params-decl`*]
->         [**`':'`** *`bases-clause`*] [**`'='`** *`type-expr`*] **`';'`** +>         [**`':'`** *`bases-clause`*] [**`'='`** *`simple-type-spec`*] **`';'`** Struct *link-time export type alias* declaration: > *`struct-decl`* =
>     [*`modifier-list`*]
>     **`'export'`** **`'struct'`** [*`identifier`*] [*`generic-params-decl`*]
->         [**`':'`** *`bases-clause`*] **`'='`** *`type-expr`* **`';'`** +>         [**`':'`** *`bases-clause`*] **`'='`** *`simple-type-spec`* **`';'`** Member list: > *`member-list`* =
@@ -42,10 +42,10 @@ Member list: - *`modifier-list`* is an optional list of modifiers (TODO: link) - *`identifier`* is an optional name of the declared struct type -- *`generic-params-decl`* is an optional generic parameters declaration. See [Generics (TODO)](TODO). +- *`generic-params-decl`* is an optional generic parameters declaration. See [generics](generics.md). - *`bases-clause`* is an optional list of inherited [interfaces](types-interface.md). -- *`type-expr`* is an optional type expression for an alias type. See [Modules (TODO)](TODO). -- *`where-clause`* is an optional generic constraint expression. See [Generics (TODO)](TODO). +- *`simple-type-spec`* is an optional type expression for an alias type. See [Modules (TODO)](TODO). +- *`where-clause`* is an optional generic constraint expression. See [generics](generics.md). - *`member-list`* is a list of struct members. A member is one of: - *`var-decl`* is a member variable declaration. See [Variables (TODO)](TODO) - *`type-decl`* is a nested [type declaration](types.md). @@ -141,10 +141,10 @@ An object is an *instance* of a `struct`. An instance consists of all non-static ### Syntax Declaration without body: (interfaces only) -> **`'__init'`** **`'('`** *`param-list`* **`')'`** (**`'where'`** *`where-clause`*)\* **`';'`** +> **`'__init'`** [*`generic-params-decl`*] **`'('`** *`param-list`* **`')'`** (**`'where'`** *`where-clause`*)\* **`';'`** Declaration with body: -> **`'__init'`** **`'('`** *`param-list`* **`')'`** (**`'where'`** *`where-clause`*)\*
+> **`'__init'`** [*`generic-params-decl`*] **`'('`** *`param-list`* **`')'`** (**`'where'`** *`where-clause`*)\*
>     **`'{'`** *`body-stmt`*\* **`'}'`** @@ -163,7 +163,7 @@ instantiation. `const` data members cannot be initialized by the constructor. -*`where-clause`* is an optional generic constraint expression, discussed in [Generics (TODO)](TODO). +*`where-clause`* is an optional generic constraint expression, discussed in [generics](generics.md). **Example:** @@ -269,10 +269,10 @@ Non-static member functions cannot be invoked without an object. ### Syntax Modern syntax, implicit `get` declaration: (interfaces only) -> **`'property'`** *`identifier`* **`':'`** *`type-expr`* **`';'`** +> **`'property'`** *`identifier`* **`':'`** *`simple-type-spec`* **`';'`** Modern syntax, explicit accessor declarations: -> **`'property'`** *`identifier`* **`':'`** *`type-expr`*
+> **`'property'`** *`identifier`* **`':'`** *`simple-type-spec`*
> **`'{'`** *`accessor-decl`*\* **`'}'`** Traditional syntax, implicit `get` declaration: (interfaces only) @@ -471,10 +471,10 @@ int tmp4 = TestStruct::incrementAndReturnB(); ### Syntax Implicit `get` declaration: (interfaces only) -> **`'__subscript'`** [**`'('`** *`param-list`* **`')'`**]
+> **`'__subscript'`** **`'('`** *`param-list`* **`')'`** **`'->'`** *`simple-type-spec`* **`';'`** Explicit accessor declarations: -> **`'__subscript'`** [**`'('`** *`param-list`* **`')'`**] **`'->'`** *`type-expr`*
+> **`'__subscript'`** **`'('`** *`param-list`* **`')'`** **`'->'`** *`simple-type-spec`*
> **`'{'`** *`accessor-decl`*\* **`'}'`** See [properties](#property) for *`accessor-decl`* syntax. @@ -532,6 +532,7 @@ struct TestStruct } } +[numthreads(1,1,1)] void main(uint3 id : SV_DispatchThreadID) { TestStruct x = { }; @@ -551,10 +552,10 @@ void main(uint3 id : SV_DispatchThreadID) ### Syntax Declaration without body: (interfaces only) -> *`type-expr`* **`'operator'`** **`'(' ')'`** **`'('`** *`param-list`* **`')'`** **`';'`** +> *`simple-type-spec`* **`'operator'`** **`'(' ')'`** **`'('`** *`param-list`* **`')'`** **`';'`** Declaration with body: -> *`type-expr`* **`'operator'`** **`'(' ')'`** **`'('`** *`param-list`* **`')'`**
+> *`simple-type-spec`* **`'operator'`** **`'(' ')'`** **`'('`** *`param-list`* **`')'`**
> **`'{'`** *`body-stmt`*\* **`'}'`** ### Description From 15946db30c0f281f09a34b7b7e17809170b8fa10 Mon Sep 17 00:00:00 2001 From: "Sami Kiminki (NVIDIA)" <235843927+skiminki-nv@users.noreply.github.com> Date: Mon, 23 Feb 2026 22:02:03 +0200 Subject: [PATCH 02/10] Update references --- docs/language-reference/generics.md | 2 +- docs/language-reference/types-array.md | 2 +- docs/language-reference/types-extension.md | 4 ++-- docs/language-reference/types-pointer.md | 2 +- docs/language-reference/types-vector-and-matrix.md | 6 +++--- docs/language-reference/types.md | 8 ++++---- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/language-reference/generics.md b/docs/language-reference/generics.md index 4d8bdcfd907..82a7ed22fa3 100644 --- a/docs/language-reference/generics.md +++ b/docs/language-reference/generics.md @@ -1,7 +1,7 @@ # Generics Generics in Slang enable compile-time parameterization of [structures](types-struct.md), -[interfaces](types-interface.md), [type aliases (TODO)](types-alias.md), [functions and member functions](TODO.md), +[interfaces](types-interface.md), [type aliases](types.md#alias), [functions and member functions](TODO.md), [subscript operators](types-struct.md#subscript-op), and [constructors](types-struct.md#constructor). Parameterization is allowed for types and `uint`/`int`/`bool`-typed values. In addition, Slang supports [generic structure diff --git a/docs/language-reference/types-array.md b/docs/language-reference/types-array.md index cc3f8be17db..dfd84f218a1 100644 --- a/docs/language-reference/types-array.md +++ b/docs/language-reference/types-array.md @@ -78,7 +78,7 @@ Restrictions for unknown-length arrays: > avoid memory copies when possible, the compiler attempts to optimize these as pass by constant references or > pointers when the target supports it. -> 📝 **Remark 5:** 0-length arrays can be used to disable data members in `struct` types. See [Generics (TODO)](TODO) +> 📝 **Remark 5:** 0-length arrays can be used to disable data members in `struct` types. See [Generics](generics.md) > for further information. diff --git a/docs/language-reference/types-extension.md b/docs/language-reference/types-extension.md index 8f3e045cec2..a3150932877 100644 --- a/docs/language-reference/types-extension.md +++ b/docs/language-reference/types-extension.md @@ -18,7 +18,7 @@ - *`type-expr`* is the type to extend. - *`generic-params-decl`* are the generic parameters for a [generic struct extension](#generic-struct). - *`bases-clause`* is an optional list of [interface](types-interface.md) conformance specifications to be added. -- *`where-clause`* is an optional generic constraint expression. See [Generics (TODO)](TODO). +- *`where-clause`* is an optional [generic constraint expression](generics.md). - *`member-list`* is a list of struct members to be added. A member is one of: - *`var-decl`* is a member static variable declaration. See [Variables (TODO)](TODO) - *`type-decl`* is a nested [type declaration](types.md). @@ -174,4 +174,4 @@ extension T } ``` -See [Generics (TODO)](TODO) for further information on generics. +See [Generics](generics.md) for further information on generics. diff --git a/docs/language-reference/types-pointer.md b/docs/language-reference/types-pointer.md index 0ebbeea1b7a..d7c92e44629 100644 --- a/docs/language-reference/types-pointer.md +++ b/docs/language-reference/types-pointer.md @@ -43,7 +43,7 @@ See [type specifier syntax](types.md#syntax) for full type specifier syntax. a [fundamental type](types-fundamental.md), [vector/matrix generic type](types-vector-and-matrix.md), user-defined type such as a named [structure type](types-struct.md), [interface type](types-interface.md), [enumeration type](types-enum.md), type alias, or a type provided by a module. -- *`generic-params-decl`* is a generic parameters declaration. See [Generics (TODO)](TODO). +- *`generic-params-decl`* is a [generic parameters declaration](generics.md). - **`'['`** [*`constant-index-expr`*] **`']'`** is an [array dimension declaration](types-array.md) with an optional constant integral expression specifying the dimension length. - **`'*'`** is a [pointer declaration](types-pointer.md). diff --git a/docs/language-reference/types-vector-and-matrix.md b/docs/language-reference/types-vector-and-matrix.md index eba5574ad5b..a2ab677d77b 100644 --- a/docs/language-reference/types-vector-and-matrix.md +++ b/docs/language-reference/types-vector-and-matrix.md @@ -4,7 +4,7 @@ A `vector` represents a vector of `N` elements of type `T` where: - `T` is a [fundamental scalar type](types-fundamental.md) -- `N` is a [specialization-time constant integer](TODO-Generics.md) in range [1, 4] denoting the number of elements. +- `N` is an [instantiation-time constant integer](generics.md) in range [1, 4] denoting the number of elements. The default values for `T` and `N` are `float` and `4`. This is for backwards compatibility. @@ -116,8 +116,8 @@ The alignment of a vector type is target-defined. The alignment of `vector Type `matrix` represents a `R`×`C` matrix of elements of type `T` where: - `T` is a [fundamental scalar type](types-fundamental.md) -- `R` is a [specialization-time constant integer](TODO-Generics.md) in range [1, 4] denoting the number of rows. -- `C` is a [specialization-time constant integer](TODO-Generics.md) in range [1, 4] denoting the number of columns. +- `R` is an [instantiation-time constant integer](generics.md) in range [1, 4] denoting the number of rows. +- `C` is an [instantiation-time constant integer](generics.md) in range [1, 4] denoting the number of columns. The default values for `T`, `R`, `C` are `float`, `4`, `4`. This is for backwards compatibility. diff --git a/docs/language-reference/types.md b/docs/language-reference/types.md index 0818d2ee551..643b1045516 100644 --- a/docs/language-reference/types.md +++ b/docs/language-reference/types.md @@ -68,14 +68,14 @@ Full type specifier, possibly declaring a new type: a [fundamental type](types-fundamental.md), [vector/matrix generic type](types-vector-and-matrix.md), user-defined type such as a named [structure type](types-struct.md), [interface type](types-interface.md), [enumeration type](types-enum.md), type alias, or a type provided by a module. -- *`generic-params-decl`* is a generic parameters declaration. See [Generics (TODO)](TODO). +- *`generic-params-decl`* is a [generic parameters declaration](generics.md). - **`'['`** [*`constant-index-expr`*] **`']'`** is an [array dimension declaration](types-array.md) with an optional constant integral expression specifying the dimension length. - **`'*'`** is a [pointer declaration](types-pointer.md). - *`param-list`* is a function parameter list. See [function parameter list (TODO)](TODO). - *`struct-decl`* is a [structure](types-struct.md) type declaration, possibly also defining the type. - *`class-decl`* is a [class (TODO)](types-class.md) type declaration, possibly also defining the type. -- *`enum-decl`* is an [enumeration (TODO)](types-enum.md) type declaration, possibly also defining the type. +- *`enum-decl`* is an [enumeration](types-enum.md) type declaration, possibly also defining the type. ### Description @@ -91,7 +91,7 @@ specifiers are used in: - [function return value type (TODO)](TODO) declarations - [structure property](types-struct.md#property) - [structure subscript operator](types-struct.md#subscript-op) -- [generic type parameter declarations (TODO)](TODO) +- [generic type parameter declarations](generics.md) - [typealias](#alias) declarations Declaration of new types is allowed in: @@ -137,7 +137,7 @@ A `typealias` declaration introduces a name for a type. A `typedef` declaration also allows declaring a new type. A generic type alias declaration declares a parameterized alias for a generic type. This is described in -[Generics (TODO)](TODO). +[Generics](generics.md). ## Complete and Incomplete Types {#incomplete} From 2a795e024e51d3b09f77a8e34a68a3b322e0a724 Mon Sep 17 00:00:00 2001 From: "Sami Kiminki (NVIDIA)" <235843927+skiminki-nv@users.noreply.github.com> Date: Tue, 24 Feb 2026 12:39:50 +0200 Subject: [PATCH 03/10] Improve example for implicit parameter binding for type and value --- docs/language-reference/generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/language-reference/generics.md b/docs/language-reference/generics.md index 82a7ed22fa3..58de72866e6 100644 --- a/docs/language-reference/generics.md +++ b/docs/language-reference/generics.md @@ -615,6 +615,7 @@ void main(uint3 id : SV_DispatchThreadID) ### Implicit parameter binding for type and value ```hlsl +// Note: assumes N >= 1 ElementType sumElements (ElementType arr[N]) { @@ -632,7 +633,6 @@ RWStructuredBuffer outputBuffer; void main(uint3 id : SV_DispatchThreadID) { int elements[7] = { 1,2,3,4,5,6,7 }; - int empty[0] = { }; // generic parameters ElementType and N are bound implicitly outputBuffer[0] = sumElements(elements); From eaf3d20f4a1e331b2034f6f8caa9c6fcd800b122 Mon Sep 17 00:00:00 2001 From: "Sami Kiminki (NVIDIA)" <235843927+skiminki-nv@users.noreply.github.com> Date: Tue, 24 Feb 2026 12:56:01 +0200 Subject: [PATCH 04/10] Add a remark about optional conformance --- docs/language-reference/generics.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/language-reference/generics.md b/docs/language-reference/generics.md index 58de72866e6..03904d75707 100644 --- a/docs/language-reference/generics.md +++ b/docs/language-reference/generics.md @@ -185,6 +185,10 @@ Value parameters cannot be constrained. > 📝 **Remark 2:** Slang also has the `__generic` modifier, which can be used to declare generic parameters as > an alternative for *`generic-params-decl`*. Using *`generic-params-decl`* is recommended. +> 📝 **Remark 3:** Optional conformance constraints are currently an experimental feature. See GitHub issues +> [#10078](https://github.com/shader-slang/slang/issues/10078) and +> [#10185](https://github.com/shader-slang/slang/issues/10185). + ### Type Parameter Packs {#type-param-packs} From abad2ed249f8d3408541208093a95381810e968c Mon Sep 17 00:00:00 2001 From: "Sami Kiminki (NVIDIA)" <235843927+skiminki-nv@users.noreply.github.com> Date: Wed, 25 Feb 2026 18:26:49 +0200 Subject: [PATCH 05/10] Terminology and textual clarifications - distinguish between parameters and arguments (= values for the parameters) - avoid type/subtype and inheritance. Simply use conformance, instead. - improve text based on feedback from Claude --- docs/language-reference/generics.md | 103 ++++++++++----------- docs/language-reference/types-interface.md | 2 +- 2 files changed, 52 insertions(+), 53 deletions(-) diff --git a/docs/language-reference/generics.md b/docs/language-reference/generics.md index 03904d75707..3e0c65e0163 100644 --- a/docs/language-reference/generics.md +++ b/docs/language-reference/generics.md @@ -1,20 +1,19 @@ # Generics -Generics in Slang enable compile-time parameterization of [structures](types-struct.md), +Generics in Slang enable parameterization of [structures](types-struct.md), [interfaces](types-interface.md), [type aliases](types.md#alias), [functions and member functions](TODO.md), [subscript operators](types-struct.md#subscript-op), and [constructors](types-struct.md#constructor). Parameterization is allowed for types and `uint`/`int`/`bool`-typed values. In addition, Slang supports [generic structure extension](types-extension.md#generic-struct) covered in [type extensions](types-extension.md). -When a generic type or function is instantiated, the compile-time parameters are bound. An instantiated -generic is a concrete type or function, which can be used as any other concrete type or +When the generic parameters are bound, a generic type or function is instantiated. An instantiated +generic is a concrete type or function, which can be used like any other concrete type or function. Conceptually, partial parameter binding can be done by defining a generic type alias for a generic object, but this does not instantiate a generic. Slang does not directly support specialization or partial specialization of generics. However, [generic struct -extension](types-extension.md#generic-struct) can be used to extend generic structures much to the same -effect. +extension](types-extension.md#generic-struct) can be used to extend generic structures to similar effect. ## Syntax @@ -36,13 +35,13 @@ Generic type alias syntax: >     **`'='`** *`simple-type-spec`*
>     (**`'where'`** *`where-clause`*)\* **`';'`** -Generic function and member function declaration: (traditional syntax) +Generic function and member function declaration (traditional syntax): > *`simple-type-spec`* *`identifier`* [*`generic-params-decl`*]
>     **`'('`** *`param-list`* **`')'`**
>     (**`'where'`** *`where-clause`*)\*
>     (**`';'`** | **`'{'`** *`body-stmt`*\* **`'}'`**) -Generic function and member function declaration: (modern syntax) +Generic function and member function declaration (modern syntax): > **`'func'`** *`identifier`* [*`generic-params-decl`*]
>     **`'('`** *`param-list`* **`')'`**
>     [**`'throws'`** *`simple-type-spec`*]
@@ -120,14 +119,14 @@ Generic parameter conformance clause: ### Parameters - *`generic-params-decl`* declares a list of generic parameters. -- *`generic-param-decl`* declares a generic value, type, or type parameter pack. +- *`generic-param-decl`* declares a generic value parameter, type parameter, or type parameter pack. - *`generic-value-param-decl`* declares a generic value parameter. - *`generic-value-param-trad-decl`* declares a generic value parameter using traditional syntax. - *`generic-type-param-decl`* declares a generic type parameter. - *`generic-type-param-pack-decl`* declares a generic type parameter pack. - *`where-clause`* is a generic parameter conformance clause. - *`generic-type-constraint-decl`* is a generic parameter conformance clause, requiring the declared parameter - to be a subtype of one or more constraining type expressions. + to conform to one or more constraining type expressions. - *`generic-type-constraint-eq-decl`* is a generic parameter conformance clause, requiring the declared parameter to be equal to the constraining type expression. - *`generic-type-constraint-coercion-decl`* is a generic parameter conformance clause, requiring the declared parameter @@ -143,47 +142,48 @@ Generic parameter conformance clause: ## Description -A generic parameter declaration list *`generic-params-decl`* adds any number of compile-time parameters to -structures and functions. These parameterized constructs are called *generic structures* and *generic -functions*. +A generic parameter declaration list *`generic-params-decl`* adds any number of parameters to structures, +interfaces, type aliases, functions, subscript operators, and constructors. These parameterized constructs are +called *generic structures*, *generic interfaces*, *generic type aliases*, *generic functions*, *generic +subscript operators*, and *generic constructors*. A generic parameter declaration is one of: - Generic value parameter declaration *`generic-value-param-decl`*, which adds a value parameter with an optional default value. The value type must be one of `bool`, `int`, `uint`. -- Generic type parameter declaration *`generic-type-param-decl`*, which adds a type parameter with optional - type constraint and default type. The keyword `typename` is optional. +- Generic type parameter declaration *`generic-type-param-decl`*, which adds a type parameter with an optional + type constraint and an optional default type. The keyword `typename` is optional. - Generic type parameter pack declaration *`generic-type-param-pack-decl`*, which adds a type parameter pack. A type parameter pack is a variable-length list of types. Types may be constrained by: -- Specifying an inline type constraint in *`generic-type-param-decl`* using the form `TypeParam : - ParentType`. This adds a single conformance requirement such that `TypeParam` must be a subtype of - `ParentType`. +- Specifying an inline type constraint in *`generic-type-param-decl`* using the form + `TypeParam : ConstrainingType`. This adds a single conformance requirement such that `TypeParam` must conform to + `ConstrainingType`. - Specifying one or more `where` clauses (*`where-clause`*). A `where` clause adds a single requirement using one of the following forms: - - Conformance declaration *`generic-type-constraint-decl`* adds a requirement that the left-hand-side type + - Conformance constraint declaration *`generic-type-constraint-decl`* adds a requirement that the left-hand-side type expression must conform to the right-hand-side type expression. - - Equivalence declaration *`generic-type-constraint-eq-decl`* adds a requirement that the left-hand-side + - Equivalence constraint declaration *`generic-type-constraint-eq-decl`* adds a requirement that the left-hand-side type expression must be equal to the right-hand-side type expression. - - Coercion declaration *`generic-type-constraint-coercion-decl`* adds a requirement that the parenthesized + - Coercion constraint declaration *`generic-type-constraint-coercion-decl`* adds a requirement that the parenthesized type expression must be convertible to the left-hand-side type expression. -Conformance and equivalence requirements may be declared as optional. When optional, expression `ParamType is -ParentType` returns `true` when `ParamType` conforms to or equals `ParentType`. When the expression is used in -an `if` statement using the form `if (ParamType is ParentType) { ... }`, then any variable of type `ParamType` may -be used as type `ParentType` in the "then" branch. +Conformance and equivalence constraints may be declared as optional. When optional, the expression `ParamType is +ConstrainingType` returns `true` when `ParamType` conforms to or equals `ConstrainingType`. When the expression is used in +an `if` statement using the form `if (ParamType is ConstrainingType) { ... }`, then any variable of type `ParamType` may +be used as type `ConstrainingType` in the "then" branch. The coercion requirement is usable only in generic structure extensions. Value parameters cannot be constrained. -> 📝 **Remark 1:** In Slang, a conformance requirement `TypeParam : ParentType` means that `TypeParam` must inherit -> from `ParentType`, and `ParentType` must be an interface. +> 📝 **Remark 1:** In Slang, a conformance requirement `TypeParam : ConstrainingType` means that `TypeParam` must +> have `ConstrainingType` as a base (either directly or transitively), and `ConstrainingType` must be an interface. > 📝 **Remark 2:** Slang also has the `__generic` modifier, which can be used to declare generic parameters as -> an alternative for *`generic-params-decl`*. Using *`generic-params-decl`* is recommended. +> an alternative to *`generic-params-decl`*. Using *`generic-params-decl`* is recommended. > 📝 **Remark 3:** Optional conformance constraints are currently an experimental feature. See GitHub issues > [#10078](https://github.com/shader-slang/slang/issues/10078) and @@ -192,10 +192,10 @@ Value parameters cannot be constrained. ### Type Parameter Packs {#type-param-packs} -A type parameter pack is declared using the `each TypeIdentifier` syntax. When a generic construct is instantiated, the -parameter pack is bound with a (possibly empty) sequence of types. +A type parameter pack is declared using the `each TypeIdentifier` syntax. When a generic construct is +instantiated, a (possibly empty) sequence of type arguments is bound to the parameter pack. -A type parameter pack is expanded using the `expand`/`each` construct using the following syntax: +A type parameter pack is expanded using the `expand`/`each` construct with the following syntax: > Expand-expression:
>     *`expand-expr`* = **`'expand'`** *`expr`* @@ -203,15 +203,15 @@ A type parameter pack is expanded using the `expand`/`each` construct using the > Each-expression:
>     *`each-expr`* = **`'each'`** *`expr`* -An each-expression evaluates to a type parameter pack, tuple, or a type-parameter-pack-typed variable. +An each-expression evaluates to a type parameter pack, tuple, or type-parameter-pack-typed variable. There must be at least one each-expression within an expand-expression. An each-expression must always be enclosed within an expand-expression except in a generic type declaration. If there are multiple each-expressions within an expand-expression, they must all have an equal number of type parameters. An expand-expression evaluates to a comma-separated value sequence whose length is the number of type -parameters of the embedded each-expressions. The sequence elements are the expand expressions with -each-expressions substituted by the Nth element of the evaluated tuple or a type-parameter-pack-typed variable. +parameters of the embedded each-expressions. Each element of the sequence is the expand expression with every +embedded each-expression replaced by the Nth element of its corresponding pack. That is, `expand-expr` is substituted with the following sequence: @@ -236,49 +236,48 @@ legal for all possible concrete types conforming to the declared constraints. The rules are as follows: - If a generic type parameter `T` has an equality constraint `T == U`, type `T` is considered to be type `U` for all intents and purposes. -- If a generic type parameter `T` has a type constraint `T : Parent`, type `T` is considered to be a subtype - of `Parent`. That is, all inherited members of `Parent` are accessible via generic type parameter `T`. +- If a generic type parameter `T` has a type conformance constraint `T : U`, type `T` is considered to conform to + `U`. That is, `T` implements all requirements of `U`. - If a generic type parameter `T` has a type constraint `U(T)`, type `T` may be converted to type `U`. -Type constraints may be declared also for the associated types. For example, `where T : Parent where T.AssocT -== int` requires that `AssocT` is `int`. Note that `Parent` must declare associated type `AssocT`. (See +Type constraints may also be declared for associated types. For example, `where T : IFace where T.AssocT +== int` requires that `T.AssocT` is `int`. Note that `IFace` must declare associated type `AssocT`. (See [interfaces](types-interface.md) for associated type declarations.) No assumptions are made about generic value parameters other than their declared type. -> 📝 **Remark:** In contrast to C++ templates, type checking of Slang generics is performed before the -> compile-time parameters are bound. In C++, type checking is performed during template instantiation. +> 📝 **Remark:** In contrast to C++ templates, type checking of Slang generics is performed before +> instantiation. In C++, type checking is performed after template instantiation. ## Parameter Binding -Compile-time parameters for a generic can be bound either explicitly, implicitly, or as a combination of -both. Binding is done at the call site. +Arguments to generic parameters can be bound explicitly, implicitly, or as a combination of both. Binding is +done at the call site. -In explicit binding, the generic parameters are listed in angle brackets after the generic type or function +In explicit binding, the arguments to the generic parameters are listed in angle brackets after the generic type or function identifier. Explicit binding cannot be used in constructs that do not use a named identifier at call sites (e.g., operator overloading). -In implicit binding, generic parameters are inferred. Inference is performed by matching the generic -parameters against the call site parameter types. It is an error if a parameter cannot be inferred from the -call site. +In implicit binding, the arguments to the generic parameters are inferred. Inference is performed by matching the generic +parameters against the call site argument expressions. It is an error if an argument to a generic parameter cannot +be inferred from the call site. -In case generic parameter inference is ambiguous for a type parameter, the following rules are used -to determine the type: -- In case all inferred types are [fundamental scalar types](types-fundamental.md#scalar) or +If inference is ambiguous for a generic type parameter, the following rules are used to determine the type: +- If all inferred types are [fundamental scalar types](types-fundamental.md#scalar) or [vector types](types-vector-and-matrix.md) of the same length, the element type with the highest promotion rank is used. The promotion ranks from the lowest to the highest are: `int8_t`, `uint8_t`, `int16_t`, `uint16_t`, `int32_t`, `uint32_t`, `int64_t`, `uint64_t`, `float`, `double`. - A fundamental type is promoted to a 1-dimensional vector if necessary. -- In all other cases, an ambiguous type parameter is an error. +- In all other cases, an ambiguous generic type argument is an error. -It is an error when inference yields multiple options for a generic value parameter. +It is an error when inference yields multiple options for a generic value argument. -Mixing explicit and implicit parameter binding is allowed. The left-most generic parameters use the provided +Mixing explicit and implicit parameter binding is allowed. The leftmost generic parameters use the provided explicit parameter bindings and the rest are inferred. -> 📝 **Remark:** In case the generic parameter inference is ambiguous and `bool` is inferred as a fundamental +> 📝 **Remark:** If the generic argument inference is ambiguous and `bool` is inferred as a fundamental > or element type, the behavior is currently undefined. See GitHub issue > [#10164](https://github.com/shader-slang/slang/issues/10164) for details. diff --git a/docs/language-reference/types-interface.md b/docs/language-reference/types-interface.md index 388461d4f7c..93083229a7f 100644 --- a/docs/language-reference/types-interface.md +++ b/docs/language-reference/types-interface.md @@ -39,7 +39,7 @@ Associated named type declaration: ## Description -An `interface` specifies a set of member functions that a conforming type must provide. An interface can then +An `interface` specifies a set of members that a conforming type must provide. An interface can then be used in place of a concrete type, allowing for the same code to use different concrete types via the methods defined by the interface. From fb3e8dd4d873dda88d9b7ef403797b462a6c93d2 Mon Sep 17 00:00:00 2001 From: "Sami Kiminki (NVIDIA)" <235843927+skiminki-nv@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:07:11 +0200 Subject: [PATCH 06/10] Some more clarifications --- docs/language-reference/generics.md | 32 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/docs/language-reference/generics.md b/docs/language-reference/generics.md index 3e0c65e0163..2f053dd5a2e 100644 --- a/docs/language-reference/generics.md +++ b/docs/language-reference/generics.md @@ -207,7 +207,8 @@ An each-expression evaluates to a type parameter pack, tuple, or type-parameter- There must be at least one each-expression within an expand-expression. An each-expression must always be enclosed within an expand-expression except in a generic type declaration. If there are multiple -each-expressions within an expand-expression, they must all have an equal number of type parameters. +each-expressions within an expand-expression, the referred parameter packs must all have an equal number of +parameters. An expand-expression evaluates to a comma-separated value sequence whose length is the number of type parameters of the embedded each-expressions. Each element of the sequence is the expand expression with every @@ -229,20 +230,21 @@ multiple declared expand/each parameters, in which case the type parameter packs ## Type Checking -Type checking of generic type parameters is performed based on their type constraints, and it is performed -before instantiation. In general, an operation on a generic type or a generic-typed variable is legal if it is +Type checking of parameterized types is performed based on their type constraints, and it is performed +before instantiation. In general, an operation on a parameterized generic type or a generic-typed variable is legal if it is legal for all possible concrete types conforming to the declared constraints. The rules are as follows: -- If a generic type parameter `T` has an equality constraint `T == U`, type `T` is considered to be type `U` +- If a parameterized type `T` has a type equality constraint `T == U`, type `T` is considered to be type `U` for all intents and purposes. -- If a generic type parameter `T` has a type conformance constraint `T : U`, type `T` is considered to conform to +- If a parameterized type `T` has a type conformance constraint `T : U`, type `T` is considered to conform to `U`. That is, `T` implements all requirements of `U`. -- If a generic type parameter `T` has a type constraint `U(T)`, type `T` may be converted to type `U`. +- If a parameterized type `T` has a type constraint `U(T)`, type `T` may be converted to type `U`. -Type constraints may also be declared for associated types. For example, `where T : IFace where T.AssocT -== int` requires that `T.AssocT` is `int`. Note that `IFace` must declare associated type `AssocT`. (See -[interfaces](types-interface.md) for associated type declarations.) +Type constraints may be declared for generic type parameters and type expressions including generic type +parameters. For example, `where T : IFace where T.AssocT == int` requires that `T.AssocT` is `int`. Note that +`IFace` must declare associated type `AssocT`. (See [interfaces](types-interface.md) for associated type +declarations.) No assumptions are made about generic value parameters other than their declared type. @@ -275,7 +277,7 @@ If inference is ambiguous for a generic type parameter, the following rules are It is an error when inference yields multiple options for a generic value argument. Mixing explicit and implicit parameter binding is allowed. The leftmost generic parameters use the provided -explicit parameter bindings and the rest are inferred. +generic arguments and the rest are inferred. > 📝 **Remark:** If the generic argument inference is ambiguous and `bool` is inferred as a fundamental > or element type, the behavior is currently undefined. See GitHub issue @@ -590,7 +592,7 @@ void main(uint3 id : SV_DispatchThreadID) > 📝 **Remark:** An extension cannot currently be used to override a more generic implementation. > See GitHub issue [#10146](https://github.com/shader-slang/slang/issues/10146). -### Explicit and implicit parameter binding +### Explicit and implicit generic arguments ```hlsl // Return type Ret is listed first, since it cannot be @@ -642,7 +644,7 @@ void main(uint3 id : SV_DispatchThreadID) } ``` -### Implicit parameter binding, ambiguous value parameter +### Implicit parameter binding, ambiguous value argument ```hlsl uint len(int[N] arr, int[N] arr2) { @@ -657,12 +659,12 @@ void main(uint3 id : SV_DispatchThreadID) int arr1[7] = { }; int arr2[6] = { }; - // error: generic parameter N cannot be inferred + // error: generic argument N cannot be inferred outputBuffer[0] = len(arr1, arr2); } ``` -### Implicit parameter binding, ambiguous type parameter +### Implicit parameter binding, ambiguous type argument ```hlsl interface IBase { @@ -687,7 +689,7 @@ void main(uint3 id : SV_DispatchThreadID) B b = { }; // Explicit parameter type binding must be used, - // since implicit type for T is ambiguous and + // since inferred type for T is ambiguous and // non-fundamental. testFunc(a, b); } From cdaf27cc1a8f02ebf738a2bc244eff361633ef6e Mon Sep 17 00:00:00 2001 From: "Sami Kiminki (NVIDIA)" <235843927+skiminki-nv@users.noreply.github.com> Date: Thu, 26 Feb 2026 19:07:16 +0200 Subject: [PATCH 07/10] Address review comments Add also more clarifications based on detailed review by Claude. --- docs/language-reference/generics.md | 74 ++++++++++++++++------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/docs/language-reference/generics.md b/docs/language-reference/generics.md index 2f053dd5a2e..235fb0ba143 100644 --- a/docs/language-reference/generics.md +++ b/docs/language-reference/generics.md @@ -3,17 +3,24 @@ Generics in Slang enable parameterization of [structures](types-struct.md), [interfaces](types-interface.md), [type aliases](types.md#alias), [functions and member functions](TODO.md), [subscript operators](types-struct.md#subscript-op), and -[constructors](types-struct.md#constructor). Parameterization is allowed for types and -`uint`/`int`/`bool`-typed values. In addition, Slang supports [generic structure -extension](types-extension.md#generic-struct) covered in [type extensions](types-extension.md). +[constructors](types-struct.md#constructor). A generic parameter can be a type or `uint`/`int`/`bool`-typed +value. In addition, Slang supports [generic structure extension](types-extension.md#generic-struct), covered +in [type extensions](types-extension.md). -When the generic parameters are bound, a generic type or function is instantiated. An instantiated -generic is a concrete type or function, which can be used like any other concrete type or -function. Conceptually, partial parameter binding can be done by defining a generic type alias for a generic -object, but this does not instantiate a generic. +When the generic parameters are bound, a generic type or function is specialized. A specialized generic is a +concrete type or function, which can be used like any other concrete type or function. Generic parameters are +bound by providing arguments (explicit binding), by inference (implicit binding), or by a combination of both. +Value-typed arguments to the generic parameters must be [link-time constants (TODO)](TODO.md). +Conceptually, partial parameter binding can be done by defining a generic type alias for a generic type or +function, but this does not specialize the generic. -Slang does not directly support specialization or partial specialization of generics. However, [generic struct -extension](types-extension.md#generic-struct) can be used to extend generic structures to similar effect. +> 📝 **Remark 1:** Slang does not support explicit specialization of generics where a Slang program +> would provide a definition for a specific combination of arguments. However, +> [generic structure extension](types-extension.md#generic-struct) can be used to extend generic structures to +> similar effect. + +> 📝 **Remark 2:** Slang does not currently support using interface-typed variables that require dynamic dispatch as +> generic parameters. See GitHub issue [#10263](https://github.com/shader-slang/slang/issues/10263). ## Syntax @@ -90,7 +97,7 @@ Generic parameters declaration: >     [**`':'`** *`simple-type-spec`*]
-Generic parameter conformance clause: +Generic parameter constraint clause: > *`where-clause`* =
>     [**`'optional'`**]
>     *`simple-type-spec`*
@@ -124,13 +131,14 @@ Generic parameter conformance clause: - *`generic-value-param-trad-decl`* declares a generic value parameter using traditional syntax. - *`generic-type-param-decl`* declares a generic type parameter. - *`generic-type-param-pack-decl`* declares a generic type parameter pack. -- *`where-clause`* is a generic parameter conformance clause. -- *`generic-type-constraint-decl`* is a generic parameter conformance clause, requiring the declared parameter - to conform to one or more constraining type expressions. -- *`generic-type-constraint-eq-decl`* is a generic parameter conformance clause, requiring the declared parameter - to be equal to the constraining type expression. -- *`generic-type-constraint-coercion-decl`* is a generic parameter conformance clause, requiring the declared parameter - to be coercible. This constraint may be used only in [generic structure extensions](types-extension.md#generic-struct). +- *`where-clause`* is a generic parameter constraint clause. +- *`generic-type-constraint-decl`* declares a generic type conformance constraint, requiring the left-hand-side + type expression to conform to one or more constraining type expressions. +- *`generic-type-constraint-eq-decl`* declares a generic type equality constraint, requiring the left-hand-side + type expression to be equal to the right-hand-side type expression. +- *`generic-type-constraint-coercion-decl`* declares a generic type coercion constraint, requiring the type + expression in parentheses to be coercible to the type expression outside the parentheses. + This constraint may be used only in [generic structure extensions](types-extension.md#generic-struct). See GitHub issue [#10087](https://github.com/shader-slang/slang/issues/10087). - *`identifier`*: see the respective syntax for a description. - *`bases-clause`*: see the respective syntax for a description. @@ -149,8 +157,8 @@ subscript operators*, and *generic constructors*. A generic parameter declaration is one of: -- Generic value parameter declaration *`generic-value-param-decl`*, which adds a value parameter with an optional - default value. The value type must be one of `bool`, `int`, `uint`. +- Generic value parameter declaration *`generic-value-param-decl`* or *`generic-value-param-trad-decl`*, which + adds a value parameter with an optional default value. The value type must be one of `bool`, `int`, `uint`. - Generic type parameter declaration *`generic-type-param-decl`*, which adds a type parameter with an optional type constraint and an optional default type. The keyword `typename` is optional. - Generic type parameter pack declaration *`generic-type-param-pack-decl`*, which adds a type parameter @@ -164,11 +172,11 @@ Types may be constrained by: - Specifying one or more `where` clauses (*`where-clause`*). A `where` clause adds a single requirement using one of the following forms: - Conformance constraint declaration *`generic-type-constraint-decl`* adds a requirement that the left-hand-side type - expression must conform to the right-hand-side type expression. + expression must conform to the right-hand-side type expressions. - Equivalence constraint declaration *`generic-type-constraint-eq-decl`* adds a requirement that the left-hand-side type expression must be equal to the right-hand-side type expression. - Coercion constraint declaration *`generic-type-constraint-coercion-decl`* adds a requirement that the parenthesized - type expression must be convertible to the left-hand-side type expression. + type expression must be coercible to the left-hand-side type expression. Conformance and equivalence constraints may be declared as optional. When optional, the expression `ParamType is ConstrainingType` returns `true` when `ParamType` conforms to or equals `ConstrainingType`. When the expression is used in @@ -193,7 +201,7 @@ Value parameters cannot be constrained. ### Type Parameter Packs {#type-param-packs} A type parameter pack is declared using the `each TypeIdentifier` syntax. When a generic construct is -instantiated, a (possibly empty) sequence of type arguments is bound to the parameter pack. +specialized, a (possibly empty) sequence of type arguments is bound to the parameter pack. A type parameter pack is expanded using the `expand`/`each` construct with the following syntax: @@ -203,11 +211,12 @@ A type parameter pack is expanded using the `expand`/`each` construct with the f > Each-expression:
>     *`each-expr`* = **`'each'`** *`expr`* -An each-expression evaluates to a type parameter pack, tuple, or type-parameter-pack-typed variable. +An expression in an each-expression evaluates to a type parameter pack, tuple, or variable whose type is a +type parameter pack. There must be at least one each-expression within an expand-expression. An each-expression must always be enclosed within an expand-expression except in a generic type declaration. If there are multiple -each-expressions within an expand-expression, the referred parameter packs must all have an equal number of +each-expressions within an expand-expression, the referenced parameter packs must all have an equal number of parameters. An expand-expression evaluates to a comma-separated value sequence whose length is the number of type @@ -230,8 +239,8 @@ multiple declared expand/each parameters, in which case the type parameter packs ## Type Checking -Type checking of parameterized types is performed based on their type constraints, and it is performed -before instantiation. In general, an operation on a parameterized generic type or a generic-typed variable is legal if it is +Type checking of parameterized types is performed based on their type constraints +before specialization. In general, an operation on a parameterized generic type or a generic-typed variable is legal if it is legal for all possible concrete types conforming to the declared constraints. The rules are as follows: @@ -241,7 +250,7 @@ The rules are as follows: `U`. That is, `T` implements all requirements of `U`. - If a parameterized type `T` has a type constraint `U(T)`, type `T` may be converted to type `U`. -Type constraints may be declared for generic type parameters and type expressions including generic type +Type constraints may be declared for generic type parameters and type expressions that include generic type parameters. For example, `where T : IFace where T.AssocT == int` requires that `T.AssocT` is `int`. Note that `IFace` must declare associated type `AssocT`. (See [interfaces](types-interface.md) for associated type declarations.) @@ -250,7 +259,7 @@ No assumptions are made about generic value parameters other than their declared > 📝 **Remark:** In contrast to C++ templates, type checking of Slang generics is performed before -> instantiation. In C++, type checking is performed after template instantiation. +> specialization. In C++, type checking is performed after template specialization and instantiation. ## Parameter Binding @@ -298,7 +307,6 @@ struct TestStruct [numthreads(10,1,1)] void main(uint3 id : SV_DispatchThreadID) { - TestStruct obj = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -476,7 +484,7 @@ void main(uint3 id : SV_DispatchThreadID) outputBuffer[id.x] = addTwoInts(intObj1, intObj2); - // FloatProperty does not conform to equivalence requirement + // FloatProperty does not satisfy the equivalence requirement // "T.PropertyType == int". Hence, the following line will // not compile. // outputBuffer[id.x] += addTwoInts(floatObj1, floatObj2); @@ -540,10 +548,10 @@ float dotProduct (expand each T a, expand each T b) where T == float { - float r = 0.0f; + float r = 0.0f; - expand dotProductHelper(each a, each b, r); - return r; + expand dotProductHelper(each a, each b, r); + return r; } RWStructuredBuffer outputBuffer; From 4bb070b94ae25d7e867cde4ffe63f565cf809ec7 Mon Sep 17 00:00:00 2001 From: "Sami Kiminki (NVIDIA)" <235843927+skiminki-nv@users.noreply.github.com> Date: Thu, 26 Feb 2026 19:21:53 +0200 Subject: [PATCH 08/10] Small clarification on type parameter packs --- docs/language-reference/generics.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/language-reference/generics.md b/docs/language-reference/generics.md index 235fb0ba143..11127026491 100644 --- a/docs/language-reference/generics.md +++ b/docs/language-reference/generics.md @@ -185,6 +185,8 @@ be used as type `ConstrainingType` in the "then" branch. The coercion requirement is usable only in generic structure extensions. +A constraint on a type parameter pack applies to every type in the pack. + Value parameters cannot be constrained. > 📝 **Remark 1:** In Slang, a conformance requirement `TypeParam : ConstrainingType` means that `TypeParam` must @@ -516,7 +518,8 @@ void sumHelper(inout int acc, int term) acc += term; } -int sumInts(expand each T terms) where T == int +int sumInts(expand each T terms) + where T == int // every 'T' type pack member must be int { int acc = 0; expand sumHelper(acc, each terms); From b346032f98b83b1e11511df081a85330ac7b33ad Mon Sep 17 00:00:00 2001 From: "Sami Kiminki (NVIDIA)" <235843927+skiminki-nv@users.noreply.github.com> Date: Thu, 26 Feb 2026 19:37:29 +0200 Subject: [PATCH 09/10] Update allowed value types (spotted by coderabbitai) The allowed value types include boolean, all integer types, and enumeration types. --- docs/language-reference/generics.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/language-reference/generics.md b/docs/language-reference/generics.md index 11127026491..b5318ed79f3 100644 --- a/docs/language-reference/generics.md +++ b/docs/language-reference/generics.md @@ -3,8 +3,9 @@ Generics in Slang enable parameterization of [structures](types-struct.md), [interfaces](types-interface.md), [type aliases](types.md#alias), [functions and member functions](TODO.md), [subscript operators](types-struct.md#subscript-op), and -[constructors](types-struct.md#constructor). A generic parameter can be a type or `uint`/`int`/`bool`-typed -value. In addition, Slang supports [generic structure extension](types-extension.md#generic-struct), covered +[constructors](types-struct.md#constructor). A generic parameter can be a type, a [Boolean](types-fundamental.md#boolean) +value, an [integer](types-fundamental.md#integer) value, or an [enumeration (TODO)](TODO.md) value. +In addition, Slang supports [generic structure extension](types-extension.md#generic-struct), covered in [type extensions](types-extension.md). When the generic parameters are bound, a generic type or function is specialized. A specialized generic is a @@ -158,7 +159,8 @@ subscript operators*, and *generic constructors*. A generic parameter declaration is one of: - Generic value parameter declaration *`generic-value-param-decl`* or *`generic-value-param-trad-decl`*, which - adds a value parameter with an optional default value. The value type must be one of `bool`, `int`, `uint`. + adds a value parameter with an optional default value. The value type must be a [Boolean](types-fundamental.md#boolean), + an [integer](types-fundamental.md#integer), or an [enumeration (TODO)](TODO.md). - Generic type parameter declaration *`generic-type-param-decl`*, which adds a type parameter with an optional type constraint and an optional default type. The keyword `typename` is optional. - Generic type parameter pack declaration *`generic-type-param-pack-decl`*, which adds a type parameter From cba7be4da98ecba9d5c38e71de5301796db257e2 Mon Sep 17 00:00:00 2001 From: "Sami Kiminki (NVIDIA)" <235843927+skiminki-nv@users.noreply.github.com> Date: Fri, 27 Feb 2026 21:42:22 +0200 Subject: [PATCH 10/10] Generic struct extension --> generic extension --- docs/language-reference/generics.md | 10 +++++----- external/spirv-headers | 2 +- external/spirv-tools | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/language-reference/generics.md b/docs/language-reference/generics.md index b5318ed79f3..efbb4705439 100644 --- a/docs/language-reference/generics.md +++ b/docs/language-reference/generics.md @@ -5,7 +5,7 @@ Generics in Slang enable parameterization of [structures](types-struct.md), [subscript operators](types-struct.md#subscript-op), and [constructors](types-struct.md#constructor). A generic parameter can be a type, a [Boolean](types-fundamental.md#boolean) value, an [integer](types-fundamental.md#integer) value, or an [enumeration (TODO)](TODO.md) value. -In addition, Slang supports [generic structure extension](types-extension.md#generic-struct), covered +In addition, Slang supports [generic extension](types-extension.md#generic-struct), covered in [type extensions](types-extension.md). When the generic parameters are bound, a generic type or function is specialized. A specialized generic is a @@ -17,7 +17,7 @@ function, but this does not specialize the generic. > 📝 **Remark 1:** Slang does not support explicit specialization of generics where a Slang program > would provide a definition for a specific combination of arguments. However, -> [generic structure extension](types-extension.md#generic-struct) can be used to extend generic structures to +> [generic extension](types-extension.md#generic-struct) can be used to extend generic structures to > similar effect. > 📝 **Remark 2:** Slang does not currently support using interface-typed variables that require dynamic dispatch as @@ -139,7 +139,7 @@ Generic parameter constraint clause: type expression to be equal to the right-hand-side type expression. - *`generic-type-constraint-coercion-decl`* declares a generic type coercion constraint, requiring the type expression in parentheses to be coercible to the type expression outside the parentheses. - This constraint may be used only in [generic structure extensions](types-extension.md#generic-struct). + This constraint may be used only in [generic extensions](types-extension.md#generic-struct). See GitHub issue [#10087](https://github.com/shader-slang/slang/issues/10087). - *`identifier`*: see the respective syntax for a description. - *`bases-clause`*: see the respective syntax for a description. @@ -185,7 +185,7 @@ ConstrainingType` returns `true` when `ParamType` conforms to or equals `Constra an `if` statement using the form `if (ParamType is ConstrainingType) { ... }`, then any variable of type `ParamType` may be used as type `ConstrainingType` in the "then" branch. -The coercion requirement is usable only in generic structure extensions. +The coercion requirement is usable only in generic extensions. A constraint on a type parameter pack applies to every type in the pack. @@ -574,7 +574,7 @@ void main(uint3 id : SV_DispatchThreadID) } ``` -### Generic struct extension for generic types +### Generic extension for generic types ```hlsl struct GenericStruct { diff --git a/external/spirv-headers b/external/spirv-headers index 04f10f650d5..f88a2d76684 160000 --- a/external/spirv-headers +++ b/external/spirv-headers @@ -1 +1 @@ -Subproject commit 04f10f650d514df88b76d25e83db360142c7b174 +Subproject commit f88a2d766840fc825af1fc065977953ba1fa4a91 diff --git a/external/spirv-tools b/external/spirv-tools index a8fa7f5032a..b2033ea811c 160000 --- a/external/spirv-tools +++ b/external/spirv-tools @@ -1 +1 @@ -Subproject commit a8fa7f5032a528c06339a58d0bd23541085fcfb2 +Subproject commit b2033ea811c6c0150982b114c3cf4d3139c65fee