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 NullableContextAttribute #36152

Merged
merged 25 commits into from
Jun 25, 2019
Merged

Add NullableContextAttribute #36152

merged 25 commits into from
Jun 25, 2019

Conversation

cston
Copy link
Member

@cston cston commented Jun 4, 2019

Emit a NullableContextAttribute at each type and method where the byte value is the most common NullableAttribute (and NullableContextAttribute) value within that scope. The redundant NullableAttribute (and NullableContextAttribute) attributes within the scope are dropped.

This is the third part of three changes to address #35888.

@cston cston force-pushed the 35888 branch 3 times, most recently from 9e91a03 to 0b2f0d8 Compare June 4, 2019 18:34

The first case, where all parts are oblivious in an oblivious context, matches the representation in legacy assemblies where there are no `NullaleAttribute` annotations.

`NullableAttribute(1)` should be placed on a type parameter definition that has a `class!` constraint.
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

Consider adding an entry for notnull #Closed


The first case, where all parts are oblivious in an oblivious context, matches the representation in legacy assemblies where there are no `NullaleAttribute` annotations.

`NullableAttribute(1)` should be placed on a type parameter definition that has a `class!` constraint.
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

Are attributes in this list subject to NullableContextAttribute optimizations? If yes, we might need to emit NullableAttribute(0) within some contexts #Closed


`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.
`NullableAttribute(2)` should be placed on a type parameter definition that has no type constraints, `class`, `struct` and `unmanaged` constraints and
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

Consider adjusting this for notnull #Closed

`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.
`NullableAttribute(2)` should be placed on a type parameter definition that has no type constraints, `class`, `struct` and `unmanaged` constraints and
is declared in a context where nullable type annotations are allowed, that is eqivalent to having an `object?` constraint.
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

Typo "eqivalent". #Closed

namespace System.Runtime.CompilerServices
{
[System.AttributeUsage(
AttributeTargets.Assembly |
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

Should be replaced with a Module target instead #Closed

The attribute constructor takes a `bool enabled` argument:
if `true`, reference types are interpreted as not nullable;
if `false`, reference types are interpreted as oblivious.
If there is no `NullableContextAttribute` on the containing member or containing types or assembly,
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

module vs. assembly #Closed


The attribute is not inherited.

The C#8 compiler will recognize `NullableContextAttribute` instances on the assembly, types (top-level and nested types),
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

module vs. assembly #Closed


The C#8 compiler will recognize `NullableContextAttribute` instances on the assembly, types (top-level and nested types),
and members.
`NullableContextAttribute` will be ignored on other targets.
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

Is including other targets as valid in attribute definition safe? I think it is better to disallow the attributes on targets that we do not plan to recognize. #Closed

The C#8 compiler will recognize `NullableContextAttribute` instances on the assembly, types (top-level and nested types),
and members.
`NullableContextAttribute` will be ignored on other targets.
Other forms of `NullableContextAttribute` on the expected targets will be treated as `NullableContext(false)`.
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

What other forms? #Closed


The C#8 compiler will emit `NullableContextAttribute(true)` on top-level types
that contain any members (including members of nested types) with reference types that are not oblivious.
No other `NullableContextAttribute` instances will be emitted on types or members,
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

If we don't emit it on nested types, it would be hard to test that we properly recognize the attribute on a nested type in metadata. #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

Same goes for other forms of the attribute. #Closed

The C#8 compiler will emit `NullableContextAttribute(true)` on top-level types
that contain any members (including members of nested types) with reference types that are not oblivious.
No other `NullableContextAttribute` instances will be emitted on types or members,
and no assembly-level attribute will be emitted.
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

module vs. assembly #Closed


```c#
// C# representation of metadata
[Nullable(2)]
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

We might want to preserve the examples, perhaps move them to the other file. #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Added some examples.


In reply to: 290459329 [](ancestors = 290459329)

The following `NullableAttribute` cases are recognized and emitted for type parameter definitions:
1. `notnull` constraint in oblivious [emit context](#NullableContextAttribute): `NullableAttribute(1)`
2. `class` constraint in `#nullable enable` and in oblivious [emit context](#NullableContextAttribute): `NullableAttribute(1)`
2. `class` constraint in `#nullable disable` and in not annotated [emit context](#NullableContextAttribute): `NullableAttribute(0)`
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

  1. [](start = 0, length = 2)

Duplicate number #Closed

2. `class` constraint in `#nullable enable` and in oblivious [emit context](#NullableContextAttribute): `NullableAttribute(1)`
2. `class` constraint in `#nullable disable` and in not annotated [emit context](#NullableContextAttribute): `NullableAttribute(0)`
3. `class?` constraint: `NullableAttribute(2)`
4. No `notnull`, `class`, `struct`, `unmanaged`, or type constraints in `#nullable enable` (the equivalent of an `object?` constraint): `NullableAttribute(2)`
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

In 'not annotated' context:
notnull - No attribute
No constraints in #nullable enable - NullableAttribute(2)
No constraints in #nullable disable - If we do not emit anything, how do we distinguish this case from notnull?

It might be good to enumerate all scenarios per NullableContextAttribute (rather than having items corresponding to different contexts in one list) to make sure there is no conflict/ambiguity.
#Closed

```C#
[Nullable(2)] string s; // string?
[Nullable({ 2, 1, 2 }] Dictionary<string, object> d; // Dictionary<string!, object?>?
[Nullable({ 2, 1 }] int[] a; // int[]?
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

[Nullable({ 2, 1 }] [](start = 0, length = 19)

I think today we emit [Nullable({ 2, 0 }]. Are you proposing a change? #Closed

[Nullable(2)] string s; // string?
[Nullable({ 2, 1, 2 }] Dictionary<string, object> d; // Dictionary<string!, object?>?
[Nullable({ 2, 1 }] int[] a; // int[]?
[Nullable(1)] int[] b; // int[]!
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

I think today we emit [Nullable({ 1, 0 }]. Are you proposing a change? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

In a NullableContext(true) emit context, we should use 1 for value types.


In reply to: 290513550 [](ancestors = 290513550)

Copy link
Contributor

Choose a reason for hiding this comment

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

In a NullableContext(true) emit context, we should use 1 for value types.

Why do we need to emit any attribute for this type in NullableContext(true) ?


In reply to: 290519517 [](ancestors = 290519517,290513550)

[Nullable({ 2, 1, 2 }] Dictionary<string, object> d; // Dictionary<string!, object?>?
[Nullable({ 2, 1 }] int[] a; // int[]?
[Nullable(1)] int[] b; // int[]!
[Nullable(0)] int[] c; // int[]~
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

I think today we do not emit an attribute. Are you proposing a change? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

We will need to emit an attribute in a NullableContext(true) emit context.


In reply to: 290513682 [](ancestors = 290513682)

The attribute constructor takes a `bool enabled` argument:
if `true`, reference types are interpreted as not nullable;
if `false`, reference types are interpreted as oblivious.
If there is no `NullableContextAttribute` on containing types or the module, unannotated reference types are interpreted as oblivious.
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

unannotated [](start = 77, length = 11)

Does this refer to the absence of the NullableAttribute? #Closed

The C#8 compiler will recognize `NullableContextAttribute(bool)` instances on the module and (top-level and nested) types.
The only valid targets for `NullableContextAttribute` are module and type - attributes on other targets will be ignored silently.

The C#8 compiler will not emit a module-level `NullableContextAttribute`.
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

Are users allowed to apply the attribute in source? On any level. #Closed


Trivial/optimized cases:
1) All parts are NotAnnotated a NullableAttribute with a single value 1 (rather than an array of 1s)
1) All parts are NotAnnotated a NullableAttribute with a single value 1 (rather than an array of 1s)
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

� [](start = 30, length = 1)

Is this an intentional change? #Closed

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.
3) All parts are Oblivious the attribute is omitted, this matches how we interpret the lack of an attribute in legacy assemblies.
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

� [](start = 27, length = 1)

Is this an intentional change? #Closed

1. No constraints (see below) in `#nullable disable`: `NullableAttribute(0)`
1. No constraints (see below) in `#nullable enable`: `NullableAttribute(2)`

"No constraints" (see above) means no `notnull`, `class`, `struct`, `unmanaged`, or type constraints - the equivalent of an `object?` constraint.
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

the equivalent of an object? constraint. [](start = 103, length = 42)

When #nullable enable ? #Closed

}
class B
{
[Nullable({ 2, 1 }] int[] a; // int[]?
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

[Nullable({ 2, 1 }] int[] a; // int[]? [](start = 3, length = 38)

I think today we emit [Nullable({ 2, 0 }]. Are you proposing a change? #Closed

class B
{
[Nullable({ 2, 1 }] int[] a; // int[]?
[Nullable(1)] int[] b; // int[]!
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

[Nullable(1)] int[] b; // int[]! [](start = 3, length = 38)

I think today we emit [Nullable({ 1, 0 }]. Are you proposing a change? #Closed

{
[Nullable({ 2, 1 }] int[] a; // int[]?
int[] b; // int[]!
[Nullable(0)] int[] c; // int[]~
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 4, 2019

Choose a reason for hiding this comment

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

[Nullable(0)] int[] c; // int[]~ [](start = 3, length = 38)

For consistency with C.a I would expect to see [Nullable({ 0, 1 }] here. Unless you are proposing to make encoding for value types dependent on flags for other constituent parts within the same type reference. Please clarify. #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes that is the proposal. Added a comment.


In reply to: 290529397 [](ancestors = 290529397)

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (iteration 24), modulo suspicious visit for property parameters in NullableAttributesVisitor

@cston cston merged commit f886470 into dotnet:master Jun 25, 2019
@cston cston deleted the 35888 branch June 25, 2019 19:48
agocke added a commit to agocke/roslyn that referenced this pull request Jun 26, 2019
agocke added a commit to agocke/roslyn that referenced this pull request Jun 28, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants