Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion standard/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2547,7 +2547,9 @@ A *this_access* is permitted only in the *block* of an instance constructor, an
- When `this` is used in a *primary_expression* within an instance constructor of a class, it is classified as a value. The type of the value is the instance type ([§15.3.2](classes.md#1532-the-instance-type)) of the class within which the usage occurs, and the value is a reference to the object being constructed.
- When `this` is used in a *primary_expression* within an instance method or instance accessor of a class, it is classified as a value. The type of the value is the instance type ([§15.3.2](classes.md#1532-the-instance-type)) of the class within which the usage occurs, and the value is a reference to the object for which the method or accessor was invoked.
- When `this` is used in a *primary_expression* within an instance constructor of a struct, it is classified as a variable. The type of the variable is the instance type ([§15.3.2](classes.md#1532-the-instance-type)) of the struct within which the usage occurs, and the variable represents the struct being constructed.
- If the constructor declaration has no constructor initializer, the `this` variable behaves exactly the same as an output parameter of the struct type. In particular, this means that the variable shall be definitely assigned in every execution path of the instance constructor.
- If the constructor declaration has no constructor initializer, the `this` variable behaves similarly to an output parameter of the struct type, except that it is not an error when the definite assignment requirements ([§9.4.1](variables.md#941-general)) are not met. Instead, the following occurs:
- When the `this` variable itself does not meet the requirements, then all unassigned instance variables within `this` at all points where requirements are violated are implicitly initialized to the default value ([§9.3](variables.md#93-default-values)) in an *initialization* phase before any other code in the constructor runs.
- When an instance variable *v* within `this` does not meet the requirements, or any instance variable at any level of nesting within *v* does not meet the requirements, then *v* is implicitly initialized to the default value in an *initialization* phase before any other code in the constructor runs.
- Otherwise, the `this` variable behaves exactly the same as a `ref` parameter of the struct type. In particular, this means that the variable is considered initially assigned.
- When `this` is used in a *primary_expression* within an instance method or instance accessor of a struct, it is classified as a variable. The type of the variable is the instance type ([§15.3.2](classes.md#1532-the-instance-type)) of the struct within which the usage occurs.
- If the method or accessor is not an iterator ([§15.15](classes.md#1515-synchronous-and-asynchronous-iterators)) or async function ([§15.14](classes.md#1514-async-functions)), the `this` variable represents the struct for which the method or accessor was invoked.
Expand Down
33 changes: 12 additions & 21 deletions standard/structs.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,18 +416,7 @@ Parameters of the primary constructor as well as members of the record struct ar

A warning shall be produced if a parameter of the primary constructor is not read.

The definite assignment rules for struct instance constructors apply to the primary constructor of record structs. For instance, the following is an error:

> <!-- Example: {template:"standalone-lib", name:"RecordStructPrimaryConstructor2", expectedErrors:["CS0171","CS8050"]} -->
> ```csharp
> record struct Pos(int X) // def assignment error in primary constructor
> {
> private int x;
> public int X {
> get { return x; } set { x = value; }
> } = X;
> }
> ```
The definite assignment rules for struct instance constructors ([§16.5.9](structs.md#1659-constructors), [§12.8.14](expressions.md#12814-this-access)) apply to the primary constructor of record structs. As for any other struct instance constructor without a `this()` initializer, any instance field that is not definitely assigned by the primary constructor is implicitly initialized to its default value in the initialization phase that runs before the body of the primary constructor.

#### 16.4.4.3 Properties

Expand Down Expand Up @@ -785,15 +774,15 @@ An explicitly declared parameterless instance constructor shall have public acce

A struct instance constructor is not permitted to include a constructor initializer of the form `base(`*argument_list*`)`, where *argument_list* is optional. The execution of an instance constructor shall not result in the execution of a constructor in the struct’s base type `System.ValueType`.

The `this` parameter of a struct instance constructor corresponds to an output parameter of the struct type. As such, `this` shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) at every location where the constructor returns. Similarly, it cannot be read (even implicitly) in the constructor body before being definitely assigned.
The `this` parameter of a struct instance constructor behaves similarly to an output parameter of the struct type, except that when the definite assignment requirements ([§9.4.1](variables.md#941-general)) for `this` (or for instance variables within `this`) are not met at a location where they would otherwise be required, that does not result in a compile-time error. Instead, the unassigned variables are implicitly initialized to the default value ([§9.3](variables.md#93-default-values)) in an *initialization* phase before any other code in the constructor runs, as described in [§12.8.14](expressions.md#12814-this-access).

If the struct instance constructor specifies a constructor initializer, that initializer is considered a definite assignment to this that occurs prior to the body of the constructor. Therefore, the body itself has no initialization requirements.
If the struct instance constructor specifies a constructor initializer, that initializer is considered a definite assignment to `this` that occurs prior to the body of the constructor. Therefore, the body itself has no initialization requirements.

Instance fields (other than `fixed` fields) shall be definitely assigned in struct instance constructors that do not have a `this()` initializer.
For a struct instance constructor that does not have a `this()` initializer, any instance field (other than a `fixed` field) that is not definitely assigned at every location where the constructor returns, or that has not yet been definitely assigned at a location where the value of `this` or of that field is read, is implicitly initialized to its default value in the *initialization* phase described in [§12.8.14](expressions.md#12814-this-access).

> *Example*: Consider the instance constructor implementation below:
>
> <!-- Example: {template:"standalone-lib-without-using", name:"Constructors2", expectedErrors:["CS0188"]} -->
> <!-- Example: {template:"standalone-lib-without-using", name:"Constructors2"} -->
> ```csharp
> struct Point
> {
Expand All @@ -811,14 +800,16 @@ Instance fields (other than `fixed` fields) shall be definitely assigned in stru
>
> public Point(int x, int y)
> {
> X = x; // error, this is not yet definitely assigned
> Y = y; // error, this is not yet definitely assigned
> X = x; // ok; x is implicitly initialized to its default value
> // before the body runs, so calling the X setter is allowed
> Y = y; // ok, for the same reason
> }
> }
> ```
>
> No instance function member (including the set accessors for the properties `X` and `Y`) can be called until all fields of the struct being constructed have been definitely assigned. Note, however, that if `Point` were a class instead of a struct, the instance constructor implementation would be permitted.
> There is one exception to this, and that involves automatically implemented properties ([§15.7.4](classes.md#1574-automatically-implemented-properties)). The definite assignment rules ([§12.24.2](expressions.md#12242-simple-assignment)) specifically exempt assignment to an auto-property of a struct type within an instance constructor of that struct type: such an assignment is considered a definite assignment of the hidden backing field of the auto-property. Thus, the following is allowed:
> Because the instance fields `x` and `y` are not definitely assigned before the calls to the property setters, they are implicitly initialized to their default values in the initialization phase before the constructor body runs. The set accessors for `X` and `Y` may therefore be invoked even though the constructor body itself does not first assign `x` and `y`.
>
> Automatically implemented properties ([§15.7.4](classes.md#1574-automatically-implemented-properties)) interact with these rules via the hidden backing field. The definite assignment rules ([§12.24.2](expressions.md#12242-simple-assignment)) specifically treat assignment to an auto-property of a struct type within an instance constructor of that struct type as a definite assignment of the hidden backing field of the auto-property. Thus, the following is also allowed:
>
> <!-- Example: {template:"standalone-lib-without-using", name:"Constructors3"} -->
> ```csharp
Expand Down Expand Up @@ -871,7 +862,7 @@ For a property accessor expression, `s.P`:

Automatically implemented properties ([§15.7.4](classes.md#1574-automatically-implemented-properties)) use hidden backing fields, which are only accessible to the property accessors.

> *Note*: This access restriction means that constructors in structs containing automatically implemented properties often need an explicit constructor initializer where they would not otherwise need one, to satisfy the requirement of all fields being definitely assigned before any function member is invoked or the constructor returns. *end note*
> *Note*: Because the backing field of an auto-property of a struct type is implicitly initialized to its default value in the initialization phase of an instance constructor that does not assign it ([§12.8.14](expressions.md#12814-this-access)), an explicit constructor initializer is not required in order to satisfy the definite-assignment rules for that backing field. *end note*

### 16.5.12 Methods

Expand Down
4 changes: 2 additions & 2 deletions standard/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ Definite assignment is a requirement in the following contexts:
> *Note*: This ensures that the function member being invoked can consider the input parameter initially assigned. *end note*
- All output parameters of a function member shall be definitely assigned at each location where the function member returns (through a return statement or through execution reaching the end of the function member body).
> *Note*: This ensures that function members do not return undefined values in output parameters, thus enabling a compiler to consider a function member invocation that takes a variable as an output parameter equivalent to an assignment to the variable. *end note*
- The `this` variable of a *struct_type* instance constructor shall be definitely assigned at each location where that instance constructor returns.
- The `this` variable of a *struct_type* instance constructor that has a constructor initializer shall be definitely assigned at each location where that instance constructor returns. For a *struct_type* instance constructor that does not have a constructor initializer, see [§12.8.14](expressions.md#12814-this-access): unassigned instance variables of `this` do not produce a compile-time error, but are instead implicitly initialized to their default values before the body of the constructor runs.

### 9.4.2 Initially assigned variables

Expand All @@ -262,7 +262,7 @@ The following categories of variables are classified as initially assigned:
The following categories of variables are classified as initially unassigned:

- Instance variables of initially unassigned struct variables.
- Output parameters, including the `this` variable of struct instance constructors without a constructor initializer.
- Output parameters, including the `this` variable of struct instance constructors without a constructor initializer (see [§12.8.14](expressions.md#12814-this-access) for the consequences of failing to satisfy the definite-assignment requirement for such a `this` variable).
- Local variables, except those declared in a `catch` clause or a `foreach` statement.

### 9.4.4 Precise rules for determining definite assignment
Expand Down
Loading