Skip to content
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

Add more NRT examples #1225

Open
wants to merge 9 commits into
base: draft-v8
Choose a base branch
from
Open
Changes from 4 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
87 changes: 79 additions & 8 deletions standard/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -891,9 +891,9 @@ A diagnostic can be produced when a variable ([§9.2.1](variables.md#921-general
> }
> }
> ```
>
> The compiler may issue a warning where the parameter that might be null is assigned to a variable that should not be null. If the parameter is null-checked before assignment, the compiler may use that in its nullable state analysis and not issue a warning:
>

The compiler may issue a warning where the parameter that might be null is assigned to a variable that should not be null. If the parameter is null-checked before assignment, the compiler may use that in its nullable state analysis and not issue a warning:

> <!-- Example: {template:"code-in-class-lib", name:"NullChecked"} -->
> ```csharp
> #nullable enable
Expand Down Expand Up @@ -936,15 +936,64 @@ The compiler can update the null state of a variable as part of its analysis.
> }
> ```
>
> In the previous example, the compiler may decide that after the statement `int length = p.Length;`, the null-state of `p` is not-null. If it were null, that statement would have thrown a `NullReferenceException`. This is similar to the behavior if the code had been preceded by `if (p == null) throw NullReferenceException();` except that the code as written may produce a warning, the purpose of which is to warn that an exception may be thrown implicitly.
> In the previous example, the compiler may decide that after the statement `int length = p.Length;`, the null-state of `p` is not-null. If it were null, that statement would have thrown a `NullReferenceException`. This is similar to the behavior if the code had been preceded by `if (p == null) throw NullReferenceException();` except that the code as written may produce a warning, the purpose of which is to warn that an exception may be thrown implicitly. *end example*

Later in the method, the code checks that `s` is not a null reference. The null-state of `s` can change to maybe null after the null-checked block closes. The compiler can infer that `s` is maybe null because the code was written to assume that it might have been null. Generally, when the code contains a null check, the compiler may infer that the value might have been null.*end example*
Later in the method, the code checks that `s` is not a null reference. The null-state of `s` can change to maybe null after the null-checked block closes. The compiler can infer that `s` is maybe null because the code was written to assume that it might have been null. Generally, when the code contains a null check, the compiler may infer that the value might have been null:

> *Example*: Each of the following expressions include some form of a null check. The null-state of `o` can change from not null to maybe null after each of these statements:
>
> <!-- Example: {template:"code-in-class-lib", name:"NullChecks", expectedWarnings:["CS8600","CS8602", "CS8602", "CS8602"]} -->
> ```csharp
> #nullable enable
> public void M(string s)
> {
> // s is not null:
> int length = s.Length;
>
> _ = s = null; // Null check by testing equality. The null state of o is maybe null
BillWagner marked this conversation as resolved.
Show resolved Hide resolved
BillWagner marked this conversation as resolved.
Show resolved Hide resolved
> length = s.Length; // warning, and changes the null state of o to not null
BillWagner marked this conversation as resolved.
Show resolved Hide resolved
>
> _ = s?.Length; // The ?. is a null check and changes the null state of o to maybe null
> if (s.Length > 4) // Warning. Changes null state of o to not null
> {
> _ = s?[4]; //is a null check and changes the null state of o to maybe null
BillWagner marked this conversation as resolved.
Show resolved Hide resolved
> _ = s.Length; // warning
> }
> }
> ```
BillWagner marked this conversation as resolved.
Show resolved Hide resolved
<!-- markdownlint-disable MD028 -->

<!-- markdownlint-enable MD028 -->
> *Example*: The compiler can treat a property ([§15.7](classes.md#157-properties)) as either a variable with state, or as independent get and set accessors ([§15.7.3](classes.md#1573-accessors)). In other words, a compiler can choose whether writing to a property changes the null state of reading the property, or if reading a property changes the null state of that property.
Both auto-property and field like event declarations make use of a compiler generated backing field. Null state may assume that assignment to the event or property as assignment to the compiler generated backing field.

> *Example*: A compiler can determine that writing an auto-property or field like event writes the corresponding compiler generated backing field. The null state of the property matches that of the backing field.
BillWagner marked this conversation as resolved.
Show resolved Hide resolved
>
> <!-- Example: {template:"standalone-console", name:"NullPropertyAnalysis", expectedException:"NullReferenceException"} -->
> ```csharp
> class Test
> {
> public string P
> {
> get;
> set;
> }
>
> public Test() {} // Warning. "P" not set to a non-null value.
>
> static void Main()
> {
> var t = new Test();
> int len = t.P.Length; // No warning. null state is not null
BillWagner marked this conversation as resolved.
Show resolved Hide resolved
> }
> }
> ```
>
> <!-- Example: {template:"standalone-console", name:"NullPropertyAnalysis"} -->
> In the previous example, the backing field for the `DisappearingProperty` is set to null when it is read. However, a compiler may assume that reading a property doesn’t change the null state of that expression. *end example*
BillWagner marked this conversation as resolved.
Show resolved Hide resolved

The compiler can treat a property ([§15.7](classes.md#157-properties)) as either a variable with state, or as independent get and set accessors ([§15.7.3](classes.md#1573-accessors)).

> *Example*: A compiler can choose whether writing to a property changes the null state of reading the property, or if reading a property changes the null state of that property.
>
> <!-- Example: {template:"standalone-console", name:"NullAutoPropertyAnalysis", ignoredWarnings:["CS8602"]} -->
> ```csharp
> class Test
> {
Expand Down Expand Up @@ -976,4 +1025,26 @@ Later in the method, the code checks that `s` is not a null reference. The null-
>
> In the previous example, the backing field for the `DisappearingProperty` is set to null when it is read. However, a compiler may assume that reading a property doesn’t change the null state of that expression. *end example*

A compiler may use any expression that dereferences a variable, property, or event to set the null state to not null. If it were null, the dereference expression would have thrown a `NullReferenceException`:
BillWagner marked this conversation as resolved.
Show resolved Hide resolved

> *Example*:
>
> <!-- Example: {template:"standalone-lib-without-using", name:"ChainedAccess", ignoredWarnings:["CS0649", "CS8602"]} -->
> ```csharp
>
> public class C
> {
> private C? child;
>
> public void M()
> {
> _ = child.child.child; // Warning dereference possible null value
> var greatGrandChild = child.child.child; // No warning
>
> }
> }
> ```
>
> *end example*

***End of conditionally normative text***
Loading