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

Use [NonNullTypes] to interpret unannotated types in source #27807

Merged
merged 16 commits into from
Jun 29, 2018

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Jun 13, 2018

In a context where NonNullTypes is true, an unannotated type is interpreted as non-null. But in a context where it is false, then such type is interpreted as null-oblivious.
Annotated types (ie. with ?) are unaffected.

The main parts of this change

  • revive a property on Symbol to indicate the presence of the attribute (on symbol or containing symbol, with module symbols terminating the chain)
  • add a similar property in Binder (generally look for the containing symbol's context, except if a binding flag was set to break cycles)
  • the logic to copy modifiers by re-applying transforms now takes the NonNullTypes context into account
  • the [NonNullTypes] attribute is currently recognized syntactically, rather that properly bound. That is a temporary solution to avoid the numerous code cycles that arose. I'll work on breaking those cycles next.

What is not yet done:

  • add more verification of symbols
  • [NonNullTypes] targeting a return value or a parameter (will be a follow-up PR)
  • add more verification of emitted metadata (for those transformed) (will be a follow-up PR)
  • I intend to unskip some more existing tests related to this attribute (will be a follow-up PR)
  • there's one difficult cycle to break in SourceNamedTypeSymbol.MakeOneDeclaredBases which I will investigate and fix in a separate PR
  • this PR focuses on the NonNullTypes attribute in source. The handling of this attribute in metadata will be a follow-up PR

@jcouv jcouv added this to the 16.0 milestone Jun 13, 2018
@jcouv jcouv self-assigned this Jun 13, 2018
@jcouv jcouv requested a review from a team as a code owner June 13, 2018 23:04
@@ -803,7 +813,7 @@ private NamespaceOrTypeSymbolWithAnnotations UnwrapAlias(NamespaceOrTypeOrAliasS
if (symbol.IsAlias)
{
AliasSymbol discarded;
return NamespaceOrTypeSymbolWithAnnotations.Create(Compilation, (NamespaceOrTypeSymbol)UnwrapAlias(symbol.Symbol, out discarded, diagnostics, syntax, basesBeingResolved));
return NamespaceOrTypeSymbolWithAnnotations.CreateNonNull(NonNullTypes, (NamespaceOrTypeSymbol)UnwrapAlias(symbol.Symbol, out discarded, diagnostics, syntax, basesBeingResolved));
Copy link
Member Author

@jcouv jcouv Jun 13, 2018

Choose a reason for hiding this comment

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

📝 I will check whether this and the similar change below are covered by test. #Resolved

@@ -588,6 +593,11 @@ private bool IsByRefTarget(int slot)
return false;
}

private void ReportWarningW(SyntaxNode syntax)
{
ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_ConvertingNullableToNonNullable, syntax);
Copy link
Member Author

@jcouv jcouv Jun 13, 2018

Choose a reason for hiding this comment

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

ReportStaticNullCheckingDiagnostics [](start = 12, length = 35)

📝 This makes it easy to set a breakpoint to catch all W warnings. #Closed

@jcouv jcouv added the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Jun 14, 2018
@jcouv
Copy link
Member Author

jcouv commented Jun 14, 2018

@AlekseyTs @cston I've run into some more non-trivial cycles. It got me thinking, does binding really need to care about merging the information from [NonNullTypes] and from annotations? What if we kept the two separate during initial binding?

On one side, we'd have the [NonNullTypes] information (which mostly comes from attributes, but they are regular well-known attributes if they are not used elsewhere in binding).
On the other side, we'd have the information about "annotated vs. unannotated" (true/false).
To decide if a type is non-null or null-oblivious, you need to put the two pieces of information together (NonNullTypes determines the meaning of unannotated). #Closed

@cston
Copy link
Member

cston commented Jun 14, 2018

                AttributeTargets.Struct,

AttributeTargets.All #Closed


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs:932 in e83865f. [](commit_id = e83865f, deletion_comment = False)

@@ -588,6 +593,11 @@ private bool IsByRefTarget(int slot)
return false;
}

private void ReportWarningW(SyntaxNode syntax)
Copy link
Member

@cston cston Jun 14, 2018

Choose a reason for hiding this comment

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

ReportWarningW [](start = 21, length = 14)

Consider naming ReportWWarning. #Resolved

@cston
Copy link
Member

cston commented Jun 14, 2018

        s /*T:string*/ .ToString(); // PROTOTYPE(NullableReferenceTypes): Expecting a string! state

Should be string~. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs:1699 in e83865f. [](commit_id = e83865f, deletion_comment = False)

@jcouv jcouv removed the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Jun 14, 2018
@cston
Copy link
Member

cston commented Jun 14, 2018

public struct C<T, NT> where T : S

Type parameter is not used here or in D<,>. #Closed


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs:2174 in e83865f. [](commit_id = e83865f, deletion_comment = False)

@cston
Copy link
Member

cston commented Jun 14, 2018

        compilation.VerifyDiagnostics();

Consider checking the [NonNullTypes] attributes had an effect. Also consider adding delegate tests with ?. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs:2240 in e83865f. [](commit_id = e83865f, deletion_comment = False)

@cston
Copy link
Member

cston commented Jun 14, 2018

        compilation.VerifyDiagnostics();

Consider testing C(nullableField) and D(nullableField) so it clear the [NonNullTypes] attributes had an effect. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs:2277 in e83865f. [](commit_id = e83865f, deletion_comment = False)

@cston
Copy link
Member

cston commented Jun 14, 2018

        comp.VerifyDiagnostics();

There are several tests where there is only [NonNullTypes(false)] but no [NonNullTypes(true)] and where no diagnostics are expected. Will we know to update these tests if and when the default is switched to [NonNullTypes(false)]? #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs:3489 in e83865f. [](commit_id = e83865f, deletion_comment = False)

Debug.Assert(!info.HasValue || info.SignatureIndex == 0);

if (info.HasValue && TryExtractValueFromAttribute(info.Handle, out value, s_attributeBooleanValueExtractor))
{
return true;
}

value = false;
value = default;
Copy link
Member

@cston cston Jun 14, 2018

Choose a reason for hiding this comment

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

default [](start = 20, length = 7)

Consider using false instead. #Closed

#region NonNullTypesAttribute

private bool? _nonNullTypes;
public bool? NonNullTypes
Copy link
Member

@cston cston Jun 14, 2018

Choose a reason for hiding this comment

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

public bool? NonNullTypes [](start = 8, length = 25)

Should these NonNullTypes properties be on C# attribute data types only: FieldEarlyWellKnownAttributeData, etc.? #Closed

@@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.CSharp
/// A specific location for binding.
/// </summary>
[Flags]
internal enum BinderFlags : uint
internal enum BinderFlags : ulong
Copy link
Member

@cston cston Jun 14, 2018

Choose a reason for hiding this comment

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

ulong [](start = 32, length = 5)

Is this change needed? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

I have to say I was surprised by that too (I thought I'd be using the last bit, but not going over). The error I got on 1 << 31 is: Error CS0031 Constant value '-2147483648' cannot be converted to a 'uint'


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

Copy link
Member

Choose a reason for hiding this comment

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

Try 1u << 31


In reply to: 195607839 [](ancestors = 195607839,195579166)

@@ -670,6 +681,13 @@ public override TypeSymbolWithAnnotations AsNotNullableReferenceType()
new NonLazyType(_typeSymbol, isNullable: false, _customModifiers);
}

public override TypeSymbolWithAnnotations AsObliviousReferenceType()
{
Copy link
Member

@cston cston Jun 14, 2018

Choose a reason for hiding this comment

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

{ [](start = 12, length = 1)

Debug.Assert(_isNullable != true); #Resolved

{
get
{
Debug.Assert(false); // PROTOTYPE(NullableReferenceTypes): Can we reach this?
Copy link
Member

@cston cston Jun 14, 2018

Choose a reason for hiding this comment

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

// PROTOTYPE(NullableReferenceTypes): Can we reach this? [](start = 37, length = 56)

Perhaps: F([NotNullTypes]List<object?> arg = default(List<object>)) #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

I still didn't manage to reach this. I'll keep PROTOTYPE for a follow-up.


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

@@ -832,13 +832,15 @@ public virtual bool HasUnsupportedMetadata
}

/// <summary>
/// Is module/type/method/field/property/event/parameter definition opted out of nullable warnings. Not valid to call on non-definitions.
/// Is module/type/method/field/property/event/parameter definition opted-in/out of treating un-annotated types as non-null.
/// This is determined by the presence of the `[NonNullTypes]` attribute and whether the containing module utilizes nullable reference types.
Copy link
Member

@cston cston Jun 14, 2018

Choose a reason for hiding this comment

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

and whether the containing module utilizes nullable reference types [](start = 81, length = 67)

Isn't this also [NonNullTypes]? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

There is also defaulting logic on modules (currently based on language version). I'll update comment.


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

@@ -6,25 +6,25 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class EventWellKnownAttributeData : CommonEventWellKnownAttributeData
{
#region NullableOptOutAttribute
#region NonNullTypesAttribute
Copy link
Member

@cston cston Jun 14, 2018

Choose a reason for hiding this comment

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

NonNullTypesAttribute [](start = 16, length = 21)

Seems surprising that some AttributeData cases are early, others are not. #Closed

@@ -6,25 +6,25 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class MethodWellKnownAttributeData : CommonMethodWellKnownAttributeData
{
#region NullableOptOutAttribute
#region NonNullTypesAttribute
Copy link
Member

@cston cston Jun 14, 2018

Choose a reason for hiding this comment

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

NonNullTypesAttribute [](start = 16, length = 21)

This is also on CommonMethodEarlyWellKnownAttributeData. Is that correct? #Closed

@@ -28,6 +28,7 @@ internal sealed class SourceOrdinaryMethodSymbol : SourceMemberMethodSymbol
private ImmutableArray<ParameterSymbol> _lazyParameters;
private TypeSymbolWithAnnotations _lazyReturnType;
private bool _lazyIsVararg;
private bool? _nonNullTypesFromAttributes;
Copy link
Member

@cston cston Jun 14, 2018

Choose a reason for hiding this comment

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

_nonNullTypesFromAttributes [](start = 22, length = 27)

_lazyNonNullTypesFromAttributes #Closed

@cston
Copy link
Member

cston commented Jun 26, 2018

    [Fact(Skip = "NonNullTypes does not control warnings")] // PROTOTYPE(NullableReferenceTypes): Update or remove test.

Let's remove these tests that expect the attribute to control warnings. Perhaps in a separate PR. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs:24755 in 31429cd. [](commit_id = 31429cd88ba99d331ca9c3b96119f33e26461979, deletion_comment = False)

@jcouv
Copy link
Member Author

jcouv commented Jun 26, 2018

    [Fact(Skip = "NonNullTypes does not control warnings")] // PROTOTYPE(NullableReferenceTypes): Update or remove test.

I'm expecting to each unskip or remove them, in a follow-up PR.


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


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs:24755 in 31429cd. [](commit_id = 31429cd88ba99d331ca9c3b96119f33e26461979, deletion_comment = False)

@jcouv
Copy link
Member Author

jcouv commented Jun 26, 2018

@dotnet/roslyn-compiler for a second review. Thanks

@jcouv
Copy link
Member Author

jcouv commented Jun 26, 2018

        compilation.VerifyDiagnostics();

@cston From our discussion, I'm disallowing [NonNullTypes] on return value and parameters.


In reply to: 399260480 [](ancestors = 399260480,397410688)


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs:2240 in e83865f. [](commit_id = e83865f, deletion_comment = False)

@jcouv
Copy link
Member Author

jcouv commented Jun 28, 2018

@333fred for a second review when you get the chance. Thanks #Resolved

[AttributeUsage(
AttributeTargets.All,
AllowMultiple = false)]
[AttributeUsage(AttributeTargets.Class |
Copy link
Member

@333fred 333fred Jun 28, 2018

Choose a reason for hiding this comment

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

Why not assembly? #Resolved

Copy link
Member Author

@jcouv jcouv Jun 28, 2018

Choose a reason for hiding this comment

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

From our discussion, I added a note to work items (module: target is indeed unfamiliar to developers). #Resolved

receiverArgument = CreateConversion(receiver, methodResult.Result.ConversionForArg(0),
GetTypeOrReturnTypeWithAdjustedNullableAnnotations(receiverParameter).TypeSymbol,
receiverArgument = CreateConversion(receiver, methodResult.Result.ConversionForArg(0),
receiverParameter.Type.TypeSymbol,
Copy link
Member

Choose a reason for hiding this comment

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

         [](start = 56, length = 13)

Nit: consider reformatting to have consistent indentation with the previous line. Looks like this is a victim of past renames.

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

LGTM. One small formatting nits.

@jcouv jcouv merged commit 45c76ee into dotnet:features/NullableReferenceTypes Jun 29, 2018
@jcouv jcouv deleted the urtann branch June 29, 2018 03:48
get
{
// PROTOTYPE(NullableReferenceTypes): temporary solution to avoid cycle
return SyntaxBasedNonNullTypes(this.AttributeDeclarationSyntaxList) ?? base.NonNullTypes;
Copy link
Member Author

Choose a reason for hiding this comment

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

@AlekseyTs In case this helps, this is the approach we had previously used for syntax-based detection of NonNullTypes.
The specific SyntaxBasedNonNullTypes in this PR was very inefficient, but we could use that model and make the check cheaper (ie. avoid .ToString()).

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.

3 participants