-
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
Conversation
Closes dotnet#30075. Closes dotnet#30065. Closes dotnet#29594.
@@ -22,54 +22,43 @@ namespace System.Runtime.CompilerServices | |||
AllowMultiple = false)] | |||
public sealed class NullableAttribute : Attribute |
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.
NullableAttribute [](start = 24, length = 17)
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
|
||
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 ar not emitted on type parameter definitions and are not specially recognized on them. |
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.
ar [](start = 33, length = 2)
typo: are #Resolved
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 comment
The reason will be displayed to describe this comment to others. Learn more.
object> [](start = 19, length = 7)
Should this be Never mind. The type here is from PE. #ClosedDictionary<..., object?>?
in source as well?
{ | ||
AttributeInfo info = FindTargetAttribute(token, AttributeDescription.NullableAttribute); | ||
Debug.Assert(!info.HasValue || info.SignatureIndex == 0 || info.SignatureIndex == 1); | ||
|
||
defaultTransform = 0; |
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.
0; [](start = 31, length = 2)
Consider using another value to represent "not a value" and only set to 0 at line 2437 (from TryExtractValueFromAttribute
). #Resolved
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.
Consider using another value to represent "not a value" and only set to 0 at line 2437 (from TryExtractValueFromAttribute).
I see no benefit in inventing new "defaults" when this value just works, this is a well established pattern for functions that return information via out parameters and return boolean value indicating success of the operation. Consumers that really depend on the fact whether the attribute was found and cracked successfully should simply check return value of this method.
In reply to: 234368109 [](ancestors = 234368109)
@@ -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 or unconstrained generic type with `?` outside a `#nullable` context. |
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.
I thought it was an error to annotate an unconstrained generic type. #Resolved
break; | ||
|
||
case NullableAnnotation.Unknown: | ||
Debug.Assert((NullableAnnotation)transformFlag == NullableAnnotation.Unknown); |
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.
Debug.Assert((NullableAnnotation)transformFlag == NullableAnnotation.Unknown); [](start = 20, length = 78)
The assert looks redundant. #Closed
|
||
case NullableAnnotation.Unknown: | ||
Debug.Assert((NullableAnnotation)transformFlag == NullableAnnotation.Unknown); | ||
if (result.NullableAnnotation != NullableAnnotation.Unknown && (!result.NullableAnnotation.IsAnyNullable() || !oldTypeSymbol.IsNullableType())) |
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.
(!result.NullableAnnotation.IsAnyNullable() || !oldTypeSymbol.IsNullableType()) [](start = 83, length = 79)
nit: took me a while to understand this check. Inverting this last block helps: .. && !(result.NullableAnnotation.IsAnyNullable() && oldTypeSymbol.IsNullableType())
Maybe add a comment too: "// keep nullable annotation on Nullable<T>
types" #Resolved
var type = comp.GetWellKnownType(WellKnownType.Microsoft_CodeAnalysis_EmbeddedAttribute); | ||
Assert.True(type.IsErrorType()); | ||
|
||
// https://github.com/dotnet/roslyn/issues/29683 CSharpCompilation.AbstractSymbolSearcher needs to inject namespaces and types too |
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.
#29683 [](start = 15, length = 45)
can be closed? #Resolved
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.
can be closed?
I think it can be, I'll add to the list.
In reply to: 234738966 [](ancestors = 234738966)
This test requires both constructors (because we have simple and mixed case of nullability). I assume we produce the same error even if we only need one constructor and it exists. #Resolved Refers to: src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs:74 in 60ce669. [](commit_id = 60ce669, deletion_comment = False) |
Yes, this change didn't change the number of constructors and didn't change the way we complain about one of them is missing. In reply to: 440002524 [](ancestors = 440002524) Refers to: src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs:74 in 60ce669. [](commit_id = 60ce669, deletion_comment = False) |
@@ -98,7 +98,7 @@ private void AddNullableAnnotations(TypeSymbolWithAnnotations typeOpt) | |||
} | |||
|
|||
if (format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier) && | |||
!typeOpt.IsNullableType() && | |||
!typeOpt.IsNullableType() && !typeOpt.IsValueType && |
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.
!typeOpt.IsValueType && [](start = 45, length = 23)
I didn't understand what's a scenario were we put a nullable annotation on a value type, but it's not Nullable<T>
.
Is the when loading bad metadata? #Closed
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.
I didn't understand what's a scenario were we put a nullable annotation on a value type, but it's not Nullable.
Is the when loading bad metadata?
On the contrary, binding from source does that, always did.
In reply to: 234741474 [](ancestors = 234741474)
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.
From our discussion, some of the weird combinations (int
marked as nullable) only come from PE.
You mentioned that one reason for letting such combinations into the compiler is to avoid cycles. Do you remember what's a scenario?
In particular, I wonder why is that problem unique to nullability, but was ok for decoding dynamic or tuple names?
If it were possible, I still think it would be better for metadata import to ignore, rather than to propagate, bad metadata combinations. I'd expect all value types (except Nullable<T>
) should be imported as oblivious. I believe that is what existing metadata import tends to do.
Along those lines, why encode a nullable annotation for value types at all? We don't encode a dynamic flag for types that are not object, and we don't encode tuple names for types that are not tuples.
In reply to: 234742698 [](ancestors = 234742698,234741474)
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.
You mentioned that one reason for letting such combinations into the compiler is to avoid cycles. Do you remember what's a scenario?
For example, when we are loading type parameter constraints, we cannot ask questions whether the type parameter is a value type or a reference type
In particular, I wonder why is that problem unique to nullability, but was ok for decoding dynamic or tuple names?
In order to decode dynamic or tuple names we do not check whether targets are value or reference types either.
If it were possible, I still think it would be better for metadata import to ignore, rather than to propagate, bad metadata combinations. I'd expect all value types (except Nullable) should be imported as oblivious. I believe that is what existing metadata import tends to do.
I do not think this is something we should pursue. In any case, I think this PR doesn't change the way we import metadata in that specific regard, and it wasn't one of the goals to change this.
Along those lines, why encode a nullable annotation for value types at all? We don't encode a dynamic flag for types that are not object, and we don't encode tuple names for types that are not tuples.
Compiler always marks value types as oblivious in metadata, but we should be prepared to deal with malformed cases. This particular line of code makes sure that display doesn't confuse the reader that it deals with Nullable<T>
case.
In reply to: 234772025 [](ancestors = 234772025,234742698,234741474)
Not related to a file in this PR: since we're undoing the injection of NNT attribute, two EnC tests can be re-enabled. #29662 Refers to: src/Workspaces/CoreTest/SymbolKeyTests.cs:68 in 60ce669. [](commit_id = 60ce669, deletion_comment = False) |
@@ -206,9 +206,8 @@ private ImmutableArray<TypeSymbolWithAnnotations> GetDeclaredConstraintTypes() | |||
} | |||
} | |||
|
|||
// https://github.com/dotnet/roslyn/issues/30075: Test different [NonNullTypes] on method and containing type. |
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.
#30075 [](start = 27, length = 45)
Can be resolved? #Resolved
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.
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.
LGTM Thanks (iteration 4)
Other forms of NullableAttribute ar 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 comment
The 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 (int
and Nullable<int>
). #Resolved
Closes #30075.
Closes #30065.
Closes #29594.