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

Treat unconstrained type parameters declared in #nullable disable context as having an unknown nullability in case they are substituted with a reference type. #34797

Merged
merged 12 commits into from
Apr 16, 2019

Conversation

AlekseyTs
Copy link
Contributor

@AlekseyTs AlekseyTs commented Apr 5, 2019

As apposed to possibly nullable or not nullable reference type.
Fixes #29980.
Fixes #34844.

…`` context as having an unknown nullability in case they are substituted with a reference type.

As apposed to possibly nullable or not nullable reference type.
Fixes dotnet#29980.
@AlekseyTs AlekseyTs requested a review from a team as a code owner April 5, 2019 19:17
@AlekseyTs AlekseyTs added the Feature - Nullable Reference Types Nullable Reference Types label Apr 5, 2019
@gafter
Copy link
Member

gafter commented Apr 5, 2019

I do not understand the value of this change. There seems to be no effect on executable code (because those are uses of type parameters, and have their own annotations). And in the declarations, it only seems to introduce warnings that are not valuable to the end user. So why would we make this change? #Closed

@gafter
Copy link
Member

gafter commented Apr 5, 2019

Please bring this to the LDM for confirmation of the proposed language change before integrating this.


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

@@ -14037,6 +14037,9 @@ partial class C1
var compilation = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());

compilation.VerifyDiagnostics(
// (10,18): error CS0761: Partial method declarations of 'C1.M1<T>(T?, T[]?, Action<T?>, Action<T?[]?>?[]?)' have inconsistent type parameter constraints
// partial void M1<T>(T? x, T[]? y, System.Action<T?> z, System.Action<T?[]?>?[]? u) where T : class
Diagnostic(ErrorCode.ERR_PartialMethodInconsistentConstraints, "M1").WithArguments("C1.M1<T>(T?, T[]?, System.Action<T?>, System.Action<T?[]?>?[]?)").WithLocation(10, 18),
Copy link
Member

@gafter gafter Apr 5, 2019

Choose a reason for hiding this comment

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

Diagnostic [](start = 16, length = 10)

This looks wrong to me #Closed

Copy link
Member

Choose a reason for hiding this comment

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

If they differ by nullable annotations only, this should be a nullability warning, not an error.


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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If they differ by nullable annotations only, this should be a nullability warning, not an error.

At the moment, nullable mismatch causes an error. We do have an issue #30229 to finalize the design and behavior around nullability mismatch in partials. For the purpose of this PR, the error is good enough to verify an expectation that there is a mismatch, even though the error might be converted to a warning, or go away completely in the future.


In reply to: 272742716 [](ancestors = 272742716,272738601)

Copy link
Member

Choose a reason for hiding this comment

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

OK, please add a comment.


In reply to: 272766054 [](ancestors = 272766054,272742716,272738601)

// (23,17): warning CS8633: Nullability in constraints for type parameter 'TF2A' of method 'A.F2<TF2A>()' doesn't match the constraints for type parameter 'TF2' of interface method 'I1.F2<TF2>()'. Consider using an explicit interface implementation instead.
// public void F2<TF2A>()
Diagnostic(ErrorCode.WRN_NullabilityMismatchInConstraintsOnImplicitImplementation, "F2").WithArguments("TF2A", "A.F2<TF2A>()", "TF2", "I1.F2<TF2>()").WithLocation(23, 17)
);
Copy link
Member

@gafter gafter Apr 5, 2019

Choose a reason for hiding this comment

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

The LDM specified that a disabled region suppresses nullability warnings. The bug here was that we were producing a nullability warning in a disabled region. It doesn't appear you fixed it by adjusting where we suppress or produce nullability diagnostics. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The LDM specified that a disabled region suppresses nullability warnings. The bug here was that we were producing a nullability warning in a disabled region. It doesn't appear you fixed it by adjusting where we suppress or produce nullability diagnostics.

The warnings can be enabled even when declarations are disabled. I'll adjust the test to reflect that. So, this isn't an issue about suppression isn't working. We do have an issue #32742 tracking the work about finalizing what warning should be suppressed when nullable warnings are disabled.


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

Copy link
Member

Choose a reason for hiding this comment

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

Please add a comment.


In reply to: 272763113 [](ancestors = 272763113,272739261)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please add a comment.

I do not think any comment is warranted here. As I mentioned in the previous reply to this thread, I am planning to adjust the test to enable nullability warnings explicitly


In reply to: 272766744 [](ancestors = 272766744,272763113,272739261)

Copy link
Member

Choose a reason for hiding this comment

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

OK. Reopening thread to track that a change (enable nullability warnings explicitly) is pending for this test.


In reply to: 272768576 [](ancestors = 272768576,272766744,272763113,272739261)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reopening thread to track that a change (enable nullability warnings explicitly) is pending for this test.

This not pending, it is done


In reply to: 273195151 [](ancestors = 273195151,272768576,272766744,272763113,272739261)


