yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Bruce Mitchenerdocs: Reduce typo count (#5671)c3557978c

master
28.6 KiB770 linesraw

Note: This document is a work in progress. It is both incomplete and, in many cases, inaccurate.

Declarations

Modules

A module consists of one or more source units that are compiled together. The global declarations in those source units comprise the body of the module.

In general, the order of declarations within a source unit does not matter; declarations can refer to other declarations (of types, functions, variables, etc.) later in the same source unit. Declarations (other than import declarations) may freely be defined in any source unit in a module; declarations in one source unit of a module may freely refer to declarations in other source units.

Imports

An import declaration is introduced with the keyword import:

import Shadowing;

An import declaration searches for a module matching the name given in the declaration, and brings the declarations in that module into scope in the current source unit.

Note: an import declaration only applies to the scope of the current source unit, and does not import the chosen module so that it is visible to other source units of the current module.

The name of the module being imported may use a compound name:

import MyApp.Shadowing;

The mechanism used to search for a module is implementation-specific.

Note: The current Slang implementation searches for a module by translating the specified module name into a file path by:

  • Replacing any dot (.) separators in a compound name with path separators (e.g., /)

  • Replacing any underscores (_) in the name with hyphens (-)

  • Appending the extension .slang

The implementation then looks for a file matching this path on any of its configured search paths. If such a file is found it is loaded as a module comprising a single source unit.

The declarations of an imported module become visible to the current module, but they are not made visible to code that later imports the current module.

Note: An experimental feature exists for an "exported" import declaration:

// inside A.slang
__exported import Shadowing;

This example imports the declarations from Shadowing into the current module (module A), and also sets up information so that if other code declares import A then it can see both the declarations in A and those in Shadowing.

Note: Mixing import declarations and traditional preprocessor-based (#include) modularity in a codebase can lead to surprising results.

Some things to be aware of:

  • Preprocessor definitions in your module do not affect the code of modules you import.

  • Preprocessor definitions in a module you import do not affect your code

  • The above caveats also apply to "include guards" and #pragma once, since they operate at the granularity of a source unit (not across modules)

  • If you import two modules, and then both #include the same file, then those two modules may end up with duplicate declarations with the same name.

As a general rule, be wary of preprocessor use inside of code meant to be an importable module.

Variables

Variables are declared using the keywords let and var:

let x = 7;
var y = 9.0;

A let declaration introduces an immutable variable, which may not be assigned to or used as the argument for an in out or out parameter. A var declaration introduces a mutable variable.

An explicit type may be given for a variable by placing it after the variable name and a colon (:):

let x : int = 7;
var y : float = 9.0;

If no type is specified for a variable, then a type will be inferred from the initial-value expression. It is an error to declare a variable that has neither a type specifier or an initial-value expression. It is an error to declare a variable with let without an initial-value expression.

A variable declared with var may be declared without an initial-value expression if it has an explicit type specifier:

var y : float;

In this case the variable is uninitialized at the point of declaration, and must be explicitly initialized by assigning to it. Code that uses the value of an uninitialized variable may produce arbitrary results, or even exhibit undefined behavior depending on the type of the variable. Implementations may issue an error or warning for code that might make use of an uninitialized variable.

Traditional Syntax

Variables may also be declared with traditional C-style syntax:

const int x = 7;
float y = 9.0;

For traditional variable declarations a type must be specified.

Note: Slang does not support an auto type specifier like C++.

Traditional variable declarations are immutable if they are declared with the const modifier, and are otherwise mutable.

Variables at Global Scope

Variables declared at global scope may be either a global constant, a static global variables, or a global shader parameters.

Global Constants

A variable declared at global scope and marked with static and const is a global constant.

A global constant must have an initial-value expression, and that initial-value expression must be a compile-time constant expression.

Static Global Variables

A variable declared at global scope and marked with static (but not with const) is a static global variable.

A static global variable provides storage for each invocation executing an entry point. Assignments to a static global variable from one invocation do not affect the value seen by other invocations.

Note: the semantics of static global variable are similar to a "thread-local" variable in other programming models.

A static global variable may include an initial-value expression; if an initial-value expression is included it is guaranteed to be evaluated and assigned to the variable before any other expression that references the variable is evaluated. There is no guarantee that the initial-value expression for a static global variable is evaluated before entry point execution begins, or even that the initial-value expression is evaluated at all (in cases where the variable might not be referenced at runtime).

Note: the above rules mean that an implementation may perform dead code elimination on static global variables, and may choose between eager and lazy initialization of those variables at its discretion.

Global Shader Parameters

A variable declared at global scope and not marked with static (even if marked with const) is a global shader parameter.

Global shader parameters are used to pass arguments from application code into invocations of an entry point. The mechanisms for parameter passing are specific to each target platform.

Note: Currently only global shader parameters of opaque types or arrays of opaque types are supported.

A global shader parameter may include an initial-value epxression, but such an expression does not affect the semantics of the compiled program.

Note: Initial-value expressions on global shader parameters are only useful to set up "default values" that can be read via reflection information and used by application code.

Variables at Function Scope

Variables declared at function scope (in the body of a function, initializer, subscript accessor, etc.) may be either a function-scope constant, function-scope static variable, or a local variable.

Function-Scope Constants

A variable declared at function scope and marked with both static and const is a function-scope constant. Semantically, a function-scope constant behaves like a global constant except that is name is only visible in the local scope.

Function-Scope Static Variables

A variable declared at function scope and marked with static (but not const) is a function-scope static variable. Semantically, a function-scope static variable behaves like a global static variable except that its name is only visible in the local scope.

The initial-value expression for a function-scope static variable may refer to non-static variables in the body of the function. In these cases initialization of the variable is guaranteed not to occur until at least the first time the function body is evaluated for a given invocation.

Local Variables

A variable declared at function scope and not marked with static (even if marked with const) is a local variable. A local variable has unique storage for each activation of a function by an invocation. When a function is called recursively, each call produces a distinct activation with its own copies of local variables.

Functions

Functions are declared using the func keyword:

func add(x: int, y: float) -> float { return float(x) + y; }

Parameters

The parameters of the function are declared as name: type pairs.

Parameters may be given a default value by including an initial-value-expression clause:

func add(x: int, y: float = 1.0f) { ... }

Parameters may be marked with a direction which affects how data is passed between caller and callee:

func add(x: in out int, y : float) { x += ... }

The available directions are:

  • in (the default) indicates typical pass-by-value (copy-in) semantics. The callee receives a copy of the argument passed by the caller.

  • out indicates copy-out semantics. The callee writes to the parameter and then a copy of that value is assigned to the argument of the caller after the call returns.

  • in out or inout indicates pass-by-value-result (copy-in and copy-out) semantics. The callee receives a copy of the argument passed by the caller, it may manipulate the copy, and then when the call returns the final value is copied back to the argument of the caller.

An implementation may assume that at every call site the arguments for out or in out parameters never alias. Under those assumptions, the out and inout cases may be optimized to use pass-by-reference instead of copy-in and copy-out.

Note: Applications that rely on the precise order in which write-back for out and in out parameters is performed are already on shaky semantic ground.

Body

The body of a function declaration consists of statements enclosed in curly braces {}.

In some cases a function declaration does not include a body, and in these cases the declaration must be terminated with a semicolon (;):

func getCount() -> int;

Note: Slang does not require "forward declaration" of functions, although forward declarations are supported as a compatibility feature.

The only place where a function declaration without a definition should be required is in the body of an interface declaration.

The result type of a function mayb be specified after the parameter list using a result type clause consisting of an arrow (->) followed by a type. If the function result type is void, the result type clause may be elided:

func modify(x: in out int) { x++; }

Traditional Syntax

Functions can also be declared with traditional C-style syntax:

float add(int x, float y) { return float(x) + y; }

void modify(in out int x) { x ++; }

Note: Currently traditional syntax must be used for shader entry point functions, because only the traditional syntax currently supports attaching semantics to parameters.

Entry Points

An entry point is a function that will be used as the starting point of execution for one or more invocations of a shader.

Structure Types

Structure types are declared using the struct keyword:

struct Person
{
    var age : int;
    float height;

    int getAge() { return age; }
    func getHeight() -> float { return this.height; }
    static func getPopulation() -> int { ... }
}

The body of a structure type declaration may include variable, type, function, and initializer declarations.

Fields

Variable declarations in the body of a structure type declaration are also referred to as fields.

A field that is marked static is shared between all instances of the type, and is semantically like a global variable marked static.

A non-static field is also called an instance field.

Methods

Function declarations in the body of a structure type declaration are also referred to as methods.

A method declaration may be marked static. A static method must be invoked on the type itself (e.g., Person.getPopulation()).

A non-static method is also referred to as an instance method. Instance methods must be invoked on an instance of the type (e.g., somePerson.getAge()). The body of an instance method has access to an implicit this parameter which refers to the instance on which the method was invoked.

By default the this parameter of an instance method acts as an immutable variable. An instance method with the [mutating] attribute receives a mutable this parameter, and can only be invoked on a mutable value of the structure type.

Inheritance

A structure type declaration may include an inheritance clause that consists of a colon (:) followed by a comma-separated list of types that the structure type inherits from:

struct Person : IHasAge, IHasName
{ .... }

When a structure type declares that it inherits from an interface, the programmer asserts that the structure type implements the required members of the interface.

Syntax Details

A structure declaration does not need to be terminated with a semicolon:

// A terminating semicolon is allowed
struct Stuff { ... };

// The semicolon is not required
struct Things { ... }

When a structure declarations ends without a semicolon, the closing curly brace (}) must be the last non-comment, non-whitespace token on its line.

For compatibility with C-style code, a structure type declaration may be used as the type specifier in a traditional-style variable declaration:

struct Association
{
    int from;
    int to;
} associations[] =
{
    { 1, 1 },
    { 2, 4 },
    { 3, 9 },
};

If a structure type declaration will be used as part of a variable declaration, then the next token of the variable declaration must appear on the same line as the closing curly brace (}) of the structure type declaration. The whole variable declaration must be terminated with a semicolon (;) as normal.

Enumeration Types

Enumeration type declarations are introduced with the enum keyword:

enum Color
{
    Red,
    Green = 3,
    Blue,
}

Cases

The body of an enumeration type declaration consists of a comma-separated list of case declarations. An optional trailing comma may terminate the lis of cases.

A case declaration consists of the name of the case, along with an optional initial-value expression that specifies the tag value for that case. If the first case declaration in the body elides an initial-value expression, the value 0 is used for the tag value. If any other case declaration elides an initial-value expressions, its tag value is one greater than the tag value of the immediately preceding case declaration.

An enumeration case is referred to as if it were a static member of the enumeration type (e.g., Color.Red).

Inheritance

An enumeration type declaration may include an inheritance clause:

enum Color : uint
{ ... }

The inheritance clause of an enumeration declaration may currently only be used to specify a single type to be used as the tag type of the enumeration type. The tag type of an enumeration must be a built-in scalar integer type. The tag value of each enumeration case will be a value of the tag type.

If no explicit tag type is specified, the type int is used instead.

Note: The current Slang implementation has bugs that prevent explicit tag types from working correctly.

Conversions

A value of an enumeration type can be implicitly converted to a value of its tag type:

int r = Color.Red;

Values of the tag type can be explicitly converted to the enumeration type:

Color red = Color(r);

Type Aliases

A type alias is declared using the typealias keyword:

typealias Height  = int;

A type alias defines a name that will be equivalent to the type to the right of =.

Traditional Syntax

Type aliases can also be declared with traditional C-style syntax:

typedef int Height;

Constant Buffers and Texture Buffers

As a compatibility feature, the cbuffer and tbuffer keywords can be used to introduce variable declarations.

A declaration of the form:

cbuffer Name
{
    F field;
    // ...
}

is equivalent to a declaration of the form:

struct AnonType
{
    F field;
    // ...
}
__transparent ConstantBuffer<AnonType> anonVar;

In this expansion, AnonType and anonVar are fresh names generated for the expansion that cannot collide with any name in user code, and the modifier __transparent makes it so that an unqualified reference to field can implicitly resolve to anonVar.field.

The keyword tbuffer uses an equivalent expansion, but with TextureBuffer<T> used instead of ConstantBuffer<T>.

Interfaces

An interface is declared using the interface keyword:

interface IRandom
{
    uint next();
}

The body of an interface declaration may contain function, initializer, subscript, and associated type declarations. Each declaration in the body of an interface introduces a requirement of the interface. Types that declare conformance to the interface must provide matching implementations of the requirements.

Functions, initializers, and subscripts declared inside an interface must not have bodies; default implementations of interface requirements are not currently supported.

An interface declaration may have an inheritance clause:

interface IBase
{
    int getBase();
}

interface IDerived : IBase
{
    int getDerived();
}

The inheritance clause for an interface must only list other interfaces. If an interface I lists another interface J in its inheritance clause, then J is a base interface of I. In order to conform to I, a type must also conform to J.

Associated Types

An associated type declaration is introduced with associatedtype:

associatedtype Iterator;

An associated type declaration introduces a type into the signature of an interface, without specifying the exact concrete type to use. An associated type is an interface requirement, and different implementations of an interface may provide different types that satisfy the same associated type interface requirement:

interface IContainer
{
    associatedtype Iterator;
    ...
}

struct MyArray : IContainer
{
    typealias Iterator = Int;
    ...
}

struct MyLinkedList : IContainer
{
    struct Iterator { ... }
    ...
}

It is an error to declare an associated type anywhere other than the body of an interface declaration.

An associated type declaration may have an inheritance clause. The inheritance clause of an associated type may only list interfaces; these are the required interfaces for the associated type. A concrete type that is used to satisfy an associated type requirement must conform to all of the required interfaces of the associated type.

Initializers

An initializer declaration is introduced with the __init keyword:

struct MyVector
{
    float x, float y;

    __init(float s)
    {
        x = s;
        y = s;
    }
}

Note: Initializer declarations are a non-finalized and unstable feature, as indicated by the double-underscore (__) prefix on the keyword. Arbitrary changes to the syntax and semantics of initializers may be introduced in future versions of Slang.

An initializer declaration may only appear in the body of an interface or a structure type. An initializer defines a method for initializing an instance of the enclosing type.

Note: A C++ programmer might think of an initializer declaration as similar to a C++ constructor.

An initializer has a parameter list and body just like a function declaration. An initializer must not include a result type clause; the result type of an initializer is always the enclosing type.

An initializer is invoked by calling the enclosing type as if it were a function. E.g., in the example above, the initializer in MyVector can be invoked as MyVector(1.0f).

An initializer has access to an implicit this variable that is the instance being initialized; an initializer must not be marked static. The this variable of an initializer is always mutable; an initializer need not, and must not, be marked [mutating].

Note: Slang currently does not enforce that a type with an initializer can only be initialized using its initializers. It is possible for user code to declare a variable of type MyVector above, and explicitly write to the x and y fields to initialize it. A future version of the language may close up this loophole.

Note: Slang does not provide any equivalent to C++ destructors which run automatically when an instance goes out of scope.

Subscripts

A subscript declaration is introduced with the __subscript keyword:

struct MyVector
{
    ...

    __subscript(int index) -> float
    {
        get { return index == 0 ? x : y; }
    }
}

Note: subscript declarations are a non-finalized and unstable feature, as indicated by the double-underscore (__) prefix on the keyword. Arbitrary changes to the syntax and semantics of subscript declarations may be introduced in future versions of Slang.

A subscript declaration introduces a way for a user-defined type to support subscripting with the [] braces:

MyVector v = ...;
float f = v[0];

A subscript declaration lists one or more parameters inside parentheses, followed by a result type clause starting with ->. The result type clause of a subscript declaration cannot be elided.

The body of a subscript declaration consists of accessor declarations. Currently only get accessor declarations are supported for user code.

A get accessor declaration introduces a getter for the subscript. The body of a getter is a code block like a function body, and must return the appropriate value for a subcript operation. The body of a getter can access the parameters of the enclosing subscript, as a well as an implicit this parameter of the type that encloses the accessor. The this parameter of a getter is immutable; [mutating] getters are not currently supported.

Extensions

An extension declaration is introduced with the extension keyword:

extension MyVector
{
    float getLength() { return sqrt(x*x + y*y); }
    static int getDimensionality() { return 2; }
}

An extension declaration adds behavior to an existing type. In the example above, the MyVector type is extended with an instance method getLength(), and a static method getDimensionality().

An extension declaration names the type being extended after the extension keyword. The body of an extension declaration may include type declarations, functions, initializers, and subscripts.

Note: The body of an extension may not include variable declarations. An extension cannot introduce members that would change the in-memory layout of the type being extended.

The members of an extension are accessed through the type that is being extended. For example, for the above extension of MyVector, the introduced methods are accessed as follows:

MyVector v = ...;

float f = v.getLength();
int n = MyVector.getDimensionality();

An extension declaration need not be placed in the same module as the type being extended; it is possible to extend a type from third-party or standard module code. The members of an extension are only visible inside of modules that import the module declaring the extension; extension members are not automatically visible wherever the type being extended is visible.

An extension declaration may include an inheritance clause:

extension MyVector : IPrintable
{
    ...
}

The inheritance clause of an extension declaration may only include interfaces. When an extension declaration lists an interface in its inheritance clause, it asserts that the extension introduces a new conformance, such that the type being extended now conforms to the given interface. The extension must ensure that the type being extended satisfies all the requirements of the interface. Interface requirements may be satisfied by the members of the extension, members of the original type, or members introduced through other extensions visible at the point where the conformance was declared.

It is an error for overlapping conformances (that is, of the same type to the same interface) to be visible at the same point. This includes cases where two extensions declare the same conformance, as well as those where the original type and an extension both declare the same conformance. The conflicting conformances may come from the same module or difference modules.

In order to avoid problems with conflicting conformances, when a module M introduces a conformance of type T to interface I, one of the following should be true:

  • the type T is declared in module M, or
  • the type I is declared in module M

Any conformance that does not follow these rules (that is, where both T and I are imported into module M) is called a retroactive conformance, and there is no way to guarantee that another module N will not introduce the same conformance. The runtime behavior of programs that include overlapping retroactive conformances is currently undefined.

Currently, extension declarations can only apply to structure types; extensions cannot apply to enumeration types or interfaces.

Generics

Many kinds of declarations can be made generic: structure types, interfaces, extensions, functions, initializers, and subscripts.

A generic declaration introduces a generic parameter list enclosed in angle brackets <>:

T myFunction<T>(T left, T right, bool condition)
{
    return condition ? left : right;
}

Generic Parameters

A generic parameter list can include one or more parameters separated by commas. The allowed forms for generic parameters are:

  • A single identifier like T is used to declare a generic type parameter with no constraints.

  • A clause like T : IFoo is used to introduce a generic type parameter T where the parameter is constrained so that it must conform to the IFoo interface.

  • A clause like let N : int is used to introduce a generic value parameter N, which takes on values of type int.

Note: The syntax for generic value parameters is provisional and subject to possible change in the future.

Generic parameters may declare a default value with =:

T anotherFunction<T = float, let N : int = 4>(vector<T,N> v);

For generic type parameters, the default value is a type to use if no argument is specified. For generic value parameters, the default value is a value of the same type to use if no argument is specified.

Explicit Specialization

A generic is specialized by applying it to generic arguments listed inside angle brackets <>:

anotherFunction<int, 3>

Specialization produces a reference to the declaration with all generic parameters bound to concrete arguments.

When specializing a generic, generic type parameters must be matched with type arguments that conform to the constraints on the parameter, if any. Generic value parameters must be matched with value arguments of the appropriate type, and that are specialization-time constants.

An explicitly specialized function, type, etc. may be used wherever a non-generic function, type, etc. is expected:

int i = anotherFunction<int,3>( int3(99) );

Implicit Specialization

If a generic function/type/etc. is used where a non-generic function/type/etc. is expected, the compiler attempts implicit specialization. Implicit specialization infers generic arguments from the context at the use site, as well as any default values specified for generic parameters.

For example, if a programmer writes:

int i = anotherFunction( int3(99) );

The compiler will infer the generic arguments <int, 3> from the way that anotherFunction was applied to a value of type int3.

Note: Inference for generic arguments currently only takes the types of value arguments into account. The expected result type does not currently affect inference.

Syntax Details

The following examples show how generic declarations of different kinds are written:

T genericFunction<T>(T value);
funct genericFunction<T>(value: T) -> T;

__init<T>(T value);

__subscript<T>(T value) -> X { ... }

struct GenericType<T>
{
    T field;
}

interface IGenericInterface<T> : IBase<T>
{
}

Note: Currently there is no user-exposed syntax for writing a generic extension.

