-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Adjust the way nullable annotations are represented in metadata #31212
Changes from all commits
934f8da
68101a3
89ad74c
60ce669
6fa2447
98df03c
6160520
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 |
---|---|---|
|
@@ -8,7 +8,7 @@ In source, nullable reference types are annotated with `?`. | |
string? OptString; // may be null | ||
Dictionary<string, object?>? OptDictionaryOptValues; // dictionary may be null, values may be null | ||
``` | ||
A warning is reported when annotating a reference type or unconstrained generic type with `?` outside a `NonNullTypes(true)` context. | ||
A warning is reported when annotating a reference type with `?` outside a `#nullable` context. | ||
|
||
In metadata, nullable reference types are annotated with a `[Nullable]` attribute. | ||
```c# | ||
|
@@ -22,54 +22,46 @@ namespace System.Runtime.CompilerServices | |
AllowMultiple = false)] | ||
public sealed class NullableAttribute : Attribute | ||
{ | ||
public NullableAttribute() { } | ||
public NullableAttribute(bool[] b) { } | ||
public NullableAttribute(byte b) { } | ||
public NullableAttribute(byte[] b) { } | ||
} | ||
} | ||
``` | ||
The parameter-less constructor is emitted for simple type references with top-level nullability and for type parameter definitions that have a `class?` constraint; | ||
the constructor with `bool[]` parameter is emitted for type references with nested types and nullability. | ||
|
||
Each type reference is accompanied by a NullableAttribute with an array of bytes, where 0 is Oblivious, 1 is NotAnnotated and 2 is Annotated. | ||
All value types are marked with flag 0 (oblivious). | ||
|
||
To optimize trivial cases the attribute can be omitted, or instead can be replaced with an attribute that takes a single byte value rather than an array. | ||
|
||
Trivial/optimized cases: | ||
1) All parts are NotAnnotated � a NullableAttribute with a single value 1 (rather than an array of 1s) | ||
2) All parts are Annotated - a NullableAttribute with a single value 2 (rather than an array of 2s) | ||
3) All parts are Oblivious � the attribute is omitted, this matches how we interpret the lack of an attribute in legacy assemblies. | ||
For completeness, we would also recognize a NullableAttribute with a single value 0 (rather than an array of 0s), | ||
but compiler will never emit an attribute like this. | ||
|
||
NullableAttribute(1) should be placed on a type parameter definition that has a `class!` constraint. | ||
NullableAttribute(2) should be placed on a type parameter definition that has a `class?` constraint. | ||
Other forms of NullableAttribute are not emitted on type parameter definitions and are not specially recognized on them. | ||
|
||
The `NullableAttribute` type declaration is synthesized by the compiler if it is not included in the compilation, but is needed to produce the output. | ||
|
||
```c# | ||
// C# representation of metadata | ||
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 would be good to expand this section with an example involving value types ( |
||
[Nullable] | ||
[Nullable(2)] | ||
string OptString; // string? | ||
[Nullable(new[] { true, false, true })] | ||
[Nullable(new[] { 2, 1, 2 })] | ||
Dictionary<string, object> OptDictionaryOptValues; // Dictionary<string!, object?>? | ||
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.
|
||
string[] Oblivious1; // string~[]~ | ||
[Nullable(0)] string[] Oblivious2; // string~[]~ | ||
[Nullable(new[] { 0, 0 })] string[] Oblivious3; // string~[]~ | ||
[Nullable(1)] string[] NotNull1; // string![]! | ||
[Nullable(new[] { 1, 1 })] string[] NotNull2; // string![]! | ||
[Nullable(new[] { 0, 2 })] string[] ObliviousMaybeNull; // string?[]~ | ||
[Nullable(new[] { 1, 2 })] string[] NotNullMaybeNull; // string?[]! | ||
int Int; // int | ||
Nullable<int> NullableInt1; // Nullable<int> | ||
``` | ||
The `NullableAttribute` type declaration is synthesized by the compiler if it is not included in the compilation. | ||
|
||
Unannotated reference types are non-nullable or null-oblivious depending on whether the containing scope includes `[NonNullTypes]`. | ||
```c# | ||
namespace System.Runtime.CompilerServices | ||
{ | ||
[AttributeUsage(AttributeTargets.Class | | ||
AttributeTargets.Constructor | | ||
AttributeTargets.Delegate | | ||
AttributeTargets.Enum | | ||
AttributeTargets.Event | | ||
AttributeTargets.Field | | ||
AttributeTargets.Interface | | ||
AttributeTargets.Method | | ||
AttributeTargets.Module | | ||
AttributeTargets.Property | | ||
AttributeTargets.Struct, | ||
AllowMultiple = false)] | ||
internal sealed class NonNullTypesAttribute : Attribute | ||
{ | ||
public NonNullTypesAttribute(bool enabled = true) { } | ||
} | ||
} | ||
``` | ||
If there is no `[NonNullTypes]` attribute at any containing scope, including the module, reference types are null-oblivious. | ||
```c# | ||
[NonNullTypes(false)] string[] Oblivious; // string~[]~ | ||
[NonNullTypes(true)] string[] NotNull; // string![]! | ||
[NonNullTypes(false), Nullable(new[] { false, true })] string[] ObliviousMaybeNull; // string?[]~ | ||
[NonNullTypes(true), Nullable(new[] { false, true })] string[] NotNullMaybeNull; // string?[]! | ||
``` | ||
The `NonNullTypesAttribute` is always implicitly included in a compilation, but is only emitted if it is referenced in source. It cannot be referenced from metadata (assembly or module). | ||
`NonNullTypesAttribute` can only be used in C# 8.0 compilations (or above). | ||
|
||
## Declaration warnings | ||
_Describe warnings reported for declarations in initial binding._ | ||
|
||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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.
Not blocking this PR: we'll need to think about how the values of the parameters will be stored in the fields of NullableAttribute. Jared mentioned we need to store those values so that they are accessible from reflection.
Tracked by #30143 #Resolved