comp2.VerifyDiagnostics(
// (3,19): error CS0265: Partial declarations of 'I1<TF1>' have inconsistent constraints for type parameter 'TF1'
// partial interface I1<TF1> where TF1 : new()
Copy link
Member

@gafter gafter Apr 5, 2019

Choose a reason for hiding this comment

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

This is correct, but they differ only in nullability, and so this should be a nullability warning that is suppressed in this disabled context. Please add a comment if this is a known issue. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is correct, but they differ only in nullability, and so this should be a nullability warning that is suppressed in this disabled context. Please add a comment if this is a known issue.

This is existing behavior and is not changed in this PR. The object constraint is the source of the error. Partial declarations cannot add any constraints.


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


comp1.VerifyDiagnostics(
// (8,14): error CS0761: Partial method declarations of 'A.M1<TF1>()' have inconsistent type parameter constraints
// partial void M1<TF1>() where TF1 : object
Copy link
Member

@gafter gafter Apr 5, 2019

Choose a reason for hiding this comment

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

Because they differ by nullability only, this should be a nullable warning and suppressed in this disabled region. Please add a comment if this is a known issue. #Closed

@gafter
Copy link
Member

gafter commented Apr 5, 2019

I've created dotnet/csharplang#2398 to describe the spec change for LDM review. #Pending

@gafter gafter self-assigned this Apr 5, 2019
@gafter
Copy link
Member

gafter commented Apr 5, 2019

Will resume reviewing implementation Monday. #Closed

/// where nullable annotations are disabled.
/// Cannot be combined with <see cref="ReferenceType"/>, <see cref="ValueType"/> and <see cref="Unmanaged"/>.
/// </summary>
UnknownNullabilityIfReferenceType = 0x40,
Copy link
Member

@gafter gafter Apr 8, 2019

Choose a reason for hiding this comment

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

UnknownNullabilityIfReferenceType [](start = 8, length = 33)

"Unknown" is now called "Oblivious" in NullableAnnotation. Please rename this to match. #Resolved

/// <summary>
/// Type parameter has no type constraints, including `struct`, `class`, `unmanaged` and is declared in a context
/// where nullable annotations are disabled.
/// Cannot be combined with <see cref="ReferenceType"/>, <see cref="ValueType"/> and <see cref="Unmanaged"/>.
Copy link
Member

@gafter gafter Apr 8, 2019

Choose a reason for hiding this comment

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

and [](start = 89, length = 3)

or #Resolved

{
ImmutableArray<TypeParameterConstraintClause> defaultClauses = binder.GetDefaultTypeParameterConstraintClauses(typeParameterList);

return defaultClauses.IsEmpty() ? ImmutableArray<TypeParameterConstraintClause>.Empty : defaultClauses;
Copy link
Member

@gafter gafter Apr 8, 2019

Choose a reason for hiding this comment

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

defaultClauses.IsEmpty() ? ImmutableArray.Empty : defaultClauses [](start = 23, length = 95)

I don't understand why this isn't just defaultClauses. Are you trying to implement an optimization that you assume ArrayBuilder doesn't perform? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't understand why this isn't just defaultClauses. Are you trying to implement an optimization that you assume ArrayBuilder doesn't perform?

defaultClauses here contains at least one item. If all items do not contain any significant data, we replace it with an empty array so that later phases don't have to worry about doing more analysis. This mimics what we would get prior to this PR.


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

Copy link
Member

@gafter gafter Apr 8, 2019

Choose a reason for hiding this comment

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

How can "defaultClauses here contains at least one item" when defaultClauses.IsEmpty()? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How can "defaultClauses here contains at least one item" when defaultClauses.IsEmpty()?

This is an extension method in TypeParameterConstraintClauseExtensions type, not a property on an ImmutableArray that checks it's length.


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

@@ -207,9 +214,26 @@ internal partial class Binder
}
}

if (!hasTypeLikeConstraint && !IsNullableEnabled(typeParameterSyntax.Identifier))
{
Copy link
Member

@gafter gafter Apr 8, 2019

Choose a reason for hiding this comment

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

It would be useful to have some comments somewhere about the strategy here: we add constraints to represent the default oblivious constraint where T: object~, which causes us to suppress the IL emission of the default nullable constraint to imitate the behavior of pre-nullable compilers. When reading an assembly, the absence of a constraint causes us to create the default oblivious constraint. where T: object~, whereas reading the default nullable constraint where T: object? causes us to drop it on the floor, as that is the new default. Adjusted to correct any misunderstanding I may have. #Resolved

Copy link
Member

@gafter gafter left a comment

Choose a reason for hiding this comment

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

Done reviewing through Iteration 3.

@@ -103,5 +119,10 @@ internal static bool IsEarly(this ImmutableArray<TypeParameterConstraintClause>
{
return constraintClauses.Any(clause => clause.IsEarly);
}

internal static bool IsEmpty(this ImmutableArray<TypeParameterConstraintClause> constraintClauses)
Copy link
Member

@gafter gafter Apr 8, 2019

Choose a reason for hiding this comment

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

IsEmpty [](start = 29, length = 7)

Can you please rename something like "ContainsOnlyEmptyConstraintClauses"? #Resolved

Copy link
Member

@gafter gafter left a comment

Choose a reason for hiding this comment

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

:shipit:

@AlekseyTs
Copy link
Contributor Author

@dotnet/roslyn-compiler Please review, need a second sign-off.

@sharwell sharwell changed the title Treat unconstrained type parameters declared in #nullable disable context as having an unknown nullability in case they are substituted with a reference type. Treat unconstrained type parameters declared in #nullable disable context as having an unknown nullability in case they are substituted with a reference type. Apr 8, 2019
@AlekseyTs
Copy link
Contributor Author

@dotnet/roslyn-compiler Please review, need a second sign-off.

{
ImmutableArray<TypeParameterConstraintClause> defaultClauses = binder.GetDefaultTypeParameterConstraintClauses(typeParameterList);

return defaultClauses.ContainsOnlyEmptyConstraintClauses() ? ImmutableArray<TypeParameterConstraintClause>.Empty : defaultClauses;
Copy link
Member

@cston cston Apr 9, 2019

Choose a reason for hiding this comment

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

defaultClauses.ContainsOnlyEmptyConstraintClauses() [](start = 23, length = 51)

Should Binder.GetDefaultTypeParameterConstraintClauses() simply return ImmutableArray<TypeParameterConstraintClause>.Empty if all are empty? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should Binder.GetDefaultTypeParameterConstraintClauses() simply return ImmutableArray.Empty if all are empty?

Some consumers do not want this optimization.


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

{
constraints = default(GenericParameterConstraintHandleCollection);
Interlocked.CompareExchange(ref _lazyConstraintsUseSiteErrorInfo, new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this), CSDiagnosticInfo.EmptyErrorInfo);
}
Copy link
Member

@cston cston Apr 9, 2019

Choose a reason for hiding this comment

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

Please extract this to a helper method, shared with GetDeclaredConstraintTypes(). #Resolved

@@ -568,7 +568,7 @@ internal bool GetIsReferenceType(ConsList<TypeParameterSymbol> inProgress)
}

// https://github.com/dotnet/roslyn/issues/26198 Should this API be exposed through ITypeParameterSymbol?
internal bool? IsNotNullableIfReferenceType => GetIsNotNullableIfReferenceType();
internal abstract bool? IsNotNullableIfReferenceType { get; }
Copy link
Member

@jaredpar jaredpar Apr 10, 2019

Choose a reason for hiding this comment

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

Should this be IsNonNullableIfReferenceType? Generally we use Non in the APIs over Not. Want to make sure that's not significant here. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should this be IsNonNullableIfReferenceType? Generally we use Non in the APIs over Not. Want to make sure that's not significant here.

This makes no difference to me. At the moment this is not a public API, we can revisit the name if we decide to make it public.


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

Copy link
Member

@jaredpar jaredpar left a comment

Choose a reason for hiding this comment

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

:shipit:

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Apr 10, 2019

@gafter, @cston Could you please review the last commit, it fixes a scenario I missed before?

@AlekseyTs AlekseyTs merged commit 651d3f0 into dotnet:master Apr 16, 2019
333fred added a commit to 333fred/roslyn that referenced this pull request Apr 17, 2019
* dotnet/master: (495 commits)
  Roslyn Installer: Stop processes that block VSIX installation. (dotnet#34886)
  Remove unused helper BeginInvokeOnUIThread
  Apply a hang mitigating timeout to InvokeOnUIThread
  Apply a hang mitigating timeout in RestoreNuGetPackages
  Apply a hang mitigating timeout to WaitForApplicationIdle
  Fix Value/Ref checks for pattern Index/Range (dotnet#35004)
  Fix assert in remove unused member analyzer
  Treat unconstrained type parameters declared in `#nullable disable` context as having an oblivious nullability in case they are substituted with a reference type. (dotnet#34797)
  Add symbol name to tests. Fix to be the correct name emitted
  PR feedback
  Improve IDE0052 diagnostic message for properties with used setter but unused getter
  Use original definition symbol for fetching control flow graph of generic local functions.
  Properly treat ambiguous explicit interface implementations (dotnet#34584)
  Remove the dependence between the order in NullableAnnotation and metadata attribute values (dotnet#34909)
  Fix warning level test.
  Fix bootstrap on Linux/Mac (dotnet#34978)
  disable completion for immediate window commands (dotnet#32631)
  Updated PreviewWarning color
  Implement design changes for "pattern" Index/Range indexers  (dotnet#34897)
  Add IVTs to 16.1 version of RemoteLS
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants