From a8a23a6c21437086df4499d4d30b8b5b47bcd1ff Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Wed, 18 Mar 2020 11:20:20 -0700 Subject: First pass at a language reference (#1279) * First pass at a language reference We already had the `language-guide.md` document under `docs/`, but this is an attempt to introduce a more full-featured reference to the Slang language and its features. Right now it is mostly focused on the syntax and what the language allows to be declared, and it is a little light on semantic details throughout (mostly relying on familiarity with C to explain the things that are left unsaid). Even so, this hopefully provides a starting point to continue adding more detail. * typos and other small fixes --- docs/language-reference/01-introduction.md | 0 docs/language-reference/02-lexcial-structure.md | 119 ++++ docs/language-reference/03-preprocessor.md | 17 + docs/language-reference/04-types.md | 336 +++++++++++ docs/language-reference/05-expressions.md | 351 +++++++++++ docs/language-reference/06-statements.md | 235 ++++++++ docs/language-reference/07-declarations.md | 771 ++++++++++++++++++++++++ docs/language-reference/08-attributes.md | 4 + docs/language-reference/README.md | 14 + 9 files changed, 1847 insertions(+) create mode 100644 docs/language-reference/01-introduction.md create mode 100644 docs/language-reference/02-lexcial-structure.md create mode 100644 docs/language-reference/03-preprocessor.md create mode 100644 docs/language-reference/04-types.md create mode 100644 docs/language-reference/05-expressions.md create mode 100644 docs/language-reference/06-statements.md create mode 100644 docs/language-reference/07-declarations.md create mode 100644 docs/language-reference/08-attributes.md create mode 100644 docs/language-reference/README.md (limited to 'docs') diff --git a/docs/language-reference/01-introduction.md b/docs/language-reference/01-introduction.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/language-reference/02-lexcial-structure.md b/docs/language-reference/02-lexcial-structure.md new file mode 100644 index 000000000..e22ea608c --- /dev/null +++ b/docs/language-reference/02-lexcial-structure.md @@ -0,0 +1,119 @@ +Lexical Structure +================= + +Source Units +------------ + +A _source unit_ comprises a sequence of zero or more _characters_ which for purposes of this document are defined as Unicode scalars (code points). +Implementations *may* accept source units stored as files on disk, buffers in memory, or any appropriate implementation-specified means. + +Encoding +-------- + +When encoding is required, source units *should* be encoded using UTF-8. +Implementations *may* support additional implemented-specified encodings. + +Whitespace +---------- + +_Horizontal whitespace_ consists of space (U+0020) and horizontal tab (U+0009). + +A _line break_ consists of a line feed (U+000A), carriage return (U+000D) or a carriage return followed by a line feed (U+000D, U+000A). +Line breaks are used as line separators rather than terminators; it is not necessary for a source unit to end with a line break. + +Escaped Line Breaks +------------------- + +An _escaped line break_ comprises a backslack (`\`, U+005C) follow immediately by a line break. + +Comments +-------- + +A _comment_ is either a line comment or a block comment: + +```hlsl +// a line comment +/* a block comment */ +``` + +A _line comment_ comprises two forward slashes (`/`, U+002F) followed by zero or more characters that do not contain a line break. +A line comment extends up to, but does not include, a subsequent line break or the end of the source unit. + +A _block comment_ begins with a forward slash (`/`, U+002F) followed by an asterisk (`*`, U+0052). +A block comment is terminated by the next instance of an asterisk followed by a forward slash (`*/`). +A block comment contains all characters between where it begins and where it terminates, including any line breaks. +Block comments do not nest. +It is an error if a block comment that begins in a source unit is not terminated in that source unit. + +Phases +------ + +Compilation of a source unit proceeds _as if_ the following steps are executed in order: + +1. Line numbering (for subsequent diagnostic messages) is noted based on the locations of line breaks + +2. Escaped line breaks are eliminated. No new characters are inserted to replace them. Any new escaped line breaks introduced by this step are not eliminated. + +3. All comments are replaced with a single space (U+0020) + +4. The source unit is _lexed_ into a sequence of tokens according the lexical grammar in this chapter + +5. The lexed sequence of tokens is _preprocessed_ to produce a new sequence of tokens (Chapter 3) + +6. Subsequent processing is performed on the preprocessed sequence of tokens + +Identifiers +----------- + +An _identifier_ begins with an uppercase or lowercase ASCII letter (`A` through `Z`, `a` through `z`), or an underscore (`_`). +After the first character, ASCII digits (`0` through `9`) may also be used in an identifier. + +The identifier consistent of a single underscore (`_`) is reserved by the language and must not be used by programs. +Otherwise, there are no fixed keywords or reserved words. +Words that name a built-in language construct can also be used as user-defined identifiers and will shadow the built-in definitions in the scope of their definition. + +Literals +-------- + +### Integer Literals + +An _integer literal_ consists of an optional radix specifier followed by digits and an optional suffix. + +The _radix specifier_ may be: + +* `0x` or `0X` to specify a hexadecimal literal (radix 16) +* `0b` or `0B` to specify a binary literal (radix 2) + +When no radix specifier is present a radix of 10 is used. + +Octal literals (radix 8) are not supported. +A `0` prefix on an integer literal does *not* specify an octal literal as it does in C. +Implementations *may* warn on integer literals with a `0` prefix in case users expect C behavior. + +The _digits_ of an integer literal may include ASCII `0` through `9`. +In the case of a hexadecimal literal, digits may include the letters `A` through `F` (and `a` through `f`) which represent digit values of 10 through 15. +It is an error for an integer literal to include a digit with a value greater than or equal to the radix. +The digits of an integer literal may also include underscore (`_`) characters, which are ignored and have no semantic impact. + +The _suffix_ on an integer literal may be used to indicate the desired type of the literal: + +* A `u` suffix indicates the `uint` type +* An `l` or `ll` suffix indicates the `int64_t` type +* A `ul` or `ull` suffix indicates the `uint64_t` type + +### Floating-Point Literals + +> Note: This section is not yet complete. + +### String Literals + +> Note: This section is not yet complete. + +### Character Literals + +> Note: This section is not yet complete. + +Operators and Punctuation +------------------------- + +> Note: This section is not yet complete. diff --git a/docs/language-reference/03-preprocessor.md b/docs/language-reference/03-preprocessor.md new file mode 100644 index 000000000..984d9ce72 --- /dev/null +++ b/docs/language-reference/03-preprocessor.md @@ -0,0 +1,17 @@ +Preprocessor +============ + +Slang supports a C-style preprocessor with the following directives: + +* `#include` +* `#define` +* `#undef` +* `#if`, `#ifdef`, `#ifndef` +* `#else`, `#elif` +* `#endif` +* `#error` +* `#warning` +* `#line` +* `#pragma` + +> Note: This section is not yet complete. diff --git a/docs/language-reference/04-types.md b/docs/language-reference/04-types.md new file mode 100644 index 000000000..6485bcf42 --- /dev/null +++ b/docs/language-reference/04-types.md @@ -0,0 +1,336 @@ +Types +===== + +This section defines the kinds of types supported by Slang. + +Types in Slang do not necessarily prescribe a single _layout_ in memory. +The discussion of each type will specify any guarantees about layout it provides; any details of layout not specified here may depend on the target platform, compiler options, and context in which a type is used. + +Void Type +--------- + +The type `void` contains no data and has a single, unnamed, value. + +A `void` value takes up no space, and thus does not affect the layout of types. +Formally, a `void` value behaves as if it has a size of zero bytes, and one-byte alignment. + +Scalar Types +------------ + +### Boolean Type + +The type `bool` is used to represent Boolean truth value: `true` and `false`. + +The size of a `bool` varies across target platforms; programs that need to ensure a matching in-memory layout between targets should not use `bool` for in-memory data structures. +On all platforms, the `bool` type must be _naturally aligned_ (its alignment is its size). + +### Integer Types + +The following integer types are defines: + +| Name | Description | +|---------------|-------------| +| `int8_t` | 8-bit signed integer | +| `int16_t` | 16-bit signed integer | +| `int` | 32-bit signed integer | +| `int64_t` | 64-bit signed integer | +| `uint8_t` | 8-bit unsigned integer | +| `uint16_t` | 16-bit unsigned integer | +| `uint` | 32-bit unsigned integer | +| `uint64_t` | 64-bit unsigned integer | + +All signed integers used two's complement representation. +All arithmetic operations on integers (both signed and unsigned) wrap on overflow/underflow. + +All target platforms must support the `int` and `uint` types. +Specific target platforms may not support the other integer types. + +All integer types are stored in memory with their natural size and alignment on all targes that support them. + +### Floating-Point Types + +The following floating-point type are defined: + +| Name | Description | +|---------------|-------------------------------| +| `half` | 16-bit floating-point number (1 sign bit, 5 exponent bits, 10 fraction bits) | +| `float` | 32-bit floating-point number (1 sign bit, 8 exponent bits, 23 fraction bits) | +| `double` | 64-bit floating-point number (1 sign bit, 11 exponent bits, 52 fraction bits) | + +All floating-point types are laid out in memory using the matching IEEE 754 standard format (`binary16`, `binary32`, `binary64`). +Target platforms may define their own rules for rounding, precision, denormals, infinities, and not-a-number values. + +All target platforms must support the `float` type. +Specific targets may not support the other floating-point types. + +All floating-point types are stored in memory with their natural size and alignment on all targets that support them. + +Vector Types +------------ + +A vector type is written as `vector` and represents an `N`-element vector with elements of type `T`. +The _element type_ `T` must be one of the built-in scalar types, and the _element count_ `N` must be a specialization-time constant integer. +The element count must be between 2 and 4, inclusive. + +A vector type allows subscripting of its elements like an array, but also supports element-wise arithmetic on its elements. +_Element-wise arithmetic_ means mapping unary and binary operators over the elements of a vector to produce a vector of results: + +```hlsl +vector a = { 1, 2, 30, 40 }; +vector b = { 10, 20, 3, 4 }; + +-a; // yields { -1, -2, -30, -40 } +a + b; // yields { 11, 22, 33, 44 } +b / a; // yields { 10, 10, 0, 0 } +a > b; // yields { false, false, true, true } +``` + +A vector type is laid out in memory as `N` contiguous values of type `T` with no padding. +The alignment of a vector type may vary by target platforms. +The alignment of `vector` will be at least the alignment of `T` and may be at most `N` times the alignment of `T`. + +As a convenience, Slang defines built-in type aliases for vectors of the built-in scalar types. +E.g., declarations equivalent to the following are provided by the Slang standard library: + +```hlsl +typealias float4 = vector; +typealias int8_t3 = vector; +``` + +### Legacy Syntax + +For compatibility with older codebases, the generic `vector` type includes default values for `T` and `N`, being declared as: + +```hlsl +struct vector { ... } +``` + +This means that the bare name `vector` may be used as a type equivalent to `float4`: + +```hlsl +// All of these variables have the same type +vector a; +float4 b; +vector c; +``` + +Matrix Types +------------ + +A matrix type is written as `matrix` and represents a matrix of `R` rows and `C` columns, with elements of type `T`. +The element type `T` must be one of the built-in scalar types. +The _row count_ `R` and _column count_ `C` must be specialization-time constant integers. +The row count and column count must each be between 2 and 4, respectively. + +A matrix type allows subscripting of its rows, similar to an `R`-element array of `vector` elements. +A matrix type also supports element-wise arithmetic. + +Matrix types support both _row-major_ and _column-major_ memory layout. +Implementations may support command-line flags or API options to control the default layout to use for matrices. + +> Note: Slang currently does *not* support the HLSL `row_major` and `column_major` modifiers to set the layout used for specific declarations. + +Under row-major layout, a matrix is laid out in memory equivalently to an `R`-element array of `vector` elements. + +Under column-major layout, a matrix is laid out in memory equivalent to the row-major layout of its transpose. +This means it will be laid out equivalently to a `C`-element array of `vector` elements. + +As a convenience, Slang defines built-in type aliases for matrices of the built-in scalar types. +E.g., declarations equivalent to the following are provided by the Slang standard library: + +```hlsl +typealias float3x4 = matrix; +typealias int64_t4x2 = matrix; +``` + +> Note: For programmers using OpenGL or Vulkan as their graphics API, and/or who are used to the GLSL language, +> it is important to recognize that the equivalent of a GLSL `mat3x4` is a Slang `float3x4`. +> This is despite the fact that GLSL defines a `mat3x4` as having 3 *columns* and 4 *rows*, while a Slang `float3x4` is defined as having 3 rows and 4 columns. +> This convention means that wherever Slang refers to "rows" or "columns" of a matrix, the equivalent terms in the GLSL, SPIR-V, OpenGL, and Vulkan specifications are "column" and "row" respectively (*including* in the compound terms of "row-major" and "column-major") +> While it may seem that this choice of convention is confusing, it is necessary to ensure that subscripting with `[]` can be efficiently implemented on all target platforms. +> This decision in the Slang language is consistent with the compilation of HLSL to SPIR-V performed by other compilers. + +### Legacy Syntax + +For compatibility with older codebases, the generic `matrix` type includes default values for `T`, `R`, and `C`, being declared as: + +```hlsl +struct matrix { ... } +``` + +This means that the bare name `matrix` may be used as a type equivalent to `float4x4`: + +```hlsl +// All of these variables have the same type +matrix a; +float4x4 b; +matrix c; +``` + +Structure Types +--------------- + +Structure types are introduced with `struct` declarations, and consist of an ordered sequence of named and typed fields: + +```hlsl +struct S +{ + float2 f; + int3 i; +} +``` + +### Standard Layout + +The _standard layout_ for a structure type uses the following algorithm: + +* Initialize variables `size` and `alignment` to zero and one, respectively +* For each field `f` of the structure type: + * Update `alignment` to be the maximum of `alignment` and the alignment of `f` + * Set `size` to the smallest multiple of `alignment` not less than `size` + * Set the offset of field `f` to `size` + * Add the size of `f` to `size` + +When this algorithm completes, `size` and `alignment` will be the size and alignment of the structure type. + +Most target platforms do not use the standard layout directly, but it provides a baseline for defining other layout algorithms. +Any layout for structure types must guarantee an alignment at least as large as the standard layout. + +### C-Style Layout + +C-style layout for structure types differs from standard layout by adding an additional final step: + +* Set `size` the smallest multiple of `alignment` not less than `size` + +This mirrors the layout rules used by typical C/C++ compilers. + +### D3D Constant Buffer Layout + +D3D constant buffer layout is similar to standard layout with two differences: + +* The initial alignment is 16 instead of one + +* If a field would have _improper straddle_, where the interval `(fieldOffset, fieldOffset+fieldSize)` (eclusive on both sides) contains any multiple of 16, *and* the field offset is not already a multiple of 16, then the offset of the field is adjusted to the next multiple of 16 + +Array Types +----------- + +An _array type_ is either a statically-sized or dynamically-sized array type. + +A known-size array type is written `T[N]` where `T` is a type and `N` is a specialization-time constant integer. +This type represents an array of exactly `N` values of type `T`. + +An unknown-size array type is written `T[]` where `T` is a type. +This type represents an array of some fixed, but statically unknown, size. + +> Note: Unlike in C and C++, arrays in Slang are always value types, meaning that assignment and parameter passing of arrays copies their elements. + +### Declaration Syntax + +For variable and parameter declarations using traditional syntax, a variable of array type may be declared by using the element type `T` as a type specifier (before the variable name) and the `[N]` to specify the element count after the variable name: + +```hlsl +int a[10]; +``` + +Alternatively, the array type itself may be used as the type specifier: + +```hlsl +int[10] a; +``` + +When using the `var` or `let` keyword to declare a variable, the array type must not be split: + +```hlsl +var a : int[10]; +``` + +> Note: when declaring arrays of arrays (often thought of as "multidimensional arrays") a programmer must be careful about the difference between the two declaration syntaxes. +> The following two declarations are equivalent: +> +> ```hlsl +> int[3][5] a; +> int a[5][3]; +> ``` +> +> In each case, `a` is a five-element array of three-element arrays of `int`s. +> However, one declaration orders the element counts as `[3][5]` and the other as `[5][3]`. + +### Element Count Inference + +When a variable is declared with an unknown-size array type, and also includes an initial-value expression: + +```hlsl +int a[] = { 0xA, 0XB, 0xC, 0xD }; +``` + +The compiler will attempt to infer an element count based on the type and/or structure of the initial-value expression. +In the above case, the compiler will infer an element count of 4 from the structure of the initializer-list expression. +Thus the preceding declaration is equivalent to: + +```hlsl +int a[4] = { 0xA, 0xB, 0xC, 0xD }; +``` + +A variable declared in this fashion semantically has a known-size array type and not an unknown-size array type; the use of an unknown-size array type for the declaration is just a convenience feature. + +### Standard Layout + +The _stride_ of a type is the smallest multiple of its alignment not less than its size. + +Using the standard layout for an array type `T[]` or `T[N]`: + +* The _element stride_ of the array type is the stride of its element type `T` +* Element `i` of the array starts at an offset that is `i` times the element stride of the array +* The alignment of the array type is the alignment of `T` +* The size of an unknown-size array type is unknown +* The size of a known-size array with zero elements is zero +* The size of a known-size array with a nonzero number `N` of elements is the size of `T` plus `N - 1` times the element stride of the array + +### C-Style Layout + +The C-style layout of an array type differs from the standard layout in that the size of a known-size array with a nonzero number `N` of elements is `N` times the element stride of the array. + +### D3D Constant Buffer Layout + +The D3D constant buffer layout of an array differs from the standard layout in that the element stride of the array is set to the smallest multiple of the alignment of `T` that is not less than the stride of `T` + +This Type +--------- + +Within the body of a structure or interface declaration, the keyword `This` may be used to refer to the enclosing type. +Inside of a structure type declaration, `This` refers to the structure type itself. +Inside of an interface declaration, `This` refers to the concrete type that is conforming to the interface (that is, the type of `this`). + +Opaque Types +------------ + +_Opaque_ types are built-in types that (depending on the target platform) may not have a well-defined size or representation in memory. +Similar languages may refer to these as "resource types" or "object types." + +The full list of opaque types supported by Slang can be found in the standard library reference, but important examples are: + +* Texture types such as `Texture2D`, `TextureCubeArray`, and `RWTexture2DMS` +* Sampler state types: `SamplerState` and `SamplerComparisonState` +* Buffer types like `ConstantBuffer` and `StructuredBuffer` +* Parameter blocks: `ParameterBlock` + +Layout for opaque types depends on the target platform, and no specific guarantees can be made about layout rules across platforms. + +Known and Unknown Size +---------------------- + +Every type has either known or unknown size. +Types with unknown size arise in a few ways: + +* An unknown-size array type has unknown size + +* A structure type has unknown size if any field type has unknown size + +The use of types with unknown size is restricted as follows: + +* A type with unknown size cannot be used as the element type of an array + +* A type with unknown size can only be used as the last field of a structure type + +* A type with unknown size cannot be used as a generic argument to specialize a user-defined type, function, etc. Specific built-in generic types/functions may support unknown-size types, and this will be documented on the specific type/function. diff --git a/docs/language-reference/05-expressions.md b/docs/language-reference/05-expressions.md new file mode 100644 index 000000000..861def5c0 --- /dev/null +++ b/docs/language-reference/05-expressions.md @@ -0,0 +1,351 @@ +Expressions +=========== + +Expressions are terms that can be _evaluated_ to produce values. +This section provides a list of the kinds of expressions that may be used in a Slang program. + +In general, the order of evaluation of a Slang expression proceeds from left to right. +Where specific expressions do not follow this order of evaluation, it will be noted. + +Some expressions can yield _l-values_, which allows them to be used on the left-hand-side of assignment, or as arguments for `out` or `in out` parameters. + +Literal Expressions +------------------- + +Literal expressions are never l-values. + +### Integer Literal Expressions + +An integer literal expression consists of a single integer literal token: + +```hlsl +123 +``` + +An unsuffixed integer literal expression always has type `int`. + +### Floating-Point Literal Expressions + +A floating-point literal expression consists of a single floating-point literal token: + +```hlsl +1.23 +``` + +A unsuffixed floating-point literal expression always has type `float`. + +### Boolean Literal Expressions + +Boolean literal expressions use the keywords `true` and `false`. + +### String Literal Expressions + +A string literal expressions consists of one or more string literal tokens in a row: + +```hlsl +"This" "is one" "string" +``` + +Identifier Expression +--------------------- + +An _identifier expression_ consists of a single identifier: + +```hlsl +someName +``` + +When evaluated, this expression looks up `someName` in the environment of the expression and yields the value of a declaration with a matching name. + +An identifier expression is an l-value if the declaration it refers to is mutable. + +### Overloading + +It is possible for an identifier expression to be _overloaded_, such that it refers to one or more candidate declarations with the same name. +If the expression appears in a context where the correct declaration to use can be disambiguated, then that declaration is used as the result of the name expression; otherwise use of an overloaded name is an error at the use site. + +### Implicit Lookup + +It is possible for a name expression to refer to nested declarations in two ways: + +* In the body of a method, a reference to `someName` may resolve to `this.someName`, using the implicit `this` parameter of the method + +* When a global-scope `cbuffer` or `tbuffer` declaration is used, `someName` may refer to a field declared inside the `cbuffer` or `tbuffer` + +Member Expression +----------------- + +A _member expression_ consists of a base expression followed by a dot (`.`) and an identifier naming a member to be accessed: + +```hlsl +base.m +``` + +When `base` is a structure type, this expression looks up the field or other member named by `m`. +Just as for an identifier expression, the result of a member expression may be overloaded, and might be disambiguated based on how it is used. + +A member expression is an l-value if the base expression is an l-value and the member it refers to is mutable. + +### Implicit Dereference + +If the base expression of a member reference is a _pointer-like type_ such as `ConstantBuffer`, then a member reference expression will implicitly dereference the base expression to refer to the pointed-to value (e.g., in the case of `ConstantBuffer` this is the buffer contents of type `T`). + +### Vector Swizzles + +When the base expression of a member expression is of a vetor type `vector` then a member expression is a _vector swizzle expression_. +The member name must conform to these constraints: + +* The member name must comprise between one and four ASCII characters +* The characters must be come either from the set (`x`, `y`, `z`, `w`) or (`r`, `g`, `b`, `a`), corresponding to element indics of (0, 1, 2, 3) +* The element index corresponding to each character must be less than `N` + +If the member name of a swizzle consists of a single character, then the expression has type `T` and is equivalent to a subscript expression with the corresponding element index. + +If the member name of a swizzle consists of `M` characters, then the result is a `vector` built from the elements of the base vector with the corresponding indices. + +A vector swizzle expression is an l-value if the base expression was an l-value and the list of indices corresponding to the characeters of the member name contains no duplicates. + +### Matrix Swizzles + +> Note: The Slang implementation currently doesn't support matrix swizzles. + +### Static Member Expressions + +When the base expression of a member expression is a type instead of a value, the result is a _static member expression_. +A static member expression can refer to a static field or static method of a structure type. +A static member expression can also refer to a case of an enumeration type. + +A static member expression (but not a member expression in general) may use the token `::` instead of `.` to separate the base and member name: + +```hlsl +// These are equivalent +Color.Red +Color::Red +``` + +This Expression +--------------- + +A _this expression_ consists of the keyword `this` and refers to the implicit instance of the enclosing type that is being operated on in instance methods, subscripts, and initializers. + +The type of `this` is `This`. + +Parenthesized Expression +---------------------- + +An expression wrapped in parentheses `()` is a _parenthesized expression_ and evaluates to the same value as the wrapped expression. + +Call Expression +--------------- + +A _call expression_ consists of a base expression and a list of argument expressions, separated by commas and enclosed in `()`: + +```hlsl +myFunction( 1.0f, 20 ) +``` + +When the base expression (e.g., `myFunction`) is overloaded, a call expression can disambiguate the overloaded expression based on the number and type or arguments present. + +The base expression of a call may be a member reference expression: + +```hlsl +myObject.myFunc( 1.0f ) +``` + +In this case the base expression of the member reference (e.g., `myObject` in this case) is used as the argument for the implicit `this` parameter of the callee. + +### Mutability + +If a `[mutating]` instance is being called, the argument for the implicit `this` parameter must be an l-value. + +The argument expressions corresponding to any `out` or `in out` parameters of the callee must be l-values. + +A call expression is never an l-value. + +### Initializer Expressions + +When the base expression of a call is a type instead of a value, the expression is an initializer expression: + +```hlsl +float2(1.0f, 2.0f) +``` + +An initializer expression initialized an instance of the specified type using the given arguments. + +An initializer expression with only a single argument is treated as a cast expression: + +```hlsl +// these are equivalent +int(1.0f) +(int) 1.0f +``` + +Subscript Expression +-------------------- + +A _subscript expression_ consists of a base expression and a list of argument expressions, separated by commas and enclosed in `[]`: + +```hlsl +myVector[someIndex] +``` + +A subscript expression invokes one of the subscript declarations in the type of the base expression. Which subscript declaration is invoked is resolved based on the number and types of the arguments. + +A subscript expression is an l-value if the base expression is an l-value and if the subscript declaration it refers to has a setter or by-reference accessor. + +Subscripts may be formed on the built-in vector, matrix, and array types. + + +Initializer List Expression +--------------------------- + +An _initializer list expression_ comprises zero or more expressions, separated by commas, enclosed in `{}`: + +``` +{ 1, "hello", 2.0f } +``` + +An initialier list expression may only be used directly as the initial-value expression of a variable or parameter declaration; initializer lists are not allowed as arbitrary sub-expressions. + +> Note: This section will need to be updated with the detailed rules for how expressions in the initializer list are used to initialize values of each kind of type. + +Cast Expression +--------------- + +A _cast expression_ attempt to coerce a single value (the base expression) to a desired type (the target type): + +```hlsl +(int) 1.0f +``` + +A cast expression can perform both built-in type conversions and invoke any single-argument initializers of the target type. + +### Compatibility Feature + +As a compatiblity feature for older code, Slang supports using a cast where the base expression is an integer literal zero and the target type is a user-defined structure type: + +```hlsl +MyStruct s = (MyStruct) 0; +``` + +The semantics of such a cast are equivalent to initialization from an empty initializer list: + +```hlsl +MyStruct s = {}; +``` + +Assignment Expression +--------------------- + +An _assignment expression_ consists of a left-hand side expression, an equals sign (`=`), and a right-hand-side expressions: + +```hlsl +myVar = someValue +``` + +The semantics of an assignment expression are to: + +* Evaluate the left-hand side to produce an l-value, +* Evaluate the right-hand side to produce a value +* Store the value of the right-hand side to the l-value of the left-hand side +* Yield the l-value of the left-hand-side + +Operator Expressions +-------------------- + +### Prefix Operator Expressions + +The following prefix operators are supported: + +| Operator | Description | +|-----------|-------------| +| `+` | identity | +| `-` | arithmetic negation | +| `~` | bit-wise Boolean negation | +| `!` | Boolean negation | +| `++` | increment in place | +| `--` | decrement in place | + +A prefix operator expression like `+val` is equivalent to a call expression to a function of the matching name `operator+(val)`, except that lookup for the function only considers functions marked with the `__prefix` keyword. + +The built-in prefix `++` and `--` operators require that their operand is an l-value, and work as folows: + +* Evaluate the operand to produce an l-value +* Read from the l-value to yield an _old value_ +* Increment or decrement the value to yield a _new value_ +* Write the new value to the l-value +* Yield the new value + +### Postfix Operator Expressions + +The following postfix operators are supported: + +| Operator | Description | +|-----------|-------------| +| `++` | increment in place | +| `--` | decrement in place | + +A postfix operator expression like `val++` is equivalent to a call expression to a function of the matching name `operator++(val)`, except that lookup for the function only considers functions marked with the `__postfix` keyword. + +The built-in prefix `++` and `--` operators require that their operand is an l-value, and work as folows: + +* Evaluate the operand to produce an l-value +* Read from the l-value to yield an _old value_ +* Increment or decrement the value to yield a _new value_ +* Write the new value to the l-value +* Yield the old value + +### Infix Operator Expressions + +The follow infix binary operators are supported: + +| Operator | Kind | Description | +|-----------|-------------|-------------| +| `*` | Multiplicative | multiplication | +| `/` | Multiplicative | division | +| `%` | Multiplicative | remainder of division | +| `+` | Additive | addition | +| `-` | Additive | subtraction | +| `<<` | Shift | left shift | +| `>>` | Shift | right shift | +| `<` | Relational | less than | +| `>` | Relational | greater than | +| `<=` | Relational | less than or equal to | +| `>=` | Relational | greater than or equal to | +| `==` | Equality | equal to | +| `!=` | Equality | not equal to | +| `&` | BitAnd | bitwise and | +| `^` | BitXor | bitwise exclusive or | +| `|` | BitOr | bitwise or | +| `&&` | And | logical and | +| `||` | Or | logical or | +| `+=` | Assignment | compound add/assign | +| `-=` | Assignment | compound subtract/assign | +| `*=` | Assignment | compound multiply/assign | +| `/=` | Assignment | compound divide/assign | +| `%=` | Assignment | compound remainder/assign | +| `<<=` | Assignment | compound left shift/assign | +| `>>=` | Assignment | compound right shift/assign | +| `&=` | Assignment | compound bitwise and/assign | +| `|=` | Assignment | compound bitwise or/assign | +| `^=` | Assignment | compound bitwise xor/assign | +| `=` | Assignment | assignment | +| `,` | Sequencing | sequence | + +With the exception of the assignment operator (`=`), an infix operator expression like `left + right` is equivalent to a call expression to a function of the matching name `operator+(left, right)`. + +### Conditional Expression + +The conditonal operator, `?:`, is used to select between two expressions based on the value of a condition: + +```hlsl +useNegative ? -1.0f : 1.0f +``` + +The condition may be either a single value of type `bool`, or a vetor of `bool`. +When a vector of `bool` is used, the two values being selected between must be vectors, and selection is performed component-wise. + +> Note: Unlike C, C++, GLSL, and most other C-family languages, Slang currently follows the precedent of HLSL where `?:` does not short-cirucuit. +> +> This decision may change (for the scalar case) in a future version of the language. +> Programmer are encouraged to write code that does not depend on whether or not `?:` short-circuits. diff --git a/docs/language-reference/06-statements.md b/docs/language-reference/06-statements.md new file mode 100644 index 000000000..f861ba299 --- /dev/null +++ b/docs/language-reference/06-statements.md @@ -0,0 +1,235 @@ +Statements +========== + +Statements are used to define the bodies of functions and deterine order of evaluation and control flow for an entire program. +Statements are distinct from expressions in that statements do not yield results and do not have types. + +This section lists the kinds of statements supported by Slang. + +Expression Statement +-------------------- + +An expression statement consists of an expression followed by a semicolon: + +```hlsl +doSomething(); +a[10] = b + 1; +``` + +An implementation may warn on an expression statement that has to effect on the results of execution. + +Declaration Statement +--------------------- + +A declaration may be used as a statement: + +```hlsl +let x = 10; +var y = x + 1; +int z = y - x; +``` + +> Note: Currently only variable declarations are allowed in statement contexts, but other kinds of declarations may be enabled in the future. + +Block Statement +--------------- + +A block statement consists of zero or more statements wrapped in curly braces `{}`: + +```hlsl +{ + int x = 10; + doSomething(x); +} +``` + +A block statement provides local scoping to declarations. +Declarations in a block are visible to later statements in the same block, but not to statements or expressions outside of the block. + +Empty Statement +--------------- + +A single semicolon (`;`) may be used as an empty statement equivalent to an empty block statement `{}`. + +Conditional Statements +---------------------- + +### If Statement + +An _if statement_ consists of the `if` keyword and a conditional expression in parentheses, followed by a statement to execute if the condition is true: + +```hlsl +if(somethingShouldHappen) + doSomething(); +``` + +An if statement may optionally include an _else clause_ consisting of the keyword `else` followed by a statement to execute if the condition is false: + +```hlsl +if(somethingShouldHappen) + doSomething(); +else + doNothing(); +``` + +### Switch Statement + +A _switch statement_ consists of the `switch` keyword followed by an expression wrapped in parentheses and a _body statement_: + +```hlsl +switch(someValue) +{ + ... +} +``` + +The body of a switch statement must be a block statement, and its body must consist of switch case clauses. +A _switch case clause_ consists of one or more case labels or default labels, followed by one or more statements: + +```hlsl +// this is a switch case clause +case 0: +case 1: + doBasicThing(); + break; + +// this is another swithc case clause +default: + doAnotherThing(); + break; +``` + +A _case label_ consists of the keyword `case` followed by an expresison and a colon (`:`). +The expression must evaluate to a compile-time constant integer. + +A _default label_ consists of the keyword `default` followed by a colon (`:`). + +It is an error for a case label or default label to appear anywhere other than the body of a `switch` statement. +It is an error for a statement to appear inside the body of a `switch` statemetn that is no part of a switch case clause. + +Each switch case clause must exit the `switch` statement via a `break` or other control transfer statement. +"Fall-through" from one switch case clause to another is not allowed. + +Loop Statements +--------------- + +### For Statement + +A _for statement_ uses the following form: + +```hlsl +for( ; ; ) +``` + +The _initial statement_ is optional, but may declare a variable whose scope is limited to the for statement. + +The _condition expression_ is optional. If present it must be an expression that can be coerced to type `bool`. If absent, a true value is used as the condition. + +The _side effect expression_ is optional. If present it will executed for its effects before each testing the condition for every loop iteration after the first. + +The _body statement_ is a statement that will be executed for each iteration of the loop. + +### While Statement + +A _while statement_ uses the following form: + +```hlsl +while( ) +``` + +and is equivalent to a `for` loop of the form: + +```hlsl +for( ; ; ) +``` + +### Do-While Statement + +A _do-while statement_ uses the following form: + +```hlsl +do while( ) +``` + +and is equivalent to a `for` loop of the form: + +```hlsl +for(;;) +{ + + if() continue; else break; +} +``` + +Control Transfer Statements +--------------------------- + +### Break Statement + +A `break` statement transfers control to after the end of the closest lexically enclosing switch statement or loop statement: + +```hlsl +break; +``` + +### Continute Statement + +A `continue` statement transfers control to the start of the next iteration of a loop statement. +In a for statement with a side effect expression, the side effect expression is evaluated when `continue` is used: + +```hlsl +break; +``` + +### Return Statement + +A `return` statement transfers control out of the current function. + +In the body of a function with a `void` result type, the `return` keyword may be followed immediately by a semicolon: + +```hlsl +return; +``` + +Otherwise, the `return` keyword must be followed by an expression to use as the value to return to the caller: + +```hlsl +return someValue; +``` + +The value returned must be able to coerce to the result type of the lexically enclosing function. + +### Discard Statement + +A `discard` statement can only be used in the context of a fragment shader, in which case it causes the current invocation to terminate and the graphics system to discard the corresponding fragment so that it does not get combined with the framebuffer pixel at its coordintes. + +Operations with side effects that were executed by the invocation before a `discard` will still be performed and their results will become visible according to the rules of the platform. + +Compile-Time For Statement +-------------------------- + +A _compile-time for statement_ is used as an alternative to preprocessor techniques for loop unrolling. +It looks like: + +```hlsl +$for( in Range(, )) +``` + +The _initial value_ and _upper bound_ expressions must be compile-time constant integers. +The semantics of a compile-time for statement are as if it were expanded into: + +```hlsl +{ + let = ; + +} +{ + let = + 1; + +} +... +{ + let = - 1; + +} +``` diff --git a/docs/language-reference/07-declarations.md b/docs/language-reference/07-declarations.md new file mode 100644 index 000000000..f55712265 --- /dev/null +++ b/docs/language-reference/07-declarations.md @@ -0,0 +1,771 @@ +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 decalrations (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`: + +```hlsl +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: + +```hlsl +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: +> +> ```hlsl +> // 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 `import`able module. + +Variables +--------- + +Variables are declared using the keywords `let` and `var`: + +```hlsl +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 afte the variable name and a colon (`:`): + +```hlsl +let x : int = 7; +var y : float = 9.0; +``` + +If no type is spefified 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. + +### Tradtional Syntax + +Variables may also be declared with traditional C-style syntax: + +```hlsl +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 include 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 acessor, 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 scoep 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 marke 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: + +```hlsl +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: + +```hlsl +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: + +```hlsl +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-refernece 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 (`;`): + +```hlsl +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: + +```hlsl +func modify(x: in out int) { x++; } +``` + + +### Traditional Syntax + +Functions can also be declared with traditional C-style syntax: + +```hlsl +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: + +```hlsl +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 instaces 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 +{ .... } +``` + +Currently only interface types may be named in the inheritance clause of a structure type. +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. + +> Note: A future version of Slang may allow a structure type to inherit from another structure type. + +### Syntax Details + +A structure declaration does *not* need to be terminated with a semicolon: + +```hlsl +// A terminating semicolon is allowed +struct Stuff { ... }; + +// The semicolon is not required +struct Things { ... } +``` + +When a structure deeclarations ends without a semicolon, the closing curly brace (`}`) must be the last non-comment, non-whitespace token on its line. + +For compatiblity with C-style code, a structure type declaration may be used as the type specifier in a traditional-style variable declaration: + +```hlsl +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: + +```hlsl +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 decalration elides an initial-value expresison, 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: + +```hlsl +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: + +```hlsl +int r = Color.Red; +``` + +Values of the tag type can be explicitly converted to the enumeration type: + +```hlsl +Color red = Color(r); +``` + +Type Aliases +------------ + +A type alias is declared using the `typealias` keyword: + +```hlsl +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: + +```hlsl +typedef int Height; +``` + +Constant Buffers and Texture Buffers +------------------------------------ + +As a compatiblity feature, the `cbuffer` and `tbuffer` keywords can be used to introduce variable declarations. + +A declaration of the form: + +```hlsl +cbuffer Name +{ + F field; + // ... +} +``` + +is equivalent to a declaration of the form: + +```hlsl +struct AnonType +{ + F field; + // ... +} +__transparent ConstantBuffer 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` used instead of `ConstantBuffer`. + +Interfaces +---------- + +An interface is declared using the `interface` keyword: + +```hlsl +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: + +```hlsl +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`: + +```hlsl +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 interaces of the associated type. + +Initializers +------------ + +An initializer declaration is introduced with the `__init` keyword: + +```hlsl +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: + +```hlsl +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: + +```hlsl +MyVector v = ...; +float f = v[0]; +``` + +A subscript declaration lists one or more parameters inside parantheses, 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: + +```hlsl +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 teh 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: + +```hlsl +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-library code. +The members of an extension are only visible inside of modules that `import` the module delcaring the extension; +extension members are *not* automatically visible wherever the type being extended is visible. + +An extension declaration may include an inheritance clause: + +```hlsl +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 `<>`: + +```hlsl +T myFunction(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 `=`: + +```hlsl +T anotherFunction(vector 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 `<>`: + +```hlsl +anotherFunction +``` + +Specialization produces a reference to the declaration with all generic parameters bound to concrete arguments. + +When specializing a generic, generic type parameters mus 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 funciton, type, etc. is expected: + +```hlsl +int i = anotherFunction( 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: + +```hlsl +int i = anotherFunction( int3(99) ); +``` + +The compiler will infer the generic arguments `` 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 value); +funct genericFunction(value: T) -> T; + +__init(T value); + +__subscript(T value) -> X { ... } + +struct GenericType +{ + T field; +} + +interface IGenericInterface : IBase +{ +} +``` + +> Note: Currently there is no user-exposed syntax for writing a generic extension. diff --git a/docs/language-reference/08-attributes.md b/docs/language-reference/08-attributes.md new file mode 100644 index 000000000..4098303cf --- /dev/null +++ b/docs/language-reference/08-attributes.md @@ -0,0 +1,4 @@ +Attributes +========== + +> Note: This section is not yet complete. diff --git a/docs/language-reference/README.md b/docs/language-reference/README.md new file mode 100644 index 000000000..9ed3c963d --- /dev/null +++ b/docs/language-reference/README.md @@ -0,0 +1,14 @@ +Slang Language Reference +======================== + +Contents +-------- + +* [1 - Introduction](01-introduction.md) +* [2 - Lexical Structure](02-lexical-structure.md) +* [3 - Preprocessor](03-preprocessor.md) +* [4 - Types](04-types.md) +* [5 - Expressions](05-expressions.md) +* [6 - Statements](06-statements.md) +* [7 - Declarations](07-declarations.md) +* [8 - Attributes](08-attributes.md) -- cgit v1.2.3