1> Note: This document is a work in progress. It is both incomplete and, in many cases, inaccurate.
2
3Declarations
4============
5
6Modules
7-------
8
9A module consists of one or more source units that are compiled together.
10The global declarations in those source units comprise the body of the module.
11
12In general, the order of declarations within a source unit does not matter; declarations can refer to other declarations (of types, functions, variables, etc.) later in the same source unit.
13Declarations (other than `import` declarations) may freely be defined in any source unit in a module; declarations in one source unit of a module may freely refer to declarations in other source units.
14
15Imports
16-------
17
18An import declaration is introduced with the keyword `import`:
19
20```hlsl
21import Shadowing;
22```
23
24An import declaration searches for a module matching the name given in the declaration, and brings the declarations in that module into scope in the current source unit.
25
26> Note: an `import` declaration only applies to the scope of the current source unit, and does *not* import the chosen module so that it is visible to other source units of the current module.
27
28The name of the module being imported may use a compound name:
29
30```hlsl
31import MyApp.Shadowing;
32```
33
34The mechanism used to search for a module is implementation-specific.
35
36> Note: The current Slang implementation searches for a module by translating the specified module name into a file path by:
37>
38> * Replacing any dot (`.`) separators in a compound name with path separators (e.g., `/`)
39>
40> * Replacing any underscores (`_`) in the name with hyphens (`-`)
41>
42> * Appending the extension `.slang`
43>
44> The implementation then looks for a file matching this path on any of its configured search paths.
45> If such a file is found it is loaded as a module comprising a single source unit.
46
47The declarations of an imported module become visible to the current module, but they are not made visible to code that later imports the current module.
48
49> Note: An experimental feature exists for an "exported" import declaration:
50>
51> ```hlsl
52> // inside A.slang
53> __exported import Shadowing;
54> ```
55>
56> This example imports the declarations from `Shadowing` into the current module (module `A`),
57> and also sets up information so that if other code declares `import A` then it can see
58> both the declarations in `A` and those in `Shadowing`.
59
60> Note: Mixing `import` declarations and traditional preprocessor-based (`#include`) modularity
61> in a codebase can lead to surprising results.
62>
63> Some things to be aware of:
64>
65> * Preprocessor definitions in your module do *not* affect the code of modules you `import`.
66>
67> * Preprocessor definitions in a module you `import` do *not* affect your code
68>
69> * The above caveats also apply to "include guards" and `#pragma once`, since they operate at the granularity of a source unit (not across modules)
70>
71> * If you `import` two modules, and then both `#include`  the same file, then those two modules may end up with duplicate declarations with the same name.
72>
73> As a general rule, be wary of preprocessor use inside of code meant to be an `import`able module.
74
75Variables
76---------
77
78Variables are declared using the keywords `let` and `var`:
79
80```hlsl
81let x = 7;
82var y = 9.0;
83```
84
85A `let` declaration introduces an immutable variable, which may not be assigned to or used as the argument for an `in out` or `out` parameter.
86A `var` declaration introduces a mutable variable.
87
88An explicit type may be given for a variable by placing it after the variable name and a colon (`:`):
89
90```hlsl
91let x : int = 7;
92var y : float = 9.0;
93```
94
95If no type is specified for a variable, then a type will be inferred from the initial-value expression.
96It is an error to declare a variable that has neither a type specifier or an initial-value expression.
97It is an error to declare a variable with `let` without an initial-value expression.
98
99A variable declared with `var` may be declared without an initial-value expression if it has an explicit type specifier:
100
101```
102var y : float;
103```
104
105In this case the variable is _uninitialized_ at the point of declaration, and must be explicitly initialized by assigning to it.
106Code that uses the value of an uninitialized variable may produce arbitrary results, or even exhibit undefined behavior depending on the type of the variable.
107Implementations *may* issue an error or warning for code that might make use of an uninitialized variable.
108
109### Traditional Syntax
110
111Variables may also be declared with traditional C-style syntax:
112
113```hlsl
114const int x = 7;
115float y = 9.0;
116```
117
118For traditional variable declarations a type must be specified.
119
120> Note: Slang does not support an `auto` type specifier like C++.
121
122Traditional variable declarations are immutable if they are declared with the `const` modifier, and are otherwise mutable.
123
124### Variables at Global Scope
125
126Variables declared at global scope may be either a global constant, a static global variables, or a global shader parameters.
127
128#### Global Constants
129
130A variable declared at global scope and marked with `static` and `const` is a _global constant_.
131
132A global constant must have an initial-value expression, and that initial-value expression must be a compile-time constant expression.
133
134#### Static Global Variables
135
136A variable declared at global scope and marked with `static` (but not with `const`) is a _static global variable_.
137
138A static global variable provides storage for each invocation executing an entry point.
139Assignments to a static global variable from one invocation do not affect the value seen by other invocations.
140
141> Note: the semantics of static global variable are similar to a "thread-local" variable in other programming models.
142
143A static global variable may include an initial-value expression; if an initial-value expression is included it is guaranteed to be evaluated and assigned to the variable before any other expression that references the variable is evaluated.
144There is no guarantee that the initial-value expression for a static global variable is evaluated before entry point execution begins, or even that the initial-value expression is evaluated at all (in cases where the variable might not be referenced at runtime).
145
146> Note: the above rules mean that an implementation may perform dead code elimination on static global variables, and may choose between eager and lazy initialization of those variables at its discretion.
147
148#### Global Shader Parameters
149
150A variable declared at global scope and not marked with `static` (even if marked with `const`) is a _global shader parameter_.
151
152Global shader parameters are used to pass arguments from application code into invocations of an entry point.
153The mechanisms for parameter passing are specific to each target platform.
154
155> Note: Currently only global shader parameters of opaque types or arrays of opaque types are supported.
156
157A global shader parameter may include an initial-value epxression, but such an expression does not affect the semantics of the compiled program.
158
159> Note: Initial-value expressions on global shader parameters are only useful to set up "default values" that can be read via reflection information and used by application code.
160
161### Variables at Function Scope
162
163Variables declared at _function scope_ (in the body of a function, initializer, subscript accessor, etc.) may be either a function-scope constant, function-scope static variable, or a local variable.
164
165#### Function-Scope Constants
166
167A variable declared at function scope and marked with both `static` and `const` is a _function-scope constant_.
168Semantically, a function-scope constant behaves like a global constant except that is name is only visible in the local scope.
169
170#### Function-Scope Static Variables
171
172A variable declared at function scope and marked with `static` (but not `const`) is a _function-scope static variable_.
173Semantically, a function-scope static variable behaves like a global static variable except that its name is only visible in the local scope.
174
175The initial-value expression for a function-scope static variable may refer to non-static variables in the body of the function.
176In these cases initialization of the variable is guaranteed not to occur until at least the first time the function body is evaluated for a given invocation.
177
178#### Local Variables
179
180A variable declared at function scope and not marked with `static` (even if marked with `const`) is a _local variable_.
181A local variable has unique storage for each _activation_ of a function by an invocation.
182When a function is called recursively, each call produces a distinct activation with its own copies of local variables.
183
184Functions
185---------
186
187Functions are declared using the `func` keyword:
188
189```hlsl
190func add(x: int, y: float) -> float { return float(x) + y; }
191```
192
193Parameters
194----------
195
196The parameters of the function are declared as `name: type` pairs.
197
198Parameters may be given a _default value_ by including an initial-value-expression clause:
199
200```hlsl
201func add(x: int, y: float = 1.0f) { ... }
202```
203
204Parameters may be marked with a _direction_ which affects how data is passed between caller and callee:
205
206```hlsl
207func add(x: in out int, y : float) { x += ... }
208```
209
210The available directions are:
211
212* `in` (the default) indicates typical pass-by-value (copy-in) semantics. The callee receives a *copy* of the argument passed by the caller.
213
214* `out` indicates copy-out semantics. The callee writes to the parameter and then a copy of that value is assigned to the argument of the caller after the call returns.
215
216* `in out` or `inout` indicates pass-by-value-result (copy-in and copy-out) semantics. The callee receives a copy of the argument passed by the caller, it may manipulate the copy, and then when the call returns the final value is copied back to the argument of the caller.
217
218An implementation may assume that at every call site the arguments for `out` or `in out` parameters never alias.
219Under those assumptions, the `out` and `inout` cases may be optimized to use pass-by-reference instead of copy-in and copy-out.
220
221> Note: Applications that rely on the precise order in which write-back for `out` and `in out` parameters is performed are already on shaky semantic ground.
222
223Body
224----
225
226The _body_ of a function declaration consists of statements enclosed in curly braces `{}`.
227
228In some cases a function declaration does not include a body, and in these cases the declaration must be terminated with a semicolon (`;`):
229
230```hlsl
231func getCount() -> int;
232```
233
234> Note: Slang does not require "forward declaration" of functions, although
235> forward declarations are supported as a compatibility feature.
236>
237> The only place where a function declaration without a definition should be
238> required is in the body of an `interface` declaration.
239
240
241The result type of a function mayb be specified after the parameter list using a _result type clause_ consisting of an arrow (`->`) followed by a type.
242If the function result type is `void`, the result type clause may be elided:
243
244```hlsl
245func modify(x: in out int) { x++; }
246```
247
248
249### Traditional Syntax
250
251Functions can also be declared with traditional C-style syntax:
252
253```hlsl
254float add(int x, float y) { return float(x) + y; }
255
256void modify(in out int x) { x ++; }
257```
258
259> Note: Currently traditional syntax must be used for shader entry point functions,
260> because only the traditional syntax currently supports attaching semantics to
261> parameters.
262
263### Entry Points
264
265An _entry point_ is a function that will be used as the starting point of execution for one or more invocations of a shader.
266
267
268
269Structure Types
270---------------
271
272Structure types are declared using the `struct` keyword:
273
274```hlsl
275struct Person
276{
277    var age : int;
278    float height;
279
280    int getAge() { return age; }
281    func getHeight() -> float { return this.height; }
282    static func getPopulation() -> int { ... }
283}
284```
285
286The body of a structure type declaration may include variable, type, function, and initializer declarations.
287
288### Fields
289
290Variable declarations in the body of a structure type declaration are also referred to as _fields_.
291
292A field that is marked `static` is shared between all instances of the type, and is semantically like a global variable marked `static`.
293
294A non-`static` field is also called an _instance field_.
295
296### Methods
297
298Function declarations in the body of a structure type declaration are also referred to as _methods_.
299
300A method declaration may be marked `static`.
301A `static` method must be invoked on the type itself (e.g., `Person.getPopulation()`).
302
303A non-`static` method is also referred to as an _instance method_.
304Instance methods must be invoked on an instance of the type (e.g., `somePerson.getAge()`).
305The body of an instance method has access to an implicit `this` parameter which refers to the instance on which the method was invoked.
306
307By default the `this` parameter of an instance method acts as an immutable variable.
308An instance method with the `[mutating]` attribute receives a mutable `this` parameter, and can only be invoked on a mutable value of the structure type.
309
310### Inheritance
311
312A structure type declaration may include an _inheritance clause_ that consists of a colon (`:`) followed by a comma-separated list of types that the structure type inherits from:
313
314```
315struct Person : IHasAge, IHasName
316{ .... }
317```
318
319When a structure type declares that it inherits from an interface, the programmer asserts that the structure type implements the required members of the interface.
320
321### Syntax Details
322
323A structure declaration does *not* need to be terminated with a semicolon:
324
325```hlsl
326// A terminating semicolon is allowed
327struct Stuff { ... };
328
329// The semicolon is not required
330struct Things { ... }
331```
332
333When a structure declarations ends without a semicolon, the closing curly brace (`}`) must be the last non-comment, non-whitespace token on its line.
334
335For compatibility with C-style code, a structure type declaration may be used as the type specifier in a traditional-style variable declaration:
336
337```hlsl
338struct Association
339{
340    int from;
341    int to;
342} associations[] =
343{
344    { 1, 1 },
345    { 2, 4 },
346    { 3, 9 },
347};
348```
349
350If a structure type declaration will be used as part of a variable declaration, then the next token of the variable declaration must appear on the same line as the closing curly brace (`}`) of the structure type declaration.
351The whole variable declaration must be terminated with a semicolon (`;`) as normal.
352
353
354Enumeration Types
355-----------------
356
357Enumeration type declarations are introduced with the `enum` keyword:
358
359```hlsl
360enum Color
361{
362    Red,
363    Green = 3,
364    Blue,
365}
366```
367
368### Cases
369
370The body of an enumeration type declaration consists of a comma-separated list of case declarations.
371An optional trailing comma may terminate the lis of cases.
372
373A _case declaration_ consists of the name of the case, along with an optional initial-value expression that specifies the _tag value_ for that case.
374If the first case declaration in the body elides an initial-value expression, the value `0` is used for the tag value.
375If any other case declaration elides an initial-value expressions, its tag value is one greater than the tag value of the immediately preceding case declaration.
376
377An enumeration case is referred to as if it were a `static` member of the enumeration type (e.g., `Color.Red`).
378
379### Inheritance
380
381An enumeration type declaration may include an inheritance clause:
382
383```hlsl
384enum Color : uint
385{ ... }
386```
387
388The inheritance clause of an enumeration declaration may currently only be used to specify a single type to be used as the _tag type_ of the enumeration type.
389The tag type of an enumeration must be a built-in scalar integer type.
390The tag value of each enumeration case will be a value of the tag type.
391
392If no explicit tag type is specified, the type `int` is used instead.
393
394> Note: The current Slang implementation has bugs that prevent explicit tag types from working correctly.
395
396### Conversions
397
398A value of an enumeration type can be implicitly converted to a value of its tag type:
399
400```hlsl
401int r = Color.Red;
402```
403
404Values of the tag type can be explicitly converted to the enumeration type:
405
406```hlsl
407Color red = Color(r);
408```
409
410Type Aliases
411------------
412
413A type alias is declared using the `typealias` keyword:
414
415```hlsl
416typealias Height  = int;
417```
418
419A type alias defines a name that will be equivalent to the type to the right of `=`.
420
421### Traditional Syntax
422
423Type aliases can also be declared with traditional C-style syntax:
424
425```hlsl
426typedef int Height;
427```
428
429Constant Buffers and Texture Buffers
430------------------------------------
431
432As a compatibility feature, the `cbuffer` and `tbuffer` keywords can be used to introduce variable declarations.
433
434A declaration of the form:
435
436```hlsl
437cbuffer Name
438{
439    F field;
440    // ...
441}
442```
443
444is equivalent to a declaration of the form:
445
446```hlsl
447struct AnonType
448{
449    F field;
450    // ...
451}
452__transparent ConstantBuffer<AnonType> anonVar;
453```
454
455In this expansion, `AnonType` and `anonVar` are fresh names generated for the expansion that cannot collide with any name in user code, and the modifier `__transparent` makes it so that an unqualified reference to `field` can implicitly resolve to `anonVar.field`.
456
457The keyword `tbuffer` uses an equivalent expansion, but with `TextureBuffer<T>` used instead of `ConstantBuffer<T>`.
458
459Interfaces
460----------
461
462An interface is declared using the `interface` keyword:
463
464```hlsl
465interface IRandom
466{
467    uint next();
468}
469```
470
471The body of an interface declaration may contain function, initializer, subscript, and associated type declarations.
472Each declaration in the body of an interface introduces a _requirement_ of the interface.
473Types that declare conformance to the interface must provide matching implementations of the requirements.
474
475Functions, initializers, and subscripts declared inside an interface must not have bodies; default implementations of interface requirements are not currently supported.
476
477An interface declaration may have an inheritance clause:
478
479```hlsl
480interface IBase
481{
482    int getBase();
483}
484
485interface IDerived : IBase
486{
487    int getDerived();
488}
489```
490
491The inheritance clause for an interface must only list other interfaces.
492If an interface `I` lists another interface `J` in its inheritance clause, then `J` is a _base interface_ of `I`.
493In order to conform to `I`, a type must also conform to `J`.
494
495Associated Types
496----------------
497
498An associated type declaration is introduced with `associatedtype`:
499
500```hlsl
501associatedtype Iterator;
502```
503
504An associated type declaration introduces a type into the signature of an interface, without specifying the exact concrete type to use.
505An associated type is an interface requirement, and different implementations of an interface may provide different types that satisfy the same associated type interface requirement:
506
507```
508interface IContainer
509{
510    associatedtype Iterator;
511    ...
512}
513
514struct MyArray : IContainer
515{
516    typealias Iterator = Int;
517    ...
518}
519
520struct MyLinkedList : IContainer
521{
522    struct Iterator { ... }
523    ...
524}
525```
526
527It is an error to declare an associated type anywhere other than the body of an interface declaration.
528
529An associated type declaration may have an inheritance clause.
530The inheritance clause of an associated type may only list interfaces; these are the _required interfaces_ for the associated type.
531A concrete type that is used to satisfy an associated type requirement must conform to all of the required interfaces of the associated type.
532
533Initializers
534------------
535
536An initializer declaration is introduced with the `__init` keyword:
537
538```hlsl
539struct MyVector
540{
541    float x, float y;
542
543    __init(float s)
544    {
545        x = s;
546        y = s;
547    }
548}
549```
550
551> Note: Initializer declarations are a non-finalized and unstable feature, as indicated by the double-underscore (`__`) prefix on the keyword.
552> Arbitrary changes to the syntax and semantics of initializers may be introduced in future versions of Slang.
553
554An initializer declaration may only appear in the body of an interface or a structure type.
555An initializer defines a method for initializing an instance of the enclosing type.
556
557> Note: A C++ programmer might think of an initializer declaration as similar to a C++ _constructor_.
558
559An initializer has a parameter list and body just like a function declaration.
560An initializer must not include a result type clause; the result type of an initializer is always the enclosing type.
561
562An initializer is invoked by calling the enclosing type as if it were a function.
563E.g., in the example above, the initializer in `MyVector` can be invoked as `MyVector(1.0f)`.
564
565
566An initializer has access to an implicit `this` variable that is the instance being initialized; an initializer must not be marked `static`.
567The `this` variable of an initializer is always mutable; an initializer need not, and must not, be marked `[mutating]`.
568
569> Note: Slang currently does not enforce that a type with an initializer can only be initialized using its initializers.
570> It is possible for user code to declare a variable of type `MyVector` above, and explicitly write to the `x` and `y` fields to initialize it.
571> A future version of the language may close up this loophole.
572
573> Note: Slang does not provide any equivalent to C++ _destructors_ which run automatically when an instance goes out of scope.
574
575Subscripts
576----------
577
578A subscript declaration is introduced with the `__subscript` keyword:
579
580```hlsl
581struct MyVector
582{
583    ...
584
585    __subscript(int index) -> float
586    {
587        get { return index == 0 ? x : y; }
588    }
589}
590```
591
592> Note: subscript declarations are a non-finalized and unstable feature, as indicated by the double-underscore (`__`) prefix on the keyword.
593> Arbitrary changes to the syntax and semantics of subscript declarations may be introduced in future versions of Slang.
594
595A subscript declaration introduces a way for a user-defined type to support subscripting with the `[]` braces:
596
597```hlsl
598MyVector v = ...;
599float f = v[0];
600```
601
602A subscript declaration lists one or more parameters inside parentheses, followed by a result type clause starting with `->`.
603The result type clause of a subscript declaration cannot be elided.
604
605The body of a subscript declaration consists of _accessor declarations_.
606Currently only `get` accessor declarations are supported for user code.
607
608A `get` accessor declaration introduces a _getter_ for the subscript.
609The body of a getter is a code block like a function body, and must return the appropriate value for a subcript operation.
610The body of a getter can access the parameters of the enclosing subscript, as a well as an implicit `this` parameter of the type that encloses the accessor.
611The `this` parameter of a getter is immutable; `[mutating]` getters are not currently supported.
612
613Extensions
614----------
615
616An extension declaration is introduced with the `extension` keyword:
617
618```hlsl
619extension MyVector
620{
621    float getLength() { return sqrt(x*x + y*y); }
622    static int getDimensionality() { return 2; }
623}
624```
625
626An extension declaration adds behavior to an existing type.
627In the example above, the `MyVector` type is extended with an instance method `getLength()`, and a static method `getDimensionality()`.
628
629An extension declaration names the type being extended after the `extension` keyword.
630The body of an extension declaration may include type declarations, functions, initializers, and subscripts.
631
632> Note: The body of an extension may *not* include variable declarations.
633> An extension cannot introduce members that would change the in-memory layout of the type being extended.
634
635The members of an extension are accessed through the type that is being extended.
636For example, for the above extension of `MyVector`, the introduced methods are accessed as follows:
637
638```hlsl
639MyVector v = ...;
640
641float f = v.getLength();
642int n = MyVector.getDimensionality();
643```
644
645An extension declaration need not be placed in the same module as the type being extended; it is possible to extend a type from third-party or standard module code.
646The members of an extension are only visible inside of modules that `import` the module declaring the extension;
647extension members are *not* automatically visible wherever the type being extended is visible.
648
649An extension declaration may include an inheritance clause:
650
651```hlsl
652extension MyVector : IPrintable
653{
654    ...
655}
656```
657
658The inheritance clause of an extension declaration may only include interfaces.
659When an extension declaration lists an interface in its inheritance clause, it asserts that the extension introduces a new conformance, such that the type being extended now conforms to the given interface.
660The extension must ensure that the type being extended satisfies all the requirements of the interface.
661Interface requirements may be satisfied by the members of the extension, members of the original type, or members introduced through other extensions visible at the point where the conformance was declared.
662
663It is an error for overlapping conformances (that is, of the same type to the same interface) to be visible at the same point.
664This includes cases where two extensions declare the same conformance, as well as those where the original type and an extension both declare the same conformance.
665The conflicting conformances may come from the same module or difference modules.
666
667In order to avoid problems with conflicting conformances, when a module `M` introduces a conformance of type `T` to interface `I`, one of the following should be true:
668
669* the type `T` is declared in module `M`, or
670* the type `I` is declared in module `M`
671
672Any conformance that does not follow these rules (that is, where both `T` and `I` are imported into module `M`) is called a _retroactive_ conformance, and there is no way to guarantee that another module `N` will not introduce the same conformance.
673The runtime behavior of programs that include overlapping retroactive conformances is currently undefined.
674
675Currently, extension declarations can only apply to structure types; extensions cannot apply to enumeration types or interfaces.
676
677Generics
678--------
679
680Many kinds of declarations can be made _generic_: structure types, interfaces, extensions, functions, initializers, and subscripts.
681
682A generic declaration introduces a _generic parameter list_ enclosed in angle brackets `<>`:
683
684```hlsl
685T myFunction<T>(T left, T right, bool condition)
686{
687    return condition ? left : right;
688}
689```
690
691### Generic Parameters
692
693A generic parameter list can include one or more parameters separated by commas.
694The allowed forms for generic parameters are:
695
696* A single identifier like `T` is used to declare a _generic type parameter_ with no constraints.
697
698* A clause like `T : IFoo` is used to introduce a generic type parameter `T` where the parameter is _constrained_ so that it must conform to the `IFoo` interface.
699
700* A clause like `let N : int` is used to introduce a generic value parameter `N`, which takes on values of type `int`.
701
702> Note: The syntax for generic value parameters is provisional and subject to possible change in the future.
703
704Generic parameters may declare a default value with `=`:
705
706```hlsl
707T anotherFunction<T = float, let N : int = 4>(vector<T,N> v);
708```
709
710For generic type parameters, the default value is a type to use if no argument is specified.
711For generic value parameters, the default value is a value of the same type to use if no argument is specified.
712
713### Explicit Specialization
714
715A generic is _specialized_ by applying it to _generic arguments_ listed inside angle brackets `<>`:
716
717```hlsl
718anotherFunction<int, 3>
719```
720
721Specialization produces a reference to the declaration with all generic parameters bound to concrete arguments.
722
723When specializing a generic, generic type parameters must be matched with type arguments that conform to the constraints on the parameter, if any.
724Generic value parameters must be matched with value arguments of the appropriate type, and that are specialization-time constants.
725
726An explicitly specialized function, type, etc. may be used wherever a non-generic function, type, etc. is expected:
727
728```hlsl
729int i = anotherFunction<int,3>( int3(99) );
730```
731
732### Implicit Specialization
733
734If a generic function/type/etc. is used where a non-generic function/type/etc. is expected, the compiler attempts _implicit specialization_.
735Implicit specialization infers generic arguments from the context at the use site, as well as any default values specified for generic parameters.
736
737For example, if a programmer writes:
738
739```hlsl
740int i = anotherFunction( int3(99) );
741```
742
743The compiler will infer the generic arguments `<int, 3>` from the way that `anotherFunction` was applied to a value of type `int3`.
744
745> Note: Inference for generic arguments currently only takes the types of value arguments into account.
746> The expected result type does not currently affect inference.
747
748### Syntax Details
749
750The following examples show how generic declarations of different kinds are written:
751
752```
753T genericFunction<T>(T value);
754funct genericFunction<T>(value: T) -> T;
755
756__init<T>(T value);
757
758__subscript<T>(T value) -> X { ... }
759
760struct GenericType<T>
761{
762    T field;
763}
764
765interface IGenericInterface<T> : IBase<T>
766{
767}
768```
769
770> Note: Currently there is no user-exposed syntax for writing a generic extension.