Skip to content

Nullability: conversion behavior #1242

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

Open
wants to merge 4 commits into
base: draft-v8
Choose a base branch
from
Open
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
137 changes: 130 additions & 7 deletions standard/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,8 @@

**The remainder of this subclause is conditionally normative.**

#### §flow-analysis Flow analysis

A compiler that generates diagnostic warnings conforms to these rules.

Every expression has one of three ***null state***s:
Expand Down Expand Up @@ -925,10 +927,10 @@
> int length = p.Length; // Warning: p is maybe null
>
> string s = p; // No warning. p is not null
>
>
> if (s != null)
> {
> int l2 = s.Length; // No warning. s is not null
> int l2 = s.Length; // No warning. s is not null
> }
> int l3 = s.Length; // Warning. s is maybe null
> }
Expand All @@ -946,13 +948,13 @@
> public void M(string s)
> {
> int length = s.Length; // No warning. s is not null
>

Check warning on line 951 in standard/types.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/types.md#L951

MDC032::Line length 87 > maximum 81
> _ = s == null; // Null check by testing equality. The null state of s is maybe null
> length = s.Length; // Warning, and changes the null state of s to not null
>

Check warning on line 954 in standard/types.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/types.md#L954

MDC032::Line length 90 > maximum 81
> _ = s?.Length; // The ?. is a null check and changes the null state of s to maybe null
> if (s.Length > 4) // Warning. Changes null state of s to not null
> {

Check warning on line 957 in standard/types.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/types.md#L957

MDC032::Line length 87 > maximum 81
> _ = s?[4]; // ?[] is a null check and changes the null state of s to maybe null
> _ = s.Length; // Warning. s is maybe null
> }
Expand Down Expand Up @@ -998,13 +1000,13 @@
> {
> get
> {
> string tmp = _field;
> _field = null;
> return tmp;
> string tmp = _field;
> _field = null;
> return tmp;
> }
> set
> {
> _field = value;
> _field = value;
> }
> }
>
Expand All @@ -1012,7 +1014,7 @@
> {
> var t = new Test();
> if (t.DisappearingProperty != null)
> {

Check warning on line 1017 in standard/types.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/types.md#L1017

MDC032::Line length 110 > maximum 81
> int len = t.DisappearingProperty.Length; // No warning. A compiler can assume property is stateful
> }
> }
Expand All @@ -1031,7 +1033,7 @@
> public class C
> {
> private C? child;
>
>
> public void M()
> {
> _ = child.child.child; // Warning. Dereference possible null value
Expand All @@ -1042,4 +1044,125 @@
>
> *end example*

#### §type-conversions Type conversions

A compiler that generates diagnostic warnings conforms to these rules.

> *Note:* Differences in top-level or nested nullability annotations in types do not affect whether conversion between the types is permitted, since there is no semantic difference between a non-nullable reference type and its corresponding nullable type ([§8.9.1](types.md#891-general)).

A compiler may issue a warning when nullability annotations differ between two types, either top-level or nested, when the conversion is narrowing.

> *Example*: Types differing in top-level annotations
>
> <!-- Example: {template:"code-in-class-lib", name:"TopLevelNullabilityConversionWarnings", ignoredWarnings:["CS8600"]} -->
> ```csharp
> #nullable enable
> public class C
> {
> public void M1(string p)
> {
> _ = (string?)p; // No warning, widening
> }
>
> public void M2(string? p)
> {
> _ = (string)p; // Warning, narrowing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following the resolution of #1309 at today’s meeting you might want to make similar changes to the comments, here and elsewhere, to echo the “may issue” on line 1053 (in #1309 “possible warning” was chosen)

> _ = (string)p!; // No warning, suppressed
> }
> }
> ```
>
> *end example*
<!-- markdownlint-disable MD028 -->

<!-- markdownlint-enable MD028 -->
> *Example*: Types differing in nested nullability annotations
>
> <!-- Example: {template:"code-in-class-lib", name:"NestedNullabilityConversionWarnings", ignoredWarnings:["CS8619"]} -->
> ```csharp
> #nullable enable
> public class C
> {
> public void M1((string, string) p)
> {
> _ = ((string?, string?))p; // No warning, widening
> }
>
> public void M2((string?, string?) p)
> {
> _ = ((string, string))p; // Warning, narrowing
> _ = ((string, string))p!; // No warning, suppressed
> }
> }
> ```
>
> *end example*

A compiler may follow rules for interface variance ([§18.2.3.3](interfaces.md#18233-variance-conversion)), delegate variance ([§20.4](delegates.md#204-delegate-compatibility)), and array covariance (§17.6) in determining whether to issue a warning for type conversions.

> <!-- Example: {template:"code-in-class-lib", name:"NullVariance", ignoredWarnings:["CS8619"]} -->
> ```csharp
> #nullable enable
> public class C
> {
> public void M1(IEnumerable<string> p)
> {
> IEnumerable<string?> v1 = p; // No warning
> }
>
> public void M2(IEnumerable<string?> p)
> {
> IEnumerable<string> v1 = p; // Warning
> IEnumerable<string> v2 = p!; // No warning
> }
>
> public void M3(Action<string?> p)
> {
> Action<string> v1 = p; // No warning
> }
>
> public void M4(Action<string> p)
> {
> Action<string?> v1 = p; // Warning
> Action<string?> v2 = p!; // No warning
> }
>
> public void M5(string[] p)
> {
> string?[] v1 = p; // No warning
> }
>
> public void M6(string?[] p)
> {
> string[] v1 = p; // Warning
> string[] v2 = p!; // No warning
> }
> }
> ```
>
> *end example*

A compiler may issue a warning when nullability differs in either direction in types which do not permit a variant conversion.

> <!-- Example: {template:"code-in-class-lib", name:"NullInvariance", ignoredWarnings:["CS8619"]} -->
> ```csharp
> #nullable enable
> public class C
> {
> public void M1(List<string> p)
> {
> List<string?> v1 = p; // Warning
> List<string?> v2 = p!; // No warning
> }
>
> public void M2(List<string?> p)
> {
> List<string> v1 = p; // Warning
> List<string> v2 = p!; // No warning
> }
> }
> ```
>
> *end example*

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