-
Notifications
You must be signed in to change notification settings - Fork 255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Design proposal for initialization. #5142
Changes from 1 commit
1890ea0
47ecaeb
2061ca9
3a1c156
bcae035
6fa4495
48704f5
6207aa2
ecb8066
e1f43f8
4783b00
4a1d918
63238b4
0eac31f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
SP #004: Initialization | ||
================= | ||
|
||
This proposal documents the desired behavior of initialization related language semantics, including defualt constructor, initialization list and variable initialization. | ||
|
||
Status | ||
------ | ||
|
||
Status: Design Review | ||
|
||
Implemtation: N/A | ||
|
||
Author: Yong He | ||
|
||
Reviewer: | ||
|
||
Background | ||
---------- | ||
|
||
Slang has introduced several different syntax around initialization to provide syntactic compatibility with HLSL/C++. As the language evolve, there aree many corners where | ||
the semantics around initialization are not well-defined, and causing confusion or leading to surprising behaviors. | ||
|
||
This proposal attempts to provide a design on where we want to language to be in turns of how initialization is handled in all different places. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. want to language -> want the language There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "in turns of" ? is "in terms of"? |
||
|
||
Related Work | ||
------------ | ||
|
||
C++ has many different ways and syntax to initialize an object: through explicit constructor calls, initialization list, or implicitly in a member/variable declaration. | ||
A variable in C++ can also be in an uninitialized state after its declaration. HLSL inherits most of these behvior from C++ by allowing variables to be uninitialized. | ||
|
||
On the other hand, languages like C# and Swift has a set of well defined rules to ensure every variable is initialized after its declaration. | ||
|
||
Proposed Approach | ||
----------------- | ||
|
||
The high level direction for Slang is it needs to help ensuring all decls are properly initialized at its declaration site, while still providing a reasonable level | ||
of backward compatibility with existing Slang/HLSL code. In this section, we document all concepts and rules to achieve this goal. | ||
|
||
## `IDefaultInitializable` interface | ||
|
||
The builtin `IDefaultInitializable` interface is defined as: | ||
```csharp | ||
interface IDefaultInitializable | ||
{ | ||
__init(); | ||
} | ||
``` | ||
|
||
Any type that conforms to `IDefaultInitializable` is treated as having the *default-initializable* property in the rest of the discussions. By default, all builtin | ||
types, such as `int`, `float[]`, `float4` are default-initializable. The values for builtin numeric types after default-initialization are 0. | ||
The default value for pointers is `nullptr`, and default value for `Optional<T>` is `none`. | ||
|
||
## Default-Initializable Type | ||
|
||
A type X is default initializable if: | ||
- It explicitly declares that `X` implements `IDefaultInitializable`. | ||
- It explicitly provides a default constructor `X::__init()` that takes no arguments, in which case we treat the type as implementing `IDefaultInitializable` even if | ||
this conformance isn't explicitly declared. | ||
- It is a struct type where all its members are default-initializable. A member is considered default-initializable if the type of the member is default-initializable, | ||
or if the member has an initialization expression that defines its default value. | ||
- It is a sized-array type where the element type is default-initializable. | ||
- It is a tuple type where all element types are default-initializable. | ||
- It is an `Optional<T>` type for any `T`. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about the type defined in shader resource type, e.g.: cbuffer Uniforms is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is considered default-initializable, although it will have no meaning since the location is in a read-only position. |
||
This means that an unsized-array type or an existential type, or any composite type that contains such types without providing an explicit default constructor | ||
are not considered default-initializable. | ||
|
||
|
||
## Variable Initialization | ||
|
||
If the type of a local variable is default-initializable, then its default initializer will be invoked at its declaration site implicitly to intialize its value: | ||
```c++ | ||
int x; // x will be default initialized to 0 because `int` is default-initializable. | ||
// The above is equivalent to: | ||
int x = int(); | ||
|
||
struct S { int x; int y; } | ||
S s; // s will be default initialized to {0, 0} because `S` is default-initializable. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add few sentences to make it clear that
And I have a question: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind, I see you you covered question this later. |
||
``` | ||
|
||
If a type is not default-initializable, and the declaration site does not provide an intial value for the variable, the compiler should generate an error: | ||
```csharp | ||
struct V { int[] arr; } | ||
|
||
V v; // error: `v` must be initialized at declaration. | ||
``` | ||
|
||
For backward compatibility, we will introduce a compiler option to turn this error into a warning, but we may deprecate this option in the future. | ||
|
||
## Generic Type Parameter | ||
|
||
A generic type parameter is not considered default-initializable by-default. As a result, the following code should produce error: | ||
```csharp | ||
void foo<T>() | ||
{ | ||
T t; // error, `t` is uninitialized. | ||
} | ||
``` | ||
|
||
## Automatic Synthesis of Default-Initializer | ||
|
||
If a `struct` type is determined to be default-initializable but a default constructor isn't explicitly provided by the user, the Slang compiler should | ||
synthesize such a constructor for the type. The synthesis logic should be recursively invoke defualt initialization on all members. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo: defualt -> default. Suggestion: |
||
|
||
## Automatic Synthesis of `IDefaultInitializable` Conformance | ||
|
||
If a `struct` type provides an explicit default constructor, the compiler should automatically add `IDefaultIinitializable` to the conformance list of | ||
the type, so it can be used for any generic parameters constrained on `IDefaultInitializble`. | ||
|
||
## Initialization List | ||
|
||
Slang allows initialization of a variable by assigning it with an initialization list. | ||
Generally, Slang will always try to resolve initialization list coercion as if it is an explicit constructor invocation. | ||
For example, given: | ||
``` | ||
S obj = {1,2}; | ||
``` | ||
Slang will try to convert the code into: | ||
``` | ||
S obj = S(1,2); | ||
``` | ||
|
||
As a special case, an empty initializer list will translate into a default-initialization: | ||
```csharp | ||
S obj = {}; | ||
// equivalent to: | ||
S obj = S(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By knowing this, I think the PR makes a mistake. Because it treats these two differently. That's why there are both |
||
``` | ||
|
||
If the above code passes type check, then it will be used as the way to initialize `obj`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't it be neater from a typing point of view to just synthesize a constructor which perform this, then we wouldn't need to special case any initializer list application and it would always resolve to a constructor call |
||
|
||
If the above code does not pass type check, Slang continues to check if `S` meets the standard of a "legacy C-style struct` type. | ||
A type is a "legacy C-Style struct" iff: | ||
- It is a struct type. | ||
- It is a basic scalar, vector or matrix type, e.g. `int`, `float4x4`. | ||
- It does not define any explicit constructors | ||
- It does not define any initialization expressions on its members. | ||
- All its members are legacy C-Style structs or arrays of legacy C-style structs. | ||
In such case, we perform a legacy "read data" style consumption of the initializer list, so that the following behavior is valid: | ||
``` | ||
struct Inner { int x; int y; }; | ||
struct Outer { Inner i; Inner j; } | ||
|
||
Outer o = {1, 2, 3, 4}; // Initializes `o` into `{ Inner{1,2}, Inner{3,4} }`. | ||
``` | ||
|
||
### Synthesis of constructors for member initialization | ||
|
||
If a type already defines any explicit constructors, do not synthesize any constructors for initializer list call. An intializer list expression | ||
for the type must exactly match one of the explicitly defined constructors. | ||
|
||
If the type doesn't provide any explicit constructors, the compiler need to synthesis the constructors for the calls that that the intializer | ||
lists translate into, so that an initializer list expression can be used to initialize a variable of the type. | ||
|
||
For each visibilty level `V` in (`private`, `internal`, `public`), we will synthesize one constructor at that visiblity level. | ||
|
||
The signature for the synthesized initializer for type `T` is: | ||
```csharp | ||
V T.__init(member0: typeof(member0) = default(member0), member1 : typeof(member1) = default(member1), ...) | ||
``` | ||
where `V` is the visibilty level, and `(member0, member1, ... memberN)` is the set of members at or above visiblity level `V`, and `default(member0)` | ||
is the value defined by the initialization expression in `member0` if it exist, or the default value of `member0`'s type. | ||
If `member0`'s type is not default initializable and the the member doesn't provide an initial value, then the parameter will not have a default value. | ||
|
||
The body of the constructor will initialize each member with the value comming from the corresponding constructor argument if such argument exists, | ||
otherwise the member will be initialized to its default value either defined by the init expr of the member, or the default value of the type if the | ||
type is default-initializable. If the member type is not default-initializable and a default value isn't provided on the member, then such the constructor | ||
synthesis will fail and the constructor will not be added to the type. Failure to synthesis a constructor is not an error, and an error will appear | ||
if the user is trying to initialize a value of the type in question assuming such a constructor exist. | ||
|
||
|
||
Alternatives Considered | ||
----------------------- | ||
|
||
One important decision point is whether or not Slang should allow variables to be left in uninitialized state after its declaration as it is allowed in C++. | ||
Our opinion is that this is not what we want to have in the long term and Slang should take the opportunity as a new language to not inherit from this | ||
undesired C++ legacy behavior. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: aree -> are